Problem
MTLD3D11CommandList::Reset frees every block of the list's CPU command ring, so the next recording on the recycled list mallocs a new 2 MB block (kStagingBlockSizeForDeferredContext). Wine's heap serves blocks that large straight from NtAllocateVirtualMemory and returns them with NtFreeVirtualMemory; on ARM64EC every such free makes FEX invalidate the range on all guest threads under its code-invalidation lock, and every allocation pays first-touch faults again. Dark Souls III records deferred contexts on its worker threads and did this ~950 times a second (~50 per frame): 1,465,664 frees of a 0x210000-byte range in a 26-minute session.
Change
Opt-in DXMT_CMDLIST_KEEP_BLOCK=1: Reset calls a new RingBumpState::free_blocks_keep_first(), which frees every block as before except the first one, which is kept (emptied) when it has the ring's normal size. All pointers into the ring are dead at that point, as with the existing free_blocks(~0). One [cmdlist-keep] log line when on. Unset or 0: unchanged.
Tested (iPhone Air, iOS 27.2, Dark Souls III, d3d11.dll built from this change together with #40, #41 and the companion DXMT_DYN_KEEP change)
- Frees of the 2 MB command-list range (the same counter as above): 1,465,664 in 26 minutes before → 26 in the 12-minute session with the switch.
- Frame rate, heavy scenes (
[gpu-perf]windows with > 300 draws/frame): median 21.5 → 36.7 fps, p10 15.2 → 28.8; after thermal throttling (performance cores at 1.31 GHz) 32–34 fps. 12-minute session, no freezes; the GPU is now ~88 % busy. - The fps numbers are for a combined run: the same session also turned on
DXMT_DYN_KEEP(#43), Madeira'sMADEIRA_THREAD_QOS(willfaust/Madeira#401) and wine'sMADEIRA_TLS_X64/MADEIRA_SYNC_LEAN(willfaust/wine#50, willfaust/wine#51); the DLL also cut one diagnostic log line ([bc-stream], ~50 lines/s) that is not part of this PR. The baseline is the same game and save with my previous settings. The share of the gain from this change alone was not measured. - This split of the change compiles (
-fsyntax-onlywith the arm64ec toolchain, both translation units that included3d11_context_impl.cpp); the DLL that ran on the device had both DXMT switches in one commit.
🤖 Generated with Claude Code