Skip to content

fix(agc): start the sample location registers at their clear-state value - #4949

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
fdfawef324:fix/agc-unwritten-sample-locations
Oct 13, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
fdfawef324:fix/agc-unwritten-sample-locations

Conversation

@fdfawef324

@fdfawef324 fdfawef324 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

What

A draw with PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES > 0 whose context never wrote PA_SC_AA_SAMPLE_LOCS_PIXEL_* (context 0x2fe-0x30d) was rejected with missing register in context bank at DWORD 0x2fe (766). decodeSampleLocations reads the registers with read(), and they were not in the initial context. The same happened on the MSAA_ENABLE single-sample path, which reads them with read() before singleSampleMsaaRejection. Resident Evil 4 (PPSA07411) stops with this error after two frames.

The sixteen registers now start at 0, both initially and after CLEAR_STATE, in the same way earlier fixes added PA_SU_POLY_OFFSET_CLAMP, SPI_PS_INPUT_ENA/ADDR and SPI_PS_INPUT_CNTL to the initial context. The value comes from:

  • libSceAgc's own register defaults (sceAgcGetRegisterDefaults2, RegisterDefaultsData.hpp). Every public table that holds context registers (V0, V4, V5, V7-V11) sets 0x2fe-0x30d to 0. The same tables supply PA_SU_VTX_CNTL = 0x2d and PA_SC_CONSERVATIVE_RASTERIZATION_CNTL = 0x6000, which the initial context already uses. amdgpu's GFX10 values for those two registers (0x5 and 0x100000) are different, so these tables are the console's own source.
  • amdgpu's GFX10 clear state: clearstate_gfx10.h, gfx10_SECT_CONTEXT_def_8 from 0xa2f5. All sixteen registers are 0 there. gfx_v10_0_cp_gfx_start loads that table before CLEAR_STATE.
  • The GC 10.3 register reset value: gc_10_3_0_default.h, mmPA_SC_AA_SAMPLE_LOCS_PIXEL_*_DEFAULT = 0.

The Mesa and PAL drivers write the registers themselves and never read the unwritten value, so they don't settle it. RADV calls radv_emit_default_sample_locations (radv_device.c) whenever no custom locations are bound. radeonsi's si_emit_sample_locations (si_state_msaa.c) writes the pattern for each sample count, including 0 for 1x. PAL's MsaaState::WriteSamplePositions writes the client's quad pattern on every CmdSetMsaaQuadSamplePattern. Their 1x pattern is also 0, which means the pixel center.

With 0, every sample sits at the pixel center. That is not the standard pattern for 2, 4 or 8 samples, so such a draw decodes as custom sample locations at the center (customSampleLocations, all locations 0). A single-sample scan into a multisampled target already decodes this way. As for any custom pattern, the draw needs VK_EXT_sample_locations for its sample count. Without it, the draw throws the existing custom sample locations require VK_EXT_sample_locations error.

The run log doesn't contain the value of PA_SC_AA_CONFIG or of the failing draw's registers. The draw sets its context with SET_CONTEXT_REG_INDIRECT, and the register lists are in guest memory. The only draw in the failing submission (queue 0x0, serial 8) is DRAW_INDIRECT with VGT_DRAW_INITIATOR = 2 (auto-index, not indexed). The error reaches the title through Driver::CheckFailure from sceAgcCreateInterpolantMapping → ResolveGraphicsAbi. Whether the title draws further is not known: this PR was not run on the title.

Tested

Windows Server 2025, MinGW (winlibs), NVIDIA RTX 3060.

  • agc_driver_graphics: the new UnwrittenSampleLocationTests start from a fresh queue state with the draw's registers written on top and no sample-location registers. They check:

    • 2x, 4x and 8x draws decode with every sample at the pixel center, and the precheck accepts them.
    • Explicit standard 4x locations (0x622ae6ae) still decode as standard.
    • Explicit custom 4x locations (0x22eea66a) still decode to the same coordinates.
    • CLEAR_STATE resets the registers to 0.
    • A 16-sample config still throws more than eight samples, and a mismatched exposed count still throws exposing fewer samples, both with unwritten registers.
    • MSAA_ENABLE on a single-sample target with unwritten registers decodes.

    On main the test fails. With the initial-context assertion disabled, the decode fails with the title's message, AGC graphics: missing register in context bank at DWORD 0x2fe (766). With the fix, it passes.

  • Also passing: agc_driver, agc_driver_pm4, agc_driver_pm4_failure, agc_driver_msaa_enable_single_sample, agc_driver_alpha_to_coverage, agc_driver_draw_cache_reuse, agc_driver_draw_cache_reuse_off, agc_driver_depth_fast_clear_submit, agc_driver_prepared_graphics, agc_driver_prepared_unregistered_graphics.

  • Not run on the title.

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: none
  • Unblocks: none
  • GPUs tested: NVIDIA GeForce RTX 3060 12 GB, driver 591.86 (32.0.15.9186), Vulkan 1.4.325, Windows Server 2025 10.0.26100
  • AI-assisted: yes

