blob: 4feac6bb14188b20a5885c2137e379bc388ff230 [file] [log] [blame]
// 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!();
}