Repository navigation
fix(agc): take RADV's fill-memory kernel as a buffer fill - #6543
Conversation
|
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
There was a problem hiding this comment.
The matcher reads the kernel the way RADV writes it (I decoded the 11 words: thread id times 16, v_min_u32 against the largest offset, one global_store_dwordx4), and with the PR agc_driver_clamped_global_fill passes on an RTX 5080, RADV (Radeon 610M) and lavapipe on main 5d9dcc4.
The test does not show the problem the PR fixes, though. With APS5_NO_FILL_HLE=1, so the dispatch takes main's ordinary path, ExecuteTests fails on all three devices with mismatch at 0 ... got 3405691582. With a 200 ms sleep added after FlushGpuWrites, the same run passes 3 of 3 on each device: the ordinary dispatch writes the right bytes, only after the readback. WaitIdle waits for the driver to handle the submission (Lifecycle.cpp:167) and FlushForAccess only syncs on pending writes a descriptor named (Recorder.cpp:1024), so nothing waits for these global stores. The red on main is that race, not the late DCC key tracking. Could the test assert what a later reader sees, for example that the fill's range is noted as a pending write when the dispatch is decoded, so a flush or a key wait syncs on it?
Correction to my earlier note here, which said vkQuake did not get far enough to check: that run had r_rtshadows on. With your settings (r_rtshadows 0, the demo loop, ANYPS5_PRESENT_MODE=immediate, 500 dumps of every second present at 1/16 scale, black meaning a mean RGB under 3) on a laptop with a Rembrandt iGPU (RADV), main 5d9dcc4 gave 25 of 500 black dumps and this PR 0 of 500. The black dumps show only the HUD and the pickup text; the PR's dumps at the same points show the scene. I also ran the ps5-opengl showcase 1.0.2 (PPSA99005), Animal Well and Balatro to their menus on main and with this PR, 90 to 120 s each: same pictures, no stop. PS5_RetroArch 2.0.0-beta-2 (PPSA99169) exits at start on both builds here, so it tells nothing about this change.
|
A title that runs this kernel, with one interaction to know about. Ruffle (PPSA68091), Rush 2 (PPSA69423) and Wave Race 64 (PPSA69424) all clear their CMASK, FMASK and DCC with it. Their dispatches match exactly: user data The interaction is with Ruffle's multisampled target, whose CMASK it clears to
#6669 reads the multisampled CMASK by its MSAA codes, so AI-assisted: yes |
|
Independent component validation of head
I also cross-checked the public RADV caller and NIR builder at that pinned source revision. The 64-thread group, clamped offset and repeated value agree with the matcher's range calculation for its guarded 16-byte-aligned subset. This does not establish other compiler encodings. Two coordination points:
These are bounded public component tests; I did not rerun vkQuake, Ruffle or Sonic, and make no new title/FPS claim. Project shader caching was disabled; driver caches were not controlled. AI-assisted validation and report: yes. Carry-forward validation on main585, 2026-10-17 UTCYour exact2fc4 commit is now carried with original authorship into fork integration6146eef46b, on main5859889 plus our retained bring-up fixes. The historical broad stack already had an equivalent11-word constant-fill matcher; our narrower integration had omitted it. This restores the existing PR's implementation rather than creating a duplicate. Full Windows/Linux relinker/library builds pass. A normal Sonic PPSA99902 RTX5080 run with all127 rebuilt libraries still reaches NOW LOADING and the resident8x DCC refusal at AI-assisted validation/reporting: Codex; author responsible. |
|
Windows cross-check of this PR alone and with #6669, on Ruffle (PPSA68091), Cross pressed at 8, 11 and 14 s ("Play the demo"):
So the Linux result above reproduces on Windows/NVIDIA: this PR should land together with or after #6669. With both, the demo's presented frames look the same as on vkQuake (PPSA99010), shareware data, demo loop,
One run with this PR stopped at 107.9 s on Tests: Not checked: AMD or Intel, Linux, the full GPU Written with Claude Code assistance (validation by a subagent, reviewed in this session). |
|
A data point for this PR, on Linux with an RTX 4070 Ti (NVIDIA 595.99.02), base With this PR (2fc4fe5), the CMASK and DCC clears of Rush 2 (PPSA69423), Wave Race 64 (PPSA69424) and Ruffle (PPSA68091) arrive as buffer fills. Without it, the multisampled target is imported before its clear lands, and all three stop at With #7250 on top, which takes those fills as the target's fast clear:
AI-assisted: yes (Claude Code) |
2fc4fe5 to
7bfb645
Compare
|
Thanks, all. I've pushed 7bfb645, rebased onto main 51cee90. @fma-rc, you're right: the old execution test failed on
With @milescoviello, @screamm, thanks for finding Ruffle. I've added #6669 under Depends on and noted why in the description: this PR orders Ruffle's @Amoenus, done: the description now says only this kernel shape with a 16-byte aligned address and largest offset is taken, and that RADV's 4-byte-aligned fills and other encodings still run as dispatches. |
|
vkQuake (PPSA99010) at the demo's first two pickups, from the dense dumps in the description (every second present at 1/16 scale). The top row is
|
fma-rc
left a comment
There was a problem hiding this comment.
Reran agc_driver_clamped_global_fill on 7bfb645 on the Rembrandt iGPU (680M, RADV). With APS5_NO_FILL_HLE=1 it failed 3 of 3 with the keys read 0000 against uploaded uncompressed, so the next key read drops the write; without it, it passed 3 of 3. The test now fails for the vkQuake reason and not the read-back race, which was my objection, so I'm clearing my earlier review. I have not run it on NVIDIA or lavapipe this time.
|
Rechecked on head
Not checked: AMD, Intel, Linux, Rush 2, Wave Race 64. Written with Claude Code assistance (validation by a subagent, reviewed in this session). |
|
An update to my data point from yesterday, on the current heads. #7253 is in
AI-assisted: yes (Claude Code) |
RADV initializes and fast-clears DCC keys with its fill_memory compute kernel, which stores through a global address. Run as an ordinary dispatch, its stores name no range. The DCC key tracking therefore saw a key fill only once it had landed in guest memory, which could be after later writes to the surface had already been recorded. vkQuake (PPSA99010) hits this on every item pickup: - the scene renders into a second color buffer; - RADV's UNDEFINED -> GENERAL barrier fills color buffer 0's keys with DCC_CLEAR_0000; - a screen-effects compute pass writes color buffer 0; - the UI pass draws on color buffer 0. The keys landed while the UI pass was being recorded. The refresh took them for a clear made after the compute pass, dropped its results and cleared the image to black. For the length of the pickup flash the scene was black, with only the HUD drawn. The fill HLE now matches the 16-byte variant of the kernel as a fill of min(threads * 16, user[4] + 16) bytes. The kernel takes its address in user[2..3], its largest store offset in user[4] and its value in user[5]. A key fill through it is handled where it is in the command stream.
7bfb645 to
2907ca6
Compare
|
Rebased onto current main (only conflict: the |

What
The fill HLE now takes RADV's
fill_memorycompute kernel as a buffer fill. RADV initializes and fast-clears DCC keys with this kernel, so the DCC key tracking now sees those key fills where they are in the command stream. This fixes vkQuake's black frames: for the length of every item-pickup flash, the scene went black and only the HUD was drawn.Title. vkQuake (PPSA99010): mihawk-99's PS5_vkQuake, rendering through their RADV fork PS5_Mesa (Mesa 25.2).
Cause. On a pickup, vkQuake's
r_passes.cruns its screen effects:color_buffers[1]instead of[0].color_buffers[0](0x4007000000, 3840x2160 A2B10G10R10, DCC keys at 0x4008fe0000) from UNDEFINED to GENERAL. RADV'sradv_init_color_image_metadatathen fills its keys withDCC_CLEAR_0000throughradv_fill_memory.screen_effectscompute pass reads[1], blends in the bonus-flash colour and writes[0]as a storage image.[0], and the post-process subpass presents it.radv_fill_memoryuses a compute kernel for 4 KiB and up (here 0x28000 bytes). The kernel stores through a global address, so as an ordinary dispatch its writes name no range: the recorder notes no write over the keys, andWaitForKeyWritershas nothing to wait for. The tracking first saw the new keys once the GPU had run the fill. That was partway through recording the UI pass, after the compute pass had already written[0].StorageTexture::Refreshthen read "uncompressed → 0000" as a fast clear made after those results: it dropped all 510 pending units and cleared the image to black.Debug traces of one pickup frame (local prints, not in this PR):
[0]dirty while its guest keys still read uncompressed, so no first-write store happens;With
APS5_STORE_AT_LABEL=1the pickup frames render correctly, at about 11 fps.How.
MatchClampedGlobalFill(BufferFill.cpp) matches the 16-byte variant of the kernel.v_mad_u32_u24, fourv_mov_b32of the value,v_lshlrev_b32by 4,v_min_u32with the largest offset,global_store_dwordx4,s_endpgm).user[2..3], the largest store offset inuser[4]and the value inuser[5], which is RADV'sfill_constants.min(thread * 16, user[4]), so the fill coversmin(threads * 16, user[4] + 16)bytes.fillBuffertries it afterMatchConstantDwordFill. From there the existing path applies:storeBufferFillclassifies the range, a key fill reachesNoteKeysFill/NoteKeysFillOnGpu, and the fill is recorded on the GPU.MatchConstantDwordFill(b83755a).libSceAgcDriver.mdlists what is not covered:Where it matters. Titles and ports that render through PS5 RADV. The fills RADV runs through this exact kernel now go through the fill HLE instead of a recompiled dispatch: DCC initializations, fast clears and
vkCmdFillBuffer, when they use the 16-byte variant with this dispatch shape and a 16-byte aligned address and largest offset. RADV's source allows 4-byte alignment; those fills, and other encodings of the kernel, still run as dispatches.According to @milescoviello's measurements, Ruffle (PPSA68091), Rush 2 (PPSA69423) and Wave Race 64 (PPSA69424) clear their CMASK, FMASK and DCC with the same kernel. On its own this PR makes Ruffle stop: its CMASK clear to
0xccnow lands before the target's first use, andmainreads that code as a DCC key. #6669 reads multisampled CMASK by its MSAA codes and fixes that. @milescoviello and @screamm both ran Ruffle to the limit with this PR and #6669 together, so this PR depends on #6669.Related: @gjonn's #5497 reads DCC keys behind another fill kernel's fast clear. It is a different kernel; neither PR depends on the other.
Tested
Windows 11, MinGW-w64 GCC 15.2.0, Release. The tests ran on this branch rebased onto main 51cee90; the vkQuake runs used the first head, on main 03c4cb5.
vkQuake (PPSA99010), RTX 4060 Laptop, driver 591.86, 3840x2160, demo loop with
r_rtshadows 0andANYPS5_PRESENT_MODE=immediate. Each run dumps every second presented frame at 1/16 scale (APS5_DUMP_FRAMES=500 APS5_DUMP_FRAMES_EVERY=2 APS5_DUMP_SCALE=16), and a dump counts as black when its mean RGB is under 3 per channel:main fa9365c (the change does not touch anything that differs between it and 03c4cb5): 25 of 500 dumps black, in two runs of 13 and 12 consecutive dumps, one at each of the demo's first two pickups. In an earlier run of a branch build, 30 of 500 were black, in three runs.
this PR: 0 of 500 black.
The demo's first two pickups ("You got the Grenade Launcher", "You get 3 rockets") show the scene under the pickup messages (attached image: top row main, bottom row this PR, at the same pickups). The
[dcc-keys]trace now showstitle fills keys 0x4008fe0000+0x28000 with 00000000 ...at each fill.Tests
New
agc_driver_clamped_global_fill, which submits everything throughsceAgcDriverSubmitDcb:0000, the same image store, and an end-of-pipe label;It passes 3 of 3 on the RTX 4060, and on the Intel UHD (
VK_LOADER_DRIVERS_DISABLE=*nv-vk*) and lavapipe.Negative check with
APS5_NO_FILL_HLE=1, so the kernel runs as onmain:once the fill landed, the keys read 0000 against uploaded uncompressed, so the next key read drops the write;the flush did not wait for the fill's range, the race @fma-rc described.RTX 4060, one at a time, on the first head:
agc_dcc_key_sharing,agc_display_buffer_dcc,agc_storage_integer_clear,agc_metadata_pass,agc_driver_cmask_fast_clear,agc_driver_constant_dword_fill,agc_driver_dcc_keys_first_write,agc_driver_depth_fast_clear,agc_driver_depth_fast_clear_submit,agc_driver_depth_htile_write,agc_driver_dword_pattern_fill,agc_driver_fill_cover_keys,agc_driver_memory_value_fill,agc_driver_multisample_fill_host_only,agc_driver_pattern_buffer_fill,agc_driver_pipelined_fill,agc_driver_staged_clear_sampled_read,agc_driver_native_depth_clears,agc_driver_native_depth_clear_refusalsandagc_driver_resource_cache_storage_clearpass. On the rebased head,cmask_fast_clear,constant_dword_fill,dcc_keys_first_write,dword_pattern_fill,fill_cover_keys,memory_value_fill,pattern_buffer_fillandpipelined_fillpass again.Intel UHD and lavapipe:
constant_dword_fill,dcc_keys_first_write,fill_cover_keys,pattern_buffer_fillandpipelined_fillpass.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)BufferFill.cpp(the dword pattern-fill matcher), and many open PRs add lines tolibSceAgcDriver.md; the overlap is textual only.