Vulkan: create texture from d3d11 shared handle (#6161)

This commit is contained in:
Xiaopeng Li 2024-10-30 11:48:06 +08:00 committed by GitHub
parent a398d9990e
commit 8b99c3c4f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 196 additions and 68 deletions

View File

@ -970,6 +970,11 @@ impl PhysicalDeviceProperties {
extensions.push(ext::robustness2::NAME);
}
// Optional `VK_KHR_external_memory_win32`
if self.supports_extension(khr::external_memory_win32::NAME) {
extensions.push(khr::external_memory_win32::NAME);
}
// Require `VK_KHR_draw_indirect_count` if the associated feature was requested
// Even though Vulkan 1.2 has promoted the extension to core, we must require the extension to avoid
// large amounts of spaghetti involved with using PhysicalDeviceVulkan12Features.
@ -1539,6 +1544,9 @@ impl super::Instance {
}),
image_format_list: phd_capabilities.device_api_version >= vk::API_VERSION_1_2
|| phd_capabilities.supports_extension(khr::image_format_list::NAME),
#[cfg(windows)]
external_memory_win32: phd_capabilities
.supports_extension(khr::external_memory_win32::NAME),
};
let capabilities = crate::Capabilities {
limits: phd_capabilities.to_wgpu_limits(),

View File

@ -686,6 +686,7 @@ impl super::Device {
super::Texture {
raw: vk_image,
drop_guard,
external_memory: None,
block: None,
usage: desc.usage,
format: desc.format,
@ -695,6 +696,167 @@ impl super::Device {
}
}
#[cfg(windows)]
fn find_memory_type_index(
&self,
type_bits_req: u32,
flags_req: vk::MemoryPropertyFlags,
) -> Option<usize> {
let mem_properties = unsafe {
self.shared
.instance
.raw
.get_physical_device_memory_properties(self.shared.physical_device)
};
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceMemoryProperties.html
for (i, mem_ty) in mem_properties.memory_types_as_slice().iter().enumerate() {
let types_bits = 1 << i;
let is_required_memory_type = type_bits_req & types_bits != 0;
let has_required_properties = mem_ty.property_flags & flags_req == flags_req;
if is_required_memory_type && has_required_properties {
return Some(i);
}
}
None
}
fn create_image_without_memory(
&self,
desc: &crate::TextureDescriptor,
external_memory_image_create_info: Option<&mut vk::ExternalMemoryImageCreateInfo>,
) -> Result<ImageWithoutMemory, crate::DeviceError> {
let copy_size = desc.copy_extent();
let mut raw_flags = vk::ImageCreateFlags::empty();
if desc.is_cube_compatible() {
raw_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE;
}
let original_format = self.shared.private_caps.map_texture_format(desc.format);
let mut vk_view_formats = vec![];
let mut wgt_view_formats = vec![];
if !desc.view_formats.is_empty() {
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
wgt_view_formats.clone_from(&desc.view_formats);
wgt_view_formats.push(desc.format);
if self.shared.private_caps.image_format_list {
vk_view_formats = desc
.view_formats
.iter()
.map(|f| self.shared.private_caps.map_texture_format(*f))
.collect();
vk_view_formats.push(original_format)
}
}
if desc.format.is_multi_planar_format() {
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
}
let mut vk_info = vk::ImageCreateInfo::default()
.flags(raw_flags)
.image_type(conv::map_texture_dimension(desc.dimension))
.format(original_format)
.extent(conv::map_copy_extent(&copy_size))
.mip_levels(desc.mip_level_count)
.array_layers(desc.array_layer_count())
.samples(vk::SampleCountFlags::from_raw(desc.sample_count))
.tiling(vk::ImageTiling::OPTIMAL)
.usage(conv::map_texture_usage(desc.usage))
.sharing_mode(vk::SharingMode::EXCLUSIVE)
.initial_layout(vk::ImageLayout::UNDEFINED);
let mut format_list_info = vk::ImageFormatListCreateInfo::default();
if !vk_view_formats.is_empty() {
format_list_info = format_list_info.view_formats(&vk_view_formats);
vk_info = vk_info.push_next(&mut format_list_info);
}
if let Some(ext_info) = external_memory_image_create_info {
vk_info = vk_info.push_next(ext_info);
}
let raw = unsafe { self.shared.raw.create_image(&vk_info, None) }.map_err(map_err)?;
fn map_err(err: vk::Result) -> crate::DeviceError {
// We don't use VK_EXT_image_compression_control
// VK_ERROR_COMPRESSION_EXHAUSTED_EXT
super::map_host_device_oom_and_ioca_err(err)
}
let req = unsafe { self.shared.raw.get_image_memory_requirements(raw) };
Ok(ImageWithoutMemory {
raw,
requirements: req,
copy_size,
view_formats: wgt_view_formats,
raw_flags,
})
}
/// # Safety
///
/// - Vulkan 1.1+ (or VK_KHR_external_memory)
/// - The `d3d11_shared_handle` must be valid and respecting `desc`
/// - `VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT` flag is used because we need to hold a reference to the handle
#[cfg(windows)]
pub unsafe fn texture_from_d3d11_shared_handle(
&self,
d3d11_shared_handle: windows::Win32::Foundation::HANDLE,
desc: &crate::TextureDescriptor,
) -> Result<super::Texture, crate::DeviceError> {
if !self.shared.private_caps.external_memory_win32 {
log::error!("VK_KHR_external_memory extension is required");
return Err(crate::DeviceError::ResourceCreationFailed);
}
let mut external_memory_image_info = vk::ExternalMemoryImageCreateInfo::default()
.handle_types(vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE);
let image =
self.create_image_without_memory(desc, Some(&mut external_memory_image_info))?;
let mut import_memory_info = vk::ImportMemoryWin32HandleInfoKHR::default()
.handle_type(vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE)
.handle(d3d11_shared_handle.0 as _);
let mem_type_index = self
.find_memory_type_index(
image.requirements.memory_type_bits,
vk::MemoryPropertyFlags::DEVICE_LOCAL,
)
.ok_or(crate::DeviceError::ResourceCreationFailed)?;
let memory_allocate_info = vk::MemoryAllocateInfo::default()
.allocation_size(image.requirements.size)
.memory_type_index(mem_type_index as _)
.push_next(&mut import_memory_info);
let memory = unsafe { self.shared.raw.allocate_memory(&memory_allocate_info, None) }
.map_err(super::map_host_device_oom_err)?;
unsafe { self.shared.raw.bind_image_memory(image.raw, memory, 0) }
.map_err(super::map_host_device_oom_err)?;
if let Some(label) = desc.label {
unsafe { self.shared.set_object_name(image.raw, label) };
}
self.counters.textures.add(1);
Ok(super::Texture {
raw: image.raw,
drop_guard: None,
external_memory: Some(memory),
block: None,
usage: desc.usage,
format: desc.format,
raw_flags: image.raw_flags,
copy_size: image.copy_size,
view_formats: image.view_formats,
})
}
/// # Safety
///
/// - `vk_buffer`'s memory must be managed by the caller
@ -1028,74 +1190,16 @@ impl crate::Device for super::Device {
&self,
desc: &crate::TextureDescriptor,
) -> Result<super::Texture, crate::DeviceError> {
let copy_size = desc.copy_extent();
let mut raw_flags = vk::ImageCreateFlags::empty();
if desc.is_cube_compatible() {
raw_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE;
}
let original_format = self.shared.private_caps.map_texture_format(desc.format);
let mut vk_view_formats = vec![];
let mut wgt_view_formats = vec![];
if !desc.view_formats.is_empty() {
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
wgt_view_formats.clone_from(&desc.view_formats);
wgt_view_formats.push(desc.format);
if self.shared.private_caps.image_format_list {
vk_view_formats = desc
.view_formats
.iter()
.map(|f| self.shared.private_caps.map_texture_format(*f))
.collect();
vk_view_formats.push(original_format)
}
}
if desc.format.is_multi_planar_format() {
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
}
let mut vk_info = vk::ImageCreateInfo::default()
.flags(raw_flags)
.image_type(conv::map_texture_dimension(desc.dimension))
.format(original_format)
.extent(conv::map_copy_extent(&copy_size))
.mip_levels(desc.mip_level_count)
.array_layers(desc.array_layer_count())
.samples(vk::SampleCountFlags::from_raw(desc.sample_count))
.tiling(vk::ImageTiling::OPTIMAL)
.usage(conv::map_texture_usage(desc.usage))
.sharing_mode(vk::SharingMode::EXCLUSIVE)
.initial_layout(vk::ImageLayout::UNDEFINED);
let mut format_list_info = vk::ImageFormatListCreateInfo::default();
if !vk_view_formats.is_empty() {
format_list_info = format_list_info.view_formats(&vk_view_formats);
vk_info = vk_info.push_next(&mut format_list_info);
}
let raw = unsafe {
self.shared
.raw
.create_image(&vk_info, None)
.map_err(map_err)?
};
fn map_err(err: vk::Result) -> crate::DeviceError {
// We don't use VK_EXT_image_compression_control
// VK_ERROR_COMPRESSION_EXHAUSTED_EXT
super::map_host_device_oom_and_ioca_err(err)
}
let req = unsafe { self.shared.raw.get_image_memory_requirements(raw) };
let image = self.create_image_without_memory(desc, None)?;
let block = unsafe {
self.mem_allocator.lock().alloc(
&*self.shared,
gpu_alloc::Request {
size: req.size,
align_mask: req.alignment - 1,
size: image.requirements.size,
align_mask: image.requirements.alignment - 1,
usage: gpu_alloc::UsageFlags::FAST_DEVICE_ACCESS,
memory_types: req.memory_type_bits & self.valid_ash_memory_types,
memory_types: image.requirements.memory_type_bits & self.valid_ash_memory_types,
},
)?
};
@ -1105,31 +1209,35 @@ impl crate::Device for super::Device {
unsafe {
self.shared
.raw
.bind_image_memory(raw, *block.memory(), block.offset())
.bind_image_memory(image.raw, *block.memory(), block.offset())
.map_err(super::map_host_device_oom_err)?
};
if let Some(label) = desc.label {
unsafe { self.shared.set_object_name(raw, label) };
unsafe { self.shared.set_object_name(image.raw, label) };
}
self.counters.textures.add(1);
Ok(super::Texture {
raw,
raw: image.raw,
drop_guard: None,
external_memory: None,
block: Some(block),
usage: desc.usage,
format: desc.format,
raw_flags,
copy_size,
view_formats: wgt_view_formats,
raw_flags: image.raw_flags,
copy_size: image.copy_size,
view_formats: image.view_formats,
})
}
unsafe fn destroy_texture(&self, texture: super::Texture) {
if texture.drop_guard.is_none() {
unsafe { self.shared.raw.destroy_image(texture.raw, None) };
}
if let Some(memory) = texture.external_memory {
unsafe { self.shared.raw.free_memory(memory, None) };
}
if let Some(block) = texture.block {
self.counters.texture_memory.sub(block.size() as isize);
@ -2584,3 +2692,11 @@ impl From<gpu_descriptor::AllocationError> for crate::DeviceError {
fn handle_unexpected(err: vk::Result) -> ! {
panic!("Unexpected Vulkan error: `{err}`")
}
struct ImageWithoutMemory {
raw: vk::Image,
requirements: vk::MemoryRequirements,
copy_size: crate::CopyExtent,
view_formats: Vec<wgt::TextureFormat>,
raw_flags: vk::ImageCreateFlags,
}

View File

@ -1095,6 +1095,7 @@ impl crate::Surface for super::Surface {
raw: swapchain.images[index as usize],
drop_guard: None,
block: None,
external_memory: None,
usage: swapchain.config.usage,
format: swapchain.config.format,
raw_flags,

View File

@ -532,6 +532,8 @@ struct PrivateCapabilities {
robust_image_access2: bool,
zero_initialize_workgroup_memory: bool,
image_format_list: bool,
#[cfg(windows)]
external_memory_win32: bool,
}
bitflags::bitflags!(
@ -760,6 +762,7 @@ impl crate::DynAccelerationStructure for AccelerationStructure {}
pub struct Texture {
raw: vk::Image,
drop_guard: Option<crate::DropGuard>,
external_memory: Option<vk::DeviceMemory>,
block: Option<gpu_alloc::MemoryBlock<vk::DeviceMemory>>,
usage: crate::TextureUses,
format: wgt::TextureFormat,