use {
fidl::endpoints::{create_endpoints, create_proxy, ClientEnd, Proxy},
fidl_fuchsia_hardware_display_types as fdisplay_types,
fidl_fuchsia_sysmem::{
self as fsysmem, AllocatorMarker, BufferCollectionInfo2, BufferCollectionMarker,
BufferCollectionProxy, BufferCollectionTokenMarker, BufferCollectionTokenProxy,
ColorSpaceType,
},
fuchsia_component::client::connect_to_protocol,
fuchsia_image_format::{
BUFFER_COLLECTION_CONSTRAINTS_DEFAULT, BUFFER_MEMORY_CONSTRAINTS_DEFAULT,
BUFFER_USAGE_DEFAULT, IMAGE_FORMAT_CONSTRAINTS_DEFAULT,
},
fuchsia_zircon::{self as zx, AsHandleRef, HandleBased},
};
use crate::{
controller::Coordinator,
error::{Error, Result},
pixel_format::PixelFormat,
types::{BufferCollectionId, ImageId},
};
#[derive(Clone)]
pub struct ImageParameters {
pub width: u32,
pub height: u32,
pub pixel_format: PixelFormat,
pub color_space: ColorSpaceType,
pub name: Option<String>,
}
pub struct Image {
pub id: ImageId,
pub collection_id: BufferCollectionId,
pub vmo: zx::Vmo,
pub parameters: ImageParameters,
pub format_constraints: fsysmem::ImageFormatConstraints,
pub buffer_settings: fsysmem::BufferMemorySettings,
proxy: BufferCollectionProxy,
coordinator: Coordinator,
}
impl Image {
pub async fn create(
coordinator: Coordinator,
image_id: ImageId,
params: &ImageParameters,
) -> Result<Image> {
let mut collection = allocate_image_buffer(coordinator.clone(), params).await?;
coordinator.import_image(collection.id, image_id, params.into()).await?;
let vmo = collection.info.buffers[0]
.vmo
.as_ref()
.ok_or(Error::BuffersNotAllocated)?
.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
collection.release();
Ok(Image {
id: image_id,
collection_id: collection.id,
vmo,
parameters: params.clone(),
format_constraints: collection.info.settings.image_format_constraints,
buffer_settings: collection.info.settings.buffer_settings,
proxy: collection.proxy.clone(),
coordinator,
})
}
}
impl Drop for Image {
fn drop(&mut self) {
let _ = self.proxy.close();
let _ = self.coordinator.release_buffer_collection(self.collection_id);
}
}
impl From<&ImageParameters> for fdisplay_types::ImageMetadata {
fn from(src: &ImageParameters) -> Self {
Self {
width: src.width,
height: src.height,
tiling_type: fdisplay_types::IMAGE_TILING_TYPE_LINEAR,
}
}
}
impl From<ImageParameters> for fdisplay_types::ImageMetadata {
fn from(src: ImageParameters) -> Self {
fdisplay_types::ImageMetadata::from(&src)
}
}
struct BufferCollection {
id: BufferCollectionId,
info: BufferCollectionInfo2,
proxy: BufferCollectionProxy,
coordinator: Coordinator,
released: bool,
}
impl BufferCollection {
fn release(&mut self) {
self.released = true;
}
}
impl Drop for BufferCollection {
fn drop(&mut self) {
if !self.released {
let _ = self.coordinator.release_buffer_collection(self.id);
let _ = self.proxy.close();
}
}
}
async fn allocate_image_buffer(
coordinator: Coordinator,
params: &ImageParameters,
) -> Result<BufferCollection> {
let allocator =
connect_to_protocol::<AllocatorMarker>().map_err(|_| Error::SysmemConnection)?;
{
let name = fuchsia_runtime::process_self().get_name()?;
let koid = fuchsia_runtime::process_self().get_koid()?;
allocator.set_debug_client_info(name.to_str()?, koid.raw_koid())?;
}
let collection_token = {
let (proxy, remote) = create_proxy::<BufferCollectionTokenMarker>()?;
allocator.allocate_shared_collection(remote)?;
proxy
};
if let Some(ref name) = params.name {
collection_token.set_name(100, name.as_str())?;
}
let display_duplicate = {
let (local, remote) = create_endpoints::<BufferCollectionTokenMarker>();
collection_token.duplicate(std::u32::MAX, remote)?;
collection_token.sync().await?;
local
};
let id = coordinator.import_buffer_collection(display_duplicate).await?;
match allocate_image_buffer_helper(params, allocator, collection_token).await {
Ok((info, proxy)) => Ok(BufferCollection { id, info, proxy, coordinator, released: false }),
Err(error) => {
let _ = coordinator.release_buffer_collection(id);
Err(error)
}
}
}
async fn allocate_image_buffer_helper(
params: &ImageParameters,
allocator: fsysmem::AllocatorProxy,
token: BufferCollectionTokenProxy,
) -> Result<(BufferCollectionInfo2, BufferCollectionProxy)> {
let collection = {
let (local, remote) = create_endpoints::<BufferCollectionMarker>();
let token_channel =
token.into_channel().map_err(|_| Error::SysmemConnection)?.into_zx_channel();
allocator.bind_shared_collection(ClientEnd::new(token_channel), remote)?;
local.into_proxy()?
};
collection.set_constraints(true, &buffer_collection_constraints(params))?;
let collection_info = {
let (status, info) = collection.wait_for_buffers_allocated().await?;
let _ = zx::Status::ok(status)?;
info
};
if collection_info.buffer_count == 0 {
collection.close()?;
return Err(Error::BuffersNotAllocated);
}
Ok((collection_info, collection))
}
fn buffer_collection_constraints(params: &ImageParameters) -> fsysmem::BufferCollectionConstraints {
let usage = fsysmem::BufferUsage {
cpu: fsysmem::CPU_USAGE_READ_OFTEN | fsysmem::CPU_USAGE_WRITE_OFTEN,
..BUFFER_USAGE_DEFAULT
};
let buffer_memory_constraints = fsysmem::BufferMemoryConstraints {
ram_domain_supported: true,
cpu_domain_supported: true,
..BUFFER_MEMORY_CONSTRAINTS_DEFAULT
};
let pixel_format = fsysmem::PixelFormat {
type_: params.pixel_format.into(),
has_format_modifier: true,
format_modifier: fsysmem::FormatModifier { value: fsysmem::FORMAT_MODIFIER_LINEAR },
};
let mut image_constraints = fsysmem::ImageFormatConstraints {
required_max_coded_width: params.width,
required_max_coded_height: params.height,
color_spaces_count: 1,
pixel_format,
..IMAGE_FORMAT_CONSTRAINTS_DEFAULT
};
image_constraints.color_space[0].type_ = params.color_space;
let mut constraints = fsysmem::BufferCollectionConstraints {
min_buffer_count: 1,
usage,
has_buffer_memory_constraints: true,
buffer_memory_constraints,
image_format_constraints_count: 1,
..BUFFER_COLLECTION_CONSTRAINTS_DEFAULT
};
constraints.image_format_constraints[0] = image_constraints;
constraints
}