use crate::CaptureError;
use mediaway_common::{GpuDeviceHandle, NativeHandle};
use windows::Win32::Foundation::HMODULE;
use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_UNKNOWN};
use windows::Win32::Graphics::Direct3D11::{
D3D11_CREATE_DEVICE_DEBUG, D3D11_CREATE_DEVICE_FLAG, D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
D3D11_SDK_VERSION, D3D11CreateDevice, ID3D11Device,
};
use windows::Win32::Graphics::Dxgi::{
CreateDXGIFactory1, DXGI_ADAPTER_FLAG_SOFTWARE, IDXGIAdapter, IDXGIAdapter1, IDXGIFactory1,
};
use windows::core::Interface;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GpuAdapterInfo {
pub index: u32,
pub name: String,
pub vendor_id: u32,
pub device_id: u32,
pub dedicated_video_memory: u64,
pub is_hardware: bool,
}
pub fn enumerate_gpu_adapters() -> Result<Vec<GpuAdapterInfo>, CaptureError> {
let factory: IDXGIFactory1 =
unsafe { CreateDXGIFactory1() }.map_err(|_| CaptureError::Backend)?;
let mut out = Vec::new();
for index in 0.. {
let Ok(adapter) = (unsafe { factory.EnumAdapters1(index) }) else {
break;
};
let Ok(desc) = (unsafe { adapter.GetDesc1() }) else {
continue;
};
out.push(GpuAdapterInfo {
index,
name: adapter_name(&desc.Description),
vendor_id: desc.VendorId,
device_id: desc.DeviceId,
dedicated_video_memory: desc.DedicatedVideoMemory as u64,
is_hardware: (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) == 0,
});
}
Ok(out)
}
fn adapter_name(description: &[u16; 128]) -> String {
let len = description
.iter()
.position(|&c| c == 0)
.unwrap_or(description.len());
String::from_utf16_lossy(&description[..len])
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum GpuAdapterSelect {
#[default]
Default,
Index(u32),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GpuDeviceOptions {
pub adapter: GpuAdapterSelect,
pub video_support: bool,
pub debug_layer: bool,
}
impl Default for GpuDeviceOptions {
fn default() -> Self {
Self {
adapter: GpuAdapterSelect::Default,
video_support: true,
debug_layer: false,
}
}
}
pub struct GpuDevice {
#[allow(dead_code, reason = "held for its Drop side effect, not read")]
device: ID3D11Device,
handle: NativeHandle,
}
impl GpuDevice {
pub fn create(options: GpuDeviceOptions) -> Result<Self, CaptureError> {
let mut flags = D3D11_CREATE_DEVICE_FLAG(0);
if options.video_support {
flags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
}
if options.debug_layer {
flags |= D3D11_CREATE_DEVICE_DEBUG;
}
let device = match options.adapter {
GpuAdapterSelect::Default => create_device(None, D3D_DRIVER_TYPE_HARDWARE, flags)?,
GpuAdapterSelect::Index(index) => {
let factory: IDXGIFactory1 =
unsafe { CreateDXGIFactory1() }.map_err(|_| CaptureError::Backend)?;
let adapter: IDXGIAdapter1 = unsafe { factory.EnumAdapters1(index) }
.map_err(|_| CaptureError::InvalidInput)?;
let adapter: IDXGIAdapter = adapter.cast().map_err(|_| CaptureError::Backend)?;
create_device(Some(&adapter), D3D_DRIVER_TYPE_UNKNOWN, flags)?
}
};
let raw = Interface::as_raw(&device) as usize;
let handle = NativeHandle::new(raw).ok_or(CaptureError::Backend)?;
Ok(Self { device, handle })
}
#[must_use]
pub const fn handle(&self) -> GpuDeviceHandle {
GpuDeviceHandle::DirectX11(self.handle)
}
}
fn create_device(
adapter: Option<&IDXGIAdapter>,
driver_type: windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE,
flags: D3D11_CREATE_DEVICE_FLAG,
) -> Result<ID3D11Device, CaptureError> {
let mut device: Option<ID3D11Device> = None;
unsafe {
D3D11CreateDevice(
adapter,
driver_type,
HMODULE::default(),
flags,
None,
D3D11_SDK_VERSION,
Some(&raw mut device),
None,
None,
)
}
.map_err(|_| CaptureError::Backend)?;
device.ok_or(CaptureError::Backend)
}
#[cfg(test)]
#[path = "gpu_tests.rs"]
mod tests;