Fixes this crash in GTA San Andreas – The Definitive Edition when it runs with -dx12 and City Glow Effect is on, on AMD (and other) GPUs:
LowLevelFatalError [File:Unknown] [Line: 681]
CommandList->Close() failed
at .../D3D12RHI/Private/D3D12CommandList.cpp:144
with error E_INVALIDARG
The usual workaround was to turn City Glow off. With this fix you can leave it on.
It also fixes the DX12 stutter while idle (standing still, AFK, or only moving the mouse): a 45–65 ms hitch every few frames that goes away as soon as you walk.
And the stuck car engine loop in the rain: the rev/acceleration or throttle-off ("brake") sound of your car keeps looping while you stand still, until you leave the car, and tyre, collision and other sounds go silent. It happens at high frame rates, which is why it looks DX12-only.
Copy DX12AMDFix.asi into <Game>\Gameface\Binaries\Win64\ (an ASI loader is required). It writes DX12AMDFix.log next to itself.
The D3D12 debug layer (-dx12 -d3ddebug) shows the invalid call just before the crash:
ID3D12CommandList::RSSetShadingRate: This device has D3D12_FEATURE_DATA_D3D12_OPTIONS6::AdditionalShadingRatesSupported==FALSE,
so shading rates of 2x4, 4x2, and 4x4 cannot be used. A shading rate of D3D12_SHADING_RATE_4X4 was specified.
The city glow material asks for 4x4 variable-rate shading. DE's FD3D12CommandContext::RHISetShadingRate passes the material's rate straight to ID3D12GraphicsCommandList5::RSSetShadingRate. The 2x4, 4x2 and 4x4 rates are optional (AdditionalShadingRatesSupported): NVIDIA supports them, AMD RDNA does not. On those GPUs the call is invalid, so the command list fails to close and UE stops the game.
The mod hooks RHISetShadingRate, which it finds by signature, so it works on any exe build with that code. It reads the cap once from the command list's device. When the cap is missing, it lowers 2x4, 4x2 and 4x4 to 2x2, the coarsest rate every VRS GPU supports. On GPUs that have the cap, nothing changes. The glow is a soft, low-detail effect, so 2x2 shading looks the same.
Cause, measured on SA DE with an RX 7800 XT (stack sampling of the game, render and RHI threads, plus logging every render-graph texture DE creates):
- While the player is idle, DE renders a 2048-tall view only every few frames, so UE's scene render targets keep switching between 2560×1440 and 2560×2048. Every texture was created in pairs of those two heights, about 380 per second.
- UE 4.26's default
r.SceneRenderTargetResizeMethod 0resizes the targets to each request, so each switch re-creates all of them (ID3D12Device::CreateCommittedResource). AMD's D3D12 driver spends 40–65 ms on each batch (insideamdxc64.dll). On DX11, creating textures is cheap, so DX11 doesn't stutter. - While walking, the large view is requested every frame, so the size never changes and nothing is re-created.
Fix: the mod sets r.SceneRenderTargetResizeMethod to 2 ("grow to the largest requested size"). It finds the variable through its registration code, not a fixed address. The render targets then only grow, so nothing is re-created; the cost is the VRAM for the 2048-tall targets, which the game allocates anyway while you move.
Found with a probe that logged DE's audio channels and the player car's engine sounds while the loop was playing.
- In the rain, every car plays a rain-drop sound every 3rd frame (
ProcessRainOnVehicle, a frame counter from the original game). At the original 30 fps that is 10 drops per second. At 140 fps it is about 47 per second for your car alone, and the probe saw them on 20+ of DE's 32 sound channels at once. Other sounds then lose their channels (tyres, collisions), and new engine sounds fail to start. - DE added a hand-over to the player car's engine sounds that the original game doesn't have. When an engine sound is cancelled, DE stops it and parks it in a slot. Every frame, the parked sound is stopped once a new engine sound has started playing; until then DE un-stops it, so there is no gap.
- When the new engine sound fails to start (the flooded channels above), the parked sound is revived every frame forever. That is the loop. Pressing the throttle replaces it; releasing it parks the acceleration sound, which then loops the same way.
Fixes, both hooks found by signature:
ProcessRainOnVehicleruns at most 30 times per second per car, so the rain drops come at the original rate at any frame rate.- A parked engine sound gets 500 ms to be replaced; after that it is stopped the same way DE stops it. A normal hand-over takes a few audio buffers (tens of ms), so normal driving sounds the same.
build.bat (VS 2022 x64 build tools). Uses MinHook.