Skip to content

Commit 8b99c3c

Browse files
authored
Vulkan: create texture from d3d11 shared handle (#6161)
1 parent a398d99 commit 8b99c3c

File tree

4 files changed

+196
-68
lines changed

4 files changed

+196
-68
lines changed

wgpu-hal/src/vulkan/adapter.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,11 @@ impl PhysicalDeviceProperties {
970970
extensions.push(ext::robustness2::NAME);
971971
}
972972

973+
// Optional `VK_KHR_external_memory_win32`
974+
if self.supports_extension(khr::external_memory_win32::NAME) {
975+
extensions.push(khr::external_memory_win32::NAME);
976+
}
977+
973978
// Require `VK_KHR_draw_indirect_count` if the associated feature was requested
974979
// Even though Vulkan 1.2 has promoted the extension to core, we must require the extension to avoid
975980
// large amounts of spaghetti involved with using PhysicalDeviceVulkan12Features.
@@ -1539,6 +1544,9 @@ impl super::Instance {
15391544
}),
15401545
image_format_list: phd_capabilities.device_api_version >= vk::API_VERSION_1_2
15411546
|| phd_capabilities.supports_extension(khr::image_format_list::NAME),
1547+
#[cfg(windows)]
1548+
external_memory_win32: phd_capabilities
1549+
.supports_extension(khr::external_memory_win32::NAME),
15421550
};
15431551
let capabilities = crate::Capabilities {
15441552
limits: phd_capabilities.to_wgpu_limits(),

wgpu-hal/src/vulkan/device.rs

Lines changed: 184 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ impl super::Device {
686686
super::Texture {
687687
raw: vk_image,
688688
drop_guard,
689+
external_memory: None,
689690
block: None,
690691
usage: desc.usage,
691692
format: desc.format,
@@ -695,6 +696,167 @@ impl super::Device {
695696
}
696697
}
697698

699+
#[cfg(windows)]
700+
fn find_memory_type_index(
701+
&self,
702+
type_bits_req: u32,
703+
flags_req: vk::MemoryPropertyFlags,
704+
) -> Option<usize> {
705+
let mem_properties = unsafe {
706+
self.shared
707+
.instance
708+
.raw
709+
.get_physical_device_memory_properties(self.shared.physical_device)
710+
};
711+
712+
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceMemoryProperties.html
713+
for (i, mem_ty) in mem_properties.memory_types_as_slice().iter().enumerate() {
714+
let types_bits = 1 << i;
715+
let is_required_memory_type = type_bits_req & types_bits != 0;
716+
let has_required_properties = mem_ty.property_flags & flags_req == flags_req;
717+
if is_required_memory_type && has_required_properties {
718+
return Some(i);
719+
}
720+
}
721+
722+
None
723+
}
724+
725+
fn create_image_without_memory(
726+
&self,
727+
desc: &crate::TextureDescriptor,
728+
external_memory_image_create_info: Option<&mut vk::ExternalMemoryImageCreateInfo>,
729+
) -> Result<ImageWithoutMemory, crate::DeviceError> {
730+
let copy_size = desc.copy_extent();
731+
732+
let mut raw_flags = vk::ImageCreateFlags::empty();
733+
if desc.is_cube_compatible() {
734+
raw_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE;
735+
}
736+
737+
let original_format = self.shared.private_caps.map_texture_format(desc.format);
738+
let mut vk_view_formats = vec![];
739+
let mut wgt_view_formats = vec![];
740+
if !desc.view_formats.is_empty() {
741+
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
742+
wgt_view_formats.clone_from(&desc.view_formats);
743+
wgt_view_formats.push(desc.format);
744+
745+
if self.shared.private_caps.image_format_list {
746+
vk_view_formats = desc
747+
.view_formats
748+
.iter()
749+
.map(|f| self.shared.private_caps.map_texture_format(*f))
750+
.collect();
751+
vk_view_formats.push(original_format)
752+
}
753+
}
754+
if desc.format.is_multi_planar_format() {
755+
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
756+
}
757+
758+
let mut vk_info = vk::ImageCreateInfo::default()
759+
.flags(raw_flags)
760+
.image_type(conv::map_texture_dimension(desc.dimension))
761+
.format(original_format)
762+
.extent(conv::map_copy_extent(&copy_size))
763+
.mip_levels(desc.mip_level_count)
764+
.array_layers(desc.array_layer_count())
765+
.samples(vk::SampleCountFlags::from_raw(desc.sample_count))
766+
.tiling(vk::ImageTiling::OPTIMAL)
767+
.usage(conv::map_texture_usage(desc.usage))
768+
.sharing_mode(vk::SharingMode::EXCLUSIVE)
769+
.initial_layout(vk::ImageLayout::UNDEFINED);
770+
771+
let mut format_list_info = vk::ImageFormatListCreateInfo::default();
772+
if !vk_view_formats.is_empty() {
773+
format_list_info = format_list_info.view_formats(&vk_view_formats);
774+
vk_info = vk_info.push_next(&mut format_list_info);
775+
}
776+
777+
if let Some(ext_info) = external_memory_image_create_info {
778+
vk_info = vk_info.push_next(ext_info);
779+
}
780+
781+
let raw = unsafe { self.shared.raw.create_image(&vk_info, None) }.map_err(map_err)?;
782+
fn map_err(err: vk::Result) -> crate::DeviceError {
783+
// We don't use VK_EXT_image_compression_control
784+
// VK_ERROR_COMPRESSION_EXHAUSTED_EXT
785+
super::map_host_device_oom_and_ioca_err(err)
786+
}
787+
let req = unsafe { self.shared.raw.get_image_memory_requirements(raw) };
788+
789+
Ok(ImageWithoutMemory {
790+
raw,
791+
requirements: req,
792+
copy_size,
793+
view_formats: wgt_view_formats,
794+
raw_flags,
795+
})
796+
}
797+
798+
/// # Safety
799+
///
800+
/// - Vulkan 1.1+ (or VK_KHR_external_memory)
801+
/// - The `d3d11_shared_handle` must be valid and respecting `desc`
802+
/// - `VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT` flag is used because we need to hold a reference to the handle
803+
#[cfg(windows)]
804+
pub unsafe fn texture_from_d3d11_shared_handle(
805+
&self,
806+
d3d11_shared_handle: windows::Win32::Foundation::HANDLE,
807+
desc: &crate::TextureDescriptor,
808+
) -> Result<super::Texture, crate::DeviceError> {
809+
if !self.shared.private_caps.external_memory_win32 {
810+
log::error!("VK_KHR_external_memory extension is required");
811+
return Err(crate::DeviceError::ResourceCreationFailed);
812+
}
813+
814+
let mut external_memory_image_info = vk::ExternalMemoryImageCreateInfo::default()
815+
.handle_types(vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE);
816+
817+
let image =
818+
self.create_image_without_memory(desc, Some(&mut external_memory_image_info))?;
819+
820+
let mut import_memory_info = vk::ImportMemoryWin32HandleInfoKHR::default()
821+
.handle_type(vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE)
822+
.handle(d3d11_shared_handle.0 as _);
823+
824+
let mem_type_index = self
825+
.find_memory_type_index(
826+
image.requirements.memory_type_bits,
827+
vk::MemoryPropertyFlags::DEVICE_LOCAL,
828+
)
829+
.ok_or(crate::DeviceError::ResourceCreationFailed)?;
830+
831+
let memory_allocate_info = vk::MemoryAllocateInfo::default()
832+
.allocation_size(image.requirements.size)
833+
.memory_type_index(mem_type_index as _)
834+
.push_next(&mut import_memory_info);
835+
let memory = unsafe { self.shared.raw.allocate_memory(&memory_allocate_info, None) }
836+
.map_err(super::map_host_device_oom_err)?;
837+
838+
unsafe { self.shared.raw.bind_image_memory(image.raw, memory, 0) }
839+
.map_err(super::map_host_device_oom_err)?;
840+
841+
if let Some(label) = desc.label {
842+
unsafe { self.shared.set_object_name(image.raw, label) };
843+
}
844+
845+
self.counters.textures.add(1);
846+
847+
Ok(super::Texture {
848+
raw: image.raw,
849+
drop_guard: None,
850+
external_memory: Some(memory),
851+
block: None,
852+
usage: desc.usage,
853+
format: desc.format,
854+
raw_flags: image.raw_flags,
855+
copy_size: image.copy_size,
856+
view_formats: image.view_formats,
857+
})
858+
}
859+
698860
/// # Safety
699861
///
700862
/// - `vk_buffer`'s memory must be managed by the caller
@@ -1028,74 +1190,16 @@ impl crate::Device for super::Device {
10281190
&self,
10291191
desc: &crate::TextureDescriptor,
10301192
) -> Result<super::Texture, crate::DeviceError> {
1031-
let copy_size = desc.copy_extent();
1032-
1033-
let mut raw_flags = vk::ImageCreateFlags::empty();
1034-
if desc.is_cube_compatible() {
1035-
raw_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE;
1036-
}
1037-
1038-
let original_format = self.shared.private_caps.map_texture_format(desc.format);
1039-
let mut vk_view_formats = vec![];
1040-
let mut wgt_view_formats = vec![];
1041-
if !desc.view_formats.is_empty() {
1042-
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
1043-
wgt_view_formats.clone_from(&desc.view_formats);
1044-
wgt_view_formats.push(desc.format);
1045-
1046-
if self.shared.private_caps.image_format_list {
1047-
vk_view_formats = desc
1048-
.view_formats
1049-
.iter()
1050-
.map(|f| self.shared.private_caps.map_texture_format(*f))
1051-
.collect();
1052-
vk_view_formats.push(original_format)
1053-
}
1054-
}
1055-
if desc.format.is_multi_planar_format() {
1056-
raw_flags |= vk::ImageCreateFlags::MUTABLE_FORMAT;
1057-
}
1058-
1059-
let mut vk_info = vk::ImageCreateInfo::default()
1060-
.flags(raw_flags)
1061-
.image_type(conv::map_texture_dimension(desc.dimension))
1062-
.format(original_format)
1063-
.extent(conv::map_copy_extent(&copy_size))
1064-
.mip_levels(desc.mip_level_count)
1065-
.array_layers(desc.array_layer_count())
1066-
.samples(vk::SampleCountFlags::from_raw(desc.sample_count))
1067-
.tiling(vk::ImageTiling::OPTIMAL)
1068-
.usage(conv::map_texture_usage(desc.usage))
1069-
.sharing_mode(vk::SharingMode::EXCLUSIVE)
1070-
.initial_layout(vk::ImageLayout::UNDEFINED);
1071-
1072-
let mut format_list_info = vk::ImageFormatListCreateInfo::default();
1073-
if !vk_view_formats.is_empty() {
1074-
format_list_info = format_list_info.view_formats(&vk_view_formats);
1075-
vk_info = vk_info.push_next(&mut format_list_info);
1076-
}
1077-
1078-
let raw = unsafe {
1079-
self.shared
1080-
.raw
1081-
.create_image(&vk_info, None)
1082-
.map_err(map_err)?
1083-
};
1084-
fn map_err(err: vk::Result) -> crate::DeviceError {
1085-
// We don't use VK_EXT_image_compression_control
1086-
// VK_ERROR_COMPRESSION_EXHAUSTED_EXT
1087-
super::map_host_device_oom_and_ioca_err(err)
1088-
}
1089-
let req = unsafe { self.shared.raw.get_image_memory_requirements(raw) };
1193+
let image = self.create_image_without_memory(desc, None)?;
10901194

10911195
let block = unsafe {
10921196
self.mem_allocator.lock().alloc(
10931197
&*self.shared,
10941198
gpu_alloc::Request {
1095-
size: req.size,
1096-
align_mask: req.alignment - 1,
1199+
size: image.requirements.size,
1200+
align_mask: image.requirements.alignment - 1,
10971201
usage: gpu_alloc::UsageFlags::FAST_DEVICE_ACCESS,
1098-
memory_types: req.memory_type_bits & self.valid_ash_memory_types,
1202+
memory_types: image.requirements.memory_type_bits & self.valid_ash_memory_types,
10991203
},
11001204
)?
11011205
};
@@ -1105,31 +1209,35 @@ impl crate::Device for super::Device {
11051209
unsafe {
11061210
self.shared
11071211
.raw
1108-
.bind_image_memory(raw, *block.memory(), block.offset())
1212+
.bind_image_memory(image.raw, *block.memory(), block.offset())
11091213
.map_err(super::map_host_device_oom_err)?
11101214
};
11111215

11121216
if let Some(label) = desc.label {
1113-
unsafe { self.shared.set_object_name(raw, label) };
1217+
unsafe { self.shared.set_object_name(image.raw, label) };
11141218
}
11151219

11161220
self.counters.textures.add(1);
11171221

11181222
Ok(super::Texture {
1119-
raw,
1223+
raw: image.raw,
11201224
drop_guard: None,
1225+
external_memory: None,
11211226
block: Some(block),
11221227
usage: desc.usage,
11231228
format: desc.format,
1124-
raw_flags,
1125-
copy_size,
1126-
view_formats: wgt_view_formats,
1229+
raw_flags: image.raw_flags,
1230+
copy_size: image.copy_size,
1231+
view_formats: image.view_formats,
11271232
})
11281233
}
11291234
unsafe fn destroy_texture(&self, texture: super::Texture) {
11301235
if texture.drop_guard.is_none() {
11311236
unsafe { self.shared.raw.destroy_image(texture.raw, None) };
11321237
}
1238+
if let Some(memory) = texture.external_memory {
1239+
unsafe { self.shared.raw.free_memory(memory, None) };
1240+
}
11331241
if let Some(block) = texture.block {
11341242
self.counters.texture_memory.sub(block.size() as isize);
11351243

@@ -2584,3 +2692,11 @@ impl From<gpu_descriptor::AllocationError> for crate::DeviceError {
25842692
fn handle_unexpected(err: vk::Result) -> ! {
25852693
panic!("Unexpected Vulkan error: `{err}`")
25862694
}
2695+
2696+
struct ImageWithoutMemory {
2697+
raw: vk::Image,
2698+
requirements: vk::MemoryRequirements,
2699+
copy_size: crate::CopyExtent,
2700+
view_formats: Vec<wgt::TextureFormat>,
2701+
raw_flags: vk::ImageCreateFlags,
2702+
}

wgpu-hal/src/vulkan/instance.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,7 @@ impl crate::Surface for super::Surface {
10951095
raw: swapchain.images[index as usize],
10961096
drop_guard: None,
10971097
block: None,
1098+
external_memory: None,
10981099
usage: swapchain.config.usage,
10991100
format: swapchain.config.format,
11001101
raw_flags,

wgpu-hal/src/vulkan/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,8 @@ struct PrivateCapabilities {
532532
robust_image_access2: bool,
533533
zero_initialize_workgroup_memory: bool,
534534
image_format_list: bool,
535+
#[cfg(windows)]
536+
external_memory_win32: bool,
535537
}
536538

537539
bitflags::bitflags!(
@@ -760,6 +762,7 @@ impl crate::DynAccelerationStructure for AccelerationStructure {}
760762
pub struct Texture {
761763
raw: vk::Image,
762764
drop_guard: Option<crate::DropGuard>,
765+
external_memory: Option<vk::DeviceMemory>,
763766
block: Option<gpu_alloc::MemoryBlock<vk::DeviceMemory>>,
764767
usage: crate::TextureUses,
765768
format: wgt::TextureFormat,

0 commit comments

Comments
 (0)