1use fsysmem2::{
6    AllocatorAllocateSharedCollectionRequest, AllocatorBindSharedCollectionRequest,
7    AllocatorSetDebugClientInfoRequest, BufferCollectionSetConstraintsRequest,
8    BufferCollectionTokenDuplicateRequest, NodeSetNameRequest,
9};
10
11use fidl::endpoints::{create_endpoints, create_proxy, Proxy};
12use fidl_fuchsia_hardware_display_types as fdisplay_types;
13use fidl_fuchsia_images2::{self as fimages2};
14use fidl_fuchsia_sysmem2::{
15    self as fsysmem2, AllocatorMarker, BufferCollectionInfo, BufferCollectionMarker,
16    BufferCollectionProxy, BufferCollectionTokenMarker, BufferCollectionTokenProxy,
17};
18use fuchsia_component::client::connect_to_protocol;
19use zx::{self as zx, AsHandleRef, HandleBased};
20
21use crate::controller::Coordinator;
22use crate::error::{Error, Result};
23use crate::pixel_format::PixelFormat;
24use crate::types::{BufferCollectionId, ImageId};
25
26#[derive(Clone)]
28pub struct ImageParameters {
29    pub width: u32,
31
32    pub height: u32,
34
35    pub pixel_format: PixelFormat,
38
39    pub color_space: fimages2::ColorSpace,
42
43    pub name: Option<String>,
45}
46
47pub struct Image {
49    pub id: ImageId,
51
52    pub collection_id: BufferCollectionId,
54
55    pub vmo: zx::Vmo,
57
58    pub parameters: ImageParameters,
60
61    pub format_constraints: fsysmem2::ImageFormatConstraints,
64
65    pub buffer_settings: fsysmem2::BufferMemorySettings,
67
68    proxy: BufferCollectionProxy,
70
71    coordinator: Coordinator,
73}
74
75impl Image {
76    pub async fn create(
80        coordinator: Coordinator,
81        image_id: ImageId,
82        params: &ImageParameters,
83    ) -> Result<Image> {
84        let mut collection = allocate_image_buffer(coordinator.clone(), params).await?;
85        coordinator.import_image(collection.id, image_id, params.into()).await?;
86        let vmo = collection.info.buffers.as_ref().unwrap()[0]
87            .vmo
88            .as_ref()
89            .ok_or(Error::BuffersNotAllocated)?
90            .duplicate_handle(zx::Rights::SAME_RIGHTS)?;
91
92        collection.release();
93
94        Ok(Image {
95            id: image_id,
96            collection_id: collection.id,
97            vmo,
98            parameters: params.clone(),
99            format_constraints: collection
100                .info
101                .settings
102                .as_ref()
103                .unwrap()
104                .image_format_constraints
105                .as_ref()
106                .unwrap()
107                .clone(),
108            buffer_settings: collection
109                .info
110                .settings
111                .as_mut()
112                .unwrap()
113                .buffer_settings
114                .take()
115                .unwrap(),
116            proxy: collection.proxy.clone(),
117            coordinator,
118        })
119    }
120}
121
122impl Drop for Image {
123    fn drop(&mut self) {
124        let _ = self.proxy.release();
125        let _ = self.coordinator.release_buffer_collection(self.collection_id);
126    }
127}
128
129impl From<&ImageParameters> for fdisplay_types::ImageMetadata {
130    fn from(src: &ImageParameters) -> Self {
131        Self {
132            dimensions: fidl_fuchsia_math::SizeU { width: src.width, height: src.height },
133            tiling_type: fdisplay_types::IMAGE_TILING_TYPE_LINEAR,
134        }
135    }
136}
137
138impl From<ImageParameters> for fdisplay_types::ImageMetadata {
139    fn from(src: ImageParameters) -> Self {
140        fdisplay_types::ImageMetadata::from(&src)
141    }
142}
143
144struct BufferCollection {
148    id: BufferCollectionId,
149    info: BufferCollectionInfo,
150    proxy: BufferCollectionProxy,
151    coordinator: Coordinator,
152    released: bool,
153}
154
155impl BufferCollection {
156    fn release(&mut self) {
157        self.released = true;
158    }
159}
160
161impl Drop for BufferCollection {
162    fn drop(&mut self) {
163        if !self.released {
164            let _ = self.coordinator.release_buffer_collection(self.id);
165            let _ = self.proxy.release();
166        }
167    }
168}
169
170async fn allocate_image_buffer(
173    coordinator: Coordinator,
174    params: &ImageParameters,
175) -> Result<BufferCollection> {
176    let allocator =
177        connect_to_protocol::<AllocatorMarker>().map_err(|_| Error::SysmemConnection)?;
178    {
179        let name = fuchsia_runtime::process_self().get_name()?;
180        let koid = fuchsia_runtime::process_self().get_koid()?;
181        allocator.set_debug_client_info(&AllocatorSetDebugClientInfoRequest {
182            name: Some(name.to_string()),
183            id: Some(koid.raw_koid()),
184            ..Default::default()
185        })?;
186    }
187    let collection_token = {
188        let (proxy, remote) = create_proxy::<BufferCollectionTokenMarker>();
189        allocator.allocate_shared_collection(AllocatorAllocateSharedCollectionRequest {
190            token_request: Some(remote),
191            ..Default::default()
192        })?;
193        proxy
194    };
195    if let Some(ref name) = params.name {
199        collection_token.set_name(&NodeSetNameRequest {
200            priority: Some(100),
201            name: Some(name.clone()),
202            ..Default::default()
203        })?;
204    }
205
206    let display_duplicate = {
208        let (local, remote) = create_endpoints::<BufferCollectionTokenMarker>();
209        collection_token.duplicate(BufferCollectionTokenDuplicateRequest {
210            rights_attenuation_mask: Some(fidl::Rights::SAME_RIGHTS),
211            token_request: Some(remote),
212            ..Default::default()
213        })?;
214        collection_token.sync().await?;
215        local
216    };
217
218    let id = coordinator.import_buffer_collection(display_duplicate).await?;
220
221    match allocate_image_buffer_helper(params, allocator, collection_token).await {
223        Ok((info, proxy)) => Ok(BufferCollection { id, info, proxy, coordinator, released: false }),
224        Err(error) => {
225            let _ = coordinator.release_buffer_collection(id);
226            Err(error)
227        }
228    }
229}
230
231async fn allocate_image_buffer_helper(
232    params: &ImageParameters,
233    allocator: fsysmem2::AllocatorProxy,
234    token: BufferCollectionTokenProxy,
235) -> Result<(BufferCollectionInfo, BufferCollectionProxy)> {
236    let collection = {
238        let (local, remote) = create_endpoints::<BufferCollectionMarker>();
239        let token_client = token.into_client_end().map_err(|_| Error::SysmemConnection)?;
240        allocator.bind_shared_collection(AllocatorBindSharedCollectionRequest {
241            token: Some(token_client),
242            buffer_collection_request: Some(remote),
243            ..Default::default()
244        })?;
245        local.into_proxy()
246    };
247
248    collection.set_constraints(BufferCollectionSetConstraintsRequest {
250        constraints: Some(buffer_collection_constraints(params)),
251        ..Default::default()
252    })?;
253    let collection_info = {
254        let response = collection
255            .wait_for_all_buffers_allocated()
256            .await?
257            .map_err(|_| Error::BuffersNotAllocated)?;
258        response.buffer_collection_info.ok_or(Error::BuffersNotAllocated)?
259    };
260
261    if collection_info.buffers.as_ref().unwrap().is_empty() {
263        collection.release()?;
264        return Err(Error::BuffersNotAllocated);
265    }
266
267    Ok((collection_info, collection))
268}
269
270fn buffer_collection_constraints(
271    params: &ImageParameters,
272) -> fsysmem2::BufferCollectionConstraints {
273    let usage = fsysmem2::BufferUsage {
274        cpu: Some(fsysmem2::CPU_USAGE_READ_OFTEN | fsysmem2::CPU_USAGE_WRITE_OFTEN),
275        ..Default::default()
276    };
277
278    let buffer_memory_constraints = fsysmem2::BufferMemoryConstraints {
279        ram_domain_supported: Some(true),
280        cpu_domain_supported: Some(true),
281        ..Default::default()
282    };
283
284    let image_constraints = fsysmem2::ImageFormatConstraints {
286        pixel_format: Some(params.pixel_format.into()),
287        pixel_format_modifier: Some(fimages2::PixelFormatModifier::Linear),
288        required_max_size: Some(fidl_fuchsia_math::SizeU {
289            width: params.width,
290            height: params.height,
291        }),
292        color_spaces: Some(vec![params.color_space]),
293        ..Default::default()
294    };
295
296    let constraints = fsysmem2::BufferCollectionConstraints {
297        min_buffer_count: Some(1),
298        usage: Some(usage),
299        buffer_memory_constraints: Some(buffer_memory_constraints),
300        image_format_constraints: Some(vec![image_constraints]),
301        ..Default::default()
302    };
303
304    constraints
305}