Skip to content

fix(agc): rasterize a single-sample scan into a multisampled depth target at the pixel center - #5915

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
SandstromPL:fix/agc-single-sample-scan-multisampled-depth
Oct 17, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
SandstromPL:fix/agc-single-sample-scan-multisampled-depth

Conversation

@SandstromPL

@SandstromPL SandstromPL commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

METAL GEAR SOLID: Peace Walker (PPSA18216) stops at its first presentation with:

AGC graphics: the depth target's sample count differs from the rasterizer's

mrfelek reported it on #5268, on main 69e7138 with #5422 and #5439, and posted the registers of that draw (draw A):

Register Value
PA_SC_AA_CONFIG 0 (one sample)
PA_SC_MODE_CNTL_0 0x2 (MSAA_ENABLE clear)
DB_Z_INFO 0xa290098b (4 samples)
DB_EQAA 0
DB_DEPTH_CONTROL 0x777
color target none

This is the depth version of #4414. #4414 rasterizes a single-sample scan into a multisampled color target with every sample at the pixel center, but it leaves out draws with a depth target. Such a draw now rasterizes the depth target's sample count with every sample at the pixel center, using the custom sample locations from #2169. A 4x color target next to the depth target works the same way.

radeonsi programs this state when GL multisampling is disabled on a multisampled framebuffer:

Two differences from that state:

  • radeonsi's comment lists Z samples as at most the coverage samples (L3439), although it programs 4 Z samples with 1 coverage sample here.
  • radeonsi sets DB_EQAA.MAX_ANCHOR_SAMPLES to the Z sample count, where the game sets DB_EQAA to 0. This change does not depend on MAX_ANCHOR_SAMPLES.

ARB_multisample defines this state: rasterization is single-sample with full coverage, and every sample may take the single-sample depth (L185-L190), with the fragment treated as in multisampling with full coverage (L422-L425). Every sample at the pixel center gives exactly that: each depth and stencil sample is tested and written with the depth at the pixel center. GL also lets an implementation test only a "centermost sample" and set every depth and stencil sample from it (L425-L434), which differs when a pixel's stored samples differ.

Triangles and rectangles are accepted; draw A's depth and stencil fill may well be a rect list. On this path, these throw, most with a new message:

  • points and lines. An aliased line is not a Vulkan multisampled line; fix(agc): rasterize a multisampled target without MSAA_ENABLE at the pixel center #5882 makes the same call.
  • alpha-to-coverage;
  • DB_EQAA Z interpolation or overrasterization (bits 18-19 and 24-27). These could change the per-sample depth;
  • a pixel program reading the sample index (ANCILLARY). Vulkan would run it once per sample, with an index the console does not give. Sample-interpolated inputs are accepted, because the recompiler reads them through barycentrics, which give the center value for every sample here.
  • a sample-mask export;
  • a single-sample color target next to the multisampled depth target. That is mixed samples, which no enabled extension provides. It throws the existing "a color target's sample count differs from the rasterizer's".

#4414's color-only path does not refuse these, and this PR leaves it unchanged. The primitive check moves out of singleSampleMsaaRejection into a helper that both rules use; that rule's behaviour and messages are unchanged.

Not measured on hardware, and recorded in the new debt entry:

  • whether the console tests every sample or only a centermost one, or instead evaluates the depth plane at each sample position (GL allows each);
  • how its occlusion queries count such a draw.

Tested

Ubuntu 24.04, GCC 13.3, Release with BUILD_TESTING=ON, on main 84b01f9. Locally, the libusb lines were removed from CMakeLists.txt, because the libusb submodule isn't checked out here.

agc_driver_graphics (tests/Graphics.cpp) gains multisampledDepthTests. Unless stated, each case uses PA_SC_AA_CONFIG 0, MSAA_ENABLE clear and a 4x Z32 depth target.

These now decode, as 4 samples with every location at the pixel center, no sample-location grid and no per-sample shading:

  • draw A's registers (DB_Z_INFO 0xa290098b, DB_DEPTH_CONTROL 0x777, DB_EQAA 0, no color target). The draw precheck accepts it;
  • the same with triangle fans, triangle strips and both rect-list types;
  • a depth clear, which writes with ALWAYS;
  • an ANCILLARY input, when the draw skips its pixel program;
  • a pixel program reading sample-interpolated inputs;
  • a 4x color target next to the depth target, which is kept. PS_ITER_SAMPLE does not turn on sample shading.

