Skip to content

fix(agc): apply CMASK fast clears per tile - #2470

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:up639/cmask-gpu-clear
Oct 10, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
oneandonlydean:up639/cmask-gpu-clear

Conversation

@oneandonlydean

Copy link
Copy Markdown
Collaborator

What

materializeCmaskClear decodes the CMASK tile by tile instead of requiring it to be all fast-cleared or all expanded:

  • One nibble per 8x8 tile in 1024x512 metablocks of 4 KiB (CmaskLayout), addressed with the pipe-aligned SW_64KB_Z_X CMASK equation for 16 pipes from AMD addrlib (GFX10_CMASK_SW_PATTERN, Gfx10Lib::HwlComputeCmaskAddrFromCoord). The sizes match addrlib and the existing CmaskBytes, which now uses the layout.
  • Code 0 stores CB_COLOR_CLEAR_WORD into the tile's texels, edge tiles included; 0xF keeps them (Mesa ac_descriptors.h CMASK_NOAA_COLOR_CLEAR_REG / CMASK_NOAA_COLOR_EXPANDED). Any other code, a cleared tile of a DCC target, and another CMASK swizzle mode throw. The CMASK is then stored as expanded.
  • For a resident target whose CMASK is host-imported, the clear is a compute pass (CmaskClear.comp, TextureDetiler::DispatchCmaskClear) recorded in stream order. It reads the nibbles from the import, stores the clear into the cleared tiles through a UINT view of the image (StorageTexture::ElementView) and fills the CMASK with 0xFF, so it never waits for pending GPU writes to the CMASK. An invalid code it reads sets a bit in a host-visible word, and the next materialization throws on it.
  • Otherwise the CPU reads the CMASK (after pending writes land), fills the resident image when every tile is cleared, and stores the cleared tiles into guest memory otherwise.
  • A CMASK nothing has stored over since it was expanded is skipped (CollectWrites + StoredOver).
  • The decode requires CB_COLOR_ATTRIB3 FMASK_SW_MODE SW_64KB_Z_X with CMASK_PIPE_ALIGNED, a 4 KiB aligned writable CMASK and texels of at most 64 bits. CMASK_BASE_EXT is now part of the draw key (the decode already read it).

Call sites and the return value of materializeCmaskClear are unchanged. TechnicalDebt's CMASK entry is updated.

Why

A title fast-clears a target and then draws over part of it before the eliminate. With the all-or-nothing check, any CMASK with both cleared and expanded tiles threw ("not all fast-cleared or all expanded"). Reading the whole CMASK on the CPU at every draw into a fast-cleared target also waited for the GPU work that writes it.

Independent of #1337: it changes metadataPassResident and FillClear, which this PR does not touch. With both, an all-cleared CMASK on a target without a host import also stays on the GPU.

This and #2469 both add a test target in core/libs/prx/libSceAgcDriver/CMakeLists.txt, so whichever lands second needs a small rebase of that file; nothing else overlaps.

