carnelian/render/generic/forma/
image.rsuse std::io::Read;
use std::sync::Arc;
use std::{mem, slice};
use anyhow::Error;
use fidl_fuchsia_sysmem2::{BufferCollectionSynchronousProxy, CoherencyDomain};
use fuchsia_trace::duration;
use mapped_vmo::Mapping;
use zx::prelude::*;
use zx::{self as zx, sys};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FormaImage(pub(crate) usize);
#[derive(Debug)]
pub(crate) struct VmoImage {
#[allow(unused)]
vmo: zx::Vmo,
#[allow(unused)]
width: u32,
#[allow(unused)]
height: u32,
#[allow(unused)]
len_bytes: u64,
mapping: Arc<Mapping>,
stride: usize,
pub(crate) buffer_layer_cache: Option<(usize, forma::buffer::BufferLayerCache)>,
coherency_domain: CoherencyDomain,
layout: forma::buffer::layout::LinearLayout,
}
impl VmoImage {
pub fn new(width: u32, height: u32) -> Self {
let len_bytes = (width * height * 4) as usize * mem::size_of::<u8>();
let (mapping, vmo) = mapped_vmo::Mapping::allocate(len_bytes as usize)
.expect("failed to allocated mapped VMO");
Self {
vmo,
width,
height,
len_bytes: len_bytes as u64,
mapping: Arc::new(mapping),
stride: (width * 4) as usize,
buffer_layer_cache: None,
coherency_domain: CoherencyDomain::Cpu,
layout: forma::buffer::layout::LinearLayout::new(
width as usize,
(width * 4) as usize,
height as usize,
),
}
}
pub fn from_png<R: Read>(reader: &mut png::Reader<R>) -> Result<Self, Error> {
let info = reader.info();
let color_type = info.color_type;
let (width, height) = info.size();
let stride = (width * 4) as usize * mem::size_of::<u8>();
let len_bytes = stride * height as usize;
let (mut mapping, vmo) = mapped_vmo::Mapping::allocate(len_bytes as usize)
.expect("failed to allocated mapped VMO");
let (data, len) = mapping.as_ptr_len();
let slice = unsafe { slice::from_raw_parts_mut(data, len) };
for dst_row in slice.chunks_mut(stride) {
let src_row = reader.next_row()?.unwrap();
match color_type {
png::ColorType::RGB | png::ColorType::Indexed => {
for (src, dst) in src_row.chunks(3).zip(dst_row.chunks_mut(4)) {
dst.copy_from_slice(&[src[2], src[1], src[0], 0xff]);
}
}
png::ColorType::RGBA => {
for (src, dst) in src_row.chunks(4).zip(dst_row.chunks_mut(4)) {
dst.copy_from_slice(&[src[2], src[1], src[0], src[3]]);
}
}
_ => panic!("unsupported color type {:#?}", color_type),
}
}
Ok(Self {
vmo,
width,
height,
len_bytes: len_bytes as u64,
mapping: Arc::new(mapping),
stride: (width * 4) as usize,
buffer_layer_cache: None,
coherency_domain: CoherencyDomain::Cpu,
layout: forma::buffer::layout::LinearLayout::new(
width as usize,
(width * 4) as usize,
height as usize,
),
})
}
pub fn from_buffer_collection(
buffer_collection: &mut BufferCollectionSynchronousProxy,
width: u32,
height: u32,
index: u32,
) -> Self {
let wait_result = buffer_collection
.wait_for_all_buffers_allocated(zx::MonotonicInstant::INFINITE)
.expect("failed to allocate buffer collection");
assert!(
wait_result.is_ok(),
"wait_for_all_buffers_allocated failed: {:?}",
wait_result.unwrap_err()
);
let buffers = wait_result.unwrap().buffer_collection_info.unwrap();
let vmo_buffer = &buffers.buffers.as_ref().unwrap()[index as usize];
let vmo = vmo_buffer
.vmo
.as_ref()
.expect("failed to get VMO buffer")
.duplicate_handle(zx::Rights::SAME_RIGHTS)
.expect("failed to duplicate VMO handle");
let settings = buffers.settings.as_ref().unwrap();
let buffer_settings = settings.buffer_settings.as_ref().unwrap();
let len_bytes = buffer_settings.size_bytes.as_ref().unwrap();
let mapping = Arc::new(
mapped_vmo::Mapping::create_from_vmo(
&vmo,
*len_bytes as usize,
zx::VmarFlags::PERM_READ
| zx::VmarFlags::PERM_WRITE
| zx::VmarFlags::MAP_RANGE
| zx::VmarFlags::REQUIRE_NON_RESIZABLE,
)
.expect("failed to crate mapping from VMO"),
);
assert!(settings.image_format_constraints.is_some());
let image_format_constraints = settings.image_format_constraints.as_ref().unwrap();
let bytes_per_row = image_format_constraints.min_bytes_per_row.as_ref().unwrap();
let divisor = image_format_constraints.bytes_per_row_divisor.as_ref().unwrap();
let bytes_per_row = ((bytes_per_row + divisor - 1) / divisor) * divisor;
let stride = bytes_per_row as usize / mem::size_of::<u8>();
Self {
vmo,
width,
height,
len_bytes: *len_bytes as u64,
mapping,
stride,
buffer_layer_cache: None,
coherency_domain: *buffer_settings.coherency_domain.as_ref().unwrap(),
layout: forma::buffer::layout::LinearLayout::new(
width as usize,
stride,
height as usize,
),
}
}
pub fn bytes_per_row(&self) -> usize {
self.stride * mem::size_of::<u8>()
}
pub fn coherency_domain(&self) -> CoherencyDomain {
self.coherency_domain
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
let (data, len) = Arc::get_mut(&mut self.mapping).unwrap().as_ptr_len();
unsafe { slice::from_raw_parts_mut(data, len) }
}
pub fn as_buffer(
&mut self,
) -> forma::buffer::Buffer<'_, '_, forma::buffer::layout::LinearLayout> {
#[derive(Debug)]
struct SliceFlusher;
impl forma::buffer::layout::Flusher for SliceFlusher {
fn flush(&self, slice: &mut [u8]) {
unsafe {
sys::zx_cache_flush(
slice.as_ptr() as *const u8,
slice.len(),
sys::ZX_CACHE_FLUSH_DATA,
);
}
}
}
let (data, len) = Arc::get_mut(&mut self.mapping).unwrap().as_ptr_len();
let raw_buffer = unsafe { slice::from_raw_parts_mut(data as *mut u8, len) };
let mut buffer = forma::buffer::BufferBuilder::new(raw_buffer, &mut self.layout);
if let Some(buffer_layer_cache) =
self.buffer_layer_cache.as_ref().map(|(_, cache)| cache).cloned()
{
buffer = buffer.layer_cache(buffer_layer_cache);
}
if self.coherency_domain == CoherencyDomain::Ram {
buffer = buffer.flusher(Box::new(SliceFlusher));
}
buffer.build()
}
pub fn clear(&mut self, clear_color: [u8; 4]) {
duration!(c"gfx", c"VmoImage::clear");
if let Some((_, buffer_layer_cache)) = self.buffer_layer_cache.as_ref() {
buffer_layer_cache.clear();
}
let coherency_domain = self.coherency_domain;
let (data, len) = Arc::get_mut(&mut self.mapping).unwrap().as_ptr_len();
let buffer = unsafe { slice::from_raw_parts_mut(data as *mut u8, len) };
forma::clear_buffer(buffer, clear_color);
if coherency_domain == CoherencyDomain::Ram {
unsafe {
sys::zx_cache_flush(
buffer.as_ptr() as *const u8,
buffer.len(),
sys::ZX_CACHE_FLUSH_DATA,
);
}
}
}
}