diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index ab6ae02c6f..6d15492f4c 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -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(), diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 9f0fc67738..a56b42ae8a 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -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 { + 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 { + 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(©_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 { + 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 { - 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(©_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 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, + raw_flags: vk::ImageCreateFlags, +} diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index 6d56ecf964..6ec300ec77 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -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, diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs index 3b0f026fde..f81dc08931 100644 --- a/wgpu-hal/src/vulkan/mod.rs +++ b/wgpu-hal/src/vulkan/mod.rs @@ -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, + external_memory: Option, block: Option>, usage: crate::TextureUses, format: wgt::TextureFormat,