xref: /aosp_15_r20/external/crosvm/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2021 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! vulkano_gralloc: Implements swapchain allocation and memory mapping
6 //! using Vulkano.
7 //!
8 //! External code found at https://github.com/vulkano-rs/vulkano.
9 
10 #![cfg(feature = "vulkano")]
11 
12 mod sys;
13 
14 use std::collections::HashMap as Map;
15 use std::convert::TryInto;
16 use std::sync::Arc;
17 
18 use log::warn;
19 use vulkano::device::physical::PhysicalDeviceType;
20 use vulkano::device::Device;
21 use vulkano::device::DeviceCreateInfo;
22 use vulkano::device::DeviceCreationError;
23 use vulkano::device::QueueCreateInfo;
24 use vulkano::device::QueueFlags;
25 use vulkano::image;
26 use vulkano::image::ImageDimensions;
27 use vulkano::image::ImageError;
28 use vulkano::image::ImageUsage;
29 use vulkano::image::SampleCount;
30 use vulkano::instance::Instance;
31 use vulkano::instance::InstanceCreateInfo;
32 use vulkano::instance::InstanceCreationError;
33 use vulkano::instance::InstanceExtensions;
34 use vulkano::instance::Version;
35 use vulkano::memory::DedicatedAllocation;
36 use vulkano::memory::DeviceMemory;
37 use vulkano::memory::DeviceMemoryError;
38 use vulkano::memory::ExternalMemoryHandleType;
39 use vulkano::memory::ExternalMemoryHandleTypes;
40 use vulkano::memory::MappedDeviceMemory;
41 use vulkano::memory::MemoryAllocateInfo;
42 use vulkano::memory::MemoryMapError;
43 use vulkano::memory::MemoryPropertyFlags;
44 use vulkano::memory::MemoryRequirements;
45 use vulkano::memory::MemoryType;
46 use vulkano::sync::Sharing;
47 use vulkano::LoadingError;
48 use vulkano::VulkanError;
49 use vulkano::VulkanLibrary;
50 
51 use crate::rutabaga_gralloc::gralloc::Gralloc;
52 use crate::rutabaga_gralloc::gralloc::ImageAllocationInfo;
53 use crate::rutabaga_gralloc::gralloc::ImageMemoryRequirements;
54 use crate::rutabaga_os::MappedRegion;
55 use crate::rutabaga_utils::*;
56 
57 /// A convenience enum for allocation
58 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
59 pub enum AllocFromRequirementsFilter {
60     Preferred,
61     Allowed,
62     Forbidden,
63 }
64 
65 /// A gralloc implementation capable of allocation `VkDeviceMemory`.
66 pub struct VulkanoGralloc {
67     devices: Map<PhysicalDeviceType, Arc<Device>>,
68     device_by_id: Map<DeviceId, Arc<Device>>,
69     has_integrated_gpu: bool,
70 }
71 
72 struct VulkanoMapping {
73     mapped_memory: MappedDeviceMemory,
74     size: usize,
75 }
76 
77 impl VulkanoMapping {
new(mapped_memory: MappedDeviceMemory, size: usize) -> VulkanoMapping78     pub fn new(mapped_memory: MappedDeviceMemory, size: usize) -> VulkanoMapping {
79         VulkanoMapping {
80             mapped_memory,
81             size,
82         }
83     }
84 }
85 
86 unsafe impl MappedRegion for VulkanoMapping {
87     /// Used for passing this region for hypervisor memory mappings.  We trust crosvm to use this
88     /// safely.
as_ptr(&self) -> *mut u889     fn as_ptr(&self) -> *mut u8 {
90         unsafe {
91             // Will not panic since the requested range of the device memory was verified on
92             // creation
93             let x = self.mapped_memory.write(0..self.size as u64).unwrap();
94             x.as_mut_ptr()
95         }
96     }
97 
98     /// Returns the size of the memory region in bytes.
size(&self) -> usize99     fn size(&self) -> usize {
100         self.size
101     }
102 
103     /// Returns rutabaga mapping representation of the region
as_rutabaga_mapping(&self) -> RutabagaMapping104     fn as_rutabaga_mapping(&self) -> RutabagaMapping {
105         let ptr: u64 = unsafe {
106             // Will not panic since the requested range of the device memory was verified on
107             // creation
108             let x = self.mapped_memory.write(0..self.size as u64).unwrap();
109             x.as_mut_ptr() as u64
110         };
111 
112         RutabagaMapping {
113             ptr,
114             size: self.size as u64,
115         }
116     }
117 }
118 
119 trait DeviceExt {
120     /// Get a unique identifier for the device.
get_id(&self) -> DeviceId121     fn get_id(&self) -> DeviceId;
122 }
123 
124 impl DeviceExt for Device {
get_id(&self) -> DeviceId125     fn get_id(&self) -> DeviceId {
126         let properties = self.physical_device().properties();
127         DeviceId {
128             device_uuid: properties.device_uuid.expect("Vulkan should support uuid"),
129             driver_uuid: properties.driver_uuid.expect("Vulkan should support uuid"),
130         }
131     }
132 }
133 
134 impl VulkanoGralloc {
135     /// Returns a new `VulkanGralloc' instance upon success.
init() -> RutabagaResult<Box<dyn Gralloc>>136     pub fn init() -> RutabagaResult<Box<dyn Gralloc>> {
137         // Initialization copied from triangle.rs in Vulkano.  Look there for a more detailed
138         // explanation of VK initialization.
139         let library = VulkanLibrary::new()?;
140 
141         let instance_extensions = InstanceExtensions {
142             khr_external_memory_capabilities: true,
143             khr_get_physical_device_properties2: true,
144             ..InstanceExtensions::empty()
145         };
146         let instance = Instance::new(
147             library,
148             InstanceCreateInfo {
149                 enabled_extensions: instance_extensions,
150                 max_api_version: Some(Version::V1_1),
151                 ..Default::default()
152             },
153         )?;
154 
155         let mut devices: Map<PhysicalDeviceType, Arc<Device>> = Default::default();
156         let mut device_by_id: Map<DeviceId, Arc<Device>> = Default::default();
157         let mut has_integrated_gpu = false;
158 
159         for physical in instance.enumerate_physical_devices()? {
160             let queue_family_index = match physical.queue_family_properties().iter().position(|q| {
161                 // We take the first queue family that supports graphics.
162                 q.queue_flags.intersects(QueueFlags::GRAPHICS)
163             }) {
164                 Some(family) => family,
165                 None => {
166                     warn!(
167                         "Skipping Vulkano for {} due to no graphics queue",
168                         physical.properties().device_name
169                     );
170                     continue;
171                 }
172             };
173 
174             let supported_extensions = physical.supported_extensions();
175 
176             let desired_extensions = Self::get_desired_device_extensions();
177 
178             let intersection = supported_extensions.intersection(&desired_extensions);
179 
180             if let Ok((device, mut _queues)) = Device::new(
181                 physical.clone(),
182                 DeviceCreateInfo {
183                     enabled_extensions: intersection,
184                     queue_create_infos: vec![QueueCreateInfo {
185                         queue_family_index: queue_family_index as u32,
186                         ..Default::default()
187                     }],
188                     ..Default::default()
189                 },
190             ) {
191                 let device_type = device.physical_device().properties().device_type;
192                 if device_type == PhysicalDeviceType::IntegratedGpu {
193                     has_integrated_gpu = true
194                 }
195 
196                 // If we have two devices of the same type (two integrated GPUs), the old value is
197                 // dropped.  Vulkano is verbose enough such that a keener selection algorithm may
198                 // be used, but the need for such complexity does not seem to exist now.
199                 devices.insert(device_type, device.clone());
200                 device_by_id.insert(device.get_id(), device);
201             };
202         }
203 
204         if devices.is_empty() {
205             return Err(RutabagaError::SpecViolation(
206                 "no matching VK devices available",
207             ));
208         }
209 
210         Ok(Box::new(VulkanoGralloc {
211             devices,
212             device_by_id,
213             has_integrated_gpu,
214         }))
215     }
216 
217     // This function is used safely in this module because gralloc does not:
218     //
219     //  (1) bind images to any memory.
220     //  (2) transition the layout of images.
221     //  (3) transfer ownership of images between queues.
222     //
223     // In addition, we trust Vulkano to validate image parameters are within the Vulkan spec.
224     // TODO(tutankhamen): Do we still need a separate MemoryRequirements?
create_image( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<(Arc<image::sys::RawImage>, MemoryRequirements)>225     unsafe fn create_image(
226         &mut self,
227         info: ImageAllocationInfo,
228     ) -> RutabagaResult<(Arc<image::sys::RawImage>, MemoryRequirements)> {
229         let device = if self.has_integrated_gpu {
230             self.devices
231                 .get(&PhysicalDeviceType::IntegratedGpu)
232                 .ok_or(RutabagaError::InvalidGrallocGpuType)?
233         } else {
234             self.devices
235                 .get(&PhysicalDeviceType::DiscreteGpu)
236                 .ok_or(RutabagaError::InvalidGrallocGpuType)?
237         };
238 
239         let usage = match info.flags.uses_rendering() {
240             true => ImageUsage::COLOR_ATTACHMENT,
241             false => ImageUsage::SAMPLED,
242         };
243 
244         // Reasonable bounds on image width.
245         if info.width == 0 || info.width > 4096 {
246             return Err(RutabagaError::InvalidGrallocDimensions);
247         }
248 
249         // Reasonable bounds on image height.
250         if info.height == 0 || info.height > 4096 {
251             return Err(RutabagaError::InvalidGrallocDimensions);
252         }
253 
254         let vulkan_format = info.drm_format.vulkan_format()?;
255         let raw_image = Arc::new(image::sys::RawImage::new(
256             device.clone(),
257             image::sys::ImageCreateInfo {
258                 dimensions: ImageDimensions::Dim2d {
259                     width: info.width,
260                     height: info.height,
261                     array_layers: 1,
262                 },
263                 format: Some(vulkan_format),
264                 samples: SampleCount::Sample1,
265                 usage,
266                 mip_levels: 1,
267                 sharing: Sharing::Exclusive,
268                 ..Default::default()
269             },
270         )?);
271 
272         // Won't panic since this not a swapchain image.
273         let memory_requirements = raw_image.memory_requirements()[0];
274         Ok((raw_image, memory_requirements))
275     }
276 }
277 
278 impl Gralloc for VulkanoGralloc {
supports_external_gpu_memory(&self) -> bool279     fn supports_external_gpu_memory(&self) -> bool {
280         for device in self.devices.values() {
281             if !device.enabled_extensions().khr_external_memory {
282                 return false;
283             }
284         }
285 
286         true
287     }
288 
supports_dmabuf(&self) -> bool289     fn supports_dmabuf(&self) -> bool {
290         for device in self.devices.values() {
291             if !device.enabled_extensions().ext_external_memory_dma_buf {
292                 return false;
293             }
294         }
295 
296         true
297     }
298 
get_image_memory_requirements( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<ImageMemoryRequirements>299     fn get_image_memory_requirements(
300         &mut self,
301         info: ImageAllocationInfo,
302     ) -> RutabagaResult<ImageMemoryRequirements> {
303         let mut reqs: ImageMemoryRequirements = Default::default();
304 
305         let (raw_image, memory_requirements) = unsafe { self.create_image(info)? };
306 
307         let device_type = if self.has_integrated_gpu {
308             &PhysicalDeviceType::IntegratedGpu
309         } else {
310             &PhysicalDeviceType::DiscreteGpu
311         };
312 
313         let device = self
314             .devices
315             .get(device_type)
316             .ok_or(RutabagaError::InvalidGrallocGpuType)?;
317 
318         let planar_layout = info.drm_format.planar_layout()?;
319 
320         // Safe because we created the image with the linear bit set and verified the format is
321         // not a depth or stencil format.  We are also using the correct image aspect.  Vulkano
322         // will panic if we are not.
323         for plane in 0..planar_layout.num_planes {
324             let aspect = info.drm_format.vulkan_image_aspect(plane)?;
325             let layout = raw_image.subresource_layout(aspect, 0, 0)?;
326             reqs.strides[plane] = layout.row_pitch as u32;
327             reqs.offsets[plane] = layout.offset as u32;
328         }
329 
330         let need_visible = info.flags.host_visible();
331         let want_cached = info.flags.host_cached();
332 
333         let (memory_type_index, memory_type) = {
334             let filter = |current_type: &MemoryType| {
335                 if need_visible
336                     && !current_type
337                         .property_flags
338                         .intersects(MemoryPropertyFlags::HOST_VISIBLE)
339                 {
340                     return AllocFromRequirementsFilter::Forbidden;
341                 }
342 
343                 if !need_visible
344                     && current_type
345                         .property_flags
346                         .intersects(MemoryPropertyFlags::DEVICE_LOCAL)
347                 {
348                     return AllocFromRequirementsFilter::Preferred;
349                 }
350 
351                 if need_visible
352                     && want_cached
353                     && current_type
354                         .property_flags
355                         .intersects(MemoryPropertyFlags::HOST_CACHED)
356                 {
357                     return AllocFromRequirementsFilter::Preferred;
358                 }
359 
360                 if need_visible
361                     && !want_cached
362                     && current_type
363                         .property_flags
364                         .intersects(MemoryPropertyFlags::HOST_COHERENT)
365                     && !current_type
366                         .property_flags
367                         .intersects(MemoryPropertyFlags::HOST_CACHED)
368                 {
369                     return AllocFromRequirementsFilter::Preferred;
370                 }
371 
372                 AllocFromRequirementsFilter::Allowed
373             };
374 
375             let first_loop = device
376                 .physical_device()
377                 .memory_properties()
378                 .memory_types
379                 .iter()
380                 .enumerate()
381                 .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Preferred));
382             let second_loop = device
383                 .physical_device()
384                 .memory_properties()
385                 .memory_types
386                 .iter()
387                 .enumerate()
388                 .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Allowed));
389             let found_type = first_loop
390                 .chain(second_loop)
391                 .filter(|&(i, _, _)| (memory_requirements.memory_type_bits & (1 << i)) != 0)
392                 .find(|&(_, t, rq)| filter(t) == rq)
393                 .ok_or(RutabagaError::SpecViolation(
394                     "unable to find required memory type",
395                 ))?;
396             (found_type.0, found_type.1)
397         };
398 
399         reqs.info = info;
400         reqs.size = memory_requirements.layout.size() as u64;
401 
402         if memory_type
403             .property_flags
404             .intersects(MemoryPropertyFlags::HOST_VISIBLE)
405         {
406             if memory_type
407                 .property_flags
408                 .intersects(MemoryPropertyFlags::HOST_CACHED)
409             {
410                 reqs.map_info = RUTABAGA_MAP_CACHE_CACHED;
411             } else if memory_type
412                 .property_flags
413                 .intersects(MemoryPropertyFlags::HOST_COHERENT)
414             {
415                 reqs.map_info = RUTABAGA_MAP_CACHE_WC;
416             }
417         }
418 
419         reqs.vulkan_info = Some(VulkanInfo {
420             memory_idx: memory_type_index as u32,
421             device_id: device.get_id(),
422         });
423 
424         Ok(reqs)
425     }
426 
allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>427     fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> {
428         let (raw_image, memory_requirements) = unsafe { self.create_image(reqs.info)? };
429 
430         let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)?;
431 
432         let device = if self.has_integrated_gpu {
433             self.devices
434                 .get(&PhysicalDeviceType::IntegratedGpu)
435                 .ok_or(RutabagaError::InvalidGrallocGpuType)?
436         } else {
437             self.devices
438                 .get(&PhysicalDeviceType::DiscreteGpu)
439                 .ok_or(RutabagaError::InvalidGrallocGpuType)?
440         };
441 
442         if vulkan_info.memory_idx as usize
443             >= device
444                 .physical_device()
445                 .memory_properties()
446                 .memory_types
447                 .len()
448         {
449             return Err(RutabagaError::InvalidVulkanInfo);
450         }
451 
452         let (export_handle_type, export_handle_types, rutabaga_type) =
453             match device.enabled_extensions().ext_external_memory_dma_buf {
454                 true => (
455                     ExternalMemoryHandleType::DmaBuf,
456                     ExternalMemoryHandleTypes::DMA_BUF,
457                     RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
458                 ),
459                 false => (
460                     ExternalMemoryHandleType::OpaqueFd,
461                     ExternalMemoryHandleTypes::OPAQUE_FD,
462                     RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD,
463                 ),
464             };
465 
466         let dedicated_allocation = match device.enabled_extensions().khr_dedicated_allocation {
467             true => {
468                 if memory_requirements.prefers_dedicated_allocation {
469                     Some(DedicatedAllocation::Image(&raw_image))
470                 } else {
471                     None
472                 }
473             }
474             false => None,
475         };
476 
477         let device_memory = DeviceMemory::allocate(
478             device.clone(),
479             MemoryAllocateInfo {
480                 allocation_size: reqs.size,
481                 memory_type_index: vulkan_info.memory_idx,
482                 dedicated_allocation,
483                 export_handle_types,
484                 ..Default::default()
485             },
486         )?;
487 
488         let descriptor = device_memory.export_fd(export_handle_type)?.into();
489 
490         Ok(RutabagaHandle {
491             os_handle: descriptor,
492             handle_type: rutabaga_type,
493         })
494     }
495 
496     /// Implementations must map the memory associated with the `resource_id` upon success.
import_and_map( &mut self, handle: RutabagaHandle, vulkan_info: VulkanInfo, size: u64, ) -> RutabagaResult<Box<dyn MappedRegion>>497     fn import_and_map(
498         &mut self,
499         handle: RutabagaHandle,
500         vulkan_info: VulkanInfo,
501         size: u64,
502     ) -> RutabagaResult<Box<dyn MappedRegion>> {
503         let device = self
504             .device_by_id
505             .get(&vulkan_info.device_id)
506             .ok_or(RutabagaError::InvalidVulkanInfo)?;
507 
508         let device_memory = unsafe {
509             VulkanoGralloc::import_memory(
510                 device.clone(),
511                 MemoryAllocateInfo {
512                     allocation_size: size,
513                     memory_type_index: vulkan_info.memory_idx,
514                     ..Default::default()
515                 },
516                 handle,
517             )?
518         };
519 
520         let mapped_memory = MappedDeviceMemory::new(device_memory, 0..size)?;
521 
522         Ok(Box::new(VulkanoMapping::new(
523             mapped_memory,
524             size.try_into()?,
525         )))
526     }
527 }
528 
529 // Vulkano should really define an universal type that wraps all these errors, say
530 // "VulkanoError(e)".
531 impl From<InstanceCreationError> for RutabagaError {
from(e: InstanceCreationError) -> RutabagaError532     fn from(e: InstanceCreationError) -> RutabagaError {
533         RutabagaError::VkInstanceCreationError(e)
534     }
535 }
536 
537 impl From<ImageError> for RutabagaError {
from(e: ImageError) -> RutabagaError538     fn from(e: ImageError) -> RutabagaError {
539         RutabagaError::VkImageCreationError(e)
540     }
541 }
542 
543 impl From<DeviceCreationError> for RutabagaError {
from(e: DeviceCreationError) -> RutabagaError544     fn from(e: DeviceCreationError) -> RutabagaError {
545         RutabagaError::VkDeviceCreationError(e)
546     }
547 }
548 
549 impl From<DeviceMemoryError> for RutabagaError {
from(e: DeviceMemoryError) -> RutabagaError550     fn from(e: DeviceMemoryError) -> RutabagaError {
551         RutabagaError::VkDeviceMemoryError(e)
552     }
553 }
554 
555 impl From<MemoryMapError> for RutabagaError {
from(e: MemoryMapError) -> RutabagaError556     fn from(e: MemoryMapError) -> RutabagaError {
557         RutabagaError::VkMemoryMapError(e)
558     }
559 }
560 
561 impl From<LoadingError> for RutabagaError {
from(e: LoadingError) -> RutabagaError562     fn from(e: LoadingError) -> RutabagaError {
563         RutabagaError::VkLoadingError(e)
564     }
565 }
566 
567 impl From<VulkanError> for RutabagaError {
from(e: VulkanError) -> RutabagaError568     fn from(e: VulkanError) -> RutabagaError {
569         RutabagaError::VkError(e)
570     }
571 }
572