blob: 15f7f70db91a2ec56d7b47841602c92d132774bd [file] [log] [blame] [edit]
// 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, FrameCollection, FrameCollectionBuilder, FrameSet,
FrameUsage, ImageId, ImageInCollection, Message, PixelFormat,
};
use fuchsia_zircon::DurationNum;
use futures::{channel::mpsc::unbounded, future, StreamExt, TryFutureExt};
use std::{
cell::RefCell,
collections::BTreeSet,
rc::Rc,
time::{Duration, Instant},
};
struct FrameManager {
frame_set: FrameSet,
frame_collection: Option<FrameCollection>,
owned: bool,
collection_id: u64,
start: Instant,
}
// 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 {
async fn make_frames(
width: u32,
height: u32,
collection_id: u64,
fb: &mut FrameBuffer,
) -> Result<(FrameCollection, FrameSet), Error> {
let config = fb.get_config();
let frame_collection_builder = FrameCollectionBuilder::new(
width,
height,
config.format.into(),
FrameUsage::Cpu,
BUFFER_COUNT,
)?;
let config = fb.get_config();
let mut frame_collection =
frame_collection_builder.build(collection_id, &config, true, fb).await?;
let mut available = BTreeSet::new();
let config = fb.get_config();
let image_ids = frame_collection.get_image_ids();
for image_id in image_ids {
let frame = frame_collection.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);
}
Ok((frame_collection, FrameSet::new(collection_id, available)))
}
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 collection_id = 10;
let config = fb.get_config();
let (frame_collection, frame_set) =
Self::make_frames(config.width, config.height, collection_id, fb).await?;
Ok(FrameManager {
frame_set,
frame_collection: Some(frame_collection),
owned: true,
collection_id,
start: Instant::now(),
})
}
pub fn present_prepared(
&mut self,
fb: &mut FrameBuffer,
sender: Option<futures::channel::mpsc::UnboundedSender<ImageInCollection>>,
) -> Result<bool, Error> {
if let Some(prepared) = self.frame_set.prepared {
if self.owned {
if let Some(frame_collection) = self.frame_collection.as_mut() {
let frame = frame_collection.get_frame(prepared);
frame.flush()?;
fb.present_frame(frame, sender, true)?;
}
}
self.frame_set.mark_presented(prepared);
Ok(true)
} else {
Ok(false)
}
}
pub fn prepare_frame<F>(&mut self, _fb: &mut FrameBuffer, target: Instant, f: F)
where
F: FnOnce(Duration, &mut Frame),
{
if self.owned {
if let Some(image_id) = self.frame_set.get_available_image() {
if let Some(frame_collection) = self.frame_collection.as_mut() {
let frame = frame_collection.get_frame_mut(image_id);
let elapsed = target - self.start;
f(elapsed, 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);
}
pub async fn ownership_changed(&mut self, owned: bool, fb: &mut FrameBuffer) {
if owned != self.owned {
self.owned = owned;
if owned {
self.collection_id += 1;
let config = fb.get_config();
let (frame_collection, frame_set) =
Self::make_frames(config.width, config.height, self.collection_id, fb)
.await
.expect("make_frames");
self.frame_set = frame_set;
self.frame_collection = Some(frame_collection);
} else {
let frame_collection = self.frame_collection.take();
frame_collection.expect("frame_collection").release(fb);
}
}
}
}
const FRAME_DELTA: u64 = 10_000_000;
fn update(config: &Config, frame: &mut Frame, duration: Duration) -> Result<(), Error> {
let timestamp = duration.as_nanos() as u64;
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");
let mut executor = fasync::LocalExecutor::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::<Message>();
// create a framebuffer
let mut fb = FrameBuffer::new(FrameUsage::Cpu, None, None, Some(sender)).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?));
fb.configure_layer(&config, 0).expect("configure_layer");
// prepare the first frame
frame_manager.borrow_mut().prepare_frame(&mut fb, Instant::now(), |duration, frame| {
update(&config, frame, duration).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::<ImageInCollection>();
// 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, Instant::now(), |duration, frame| {
update(&config, frame, duration).expect("update to work");
});
// 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 frame_manager_ownership = 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_in_collection) = 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_in_collection.image_id);
// Use elapsed time to animate the horizontal and vertical
// lines
let mut fb = fb_ptr.borrow_mut();
frame_manager.prepare_frame(&mut fb, Instant::now(), |duration, frame| {
update(&config, frame, duration).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(message) = receiver.next().await {
match message {
Message::Ownership(in_owned) => {
let mut frame_manager = frame_manager_ownership.borrow_mut();
frame_manager
.ownership_changed(in_owned, &mut fb_ptr2.borrow_mut())
.await;
if in_owned {
frame_manager.prepare_frame(
&mut fb_ptr2.borrow_mut(),
Instant::now(),
|duration, frame| {
update(&config, frame, duration).expect("update to work");
},
);
frame_manager
.present_prepared(
&mut fb_ptr2.borrow_mut(),
Some(image_sender.clone()),
)
.expect("present to work");
frame_manager.prepare_frame(
&mut fb_ptr2.borrow_mut(),
Instant::now(),
|duration, frame| {
update(&config, frame, duration).expect("update to work");
},
);
}
}
Message::VSync(vsync_message) => {
// 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();
// Present the previously prepared image. As a side effect,
// the currently presented image will be eventually freed.
frame_manager
.present_prepared(
&mut fb_ptr2.borrow_mut(),
Some(image_sender.clone()),
)
.expect("FrameManager::present_prepared to work");
fb_ptr2
.borrow_mut()
.acknowledge_vsync(vsync_message.cookie)
.unwrap_or_else(|e: anyhow::Error| {
println!("acknowledge_vsync: error {:#?}", e);
});
}
}
}
Ok(())
}
.unwrap_or_else(|e: anyhow::Error| {
println!("error {:#?}", e);
}),
)
.detach();
Ok::<(), Error>(())
})?;
executor.run_singlethreaded(future::pending::<()>());
unreachable!();
}