You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[WSL / D3D12, NVIDIA] GPU timeouts (TDR, DXGI_ERROR_DEVICE_HUNG) after a depth-stencil CopyResource under sustained load #42283
NVIDIA RTX PRO 500 Blackwell Generation Laptop GPU, driver 32.0.15.9595 (also the Intel iGPU, not used)
The translation layer: Wine/Proton 11 with a D3D12 passthrough to the WSL runtime (patches available)
Repro Steps
There is no standalone reproduction yet; the hang happens in a game.
WSL2 with WSLg on an NVIDIA GPU (versions above), Ubuntu 24.04.
Steam for Linux in WSL, running Diablo IV (Steam build) through Wine/Proton 11 with a D3D12 passthrough: every D3D12 call is forwarded unchanged to /usr/lib/wsl/lib/libd3d12.so (no vkd3d, no Vulkan).
Play normally (town, open world, dungeons) at 3440x1440 or 1920x1200.
After 8–45 minutes the GPU times out: all displays freeze for ~2–5 s, Windows logs nvlddmkm event 153 (usually two, 3–4 s apart), and the game's device reports DXGI_ERROR_DEVICE_HUNG (0x887A0006).
Seen 16 times on 2026-11-13 and 7 times on 2026-11-14 (session table in the attached report). No GPU timeouts in the 30 days before, outside these sessions.
Where the GPU stops (our own breadcrumbs via WriteBufferImmediate; DRED returns no data on WSL), in all 7 captured hangs, on the direct queue:
DrawIndexedInstanced xN into depth A (R24G8_TYPELESS, 128x128 or 256x256)
ResourceBarrier A DEPTH_WRITE -> COPY_SOURCE, B SRV -> COPY_DEST
CopyResource(B, A) <- GPU stops here
ResourceBarrier B COPY_DEST -> SRV <- or here
A standalone program looping exactly this sequence (attached, depthcopy/)
ran >8 million iterations on WSL without a hang, so it does not reproduce alone.
Expected Behavior
The same workload runs without GPU timeouts, as it does on Windows with the
same GPU and driver.
Actual Behavior
A GPU timeout (TDR) every 8–45 minutes of play: nvlddmkm event 153, all
displays freeze for a few seconds, DXGI_ERROR_DEVICE_HUNG.
Copying the depth texture per subresource, or through a buffer
(texture -> buffer -> texture), does not prevent it.
Recovering takes 15 s to over 3 minutes, because the game has to release tens
of thousands of objects through the WSL runtime first (details in the report);
after more than about a minute the game's online session is lost.
Diagnostic Logs
Attachments: GPU timeout events (nvlddmkm 153), passthrough removal logs, and the standalone stress program (depthcopy). Full write-up in the first comment.
Summary
A D3D12 game (Diablo IV, Steam build) runs on WSL2 through a Wine passthrough
to /usr/lib/wsl/lib/libd3d12.so: every D3D12 call is forwarded unchanged to
the WSL runtime and the NVIDIA WSL driver. After 15–45 minutes of play the GPU
times out. Windows logs nvlddmkm event 153, every display freezes for about
2 s, and the device reports DXGI_ERROR_DEVICE_HUNG. With TdrDelay=10 the hang
still happens (event pairs 13 s apart), so the GPU is stuck, not slow. No
TDRs occurred on this machine in the previous 30 days outside these sessions.
Where the GPU stops
Our own breadcrumbs (WriteBufferImmediate MARKER_IN/MARKER_OUT around every
operation; DRED returns no auto-breadcrumbs or page-fault data on WSL) caught
7 hangs. All of them are in the same short sequence on the direct queue, in a
pass that renders a 128×128 or 256×256 view:
DrawIndexedInstanced ×N into depth A (R24G8_TYPELESS, ALLOW_DEPTH_STENCIL, committed, DEFAULT heap)
PSO: DSV D24_UNORM_S8_UINT, 4 BGRA8 RTs, depth LESS_EQUAL write,
stencil REPLACE/ALWAYS front, NEVER back, write mask 0xFC, cull back, CCW
ResourceBarrier: A DEPTH_WRITE -> COPY_SOURCE, B PIXEL|NON_PIXEL_SHADER_RESOURCE -> COPY_DEST
CopyResource(B, A) (same size and format)
ResourceBarrier: B COPY_DEST -> PIXEL|NON_PIXEL_SHADER_RESOURCE (or DEPTH_WRITE), A COPY_SOURCE -> DEPTH_WRITE
DrawInstanced(3) / DrawIndexedInstanced sampling B
The GPU is either inside the CopyResource or inside the barrier right after
it. Splitting the copy per subresource (CopyTextureRegion per plane) moves the
hang to the following barrier. The compute queue is merely waiting on the
direct queue (no cross-queue deadlock).
What does not reproduce it
A standalone program that loops exactly this sequence, with the same formats,
sizes, pipeline states, an async compute queue with cross-queue waits, and
130% VRAM over-commitment with continuous resource churn, ran more than
8 million iterations through the same passthrough on WSL without a hang.
Native Windows passes too. So the trigger depends on state we cannot see from
the API.
How often
Sessions on 2026-11-14 (one game launch each; times are local):
Launch GPU timeout Time to timeout Recovery (removal to new swap chain)
08:29 08:59 30 min 17 s
09:42 none (left at 10:45) > 63 min
10:46 11:02 16 min none: stopped with 415 resources unreleased
11:08 none (game exited at 13:20 for an unrelated reason) > 2 h 12 min
13:31 13:46 15 min not counted (our diagnostics interfered)
14:08 14:29 21 min 66 s
14:40 15:24 44 min 3 min 8 s
On 2026-11-13, a day of testing on the same machine, Windows logged 16 timeouts
(nvlddmkm event 153, in pairs).
Recovery takes minutes
After DXGI_ERROR_DEVICE_HUNG the game releases everything and creates a new
device (a D3D12 device is a per-adapter singleton, so the old one must be
fully released first). On WSL this takes 17 s to more than 3 minutes,
growing with play time, because the game by then holds tens of thousands of
objects:
At 15:24: about 51,000 device references, among them 21,700 pipeline
states and 27,900 resources (almost all small block-compressed textures,
64–512 px, each a separate CreateCommittedResource in a DEFAULT heap).
Pipeline states were released at only 150–500 per second (2–7 ms each).
A CPU profile of the game process during this phase: game code 38%,
glibc free 26%, libnvwgf2umx.so 24%, libd3d12core.so 6%, the
passthrough under 1%. For comparison, a test program releases 4,000
pipeline states that share one shader in 16 ms (about 4 µs each), so the
cost seems to be per unique pipeline.
A recovery longer than about a minute disconnects the game from its online
service, so for the player every timeout ends the session.
Related observations in the same sessions
After some timeouts the game keeps a few resources of the removed device
(12 small buffers in one case, 415 resources in another), so its next
D3D12CreateDevice() returns DXGI_ERROR_DEVICE_REMOVED and the game
cannot recover. This is the game's side, worked around in the
passthrough; mentioned because it is part of what the player sees.
Glyphs and parts of map textures go missing after long play (UI text,
e.g. "16.7%" drawn as "16. %"), but only with the game in Traditional
Chinese; in English it does not happen. Probably the game's glyph cache,
which changes far more often with Chinese text; not investigated further here.
Submission is about 10× more expensive than native: ExecuteCommandLists
230–450 µs, command list Reset/Close 60–75 µs, allocator Reset 18–30 µs,
queue Signal 35–40 µs, queue Wait 85 µs (measured per call around the WSL
runtime).
Environment
Windows 11 25H2 (26200.9711), WSL 3.0.5.0, kernel 6.18.58.1-1, WSLg 1.0.79
Direct3D (WSL) 1.611.1-81528511, DXCore 10.0.26100.1
NVIDIA RTX PRO 500 Blackwell Generation Laptop GPU, driver 32.0.15.9595 (also the Intel iGPU, not used)
The translation layer: Wine/Proton 11 with a D3D12 passthrough to the WSL runtime (patches available)
Attachments (02-attachments/)
windows-gpu-timeouts.txt: every nvlddmkm event 153 on this machine
(2026-11-12 to 2026-11-14).
passthrough-removal-log.txt: launches, device removals, and the resources
still alive when a recovery stalled (2026-11-14 afternoon and evening).
removal-release-progress.txt: device references and live objects every
0.25–2 s during those recoveries.
depthcopy/: the standalone stress program (source, shaders, run script).
Breadcrumb dumps of the 7 hangs are summarized above; the raw logs were
rotated away. The passthrough source is at (link to the patches).
Possibly related: #42261 reports a memcpy in libd3d12core.so (same 2023-10-20
build) overrunning a 128 KiB shared allocation under a real 3D workload, sometimes
surfacing as a lost device, and not reproducible with a synthetic harness. Our #42280 is also heap corruption inside libd3d12core. If a similar overrun hit
data shared with the GPU instead of a guard page, it could explain hangs like
these that only a real game triggers. This is a guess; we have no direct
evidence of an overrun in our case.
Activity
github-actions commentedon Nov 15, 2026
No logs.etl found in the archive. Make sure that you ran
collect-wsl-logs.ps1as administrator and that thelogs.etlfile is in the archive.Diagnostic information
chaoyen1980 commentedon Nov 15, 2026
Summary
A D3D12 game (Diablo IV, Steam build) runs on WSL2 through a Wine passthrough
to /usr/lib/wsl/lib/libd3d12.so: every D3D12 call is forwarded unchanged to
the WSL runtime and the NVIDIA WSL driver. After 15–45 minutes of play the GPU
times out. Windows logs nvlddmkm event 153, every display freezes for about
2 s, and the device reports DXGI_ERROR_DEVICE_HUNG. With TdrDelay=10 the hang
still happens (event pairs 13 s apart), so the GPU is stuck, not slow. No
TDRs occurred on this machine in the previous 30 days outside these sessions.
Where the GPU stops
Our own breadcrumbs (WriteBufferImmediate MARKER_IN/MARKER_OUT around every
operation; DRED returns no auto-breadcrumbs or page-fault data on WSL) caught
7 hangs. All of them are in the same short sequence on the direct queue, in a
pass that renders a 128×128 or 256×256 view:
DrawIndexedInstanced ×N into depth A (R24G8_TYPELESS, ALLOW_DEPTH_STENCIL, committed, DEFAULT heap)
PSO: DSV D24_UNORM_S8_UINT, 4 BGRA8 RTs, depth LESS_EQUAL write,
stencil REPLACE/ALWAYS front, NEVER back, write mask 0xFC, cull back, CCW
ResourceBarrier: A DEPTH_WRITE -> COPY_SOURCE, B PIXEL|NON_PIXEL_SHADER_RESOURCE -> COPY_DEST
CopyResource(B, A) (same size and format)
ResourceBarrier: B COPY_DEST -> PIXEL|NON_PIXEL_SHADER_RESOURCE (or DEPTH_WRITE), A COPY_SOURCE -> DEPTH_WRITE
DrawInstanced(3) / DrawIndexedInstanced sampling B
The GPU is either inside the CopyResource or inside the barrier right after
it. Splitting the copy per subresource (CopyTextureRegion per plane) moves the
hang to the following barrier. The compute queue is merely waiting on the
direct queue (no cross-queue deadlock).
What does not reproduce it
A standalone program that loops exactly this sequence, with the same formats,
sizes, pipeline states, an async compute queue with cross-queue waits, and
130% VRAM over-commitment with continuous resource churn, ran more than
8 million iterations through the same passthrough on WSL without a hang.
Native Windows passes too. So the trigger depends on state we cannot see from
the API.
How often
Sessions on 2026-11-14 (one game launch each; times are local):
Launch GPU timeout Time to timeout Recovery (removal to new swap chain)
08:29 08:59 30 min 17 s
09:42 none (left at 10:45) > 63 min
10:46 11:02 16 min none: stopped with 415 resources unreleased
11:08 none (game exited at 13:20 for an unrelated reason) > 2 h 12 min
13:31 13:46 15 min not counted (our diagnostics interfered)
14:08 14:29 21 min 66 s
14:40 15:24 44 min 3 min 8 s
On 2026-11-13, a day of testing on the same machine, Windows logged 16 timeouts
(nvlddmkm event 153, in pairs).
Recovery takes minutes
After DXGI_ERROR_DEVICE_HUNG the game releases everything and creates a new
device (a D3D12 device is a per-adapter singleton, so the old one must be
fully released first). On WSL this takes 17 s to more than 3 minutes,
growing with play time, because the game by then holds tens of thousands of
objects:
At 15:24: about 51,000 device references, among them 21,700 pipeline
states and 27,900 resources (almost all small block-compressed textures,
64–512 px, each a separate CreateCommittedResource in a DEFAULT heap).
Pipeline states were released at only 150–500 per second (2–7 ms each).
A CPU profile of the game process during this phase: game code 38%,
glibc free 26%, libnvwgf2umx.so 24%, libd3d12core.so 6%, the
passthrough under 1%. For comparison, a test program releases 4,000
pipeline states that share one shader in 16 ms (about 4 µs each), so the
cost seems to be per unique pipeline.
A recovery longer than about a minute disconnects the game from its online
service, so for the player every timeout ends the session.
Related observations in the same sessions
After some timeouts the game keeps a few resources of the removed device
(12 small buffers in one case, 415 resources in another), so its next
D3D12CreateDevice() returns DXGI_ERROR_DEVICE_REMOVED and the game
cannot recover. This is the game's side, worked around in the
passthrough; mentioned because it is part of what the player sees.
Glyphs and parts of map textures go missing after long play (UI text,
e.g. "16.7%" drawn as "16. %"), but only with the game in Traditional
Chinese; in English it does not happen. Probably the game's glyph cache,
which changes far more often with Chinese text; not investigated further here.
Submission is about 10× more expensive than native: ExecuteCommandLists
230–450 µs, command list Reset/Close 60–75 µs, allocator Reset 18–30 µs,
queue Signal 35–40 µs, queue Wait 85 µs (measured per call around the WSL
runtime).
Environment
Windows 11 25H2 (26200.9711), WSL 3.0.5.0, kernel 6.18.58.1-1, WSLg 1.0.79
Direct3D (WSL) 1.611.1-81528511, DXCore 10.0.26100.1
NVIDIA RTX PRO 500 Blackwell Generation Laptop GPU, driver 32.0.15.9595 (also the Intel iGPU, not used)
The translation layer: Wine/Proton 11 with a D3D12 passthrough to the WSL runtime (patches available)
Attachments (02-attachments/)
windows-gpu-timeouts.txt: every nvlddmkm event 153 on this machine
(2026-11-12 to 2026-11-14).
passthrough-removal-log.txt: launches, device removals, and the resources
still alive when a recovery stalled (2026-11-14 afternoon and evening).
removal-release-progress.txt: device references and live objects every
0.25–2 s during those recoveries.
depthcopy/: the standalone stress program (source, shaders, run script).
Breadcrumb dumps of the 7 hangs are summarized above; the raw logs were
rotated away. The passthrough source is at (link to the patches).
chaoyen1980 commentedon Nov 15, 2026
Possibly related: #42261 reports a memcpy in libd3d12core.so (same 2023-10-20
build) overrunning a 128 KiB shared allocation under a real 3D workload, sometimes
surfacing as a lost device, and not reproducible with a synthetic harness. Our
#42280 is also heap corruption inside libd3d12core. If a similar overrun hit
data shared with the GPU instead of a guard page, it could explain hangs like
these that only a real game triggers. This is a guess; we have no direct
evidence of an overrun in our case.