rutabaga_gfx/rutabaga_gralloc/
gralloc.rs1use std::collections::BTreeMap as Map;
9
10#[cfg(feature = "vulkano")]
11use log::error;
12use mesa3d_util::round_up_to_page_size;
13use mesa3d_util::MappedRegion;
14use mesa3d_util::MesaError;
15use mesa3d_util::MesaHandle;
16
17use crate::rutabaga_gralloc::formats::*;
18#[cfg(feature = "gbm")]
19use crate::rutabaga_gralloc::minigbm::MinigbmDevice;
20use crate::rutabaga_gralloc::system_gralloc::SystemGralloc;
21#[cfg(feature = "vulkano")]
22use crate::rutabaga_gralloc::vulkano_gralloc::VulkanoGralloc;
23use crate::rutabaga_utils::RutabagaError;
24use crate::rutabaga_utils::RutabagaResult;
25use crate::rutabaga_utils::VulkanInfo;
26
27const RUTABAGA_GRALLOC_BACKEND_SYSTEM: u32 = 1 << 0;
28const RUTABAGA_GRALLOC_BACKEND_GBM: u32 = 1 << 1;
29const RUTABAGA_GRALLOC_BACKEND_VULKANO: u32 = 1 << 2;
30
31#[derive(Copy, Clone, Eq, PartialEq, Default)]
33pub struct RutabagaGrallocBackendFlags(pub u32);
34
35impl RutabagaGrallocBackendFlags {
36 #[inline(always)]
38 pub fn new() -> RutabagaGrallocBackendFlags {
39 RutabagaGrallocBackendFlags(
40 RUTABAGA_GRALLOC_BACKEND_SYSTEM
41 | RUTABAGA_GRALLOC_BACKEND_GBM
42 | RUTABAGA_GRALLOC_BACKEND_VULKANO,
43 )
44 }
45
46 #[inline(always)]
47 pub fn disable_vulkano(self) -> RutabagaGrallocBackendFlags {
48 RutabagaGrallocBackendFlags(self.0 & !RUTABAGA_GRALLOC_BACKEND_VULKANO)
49 }
50
51 pub fn uses_system(&self) -> bool {
52 self.0 & RUTABAGA_GRALLOC_BACKEND_SYSTEM != 0
53 }
54
55 pub fn uses_gbm(&self) -> bool {
56 self.0 & RUTABAGA_GRALLOC_BACKEND_GBM != 0
57 }
58
59 pub fn uses_vulkano(&self) -> bool {
60 self.0 & RUTABAGA_GRALLOC_BACKEND_VULKANO != 0
61 }
62}
63
64const RUTABAGA_GRALLOC_USE_SCANOUT: u32 = 1 << 0;
69const RUTABAGA_GRALLOC_USE_RENDERING: u32 = 1 << 2;
70const RUTABAGA_GRALLOC_USE_LINEAR: u32 = 1 << 4;
71const RUTABAGA_GRALLOC_USE_TEXTURING: u32 = 1 << 5;
72const RUTABAGA_GRALLOC_USE_CAMERA_WRITE: u32 = 1 << 6;
73const RUTABAGA_GRALLOC_USE_CAMERA_READ: u32 = 1 << 7;
74#[allow(dead_code)]
75const RUTABAGA_GRALLOC_USE_PROTECTED: u32 = 1 << 8;
76
77const RUTABAGA_GRALLOC_USE_SW_READ_OFTEN: u32 = 1 << 9;
79const RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN: u32 = 1 << 11;
80
81#[allow(dead_code)]
82const RUTABAGA_GRALLOC_VIDEO_DECODER: u32 = 1 << 13;
83#[allow(dead_code)]
84const RUTABAGA_GRALLOC_VIDEO_ENCODER: u32 = 1 << 14;
85
86#[derive(Copy, Clone, Eq, PartialEq, Default)]
88pub struct RutabagaGrallocFlags(pub u32);
89
90impl RutabagaGrallocFlags {
91 #[inline(always)]
93 pub fn empty() -> RutabagaGrallocFlags {
94 RutabagaGrallocFlags(0)
95 }
96
97 #[inline(always)]
100 pub fn new(raw: u32) -> RutabagaGrallocFlags {
101 RutabagaGrallocFlags(raw)
102 }
103
104 #[inline(always)]
106 pub fn use_scanout(self, e: bool) -> RutabagaGrallocFlags {
107 if e {
108 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SCANOUT)
109 } else {
110 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SCANOUT)
111 }
112 }
113
114 #[inline(always)]
116 pub fn use_rendering(self, e: bool) -> RutabagaGrallocFlags {
117 if e {
118 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_RENDERING)
119 } else {
120 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_RENDERING)
121 }
122 }
123
124 #[inline(always)]
126 pub fn use_linear(self, e: bool) -> RutabagaGrallocFlags {
127 if e {
128 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_LINEAR)
129 } else {
130 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_LINEAR)
131 }
132 }
133
134 #[inline(always)]
136 pub fn use_sw_write(self, e: bool) -> RutabagaGrallocFlags {
137 if e {
138 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN)
139 } else {
140 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN)
141 }
142 }
143
144 #[inline(always)]
146 pub fn use_sw_read(self, e: bool) -> RutabagaGrallocFlags {
147 if e {
148 RutabagaGrallocFlags(self.0 | RUTABAGA_GRALLOC_USE_SW_READ_OFTEN)
149 } else {
150 RutabagaGrallocFlags(self.0 & !RUTABAGA_GRALLOC_USE_SW_READ_OFTEN)
151 }
152 }
153
154 #[inline(always)]
156 pub fn uses_texturing(self) -> bool {
157 self.0 & RUTABAGA_GRALLOC_USE_TEXTURING != 0
158 }
159
160 #[inline(always)]
162 pub fn uses_rendering(self) -> bool {
163 self.0 & RUTABAGA_GRALLOC_USE_RENDERING != 0
164 }
165
166 #[inline(always)]
169 pub fn host_visible(self) -> bool {
170 self.0 & RUTABAGA_GRALLOC_USE_SW_READ_OFTEN != 0
171 || self.0 & RUTABAGA_GRALLOC_USE_SW_WRITE_OFTEN != 0
172 || self.0 & RUTABAGA_GRALLOC_USE_CAMERA_WRITE != 0
173 || self.0 & RUTABAGA_GRALLOC_USE_CAMERA_READ != 0
174 }
175
176 #[inline(always)]
179 pub fn host_cached(self) -> bool {
180 self.0 & RUTABAGA_GRALLOC_USE_CAMERA_READ != 0
181 || self.0 & RUTABAGA_GRALLOC_USE_SW_READ_OFTEN != 0
182 }
183}
184
185#[derive(Copy, Clone, Default)]
187pub struct ImageAllocationInfo {
188 pub width: u32,
189 pub height: u32,
190 pub drm_format: DrmFormat,
191 pub flags: RutabagaGrallocFlags,
192}
193
194#[derive(Copy, Clone, Default)]
196pub struct ImageMemoryRequirements {
197 pub info: ImageAllocationInfo,
198 pub map_info: u32,
199 pub strides: [u32; 4],
200 pub offsets: [u32; 4],
201 pub modifier: u64,
202 pub size: u64,
203 pub vulkan_info: Option<VulkanInfo>,
204}
205
206pub trait Gralloc: Send {
212 fn supports_external_gpu_memory(&self) -> bool;
217
218 fn supports_dmabuf(&self) -> bool;
221
222 fn get_image_memory_requirements(
225 &mut self,
226 info: ImageAllocationInfo,
227 ) -> RutabagaResult<ImageMemoryRequirements>;
228
229 fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<MesaHandle>;
232
233 fn import_and_map(
236 &mut self,
237 _handle: MesaHandle,
238 _vulkan_info: VulkanInfo,
239 _size: u64,
240 ) -> RutabagaResult<Box<dyn MappedRegion>> {
241 Err(MesaError::Unsupported.into())
242 }
243}
244
245#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
247pub enum GrallocBackend {
248 #[allow(dead_code)]
249 Vulkano,
250 #[allow(dead_code)]
251 Minigbm,
252 System,
253}
254
255pub struct RutabagaGralloc {
257 grallocs: Map<GrallocBackend, Box<dyn Gralloc>>,
258}
259
260impl RutabagaGralloc {
261 pub fn new(flags: RutabagaGrallocBackendFlags) -> RutabagaResult<RutabagaGralloc> {
264 let mut grallocs: Map<GrallocBackend, Box<dyn Gralloc>> = Default::default();
265
266 if flags.uses_system() {
267 let system = SystemGralloc::init()?;
268 grallocs.insert(GrallocBackend::System, system);
269 }
270
271 #[cfg(feature = "gbm")]
272 if flags.uses_gbm() {
273 if let Ok(gbm_device) = MinigbmDevice::init() {
279 grallocs.insert(GrallocBackend::Minigbm, gbm_device);
280 }
281 }
282
283 #[cfg(feature = "vulkano")]
284 if flags.uses_vulkano() {
285 match VulkanoGralloc::init() {
286 Ok(vulkano) => {
287 grallocs.insert(GrallocBackend::Vulkano, vulkano);
288 }
289 Err(e) => {
290 error!("failed to init Vulkano gralloc: {:?}", e);
291 }
292 }
293 }
294
295 Ok(RutabagaGralloc { grallocs })
296 }
297
298 pub fn supports_external_gpu_memory(&self) -> bool {
300 for gralloc in self.grallocs.values() {
301 if gralloc.supports_external_gpu_memory() {
302 return true;
303 }
304 }
305
306 false
307 }
308
309 pub fn supports_dmabuf(&self) -> bool {
311 for gralloc in self.grallocs.values() {
312 if gralloc.supports_dmabuf() {
313 return true;
314 }
315 }
316
317 false
318 }
319
320 fn determine_optimal_backend(&self, _info: ImageAllocationInfo) -> GrallocBackend {
322 #[allow(clippy::let_and_return)]
329 let mut _backend = GrallocBackend::System;
330
331 #[cfg(feature = "gbm")]
332 {
333 if self.grallocs.contains_key(&GrallocBackend::Minigbm) {
335 _backend = GrallocBackend::Minigbm;
336 }
337 }
338
339 #[cfg(feature = "vulkano")]
340 {
341 _backend = GrallocBackend::Vulkano;
342 }
343
344 _backend
345 }
346
347 pub fn get_image_memory_requirements(
349 &mut self,
350 info: ImageAllocationInfo,
351 ) -> RutabagaResult<ImageMemoryRequirements> {
352 let backend = self.determine_optimal_backend(info);
353
354 let gralloc = self
355 .grallocs
356 .get_mut(&backend)
357 .ok_or(RutabagaError::InvalidGrallocBackend)?;
358
359 let mut reqs = gralloc.get_image_memory_requirements(info)?;
360 reqs.size = round_up_to_page_size(reqs.size)?;
361 Ok(reqs)
362 }
363
364 pub fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<MesaHandle> {
366 let backend = self.determine_optimal_backend(reqs.info);
367
368 let gralloc = self
369 .grallocs
370 .get_mut(&backend)
371 .ok_or(RutabagaError::InvalidGrallocBackend)?;
372
373 gralloc.allocate_memory(reqs)
374 }
375
376 pub fn import_and_map(
379 &mut self,
380 handle: MesaHandle,
381 vulkan_info: VulkanInfo,
382 size: u64,
383 ) -> RutabagaResult<Box<dyn MappedRegion>> {
384 let gralloc = self
385 .grallocs
386 .get_mut(&GrallocBackend::Vulkano)
387 .ok_or(RutabagaError::InvalidGrallocBackend)?;
388
389 gralloc.import_and_map(handle, vulkan_info, size)
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396
397 #[test]
398 #[cfg_attr(target_os = "windows", ignore)]
399 fn create_render_target() {
400 let gralloc_result = RutabagaGralloc::new(RutabagaGrallocBackendFlags::new());
401 if gralloc_result.is_err() {
402 return;
403 }
404
405 let mut gralloc = gralloc_result.unwrap();
406
407 let info = ImageAllocationInfo {
408 width: 512,
409 height: 1024,
410 drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'),
411 flags: RutabagaGrallocFlags::empty().use_scanout(true),
412 };
413
414 let reqs = gralloc.get_image_memory_requirements(info).unwrap();
415 let min_reqs = canonical_image_requirements(info).unwrap();
416
417 assert!(reqs.strides[0] >= min_reqs.strides[0]);
418 assert!(reqs.size >= min_reqs.size);
419
420 let _handle = gralloc.allocate_memory(reqs).unwrap();
421
422 let _handle2 = gralloc.allocate_memory(reqs).unwrap();
424 }
425
426 #[test]
427 #[cfg_attr(target_os = "windows", ignore)]
428 fn create_video_buffer() {
429 let gralloc_result = RutabagaGralloc::new(RutabagaGrallocBackendFlags::new());
430 if gralloc_result.is_err() {
431 return;
432 }
433
434 let mut gralloc = gralloc_result.unwrap();
435
436 let info = ImageAllocationInfo {
437 width: 512,
438 height: 1024,
439 drm_format: DrmFormat::new(b'N', b'V', b'1', b'2'),
440 flags: RutabagaGrallocFlags::empty().use_linear(true),
441 };
442
443 let reqs = gralloc.get_image_memory_requirements(info).unwrap();
444 let min_reqs = canonical_image_requirements(info).unwrap();
445
446 assert!(reqs.strides[0] >= min_reqs.strides[0]);
447 assert!(reqs.strides[1] >= min_reqs.strides[1]);
448 assert_eq!(reqs.strides[2], 0);
449 assert_eq!(reqs.strides[3], 0);
450
451 assert!(reqs.offsets[0] >= min_reqs.offsets[0]);
452 assert!(reqs.offsets[1] >= min_reqs.offsets[1]);
453 assert_eq!(reqs.offsets[2], 0);
454 assert_eq!(reqs.offsets[3], 0);
455
456 assert!(reqs.size >= min_reqs.size);
457
458 let _handle = gralloc.allocate_memory(reqs).unwrap();
459
460 let _handle2 = gralloc.allocate_memory(reqs).unwrap();
462 }
463
464 #[test]
465 #[cfg_attr(target_os = "windows", ignore)]
466 fn export_and_map() {
467 let gralloc_result = RutabagaGralloc::new(RutabagaGrallocBackendFlags::new());
468 if gralloc_result.is_err() {
469 return;
470 }
471
472 let mut gralloc = gralloc_result.unwrap();
473
474 let info = ImageAllocationInfo {
475 width: 512,
476 height: 1024,
477 drm_format: DrmFormat::new(b'X', b'R', b'2', b'4'),
478 flags: RutabagaGrallocFlags::empty()
479 .use_linear(true)
480 .use_sw_write(true)
481 .use_sw_read(true),
482 };
483
484 let mut reqs = gralloc.get_image_memory_requirements(info).unwrap();
485
486 if reqs.vulkan_info.is_none() {
488 return;
489 }
490
491 let handle = gralloc.allocate_memory(reqs).unwrap();
492 let vulkan_info = reqs.vulkan_info.take().unwrap();
493
494 let mapping = gralloc
495 .import_and_map(handle, vulkan_info, reqs.size)
496 .unwrap();
497
498 let addr = mapping.as_ptr();
499 let size = mapping.size();
500
501 assert_eq!(size as u64, reqs.size);
502 assert_ne!(addr as *const u8, std::ptr::null());
503 }
504}