#![allow(unsafe_code)]
use crate::DecodeError;
use mediaway_common::NativeHandle;
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::Graphics::Direct3D11::{
D3D11_BIND_SHADER_RESOURCE, D3D11_QUERY_DESC, D3D11_QUERY_EVENT, D3D11_RESOURCE_MISC_SHARED,
D3D11_RESOURCE_MISC_SHARED_NTHANDLE, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, ID3D11Device,
ID3D11DeviceContext, ID3D11Query, ID3D11Texture2D,
};
use windows::Win32::Graphics::Direct3D12::{ID3D12Device, ID3D12Resource};
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_NV12, DXGI_SAMPLE_DESC};
use windows::Win32::Graphics::Dxgi::{
DXGI_SHARED_RESOURCE_READ, DXGI_SHARED_RESOURCE_WRITE, IDXGIDevice, IDXGIResource1,
};
use windows::core::{Interface, PCWSTR};
pub struct D3d11SharedDecodeBridge {
d3d12_resource: ID3D12Resource,
d3d11_device: ID3D11Device,
d3d11_context: ID3D11DeviceContext,
d3d11_texture: ID3D11Texture2D,
shared_handle: HANDLE,
}
impl D3d11SharedDecodeBridge {
pub fn open(
d3d11_device: NativeHandle,
d3d12_device: NativeHandle,
width: u32,
height: u32,
) -> Result<Self, DecodeError> {
if width == 0 || height == 0 {
return Err(DecodeError::InvalidInput);
}
let raw11 = d3d11_device.get() as *mut std::ffi::c_void;
let borrowed11 =
unsafe { ID3D11Device::from_raw_borrowed(&raw11) }.ok_or(DecodeError::InvalidInput)?;
let d3d11_device: ID3D11Device = borrowed11.clone();
let raw12 = d3d12_device.get() as *mut std::ffi::c_void;
let borrowed12 =
unsafe { ID3D12Device::from_raw_borrowed(&raw12) }.ok_or(DecodeError::InvalidInput)?;
let d3d12_device: ID3D12Device = borrowed12.clone();
let d3d12_luid = unsafe { d3d12_device.GetAdapterLuid() };
let dxgi_device: IDXGIDevice = d3d11_device.cast().map_err(|_| DecodeError::Backend)?;
let adapter = unsafe { dxgi_device.GetAdapter() }.map_err(|_| DecodeError::Backend)?;
let adapter_desc = unsafe { adapter.GetDesc() }.map_err(|_| DecodeError::Backend)?;
let d3d11_luid = adapter_desc.AdapterLuid;
if d3d11_luid.LowPart != d3d12_luid.LowPart || d3d11_luid.HighPart != d3d12_luid.HighPart {
return Err(DecodeError::InvalidInput);
}
let desc = D3D11_TEXTURE2D_DESC {
Width: width,
Height: height,
MipLevels: 1,
ArraySize: 1,
Format: DXGI_FORMAT_NV12,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Usage: D3D11_USAGE_DEFAULT,
BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
MiscFlags: D3D11_RESOURCE_MISC_SHARED.0 as u32
| D3D11_RESOURCE_MISC_SHARED_NTHANDLE.0 as u32,
CPUAccessFlags: 0,
};
let mut texture: Option<ID3D11Texture2D> = None;
unsafe {
d3d11_device
.CreateTexture2D(&raw const desc, None, Some(&raw mut texture))
.map_err(|_| DecodeError::Backend)?;
}
let d3d11_texture = texture.ok_or(DecodeError::Backend)?;
let resource1: IDXGIResource1 = d3d11_texture.cast().map_err(|_| DecodeError::Backend)?;
let shared_handle: HANDLE = unsafe {
resource1
.CreateSharedHandle(
None,
DXGI_SHARED_RESOURCE_READ.0 | DXGI_SHARED_RESOURCE_WRITE.0,
PCWSTR::null(),
)
.map_err(|_| DecodeError::Backend)?
};
let mut d3d12_resource: Option<ID3D12Resource> = None;
unsafe {
d3d12_device
.OpenSharedHandle(shared_handle, &raw mut d3d12_resource)
.map_err(|_| DecodeError::Backend)?;
}
let d3d12_resource = d3d12_resource.ok_or(DecodeError::Backend)?;
let d3d11_context =
unsafe { d3d11_device.GetImmediateContext() }.map_err(|_| DecodeError::Backend)?;
Ok(Self {
d3d12_resource,
d3d11_device,
d3d11_context,
d3d11_texture,
shared_handle,
})
}
pub fn copy_from_decoded(
&self,
texture: NativeHandle,
subresource: u32,
) -> Result<(), DecodeError> {
let raw = texture.get() as *mut std::ffi::c_void;
let borrowed =
unsafe { ID3D11Texture2D::from_raw_borrowed(&raw) }.ok_or(DecodeError::InvalidInput)?;
let owning_device = unsafe { borrowed.GetDevice() }.map_err(|_| DecodeError::Backend)?;
if Interface::as_raw(&owning_device) != Interface::as_raw(&self.d3d11_device) {
return Err(DecodeError::InvalidInput);
}
unsafe {
self.d3d11_context.CopySubresourceRegion(
&self.d3d11_texture,
0,
0,
0,
0,
borrowed,
subresource,
None,
);
}
let query_desc = D3D11_QUERY_DESC {
Query: D3D11_QUERY_EVENT,
MiscFlags: 0,
};
let mut query: Option<ID3D11Query> = None;
unsafe {
self.d3d11_device
.CreateQuery(&raw const query_desc, Some(&raw mut query))
.map_err(|_| DecodeError::Backend)?;
}
let query = query.ok_or(DecodeError::Backend)?;
unsafe { self.d3d11_context.End(&query) };
unsafe { self.d3d11_context.Flush() };
let deadline = std::time::Instant::now() + std::time::Duration::from_millis(500);
loop {
let mut done: i32 = 0;
let poll = unsafe {
self.d3d11_context.GetData(
&query,
Some((&raw mut done).cast::<std::ffi::c_void>()),
u32::try_from(std::mem::size_of::<i32>()).unwrap_or(4),
0,
)
};
if poll.is_ok() && done != 0 {
return Ok(());
}
if std::time::Instant::now() > deadline {
return Err(DecodeError::Backend);
}
std::thread::sleep(std::time::Duration::from_millis(1));
}
}
pub fn d3d12_resource_handle(&self) -> Result<NativeHandle, DecodeError> {
to_native_handle(&self.d3d12_resource)
}
}
fn to_native_handle<T: Interface>(obj: &T) -> Result<NativeHandle, DecodeError> {
NativeHandle::new(Interface::as_raw(obj) as usize).ok_or(DecodeError::Backend)
}
impl Drop for D3d11SharedDecodeBridge {
fn drop(&mut self) {
if !self.shared_handle.is_invalid() {
let _ = unsafe { CloseHandle(self.shared_handle) };
}
}
}
#[cfg(test)]
#[path = "d3d11_shared_decode_bridge_tests.rs"]
mod tests;