mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 22:34:43 +00:00
Import image from dma_buf following VK_EXT_external_memory_dma_buf (#2145)
* Import image from dma_buf Implements importing an image into Vulkan from a Linux dma_buf, according to the following Vulkan extensions: - https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_external_memory_dma_buf.html - https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_image_drm_format_modifier.html * Only compile dmabuf image importing on Linux Adds conditional compilation checks to functionality for importing vulkan images from a Linux dmabuf, as doing this only makes sense on Linux. * Add VUID checking for VkImageCreateInfo * Avoid Linux-only dependencies on other OSs * Add missing initializer to StorageImage * Add more VUID validation Check for VUID-vkGetPhysicalDeviceImageFormatProperties-tiling-02248, and VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02249 * Add some more VUIDs Or explanations of why they cannot yet be added * Small fix * Add suggested fixes Use lowercase for error, replace panic! with todo!, and make some comments show up in documentation.
This commit is contained in:
parent
836b9098ea
commit
b7ecee345d
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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<Device>,
|
||||
dimensions: ImageDimensions,
|
||||
format: Format,
|
||||
usage: ImageUsage,
|
||||
flags: ImageCreateFlags,
|
||||
queue_family_indices: impl IntoIterator<Item = u32>,
|
||||
mut subresource_data: Vec<SubresourceData>,
|
||||
drm_format_modifier: u64,
|
||||
) -> Result<Arc<StorageImage>, 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<SubresourceLayout> = 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<RawFd> = 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<Device> {
|
||||
|
@ -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<ImageDrmFormatModifierExplicitCreateInfoEXT>,
|
||||
|
||||
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`"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -784,6 +784,7 @@ impl From<ImageTiling> for AllocationType {
|
||||
match tiling {
|
||||
ImageTiling::Optimal => AllocationType::NonLinear,
|
||||
ImageTiling::Linear => AllocationType::Linear,
|
||||
ImageTiling::DrmFormatModifier => AllocationType::Linear, // TODO: improve
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user