Tested

  • Linux (CachyOS, GCC 16.2, RTX 4090, NVIDIA 610.57.04), on main 6a2b99b: ctest 507/507. tools/check_conventions.py passes.

  • New agc_driver_cmask_fast_clear (Vulkan, in ctest): linear and SW_64KB_R_X targets that are fast-cleared and then partly drawn, drawn again and read back; partial, edge and checkerboard eliminates through the compute pass; a fast-cleared bottom edge row under a draw of the top rows; an invalid code read by the compute pass throws. Fails on main ("CMASK whose tiles are not all fast-cleared or all expanded is not modeled").

  • agc_driver_graphics: CMASK sizes and nibble addresses against addrlib for 11 surfaces up to 4096x4096; decode rejections (swizzle mode, pipe alignment, misaligned CMASK); draw key coverage of the decode; CPU eliminate of a partial CMASK, of a DCC decompress pass and of a CMASK whose nibbles past the target are expanded; invalid codes and a cleared tile of a DCC target throw. Fails on main with the same error.

  • A heavier title, frames per second, warm caches. It does not reach its menus on main alone, so both builds carry the fixes it needs (boot fixes, feat(agc): depth planes sampled as 2D arrays, cubes and integer views #425, perf(agc): size the storage and sampled texture caches to the device #1059, fix(recompiler): split image reads whose descriptor is a web of SRT Phis #1274, Fix/agc hold flip behind worker #1745 and two pending perf changes); "base" is that build. Gate run with presses at 200/210 s (two runs each after a warm-up run), and one level loaded directly (level start +30 to +150 s):

    build intro title menu first level after the menu (fixed frame-clock window) loaded level
    base 30.52 / 30.50 4.95 / 4.61 6.67 / 4.41* 7.14 / 6.92 0.669 / 0.620
    base + this 31.95 / 32.48 5.62 / 5.87 9.84 / 10.83 8.85 / 9.03 0.632 / 0.633
    change +5% +20% +56%* +28% none (bound by other GPU work)

    * the second base run's presses landed 30 s earlier on the frame clock, which moves its menu window; the menu change uses the first base run. No CMASK code was rejected in any run.

  • A lighter title that runs at 60 fps, on main + this: all screen checks pass, 59.9 fps menu and gameplay, no skipped draws, no single-frame flashes.

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

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 9, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 9, 2026
@Kikoarl

Kikoarl commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Tested on Linux (CachyOS x86_64, Intel RPL-S iGPU / Mesa 26.3.0-devel / Vulkan 1.4.363, GCC 16.2.1) at f44b3cbd:

  • Build: Built cleanly in RAM (/tmp/anyps5_build) with zero warnings/errors.
  • Conventions: python3 tools/check_conventions.py --base origin/main --head HEAD -> PASS (0 errors).
  • Verification: Verified CMASK per-tile fast clearing logic and tests in libSceAgcDriver. Correctly calculates tile clear strides and eliminates full surface clears when only dirty tiles require clearing.

LGTM!

@popcoder565

popcoder565 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Native Windows run, since no Windows results were posted. Setup: Windows 10 (19045), RTX 3060 Laptop (NVIDIA 616.92), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release. Head f44b3cbd, merged onto main 09a3462 with #2473, which current main needs to build on Windows, and #2451.

No title was run.

🤖 Addressed by Claude Code

@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed labels Oct 9, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 10, 2026
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed labels Oct 10, 2026
A CMASK that was neither all fast-cleared nor all expanded threw, so a
title that fast clears a target and then draws over part of it before the
eliminate could not run past it.

The CMASK is now decoded tile by tile: one nibble per 8x8 tile in
1024x512 metablocks of 4 KiB, addressed with the pipe-aligned
SW_64KB_Z_X CMASK equation for 16 pipes (addrlib
GFX10_CMASK_SW_PATTERN, Gfx10Lib::HwlComputeCmaskAddrFromCoord). Code 0
stores CB_COLOR_CLEAR_WORD into the tile's texels, 0xF keeps them; any
other code, a cleared tile of a DCC target and any other CMASK swizzle
mode throw. Edge tiles are included and the CMASK is then stored as
expanded.

For a resident target whose CMASK is host-imported the clear is a
compute pass recorded in stream order: it reads the nibbles from the
import, stores the clear into the cleared tiles through a UINT view of
the image and fills the CMASK expanded, so nothing waits for pending GPU
writes of the CMASK. Invalid codes it reads set a bit that the next
materialization throws on. Otherwise the CPU reads the CMASK and stores
the cleared tiles into guest memory, or fills the resident image when
every tile is cleared. A CMASK nothing has stored over since it was
expanded is skipped.

The decode requires CB_COLOR_ATTRIB3 FMASK_SW_MODE SW_64KB_Z_X with
CMASK_PIPE_ALIGNED, a 4 KiB aligned writable CMASK and texels of at most
64 bits, and CMASK_BASE_EXT is part of the draw key.

Tests: CMASK sizes and nibble addresses against addrlib; decode
rejections and draw key coverage; CPU eliminate of full, partial,
out-of-range-nibble and DCC decompress cases and invalid codes. The new
agc_driver_cmask_fast_clear GPU test draws over fast-cleared and partly
fast-cleared linear and SW_64KB_R_X targets, eliminates partial, edge
and checkerboard CMASKs through the compute pass and checks that an
invalid code it reads throws.
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 10, 2026
@github-actions

github-actions Bot commented Oct 10, 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
#1959 core/libs/prx/libSceAgcDriver/CMakeLists.txt L748
#2555 core/libs/prx/libSceAgcDriver/Graphics/shaders/CmaskClear.comp
core/libs/prx/libSceAgcDriver/tests/CmaskFastClear.cpp

@oneandonlydean
oneandonlydean merged commit 0011248 into boykopovar:main Oct 10, 2026
12 checks passed
SleglJan pushed a commit to SleglJan/AnyPS5 that referenced this pull request Oct 10, 2026
…u-clear

fix(agc): apply CMASK fast clears per tile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants