Files
punktfunk/packaging/windows/vdisplay-driver/pf-vdisplay/src/direct_3d_device.rs
T
enricobuehler c7ef0e411a feat(windows): pf-vdisplay IDD-push — HDR + pipelined zero-copy capture
HDR (display-driven, matching the WGC path):
- CTA-861.3 HDR EDID (BT.2020 primaries + HDR Static Metadata block) so Windows
  offers "Use HDR" on the virtual display. The host FOLLOWS the display's live
  advanced-color state, recreating the shared ring at the matching format
  (FP16 in HDR / BGRA in SDR) on a toggle — no freeze.
- Always emit Main10/BT.2020-PQ Rgb10a2 while the display is HDR; the client
  auto-detects PQ from the HEVC VUI (clients under-report VIDEO_CAP_10BIT).
  Generic HDR10 mastering SEI on every IDR.
- Generation-tagged `latest` (gen<<40|seq<<8|slot) + driver `is_stale` re-attach
  kill the toggle-time garbage frame and any stale-ring read.

Perf:
- Pipeline the encode loop (Capturer::pipeline_depth; IDD-push = 2): submit N+1
  before polling N so the convert/copy on the 3D engine overlaps the NVENC encode
  of N on the ASIC. PUNKTFUNK_IDD_DEPTH overrides (1 = synchronous).
- Rotating host output ring (OUT_RING) so the in-flight encode and the next
  convert never touch the same texture.
- HDR converts directly from the keyed-mutex slot's SRV into the output ring
  (drops the redundant slot->fp16 scratch copy); SDR copies the BGRA slot in.
  The slot mutex is held only across the convert/copy, not the encode.
  RING_LEN 3->6 for publish headroom.
- Capture-health diagnostic: new_fps vs repeat_fps under PUNKTFUNK_PERF (a low
  new_fps at a high send rate means the source isn't compositing, not an encode
  stall).

Validated live on the RTX box: 5120x1440@240 HDR streams; driver composes
~180 new fps, encode 240 fps @ ~4.3 ms p50.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 00:39:28 +02:00

96 lines
3.2 KiB
Rust

use std::sync::atomic::{AtomicI32, Ordering};
use windows::{
core::Error,
Win32::{
Foundation::LUID,
Graphics::{
Direct3D::D3D_DRIVER_TYPE_UNKNOWN,
Direct3D11::{
D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
D3D11_CREATE_DEVICE_PREVENT_ALTERING_LAYER_SETTINGS_FROM_REGISTRY,
D3D11_CREATE_DEVICE_SINGLETHREADED, D3D11_SDK_VERSION,
},
Dxgi::{CreateDXGIFactory2, IDXGIAdapter1, IDXGIFactory5, DXGI_CREATE_FACTORY_FLAGS},
},
},
};
#[derive(thiserror::Error, Debug)]
pub enum Direct3DError {
#[error("Direct3DError({0:?})")]
Win32(#[from] Error),
#[error("Direct3DError(\"{0}\")")]
Other(&'static str),
}
impl From<&'static str> for Direct3DError {
fn from(value: &'static str) -> Self {
Direct3DError::Other(value)
}
}
/// DIAGNOSTIC: live `Direct3DDevice` count. Each one holds an `ID3D11Device` whose NVIDIA UMD spawns
/// ~dozens of worker threads; if this climbs without bound across reconnects, devices are leaking.
pub static LIVE_DEVICES: AtomicI32 = AtomicI32::new(0);
#[derive(Debug)]
pub struct Direct3DDevice {
// The following are already refcounted, so they're safe to use directly without additional drop impls
_dxgi_factory: IDXGIFactory5,
_adapter: IDXGIAdapter1,
pub device: ID3D11Device,
/// The single (SINGLETHREADED) immediate context — used by the frame-push publisher's
/// `CopyResource` on the swap-chain processor thread (the one thread this device is touched from).
pub device_context: ID3D11DeviceContext,
}
impl Direct3DDevice {
pub fn init(adapter_luid: LUID) -> Result<Self, Direct3DError> {
let dxgi_factory =
unsafe { CreateDXGIFactory2::<IDXGIFactory5>(DXGI_CREATE_FACTORY_FLAGS(0))? };
let adapter = unsafe { dxgi_factory.EnumAdapterByLuid::<IDXGIAdapter1>(adapter_luid)? };
let mut device = None;
let mut device_context = None;
unsafe {
D3D11CreateDevice(
&adapter,
D3D_DRIVER_TYPE_UNKNOWN,
None,
D3D11_CREATE_DEVICE_BGRA_SUPPORT
| D3D11_CREATE_DEVICE_SINGLETHREADED
| D3D11_CREATE_DEVICE_PREVENT_ALTERING_LAYER_SETTINGS_FROM_REGISTRY,
None,
D3D11_SDK_VERSION,
Some(&mut device),
None,
Some(&mut device_context),
)?;
}
let device = device.ok_or("ID3D11Device not found")?;
let device_context = device_context.ok_or("ID3D11DeviceContext not found")?;
let live = LIVE_DEVICES.fetch_add(1, Ordering::Relaxed) + 1;
log::error!("Direct3DDevice::init OK — live D3D devices = {live}");
Ok(Self {
_dxgi_factory: dxgi_factory,
_adapter: adapter,
device,
device_context,
})
}
}
impl Drop for Direct3DDevice {
fn drop(&mut self) {
let live = LIVE_DEVICES.fetch_sub(1, Ordering::Relaxed) - 1;
log::error!("Direct3DDevice::drop — live D3D devices = {live}");
}
}