Skip to content

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

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
SandstromPL:fix/agc-msaa-enable-clear-depth-target
Oct 18, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
SandstromPL:fix/agc-msaa-enable-clear-depth-target

Conversation

@SandstromPL

@SandstromPL SandstromPL commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

#5882 is merged (7b20318). This branch was built on #5882's head e10ec3904, which main now contains, so this PR has only its own commit. GitHub's commit list may still show #5882's two commits until the next push. billythemenace2 asked for this follow-up.

With #5915 (draw A), #5882 and #5439, METAL GEAR SOLID: Peace Walker (PPSA18216) stops at draw B of its first presentation:

AGC graphics: multisampled targets rasterized without MSAA_ENABLE are unsupported for points, lines, a depth target or alpha-to-coverage

mrfelek posted the registers of draw B:

Register Value
PA_SC_AA_CONFIG 0x0020c002 (4 samples)
PA_SC_MODE_CNTL_0 0x2 (MSAA_ENABLE clear)
DB_Z_INFO 0xa290098b (4 samples)
DB_EQAA 0x00010002
DB_DEPTH_CONTROL 0x710 and 0x711 (depth test off; 0x711 has a stencil test)
CB_COLOR0_ATTRIB 0x00012000 (4 samples, 4 fragments)

Only 0x711 binds the depth/stencil target, through its stencil test. That is the draw #5882 refuses. mrfelek posted its stencil registers too:

  • DB_STENCIL_INFO 0x20000181 (an 8-bit stencil plane);
  • DB_STENCIL_CONTROL 0x333 and DB_STENCILREFMASK 0x00ff0000: the fail, depth-fail and pass ops replace with the test value 0, write mask 0xff;
  • the standard 4x sample pattern (0x622ae6ae) in all four pixels.

So every sample of the 4x target that draw B covers gets stencil 0, which mrfelek reads as a stencil clear.

With #5439, #5915, #5882's e10ec3904 and this PR, merged by hand the way it is now restacked, mrfelek's run gets past draw B. The first presentation then stops at draw C, a one-sample color target under the same stale 4x PA_SC_AA_CONFIG. #6196 handles that.

#5882 rasterizes a multisampled target without MSAA_ENABLE with the target's sample count and every sample at the pixel center, and it refuses a depth target. A depth/stencil target with the same sample count now goes the same way. Its samples are at the pixel center too, so each depth and stencil sample is tested and written with the depth at the pixel center.

GL allows this when multisampling is disabled. Every sample may take the single-sample depth (ARB_multisample L185-L190), and the fragment may be treated as in multisampling with full coverage (L422-L425). #5915 uses the same rule for a single-sample rasterizer.

These throw:

  • a depth target with another sample count. The existing depth check already requires the counts to match.
  • with a depth target, DB_EQAA Z interpolation or overrasterization (bits 18-19 and 24-27). Draw B's DB_EQAA has neither.
  • with a depth target, a sample-mask export. With multisampling disabled, GL gives the fragment full coverage.
  • points, lines and alpha-to-coverage, as in fix(agc): rasterize a multisampled target without MSAA_ENABLE at the pixel center #5882.

#5915 also refuses a pixel program reading the sample index, because it widens a single-sample rasterizer to the depth target's count. Here the rasterizer already has that count, so #5882's rules for a multisampled target apply unchanged.

Not measured on hardware, and recorded in #5882's debt entry, which this extends:

Tested

Ubuntu 24.04, GCC 13.3, Release with BUILD_TESTING=ON, on #5882's head e10ec3904 (base main fa539dd). 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 msaaEnableClearDepthTests. It uses draw B's posted registers, including the stencil registers and sample locations.

