rutabaga_gfx/rutabaga_gralloc/
gralloc.rs
1use std::collections::BTreeMap as Map;
9
10#[cfg(feature = "vulkano")]
11use log::error;
12
13use crate::rutabaga_gralloc::formats::*;
14#[cfg(feature = "minigbm")]
15use crate::rutabaga_gralloc::minigbm::MinigbmDevice;
16use crate::rutabaga_gralloc::system_gralloc::SystemGralloc;
17#[cfg(feature = "vulkano")]
18use crate::rutabaga_gralloc::vulkano_gralloc::VulkanoGralloc;
19use crate::rutabaga_os::round_up_to_page_size;
20use crate::rutabaga_os::MappedRegion;
21use crate::rutabaga_utils::*;
22
23const RUTABAGA_GRALLOC_USE_SCANOUT: u32 = 1 << 0;
28const RUTABAGA_GRALLOC_USE_RENDERING: u32 = 1 << 2;
29const RUTABAGA_GRALLOC_USE_LINEAR: u32 = 1 << 4;
30const RUTABAGA_GRALLOC_USE_TEXTURING: u32 = 1 << 5;
31const RUTABAGA_GRALLOC_USE_CAMERA_WRITE: u32 = 1 << 6;
32const RUTABAGA_GRALLOC_USE_CAMERA_READ: u32 = 1 << 7;
33#[allow(dead_code)]
34const RUTABAGA_GRALLOC_USE_PROTECTED: u32 = 1 << 8;
35
36const RUTABAGA_GRALLOC_USE_SW_READ_OFTEN: u32 = 1 << 9;
38const RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN: u32 = 1 << 11;
39
40#[allow(dead_code)]
41const RUTABAGA_GRALLOC_VIDEO_DECODER: u32 = 1 << 13;
42#[allow(dead_code)]
43const RUTABAGA_GRALLOC_VIDEO_ENCODER: u32 = 1 << 14;
44
45#[derive(Copy, Clone, Eq, PartialEq, Default)]
47pub struct RutabagaGrallocFlags(pub u32);
48
49impl RutabagaGrallocFlags {
50 #[inline(always)]
52 pub fn empty() -> RutabagaGrallocFlags {
53 RutabagaGrallocFlags(0)
54 }
55
56 #[inline(always)]
59 pub fn new(raw: u32) -> RutabagaGrallocFlags {
60 RutabagaGrallocFlags(raw)
61 }
62
63 #[inline(always)]
65 pub fn use_scanout(self, e: bool) -> RutabagaGrallocFlags {
66 if e {
67 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SCANOUT)
68 } else {
69 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SCANOUT)
70 }
71 }
72
73 #[inline(always)]
75 pub fn use_rendering(self, e: bool) -> RutabagaGrallocFlags {
76 if e {
77 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_RENDERING)
78 } else {
79 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_RENDERING)
80 }
81 }
82
83 #[inline(always)]
85 pub fn use_linear(self, e: bool) -> RutabagaGrallocFlags {
86 if e {
87 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_LINEAR)
88 } else {
89 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_LINEAR)
90 }
91 }
92
93 #[inline(always)]
95 pub fn use_sw_write(self, e: bool) -> RutabagaGrallocFlags {
96 if e {
97 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN)
98 } else {
99 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN)
100 }
101 }
102
103 #[inline(always)]
105 pub fn use_sw_read(self, e: bool) -> RutabagaGrallocFlags {
106 if e {
107 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_READ_OFTEN)
108 } else {
109 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_READ_OFTEN)
110 }
111 }
112
113 #[inline(always)]
115 pub fn uses_texturing(self) -> bool {
116 self.0 & RUTABAGA_GRALLOC_USE_TEXTURING != 0
117 }
118
119 #[inline(always)]
121 pub fn uses_rendering(self) -> bool {
122 self.0 & RUTABAGA_GRALLOC_USE_RENDERING != 0
123 }
124
125 #[inline(always)]
128 pub fn host_visible(self) -> bool {
129 self.0 & RUTABAGA_GRALLOC_USE_SW_READ_OFTEN != 0
130 || self.0 & RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN != 0
131 || self.0 & RUTABAGA_GRALLOC_USE_CAMERA_WRITE != 0
132 || self.0 & RUTABAGA_GRALLOC_USE_CAMERA_READ != 0
133 }
134
135 #[inline(always)]
138 pub fn host_cached(self) -> bool {
139 self.0 & RUTABAGA_GRALLOC_USE_CAMERA_READ != 0
140 || self.0 & RUTABAGA_GRALLOC_USE_SW_READ_OFTEN != 0
141 }
142}
143
144#[derive(Copy, Clone, Default)]
146pub struct ImageAllocationInfo {
147 pub width: u32,
148 pub height: u32,
149 pub drm_format: DrmFormat,
150 pub flags: RutabagaGrallocFlags,
151}
152
153#[derive(Copy, Clone, Default)]
155pub struct ImageMemoryRequirements {
156 pub info: ImageAllocationInfo,
157 pub map_info: u32,
158 pub strides: [u32; 4],
159 pub offsets: [u32; 4],
160 pub modifier: u64,
161 pub size: u64,
162 pub vulkan_info: Option<VulkanInfo>,
163}
164
165pub trait Gralloc: Send {
171 fn supports_external_gpu_memory(&self) -> bool;
176
177 fn supports_dmabuf(&self) -> bool;
180
181 fn get_image_memory_requirements(
184 &mut self,
185 info: ImageAllocationInfo,
186 ) -> RutabagaResult<ImageMemoryRequirements>;
187
188 fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>;
191
192 fn import_and_map(
195 &mut self,
196 _handle: RutabagaHandle,
197 _vulkan_info: VulkanInfo,
198 _size: u64,
199 ) -> RutabagaResult<Box<dyn MappedRegion>> {
200 Err(RutabagaError::Unsupported)
201 }
202}
203
204#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
206pub enum GrallocBackend {
207 #[allow(dead_code)]
208 Vulkano,
209 #[allow(dead_code)]
210 Minigbm,
211 System,
212}
213
214pub struct RutabagaGralloc {
216 grallocs: Map<GrallocBackend, Box<dyn Gralloc>>,
217}
218
219impl RutabagaGralloc {
220 pub fn new() -> RutabagaResult<RutabagaGralloc> {
223 let mut grallocs: Map<GrallocBackend, Box<dyn Gralloc>> = Default::default();
224
225 let system = SystemGralloc::init()?;
226 grallocs.insert(GrallocBackend::System, system);
227
228 #[cfg(feature = "minigbm")]
229 {
230 if let Ok(gbm_device) = MinigbmDevice::init() {
236 grallocs.insert(GrallocBackend::Minigbm, gbm_device);
237 }
238 }
239
240 #[cfg(feature = "vulkano")]
241 {
242 match VulkanoGralloc::init() {
243 Ok(vulkano) => {
244 grallocs.insert(GrallocBackend::Vulkano, vulkano);
245 }
246 Err(e) => {
247 error!("failed to init Vulkano gralloc: {:?}", e);
248 }
249 }
250 }
251
252 Ok(RutabagaGralloc { grallocs })
253 }
254
255 pub fn supports_external_gpu_memory(&self) -> bool {
257 for gralloc in self.grallocs.values() {
258 if gralloc.supports_external_gpu_memory() {
259 return true;
260 }
261 }
262
263 false
264 }
265
266 pub fn supports_dmabuf(&self) -> bool {
268 for gralloc in self.grallocs.values() {
269 if gralloc.supports_dmabuf() {
270 return true;
271 }
272 }
273
274 false
275 }
276
277 fn determine_optimal_backend(&self, _info: ImageAllocationInfo) -> GrallocBackend {
279 #[allow(clippy::let_and_return)]
286 let mut _backend = GrallocBackend::System;
287
288 #[cfg(feature = "minigbm")]
289 {
290 if self.grallocs.contains_key(&GrallocBackend::Minigbm) {
292 _backend = GrallocBackend::Minigbm;
293 }
294 }
295
296 #[cfg(feature = "vulkano")]
297 {
298 _backend = GrallocBackend::Vulkano;
299 }
300
301 _backend
302 }
303
304 pub fn get_image_memory_requirements(
306 &mut self,
307 info: ImageAllocationInfo,
308 ) -> RutabagaResult<ImageMemoryRequirements> {
309 let backend = self.determine_optimal_backend(info);
310
311 let gralloc = self
312 .grallocs
313 .get_mut(&backend)
314 .ok_or(RutabagaError::InvalidGrallocBackend)?;
315
316 let mut reqs = gralloc.get_image_memory_requirements(info)?;
317 reqs.size = round_up_to_page_size(reqs.size)?;
318 Ok(reqs)
319 }
320
321 pub fn allocate_memory(
323 &mut self,
324 reqs: ImageMemoryRequirements,
325 ) -> RutabagaResult<RutabagaHandle> {
326 let backend = self.determine_optimal_backend(reqs.info);
327
328 let gralloc = self
329 .grallocs
330 .get_mut(&backend)
331 .ok_or(RutabagaError::InvalidGrallocBackend)?;
332
333 gralloc.allocate_memory(reqs)
334 }
335
336 pub fn import_and_map(
339 &mut self,
340 handle: RutabagaHandle,
341 vulkan_info: VulkanInfo,
342 size: u64,
343 ) -> RutabagaResult<Box<dyn MappedRegion>> {
344 let gralloc = self
345 .grallocs
346 .get_mut(&GrallocBackend::Vulkano)
347 .ok_or(RutabagaError::InvalidGrallocBackend)?;
348
349 gralloc.import_and_map(handle, vulkan_info, size)
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use super::*;
356
357 #[test]
358 #[cfg_attr(target_os = "windows", ignore)]
359 fn create_render_target() {
360 let gralloc_result = RutabagaGralloc::new();
361 if gralloc_result.is_err() {
362 return;
363 }
364
365 let mut gralloc = gralloc_result.unwrap();
366
367 let info = ImageAllocationInfo {
368 width: 512,
369 height: 1024,
370 drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'),
371 flags: RutabagaGrallocFlags::empty().use_scanout(true),
372 };
373
374 let reqs = gralloc.get_image_memory_requirements(info).unwrap();
375 let min_reqs = canonical_image_requirements(info).unwrap();
376
377 assert!(reqs.strides[0] >= min_reqs.strides[0]);
378 assert!(reqs.size >= min_reqs.size);
379
380 let _handle = gralloc.allocate_memory(reqs).unwrap();
381
382 let _handle2 = gralloc.allocate_memory(reqs).unwrap();
384 }
385
386 #[test]
387 #[cfg_attr(target_os = "windows", ignore)]
388 fn create_video_buffer() {
389 let gralloc_result = RutabagaGralloc::new();
390 if gralloc_result.is_err() {
391 return;
392 }
393
394 let mut gralloc = gralloc_result.unwrap();
395
396 let info = ImageAllocationInfo {
397 width: 512,
398 height: 1024,
399 drm_format: DrmFormat::new(b'N', b'V', b'1', b'2'),
400 flags: RutabagaGrallocFlags::empty().use_linear(true),
401 };
402
403 let reqs = gralloc.get_image_memory_requirements(info).unwrap();
404 let min_reqs = canonical_image_requirements(info).unwrap();
405
406 assert!(reqs.strides[0] >= min_reqs.strides[0]);
407 assert!(reqs.strides[1] >= min_reqs.strides[1]);
408 assert_eq!(reqs.strides[2], 0);
409 assert_eq!(reqs.strides[3], 0);
410
411 assert!(reqs.offsets[0] >= min_reqs.offsets[0]);
412 assert!(reqs.offsets[1] >= min_reqs.offsets[1]);
413 assert_eq!(reqs.offsets[2], 0);
414 assert_eq!(reqs.offsets[3], 0);
415
416 assert!(reqs.size >= min_reqs.size);
417
418 let _handle = gralloc.allocate_memory(reqs).unwrap();
419
420 let _handle2 = gralloc.allocate_memory(reqs).unwrap();
422 }
423
424 #[test]
425 #[cfg_attr(target_os = "windows", ignore)]
426 fn export_and_map() {
427 let gralloc_result = RutabagaGralloc::new();
428 if gralloc_result.is_err() {
429 return;
430 }
431
432 let mut gralloc = gralloc_result.unwrap();
433
434 let info = ImageAllocationInfo {
435 width: 512,
436 height: 1024,
437 drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'),
438 flags: RutabagaGrallocFlags::empty()
439 .use_linear(true)
440 .use_sw_write(true)
441 .use_sw_read(true),
442 };
443
444 let mut reqs = gralloc.get_image_memory_requirements(info).unwrap();
445
446 if reqs.vulkan_info.is_none() {
448 return;
449 }
450
451 let handle = gralloc.allocate_memory(reqs).unwrap();
452 let vulkan_info = reqs.vulkan_info.take().unwrap();
453
454 let mapping = gralloc
455 .import_and_map(handle, vulkan_info, reqs.size)
456 .unwrap();
457
458 let addr = mapping.as_ptr();
459 let size = mapping.size();
460
461 assert_eq!(size as u64, reqs.size);
462 assert_ne!(addr as *const u8, std::ptr::null());
463 }
464}