#![allow(unsafe_code)]
use mediaway_common::{GpuBufferHandle, GpuDeviceHandle, NativeHandle};
use mediaway_encoder::EncodeError;
use windows_hal_interop::Win32::Graphics::Direct3D12::{ID3D12Device, ID3D12Resource};
use windows_hal_interop::core::Interface;
use crate::wgpu::error::WgpuInteropError;
pub const BRIDGE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8Unorm;
pub struct WgpuDx12Bridge {
bridge: mediaway_encoder::windows::D3d12SharedEncodeBridge,
dest: wgpu::Texture,
width: u32,
height: u32,
}
impl WgpuDx12Bridge {
pub fn new(device: &wgpu::Device, width: u32, height: u32) -> Result<Self, WgpuInteropError> {
if width == 0 || height == 0 {
return Err(WgpuInteropError::InvalidInput);
}
let native_device = unsafe { device.as_hal::<wgpu::hal::api::Dx12>() }
.ok_or(WgpuInteropError::HalUnavailable)?;
let raw_device: &ID3D12Device = native_device.raw_device();
let device_handle = NativeHandle::new(Interface::as_raw(raw_device) as usize)
.ok_or(WgpuInteropError::InvalidInput)?;
drop(native_device);
let bridge =
mediaway_encoder::windows::D3d12SharedEncodeBridge::open(device_handle, width, height)?;
let dest = wrap_bridge_resource(device, &bridge, width, height)?;
Ok(Self {
bridge,
dest,
width,
height,
})
}
#[must_use = "the returned handle is required to open a Zero-Copy-input encoder"]
pub fn gpu_device_handle(&self) -> Result<GpuDeviceHandle, WgpuInteropError> {
Ok(GpuDeviceHandle::DirectX11(
self.bridge.d3d11_device_handle()?,
))
}
pub fn copy_frame(
&self,
device: &wgpu::Device,
queue: &wgpu::Queue,
source: &wgpu::Texture,
) -> Result<GpuBufferHandle, WgpuInteropError> {
if source.width() != self.width
|| source.height() != self.height
|| source.format() != BRIDGE_FORMAT
{
return Err(WgpuInteropError::InvalidInput);
}
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("mediaway-wgpu dx12 bridge copy"),
});
let extent = wgpu::Extent3d {
width: self.width,
height: self.height,
depth_or_array_layers: 1,
};
encoder.copy_texture_to_texture(
wgpu::TexelCopyTextureInfo {
texture: source,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
wgpu::TexelCopyTextureInfo {
texture: &self.dest,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
extent,
);
let submission_index = queue.submit(std::iter::once(encoder.finish()));
device
.poll(wgpu::PollType::WaitForSubmissionIndex(submission_index))
.map_err(|_| WgpuInteropError::Bridge(EncodeError::Backend))?;
Ok(self.bridge.as_dx11_handle()?)
}
}
fn wrap_bridge_resource(
device: &wgpu::Device,
bridge: &mediaway_encoder::windows::D3d12SharedEncodeBridge,
width: u32,
height: u32,
) -> Result<wgpu::Texture, WgpuInteropError> {
let handle = bridge.d3d12_resource_handle()?;
let raw = handle.get() as *mut core::ffi::c_void;
let borrowed =
unsafe { ID3D12Resource::from_raw_borrowed(&raw) }.ok_or(WgpuInteropError::InvalidInput)?;
let resource: ID3D12Resource = borrowed.clone();
let size = wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let hal_texture = unsafe {
wgpu::hal::dx12::Device::texture_from_raw(
resource,
BRIDGE_FORMAT,
wgpu::TextureDimension::D2,
size,
1,
1,
)
};
let texture_desc = wgpu::TextureDescriptor {
label: Some("mediaway-wgpu dx12 bridge dest"),
size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: BRIDGE_FORMAT,
usage: wgpu::TextureUsages::COPY_DST,
view_formats: &[],
};
let wgpu_texture = unsafe {
device.create_texture_from_hal::<wgpu::hal::api::Dx12>(hal_texture, &texture_desc)
};
Ok(wgpu_texture)
}