Skip to content

fix(agc): size CMASK by whole 1024x512 metablocks - #1237

Closed
CnRJay wants to merge 8 commits into
boykopovar:mainfrom
CnRJay:fix/agc-cmask-metablock-size
Closed

CnRJay wants to merge 8 commits into
boykopovar:mainfrom
CnRJay:fix/agc-cmask-metablock-size

Conversation

@CnRJay

@CnRJay CnRJay commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

What

Follow-up to #1072. The branch contains #1072's two commits; only the last commit (fix(agc): size CMASK by whole 1024x512 metablocks) is new.

#1072 takes a target's CMASK as one nibble per 8x8 tile, packed ((tiles + 1) / 2 bytes). The CMASK is actually laid out in 4 KiB metablocks of 128x64 tiles (1024x512 pixels), and the nibble addresses are swizzled within them. For 1920x1080 it spans 2x3 metablocks = 0x6000 bytes, not 16200. The nibbles of the lower-right tiles lie past the packed size, so:

  • the all-cleared check never looks at them, and
  • the expanded (0xFF) store leaves them fast-cleared.

A title that reads its CMASK back then sees those tiles as never drawn.

  • CmaskBytes(width, height) (State) returns ceil(w / 1024) * ceil(h / 512) * 4096, and DecodeColorBuffer uses it for ColorTarget::cmaskBytes.
  • The cmaskTests CMASK is one metablock (4096 bytes). The mixed case sets the last byte. New checks cover the sizes for 1920x1080, 960x544, 800x450, 1024x512 and 3840x2160.
  • A mixed CMASK (some tiles fast-cleared, some expanded) is now left as it is, and the draw goes ahead. feat(agc): render into CMASK fast-clear color targets #1072 refused it, but with the packed size the check rarely saw one. With the whole metablocks, the check also covers the swizzled nibbles past the packed size, so titles that draw over part of a fast-cleared target hit it (a tester saw new "not all fast-cleared or all expanded" skips in PPSA20515). The first such target is reported once on stderr ([gpu] CMASK of color target 0x… is mixed … left as it is), so logs show when a title relies on this. Only an all-cleared CMASK is materialized, as before. The fast-cleared tiles of a mixed CMASK keep their stale texels, and the TechnicalDebt line says so. cmaskTests now checks that a mixed CMASK leaves both the texels and the CMASK unchanged.
  • The TechnicalDebt line for CMASK fast clears is updated.

The size matches every CMASK clear PPSA23566 (UFC 6) issues, for 8, 16 and 64 bpp color formats. The title fills each CMASK with a compute fill of exactly this many bytes before using it:

Target Packed size (#1072) Game's fill = CmaskBytes
800x450 2812 0x1000
960x544 (3 targets, 3 formats) 4080 0x2000
1920x1080 16200 0x6000

The metablock size also follows from the title's own CMASK reader. Its address equation takes tile x >> 7 and tile y >> 6 as the metablock coordinates and steps 0x2000 nibbles per metablock.

Tested

Windows 11, MinGW, NVIDIA RTX (Vulkan).

  • PPSA23566 (UFC 6) reads its UI target's CMASK in a compute pass to build a per-tile mask of where the UI was drawn. The composite blends the UI only on those tiles. With the packed size, 210 of 4050 mask words were missing bits:

    • the bottom 48 pixel rows were black,
    • black diagonal streaks ran across the lower right of the EA splash.

    With this change all 4050 words are set and the splash renders cleanly. That run also includes the host-import fix from fix(agc): retire host imports of ranges mapped again #1235, which the title needs to read the CMASK at all.

  • ctest: 383 of 383 pass, including agc_driver_graphics with the new size checks.

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: feat(agc): render into CMASK fast-clear color targets #1072
  • AI-assisted: yes

🤖 Generated with Claude Code

alfalfa-star and others added 3 commits October 7, 2026 09:54
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>
A CMASK holds 4 KiB per 1024x512-pixel metablock, not one nibble per 8x8
tile of the target: 1920x1080 spans 0x6000 bytes, as titles clear it,
not 16200. The swizzled nibbles of the lower-right tiles lie past the
packed size, so the fast-clear check missed them and the expanded store
left them cleared.

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

Copy link
Copy Markdown
Collaborator

Thanks. The sizes match an independent model of the CMASK as 4 KiB metablocks of 128x64 tiles: 0x1000 for 800x450, 0x2000 for 960x544, 0x6000 for 1920x1080.

This is being tested now on top of main with #1235, which also covers #1072's render-target changes.

@alfalfa-star, if testing passes, the plan is to merge #1072 first and then this PR, so #1072 closes as merged. After that, #788 and #1073 will need a rebase. I'll post the results here.

CnRJay and others added 2 commits October 7, 2026 16:57
With the CMASK sized by whole metablocks, the check covers the swizzled
nibbles past the packed size, and titles that partly draw over a
fast-cleared target now show a mixed CMASK where the packed check saw a
uniform one. Refusing those draws drops them; the target is drawn as it
is instead, and only an all-cleared CMASK is materialized.

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

Copy link
Copy Markdown
Collaborator

Please drop 1cd113e52 and 541d1748c: drawing over a mixed CMASK with stale texels and a one-time warning is a fallback, and the rule here is exact or throw. Either keep the throw from 1c848903a, or decode the per-tile nibbles exactly.

CnRJay added 3 commits October 8, 2026 13:27
…lock-size

# Conflicts:
#	core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
#	core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
#	core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
#	docs/dev/TechnicalDebt.md
@CnRJay

CnRJay commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Closing: the CMASK metablock sizing (CmaskBytes, ceil(w / 1024) * ceil(h / 512) * 4 KiB) and its test landed in main together with #1072, so this branch now matches main. The mixed-CMASK fallback commits were reverted per review: a mixed CMASK throws, as in #1072. Exact per-tile decoding would be a separate PR.

🤖 Generated with Claude Code

@CnRJay CnRJay closed this Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs needs rebase Conflicts with main, rebase needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants