• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
104 trait DeviceExt {
105     /// Get a unique identifier for the device.
get_id(&self) -> DeviceId106     fn get_id(&self) -> DeviceId;
107 }
108 
109 impl DeviceExt for Device {
get_id(&self) -> DeviceId110     fn get_id(&self) -> DeviceId {
111         let properties = self.physical_device().properties();
112         DeviceId {
113             device_uuid: properties.device_uuid.expect("Vulkan should support uuid"),
114             driver_uuid: properties.driver_uuid.expect("Vulkan should support uuid"),
115         }
116     }
117 }
118 
119 impl VulkanoGralloc {
120     /// Returns a new `VulkanGralloc' instance upon success.
init() -> RutabagaResult<Box<dyn Gralloc>>121     pub fn init() -> RutabagaResult<Box<dyn Gralloc>> {
122         // Initialization copied from triangle.rs in Vulkano.  Look there for a more detailed
123         // explanation of VK initialization.
124         let library = VulkanLibrary::new()?;
125 
126         let instance_extensions = InstanceExtensions {
127             khr_external_memory_capabilities: true,
128             khr_get_physical_device_properties2: true,
129             ..InstanceExtensions::empty()
130         };
131         let instance = Instance::new(
132             library,
133             InstanceCreateInfo {
134                 enabled_extensions: instance_extensions,
135                 max_api_version: Some(Version::V1_1),
136                 ..Default::default()
137             },
138         )?;
139 
140         let mut devices: Map<PhysicalDeviceType, Arc<Device>> = Default::default();
141         let mut device_by_id: Map<DeviceId, Arc<Device>> = Default::default();
142         let mut has_integrated_gpu = false;
143 
144         for physical in instance.enumerate_physical_devices()? {
145             let queue_family_index = match physical.queue_family_properties().iter().position(|q| {
146                 // We take the first queue family that supports graphics.
147                 q.queue_flags.intersects(QueueFlags::GRAPHICS)
148             }) {
149                 Some(family) => family,
150                 None => {
151                     warn!(
152                         "Skipping Vulkano for {} due to no graphics queue",
153                         physical.properties().device_name
154                     );
155                     continue;
156                 }
157             };
158 
159             let supported_extensions = physical.supported_extensions();
160 
161             let desired_extensions = Self::get_desired_device_extensions();
162 
163             let intersection = supported_extensions.intersection(&desired_extensions);
164 
165             if let Ok((device, mut _queues)) = Device::new(
166                 physical.clone(),
167                 DeviceCreateInfo {
168                     enabled_extensions: intersection,
169                     queue_create_infos: vec![QueueCreateInfo {
170                         queue_family_index: queue_family_index as u32,
171                         ..Default::default()
172                     }],
173                     ..Default::default()
174                 },
175             ) {
176                 let device_type = device.physical_device().properties().device_type;
177                 if device_type == PhysicalDeviceType::IntegratedGpu {
178                     has_integrated_gpu = true
179                 }
180 
181                 // If we have two devices of the same type (two integrated GPUs), the old value is
182                 // dropped.  Vulkano is verbose enough such that a keener selection algorithm may
183                 // be used, but the need for such complexity does not seem to exist now.
184                 devices.insert(device_type, device.clone());
185                 device_by_id.insert(device.get_id(), device);
186             };
187         }
188 
189         if devices.is_empty() {
190             return Err(RutabagaError::SpecViolation(
191                 "no matching VK devices available",
192             ));
193         }
194 
195         Ok(Box::new(VulkanoGralloc {
196             devices,
197             device_by_id,
198             has_integrated_gpu,
199         }))
200     }
201 
202     // This function is used safely in this module because gralloc does not:
203     //
204     //  (1) bind images to any memory.
205     //  (2) transition the layout of images.
206     //  (3) transfer ownership of images between queues.
207     //
208     // In addition, we trust Vulkano to validate image parameters are within the Vulkan spec.
209     // TODO(tutankhamen): Do we still need a separate MemoryRequirements?
create_image( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<(Arc<image::sys::RawImage>, MemoryRequirements)>210     unsafe fn create_image(
211         &mut self,
212         info: ImageAllocationInfo,
213     ) -> RutabagaResult<(Arc<image::sys::RawImage>, MemoryRequirements)> {
214         let device = if self.has_integrated_gpu {
215             self.devices
216                 .get(&PhysicalDeviceType::IntegratedGpu)
217                 .ok_or(RutabagaError::InvalidGrallocGpuType)?
218         } else {
219             self.devices
220                 .get(&PhysicalDeviceType::DiscreteGpu)
221                 .ok_or(RutabagaError::InvalidGrallocGpuType)?
222         };
223 
224         let usage = match info.flags.uses_rendering() {
225             true => ImageUsage::COLOR_ATTACHMENT,
226             false => ImageUsage::SAMPLED,
227         };
228 
229         // Reasonable bounds on image width.
230         if info.width == 0 || info.width > 4096 {
231             return Err(RutabagaError::InvalidGrallocDimensions);
232         }
233 
234         // Reasonable bounds on image height.
235         if info.height == 0 || info.height > 4096 {
236             return Err(RutabagaError::InvalidGrallocDimensions);
237         }
238 
239         let vulkan_format = info.drm_format.vulkan_format()?;
240         let raw_image = Arc::new(image::sys::RawImage::new(
241             device.clone(),
242             image::sys::ImageCreateInfo {
243                 dimensions: ImageDimensions::Dim2d {
244                     width: info.width,
245                     height: info.height,
246                     array_layers: 1,
247                 },
248                 format: Some(vulkan_format),
249                 samples: SampleCount::Sample1,
250                 usage,
251                 mip_levels: 1,
252                 sharing: Sharing::Exclusive,
253                 ..Default::default()
254             },
255         )?);
256 
257         // Won't panic since this not a swapchain image.
258         let memory_requirements = raw_image.memory_requirements()[0];
259         Ok((raw_image, memory_requirements))
260     }
261 }
262 
263 impl Gralloc for VulkanoGralloc {
supports_external_gpu_memory(&self) -> bool264     fn supports_external_gpu_memory(&self) -> bool {
265         for device in self.devices.values() {
266             if !device.enabled_extensions().khr_external_memory {
267                 return false;
268             }
269         }
270 
271         true
272     }
273 
supports_dmabuf(&self) -> bool274     fn supports_dmabuf(&self) -> bool {
275         for device in self.devices.values() {
276             if !device.enabled_extensions().ext_external_memory_dma_buf {
277                 return false;
278             }
279         }
280 
281         true
282     }
283 
get_image_memory_requirements( &mut self, info: ImageAllocationInfo, ) -> RutabagaResult<ImageMemoryRequirements>284     fn get_image_memory_requirements(
285         &mut self,
286         info: ImageAllocationInfo,
287     ) -> RutabagaResult<ImageMemoryRequirements> {
288         let mut reqs: ImageMemoryRequirements = Default::default();
289 
290         let (raw_image, memory_requirements) = unsafe { self.create_image(info)? };
291 
292         let device_type = if self.has_integrated_gpu {
293             &PhysicalDeviceType::IntegratedGpu
294         } else {
295             &PhysicalDeviceType::DiscreteGpu
296         };
297 
298         let device = self
299             .devices
300             .get(device_type)
301             .ok_or(RutabagaError::InvalidGrallocGpuType)?;
302 
303         let planar_layout = info.drm_format.planar_layout()?;
304 
305         // Safe because we created the image with the linear bit set and verified the format is
306         // not a depth or stencil format.  We are also using the correct image aspect.  Vulkano
307         // will panic if we are not.
308         for plane in 0..planar_layout.num_planes {
309             let aspect = info.drm_format.vulkan_image_aspect(plane)?;
310             let layout = raw_image.subresource_layout(aspect, 0, 0)?;
311             reqs.strides[plane] = layout.row_pitch as u32;
312             reqs.offsets[plane] = layout.offset as u32;
313         }
314 
315         let need_visible = info.flags.host_visible();
316         let want_cached = info.flags.host_cached();
317 
318         let (memory_type_index, memory_type) = {
319             let filter = |current_type: &MemoryType| {
320                 if need_visible
321                     && !current_type
322                         .property_flags
323                         .intersects(MemoryPropertyFlags::HOST_VISIBLE)
324                 {
325                     return AllocFromRequirementsFilter::Forbidden;
326                 }
327 
328                 if !need_visible
329                     && current_type
330                         .property_flags
331                         .intersects(MemoryPropertyFlags::DEVICE_LOCAL)
332                 {
333                     return AllocFromRequirementsFilter::Preferred;
334                 }
335 
336                 if need_visible
337                     && want_cached
338                     && current_type
339                         .property_flags
340                         .intersects(MemoryPropertyFlags::HOST_CACHED)
341                 {
342                     return AllocFromRequirementsFilter::Preferred;
343                 }
344 
345                 if need_visible
346                     && !want_cached
347                     && current_type
348                         .property_flags
349                         .intersects(MemoryPropertyFlags::HOST_COHERENT)
350                     && !current_type
351                         .property_flags
352                         .intersects(MemoryPropertyFlags::HOST_CACHED)
353                 {
354                     return AllocFromRequirementsFilter::Preferred;
355                 }
356 
357                 AllocFromRequirementsFilter::Allowed
358             };
359 
360             let first_loop = device
361                 .physical_device()
362                 .memory_properties()
363                 .memory_types
364                 .iter()
365                 .enumerate()
366                 .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Preferred));
367             let second_loop = device
368                 .physical_device()
369                 .memory_properties()
370                 .memory_types
371                 .iter()
372                 .enumerate()
373                 .map(|(i, t)| (i, t, AllocFromRequirementsFilter::Allowed));
374             let found_type = first_loop
375                 .chain(second_loop)
376                 .filter(|&(i, _, _)| (memory_requirements.memory_type_bits & (1 << i)) != 0)
377                 .find(|&(_, t, rq)| filter(t) == rq)
378                 .ok_or(RutabagaError::SpecViolation(
379                     "unable to find required memory type",
380                 ))?;
381             (found_type.0, found_type.1)
382         };
383 
384         reqs.info = info;
385         reqs.size = memory_requirements.layout.size() as u64;
386 
387         if memory_type
388             .property_flags
389             .intersects(MemoryPropertyFlags::HOST_VISIBLE)
390         {
391             if memory_type
392                 .property_flags
393                 .intersects(MemoryPropertyFlags::HOST_CACHED)
394             {
395                 reqs.map_info = RUTABAGA_MAP_CACHE_CACHED;
396             } else if memory_type
397                 .property_flags
398                 .intersects(MemoryPropertyFlags::HOST_COHERENT)
399             {
400                 reqs.map_info = RUTABAGA_MAP_CACHE_WC;
401             }
402         }
403 
404         reqs.vulkan_info = Some(VulkanInfo {
405             memory_idx: memory_type_index as u32,
406             device_id: device.get_id(),
407         });
408 
409         Ok(reqs)
410     }
411 
allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle>412     fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> {
413         let (raw_image, memory_requirements) = unsafe { self.create_image(reqs.info)? };
414 
415         let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)?;
416 
417         let device = if self.has_integrated_gpu {
418             self.devices
419                 .get(&PhysicalDeviceType::IntegratedGpu)
420                 .ok_or(RutabagaError::InvalidGrallocGpuType)?
421         } else {
422             self.devices
423                 .get(&PhysicalDeviceType::DiscreteGpu)
424                 .ok_or(RutabagaError::InvalidGrallocGpuType)?
425         };
426 
427         if vulkan_info.memory_idx as usize
428             >= device
429                 .physical_device()
430                 .memory_properties()
431                 .memory_types
432                 .len()
433         {
434             return Err(RutabagaError::InvalidVulkanInfo);
435         }
436 
437         let (export_handle_type, export_handle_types, rutabaga_type) =
438             match device.enabled_extensions().ext_external_memory_dma_buf {
439                 true => (
440                     ExternalMemoryHandleType::DmaBuf,
441                     ExternalMemoryHandleTypes::DMA_BUF,
442                     RUTABAGA_MEM_HANDLE_TYPE_DMABUF,
443                 ),
444                 false => (
445                     ExternalMemoryHandleType::OpaqueFd,
446                     ExternalMemoryHandleTypes::OPAQUE_FD,
447                     RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD,
448                 ),
449             };
450 
451         let dedicated_allocation = match device.enabled_extensions().khr_dedicated_allocation {
452             true => {
453                 if memory_requirements.prefers_dedicated_allocation {
454                     Some(DedicatedAllocation::Image(&raw_image))
455                 } else {
456                     None
457                 }
458             }
459             false => None,
460         };
461 
462         let device_memory = DeviceMemory::allocate(
463             device.clone(),
464             MemoryAllocateInfo {
465                 allocation_size: reqs.size,
466                 memory_type_index: vulkan_info.memory_idx,
467                 dedicated_allocation,
468                 export_handle_types,
469                 ..Default::default()
470             },
471         )?;
472 
473         let descriptor = device_memory.export_fd(export_handle_type)?.into();
474 
475         Ok(RutabagaHandle {
476             os_handle: descriptor,
477             handle_type: rutabaga_type,
478         })
479     }
480 
481     /// 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>>482     fn import_and_map(
483         &mut self,
484         handle: RutabagaHandle,
485         vulkan_info: VulkanInfo,
486         size: u64,
487     ) -> RutabagaResult<Box<dyn MappedRegion>> {
488         let device = self
489             .device_by_id
490             .get(&vulkan_info.device_id)
491             .ok_or(RutabagaError::InvalidVulkanInfo)?;
492 
493         let device_memory = unsafe {
494             VulkanoGralloc::import_memory(
495                 device.clone(),
496                 MemoryAllocateInfo {
497                     allocation_size: size,
498                     memory_type_index: vulkan_info.memory_idx,
499                     ..Default::default()
500                 },
501                 handle,
502             )?
503         };
504 
505         let mapped_memory = MappedDeviceMemory::new(device_memory, 0..size)?;
506 
507         Ok(Box::new(VulkanoMapping::new(
508             mapped_memory,
509             size.try_into()?,
510         )))
511     }
512 }
513 
514 // Vulkano should really define an universal type that wraps all these errors, say
515 // "VulkanoError(e)".
516 impl From<InstanceCreationError> for RutabagaError {
from(e: InstanceCreationError) -> RutabagaError517     fn from(e: InstanceCreationError) -> RutabagaError {
518         RutabagaError::VkInstanceCreationError(e)
519     }
520 }
521 
522 impl From<ImageError> for RutabagaError {
from(e: ImageError) -> RutabagaError523     fn from(e: ImageError) -> RutabagaError {
524         RutabagaError::VkImageCreationError(e)
525     }
526 }
527 
528 impl From<DeviceCreationError> for RutabagaError {
from(e: DeviceCreationError) -> RutabagaError529     fn from(e: DeviceCreationError) -> RutabagaError {
530         RutabagaError::VkDeviceCreationError(e)
531     }
532 }
533 
534 impl From<DeviceMemoryError> for RutabagaError {
from(e: DeviceMemoryError) -> RutabagaError535     fn from(e: DeviceMemoryError) -> RutabagaError {
536         RutabagaError::VkDeviceMemoryError(e)
537     }
538 }
539 
540 impl From<MemoryMapError> for RutabagaError {
from(e: MemoryMapError) -> RutabagaError541     fn from(e: MemoryMapError) -> RutabagaError {
542         RutabagaError::VkMemoryMapError(e)
543     }
544 }
545 
546 impl From<LoadingError> for RutabagaError {
from(e: LoadingError) -> RutabagaError547     fn from(e: LoadingError) -> RutabagaError {
548         RutabagaError::VkLoadingError(e)
549     }
550 }
551 
552 impl From<VulkanError> for RutabagaError {
from(e: VulkanError) -> RutabagaError553     fn from(e: VulkanError) -> RutabagaError {
554         RutabagaError::VkError(e)
555     }
556 }
557