Case Result
Draw B: 4x color target and 4x depth/stencil target with DB_DEPTH_CONTROL 0x711 decodes as 4 samples. It keeps both targets (D32_SFLOAT_S8_UINT, with its stencil test decoding to ALWAYS, REPLACE with reference 0 and write mask 0xff). It puts every sample at the pixel center and passes the draw precheck.
DB_DEPTH_CONTROL 0x710 binds no depth target
a depth-tested 4x pair, and the same with no color target decodes the same way
DB_EQAA Z interpolation (bit 18 or 19) or overrasterization throws
DB_EQAA Z interpolation with no depth target not refused
a sample-mask export with a depth target throws
alpha-to-coverage throws (#5882's message, now without "a depth target")
a 2x depth target under the 4x rasterizer throws (the existing check)

#5882's own refusal test is updated for the shorter message. With #5882 alone, the new test fails at draw B with the message above. With this commit it passes, 5 runs out of 5.

Other checks:

@mrfelek, thanks for the run and for the stencil registers. The test now uses them.

Checklist

🤖 Generated with Claude Code

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>
@mrfelek

mrfelek commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Peace Walker (PPSA18216) with this PR: draw B passes, draw C is next

Linux, RTX 3080 Ti (NVIDIA 615.71.09). Build: main b72378b plus #5439 (f2b1353), #5915 (2c8dda1), #5882 (e10ec39) and this PR (82d90b0), with the launcher hand-over from #5268.

Draw State Result
A depth only, 1-sample rasterizer over a 4-sample depth target passes (#5915)
B depth and colour 4x, 4-sample PA_SC_AA_CONFIG, MSAA_ENABLE clear passes (this PR)
C 1-sample colour target, no depth, 4-sample PA_SC_AA_CONFIG, MSAA_ENABLE clear throws

The frame then ends at draw C with a color target's sample count differs from the rasterizer's. With a local print of the values: colour 1 sample, rasterizer 4, depth 0, PA_SC_MODE_CNTL_0 0x2 (MSAA_ENABLE clear), PA_SC_AA_CONFIG 0x0020c002, DB_EQAA 0x00010002, CB_COLOR0_ATTRIB 0, slot 0. (The registers for A, B and C are in the table on #5268.)

Note: this PR is built on #5882's earlier head d9d8fb7. On #5882's current head (e10ec39, which adds result.sampleLocationGrid = false;) it conflicts in State.cpp (the Require(filled && ...) block), tests/Graphics.cpp and docs/dev/debt/libSceAgcDriver.md. For this run I kept this PR's depth checks and #5882's new line.

AI-assisted; the runs are from my machine.

@mrfelek

mrfelek commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Draw B registers (Peace Walker, PPSA18216)

The first presentation gets past draw B with #5915, #5882 and this PR (details in my earlier comment); the frame then ends at draw C. Registers read at draw B (the DB_DEPTH_CONTROL 0x711 draw), same build and machine:

Register Value
DB_STENCIL_INFO 0x20000181
DB_DEPTH_CONTROL 0x00000711
DB_STENCIL_CONTROL 0x00000333
DB_STENCILREFMASK 0x00ff0000
DB_STENCILREFMASK_BF 0x00000000
DB_RENDER_CONTROL 0x00000000
PA_SC_CENTROID_PRIORITY_0 / _1 0x00003210 / 0
PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0 to X1Y1_3 (0x2fe to 0x30d) 0x622ae6ae, 0, 0, 0, repeated for each of the 4 pixels

My reading of the fields (please check): DB_STENCIL_INFO has an 8-bit stencil format; the stencil function is ALWAYS (DB_DEPTH_CONTROL bits 8 to 10 = 7) with Z test and write off; fail, Z-pass and Z-fail ops are all REPLACE_TEST (3) with test value 0 and write mask 0xff; the sample locations are the same standard 4x pattern, (-2,-6), (6,-2), (-6,2), (2,6), for all four pixels. So the test's assumptions of a stencil plane, ALWAYS, and the uniform pattern hold, and this draw looks like a stencil clear to 0.

AI-assisted; the runs are from my machine.

…t the pixel center

A draw with PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES 2 (4x) and MSAA_ENABLE
clear into a 4x depth/stencil and color pair threw "multisampled
targets rasterized without MSAA_ENABLE are unsupported for points,
lines, a depth target or alpha-to-coverage", which stops METAL GEAR
SOLID: Peace Walker (PPSA18216) at its first presentation (draw B on
boykopovar#5268). A depth target with the same sample count now gets the same
pixel center samples as the color targets, so each depth and stencil
sample is tested and written with the depth at the pixel center, as GL
allows when multisampling is disabled.

DB_EQAA Z interpolation or overrasterization and a sample-mask export
with a depth target throw on this path.

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

Copy link
Copy Markdown
Contributor Author

@mrfelek Thanks for the run and for the draw B registers! I've restacked this PR onto #5882's current head e10ec3904. Only this PR's commit moved, and it applies there without conflicts. The conflicts you saw came from merging the older copy of #5882's commit (d9d8fb737) next to the rebased one. The test now uses your stencil registers and sample locations: they decode to ALWAYS, REPLACE with reference 0 and write mask 0xff, with every sample at the pixel center. Draw C is up as #6196, also stacked on #5882.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

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 5372 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 5999 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
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 6194 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 6223 docs/dev/debt/libSceAgcDriver.md
PR 6235 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6273 docs/dev/debt/libSceAgcDriver.md
PR 6297 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6300 docs/dev/debt/libSceAgcDriver.md
PR 6320 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6412 docs/dev/debt/libSceAgcDriver.md
PR 6472 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6481 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
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 6549 docs/dev/debt/libSceAgcDriver.md
PR 6563 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6741 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6786 docs/dev/debt/libSceAgcDriver.md
PR 6808 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6868 docs/dev/debt/libSceAgcDriver.md
PR 6972 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7390 docs/dev/debt/libSceAgcDriver.md
PR 7426 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7505 docs/dev/debt/libSceAgcDriver.md

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the decode of a multisampled depth/stencil target without MSAA_ENABLE: agc_driver_graphics passes on the PR merged onto main and fails with the State.cpp change reverted. The remaining libSceAgcDriver scope is green apart from agc_driver_tessellation, which segfaults on main too. The throws (other sample count, DB_EQAA Z interpolation/overrasterization, sample-mask export, points, lines, alpha-to-coverage) match the tests, the debt entry marks the pixel-center result as inferred, and @mrfelek's Peace Walker run covers draw B. Merging once the full Build is green.

@SandstromPL

Copy link
Copy Markdown
Contributor Author

@adriadam10 #6014 dropped out of the Build batches after 02:44Z without being merged, and I couldn't tell why. Run 38994273700, with #6014 first at 4ef2af6, was green on every job at 02:43Z. In the smaller run 38994264368, Linux failed only agc_driver_store_over_untracked. The first failure matches #6383, which #6385 fixed on main at 02:58Z.

The head hasn't changed, it still merges cleanly into current main, and #5915 (draw A) is merged now. If anything else is needed, I'll do it. Otherwise, could you add it back when you next run the queue?

AI-assisted: yes (Claude Code)

@savvaniss

Copy link
Copy Markdown
Contributor

Intel and AMD Windows data: no failures from this change.

Windows 11 Pro 26300, WinLibs GCC 15.2.0 r7, Release, every execution test built, ANYPS5_NO_SHADER_CACHE=1, serial CTest, main 47a266cf9. Intel UHD Graphics 770 (8086:a780, 32-wide subgroups), driver 32.0.101.7088. AMD Radeon RX 9070 XT (1002:7550, 64-wide subgroups), driver 32.0.31041.1004, with VkCache emptied first.

This head 4ef2af62e merged into main: the 16 tests built from tests/Graphics.cpp, ×3, pass on the RX 9070 XT. On the UHD 770, agc_pipeline_library_clear_waits_for_build failed once in its first three runs (clearing during a library build leaked Vulkan objects). It did not recur in 20 more runs with this PR, 20 on main and 10 on the RX 9070 XT. It looks intermittent and isn't tied to this change.

Full CTest (--parallel 4) with six open PRs merged into main (#6638, #6604, #6014, #6773, #6772, #6725): the same failures as main alone on both GPUs, nothing new. main alone: UHD 770 1392 of 1398, RX 9070 XT 1396 of 1398. The failures are image tables (#6647) and sample_multisample_target (#6971) on Intel, and quad_pixel_mask (#6255) and layered_volume_vertex_no_gpl (#4768) on AMD.

AI-assisted: yes (Claude Code)

SandstromPL added a commit to SandstromPL/AnyPS5 that referenced this pull request Oct 17, 2026
…zer without MSAA_ENABLE as one sample

With boykopovar#6014 merged, the first draw of METAL GEAR SOLID: Peace Walker's
(PPSA18216) opening movie stops on "the depth target's sample count
differs from the rasterizer's" (mrfelek on boykopovar#6196). The state he reported
is a 4x PA_SC_AA_CONFIG, MSAA_ENABLE clear, a one-sample 1440x810 depth
target and no color target; on main alone it throws earlier, on "a depth
target". Like the one-sample color targets of bb87779, such
a depth target, with or without one-sample color targets, is now drawn
as a one-sample draw at the pixel center, before the multisampled-target
check and instead of the sample-0 path, which needs MSAA_ENABLE.

Points, lines, alpha-to-coverage, a sample-mask export and DB_EQAA Z
interpolation or overrasterization throw on this path, and so do
PS_ITER_SAMPLE and ANCILLARY or SAMPLE_COVERAGE pixel inputs when the
pixel program runs. A skipped pixel program is replaced by the null
program, which uses neither.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adriadam10
adriadam10 merged commit 9b68377 into boykopovar:main Oct 18, 2026
10 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