diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index 0ae07273..cfee2951 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -19,7 +19,7 @@ use crate::{ }, instance::Instance, macros::{impl_id_counter, vulkan_bitflags, vulkan_enum}, - memory::MemoryProperties, + memory::{ExternalMemoryHandleType, MemoryProperties}, swapchain::{ ColorSpace, FullScreenExclusive, PresentMode, Surface, SurfaceApi, SurfaceCapabilities, SurfaceInfo, SurfaceTransforms, @@ -1023,6 +1023,9 @@ impl PhysicalDevice { image_view_type.validate_physical_device(self)?; } + // TODO: VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02313 + // Currently there is nothing in Vulkano for for adding a VkImageFormatListCreateInfo. + Ok(()) } @@ -1158,6 +1161,12 @@ impl PhysicalDevice { if !info2_vk.p_next.is_null() { return Ok(None); } + if let Some(ExternalMemoryHandleType::DmaBuf) = external_memory_handle_type + { + // VUID-vkGetPhysicalDeviceImageFormatProperties-tiling-02248 + // VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02249 + return Ok(None); + } (fns.v1_0.get_physical_device_image_format_properties)( self.handle, diff --git a/vulkano/src/image/mod.rs b/vulkano/src/image/mod.rs index 07e23065..6b8c2b64 100644 --- a/vulkano/src/image/mod.rs +++ b/vulkano/src/image/mod.rs @@ -58,6 +58,10 @@ pub use self::{ usage::ImageUsage, view::{ImageViewAbstract, ImageViewType}, }; + +#[cfg(target_os = "linux")] +pub use self::storage::SubresourceData; + use crate::{ format::Format, macros::{vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum}, @@ -367,11 +371,10 @@ vulkan_enum! { // TODO: document Linear = LINEAR, - /* TODO: enable // TODO: document DrmFormatModifier = DRM_FORMAT_MODIFIER_EXT { device_extensions: [ext_image_drm_format_modifier], - },*/ + }, } /// The dimensions of an image. diff --git a/vulkano/src/image/storage.rs b/vulkano/src/image/storage.rs index 42ff167a..27094f1e 100644 --- a/vulkano/src/image/storage.rs +++ b/vulkano/src/image/storage.rs @@ -29,6 +29,17 @@ use crate::{ DeviceSize, }; use smallvec::SmallVec; + +#[cfg(target_os = "linux")] +use crate::{ + image::ImageTiling, + memory::{allocator::MemoryAlloc, DeviceMemory, MemoryAllocateFlags, MemoryAllocateInfo}, +}; +#[cfg(target_os = "linux")] +use ash::vk::{ImageDrmFormatModifierExplicitCreateInfoEXT, SubresourceLayout}; +#[cfg(target_os = "linux")] +use std::os::unix::prelude::{FromRawFd, IntoRawFd, RawFd}; + use std::{ fs::File, hash::{Hash, Hasher}, @@ -223,6 +234,146 @@ impl StorageImage { } } + #[cfg(target_os = "linux")] + /// Creates a new image from a set of Linux dma_buf file descriptors. The memory will be imported from the file desciptors, and will be bound to the image. + /// # Arguments + /// * `fds` - The list of file descriptors to import from. Single planar images should only use one, and multiplanar images can use multiple, for example, for each color. + /// * `offset` - The byte offset from the start of the image of the plane where the image subresource begins. + /// * `pitch` - Describes the number of bytes between each row of texels in an image. + pub fn new_from_dma_buf_fd( + allocator: &(impl MemoryAllocator + ?Sized), + device: Arc, + dimensions: ImageDimensions, + format: Format, + usage: ImageUsage, + flags: ImageCreateFlags, + queue_family_indices: impl IntoIterator, + mut subresource_data: Vec, + drm_format_modifier: u64, + ) -> Result, ImageError> { + let queue_family_indices: SmallVec<[_; 4]> = queue_family_indices.into_iter().collect(); + + // TODO: Support multiplanar image importing from Linux FD + if subresource_data.len() > 1 { + todo!(); + } + + // Create a vector of the layout of each image plane. + + // All of the following are automatically true, since the values are explicitly set as such: + // VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-size-02267 + // VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-arrayPitch-02268 + // VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-depthPitch-02269 + let layout: Vec = subresource_data + .iter_mut() + .map( + |SubresourceData { + fd: _, + offset, + row_pitch, + }| { + SubresourceLayout { + offset: *offset, + size: 0, + row_pitch: *row_pitch, + array_pitch: 0_u64, + depth_pitch: 0_u64, + } + }, + ) + .collect(); + + let fds: Vec = subresource_data + .iter_mut() + .map( + |SubresourceData { + fd, + offset: _, + row_pitch: _, + }| { *fd }, + ) + .collect(); + + let drm_mod = ImageDrmFormatModifierExplicitCreateInfoEXT::builder() + .drm_format_modifier(drm_format_modifier) + .plane_layouts(layout.as_ref()) + .build(); + + let external_memory_handle_types = ExternalMemoryHandleTypes::DMA_BUF; + + let image = RawImage::new( + device.clone(), + ImageCreateInfo { + flags, + dimensions, + format: Some(format), + usage, + sharing: if queue_family_indices.len() >= 2 { + Sharing::Concurrent(queue_family_indices) + } else { + Sharing::Exclusive + }, + external_memory_handle_types, + tiling: ImageTiling::DrmFormatModifier, + image_drm_format_modifier_create_info: Some(drm_mod), + ..Default::default() + }, + )?; + + let requirements = image.memory_requirements()[0]; + let memory_type_index = allocator + .find_memory_type_index(requirements.memory_type_bits, MemoryUsage::GpuOnly.into()) + .expect("failed to find a suitable memory type"); + + assert!(device.enabled_extensions().khr_external_memory_fd); + assert!(device.enabled_extensions().khr_external_memory); + assert!(device.enabled_extensions().ext_external_memory_dma_buf); + + let memory = unsafe { + // TODO: For completeness, importing memory from muliple file descriptors should be added (In order to support importing multiplanar images). As of now, only single planar image importing will work. + if fds.len() != 1 { + todo!(); + } + + // Try cloning underlying fd + let file = File::from_raw_fd(*fds.first().expect("file descriptor Vec is empty")); + let new_file = file.try_clone().expect("error cloning file descriptor"); + + // Turn the original file descriptor back into a raw fd to avoid ownership problems + file.into_raw_fd(); + DeviceMemory::import( + device, + MemoryAllocateInfo { + allocation_size: requirements.layout.size(), + memory_type_index, + dedicated_allocation: Some(DedicatedAllocation::Image(&image)), + export_handle_types: ExternalMemoryHandleTypes::empty(), + flags: MemoryAllocateFlags::empty(), + ..Default::default() + }, + crate::memory::MemoryImportInfo::Fd { + handle_type: crate::memory::ExternalMemoryHandleType::DmaBuf, + file: new_file, + }, + ) + .unwrap() // TODO: Handle + }; + + let mem_alloc = MemoryAlloc::new(memory).unwrap(); + + debug_assert!(mem_alloc.offset() % requirements.layout.alignment().as_nonzero() == 0); + debug_assert!(mem_alloc.size() == requirements.layout.size()); + + let inner = Arc::new(unsafe { + image + .bind_memory_unchecked([mem_alloc]) + .map_err(|(err, _, _)| err)? + }); + Ok(Arc::new(StorageImage { + inner, + layout_initialized: AtomicBool::new(false), + })) + } /// Allows the creation of a simple 2D general purpose image view from `StorageImage`. #[inline] pub fn general_purpose_image_view( @@ -285,6 +436,20 @@ impl StorageImage { } } +#[cfg(target_os = "linux")] +/// Struct that contains a Linux file descriptor for importing, when creating an image. Since a file descriptor is used for each +/// plane in the case of multiplanar images, each fd needs to have an offset and a row pitch in order to interpret the imported data. +pub struct SubresourceData { + /// The file descriptor handle of a layer of an image. + pub fd: RawFd, + + /// The byte offset from the start of the plane where the image subresource begins. + pub offset: u64, + + /// Describes the number of bytes between each row of texels in an image plane. + pub row_pitch: u64, +} + unsafe impl DeviceOwned for StorageImage { #[inline] fn device(&self) -> &Arc { diff --git a/vulkano/src/image/sys.rs b/vulkano/src/image/sys.rs index 9af0e6f5..1933ffee 100644 --- a/vulkano/src/image/sys.rs +++ b/vulkano/src/image/sys.rs @@ -38,6 +38,7 @@ use crate::{ sync::{future::AccessError, CurrentAccess, Sharing}, DeviceSize, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; +use ash::vk::ImageDrmFormatModifierExplicitCreateInfoEXT; use parking_lot::{Mutex, MutexGuard}; use smallvec::{smallvec, SmallVec}; use std::{ @@ -134,6 +135,7 @@ impl RawImage { initial_layout, external_memory_handle_types, _ne: _, + image_drm_format_modifier_create_info, } = create_info; let physical_device = device.physical_device(); @@ -206,6 +208,14 @@ impl RawImage { || flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) ); + // VUID-VkImageCreateInfo-tiling-02261 + // VUID-VkImageCreateInfo-pNext-02262 + if (tiling == ImageTiling::DrmFormatModifier) + != image_drm_format_modifier_create_info.is_some() + { + return Err(ImageError::DrmFormatModifierRequiresCreateInfo); + } + // Get format features let format_features = { // Use unchecked, because all validation has been done above. @@ -213,9 +223,13 @@ impl RawImage { match tiling { ImageTiling::Linear => format_properties.linear_tiling_features, ImageTiling::Optimal => format_properties.optimal_tiling_features, + ImageTiling::DrmFormatModifier => format_properties.linear_tiling_features, // TODO: Improve } }; + // TODO: VUID-VkImageCreateInfo-tiling-02353 + // Vulkano currently has no high-level way to add or check for VkImageFormatListCreateInfo. + // Format isn't supported at all? if format_features.is_empty() { return Err(ImageError::FormatNotSupported); @@ -871,6 +885,7 @@ impl RawImage { initial_layout, external_memory_handle_types, _ne: _, + mut image_drm_format_modifier_create_info, } = &create_info; let aspects = format.map_or_else(Default::default, |format| format.aspects()); @@ -953,6 +968,13 @@ impl RawImage { info_vk.p_next = next as *const _ as *const _; } + if external_memory_handle_types.intersects(ExternalMemoryHandleTypes::DMA_BUF) { + let next = image_drm_format_modifier_create_info.as_mut().unwrap(); + + next.p_next = info_vk.p_next; + info_vk.p_next = next as *const _ as *const _; + } + let handle = { let fns = device.fns(); let mut output = MaybeUninit::uninit(); @@ -1000,6 +1022,7 @@ impl RawImage { initial_layout, external_memory_handle_types, _ne: _, + image_drm_format_modifier_create_info: _, } = create_info; let aspects = format.map_or_else(Default::default, |format| format.aspects()); @@ -1020,6 +1043,7 @@ impl RawImage { match tiling { ImageTiling::Linear => format_properties.linear_tiling_features, ImageTiling::Optimal => format_properties.optimal_tiling_features, + ImageTiling::DrmFormatModifier => format_properties.linear_tiling_features, // TODO: improve } }; @@ -1755,7 +1779,10 @@ impl RawImage { // Ensured by use of enum `ImageAspect`. // VUID-vkGetImageSubresourceLayout-image-02270 - if !matches!(self.tiling, ImageTiling::Linear) { + if !matches!( + self.tiling, + ImageTiling::DrmFormatModifier | ImageTiling::Linear + ) { return Err(ImageError::OptimalTilingNotSupported); } @@ -1792,6 +1819,11 @@ impl RawImage { allowed_aspects -= ImageAspects::COLOR; } + // TODO: VUID-vkGetImageSubresourceLayout-tiling-02271 + //if self.tiling == ImageTiling::DrmFormatModifier { + // Only one-plane image importing is possible for now. + //} + // VUID-vkGetImageSubresourceLayout-format-04461 // VUID-vkGetImageSubresourceLayout-format-04462 // VUID-vkGetImageSubresourceLayout-format-04463 @@ -1962,6 +1994,9 @@ pub struct ImageCreateInfo { /// The default value is [`ExternalMemoryHandleTypes::empty()`]. pub external_memory_handle_types: ExternalMemoryHandleTypes, + /// Specify that an image be created with the provided DRM format modifier and explicit memory layout + pub image_drm_format_modifier_create_info: Option, + pub _ne: crate::NonExhaustive, } @@ -1984,6 +2019,7 @@ impl Default for ImageCreateInfo { sharing: Sharing::Exclusive, initial_layout: ImageLayout::Undefined, external_memory_handle_types: ExternalMemoryHandleTypes::empty(), + image_drm_format_modifier_create_info: None, _ne: crate::NonExhaustive(()), } } @@ -2960,6 +2996,9 @@ pub enum ImageError { YcbcrFormatNot2d, DirectImageViewCreationFailed(ImageViewCreationError), + + /// If and only if tiling is `DRMFormatModifier`, then `image_drm_format_modifier_create_info` must not be `None`. + DrmFormatModifierRequiresCreateInfo, } impl Error for ImageError { @@ -3219,6 +3258,7 @@ impl Display for ImageError { write!(f, "a YCbCr format was given, but the image type was not 2D") } Self::DirectImageViewCreationFailed(e) => write!(f, "Image view creation failed {}", e), + Self::DrmFormatModifierRequiresCreateInfo => write!(f, "If and only if tiling is `DRMFormatModifier`, then `image_drm_format_modifier_create_info` must be `Some`"), } } } diff --git a/vulkano/src/image/view.rs b/vulkano/src/image/view.rs index 98ab6ae7..99bed3b5 100644 --- a/vulkano/src/image/view.rs +++ b/vulkano/src/image/view.rs @@ -726,6 +726,7 @@ where match image.tiling() { ImageTiling::Optimal => format_properties.optimal_tiling_features, ImageTiling::Linear => format_properties.linear_tiling_features, + ImageTiling::DrmFormatModifier => format_properties.linear_tiling_features, } } else { image.format_features() diff --git a/vulkano/src/memory/allocator/suballocator.rs b/vulkano/src/memory/allocator/suballocator.rs index 4a1269af..167a18d5 100644 --- a/vulkano/src/memory/allocator/suballocator.rs +++ b/vulkano/src/memory/allocator/suballocator.rs @@ -784,6 +784,7 @@ impl From for AllocationType { match tiling { ImageTiling::Optimal => AllocationType::NonLinear, ImageTiling::Linear => AllocationType::Linear, + ImageTiling::DrmFormatModifier => AllocationType::Linear, // TODO: improve } } }