Skip to content

fix(agc): alpha-to-coverage from a 32_AR MRT0 export without a color target - #5521

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
codingreapermaps:feat/agc-alpha-coverage-without-color
Oct 14, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
codingreapermaps:feat/agc-alpha-coverage-without-color

Conversation

@codingreapermaps

@codingreapermaps codingreapermaps commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What

Undithered alpha-to-coverage (DB_ALPHA_TO_MASK = 0xaa01) was refused on draws without a color target on MRT0, and on 32_AR MRT0 exports. Those are the draws radeonsi produces for alpha-to-coverage without a color buffer: the color format of MRT0 becomes SPI_SHADER_32_AR so that the alpha still reaches the DB. PPSA12361 (Transport Fever 2: Console Edition) issues them when the camera zooms in on a map and stopped with alpha-to-coverage without a written MRT0 color target is unsupported.

  • DecodeState accepts alpha-to-coverage when no color target is written on MRT0, and accepts the 32_AR export format.
  • The MRT0 export format is read from SPI_SHADER_COL_FORMAT. The decoder masked the export formats to 0 for draws without a color target, so such a draw was refused as format 0 even with a 32_AR export.
  • A draw that enables alpha-to-coverage with no MRT0 export at all still throws, as does a component-swapped or packed MRT0 target. The debt entry for alpha-to-coverage records the radeonsi source.

Which channel of a 32_AR export carries the alpha is #5110's change, which this PR depends on: its first commit here is #5110 unchanged. Before #5110 the recompiler took the alpha from channel 3 and the coverage of these draws ignored it. Dithered alpha-to-coverage (#4451) is unrelated: this PR does not change which DB_ALPHA_TO_MASK values are accepted.

Tested

Windows 11, MinGW-w64 GCC 15.2.0, NVIDIA GeForce MX450 (driver 581.83).

  • agc_driver_alpha_to_coverage_tests: new DepthOnlyCoverage draws a depth-only triangle with alpha-to-coverage and a 32_AR export over a cleared depth surface, then counts the pixels a depth test still passes. With export mask 0x3, alpha 0.75 writes the depth everywhere and alpha 0.25 writes none. With export mask 0xF, alpha 0.25 in channel 1 and 0.75 in channel 3, it writes none: the coverage follows channel 1 and channel 3 is ignored. All alpha-to-coverage tests pass.
  • agc_driver_graphics_tests: 32_AR is accepted with a color target; with CB_TARGET_MASK 0 and a 32_AR export the state decodes with alpha-to-coverage and no target and passes the precheck; formats 1 and 2, and no MRT0 export without a target, are refused. All graphics tests pass.
  • agc_driver_pixel32_ar_export_tests from fix(shader): take the 32_AR export alpha from channel 1 as GFX10 does #5110 passes on this branch.
  • PPSA12361: with the other local changes the title still needs, zooming in on a map no longer stops the title.
  • Built and tested on main at 30f961266 with fix(shader): take the 32_AR export alpha from channel 1 as GFX10 does #5110.

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 under Silent stubs in their library's technical debt file
  • 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: fix(shader): take the 32_AR export alpha from channel 1 as GFX10 does #5110
  • Unblocks: PPSA12361 (no Game bring-up discussion yet)
  • GPUs tested: NVIDIA GeForce MX450, driver 581.83, Windows 11
  • AI-assisted: yes

🤖 Generated with Claude Code

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mode == 3u branch remaps only source components 0 and 1 to raw R and A, but the revised exported mask now permits exp.en bits 2 and 3 too. For a valid 32_AR export with EXP_EN=0xF (or 0xC), those source components are never copied into raw; after the export mapping the corresponding channels become zero/default, and in particular an alpha supplied in source component 3 is lost because A is taken from component 1. The new depth-only fixture appears to use only EXP_EN=0x3, so it would not catch this. Could you add a focused mask 0xF regression and either preserve the enabled channels consistently with the 32_AR semantics or reject unsupported enable masks?

@codingreapermaps

Copy link
Copy Markdown
Contributor Author

Thanks, you're right, and the overlap bot points at the same line: #5110 already fixes the 32_AR channel layout, with more sources, and its mask handling answers this. I've rebased onto main with #5110 as the first commit and dropped my own ExportVector change, so the mode == 3u branch here is #5110's: the enabled channels are masked to 0x3 and channel 1 goes to alpha, so with EXP_EN=0xF channels 2 and 3 are ignored, as GFX10 does (ac_nir_lower_ps_late.c).

