use std::collections::HashSet;
use std::sync::OnceLock;
use windows::Win32::Foundation::HMODULE;
use windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE_HARDWARE;
use windows::Win32::Graphics::Direct3D11::{
D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, D3D11_SDK_VERSION,
D3D11CreateDevice, ID3D11Device, ID3D11Multithread, ID3D11VideoDevice,
};
use windows::Win32::Media::MediaFoundation::{IMFDXGIDeviceManager, MFCreateDXGIDeviceManager};
use windows::core::{GUID, Interface};
use crate::hw::select::HwCodec;
pub struct Gpu {
pub manager: IMFDXGIDeviceManager,
profiles: HashSet<u128>,
_device: ID3D11Device,
}
unsafe impl Send for Gpu {}
unsafe impl Sync for Gpu {}
pub fn gpu() -> Option<&'static Gpu> {
static GPU: OnceLock<Option<Gpu>> = OnceLock::new();
GPU.get_or_init(|| create().map_err(|e| log::info!("no D3D11 video device: {e}")).ok()).as_ref()
}
fn create() -> windows::core::Result<Gpu> {
let mut device = None;
unsafe {
D3D11CreateDevice(
None,
D3D_DRIVER_TYPE_HARDWARE,
HMODULE::default(),
D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT,
None,
D3D11_SDK_VERSION,
Some(&mut device),
None,
None,
)?;
}
let device: ID3D11Device = device.ok_or_else(|| windows::core::Error::from_hresult(windows::Win32::Foundation::E_FAIL))?;
unsafe {
let _ = device.cast::<ID3D11Multithread>()?.SetMultithreadProtected(true);
let video: ID3D11VideoDevice = device.cast()?;
let mut profiles = HashSet::new();
for i in 0..video.GetVideoDecoderProfileCount() {
if let Ok(g) = video.GetVideoDecoderProfile(i) {
profiles.insert(g.to_u128());
}
}
let mut token = 0;
let mut manager = None;
MFCreateDXGIDeviceManager(&mut token, &mut manager)?;
let manager = manager.ok_or_else(|| windows::core::Error::from_hresult(windows::Win32::Foundation::E_FAIL))?;
manager.ResetDevice(&device, token)?;
Ok(Gpu { manager, profiles, _device: device })
}
}
impl Gpu {
pub fn decodes(&self, codec: HwCodec) -> bool {
profiles(codec).iter().any(|p| self.profiles.contains(&p.to_u128()))
}
}
fn profiles(codec: HwCodec) -> &'static [GUID] {
use windows::Win32::Graphics::Direct3D11::*;
match codec {
HwCodec::H264 => &[D3D11_DECODER_PROFILE_H264_VLD_NOFGT, D3D11_DECODER_PROFILE_H264_VLD_FGT],
HwCodec::Hevc => &[D3D11_DECODER_PROFILE_HEVC_VLD_MAIN, D3D11_DECODER_PROFILE_HEVC_VLD_MAIN10],
HwCodec::Vp9 => &[D3D11_DECODER_PROFILE_VP9_VLD_PROFILE0, D3D11_DECODER_PROFILE_VP9_VLD_10BIT_PROFILE2],
HwCodec::Av1 => &[D3D11_DECODER_PROFILE_AV1_VLD_PROFILE0],
_ => &[],
}
}