These now throw:

  • DB_EQAA Z interpolation;
  • line lists and line strips;
  • alpha-to-coverage;
  • an ANCILLARY input with a pixel program;
  • a sample-mask export;
  • a 1x color target next to the depth target.

Pinned unchanged (these pass on main too):

  • a 4x color target with a 1x depth target;
  • MSAA_ENABLE set, with sample 0 at the center;
  • a 2x rasterizer with the 4x depth target.

On main the test fails at draw A with the message above. With the change it passes, 5 runs out of 5. The existing single-sample MSAA_ENABLE checks still pass, in agc_driver_graphics and agc_driver_msaa_enable_single_sample.

Other checks:

@mrfelek, thanks for the registers and the runs.

Checklist

🤖 Generated with Claude Code

@mrfelek

mrfelek commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Real-title check for the Peace Walker case. METAL GEAR SOLID: Peace Walker (PPSA18216), Linux, RTX 3080 Ti (NVIDIA 615.71.09), main e34c479 plus #5439, this PR and #5882, with the launcher hand-over from #5268. The game loads (345 file opens) and reaches its first presentation. With this PR the original the depth target's sample count differs from the rasterizer's (draw A) no longer throws. The frame then stops at #5882's multisampled targets rasterized without MSAA_ENABLE are unsupported for points, lines, a depth target or alpha-to-coverage (draw B on #5268: PA_SC_AA_CONFIG 0x0020c002, MSAA_ENABLE clear, DB_Z_INFO 0xa290098b, CB_COLOR0_ATTRIB 0x00012000). Draw C was not reached, since B ends the frame first.

AI-assisted; the runs are from my machine.

@SandstromPL

Copy link
Copy Markdown
Contributor Author

@mrfelek Thanks for the run! That matches the description: draw A gets through, and draw B is next, on #5882's depth-target refusal.

@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
PR 6451 docs/dev/debt/libSceAgcDriver.md L124
PR 6470 docs/dev/debt/libSceAgcDriver.md L124
PR 6563 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp L394-400, L411-412
PR 6683 docs/dev/debt/libSceAgcDriver.md L124

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 1191 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
PR 1395 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 2434 docs/dev/debt/libSceAgcDriver.md
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 5390 docs/dev/debt/libSceAgcDriver.md
PR 5441 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
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 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 6215 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
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
PR 6297 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6320 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6412 docs/dev/debt/libSceAgcDriver.md
PR 6529 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6535 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6548 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6647 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6808 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6946 docs/dev/debt/libSceAgcDriver.md
PR 6947 docs/dev/debt/libSceAgcDriver.md

@mrfelek

mrfelek commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Checked, as asked in the description. Peace Walker (PPSA18216), Linux, RTX 3080 Ti (NVIDIA 615.71.09), main b72378b plus #5439, this PR (2c8dda1), #5882 (e10ec39) and #6014 (82d90b0), with the launcher hand-over from #5268.

AI-assisted; the runs are from my machine.

@SandstromPL

Copy link
Copy Markdown
Contributor Author

@mrfelek Thanks for checking! Draw C is handled in #6196.

@simonbronk

Copy link
Copy Markdown
Contributor

Overlap note: this PR and #5907 (mine) both edit the depth sample-count check in DecodeState. They conflict when merged together.

They handle opposite cases:

They combine without changing either: keep #5907's depthKeepsSampleZero and its sample-zero block. Then put this PR's single-sample-scan block after it, and end with:

Require(!result.depth || depthKeepsSampleZero || result.depth->samples == result.samples, "the depth target's sample count differs from the rasterizer's");

I ran that combination with Onimusha: Way of the Sword (PPSA27836) on main 0afa6a6, Windows, RTX 5070. That title only takes #5907's side (depth 1, raster 2), so this run doesn't exercise this PR's path. Whichever lands second will need this resolution.

@SandstromPL

Copy link
Copy Markdown
Contributor Author

@simonbronk Thanks, agreed! I'd come to the same resolution. I rebuilt it on current main (e49ec59) with #5907 (2ce4e57) and this PR (2c8dda1): your depthKeepsSampleZero and sample-zero block, then this PR's single-sample-scan block, then the Require above.