I added the 0xF regression you asked for to DepthOnlyCoverage: alpha 0.25 in channel 1 and 0.75 in channel 3 with EXP_EN=0xF writes no depth, so the coverage follows channel 1. This PR is now only the decoder side (alpha-to-coverage without an MRT0 color target, 32_AR accepted, the MRT0 export format read from SPI_SHADER_COL_FORMAT), with Depends on: #5110.

@MarcZX10

Copy link
Copy Markdown
Contributor

Thanks, that addresses my specific concern. The new EXP_EN=0xF depth-only case checks that the alpha in channel 1 drives coverage while channel 3 is ignored for 32_AR, and the PR now records its dependency on #5110. The latest convention check passes; the full Linux/Windows jobs are path-skipped for this diff.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 14, 2026
@xXPBSXx

xXPBSXx commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Windows test of #5521: Windows 10 (19045), MinGW-w64 GCC 15.2, Release, ninja -j 4, ctest called directly; ctest ran without -V, so the logs of the passing runs do not show which GPU, if any, the tests used (not verified). Tree: main 54c2e14dc + merge of PR head 269d806f532c748e4dbfe5a608ae5c3267f71778 (local merge 30ce9121a, no conflict). Logs: rtx5521_*_N.log, rtx5521_result.txt.
3 ctest targets, 3 runs each, exit code 0 in 9 of 9 runs: agc_driver_alpha_to_coverage, agc_driver_graphics, agc_driver_pixel32_ar_export.
Not tested: that the new or changed tests fail without the change (no baseline run), other ctest targets, the AMD GPU unless named above, Linux, a title run.

AI-assisted (Claude Code).

@oneandonlydean oneandonlydean self-assigned this Oct 14, 2026
nikowolf123 and others added 2 commits October 14, 2026 17:56
A pixel export to an MRT whose SPI_SHADER_COL_FORMAT is 32_AR read its
alpha from export channel 3 (EN mask 0x9), the layout before GFX10. On
GFX10, which the PS5's gfx1013 is, the alpha is channel 1: Mesa's
emit_ps_color_export (ac_nir_lower_ps_late.c, index
gfx_level >= GFX10 ? 1 : 3), ACO and radeonsi in Mesa 22.0
(enabled_channels 0x3 with the alpha in channel 1) and LLPC's
FragmentColorExport.cpp (EXP_FORMAT_32_AR, exports[1]) all emit it
there. A console shader's `exp mrt0 v0, v1, off, off` therefore lost
its alpha: it stayed at the 1.0 default, so an alpha-only COLOR_32
ALT_REV target stored 1.0 and an R32 target blended with SRC_ALPHA
blended as if the alpha were 1. With channel 3 enabled, channel 3 was
taken instead.

ExportVector now reads channels 0 and 1 for 32_AR and puts channel 1
in the alpha component; channels 2 and 3 are ignored. The layout comes
from the compilers, not from a hardware measurement, and is recorded in
docs/dev/debt/shader.md.

agc_driver_pixel32_ar_export draws 32_AR exports with EN 0x3, 0xb and
0xf, as wave32 and wave64, into a COLOR_32 FLOAT ALT_REV target and a
COLOR_32 FLOAT target blended with SRC_ALPHA and ONE_MINUS_SRC_ALPHA,
and compares every stored value bit for bit. Without the fix every row
fails: the stored value uses an alpha of 1.0 or of channel 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… color target

radeonsi exports MRT0 as 32_AR when alpha-to-coverage is on and no color
buffer is bound, so a depth-only draw can depend on that alpha. The
decoder now accepts 32_AR and an absent MRT0 target, and reads the MRT0
export format from SPI_SHADER_COL_FORMAT, which it masked to 0 for
draws without a color target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@oneandonlydean
oneandonlydean force-pushed the feat/agc-alpha-coverage-without-color branch from 269d806 to c8662b6 Compare October 14, 2026 04:57
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Rebased on current main; only docs/dev/debt/libSceAgcDriver.md conflicted (kept both entries). #5110 is closed, so this PR now carries its commit itself.

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

github-actions Bot commented Oct 14, 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
#4451 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp L989-992
docs/dev/debt/libSceAgcDriver.md L10

@oneandonlydean
oneandonlydean merged commit 6438031 into boykopovar:main Oct 14, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants