#![allow(unsafe_code)]
use crate::EncodeError;
use mediaway_common::{GpuBufferHandle, NativeHandle};
use windows::Win32::Foundation::{CloseHandle, GENERIC_ALL, HANDLE, HMODULE};
use windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE_UNKNOWN;
use windows::Win32::Graphics::Direct3D::D3D_FEATURE_LEVEL;
use windows::Win32::Graphics::Direct3D::D3D_FEATURE_LEVEL_11_0;
use windows::Win32::Graphics::Direct3D::D3D_FEATURE_LEVEL_11_1;
use windows::Win32::Graphics::Direct3D11::{
D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, D3D11_SDK_VERSION,
D3D11CreateDevice, ID3D11Device, ID3D11Device1, ID3D11Resource, ID3D11Texture2D,
};
use windows::Win32::Graphics::Direct3D12::{
D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_HEAP_FLAG_SHARED, D3D12_HEAP_PROPERTIES,
D3D12_HEAP_TYPE_DEFAULT, D3D12_MEMORY_POOL_UNKNOWN, D3D12_RESOURCE_DESC,
D3D12_RESOURCE_DIMENSION_TEXTURE2D, D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,
D3D12_RESOURCE_STATE_COMMON, D3D12_TEXTURE_LAYOUT_UNKNOWN, ID3D12Device, ID3D12Resource,
};
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC};
use windows::Win32::Graphics::Dxgi::{CreateDXGIFactory2, IDXGIAdapter, IDXGIFactory4};
use windows::core::{Interface, PCWSTR};
pub struct D3d12SharedEncodeBridge {
d3d12_resource: ID3D12Resource,
d3d11_device: ID3D11Device,
d3d11_texture: ID3D11Texture2D,
shared_handle: HANDLE,
}
impl D3d12SharedEncodeBridge {
pub fn open(d3d12_device: NativeHandle, width: u32, height: u32) -> Result<Self, EncodeError> {
if width == 0 || height == 0 {
return Err(EncodeError::InvalidInput);
}
let raw = d3d12_device.get() as *mut std::ffi::c_void;
let borrowed =
unsafe { ID3D12Device::from_raw_borrowed(&raw) }.ok_or(EncodeError::InvalidInput)?;
let d3d12: ID3D12Device = borrowed.clone();
let heap_props = D3D12_HEAP_PROPERTIES {
Type: D3D12_HEAP_TYPE_DEFAULT,
CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN,
CreationNodeMask: 1,
VisibleNodeMask: 1,
};
let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,
Alignment: 0,
Width: u64::from(width),
Height: height,
DepthOrArraySize: 1,
MipLevels: 1,
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Layout: D3D12_TEXTURE_LAYOUT_UNKNOWN,
Flags: D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET,
};
let mut resource: Option<ID3D12Resource> = None;
unsafe {
d3d12
.CreateCommittedResource(
&raw const heap_props,
D3D12_HEAP_FLAG_SHARED,
&raw const desc,
D3D12_RESOURCE_STATE_COMMON,
None,
&raw mut resource,
)
.map_err(|_| EncodeError::Backend)?;
}
let resource = resource.ok_or(EncodeError::Backend)?;
let shared_handle: HANDLE = unsafe {
d3d12
.CreateSharedHandle(&resource, None, GENERIC_ALL.0, PCWSTR::null())
.map_err(|_| EncodeError::Backend)?
};
let adapter_luid = unsafe { d3d12.GetAdapterLuid() };
let factory: IDXGIFactory4 = unsafe {
CreateDXGIFactory2(windows::Win32::Graphics::Dxgi::DXGI_CREATE_FACTORY_FLAGS(0))
}
.map_err(|_| EncodeError::Backend)?;
let adapter: IDXGIAdapter =
unsafe { factory.EnumAdapterByLuid(adapter_luid) }.map_err(|_| EncodeError::Backend)?;
let mut d3d11_device: Option<ID3D11Device> = None;
let mut feature_level = D3D_FEATURE_LEVEL::default();
unsafe {
D3D11CreateDevice(
&adapter,
D3D_DRIVER_TYPE_UNKNOWN,
HMODULE::default(),
D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT,
Some(&[D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0]),
D3D11_SDK_VERSION,
Some(&raw mut d3d11_device),
Some(&raw mut feature_level),
None,
)
}
.map_err(|_| EncodeError::Backend)?;
let _ = feature_level;
let d3d11_device = d3d11_device.ok_or(EncodeError::Backend)?;
let device1: ID3D11Device1 = d3d11_device.cast().map_err(|_| EncodeError::Backend)?;
let opened: ID3D11Resource = unsafe { device1.OpenSharedResource1(shared_handle) }
.map_err(|_| EncodeError::Backend)?;
let d3d11_texture: ID3D11Texture2D = opened.cast().map_err(|_| EncodeError::Backend)?;
Ok(Self {
d3d12_resource: resource,
d3d11_device,
d3d11_texture,
shared_handle,
})
}
pub fn d3d12_resource_handle(&self) -> Result<NativeHandle, EncodeError> {
to_native_handle(&self.d3d12_resource)
}
pub fn d3d11_device_handle(&self) -> Result<NativeHandle, EncodeError> {
to_native_handle(&self.d3d11_device)
}
pub fn d3d11_texture_handle(&self) -> Result<NativeHandle, EncodeError> {
to_native_handle(&self.d3d11_texture)
}
pub fn shared_handle(&self) -> Result<GpuBufferHandle, EncodeError> {
let handle =
NativeHandle::new(self.shared_handle.0 as usize).ok_or(EncodeError::Backend)?;
Ok(GpuBufferHandle::DirectXShared { handle })
}
pub fn as_dx11_handle(&self) -> Result<GpuBufferHandle, EncodeError> {
Ok(GpuBufferHandle::DirectX11 {
texture: self.d3d11_texture_handle()?,
subresource: 0,
})
}
}
fn to_native_handle<T: Interface>(obj: &T) -> Result<NativeHandle, EncodeError> {
NativeHandle::new(Interface::as_raw(obj) as usize).ok_or(EncodeError::Backend)
}
impl Drop for D3d12SharedEncodeBridge {
fn drop(&mut self) {
if !self.shared_handle.is_invalid() {
let _ = unsafe { CloseHandle(self.shared_handle) };
}
}
}