use crate::EncodeError;
use windows::Win32::Foundation::HMODULE;
use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL_11_0};
use windows::Win32::Graphics::Direct3D11::{
D3D11_BIND_SHADER_RESOURCE, D3D11_CPU_ACCESS_WRITE, D3D11_CREATE_DEVICE_FLAG, D3D11_MAP_WRITE,
D3D11_MAPPED_SUBRESOURCE, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, D3D11_USAGE,
D3D11_USAGE_DEFAULT, D3D11_USAGE_STAGING, D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
ID3D11Texture2D,
};
use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_NV12, DXGI_SAMPLE_DESC};
pub(crate) fn open_device() -> Result<(ID3D11Device, ID3D11DeviceContext), EncodeError> {
let mut device = None;
let mut ctx = None;
unsafe {
D3D11CreateDevice(
None,
D3D_DRIVER_TYPE_HARDWARE,
HMODULE(std::ptr::null_mut()),
D3D11_CREATE_DEVICE_FLAG(0),
Some(&[D3D_FEATURE_LEVEL_11_0]),
D3D11_SDK_VERSION,
Some(&raw mut device),
None,
Some(&raw mut ctx),
)
}
.map_err(|_| EncodeError::Backend)?;
let device = device.ok_or(EncodeError::Backend)?;
let ctx = ctx.ok_or(EncodeError::Backend)?;
Ok((device, ctx))
}
pub(crate) struct Dx11Upload {
ctx: ID3D11DeviceContext,
staging: ID3D11Texture2D,
gpu_texture: ID3D11Texture2D,
}
impl Dx11Upload {
pub(crate) fn new(
device: &ID3D11Device,
ctx: ID3D11DeviceContext,
width: u32,
height: u32,
) -> Result<Self, EncodeError> {
let staging = create_nv12_texture(
device,
width,
height,
D3D11_USAGE_STAGING,
D3D11_CPU_ACCESS_WRITE.0 as u32,
0,
)?;
let gpu_texture = create_nv12_texture(
device,
width,
height,
D3D11_USAGE_DEFAULT,
0,
D3D11_BIND_SHADER_RESOURCE.0 as u32,
)?;
Ok(Self {
ctx,
staging,
gpu_texture,
})
}
pub(crate) const fn gpu_texture(&self) -> &ID3D11Texture2D {
&self.gpu_texture
}
pub(crate) fn upload_cpu_nv12(
&self,
data: &[u8],
width: u32,
height: u32,
) -> Result<(), EncodeError> {
let w = width as usize;
let h = height as usize;
let y_plane_bytes = w * h;
let uv_rows = h / 2;
if data.len() < y_plane_bytes + uv_rows * w {
return Err(EncodeError::InvalidInput);
}
let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
unsafe {
self.ctx
.Map(&self.staging, 0, D3D11_MAP_WRITE, 0, Some(&raw mut mapped))
}
.map_err(|_| EncodeError::Backend)?;
let row_pitch = mapped.RowPitch as usize;
unsafe {
let base = mapped.pData.cast::<u8>();
for row in 0..h {
let src = &data[row * w..row * w + w];
let dst = std::slice::from_raw_parts_mut(base.add(row * row_pitch), w);
dst.copy_from_slice(src);
}
let uv_base = base.add(row_pitch * h);
for row in 0..uv_rows {
let src_off = y_plane_bytes + row * w;
let src = &data[src_off..src_off + w];
let dst = std::slice::from_raw_parts_mut(uv_base.add(row * row_pitch), w);
dst.copy_from_slice(src);
}
}
unsafe { self.ctx.Unmap(&self.staging, 0) };
unsafe { self.ctx.CopyResource(&self.gpu_texture, &self.staging) };
Ok(())
}
}
fn create_nv12_texture(
device: &ID3D11Device,
width: u32,
height: u32,
usage: D3D11_USAGE,
cpu_access_flags: u32,
bind_flags: u32,
) -> Result<ID3D11Texture2D, EncodeError> {
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: usage,
BindFlags: bind_flags,
CPUAccessFlags: cpu_access_flags,
MiscFlags: 0,
};
let mut texture = None;
unsafe { device.CreateTexture2D(&raw const desc, None, Some(&raw mut texture)) }
.map_err(|_| EncodeError::Backend)?;
texture.ok_or(EncodeError::Backend)
}