🤖 Generated with Claude Code

@fdfawef324

Copy link
Copy Markdown
Contributor Author

Tested with a title: Resident Evil 4 (PPSA07411) on Windows Server 2025, RTX 3060.

  • main at 7e181fc: the title renders 2 frames and aborts after 22 s with AGC graphics: missing register in context bank at DWORD 0x2fe (766).
  • Same build with this PR applied: it runs past that point and renders 2151 frames at 59.94 FPS in the first 57 s, with no error; the run was stopped there by my test harness, not by the title.

@fdfawef324

Copy link
Copy Markdown
Contributor Author

Correction to my previous comment: "renders" overstated it. In a 10-minute run with this PR the title presents frames at 59.95 FPS without any error, but every captured frame is black: it is stalled on what looks like a file-loading wait, which is unrelated to sample locations and which I am looking into separately. What this PR changes for the title is only that the missing register in context bank at DWORD 0x2fe abort is gone.

@github-actions github-actions Bot added the agc libSceAgc* label Oct 13, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 13, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 13, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Conflicts with main in core/libs/prx/libSceAgcDriver/tests/Graphics.cpp after #4584; please rebase. The full build on f982c7e failed only on a test main has since fixed.

@xXPBSXx

xXPBSXx commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Another data point for the title in the description: in three of my saved runs of PPSA07411 (2026-10-12, before this change) the title stops with AGC graphics: missing register in context bank at DWORD 0x2fe (766), the same register and the same message. I have not run it with this PR applied, so I cannot say whether it gets further than that point.

AI-assisted (Claude Code).

@fdfawef324

Copy link
Copy Markdown
Contributor Author

I rebased onto main 91ed4ce. The only conflict was the test list in main() of tests/Graphics.cpp, where I kept main's clipDistanceTests() and framebufferLayerTests() and then this PR's UnwrittenSampleLocationTests(). agc_driver_graphics passes, and it fails again when the QueueState.hpp line is removed.

@EagleBearerYS

Copy link
Copy Markdown
Contributor

Resident Evil Village (PPSA01556, 01.210.000) stops on the same line on main, at its first presented frame, about 20 s in. Its register state is in #2776: the same single-sample MSAA_ENABLE branch as RE4, with all sixteen location registers unwritten. Setup: Windows 11, WinLibs GCC 15.2.0 r7, Release, RTX 3070 Laptop.

With this PR's QueueState.hpp change on main b478fbb, the title gets past that frame. In seven runs of 90 to 300 s there was no 0x2fe stop and no other presentation failure while the game created its PSO cache: 3,003 shaders in 300 s from a cold cache. I applied only that one line, because the PR's test in tests/Graphics.cpp now conflicts with main.

A draw with PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES > 0 whose context never
wrote PA_SC_AA_SAMPLE_LOCS_PIXEL_* (context 0x2fe-0x30d) was rejected
with "missing register in context bank at DWORD 0x2fe", because the
registers were absent from the initial context (PPSA07411 stops on it
after two frames). The MSAA_ENABLE single-sample path read them the
same way.

The sixteen registers now start at 0, both initially and after
CLEAR_STATE. That is the value in libSceAgc's own register defaults
(sceAgcGetRegisterDefaults2, every public table that holds context
registers, the tables the initial context already follows for
PA_SU_VTX_CNTL and PA_SC_CONSERVATIVE_RASTERIZATION_CNTL), in amdgpu's
GFX10 clear-state table (clearstate_gfx10.h, gfx10_SECT_CONTEXT_def_8,
loaded by gfx_v10_0_cp_gfx_start before CLEAR_STATE) and the GC 10.3
register reset value (gc_10_3_0_default.h). RADV, radeonsi and PAL
always write the locations themselves and do not settle the unwritten
value.

0 puts every sample at the pixel center. That is not the standard
pattern for any sample count, so such a draw decodes as custom
locations at the center, as a single-sample scan into a multisampled
target already does.

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*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants