Skip to content

fix(agc): rasterize a multisampled target without MSAA_ENABLE at the pixel center - #5882

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
billythemenace2:fix/agc-msaa-target-without-msaa-enable
Oct 15, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
billythemenace2:fix/agc-msaa-target-without-msaa-enable

Conversation

@billythemenace2

@billythemenace2 billythemenace2 commented Oct 14, 2026 •

Copy link
Copy Markdown

Requested by billy

What

Before: a draw whose PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES is above 0 while PA_SC_MODE_CNTL_0.MSAA_ENABLE is clear threw "multisampled targets rasterized without MSAA_ENABLE are unsupported". SONIC SUPERSTARS (PPSA06888) stops on such a draw: a 2x color target, no depth target, no alpha-to-coverage, PA_SC_AA_CONFIG 0x00108001, PA_SC_MODE_CNTL_0 0, nonstandard sample locations (0xc44c).

After: such a draw keeps the target's sample count and puts every sample at the pixel center, the representation #4414 already uses for a single-sample scan into a multisampled target. Points, lines, a depth target and alpha-to-coverage still throw.

Where the semantics come from (not measured on hardware; the hardware oracle runs compute kernels only):

  • On GFX10 and later, MSAA_ENABLE is what makes the rasterizer use the programmed sample locations: RADV 061bc615 sets it for 1x user sample locations because they have no effect without it. The MSAA_ENABLE on a single-sample target debt entry relies on the same commit.
  • radeonsi sets MSAA_ENABLE from the GL multisample state (si_state.c, mesa-25.2.0). With multisampling disabled, GL rasterizes at the pixel center and gives the fragment full coverage, so every sample gets the pixel's value.
  • AMD's Evergreen register reference describes the bit as "Enable MultiSample AA in DX10 and below. Used for lines in DX10.1 and above". That predates the RADV finding for GFX10, which shows the bit still changes triangle sampling there. Lines are left throwing, because an aliased line on the hardware is not a Vulkan rectangular line.

The debt entry in docs/dev/debt/libSceAgcDriver.md records that the result with MSAA_NUM_SAMPLES above 0 is inferred.

How

DecodeState used to refuse the combination after decoding the depth target and alpha-to-coverage state. It now accepts it for filled primitives without either, and sets customSampleLocations with every location at the center. That is the same path SampleLocations already serves for #4414. The Graphics.cpp case that expected the refusal now checks the sample count and the centered locations, and that alpha-to-coverage still throws.

Tested

Windows 11, MinGW-w64 GCC 15, Release. agc_driver_graphics and agc_driver_msaa_enable_single_sample pass, and tools/check_conventions.py --base origin/main passes. SONIC SUPERSTARS (PPSA06888), on main with #5865, #5375 and #5456 merged locally, gets past this stop and reaches the next one: a shader reading a multisampled image. I did not compare the picture with a console. Not tested: Linux, other GPU vendors.

Checklist

🤖 Generated with Claude Code

@mrfelek

mrfelek commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Real-title check. METAL GEAR SOLID: Peace Walker (PPSA18216), Linux, RTX 3080 Ti (NVIDIA 615.71.09), main e34c479 plus #5439, #5915 and this PR, with the launcher hand-over from #5268. After #5915 clears the single-sample draw over a 4-sample depth target, the frame stops at this PR's multisampled targets rasterized without MSAA_ENABLE are unsupported for points, lines, a depth target or alpha-to-coverage (the !result.depth check in State.cpp). The draw is a depth plus colour pair with MSAA_ENABLE clear: PA_SC_AA_CONFIG 0x0020c002, DB_EQAA 0x00010002, DB_Z_INFO 0xa290098b (4 samples), DB_DEPTH_CONTROL 0x00000710 and 0x00000711, CB_COLOR0_INFO 0x00008028, CB_COLOR0_ATTRIB 0x00012000 (4 samples, 4 fragments), no PA_SC_MODE_CNTL_0 bit 0 (value 0x2). So this is the case discussed on #5268 as draw B. I can rerun with more registers if useful.

AI-assisted; the runs are from my machine.

@SandstromPL

SandstromPL commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

