use std::ffi::c_void;
use ash::vk;
use dpi::PhysicalSize;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};
use crate::{HostWgpuContext, InteropError};
const EGL_DRAW: i32 = 0x3059;
const EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE: i32 = 0x3200;
type FnGetCurrentDisplay = unsafe extern "system" fn() -> *mut c_void;
type FnGetCurrentSurface = unsafe extern "system" fn(readdraw: i32) -> *mut c_void;
type FnQuerySurfacePointerANGLE = unsafe extern "system" fn(
dpy: *mut c_void,
surface: *mut c_void,
attribute: i32,
value: *mut *mut c_void,
) -> u32;
unsafe fn get_egl_fn(name: &[u8]) -> Option<*const c_void> {
let module = unsafe { GetModuleHandleA(windows::core::PCSTR(b"libEGL.dll\0".as_ptr())).ok()? };
unsafe {
GetProcAddress(module, windows::core::PCSTR(name.as_ptr())).map(|f| f as *const c_void)
}
}
unsafe fn import_kmt_texture(
hal_device: &wgpu_hal::vulkan::Device,
share_handle: HANDLE,
desc: &wgpu_hal::TextureDescriptor,
) -> Result<wgpu_hal::vulkan::Texture, String> {
let raw_device = hal_device.raw_device();
let raw_physical = hal_device.raw_physical_device();
let raw_instance = hal_device.shared_instance().raw_instance();
let mut ext_mem_info = vk::ExternalMemoryImageCreateInfo::default()
.handle_types(vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE_KMT);
let image_info = vk::ImageCreateInfo::default()
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::B8G8R8A8_UNORM)
.extent(vk::Extent3D {
width: desc.size.width,
height: desc.size.height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::OPTIMAL)
.usage(vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::COLOR_ATTACHMENT)
.sharing_mode(vk::SharingMode::EXCLUSIVE)
.initial_layout(vk::ImageLayout::UNDEFINED)
.push_next(&mut ext_mem_info);
let vk_image = unsafe { raw_device.create_image(&image_info, None) }
.map_err(|e| format!("vkCreateImage failed: {e:?}"))?;
let mem_req = unsafe { raw_device.get_image_memory_requirements(vk_image) };
let mem_props = unsafe { raw_instance.get_physical_device_memory_properties(raw_physical) };
let mem_type_index = (0..mem_props.memory_type_count as usize)
.find(|&i| {
let type_ok = mem_req.memory_type_bits & (1 << i) != 0;
let flags_ok = mem_props.memory_types[i]
.property_flags
.contains(vk::MemoryPropertyFlags::DEVICE_LOCAL);
type_ok && flags_ok
})
.ok_or_else(|| "no DEVICE_LOCAL memory type found for KMT import".to_string())?;
let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(vk_image);
let mut import_info = vk::ImportMemoryWin32HandleInfoKHR::default()
.handle_type(vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE_KMT)
.handle(share_handle.0 as _);
import_info.p_next = std::ptr::from_mut(&mut dedicated).cast();
let alloc_info = vk::MemoryAllocateInfo::default()
.allocation_size(mem_req.size)
.memory_type_index(mem_type_index as u32)
.push_next(&mut import_info);
let memory = unsafe { raw_device.allocate_memory(&alloc_info, None) }
.map_err(|e| format!("vkAllocateMemory (KMT import) failed: {e:?}"))?;
unsafe { raw_device.bind_image_memory(vk_image, memory, 0) }.map_err(|e| {
unsafe { raw_device.free_memory(memory, None) };
format!("vkBindImageMemory failed: {e:?}")
})?;
Ok(unsafe {
hal_device.texture_from_raw(
vk_image,
desc,
None,
wgpu_hal::vulkan::TextureMemory::Dedicated(memory),
)
})
}
pub fn query_angle_share_handle() -> Option<HANDLE> {
let get_display: FnGetCurrentDisplay = unsafe {
let ptr = get_egl_fn(b"eglGetCurrentDisplay\0")?;
std::mem::transmute(ptr)
};
let get_surface: FnGetCurrentSurface = unsafe {
let ptr = get_egl_fn(b"eglGetCurrentSurface\0")?;
std::mem::transmute(ptr)
};
let query_ptr: FnQuerySurfacePointerANGLE = unsafe {
let ptr = get_egl_fn(b"eglQuerySurfacePointerANGLE\0")?;
std::mem::transmute(ptr)
};
let display = unsafe { get_display() };
let surface = unsafe { get_surface(EGL_DRAW) };
if display.is_null() || surface.is_null() {
return None;
}
let mut raw_handle: *mut c_void = std::ptr::null_mut();
let ok = unsafe {
query_ptr(
display,
surface,
EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
&mut raw_handle,
)
};
if ok == 0 || raw_handle.is_null() {
return None;
}
Some(HANDLE(raw_handle))
}
pub fn import_angle_d3d11_frame(
size: PhysicalSize<u32>,
host: &HostWgpuContext,
) -> Result<wgpu::Texture, InteropError> {
let share_handle = query_angle_share_handle().ok_or_else(|| {
InteropError::Angle(
"eglQuerySurfacePointerANGLE returned no handle — \
is the current EGL context an ANGLE pbuffer surface?"
.into(),
)
})?;
let hal_device = unsafe { host.device.as_hal::<wgpu::wgc::api::Vulkan>() }.ok_or(
InteropError::BackendMismatch {
expected: "Vulkan",
actual: "non-Vulkan (enable Backends::VULKAN)",
},
)?;
let hal_desc = wgpu_hal::TextureDescriptor {
label: Some("angle-d3d11-kmt-import"),
size: wgpu::Extent3d {
width: size.width,
height: size.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8Unorm,
usage: wgpu::TextureUses::RESOURCE | wgpu::TextureUses::COLOR_TARGET,
view_formats: vec![],
memory_flags: wgpu_hal::MemoryFlags::empty(),
};
let hal_texture = unsafe { import_kmt_texture(&*hal_device, share_handle, &hal_desc) }
.map_err(|e| InteropError::Vulkan(format!("D3D11 KMT handle import failed: {e}")))?;
let texture = unsafe {
crate::wgpu_compat::create_texture_from_hal::<wgpu_hal::api::Vulkan>(
&host.device,
hal_texture,
&wgpu::TextureDescriptor {
label: Some("angle-d3d11-kmt-import"),
size: wgpu::Extent3d {
width: size.width,
height: size.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
},
wgpu::TextureUses::UNINITIALIZED,
)
};
Ok(texture)
}