refactor(windows-host): move resolve_render_adapter_luid to a neutral module (audit §9 / F1 pt 1)
The discrete-render-GPU LUID picker was display-utility living in the SudoVDA backend; moved it verbatim to a backend-neutral crate::win_adapter module (the plan's windows/adapter.rs). The IDD-push capturer + pf-vdisplay backend now depend on it as a PEER instead of reaching into vdisplay::sudovda — the first step in breaking the circular reach-in so SudoVDA can eventually be dropped (Goal 2). sudovda re-exports it for its own callers. Remaining F1 increments: the CCD/HDR helpers (resolve_gdi_name, set_advanced_color, advanced_color_enabled, set_active_mode, isolate/restore_displays_ccd) → a neutral win_display module. Verified: host clippy (nvenc) clean on the RTX box. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -884,7 +884,7 @@ pub fn spawn_observer(target: WinCaptureTarget, preferred: Option<(u32, u32, u32
|
|||||||
|
|
||||||
/// The discrete render GPU LUID (where NVENC runs), falling back to the monitor's `OsAdapterLuid`.
|
/// The discrete render GPU LUID (where NVENC runs), falling back to the monitor's `OsAdapterLuid`.
|
||||||
fn resolve_render_adapter_luid_or(fallback_packed: i64) -> LUID {
|
fn resolve_render_adapter_luid_or(fallback_packed: i64) -> LUID {
|
||||||
if let Some(l) = unsafe { crate::vdisplay::sudovda::resolve_render_adapter_luid() } {
|
if let Some(l) = unsafe { crate::win_adapter::resolve_render_adapter_luid() } {
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
LUID {
|
LUID {
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ mod spike;
|
|||||||
mod vdisplay;
|
mod vdisplay;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
mod wgc_helper;
|
mod wgc_helper;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
mod win_adapter;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod zerocopy;
|
mod zerocopy;
|
||||||
|
|
||||||
|
|||||||
@@ -41,9 +41,10 @@ use super::{Mode, VirtualDisplay, VirtualOutput};
|
|||||||
// is a real OS target id, so these operate identically). The shared MON_GEN/CURRENT_MON_GEN generation
|
// is a real OS target id, so these operate identically). The shared MON_GEN/CURRENT_MON_GEN generation
|
||||||
// counter is reused too, so the IDD-push stale-ring bail works regardless of which backend is active.
|
// counter is reused too, so the IDD-push stale-ring bail works regardless of which backend is active.
|
||||||
use super::sudovda::{
|
use super::sudovda::{
|
||||||
isolate_displays_ccd, resolve_gdi_name, resolve_render_adapter_luid, restore_displays_ccd,
|
isolate_displays_ccd, resolve_gdi_name, restore_displays_ccd, set_active_mode, SavedConfig,
|
||||||
set_active_mode, SavedConfig, CURRENT_MON_GEN, MON_GEN,
|
CURRENT_MON_GEN, MON_GEN,
|
||||||
};
|
};
|
||||||
|
use crate::win_adapter::resolve_render_adapter_luid;
|
||||||
|
|
||||||
// pf-vdisplay device-interface GUID (pf_vdisplay_proto::PF_VDISPLAY_INTERFACE_GUID_U128). Deliberately
|
// pf-vdisplay device-interface GUID (pf_vdisplay_proto::PF_VDISPLAY_INTERFACE_GUID_U128). Deliberately
|
||||||
// NOT SudoVDA's `{e5bcc234-…}` — we own this driver, so a private interface GUID signals it and avoids
|
// NOT SudoVDA's `{e5bcc234-…}` — we own this driver, so a private interface GUID signals it and avoids
|
||||||
|
|||||||
@@ -135,59 +135,10 @@ unsafe fn set_render_adapter(h: HANDLE, luid: LUID) -> Result<()> {
|
|||||||
.context("SudoVDA SET_RENDER_ADAPTER")
|
.context("SudoVDA SET_RENDER_ADAPTER")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve the LUID of the GPU that should RENDER the virtual display = the GPU that drives NVENC +
|
// `resolve_render_adapter_luid` moved to the backend-neutral `crate::win_adapter` (audit §9 / Goal 2:
|
||||||
/// Desktop Duplication (e.g. the RTX 4090). Default: the discrete adapter with the most
|
// it is display-utility, not SudoVDA-specific). Re-exported so this backend's own callers keep the short
|
||||||
/// `DedicatedVideoMemory`, skipping WARP / Basic-Render and the SudoVDA software adapter (≈0 VRAM).
|
// name; external callers (idd_push, pf_vdisplay) use `crate::win_adapter` directly.
|
||||||
/// `PUNKTFUNK_RENDER_ADAPTER=<substring>` forces a match by Description (Apollo's `adapter_name`).
|
pub(crate) use crate::win_adapter::resolve_render_adapter_luid;
|
||||||
/// `pub(crate)` so the IDD direct-push capturer can create its shared textures on the same discrete
|
|
||||||
/// GPU it pins here (and where NVENC runs).
|
|
||||||
pub(crate) unsafe fn resolve_render_adapter_luid() -> Option<LUID> {
|
|
||||||
use windows::Win32::Graphics::Dxgi::{CreateDXGIFactory1, IDXGIFactory1};
|
|
||||||
let want = std::env::var("PUNKTFUNK_RENDER_ADAPTER")
|
|
||||||
.ok()
|
|
||||||
.filter(|s| !s.is_empty());
|
|
||||||
let factory: IDXGIFactory1 = CreateDXGIFactory1().ok()?;
|
|
||||||
let mut best: Option<(LUID, u64, String)> = None;
|
|
||||||
let mut i = 0u32;
|
|
||||||
while let Ok(a) = factory.EnumAdapters1(i) {
|
|
||||||
i += 1;
|
|
||||||
let Ok(d) = a.GetDesc1() else { continue };
|
|
||||||
let name = String::from_utf16_lossy(&d.Description);
|
|
||||||
let name = name.trim_end_matches('\u{0}').to_string();
|
|
||||||
let lname = name.to_ascii_lowercase();
|
|
||||||
if lname.contains("basic render") || lname.contains("warp") {
|
|
||||||
continue; // never pin to the software rasterizer
|
|
||||||
}
|
|
||||||
if let Some(w) = &want {
|
|
||||||
if lname.contains(&w.to_ascii_lowercase()) {
|
|
||||||
tracing::info!(
|
|
||||||
adapter = name,
|
|
||||||
"render adapter chosen by PUNKTFUNK_RENDER_ADAPTER"
|
|
||||||
);
|
|
||||||
return Some(d.AdapterLuid);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let vram = d.DedicatedVideoMemory as u64; // SudoVDA software adapter ≈ 0 → loses to the dGPU
|
|
||||||
if best.as_ref().is_none_or(|(_, v, _)| vram > *v) {
|
|
||||||
best = Some((d.AdapterLuid, vram, name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match best {
|
|
||||||
Some((luid, vram, name)) => {
|
|
||||||
tracing::info!(
|
|
||||||
adapter = name,
|
|
||||||
vram_mb = vram / (1024 * 1024),
|
|
||||||
"render adapter chosen (max VRAM)"
|
|
||||||
);
|
|
||||||
Some(luid)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
tracing::warn!("no suitable render adapter found for SET_RENDER_ADAPTER");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct RemoveParams {
|
struct RemoveParams {
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
//! Backend-neutral DXGI adapter selection.
|
||||||
|
//!
|
||||||
|
//! The discrete render-GPU LUID picker used to live in the SudoVDA backend (`vdisplay::sudovda`) — a
|
||||||
|
//! historical accident, since it is display-utility, not SudoVDA-specific. It lives here so the capturers
|
||||||
|
//! (IDD-push) and the pf-vdisplay backend depend on it as a *peer* instead of reaching into the SudoVDA
|
||||||
|
//! module — breaking that circular reach-in so SudoVDA can eventually be dropped without losing this
|
||||||
|
//! helper (audit §9 / Goal 2). This is the plan's `windows/adapter.rs`.
|
||||||
|
|
||||||
|
use windows::Win32::Foundation::LUID;
|
||||||
|
|
||||||
|
/// Pick the discrete render GPU LUID: the adapter with the most `DedicatedVideoMemory`, skipping
|
||||||
|
/// WARP / Basic-Render and the SudoVDA software adapter (≈0 VRAM). `PUNKTFUNK_RENDER_ADAPTER=<substring>`
|
||||||
|
/// forces a match by Description (Apollo's `adapter_name`). Used by the IDD direct-push capturer (to
|
||||||
|
/// create its shared textures on the same discrete GPU it pins, where NVENC runs) and SET_RENDER_ADAPTER.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Creates + enumerates a DXGI factory; the COM calls run in the caller's apartment (the existing callers
|
||||||
|
/// already satisfy this).
|
||||||
|
pub(crate) unsafe fn resolve_render_adapter_luid() -> Option<LUID> {
|
||||||
|
use windows::Win32::Graphics::Dxgi::{CreateDXGIFactory1, IDXGIFactory1};
|
||||||
|
let want = std::env::var("PUNKTFUNK_RENDER_ADAPTER")
|
||||||
|
.ok()
|
||||||
|
.filter(|s| !s.is_empty());
|
||||||
|
let factory: IDXGIFactory1 = CreateDXGIFactory1().ok()?;
|
||||||
|
let mut best: Option<(LUID, u64, String)> = None;
|
||||||
|
let mut i = 0u32;
|
||||||
|
while let Ok(a) = factory.EnumAdapters1(i) {
|
||||||
|
i += 1;
|
||||||
|
let Ok(d) = a.GetDesc1() else { continue };
|
||||||
|
let name = String::from_utf16_lossy(&d.Description);
|
||||||
|
let name = name.trim_end_matches('\u{0}').to_string();
|
||||||
|
let lname = name.to_ascii_lowercase();
|
||||||
|
if lname.contains("basic render") || lname.contains("warp") {
|
||||||
|
continue; // never pin to the software rasterizer
|
||||||
|
}
|
||||||
|
if let Some(w) = &want {
|
||||||
|
if lname.contains(&w.to_ascii_lowercase()) {
|
||||||
|
tracing::info!(
|
||||||
|
adapter = name,
|
||||||
|
"render adapter chosen by PUNKTFUNK_RENDER_ADAPTER"
|
||||||
|
);
|
||||||
|
return Some(d.AdapterLuid);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let vram = d.DedicatedVideoMemory as u64; // SudoVDA software adapter ≈ 0 → loses to the dGPU
|
||||||
|
if best.as_ref().is_none_or(|(_, v, _)| vram > *v) {
|
||||||
|
best = Some((d.AdapterLuid, vram, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match best {
|
||||||
|
Some((luid, vram, name)) => {
|
||||||
|
tracing::info!(
|
||||||
|
adapter = name,
|
||||||
|
vram_mb = vram / (1024 * 1024),
|
||||||
|
"render adapter chosen (max VRAM)"
|
||||||
|
);
|
||||||
|
Some(luid)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
tracing::warn!("no suitable render adapter found for SET_RENDER_ADAPTER");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user