MGS Peace Walker (PPSA18216, #5268) reaches this refusal with a depth target bound. On main + #5439 + #5915 + this PR, mrfelek's run stops at draw B: PA_SC_AA_CONFIG 0x0020c002 (4x), MSAA_ENABLE clear, DB_Z_INFO 0xa290098b (4x), CB_COLOR0_ATTRIB 0x00012000 (4x/4f), from #5268 (reply in thread). #5915 handles the single-sample-rasterizer version with a depth target by centring the depth samples too. Would you extend this PR to a depth target the same way, or would you prefer a follow-up after it merges? Happy to send that follow-up.
AI-assisted

@billythemenace2

Copy link
Copy Markdown
Author

@mrfelek @SandstromPL Thanks for the Peace Walker data. I'd keep this PR to the case it was checked on, filled primitives with no depth target and no alpha-to-coverage, and take the depth target in a follow-up after it merges. #5915 already centres the depth samples for the single-sample-rasterizer case, so the depth handling belongs with that work. @SandstromPL, please go ahead with the follow-up.

AI-assisted (Claude Code).

@SandstromPL

Copy link
Copy Markdown
Contributor

One thing for this PR on current main: since #5861, decodeSampleLocations sets sampleLocationGrid for a pattern that varies between pixels (State.cpp:438), and this block fills the locations with zeros but leaves the flag set. The locations are then all centred, but Pipeline.cpp:120 still requires a 2x2 sample-location grid and the pipeline keys differ for the same state. Adding result.sampleLocationGrid = false; next to result.sampleLocations.fill(0); covers it. The draw B follow-up is up as #6014 , stacked on this PR.

billythemenace2 and others added 2 commits October 14, 2026 21:00
…pixel center

A draw with PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES > 0 and
PA_SC_MODE_CNTL_0.MSAA_ENABLE clear threw. On GFX10 and later the bit
is what makes the rasterizer use the programmed sample locations; with
it clear the draw now keeps the target's sample count with every sample
at the pixel center. Points, lines, a depth target and
alpha-to-coverage still throw.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…MSAA_ENABLE

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@billythemenace2
billythemenace2 force-pushed the fix/agc-msaa-target-without-msaa-enable branch from d9d8fb7 to e10ec39 Compare October 14, 2026 21:03
@billythemenace2

Copy link
Copy Markdown
Author

@SandstromPL Thanks, good catch. Done in e10ec3904 on top of a rebase onto current main: the block now also clears sampleLocationGrid. The test adds a 2x target without MSAA_ENABLE whose pixel 1 holds different locations, and checks that it ends up centred with no grid. The test fails without the line and passes with it, and tools/check_conventions.py passes.

AI-assisted (Claude Code).

@github-actions

github-actions Bot commented Oct 15, 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
PR 6563 docs/dev/debt/libSceAgcDriver.md L21

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.

Pull request Shared files not compared
PR 1395 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 4040 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 4448 docs/dev/debt/libSceAgcDriver.md
PR 4650 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5058 docs/dev/debt/libSceAgcDriver.md
PR 5499 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5500 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5761 docs/dev/debt/libSceAgcDriver.md
PR 5867 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
PR 5925 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6188 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6222 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6235 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked on the merged tree: agc_driver_graphics passes and fails without the State.cpp change (the 2x case, the alpha-to-coverage throw and the cleared sampleLocationGrid are all pinned). The libSceAgcDriver ctest scope is green apart from agc_driver_tessellation, which segfaults on main too. The debt entry is in place and marks the pixel-center result as inferred. Merging once the full Build is green.

@adriadam10
adriadam10 merged commit 7b20318 into boykopovar:main Oct 15, 2026
9 checks passed
simonbronk added a commit to simonbronk/AnyPS5 that referenced this pull request Oct 16, 2026
…get under a 2x scan without MSAA_ENABLE

boykopovar#5882 lets a multisampled scan without MSAA_ENABLE through the earlier
check, so the draw is now refused where its one-sample color target meets
the rasterizer's sample count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SandstromPL added a commit to SandstromPL/AnyPS5 that referenced this pull request Oct 16, 2026
…NABLE is clear

On top of boykopovar#5882, a draw with PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES 2 (4x),
MSAA_ENABLE clear, a one-sample color target and no depth target threw
"a color target's sample count differs from the rasterizer's", which
stops METAL GEAR SOLID: Peace Walker (PPSA18216) at its first
presentation (draw C on boykopovar#5268). Without MSAA_ENABLE every sample is at
the pixel center, and a one-sample target keeps the coverage of sample
0, so the draw is now drawn as a one-sample draw.

PS_ITER_SAMPLE, ANCILLARY or SAMPLE_COVERAGE pixel inputs and a
sample-mask export throw on this path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

4 participants