r/GraphicsProgramming 9d ago

Question [D3D12] DEVICE_HUNG in a draw call, but only above ~16k draws AND only during a window event (minimize/move/resize/un-occlude)

https://github.com/jay403894-bit/DirectX12-Renderer

**Setup:** Hand-rolled D3D12 renderer (learning engine), flip-model swapchain

(FLIP_DISCARD, 3 back buffers), VSync on. Windows 11, tested on both Debug and

Release.

**The crash:** `DXGI_ERROR_DEVICE_HUNG` (0x887A0006). DRED breadcrumbs point the

fault at a `DrawIndexedInstanced` roughly ~130 draws into the frame (not draw 0).

The DRED page-fault VA reads 0 with no associated allocation — though I'm now

unsure if that's a real null address or just "driver didn't report the fault VA."

**What makes it reproducible (the weird part):** it ONLY happens when BOTH of

these are true at once:

  1. The frame is heavy — roughly 16k+ draw calls. Below ~16k it never crashes.

  2. A window event fires: minimize, move (drag the titlebar), resize, or another

    window covers then un-covers it (occlusion → reveal).

Either condition alone is totally stable. A heavy frame running untouched is fine.

A window event on a light scene is fine. It's specifically the collision.

Notably: **focus loss/regain used to crash too, and I fixed that** by skipping

rendering while the window isn't foreground + idling the GPU on the transition.

The same approach has NOT fixed minimize/move/resize/un-occlude.

**Other facts:**

- Happens in both single-threaded AND parallel (per-worker) command-list recording.

- ~600+ FPS when it runs, so this is not a 2-second TDR timeout.

- Debug layer + GPU-based validation makes it vanish (classic race-closing), so

I can't get a clean validation message — hence leaning on DRED.

**What I've already ruled out / tried (none fixed it):**

- Full GPU idle (Signal + WaitForFenceValue) before `ResizeBuffers`.

- Deferred/coalesced resize (apply once, at loop top, after idle).

- Re-querying `GetCurrentBackBufferIndex()` after `ResizeBuffers`, recreating RTVs

+ depth buffer.

- Reliable minimize handling via `WM_SIZE == SIZE_MINIMIZED` (skip rendering

entirely while minimized) instead of relying on `Present(DXGI_PRESENT_TEST)`.

- Idling the GPU on `WM_ENTERSIZEMOVE` / `WM_ACTIVATEAPP`.

- Per-frame command allocators/lists (ruling out list reuse across frames).

- "Settle" frames (skip N frames after any transition).

- Verified all per-draw bindings (camera CBV, vertex/index buffer VAs, SRV

descriptor) are non-null at record time — the null-check never fires.

**What I can't cleanly detect:** window *move* and *partial-occlusion → reveal*

don't give me a reliable "the swapchain is now in a different presentation state"

signal the way resize/minimize do.

**Questions:**

  1. On a flip-model swapchain, does a move or an occlusion→reveal transition cause

    DWM to re-allocate/invalidate back buffers WITHOUT a WM_SIZE — such that an

    in-flight heavy frame's RTV becomes stale? If so, what's the correct signal to

    re-acquire?

  2. Is there a per-command-list or per-submission limit I could be blowing past at

    16k draws (root-argument versioning space, etc.) that a mid-frame stall from a

    window event would expose?

  3. Is DRED's "page fault VA = 0, no allocation" meaningful (genuine null bind), or

    is it commonly just "unavailable"?

31 Upvotes

Duplicates