blob: 2a89ba1ece12da12c0c17be49e954719adcefd15 [file] [log] [blame]
// Copyright 2018 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::{format_err, Context as _, Error};
use fdio::watch_directory;
use fidl::endpoints::{self, create_endpoints, ClientEnd, Proxy};
use fidl_fuchsia_hardware_display::{
ControllerEvent, ControllerMarker, ControllerProxy, ImageConfig, ProviderSynchronousProxy,
};
use fuchsia_async::{self as fasync, DurationExt, OnSignals, TimeoutExt};
use fuchsia_component::client::connect_to_service;
use fuchsia_zircon::{
self as zx, sys::ZX_TIME_INFINITE, AsHandleRef, DurationNum, Event, HandleBased, Signals,
Status, Vmo, VmoOp,
};
use futures::{StreamExt, TryFutureExt, TryStreamExt};
use mapped_vmo::Mapping;
use std::fs::OpenOptions;
use std::{
collections::{BTreeMap, BTreeSet},
sync::Arc,
};
#[cfg(test)]
use std::ops::Range;
pub mod sysmem;
const BUFFER_COLLECTION_ID: u64 = 1;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_NONE: u32 = 0;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_RGB_565: u32 = 131073;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_RGB_332: u32 = 65538;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_RGB_2220: u32 = 65539;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_ARGB_8888: u32 = 262148;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_RGB_x888: u32 = 262149;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_MONO_8: u32 = 65543;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_GRAY_8: u32 = 65543;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_MONO_1: u32 = 6;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_ABGR_8888: u32 = 262154;
#[allow(non_camel_case_types, non_upper_case_globals)]
const ZX_PIXEL_FORMAT_BGR_x888: u32 = 262155;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PixelFormat {
Abgr8888,
Argb8888,
BgrX888,
Gray8,
Mono1,
Mono8,
Rgb2220,
Rgb332,
Rgb565,
RgbX888,
Unknown,
}
impl Default for PixelFormat {
fn default() -> PixelFormat {
PixelFormat::Unknown
}
}
impl From<u32> for PixelFormat {
fn from(pixel_format: u32) -> Self {
#[allow(non_upper_case_globals)]
match pixel_format {
ZX_PIXEL_FORMAT_ABGR_8888 => PixelFormat::Abgr8888,
ZX_PIXEL_FORMAT_ARGB_8888 => PixelFormat::Argb8888,
ZX_PIXEL_FORMAT_BGR_x888 => PixelFormat::BgrX888,
ZX_PIXEL_FORMAT_MONO_1 => PixelFormat::Mono1,
ZX_PIXEL_FORMAT_MONO_8 => PixelFormat::Mono8,
ZX_PIXEL_FORMAT_RGB_2220 => PixelFormat::Rgb2220,
ZX_PIXEL_FORMAT_RGB_332 => PixelFormat::Rgb332,
ZX_PIXEL_FORMAT_RGB_565 => PixelFormat::Rgb565,
ZX_PIXEL_FORMAT_RGB_x888 => PixelFormat::RgbX888,
// ZX_PIXEL_FORMAT_GRAY_8 is an alias for ZX_PIXEL_FORMAT_MONO_8
ZX_PIXEL_FORMAT_NONE => PixelFormat::Unknown,
_ => PixelFormat::Unknown,
}
}
}
impl Into<u32> for PixelFormat {
fn into(self) -> u32 {
match self {
PixelFormat::Abgr8888 => ZX_PIXEL_FORMAT_ABGR_8888,
PixelFormat::Argb8888 => ZX_PIXEL_FORMAT_ARGB_8888,
PixelFormat::BgrX888 => ZX_PIXEL_FORMAT_BGR_x888,
PixelFormat::Mono1 => ZX_PIXEL_FORMAT_MONO_1,
PixelFormat::Mono8 => ZX_PIXEL_FORMAT_MONO_8,
PixelFormat::Rgb2220 => ZX_PIXEL_FORMAT_RGB_2220,
PixelFormat::Rgb332 => ZX_PIXEL_FORMAT_RGB_332,
PixelFormat::Rgb565 => ZX_PIXEL_FORMAT_RGB_565,
PixelFormat::RgbX888 => ZX_PIXEL_FORMAT_RGB_x888,
PixelFormat::Gray8 => ZX_PIXEL_FORMAT_GRAY_8,
PixelFormat::Unknown => ZX_PIXEL_FORMAT_NONE,
}
}
}
impl Into<fidl_fuchsia_sysmem::PixelFormatType> for PixelFormat {
fn into(self) -> fidl_fuchsia_sysmem::PixelFormatType {
match self {
PixelFormat::Abgr8888 => fidl_fuchsia_sysmem::PixelFormatType::R8G8B8A8,
PixelFormat::Argb8888 => fidl_fuchsia_sysmem::PixelFormatType::Bgra32,
PixelFormat::RgbX888 => fidl_fuchsia_sysmem::PixelFormatType::Bgra32,
PixelFormat::BgrX888 => fidl_fuchsia_sysmem::PixelFormatType::R8G8B8A8,
_ => fidl_fuchsia_sysmem::PixelFormatType::Invalid,
}
}
}
fn pixel_format_bytes(pixel_format: u32) -> usize {
((pixel_format >> 16) & 7) as usize
}
pub type ImageId = u64;
#[derive(Debug)]
pub struct FrameSet {
image_count: usize,
available: BTreeSet<ImageId>,
pub prepared: Option<ImageId>,
presented: BTreeSet<ImageId>,
}
impl FrameSet {
pub fn new(available: BTreeSet<ImageId>) -> FrameSet {
FrameSet {
image_count: available.len(),
available,
prepared: None,
presented: BTreeSet::new(),
}
}
#[cfg(test)]
pub fn new_with_range(r: Range<ImageId>) -> FrameSet {
let mut available = BTreeSet::new();
for image_id in r {
available.insert(image_id);
}
Self::new(available)
}
pub fn mark_presented(&mut self, image_id: ImageId) {
assert!(
!self.presented.contains(&image_id),
"Attempted to mark as presented image {} which was already in the presented image set",
image_id
);
self.presented.insert(image_id);
self.prepared = None;
}
pub fn mark_done_presenting(&mut self, image_id: ImageId) {
assert!(
self.presented.remove(&image_id),
"Attempted to mark as freed image {} which was not the presented image",
image_id
);
self.available.insert(image_id);
}
pub fn mark_prepared(&mut self, image_id: ImageId) {
assert!(self.prepared.is_none(), "Trying to mark image {} as prepared when image {} is prepared and has not been presented", image_id, self.prepared.unwrap());
self.prepared.replace(image_id);
self.available.remove(&image_id);
}
pub fn get_available_image(&mut self) -> Option<ImageId> {
let first = self.available.iter().next().map(|a| *a);
if let Some(first) = first {
self.available.remove(&first);
}
first
}
pub fn return_image(&mut self, image_id: ImageId) {
self.available.insert(image_id);
}
pub fn no_images_in_use(&self) -> bool {
self.available.len() == self.image_count
}
}
#[cfg(test)]
mod frameset_tests {
use crate::{FrameSet, ImageId};
use std::ops::Range;
const IMAGE_RANGE: Range<ImageId> = 200..202;
#[test]
#[should_panic]
fn test_double_prepare() {
let mut fs = FrameSet::new_with_range(IMAGE_RANGE);
fs.mark_prepared(100);
fs.mark_prepared(200);
}
#[test]
#[should_panic]
fn test_not_presented() {
let mut fs = FrameSet::new_with_range(IMAGE_RANGE);
fs.mark_done_presenting(100);
}
#[test]
#[should_panic]
fn test_already_presented() {
let mut fs = FrameSet::new_with_range(IMAGE_RANGE);
fs.mark_presented(100);
fs.mark_presented(100);
}
#[test]
fn test_basic_use() {
let mut fs = FrameSet::new_with_range(IMAGE_RANGE);
let avail = fs.get_available_image();
assert!(avail.is_some());
let avail = avail.unwrap();
assert!(!fs.available.contains(&avail));
assert!(!fs.presented.contains(&avail));
fs.mark_prepared(avail);
assert_eq!(fs.prepared.unwrap(), avail);
fs.mark_presented(avail);
assert!(fs.prepared.is_none());
assert!(!fs.available.contains(&avail));
assert!(fs.presented.contains(&avail));
fs.mark_done_presenting(avail);
assert!(fs.available.contains(&avail));
assert!(!fs.presented.contains(&avail));
}
}
pub fn to_565(pixel: &[u8; 4]) -> [u8; 2] {
let red = pixel[0] >> 3;
let green = pixel[1] >> 2;
let blue = pixel[2] >> 3;
let b1 = (red << 3) | ((green & 0b11_1000) >> 3);
let b2 = ((green & 0b111) << 5) | blue;
[b2, b1]
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Config {
pub display_id: u64,
pub width: u32,
pub height: u32,
pub refresh_rate_e2: u32,
pub linear_stride_bytes: u32,
pub format: PixelFormat,
pub pixel_size_bytes: u32,
}
impl Config {
pub fn linear_stride_bytes(&self) -> usize {
self.linear_stride_bytes as usize
}
}
// TODO: this should eventually be removed in favor of client adding
// CPU access requirements if needed
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FrameUsage {
Cpu,
Gpu,
}
pub struct Frame {
config: Config,
pub image_id: u64,
pub image_index: u32,
pub signal_event: Event,
signal_event_id: u64,
pub wait_event: Event,
wait_event_id: u64,
image_vmo: Vmo,
pub mapping: Arc<Mapping>,
}
impl Frame {
fn create_image_config(image_type: u32, config: &Config) -> ImageConfig {
ImageConfig {
width: config.width,
height: config.height,
pixel_format: config.format.into(),
type_: image_type,
}
}
async fn import_buffer(
framebuffer: &FrameBuffer,
config: &Config,
image_type: u32,
index: u32,
) -> Result<u64, Error> {
let mut image_config = Self::create_image_config(image_type, config);
let (status, image_id) = framebuffer
.controller
.import_image(&mut image_config, BUFFER_COLLECTION_ID, index)
.await
.context("controller import_image")?;
if status != 0 {
return Err(format_err!(
"import_image error {} ({})",
Status::from_raw(status),
status
));
}
Ok(image_id)
}
async fn create_and_import_event(framebuffer: &FrameBuffer) -> Result<(Event, u64), Error> {
let event = Event::create()?;
let their_event = event.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
let event_id = event.get_koid()?.raw_koid();
framebuffer
.controller
.import_event(Event::from_handle(their_event.into_handle()), event_id)?;
Ok((event, event_id))
}
pub(crate) async fn new(
framebuffer: &FrameBuffer,
image_vmo: Vmo,
config: &Config,
image_type: u32,
index: u32,
) -> Result<Frame, Error> {
// TODO: don't require a CPU mapping when it is not needed.
let mapping = match framebuffer.usage {
FrameUsage::Cpu => Mapping::create_from_vmo(
&image_vmo,
framebuffer.byte_size(),
zx::VmarFlags::PERM_READ
| zx::VmarFlags::PERM_WRITE
| zx::VmarFlags::MAP_RANGE
| zx::VmarFlags::REQUIRE_NON_RESIZABLE,
)
.context("Frame::new() Mapping::create_from_vmo failed")?,
FrameUsage::Gpu => Mapping::allocate(4096).expect("Unused VMO allocation failed").0,
};
// import image
let image_id = Self::import_buffer(framebuffer, config, image_type, index)
.await
.context("Frame::new() import_buffer")?;
let (signal_event, signal_event_id) = Self::create_and_import_event(framebuffer).await?;
let (wait_event, wait_event_id) = Self::create_and_import_event(framebuffer).await?;
Ok(Frame {
config: *config,
image_id: image_id,
image_index: index,
signal_event,
signal_event_id,
wait_event,
wait_event_id,
image_vmo,
mapping: Arc::new(mapping),
})
}
pub fn write_pixel(&mut self, x: u32, y: u32, value: &[u8]) {
if x < self.config.width && y < self.config.height {
let pixel_size = self.config.pixel_size_bytes as usize;
let offset = self.linear_stride_bytes() * y as usize + x as usize * pixel_size;
self.write_pixel_at_offset(offset, value);
}
}
pub fn write_pixel_at_offset(&mut self, offset: usize, value: &[u8]) {
self.mapping.write_at(offset, value);
}
pub fn fill_rectangle(&mut self, x: u32, y: u32, width: u32, height: u32, value: &[u8]) {
let left = x.min(self.config.width);
let right = (left + width).min(self.config.width);
let top = y.min(self.config.height);
let bottom = (top + height).min(self.config.width);
for j in top..bottom {
for i in left..right {
self.write_pixel(i, j, value);
}
}
}
pub fn linear_stride_bytes(&self) -> usize {
self.config.linear_stride_bytes as usize
}
pub fn pixel_size_bytes(&self) -> usize {
self.config.pixel_size_bytes as usize
}
}
pub struct VSyncMessage {
pub display_id: u64,
pub timestamp: u64,
pub images: Vec<u64>,
pub cookie: u64,
}
pub struct FrameBuffer {
#[allow(unused)]
display_controller: zx::Channel,
pub controller: ControllerProxy,
sysmem: fidl_fuchsia_sysmem::AllocatorProxy,
pub local_token: Option<fidl_fuchsia_sysmem::BufferCollectionTokenProxy>,
config: Config,
layer_id: u64,
frames: BTreeMap<u64, Frame>,
#[allow(unused)]
pub usage: FrameUsage,
}
impl FrameBuffer {
async fn create_config_from_event_stream(
stream: &mut fidl_fuchsia_hardware_display::ControllerEventStream,
) -> Result<Config, Error> {
let display_id;
let pixel_format;
let width;
let height;
let refresh_rate_e2;
loop {
let timeout = 2_i64.seconds().after_now();
let event = stream.next().on_timeout(timeout, || None).await;
if let Some(event) = event {
if let Ok(ControllerEvent::OnDisplaysChanged { added, .. }) = event {
let first_added = &added[0];
display_id = first_added.id;
pixel_format = first_added.pixel_format[0];
width = first_added.modes[0].horizontal_resolution;
height = first_added.modes[0].vertical_resolution;
refresh_rate_e2 = first_added.modes[0].refresh_rate_e2;
break;
}
} else {
return Err(format_err!(
"Timed out waiting for display controller to send a OnDisplaysChanged event"
));
}
}
let pixel_size_bytes = pixel_format_bytes(pixel_format) as u32;
Ok(Config {
display_id: display_id,
width: width,
height: height,
refresh_rate_e2: refresh_rate_e2,
linear_stride_bytes: width * pixel_size_bytes,
format: pixel_format.into(),
pixel_size_bytes: pixel_size_bytes,
})
}
async fn configure_layer(
config: &Config,
image_type: u32,
proxy: &ControllerProxy,
) -> Result<u64, Error> {
let (status, layer_id) = proxy.create_layer().await?;
if status != zx::sys::ZX_OK {
return Err(format_err!("Failed to create layer {}", Status::from_raw(status)));
}
let mut image_config = Frame::create_image_config(image_type, config);
proxy.set_layer_primary_config(layer_id, &mut image_config)?;
proxy.set_display_layers(config.display_id, &[layer_id])?;
Ok(layer_id)
}
async fn import_buffer_collection(
&mut self,
buffer_collection_id: u64,
buffer_collection: endpoints::ClientEnd<fidl_fuchsia_sysmem::BufferCollectionTokenMarker>,
) -> Result<(), Error> {
self.controller.import_buffer_collection(buffer_collection_id, buffer_collection).await?;
Ok(())
}
async fn set_buffer_collection_constraints(
&mut self,
buffer_collection_id: u64,
config: &Config,
) -> Result<(), Error> {
let mut image_config = Frame::create_image_config(0, config);
self.controller
.set_buffer_collection_constraints(buffer_collection_id, &mut image_config)
.await?;
Ok(())
}
async fn allocate_buffer_collection(
&mut self,
frame_count: usize,
config: &Config,
) -> Result<fidl_fuchsia_sysmem::BufferCollectionProxy, Error> {
let local_token = self.local_token.take().expect("token in allocate_buffer_collection");
let (display_token, display_token_request) =
create_endpoints::<fidl_fuchsia_sysmem::BufferCollectionTokenMarker>()?;
// Duplicate local token for display.
local_token.duplicate(std::u32::MAX, display_token_request)?;
// Ensure that duplicate message has been processed by sysmem.
local_token.sync().await?;
// Frame buffer import of buffer collection.
self.import_buffer_collection(BUFFER_COLLECTION_ID, display_token).await?;
self.set_buffer_collection_constraints(BUFFER_COLLECTION_ID, config).await?;
// Bind and set local buffer collection constraints.
let (collection_client, collection_request) =
create_endpoints::<fidl_fuchsia_sysmem::BufferCollectionMarker>()?;
self.sysmem.bind_shared_collection(
ClientEnd::new(local_token.into_channel().unwrap().into_zx_channel()),
collection_request,
)?;
let collection_client = collection_client.into_proxy()?;
let pixel_format: fidl_fuchsia_sysmem::PixelFormatType = config.format.into();
let mut buffer_collection_constraints = crate::sysmem::buffer_collection_constraints(
config.width,
config.height,
pixel_format,
frame_count as u32,
self.usage,
);
collection_client
.set_constraints(true, &mut buffer_collection_constraints)
.context("Sending buffer constraints to sysmem")?;
Ok(collection_client)
}
pub async fn new(
usage: FrameUsage,
display_index: Option<usize>,
vsync_sender: Option<futures::channel::mpsc::UnboundedSender<VSyncMessage>>,
) -> Result<FrameBuffer, Error> {
let device_path = if let Some(index) = display_index {
format!("/dev/class/display-controller/{:03}", index)
} else {
// If the caller did not supply a display index, we watch the
// display-controller and use the first display that appears.
let mut first_path = None;
let dir = OpenOptions::new().read(true).open("/dev/class/display-controller")?;
watch_directory(&dir, ZX_TIME_INFINITE, |_event, path| {
first_path = Some(format!("/dev/class/display-controller/{}", path.display()));
Err(zx::Status::STOP)
});
first_path.unwrap()
};
let file = OpenOptions::new().read(true).write(true).open(device_path)?;
let channel = fdio::clone_channel(&file)?;
let mut provider = ProviderSynchronousProxy::new(channel);
let (device_client, device_server) = zx::Channel::create()?;
let (dc_client, dc_server) = endpoints::create_endpoints::<ControllerMarker>()?;
let status = provider.open_controller(device_server, dc_server, zx::Time::INFINITE)?;
if status != zx::sys::ZX_OK {
return Err(format_err!("Failed to open display controller"));
}
let proxy = dc_client.into_proxy()?;
FrameBuffer::new_with_proxy(usage, proxy, device_client, vsync_sender).await
}
async fn new_with_proxy(
usage: FrameUsage,
proxy: ControllerProxy,
device_client: zx::Channel,
vsync_sender: Option<futures::channel::mpsc::UnboundedSender<VSyncMessage>>,
) -> Result<FrameBuffer, Error> {
let mut stream = proxy.take_event_stream();
let config = Self::create_config_from_event_stream(&mut stream).await?;
if let Some(vsync_sender) = vsync_sender {
proxy.enable_vsync(true).context("enable_vsync failed")?;
fasync::Task::local(
stream
.map_ok(move |request| match request {
ControllerEvent::OnVsync { display_id, timestamp, images, cookie } => {
vsync_sender
.unbounded_send(VSyncMessage {
display_id,
timestamp,
images,
cookie,
})
.unwrap_or_else(|e| eprintln!("{:?}", e));
}
_ => (),
})
.try_collect::<()>()
.unwrap_or_else(|e| eprintln!("view listener error: {:?}", e)),
)
.detach();
}
// Connect to sysmem and allocate shared buffer collection.
let sysmem = connect_to_service::<fidl_fuchsia_sysmem::AllocatorMarker>()?;
let (local_token, local_token_request) =
create_endpoints::<fidl_fuchsia_sysmem::BufferCollectionTokenMarker>()?;
let local_token = local_token.into_proxy()?;
sysmem.allocate_shared_collection(local_token_request)?;
let fb = FrameBuffer {
display_controller: device_client,
controller: proxy,
sysmem,
local_token: Some(local_token),
config,
layer_id: 0,
frames: BTreeMap::new(),
usage,
};
Ok(fb)
}
pub async fn allocate_frames(
&mut self,
frame_count: usize,
format: PixelFormat,
) -> Result<(), Error> {
let pixel_size_bytes = pixel_format_bytes(format.into()) as u32;
let mut config = Config {
display_id: self.config.display_id,
width: self.config.width,
height: self.config.height,
refresh_rate_e2: self.config.refresh_rate_e2,
linear_stride_bytes: self.config.width * pixel_size_bytes,
format: format.into(),
pixel_size_bytes: pixel_size_bytes,
};
let collection_proxy = self.allocate_buffer_collection(frame_count, &config).await?;
let (status, buffers) = collection_proxy.wait_for_buffers_allocated().await?;
if status != zx::sys::ZX_OK {
return Err(format_err!(
"Failed to wait for buffers {}({})",
Status::from_raw(status),
status
));
}
if !buffers.settings.has_image_format_constraints {
return Err(format_err!("No image format constraints"));
}
let image_type =
if buffers.settings.image_format_constraints.pixel_format.has_format_modifier {
match buffers.settings.image_format_constraints.pixel_format.format_modifier.value {
fidl_fuchsia_sysmem::FORMAT_MODIFIER_INTEL_I915_X_TILED => 1,
_ => 0,
}
} else {
0
};
config.linear_stride_bytes = crate::sysmem::minimum_row_bytes(
buffers.settings.image_format_constraints,
self.config.width,
)?;
self.config.linear_stride_bytes = config.linear_stride_bytes;
self.layer_id = Self::configure_layer(&config, image_type, &self.controller).await?;
// Clean close of collection in order to allow other clients to
// continue usage.
collection_proxy.close()?;
for index in 0..frame_count {
let vmo_buffer = &buffers.buffers[index];
let vmo = vmo_buffer
.vmo
.as_ref()
.expect("vmo_buffer")
.duplicate_handle(zx::Rights::SAME_RIGHTS)
.context("duplicating buffer vmo")?;
let frame = Frame::new(self, vmo, &config, image_type, index as u32)
.await
.context("new_with_vmo")?;
self.frames.insert(frame.image_id, frame);
}
Ok(())
}
pub fn get_config(&self) -> Config {
self.config
}
pub fn byte_size(&self) -> usize {
self.config.height as usize * self.config.linear_stride_bytes()
}
pub fn get_frame_count(&self) -> usize {
self.frames.len()
}
pub fn get_frame(&self, image_id: u64) -> &Frame {
self.frames.get(&image_id).expect("to find image")
}
pub fn get_frame_mut(&mut self, image_id: u64) -> &mut Frame {
self.frames.get_mut(&image_id).expect("to find image")
}
pub fn get_image_ids(&self) -> BTreeSet<u64> {
self.frames.keys().map(|image_id| *image_id).collect::<BTreeSet<_>>()
}
pub fn get_first_frame_id(&self) -> u64 {
if let Some(image_id) = self.get_image_ids().iter().next() {
*image_id
} else {
// this should be impossible, as a frame buffer cannot be
// created with zero frames
panic!("no allocated frames")
}
}
pub fn flush_frame(&mut self, image_id: u64) -> Result<(), Error> {
let frame = self.get_frame(image_id);
frame.image_vmo.op_range(VmoOp::CACHE_CLEAN_INVALIDATE, 0, frame.image_vmo.get_size()?)?;
Ok(())
}
pub fn present_frame(
&mut self,
image_id: u64,
sender: Option<futures::channel::mpsc::UnboundedSender<u64>>,
signal_wait_event: bool,
) -> Result<(), Error> {
let frame = self.get_frame(image_id);
self.controller.set_display_layers(self.config.display_id, &[self.layer_id])?;
self.controller
.set_layer_image(
self.layer_id,
frame.image_id,
frame.wait_event_id,
frame.signal_event_id,
)
.context("Frame::present() set_layer_image")?;
self.controller.apply_config().context("Frame::present() apply_config")?;
if signal_wait_event {
frame.wait_event.as_handle_ref().signal(Signals::NONE, Signals::EVENT_SIGNALED)?;
}
if let Some(signal_sender) = sender.as_ref() {
let signal_sender = signal_sender.clone();
let image_id = frame.image_id;
let local_event = frame.signal_event.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
local_event.as_handle_ref().signal(Signals::EVENT_SIGNALED, Signals::NONE)?;
fasync::Task::local(async move {
let signals = OnSignals::new(&local_event, Signals::EVENT_SIGNALED);
signals.await.expect("to wait");
signal_sender.unbounded_send(image_id).expect("send to work");
})
.detach();
}
Ok(())
}
pub fn present_first_frame(
&mut self,
sender: Option<futures::channel::mpsc::UnboundedSender<u64>>,
signal_wait_event: bool,
) -> Result<(), Error> {
self.present_frame(self.get_first_frame_id(), sender, signal_wait_event)
}
pub fn acknowledge_vsync(&mut self, cookie: u64) -> Result<(), Error> {
self.controller.acknowledge_vsync(cookie)?;
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use fidl_fuchsia_hardware_display::ControllerRequest;
use std::cell::Cell;
use std::rc::Rc;
#[fasync::run_singlethreaded(test)]
async fn test_no_vsync() -> Result<(), anyhow::Error> {
let (client, server) = fidl::endpoints::create_endpoints::<ControllerMarker>()?;
let mut stream = server.into_stream()?;
let vsync_enabled = Rc::new(Cell::new(false));
let vsync_enabled_clone = Rc::clone(&vsync_enabled);
fasync::Task::local(async move {
while let Some(req) = stream.try_next().await.expect("Failed to get request!") {
match req {
ControllerRequest::EnableVsync { enable: true, control_handle: _ } => {
vsync_enabled_clone.set(true)
}
ControllerRequest::EnableVsync { enable: false, control_handle: _ } => {
vsync_enabled_clone.set(false)
}
_ => panic!("Unexpected request"),
}
}
})
.detach();
let proxy = client.into_proxy()?;
let (dummy, _) = zx::Channel::create()?;
let _fb = FrameBuffer::new_with_proxy(FrameUsage::Cpu, proxy, dummy, None);
if vsync_enabled.get() {
panic!("Vsync should be disabled");
}
Ok(())
}
}