1use crate::FrameUsage;
6use anyhow::{Context, Error, format_err};
7use fidl::endpoints::{ClientEnd, Proxy, create_endpoints};
8use fidl_fuchsia_images2::{ColorSpace, PixelFormat, PixelFormatModifier};
9use fidl_fuchsia_sysmem2::{
10 AllocatorAllocateSharedCollectionRequest, AllocatorBindSharedCollectionRequest,
11 AllocatorMarker, AllocatorProxy, AllocatorSetDebugClientInfoRequest,
12 BufferCollectionConstraints, BufferCollectionInfo, BufferCollectionMarker,
13 BufferCollectionProxy, BufferCollectionSetConstraintsRequest,
14 BufferCollectionTokenDuplicateRequest, BufferCollectionTokenMarker, BufferCollectionTokenProxy,
15 BufferMemoryConstraints, BufferUsage, CPU_USAGE_READ_OFTEN, CPU_USAGE_WRITE_OFTEN,
16 ImageFormatConstraints, NONE_USAGE, NodeSetNameRequest,
17};
18use fuchsia_component::client::connect_to_protocol;
19use std::cmp;
20
21fn linear_image_format_constraints(
22 width: u32,
23 height: u32,
24 pixel_type: PixelFormat,
25) -> ImageFormatConstraints {
26 ImageFormatConstraints {
27 pixel_format: Some(pixel_type),
28 pixel_format_modifier: Some(PixelFormatModifier::Linear),
29 color_spaces: Some(vec![ColorSpace::Srgb]),
30 required_min_size: Some(fidl_fuchsia_math::SizeU { width, height }),
31 required_max_size: Some(fidl_fuchsia_math::SizeU { width, height }),
32 ..Default::default()
33 }
34}
35
36fn buffer_memory_constraints(width: u32, height: u32) -> BufferMemoryConstraints {
37 BufferMemoryConstraints {
38 min_size_bytes: Some(width as u64 * height as u64 * 4u64),
39 physically_contiguous_required: Some(false),
40 secure_required: Some(false),
41 ram_domain_supported: Some(true),
42 cpu_domain_supported: Some(true),
43 inaccessible_domain_supported: Some(false),
44 ..Default::default()
45 }
46}
47
48fn buffer_collection_constraints(
49 width: u32,
50 height: u32,
51 pixel_format: PixelFormat,
52 buffer_count: u32,
53 frame_usage: FrameUsage,
54) -> BufferCollectionConstraints {
55 let (usage, has_buffer_memory_constraints, has_image_format_constraints) = match frame_usage {
56 FrameUsage::Cpu => (
57 BufferUsage {
58 cpu: Some(CPU_USAGE_WRITE_OFTEN | CPU_USAGE_READ_OFTEN),
59 ..Default::default()
60 },
61 true,
62 true,
63 ),
64 FrameUsage::Gpu => {
65 (BufferUsage { none: Some(NONE_USAGE), ..Default::default() }, false, false)
66 }
67 };
68 BufferCollectionConstraints {
69 usage: Some(usage),
70 min_buffer_count: Some(buffer_count),
71 buffer_memory_constraints: if has_buffer_memory_constraints {
72 Some(buffer_memory_constraints(width, height))
73 } else {
74 None
75 },
76 image_format_constraints: if has_image_format_constraints {
77 Some(vec![linear_image_format_constraints(width, height, pixel_format)])
78 } else {
79 None
80 },
81 ..Default::default()
82 }
83}
84
85fn stride_bytes_per_width_pixel(pixel_type: PixelFormat) -> Result<u32, Error> {
87 match pixel_type {
88 PixelFormat::R8G8B8A8 => Ok(4),
89 PixelFormat::B8G8R8A8 => Ok(4),
90 PixelFormat::B8G8R8 => Ok(3),
91 PixelFormat::I420 => Ok(1),
92 PixelFormat::M420 => Ok(1),
93 PixelFormat::Nv12 => Ok(1),
94 PixelFormat::Yuy2 => Ok(2),
95 PixelFormat::Yv12 => Ok(1),
96 PixelFormat::R5G6B5 => Ok(2),
97 PixelFormat::R3G3B2 => Ok(1),
98 PixelFormat::R2G2B2X2 => Ok(1),
99 PixelFormat::L8 => Ok(1),
100 _ => return Err(format_err!("Unsupported format")),
101 }
102}
103
104fn round_up_to_align(x: u32, align: u32) -> u32 {
105 if align == 0 { x } else { ((x + align - 1) / align) * align }
106}
107
108pub fn minimum_row_bytes(constraints: &ImageFormatConstraints, width: u32) -> Result<u32, Error> {
110 if width < constraints.min_size.ok_or("missing min_size").unwrap().width
111 || width > constraints.max_size.ok_or("missing max_size").unwrap().width
112 {
113 return Err(format_err!("Invalid width for constraints"));
114 }
115
116 let bytes_per_pixel = stride_bytes_per_width_pixel(
117 constraints.pixel_format.ok_or("missing pixel_format").unwrap(),
118 )?;
119 Ok(round_up_to_align(
120 cmp::max(
121 bytes_per_pixel * width,
122 constraints.min_bytes_per_row.ok_or("missing min_bytes_per_row").unwrap(),
123 ),
124 constraints.bytes_per_row_divisor.ok_or("missing bytes_per_row_divisor").unwrap(),
125 ))
126}
127
128pub struct BufferCollectionAllocator {
129 token: Option<BufferCollectionTokenProxy>,
130 width: u32,
131 height: u32,
132 pixel_format: PixelFormat,
133 usage: FrameUsage,
134 buffer_count: usize,
135 sysmem: AllocatorProxy,
136 collection_client: Option<BufferCollectionProxy>,
137}
138
139pub fn set_allocator_name(sysmem_client: &AllocatorProxy) -> Result<(), Error> {
140 Ok(sysmem_client.set_debug_client_info(&AllocatorSetDebugClientInfoRequest {
141 name: Some(fuchsia_runtime::process_self().get_name()?.to_string()),
142 id: Some(fuchsia_runtime::process_self().koid()?.raw_koid()),
143 ..Default::default()
144 })?)
145}
146
147impl BufferCollectionAllocator {
148 pub fn new(
149 width: u32,
150 height: u32,
151 pixel_format: PixelFormat,
152 usage: FrameUsage,
153 buffer_count: usize,
154 ) -> Result<BufferCollectionAllocator, Error> {
155 let sysmem = connect_to_protocol::<AllocatorMarker>()?;
156
157 let _ = set_allocator_name(&sysmem);
158
159 let (local_token, local_token_request) = create_endpoints::<BufferCollectionTokenMarker>();
160
161 sysmem.allocate_shared_collection(AllocatorAllocateSharedCollectionRequest {
162 token_request: Some(local_token_request),
163 ..Default::default()
164 })?;
165
166 Ok(BufferCollectionAllocator {
167 token: Some(local_token.into_proxy()),
168 width,
169 height,
170 pixel_format,
171 usage,
172 buffer_count,
173 sysmem,
174 collection_client: None,
175 })
176 }
177
178 pub fn set_name(&mut self, priority: u32, name: &str) -> Result<(), Error> {
179 Ok(self.token.as_ref().expect("token in set_name").set_name(&NodeSetNameRequest {
180 priority: Some(priority),
181 name: Some(name.into()),
182 ..Default::default()
183 })?)
184 }
185
186 pub fn set_pixel_type(&mut self, pixel_format: PixelFormat) {
187 self.pixel_format = pixel_format;
188 }
189
190 pub async fn allocate_buffers(
191 &mut self,
192 set_constraints: bool,
193 ) -> Result<BufferCollectionInfo, Error> {
194 let token = self.token.take().expect("token in allocate_buffers");
195 let (collection_client, collection_request) = create_endpoints::<BufferCollectionMarker>();
196 self.sysmem.bind_shared_collection(AllocatorBindSharedCollectionRequest {
197 token: Some(token.into_client_end().unwrap()),
198 buffer_collection_request: Some(collection_request),
199 ..Default::default()
200 })?;
201 let collection_client = collection_client.into_proxy();
202 self.allocate_buffers_proxy(collection_client, set_constraints).await
203 }
204
205 async fn allocate_buffers_proxy(
206 &mut self,
207 collection_client: BufferCollectionProxy,
208 set_constraints: bool,
209 ) -> Result<BufferCollectionInfo, Error> {
210 let buffer_collection_constraints = buffer_collection_constraints(
211 self.width,
212 self.height,
213 self.pixel_format,
214 self.buffer_count as u32,
215 self.usage,
216 );
217 collection_client
218 .set_constraints(BufferCollectionSetConstraintsRequest {
219 constraints: if set_constraints {
220 Some(buffer_collection_constraints)
221 } else {
222 None
223 },
224 ..Default::default()
225 })
226 .context("Sending buffer constraints to sysmem")?;
227 let wait_result = collection_client.wait_for_all_buffers_allocated().await;
228 self.collection_client = Some(collection_client);
229 if wait_result.is_err() {
230 let error: fidl::Error = wait_result.unwrap_err();
231 return Err(format_err!("Failed to wait for buffers {}", error));
232 }
233 if wait_result.as_ref().unwrap().is_err() {
234 let error: fidl_fuchsia_sysmem2::Error = wait_result.unwrap().unwrap_err();
235 return Err(format_err!("Wait for buffers failed {:?}", error));
236 }
237 let buffers = wait_result.unwrap().unwrap().buffer_collection_info.unwrap();
238 Ok(buffers)
239 }
240
241 pub async fn duplicate_token(
242 &mut self,
243 ) -> Result<ClientEnd<BufferCollectionTokenMarker>, Error> {
244 let (requested_token, requested_token_request) =
245 create_endpoints::<BufferCollectionTokenMarker>();
246
247 self.token.as_ref().expect("token in duplicate_token[duplicate]").duplicate(
248 BufferCollectionTokenDuplicateRequest {
249 rights_attenuation_mask: Some(fidl::Rights::SAME_RIGHTS),
250 token_request: Some(requested_token_request),
251 ..Default::default()
252 },
253 )?;
254 self.token.as_ref().expect("token in duplicate_token_2[sync]").sync().await?;
255 Ok(requested_token)
256 }
257}
258
259impl Drop for BufferCollectionAllocator {
260 fn drop(&mut self) {
261 if let Some(collection_client) = self.collection_client.as_mut() {
262 collection_client
263 .release()
264 .unwrap_or_else(|err| eprintln!("collection_client.release failed with {}", err));
265 }
266 }
267}
268
269#[cfg(test)]
270mod test {
271 use super::*;
272 use fidl_fuchsia_sysmem2::{
273 BufferCollectionRequest, BufferCollectionWaitForAllBuffersAllocatedResponse,
274 BufferMemorySettings, CoherencyDomain, Heap, SingleBufferSettings, VmoBuffer,
275 };
276 use fuchsia_async as fasync;
277 use futures::prelude::*;
278
279 const BUFFER_COUNT: usize = 3;
280
281 fn spawn_allocator_server() -> Result<AllocatorProxy, Error> {
282 let (proxy, mut stream) = fidl::endpoints::create_proxy_and_stream::<AllocatorMarker>();
283
284 fasync::Task::spawn(async move {
285 while let Some(_) = stream.try_next().await.expect("Failed to get request") {}
286 })
287 .detach();
288 Ok(proxy)
289 }
290
291 fn spawn_buffer_collection() -> Result<BufferCollectionProxy, Error> {
292 let (proxy, mut stream) =
293 fidl::endpoints::create_proxy_and_stream::<BufferCollectionMarker>();
294
295 fasync::Task::spawn(async move {
296 let mut stored_constraints = None;
297 while let Some(req) = stream.try_next().await.expect("Failed to get request") {
298 match req {
299 BufferCollectionRequest::SetConstraints { payload, control_handle: _ } => {
300 stored_constraints = payload.constraints;
301 }
302 BufferCollectionRequest::WaitForAllBuffersAllocated { responder } => {
303 let constraints =
304 stored_constraints.take().expect("Expected SetConstraints first");
305 let mut buffers: Vec<VmoBuffer> = vec![];
306 for _ in 0..*constraints.min_buffer_count.as_ref().unwrap() {
307 buffers.push(fidl_fuchsia_sysmem2::VmoBuffer { ..Default::default() });
308 }
309 let buffer_collection_info = BufferCollectionInfo {
310 settings: Some(SingleBufferSettings {
311 buffer_settings: Some(BufferMemorySettings {
312 size_bytes: Some(0),
313 is_physically_contiguous: Some(false),
314 is_secure: Some(false),
315 coherency_domain: Some(CoherencyDomain::Cpu),
316 heap: Some(Heap {
317 heap_type: Some(
318 bind_fuchsia_sysmem_heap::HEAP_TYPE_SYSTEM_RAM.into(),
319 ),
320 ..Default::default()
321 }),
322 ..Default::default()
323 }),
324 image_format_constraints: Some(linear_image_format_constraints(
325 0,
326 0,
327 PixelFormat::Invalid,
328 )),
329 ..Default::default()
330 }),
331 buffers: Some(buffers),
332 ..Default::default()
333 };
334 let response = BufferCollectionWaitForAllBuffersAllocatedResponse {
335 buffer_collection_info: Some(buffer_collection_info),
336 ..Default::default()
337 };
338 responder.send(Ok(response)).expect("Failed to send");
339 }
340 _ => panic!("Unexpected request"),
341 }
342 }
343 })
344 .detach();
345
346 return Ok(proxy);
347 }
348
349 #[fasync::run_singlethreaded(test)]
350 async fn test_buffer_collection_allocator() -> std::result::Result<(), anyhow::Error> {
351 let alloc_proxy = spawn_allocator_server()?;
352 let buf_proxy = spawn_buffer_collection()?;
353
354 let mut bca = BufferCollectionAllocator {
356 token: None,
357 width: 200,
358 height: 200,
359 pixel_format: PixelFormat::B8G8R8A8,
360 usage: FrameUsage::Cpu,
361 buffer_count: BUFFER_COUNT,
362 sysmem: alloc_proxy,
363 collection_client: None,
364 };
365
366 let buffers = bca.allocate_buffers_proxy(buf_proxy, true).await?;
367 assert_eq!(buffers.buffers.unwrap().len(), BUFFER_COUNT);
368
369 Ok(())
370 }
371}