blob: eaafd38df5dd58512ae1bb99cb9bfdb0d4bd9017 [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.
#![allow(dead_code)]
use failure::{format_err, Error, ResultExt};
use fdio::fdio_sys::{fdio_ioctl, IOCTL_FAMILY_DISPLAY_CONTROLLER, IOCTL_KIND_GET_HANDLE};
use fdio::make_ioctl;
use fidl_fuchsia_hardware_display::{ControllerEvent, ControllerProxy, ImageConfig, ImagePlane};
use fuchsia_async as fasync;
use fuchsia_zircon::{
self as zx,
sys::{
zx_cache_flush, zx_cache_policy_t::ZX_CACHE_POLICY_WRITE_COMBINING, zx_handle_t,
ZX_CACHE_FLUSH_DATA,
},
Handle, Vmar, VmarFlags, Vmo,
};
use futures::{future, StreamExt, TryFutureExt, TryStreamExt};
use shared_buffer::SharedBuffer;
use std::cell::RefCell;
use std::fs::{File, OpenOptions};
use std::mem;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::rc::Rc;
use std::{thread, time};
#[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;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PixelFormat {
Argb8888,
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_ARGB_8888 => PixelFormat::Argb8888,
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::Argb8888 => ZX_PIXEL_FORMAT_ARGB_8888,
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,
}
}
}
fn pixel_format_bytes(pixel_format: u32) -> usize {
((pixel_format >> 16) & 7) as usize
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Config {
pub display_id: u64,
pub width: u32,
pub height: u32,
pub linear_stride_pixels: u32,
pub format: PixelFormat,
pub pixel_size_bytes: u32,
}
impl Config {
pub fn linear_stride_bytes(&self) -> usize {
self.linear_stride_pixels as usize * self.pixel_size_bytes as usize
}
}
pub struct Frame {
config: Config,
image_id: u64,
pixel_buffer_addr: usize,
pixel_buffer: SharedBuffer,
}
impl Frame {
fn allocate_image_vmo(
framebuffer: &FrameBuffer, executor: &mut fasync::Executor,
) -> Result<Vmo, Error> {
let vmo: Rc<RefCell<Option<Vmo>>> = Rc::new(RefCell::new(None));
let vmo_response = framebuffer
.controller
.allocate_vmo(framebuffer.byte_size() as u64)
.map_ok(|(status, allocated_vmo)| {
if status == zx::sys::ZX_OK {
vmo.replace(allocated_vmo);
}
});
executor
.run_singlethreaded(vmo_response)
.context("allocate_image_vmo - run_singlethreaded")?;
let vmo = vmo.replace(None);
if let Some(vmo) = vmo {
Ok(vmo)
} else {
Err(format_err!("Could not allocate image vmo"))
}
}
fn import_image_vmo(
framebuffer: &FrameBuffer, executor: &mut fasync::Executor, image_vmo: Vmo,
) -> Result<u64, Error> {
let pixel_format: u32 = framebuffer.config.format.into();
let plane = ImagePlane {
byte_offset: 0,
bytes_per_row: framebuffer.config.linear_stride_bytes() as u32,
};
let mut image_config = ImageConfig {
width: framebuffer.config.width,
height: framebuffer.config.height,
pixel_format: pixel_format as u32,
type_: 0,
planes: [
plane,
ImagePlane {
byte_offset: 0,
bytes_per_row: 0,
},
ImagePlane {
byte_offset: 0,
bytes_per_row: 0,
},
ImagePlane {
byte_offset: 0,
bytes_per_row: 0,
},
],
};
let image_id: Rc<RefCell<Option<u64>>> = Rc::new(RefCell::new(None));
let import_response = framebuffer
.controller
.import_vmo_image(&mut image_config, image_vmo, 0)
.map_ok(|(status, id)| {
if status == zx::sys::ZX_OK {
image_id.replace(Some(id));
}
});
executor
.run_singlethreaded(import_response)
.context("import_image_vmo - run_singlethreaded")?;
let image_id = image_id.replace(None);
if let Some(image_id) = image_id {
Ok(image_id)
} else {
Err(format_err!("Could not import image vmo"))
}
}
pub fn new(framebuffer: &FrameBuffer, executor: &mut fasync::Executor) -> Result<Frame, Error> {
let image_vmo = Self::allocate_image_vmo(framebuffer, executor)
.context("Frame::new() allocate_image_vmo")?;
image_vmo
.set_cache_policy(ZX_CACHE_POLICY_WRITE_COMBINING)
.unwrap_or_else(|_err| println!("set_cache_policy failed"));
// map image VMO
let pixel_buffer_addr = Vmar::root_self().map(
0,
&image_vmo,
0,
framebuffer.byte_size(),
VmarFlags::PERM_READ | VmarFlags::PERM_WRITE,
)?;
// import image VMO
let image_id = Self::import_image_vmo(framebuffer, executor, image_vmo)
.context("Frame::new() import_image_vmo")?;
// construct frame
let frame_buffer_pixel_ptr = pixel_buffer_addr as *mut u8;
Ok(Frame {
config: framebuffer.get_config(),
image_id: image_id,
pixel_buffer_addr,
pixel_buffer: unsafe {
SharedBuffer::new(frame_buffer_pixel_ptr, framebuffer.byte_size())
},
})
}
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.pixel_buffer.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 present(&self, framebuffer: &FrameBuffer) -> Result<(), Error> {
// TODO: This explicitly violates the safety constraints of SharedBuffer.
let frame_buffer_pixel_ptr = self.pixel_buffer_addr as *mut u8;
let result = unsafe {
zx_cache_flush(
frame_buffer_pixel_ptr,
self.byte_size(),
ZX_CACHE_FLUSH_DATA,
)
};
if result != 0 {
return Err(format_err!("zx_cache_flush failed: {}", result));
}
framebuffer
.controller
.set_layer_image(framebuffer.layer_id, self.image_id, 0, 0)
.context("Frame::present() set_layer_image")?;
framebuffer
.controller
.apply_config()
.context("Frame::present() apply_config")?;
Ok(())
}
fn byte_size(&self) -> usize {
self.linear_stride_bytes() * self.config.height as usize
}
pub fn linear_stride_bytes(&self) -> usize {
self.config.linear_stride_pixels as usize * self.config.pixel_size_bytes as usize
}
pub fn pixel_size_bytes(&self) -> usize {
self.config.pixel_size_bytes as usize
}
}
impl Drop for Frame {
fn drop(&mut self) {
unsafe {
let (ptr, len) = self.pixel_buffer.as_ptr_len();
Vmar::root_self().unmap(ptr as usize, len).unwrap();
}
}
}
pub struct FrameBuffer {
display_controller: File,
controller: ControllerProxy,
config: Config,
layer_id: u64,
}
impl FrameBuffer {
fn get_display_handle(file: &File) -> Result<Handle, Error> {
let fd = file.as_raw_fd() as i32;
let ioctl_display_controller_get_handle =
make_ioctl(IOCTL_KIND_GET_HANDLE, IOCTL_FAMILY_DISPLAY_CONTROLLER, 1);
let mut display_handle: zx_handle_t = 0;
let display_handle_ptr: *mut std::os::raw::c_void =
&mut display_handle as *mut _ as *mut std::os::raw::c_void;
let result_size = unsafe {
fdio_ioctl(
fd,
ioctl_display_controller_get_handle,
ptr::null(),
0,
display_handle_ptr,
mem::size_of::<zx_handle_t>(),
)
};
if result_size != mem::size_of::<zx_handle_t>() as isize {
return Err(format_err!(
"ioctl_display_controller_get_handle failed: {}",
result_size
));
}
Ok(unsafe { Handle::from_raw(display_handle) })
}
fn create_config_from_event_stream(
proxy: &ControllerProxy, executor: &mut fasync::Executor,
) -> Result<Config, Error> {
let display_info: Rc<RefCell<Option<(u64, u32, u32, u32)>>> = Rc::new(RefCell::new(None));
let stream = proxy.take_event_stream();
let mut event_listener = stream.filter(|event| {
if let Ok(ControllerEvent::DisplaysChanged { added, .. }) = event {
if added.len() > 0 {
let first_added = &added[0];
if first_added.pixel_format.len() > 0 && first_added.modes.len() > 0 {
let display_id = first_added.id;
let pixel_format = first_added.pixel_format[0];
let width = first_added.modes[0].horizontal_resolution;
let height = first_added.modes[0].vertical_resolution;
display_info.replace(Some((display_id, pixel_format, width, height)));
}
}
}
future::ready(true)
});
// run for two seconds waiting for a ControllerEvent::DisplaysChanged event,
// needed since other events may come in first
let delay = time::Duration::from_millis(100);
let timeout = time::Duration::from_millis(2000);
let now = time::Instant::now();
while display_info.borrow().is_none() && now.elapsed() < timeout {
executor.run_singlethreaded(event_listener.try_next())?;
thread::sleep(delay);
}
let display_info = display_info.replace(None);
if let Some((display_id, pixel_format, width, height)) = display_info {
let stride: Rc<RefCell<u32>> = Rc::new(RefCell::new(0));
let stride_response = proxy
.compute_linear_image_stride(width, pixel_format)
.map_ok(|px_stride| {
stride.replace(px_stride);
});
executor.run_singlethreaded(stride_response).context("")?;
let stride = stride.replace(0);
if 0 == stride {
Err(format_err!("Could not calculate stride"))
} else {
Ok(Config {
display_id: display_id,
width: width,
height: height,
linear_stride_pixels: stride,
format: pixel_format.into(),
pixel_size_bytes: pixel_format_bytes(pixel_format) as u32,
})
}
} else {
Err(format_err!("Could not find display"))
}
}
fn configure_layer(
config: Config, proxy: &ControllerProxy, executor: &mut fasync::Executor,
) -> Result<u64, Error> {
let layer_id: Rc<RefCell<Option<u64>>> = Rc::new(RefCell::new(None));
let layer_id_response = proxy.create_layer().map_ok(|(status, id)| {
if status == zx::sys::ZX_OK {
layer_id.replace(Some(id));
}
});
executor.run_singlethreaded(layer_id_response)?;
let layer_id = layer_id.replace(None);
if let Some(id) = layer_id {
let pixel_format: u32 = config.format.into();
let plane = ImagePlane {
byte_offset: 0,
bytes_per_row: config.linear_stride_bytes() as u32,
};
let mut image_config = ImageConfig {
width: config.width,
height: config.height,
pixel_format: pixel_format as u32,
type_: 0,
planes: [
plane,
ImagePlane {
byte_offset: 0,
bytes_per_row: 0,
},
ImagePlane {
byte_offset: 0,
bytes_per_row: 0,
},
ImagePlane {
byte_offset: 0,
bytes_per_row: 0,
},
],
};
proxy.set_layer_primary_config(id, &mut image_config)?;
let mut layers = std::iter::once(id);
proxy.set_display_layers(config.display_id, &mut layers)?;
Ok(id)
} else {
Err(format_err!("Failed to create layer"))
}
}
pub fn new(
display_index: Option<usize>, executor: &mut fasync::Executor,
) -> Result<FrameBuffer, Error> {
let device_path = format!(
"/dev/class/display-controller/{:03}",
display_index.unwrap_or(0)
);
let file = OpenOptions::new()
.read(true)
.write(true)
.open(device_path)?;
let zx_handle = Self::get_display_handle(&file)?;
let channel = fasync::Channel::from_channel(zx_handle.into())?;
let proxy = ControllerProxy::new(channel);
let config = Self::create_config_from_event_stream(&proxy, executor)?;
let layer = Self::configure_layer(config, &proxy, executor)?;
Ok(FrameBuffer {
display_controller: file,
controller: proxy,
config: config,
layer_id: layer,
})
}
pub fn new_frame(&self, executor: &mut fasync::Executor) -> Result<Frame, Error> {
Frame::new(&self, executor)
}
pub fn get_config(&self) -> Config {
self.config
}
pub fn byte_size(&self) -> usize {
self.config.height as usize * self.config.linear_stride_bytes()
}
}
impl Drop for FrameBuffer {
fn drop(&mut self) {}
}
#[cfg(test)]
mod tests {
use fuchsia_async as fasync;
use FrameBuffer;
#[test]
fn test_framebuffer() {
let mut executor = fasync::Executor::new().unwrap();
let fb = FrameBuffer::new(None, &mut executor).unwrap();
let _frame = fb.new_frame(&mut executor).unwrap();
}
}