Skip to content

feat(agc): render into CMASK fast-clear color targets - #1072

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
alfalfa-star:feat/agc-cmask-fast-clear
Oct 8, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
alfalfa-star:feat/agc-cmask-fast-clear

Conversation

@alfalfa-star

Copy link
Copy Markdown
Contributor

What

Draws into a color target with CB_COLOR_INFO.FAST_CLEAR (bit 13) are no longer rejected. The fast clear is modeled on its CMASK:

  • DecodeColorBuffer reads the CMASK address (CB_COLOR0_CMASK, CB_COLOR0_CMASK_BASE_EXT) and the bytes one nibble per 8x8 tile spans (ColorTarget::cmaskAddress, cmaskBytes).
  • Before a draw renders into such a target, and in a fast-clear eliminate pass (RunColorMetadataPass), a CMASK whose nibbles are all 0 (fast-cleared) is materialized. The whole target is filled with the CB_COLOR_CLEAR_WORD texel: FillClear on its resident image, else guest memory on the CPU. The CMASK bytes are then stored as expanded (0xF nibbles). The final pixels are those of the draw followed by the eliminate pass.
  • An all-expanded CMASK changes nothing. A mixed CMASK throws, as do fast clears of DCC, mipmapped, 3D and array targets.

The CMASK reads and stores reuse the DCC key paths, because expanded nibbles and "uncompressed" keys are both 0xFF bytes. CurrentDccKeys waits for recorded work that writes the range (the title's fast-clear fill may still be pending on the GPU), and MarkDccUncompressed records the store into the open batch when the memory is imported. A TechnicalDebt line records what is not modeled. The CMASK layout is not decoded, which is why only uniform CMASKs are accepted.

materializeCmaskClear runs after the resident lookup's try block, so a refused CMASK fails the draw instead of leaving the target non-resident with the clear skipped.

Tested

Linux 7.2.9 (Arch), Mesa 26.2.4 RADV, RX 9070 XT.

  • PPSA14632 (LEGO Horizon Adventures) fast-clears its two 3840x2160 display buffers through CMASK (CB_COLOR_INFO 0xa024, CMASK right after the texels, clear words 0). Its final composite into the display buffer was dropped on every frame, so only black was presented (344 of 344 presents in a 150 s run). With this change, the PlayStation Studios logo and the intro render. A 200 s run has no CMASK refusals.
  • New cmaskTests in agc_driver_graphics cover the decode (address, size, the DCC, mipmapped and missing-address refusals) and the eliminate pass: a cleared CMASK becomes clear texels with an expanded CMASK, an expanded one leaves the texels alone, and a mixed one throws without changing them.
  • ctest: 386 of 386 pass.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
#1051 docs/dev/TechnicalDebt.md L255
#1177 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp L2077-2085
#1189 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp L644
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp L765, L2149
#1191 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp L1529
#1193 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp L1529
#1201 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp L1529

@alfalfa-star

Copy link
Copy Markdown
Contributor Author

On the overlap with #419: both PRs decode CB_COLOR0_CMASK/CMASK_BASE_EXT into ColorTarget::cmaskAddress. They cover different cases and use different models:

  • feat(agc): multisampled color and depth targets with CB resolves #419 handles multisampled targets. It hooks the title's zero fill of the CMASK (fill kernel or CP DMA) and clears the target on its next use. Single-sample CMASK compression stays refused there, and @MichonGoddijn231849 asked for it as a follow-up.
  • This PR handles single-sample targets. It reads the CMASK bytes when a draw or eliminate pass uses the target, so it does not depend on how the fill reached memory. LEGO's fill of its display buffer CMASK may still be pending on the GPU at that point, which is why CurrentDccKeys waits for recorded work.

Whichever PR merges second keeps both: one cmaskAddress field, the multisampled decode from #419 and the single-sample decode from here. I'm happy to do that rebase if #419 lands first.

New commit 44d855a: @ThisIsAkill reported on #419 that PPSA21567's DCC targets (CB_COLOR_INFO 0x10042730) set FAST_CLEAR, fill their CMASK with 0xffffffff, and keep the clear in the DCC keys, which is what radv does (radv_get_cmask_fast_clear_value). Those targets now decode, and the existing DCC path materializes their clear. A DCC target's CMASK that is not all expanded still throws. cmaskTests covers both cases, and the throw is checked to fail without the guard. ctest: 386 of 386 pass. I have not run PPSA21567 itself.

alfalfa-star and others added 2 commits October 8, 2026 07:58
Draws into a color target with CB_COLOR_INFO FAST_CLEAR (bit 13) were
rejected. The CMASK keeps one nibble per 8x8 tile: 0 means the tile is
fast-cleared and reads as CB_COLOR_CLEAR_WORD, 0xF means expanded (the
texels hold the color). The texture units and display scanout do not read
it, so a title runs a fast-clear eliminate pass before sampling or
flipping such a target.

DecodeColorBuffer now takes the CMASK address (CB_COLOR0_CMASK and
CB_COLOR0_CMASK_BASE_EXT) and the bytes one nibble per 8x8 tile spans.
Before a draw renders into the target, and in a fast-clear eliminate
pass, a CMASK whose nibbles are all 0 is materialized: the target is
filled with the clear texel (FillClear on its resident image, else
guest memory on the CPU) and the CMASK bytes are stored as expanded,
through the same GPU-ordered store as the DCC "uncompressed" keys
(0xFF either way). The result equals the draw followed by the eliminate
pass. An all-expanded CMASK changes nothing; a mixed one throws, as do
fast clears of DCC, mipmapped, 3D and array targets.

PPSA14632 (LEGO Horizon Adventures) fast-clears its two 3840x2160
display buffers through CMASK, so its final composite draw was dropped
on every frame and only black was presented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A color target with both DCC and CB_COLOR_INFO.FAST_CLEAR keeps its fast
clear in the DCC keys and fills its CMASK with 0xF (expanded), as radv
does (radv_get_cmask_fast_clear_value) and as PPSA21567 does
(CB_COLOR_INFO 0x10042730, reported on boykopovar#419). Such targets now decode;
their clear is materialized by the existing DCC path. A DCC target's
CMASK that is not all expanded throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the CMASK decode (address, size, refusals), the eliminate pass (cleared / expanded / mixed CMASK, DCC under an expanded CMASK) and cmaskTests; the TechnicalDebt line is in "Functional". Rebased onto current main (only a TechnicalDebt adjacency conflict); build is green and ctest passes except audio_out2_port_layouts, an unrelated flake that passes 3/3 on its own.

This overlaps #419 (both decode CB_COLOR0_CMASK into ColorTarget::cmaskAddress), so this one lands first and #419 rebases on top of this CMASK decode. Merging.

@adriadam10
adriadam10 force-pushed the feat/agc-cmask-fast-clear branch from 44d855a to b5be685 Compare October 8, 2026 06:08
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
@adriadam10
adriadam10 merged commit 56f3da2 into boykopovar:main Oct 8, 2026
4 checks passed
@oneandonlydean

Copy link
Copy Markdown
Collaborator

cmaskBytes (State.cpp:698) packs one nibble per 8x8 tile, but the CMASK is laid out in 4 KiB metablocks of 1024x512 pixels with the nibbles swizzled inside them, so at 1920x1080 it spans 0x6000 bytes, not 16200, and the all-cleared check and the expanded store miss the lower-right tiles. Please take 1c84890 from #1237 (the CmaskBytes size and its tests, without the mixed-CMASK commits after it) into this PR before it lands.

@alfalfa-star
alfalfa-star deleted the feat/agc-cmask-fast-clear branch October 8, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants