use dpi::PhysicalSize;
use glow::HasContext;
use std::ffi::c_void;
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::Graphics::Direct3D12::{
D3D12_HEAP_FLAG_SHARED, D3D12_HEAP_PROPERTIES, D3D12_HEAP_TYPE_DEFAULT, D3D12_RESOURCE_DESC,
D3D12_RESOURCE_DIMENSION_TEXTURE2D, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,
D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS, D3D12_RESOURCE_STATE_COMMON,
D3D12_TEXTURE_LAYOUT_UNKNOWN, ID3D12Resource,
};
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_SAMPLE_DESC};
use windows::Win32::Graphics::Dxgi::{DXGI_SHARED_RESOURCE_READ, DXGI_SHARED_RESOURCE_WRITE};
use crate::{HostWgpuContext, InteropError, gl_bindings as gl};
pub fn import_gl_framebuffer_dx12(
gl: &glow::Context,
gl_extension_loader: &dyn Fn(&str) -> *const c_void,
source_fbo: u32,
size: PhysicalSize<u32>,
host: &HostWgpuContext,
) -> Result<wgpu::Texture, InteropError> {
use gl::Gles2 as Gl;
unsafe {
let hal_device =
host.device
.as_hal::<wgpu::wgc::api::Dx12>()
.ok_or(InteropError::BackendMismatch {
expected: "Dx12",
actual: "non-Dx12",
})?;
let d3d_device = hal_device.raw_device().clone();
let heap_props = D3D12_HEAP_PROPERTIES {
Type: D3D12_HEAP_TYPE_DEFAULT,
..Default::default()
};
let resource_desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Width: size.width as u64,
Height: size.height,
DepthOrArraySize: 1,
MipLevels: 1,
Format: DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_UNKNOWN,
Flags: D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET
| D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS,
..Default::default()
};
let mut d3d12_resource: Option<ID3D12Resource> = None;
d3d_device
.CreateCommittedResource(
&heap_props,
D3D12_HEAP_FLAG_SHARED,
&resource_desc,
D3D12_RESOURCE_STATE_COMMON,
None,
&mut d3d12_resource,
)
.map_err(|e| InteropError::Dx12(e.to_string()))?;
let d3d12_resource = d3d12_resource
.ok_or_else(|| InteropError::Dx12("CreateCommittedResource returned null".into()))?;
let alloc_info = d3d_device.GetResourceAllocationInfo(0, &[resource_desc]);
let shared_handle = d3d_device
.CreateSharedHandle(
&d3d12_resource,
None,
(DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE).0,
windows::core::PCWSTR::null(),
)
.map_err(|e| InteropError::Dx12(e.to_string()))?;
let gl_with_extensions = Gl::load_with(|function_name| gl_extension_loader(function_name));
if !gl_with_extensions.CreateMemoryObjectsEXT.is_loaded()
|| !gl_with_extensions.ImportMemoryWin32HandleEXT.is_loaded()
|| !gl_with_extensions.TexStorageMem2DEXT.is_loaded()
{
let _ = CloseHandle(shared_handle);
return Err(InteropError::OpenGl(
"GL_EXT_memory_object_win32 not available (ANGLE's D3D11 backend \
does not support external memory extensions — use CPU readback instead)"
.into(),
));
}
let mut memory_object = 0u32;
gl_with_extensions.CreateMemoryObjectsEXT(1, &mut memory_object);
gl_with_extensions.ImportMemoryWin32HandleEXT(
memory_object,
alloc_info.SizeInBytes,
gl::HANDLE_TYPE_D3D12_RESOURCE_EXT,
shared_handle.0 as *mut c_void,
);
let _ = CloseHandle(shared_handle);
let texture = gl.create_texture().map_err(InteropError::OpenGl)?;
gl.bind_texture(gl::TEXTURE_2D, Some(texture));
gl_with_extensions.TexStorageMem2DEXT(
gl::TEXTURE_2D,
1,
gl::RGBA8,
size.width as i32,
size.height as i32,
memory_object,
0,
);
let draw_framebuffer = gl.create_framebuffer().map_err(InteropError::OpenGl)?;
let read_framebuffer = if source_fbo == 0 {
None
} else {
Some(glow::NativeFramebuffer(
std::num::NonZeroU32::new(source_fbo)
.ok_or(InteropError::InvalidFrame("invalid FBO id"))?,
))
};
gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, Some(draw_framebuffer));
gl.framebuffer_texture_2d(
gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
Some(texture),
0,
);
gl.bind_framebuffer(gl::READ_FRAMEBUFFER, read_framebuffer);
gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, Some(draw_framebuffer));
gl.blit_framebuffer(
0,
0,
size.width as i32,
size.height as i32,
0,
size.height as i32, size.width as i32,
0,
gl::COLOR_BUFFER_BIT,
gl::NEAREST,
);
gl.flush();
gl.delete_framebuffer(draw_framebuffer);
gl.delete_texture(texture);
gl_with_extensions.DeleteMemoryObjectsEXT(1, &memory_object);
let hal_texture = wgpu_hal::dx12::Device::texture_from_raw(
d3d12_resource,
wgpu::TextureFormat::Rgba8Unorm,
wgpu::TextureDimension::D2,
wgpu::Extent3d {
width: size.width,
height: size.height,
depth_or_array_layers: 1,
},
1, 1, );
let imported = host.device.create_texture_from_hal::<wgpu::wgc::api::Dx12>(
hal_texture,
&wgpu::TextureDescriptor {
label: Some("gl-frame-dx12-import"),
size: wgpu::Extent3d {
width: size.width,
height: size.height,
depth_or_array_layers: 1,
},
format: wgpu::TextureFormat::Rgba8Unorm,
dimension: wgpu::TextureDimension::D2,
mip_level_count: 1,
sample_count: 1,
usage: wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
},
);
Ok(imported)
}
}