The debt file conflicts too. #5907 rewrites the "Custom MSAA sample locations" entry and this PR adds its entry right after it; keeping both lines resolves it.

With that, agc_driver_graphics passes with both PRs' tests (Linux, RTX 3050 Ti), and ctest -R agc passes 582 of 583. The one failure, agc_pending_key_stores, fails the same way on plain main e49ec59. If this PR lands second, I'll do the rebase.

@adriadam10 adriadam10 self-assigned this Oct 16, 2026
@adriadam10

Copy link
Copy Markdown
Collaborator

Re-checked on the PR merged onto current main (65d56a2): agc_driver_graphics passes, and the new multisampledDepthTests fail with main's State.cpp (the original "depth target's sample count differs" message). Rejections, tests and the debt entry match the description; mrfelek's Peace Walker run covers draw A. Merging once main builds again (#6784); #5907 overlaps the same check and will need a small rebase if it lands second.

@SandstromPL

Copy link
Copy Markdown
Contributor Author

@adriadam10 Thanks for the re-checks! In case it helps break the loop: GitHub shows no merge queue for main and no queue entry for this PR, so nothing will pick it up on its own. Run 38899406464, on the red base eb901bcf4, is still the only full Build on this head (2c8dda1dd). A fresh Build on current main should give it a green report.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@SandstromPL
SandstromPL force-pushed the fix/agc-single-sample-scan-multisampled-depth branch from 2c8dda1 to 4355eb4 Compare October 16, 2026 19:17
@SandstromPL

Copy link
Copy Markdown
Contributor Author

Rebased onto main f474fb7 now that #5907 is in, as agreed with @simonbronk: #5907’s depthKeepsSampleZero path runs first, then this PR’s single-sample-scan path, then one sample-count check that accepts both. agc_driver_graphics passes 3 of 3 on an RTX 3050 Ti and on lavapipe, and the new tests still fail with main’s State.cpp. The debt file keeps both entries.

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

Copy link
Copy Markdown
Collaborator

Re-checked the rebased head (4355eb4) on main: agc_driver_graphics passes and fails without the State.cpp change; #5907's depthKeepsSampleZero path runs first, then the single-sample-scan path, then the single sample-count check. Rejections, tests and the debt entry match the description; mrfelek's Peace Walker run covers draw A. Merging once the full Build on this head is green.

SandstromPL and others added 2 commits October 17, 2026 00:17
…rget at the pixel center

A draw with PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES 0 and MSAA_ENABLE clear
into a 4x depth target threw "the depth target's sample count differs
from the rasterizer's", which stops METAL GEAR SOLID: Peace Walker
(PPSA18216) at its first presentation. Such a draw now rasterizes the
depth target's sample count with every sample at the pixel center, as
a single-sample scan into a multisampled color target already does.
radeonsi programs this MSAA_ENABLE and PA_SC_AA_CONFIG with the depth
buffer's own sample count when GL multisampling is disabled, and GL
then gives full coverage with every depth and stencil sample set from
the pixel center.

Triangles and rectangles are accepted. Points, lines, alpha-to-coverage,
DB_EQAA Z interpolation or overrasterization, a pixel program reading
the sample index and a sample-mask export throw on this path, as does
a single-sample color target next to the multisampled depth target.
The primitive check is shared with the single-sample MSAA_ENABLE rule,
whose behaviour and messages are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The debt entry cited ARB_multisample's centermost-sample shortcut as
what a single-sample scan into a multisampled depth target draws. The
draw tests each depth and stencil sample with the depth at the pixel
center, which is the rule above that shortcut (L185-L190 and
L422-L425); the shortcut is now named as an unmeasured alternative.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@adriadam10

Copy link
Copy Markdown
Collaborator

Rebased onto current main (57f2a59): the only conflict was adjacency in tests/Graphics.cpp with the multisampled DCC state test from a0c51d5; both tests are kept, State.cpp and the debt file merged cleanly. agc_driver_graphics passes on the rebased head and fails without the State.cpp change. Merging once the full Build is green.

@adriadam10
adriadam10 force-pushed the fix/agc-single-sample-scan-multisampled-depth branch from 4355eb4 to 237fe9c Compare October 16, 2026 22:51
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@adriadam10
adriadam10 merged commit d7262bd into boykopovar:main Oct 17, 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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants