| // Copyright 2019 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| use anyhow::{Context as _, Error}; | |
| use fuchsia_async::{self as fasync, DurationExt, Timer}; | |
| use fuchsia_framebuffer::{ | |
| to_565, Config, Frame, FrameBuffer, FrameSet, FrameUsage, ImageId, PixelFormat, VSyncMessage, | |
| }; | |
| use fuchsia_zircon::DurationNum; | |
| use futures::{channel::mpsc::unbounded, future, StreamExt, TryFutureExt}; | |
| use std::{ | |
| cell::RefCell, | |
| collections::BTreeSet, | |
| env, | |
| io::{self, Read}, | |
| rc::Rc, | |
| thread, | |
| time::Instant, | |
| }; | |
| /// Convenience function that can be called from main and causes the Fuchsia process being | |
| /// run over ssh to be terminated when the user hits control-C. | |
| pub fn wait_for_close() { | |
| if let Some(argument) = env::args().next() { | |
| if !argument.starts_with("/tmp") { | |
| return; | |
| } | |
| } | |
| thread::spawn(move || loop { | |
| let mut input = [0; 1]; | |
| match io::stdin().read_exact(&mut input) { | |
| Ok(()) => {} | |
| Err(_) => { | |
| std::process::exit(0); | |
| } | |
| } | |
| }); | |
| } | |
| struct FrameManager { | |
| frame_set: FrameSet, | |
| } | |
| // this buffering approach will only work with two images, since | |
| // as soon as it is released by the display controller it is prepared | |
| // for use again and their can only be one prepared image. | |
| const BUFFER_COUNT: usize = 2; | |
| impl FrameManager { | |
| pub async fn new(fb: &mut FrameBuffer) -> Result<FrameManager, Error> { | |
| // this buffering approach will only work with two images, since | |
| // as soon as it is released by the display controller it is prepared | |
| // for use again and their can only be one prepared image. | |
| let mut available = BTreeSet::new(); | |
| let config = fb.get_config(); | |
| let image_ids = fb.get_image_ids(); | |
| for image_id in image_ids { | |
| let frame = fb.get_frame_mut(image_id); | |
| let black = [0x00, 0x00, 0x00, 0xFF]; | |
| if config.format != PixelFormat::Rgb565 { | |
| frame.fill_rectangle(0, 0, config.width, config.height, &black); | |
| } else { | |
| frame.fill_rectangle(0, 0, config.width, config.height, &to_565(&black)); | |
| } | |
| available.insert(image_id); | |
| } | |
| let frame_set = FrameSet::new(available); | |
| Ok(FrameManager { frame_set }) | |
| } | |
| pub fn present_prepared( | |
| &mut self, | |
| fb: &mut FrameBuffer, | |
| sender: Option<futures::channel::mpsc::UnboundedSender<ImageId>>, | |
| ) -> Result<bool, Error> { | |
| if let Some(prepared) = self.frame_set.prepared { | |
| fb.flush_frame(prepared)?; | |
| fb.present_frame(prepared, sender, true)?; | |
| self.frame_set.mark_presented(prepared); | |
| Ok(true) | |
| } else { | |
| Ok(false) | |
| } | |
| } | |
| pub fn prepare_frame<F>(&mut self, fb: &mut FrameBuffer, f: F) | |
| where | |
| F: FnOnce(&mut Frame), | |
| { | |
| if let Some(image_id) = self.frame_set.get_available_image() { | |
| let frame = fb.get_frame_mut(image_id); | |
| f(frame); | |
| self.frame_set.mark_prepared(image_id); | |
| } else { | |
| println!("no free image to prepare"); | |
| } | |
| } | |
| pub fn frame_done_presenting(&mut self, image_id: ImageId) { | |
| self.frame_set.mark_done_presenting(image_id); | |
| } | |
| } | |
| const FRAME_DELTA: u64 = 10_000_000; | |
| fn update(config: &Config, frame: &mut Frame, timestamp: u64) -> Result<(), Error> { | |
| let box_color = [0x80, 0x00, 0x80, 0xFF]; | |
| let white = [0xFF, 0xFF, 0xFF, 0xFF]; | |
| let box_size = 500; | |
| let box_x = config.width / 2 - box_size / 2; | |
| let box_y = config.height / 2 - box_size / 2; | |
| if config.format != PixelFormat::Rgb565 { | |
| frame.fill_rectangle(box_x, box_y, box_size, box_size, &box_color); | |
| } else { | |
| frame.fill_rectangle(box_x, box_y, box_size, box_size, &to_565(&box_color)); | |
| } | |
| let delta = ((timestamp / FRAME_DELTA) % box_size as u64) as u32; | |
| let x = box_x + delta; | |
| let y = box_y + delta; | |
| if config.format != PixelFormat::Rgb565 { | |
| frame.fill_rectangle(x, box_y, 1, box_size, &white); | |
| frame.fill_rectangle(box_x, y, box_size, 1, &white); | |
| } else { | |
| frame.fill_rectangle(x, box_y, 1, box_size, &to_565(&white)); | |
| frame.fill_rectangle(box_x, y, box_size, 1, &to_565(&white)); | |
| } | |
| Ok(()) | |
| } | |
| fn main() -> Result<(), Error> { | |
| println!("box: started"); | |
| // this method is a hack that causes the application to terminate when | |
| // control-c is called and it has been run via scp/ssh. | |
| wait_for_close(); | |
| let mut executor = fasync::Executor::new().context("Failed to create executor")?; | |
| executor.run_singlethreaded(async { | |
| // create async channel sender/receiver pair to receive vsync messages | |
| let (sender, mut receiver) = unbounded::<VSyncMessage>(); | |
| // create a framebuffer | |
| let mut fb = FrameBuffer::new(FrameUsage::Cpu, None, Some(sender)).await?; | |
| fb.allocate_frames(BUFFER_COUNT, PixelFormat::Argb8888).await?; | |
| // Find out the details of the display this frame buffer targets | |
| let config = fb.get_config(); | |
| // Create our sample frame manager, which will create and manage the frames | |
| // and the display controller images they use. | |
| let frame_manager = Rc::new(RefCell::new(FrameManager::new(&mut fb).await?)); | |
| // prepare the first frame | |
| frame_manager.borrow_mut().prepare_frame(&mut fb, |frame| { | |
| update(&config, frame, 0).expect("update to work"); | |
| }); | |
| // create an async channel sender/receiver pair to receive messages when | |
| // the image managed by a frame is no longer being displayed. | |
| let (image_sender, mut image_receiver) = unbounded::<u64>(); | |
| // Present the first image. Without this present, the display controller | |
| // will not send vsync messages. | |
| frame_manager | |
| .borrow_mut() | |
| .present_prepared(&mut fb, Some(image_sender.clone())) | |
| .expect("present to work"); | |
| // Prepare a second image. There always wants to be a prepared image | |
| // to present at a fixed time after vsync. | |
| frame_manager.borrow_mut().prepare_frame(&mut fb, |frame| { | |
| update(&config, frame, 0).expect("update to work"); | |
| }); | |
| // keep track of the start time to use to animate the horizontal and | |
| // vertical lines. | |
| let start_time = Instant::now(); | |
| // Create a clone of the Rc holding the frame manager to move into | |
| // the async block that receives messages about images being no longer | |
| // in use. | |
| let frame_manager_image = frame_manager.clone(); | |
| let fb_ptr = Rc::new(RefCell::new(fb)); | |
| let fb_ptr2 = fb_ptr.clone(); | |
| // wait for events from the image freed fence to know when an | |
| // image can prepared. | |
| fasync::Task::local( | |
| async move { | |
| while let Some(image_id) = image_receiver.next().await { | |
| // Grab a mutable reference. This is guaranteed to work | |
| // since only one of this closure or the vsync closure can | |
| // be in scope at once. | |
| let mut frame_manager = frame_manager_image.borrow_mut(); | |
| // Note the freed image and immediately prepare it. Since there | |
| // are only two frames we are guaranteed that at most one can | |
| // be free at one time. | |
| frame_manager.frame_done_presenting(image_id); | |
| // Use elapsed time to animate the horizontal and vertical | |
| // lines | |
| let time = Instant::now().duration_since(start_time).as_nanos() as u64; | |
| let mut fb = fb_ptr.borrow_mut(); | |
| frame_manager.prepare_frame(&mut fb, |frame| { | |
| update(&config, frame, time).expect("update to work"); | |
| }); | |
| } | |
| Ok(()) | |
| } | |
| .unwrap_or_else(|e: anyhow::Error| { | |
| println!("error {:#?}", e); | |
| }), | |
| ) | |
| .detach(); | |
| // Listen for vsync messages to schedule an update of the displayed image | |
| fasync::Task::local( | |
| async move { | |
| while let Some(_vsync_message) = receiver.next().await { | |
| // Wait an arbitrary 10 milliseconds after vsync to present the | |
| // next prepared image. | |
| Timer::new(10_i64.millis().after_now()).await; | |
| // Grab a mutable reference. This is guaranteed to work | |
| // since only one of this closure or the vsync closure can | |
| // be in scope at once. | |
| let mut frame_manager = frame_manager.borrow_mut(); | |
| let mut fb = fb_ptr2.borrow_mut(); | |
| // Present the previously prepared image. As a side effect, | |
| // the currently presented image will be eventually freed. | |
| frame_manager | |
| .present_prepared(&mut fb, Some(image_sender.clone())) | |
| .expect("FrameManager::present_prepared to work"); | |
| } | |
| Ok(()) | |
| } | |
| .unwrap_or_else(|e: anyhow::Error| { | |
| println!("error {:#?}", e); | |
| }), | |
| ) | |
| .detach(); | |
| Ok::<(), Error>(()) | |
| })?; | |
| executor.run_singlethreaded(future::pending::<()>()); | |
| unreachable!(); | |
| } |