fix(windows): IDD-push audit highs — keyed-mutex timeout, two per-frame leaks, IDD_PUSH knob, pooled-device threading
Five verified findings from the IDD-push/pf-vdisplay deep audit: - Keyed-mutex acquire (BOTH endpoints): AcquireSync returns WAIT_TIMEOUT (0x102) / WAIT_ABANDONED (0x80) as SUCCESS-severity HRESULTs, which the windows-rs Result wrapper erases — a busy slot read as "acquired", so driver and host could race the same ring texture (torn frames) and the designed busy-skip backpressure was dead code. Both sides now classify the raw vtable HRESULT; WAIT_ABANDONED counts as acquired (ownership transfers — refusing it would wedge the slot forever). - Host SDR hot path leaked one ID3D11VideoProcessorInputView per converted frame: the D3D11_VIDEO_PROCESSOR_STREAM ManuallyDrop field suppressed the release after VideoProcessorBlt. Released by hand now, success or not. - Driver leaked IddCx's per-acquire surface reference (from_raw_borrowed on a TRANSFERRED reference — the MS sample Attach/Reset's it): the swap-chain surface set survived swap-chain destruction, the likely true root cause of the ~50 MB-per-reconnect VRAM loss that device pooling only mitigated. Now adopted via from_raw (publisher or not) and dropped pre-Finished. - PUNKTFUNK_IDD_PUSH removed: capture is unconditionally IDD-push, but the vdisplay manager still gated the lingering-monitor preempt (and render pin) on the knob, whose default was OFF — dev/CLI runs reused a lingering monitor whose IddCx swap-chain is dead (black reconnect). The preempt and the render-GPU pin are now unconditional; host.env comments no longer promise the removed DDA/WGC fallback. - Driver D3D device: dropped D3D11_CREATE_DEVICE_SINGLETHREADED (unsound since DEVICE_POOL shares one device across processors) and the pooled immediate context is now SetMultithreadProtected — two concurrent monitors' workers otherwise race an unlocked context (UB in the UMD). No wire-contract change (pf-driver-proto untouched); the driver fixes take effect on the next pf-vdisplay redeploy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -342,19 +342,28 @@ impl SwapChainProcessor {
|
||||
logged_frame = true;
|
||||
}
|
||||
// STEP 6: copy the acquired surface into the shared ring BEFORE FinishedProcessingFrame
|
||||
// (the surface is valid until the next ReleaseAndAcquire). The pointer is BORROWED —
|
||||
// `from_raw_borrowed` does NOT take IddCx's refcount — and the GPU-side copy is ordered
|
||||
// before the consumer via the slot keyed mutex. (Attach happens at the loop top.)
|
||||
if let Some(p) = publisher.as_mut() {
|
||||
// (the surface is valid until the next ReleaseAndAcquire). Every successful acquire
|
||||
// TRANSFERS one surface reference to the driver — the MS sample `Attach`es it into a
|
||||
// ComPtr and `Reset`s BEFORE FinishedProcessingFrame, warning that a driver which
|
||||
// "forgets to release the reference" leaves the surfaces alive after the swap-chain is
|
||||
// destroyed. Holding it (the old `from_raw_borrowed`) leaked the swap-chain's whole
|
||||
// surface set per assign/unassign cycle (reconnect, mode change, HDR flip) — so adopt
|
||||
// the reference UNCONDITIONALLY (publisher or not); it is released when `res` drops at
|
||||
// the end of this block. (Publisher attach happens at the loop top.)
|
||||
{
|
||||
let raw = buffer.MetaData.pSurface as *mut core::ffi::c_void;
|
||||
if !raw.is_null() {
|
||||
// SAFETY: `raw` is IddCx's live surface pointer (valid until the next
|
||||
// ReleaseAndAcquire); `from_raw_borrowed` does not consume the refcount.
|
||||
if let Some(res) = unsafe { IDXGIResource::from_raw_borrowed(&raw) }
|
||||
// SAFETY: `raw` is the live surface IddCx just handed us, carrying the acquire's
|
||||
// transferred reference; `from_raw` adopts exactly that reference (released on
|
||||
// drop, below — the queued GPU copy is unaffected: D3D defers destruction, and
|
||||
// the copy is ordered before the consumer via the slot keyed mutex).
|
||||
let res = unsafe { IDXGIResource::from_raw(raw) };
|
||||
if let Some(p) = publisher.as_mut()
|
||||
&& let Ok(tex) = res.cast::<ID3D11Texture2D>()
|
||||
{
|
||||
p.publish(&tex);
|
||||
}
|
||||
// `res` drops here → the acquire's surface reference is released, pre-Finished.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user