Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulkan: create texture from d3d11 shared handle #6161

Open
wants to merge 13 commits into
base: trunk
Choose a base branch
from
8 changes: 8 additions & 0 deletions wgpu-hal/src/vulkan/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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(),
Expand Down
252 changes: 184 additions & 68 deletions wgpu-hal/src/vulkan/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
xiaopengli89 marked this conversation as resolved.
Show resolved Hide resolved
}

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
Expand Down Expand Up @@ -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,
},
)?
};
Expand All @@ -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);

Expand Down Expand Up @@ -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,
}
1 change: 1 addition & 0 deletions wgpu-hal/src/vulkan/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions wgpu-hal/src/vulkan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down Expand Up @@ -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,
Expand Down