Skip to content

fix(libSceAgc): return the console's register-defaults table and type pairs - #6270

Merged
adriadam10 merged 4 commits into
boykopovar:mainfrom
YossiYad:fix/agc-register-defaults-pairs
Oct 18, 2026
Merged

adriadam10 merged 4 commits into
boykopovar:mainfrom
YossiYad:fix/agc-register-defaults-pairs

Conversation

@YossiYad

Copy link
Copy Markdown
Contributor

What

PS5 FSR4 Showcase (PPSA99011, catalog 01.000.010) with #5648 stops at its first compose pass, 11 to 15 ms after chapter 0 starts:

FSR4_SHOWCASE_ERROR fence=-4 stage=compose after_ms=15

No submission reaches the queue for it. The title's Vulkan driver (ps5-vulkan at 4bb0c8b) logs over the network, so I caught its lines on the guest vsnprintf with gdb:

PS5VK_GRAPHICS_PREPARE_FAILED serial=34 phase=attachment-layout site=1030 early=0 rc=-3

Line 1030 of native/graphics_queue_ps5.c is ps5vk_color_runtime_defaults(defaults, sceAgcGetRegisterDefaults()). color_runtime_defaults.h looks up the MRT0 colour-target block by its key 0x38e92c91 and refuses the table. There are two reasons:

  • The root's type table (+0x30) held the compact 12-byte entries. Their second word also packs the block's register count and stride above bit 10 (0x00204138 for MRT0 in the 137-entry table). The driver, like the other homebrew that share its ps5_agc_registers.h, reads 8-byte {key, index << 2 | bank} pairs. So it read MRT0 at index 529482.
  • sceAgcGetRegisterDefaults returned the version 0 table. ps5-vulkan measured the console returning the running system software's table: 128 entries over 79 context blocks on 10.x, 137 over 84 on 12.02 and 13.x. The version 0 table has 150 entries, and its MRT0 block has 0x319 where the colour target has 0x31b, so the driver refuses it even with the pairs fixed.

Changes:

  • libSceAgc: GetRegisterDefaults builds the type table once per table as {key, entry & 0x3ff} pairs and checks that each index lies inside its bank.
  • libSceAgc: sceAgcGetRegisterDefaults returns the version 13 public table, which is the 137-entry one. sceAgcGetRegisterDefaults2(version) is unchanged. guest_shader_link now expects that table (488 context registers instead of 523).
  • Test in agc_commands and agc_initialization: reads the root at its byte offsets, walks the pairs and follows the MRT0 key to the sixteen colour-target registers. It expects block 78 for sceAgcGetRegisterDefaults and block 73 for version 10, and a valid block in every version from 4.
  • Debt entry: the version choice and the dropped high bits. No console dump of the runtime type table was compared.

Title. PPSA99011 on main + #5648:

Tested

Linux (Bazzite 44, GCC 16.2.1, Ninja, Release, -DBUILD_TESTING=ON), RX 7700 XT (RADV), based on main d92a8dad3:

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; no silent stubs added
  • No notes, investigation .md files or images added to the repository
  • No new third-party code
  • Generic behaviour, not specific to one title
  • Depends on: none (PPSA99011 also needs fix(shader): accept buffer resource tuples ending in VCC #5648 to get this far)
  • Unblocks: PPSA99011 compose pass
  • GPUs tested: AMD RX 7700 XT (RADV)
  • AI-assisted: yes

… pairs

The defaults root exposed the compact 12-byte type entries, whose second
word also packs each block's register count and stride above bit 10.
Titles read the type table as 8-byte {key, index << 2 | bank} pairs, so
PS5 FSR4 Showcase (PPSA99011) found the MRT0 key at index 529482 and
refused to build its compose pass. Build the pairs once per table and
check that every index lies inside its bank.
…cGetRegisterDefaults

The legacy call takes no version and answered with the version 0 table.
On a console it returns the running system software's table: 128 keyed
entries over 79 context blocks on 10.x and 137 over 84 on 12.02 and 13.x,
as ps5-vulkan measured and relies on. Version 0 holds 150 entries and its
MRT0 block starts at 0x318 followed by 0x319, so PPSA99011 refused it
even with the types fixed. Return the version 13 table, which is the
137-entry one.
… does

Reads the root at its byte offsets, walks the type table as key and
bank-index pairs and checks that the MRT0 key leads to the sixteen
colour-target registers: block 78 of the table sceAgcGetRegisterDefaults
returns, block 73 of version 10, and a valid block in every version from
4. Fails on the compact entries and on the version 0 legacy table.
@github-actions github-actions Bot added docs agc libSceAgc* labels Oct 15, 2026
@zaryabzip

Copy link
Copy Markdown
Contributor

Windows build and tests, for "Windows and macOS were not built here". Windows 11 (26200), RTX 3060 Laptop GPU (NVIDIA 617.42), WinLibs GCC 15.2.0 r7 (UCRT), Release, BUILD_TESTING=ON. Head 39575d66e merged onto main c0be1f69b (clean).

  • The full build succeeds. agc_commands, agc_initialization and guest_shader_link pass.
  • With both RegisterDefaults.cpp files from main and the PR's tests:
    • agc_commands and agc_initialization fail with legacy register defaults are not the newest public table (2 of 2 runs).
    • guest_shader_link aborts (0xc0000409).
  • With only Command/src/RegisterDefaults.cpp from main:
    • agc_commands and agc_initialization fail with MRT0 defaults key is missing, repeated or outside the context blocks.
    • guest_shader_link passes.
  • ctest -R "agc|shader|guest_playgo" -j4: 579/579 pass with the PR (3 skipped, same as main). On main, agc_driver_resource_cache_storage_admission failed once under -j4 and passed 2 of 2 alone.

Not run: PPSA99011, macOS.

AI-assisted: yes (Claude Code)

@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 6044 core/libs/tests/GuestShaderLink.cpp
PR 6447 core/libs/prx/libSceAgc/tests/Commands.cpp
docs/dev/debt/libSceAgc.md

@screamm

screamm commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Cross-validation on Windows 11 Pro 26220, NVIDIA RTX 4070 (596.21), MinGW GCC 15.2, PR head 39575d66e merged on main 8856f180c with #5648 943d362d2:

#5648 has since been merged as 23dcb8249 with the same head. Both merges are clean. Full Release build (BUILD_TESTING=ON) and libs pass, with no warnings in the changed files. Ryzen 7 5700X. Title relinked as in our catalog runs: --windows --trap-sdk-syscall-stubs --registry unused-filter=0, app0/eboot.bin staged, ANYPS5_REQUIRE_DISPLAY=1, real window, 120 s limit.

PS5 FSR4 Showcase (PPSA99011, 01.000.010). The "before" side is the same tree with only the two RegisterDefaults.cpp reset to main, then libs rebuilt. "Cold" means the title's shader_cache folder was deleted first; "warm" means it was reused.

Build Run Exit Frames Stop
this PR cold 1 0x00000000, 28.6 s none FSR4_SHOWCASE_ERROR fence=-4 stage=fsr4 after_ms=4555, FSR4_SHOWCASE_END result=FAIL
this PR cold 2 0x00000000, 27.9 s none FSR4_SHOWCASE_ERROR fence=-4 stage=fsr4 after_ms=4583, FSR4_SHOWCASE_END result=FAIL
this PR warm 0x00000000, 44.9 s none FSR4_SHOWCASE_ERROR fence=-4 stage=fsr4 after_ms=4540, FSR4_SHOWCASE_END result=FAIL
before cold 0x00000000, 19.0 s none FSR4_SHOWCASE_ERROR fence=-4 stage=fsr4 after_ms=4553, FSR4_SHOWCASE_END result=FAIL
before warm 0x00000000, 105.3 s none FSR4_SHOWCASE_ERROR fence=-4 stage=fsr4 after_ms=4580, FSR4_SHOWCASE_END result=FAIL
  • Every run reaches FSR4_SHOWCASE_READY and CHAPTER index=0.
  • With and without the PR, the runs prepare the same compute shaders at dispatch (0x2754c0000..0x275580000 cold, a few more warm) and then fail the FSR4 fence.
  • The warm runs load the pipeline cache (7.7 and 9.1 MiB, 16 and 17 disk-cache hits), so the fence wait is not compile time.
  • No run shows presentation failed, MUBUF/MTBUF or a device loss, and no run presents a frame.

So on NVIDIA I can't reproduce the stage=compose after_ms=15 stop. The title stops one stage earlier, at the same stage=fsr4 fence that @LeandroLP23 (Radeon 860M, Windows) and you (RX 7700 XT, RADV, 4808d9c20 + #5648) reported in #5648. The compose pass that reads the register defaults is never reached here, so the title can't show this PR's effect on this machine. What got your run past stage=fsr4? The body lists only #5632 and a hidden display on top of #5648.

Tests

  • agc_commands, agc_initialization and guest_shader_link: 3/3 with --repeat until-fail:3.
  • Negative control: the same tree with both RegisterDefaults.cpp from main and this PR's tests kept.
    • agc_commands and agc_initialization fail with legacy register defaults are not the newest public table.
    • guest_shader_link aborts with 0xC0000409.
    • All three pass again after restoring.
  • Non-GPU suite (-j8 -E '^agc_driver_|^video_out_flip'): 793/817 on the first pass, 2 skipped. The machine was also running two other builds at the time. The 24 failures were 22 ctest timeouts, conventions_wrapped_declarations (its internal 20 s subprocess timeout) and guest_kqueue (0xC0000409). Rerun serially, 21 of 24 pass. With a 600 s timeout, conventions_function_bodies and debt_split pass too. hardware_oracle_compare still hits its fixed 20 s TIMEOUT property. None of them touches libSceAgc.
  • GPU suite (-j1 -R '^agc_driver_' --repeat until-fail:3): 439/439. 437 pass 3 of 3, 2 skip (agc_driver_sparse_dma_buf_import, agc_driver_stencil_ref_export).
  • tools/check_conventions.py --base origin/main: All conventions checks passed. git diff --check: clean.

Review notes

  • The version 13 choice and the dropped type bits are inferred from ps5-vulkan, not from a console dump of the root, as the debt entry says. In this tree, version 13 is the V11 table (137 entries, 84 context blocks), and version 12 is the 128-entry V10 shape.
  • The cited color_runtime_defaults.h uses type_index_pairs and ps5_agc_type_index_bank/value. Their definition, ps5_agc_registers.h, is not in ps5-vulkan's tree at 4bb0c8b, so the 8-byte pair layout can't be checked from the linked source.
  • Nothing else in core/ reads RegisterDefaults::types, so dropping the count/stride bits and word 2 loses nothing internally.
  • testDefaults builds every public and internal table for versions 0–13, so the new bank-index Require is checked on all of them.

Verdict: build, tests and conventions are confirmed on NVIDIA/Windows, and no regression found. Not confirmed: the PPSA99011 compose fix. On this GPU, with and without the PR, the title stops earlier at stage=fsr4.

Written with Claude Code assistance.

@LeandroLP23

Copy link
Copy Markdown
Contributor

AMD confirmation: with this PR, PS5 FSR4 Showcase gets past compose and plays chapters 0, 1 and 2. On the same warm cache, main stops at compose.

Windows 11 (26300), Radeon 860M (RDNA 3.5, driver 32.0.13058.8001), WinLibs GCC 15.2.0, Release. main 8856f180c (with #5648 merged), alone and with head 39575d66e merged (clean). Catalog 01.000.010, relinked with --windows --trap-sdk-syscall-stubs. All runs use the same run folder, whose shader_cache was already warm (cache_in=28026892).

Build Run Result
main warm CHAPTER index=0, then FSR4_SHOWCASE_ERROR fence=-4 stage=compose after_ms=45
this PR 1, 2 CHAPTER index=0, 1 (Against a bilinear upscale), 2 (One pixel in nine), then FSR4_SHOWCASE_ERROR vkQueueSubmit(queue, 1, &s, fence) = -4
this PR 3, APS5_PROFILE_DRAW=1 same, 15 and 13 presents per 10 s in chapters 0 and 1

The run before the main control in this table had stopped earlier, at stage=fsr4 (after_ms=4528). On the new main one slow pipeline was missing from the cache, and that run built and saved it. The control then ran on a fully warm cache.

The stop in chapter 2 is pipeline build time again, as in #6118. Chapter 2 dispatches programs the earlier chapters don't, and one of them wasn't cached yet:

[debug:0] FSR4_SHOWCASE_CHAPTER index=2 title=One pixel in nine
[dispatch] created a pipeline for 794196 SPIR-V words in 14 ms (program 0x275760000)
[dispatch] created a pipeline for 793760 SPIR-V words in 15 ms (program 0x275780000)
[dispatch] creating a pipeline for 299294 SPIR-V words (program 0x2757a0000)
[debug:0] FSR4_SHOWCASE_ERROR vkQueueSubmit(queue, 1, &s, fence) = -4
[dispatch] created a pipeline for 299294 SPIR-V words in 11261 ms (program 0x2757a0000)

@screamm, on getting past stage=fsr4: here it took a pipeline cache built over 7 runs. Each run stops while the queue worker is still building one large FSR4 compute pipeline: 130k–800k SPIR-V words, up to 66 s each on this GPU. That build finishes after the title gives up, and [pipeline-cache] saved keeps it at teardown, so the next run gets one pipeline further. The full table is in #6118. A run only adds to the cache if the process stays up until that build ends. I let each run go 60–90 s.

AI-assisted: yes (Claude Code).

@YossiYad

YossiYad commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor Author

@screamm on your two points:

  • Getting past stage=fsr4: as @LeandroLP23 described, my run used a shader_cache built over earlier runs. Each run that stays up until the slow FSR4 compute pipeline finishes building saves it at teardown, so later runs get past the fence. With a cold cache, my runs also stop at stage=fsr4, as in AGC: PS5 FSR4 Showcase (PPSA99011) gives up in its first FSR4 pass while the queue worker builds 130k-800k-word compute pipelines (up to 66 s each on AMD) #6118. compose only shows up once that stage is cached.
  • ps5_agc_registers.h: it isn't in ps5-vulkan itself. Its Makefile takes it from a sibling repository, -I$(LAB_SIBLINGS)/ps5-agc-gears/include. The header is include/ps5_agc_registers.h in ps5-agc-gears. There, struct ps5_agc_type_index is {uint32_t key; uint32_t encoded_index;}, which is 8 bytes. The bank is encoded_index & 3 and the index is encoded_index >> 2. Static asserts pin type_index_pairs at root offset 0x30 and the root at 0x40 bytes ("FW 12.02 defaults root ABI"). That matches the pairs this PR produces. ps5-vulkan's own tests/test_color_runtime_defaults.c also builds its fake table as value << 2 | bank.

@adriadam10 adriadam10 self-assigned this Oct 18, 2026
@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 18, 2026
@adriadam10
adriadam10 merged commit 2da2284 into boykopovar:main Oct 18, 2026
9 checks passed
@ArielWolle

Copy link
Copy Markdown

Regression report for PPSA34063 (NHL 27, update 01.003.001) on Windows. I'm not claiming a cause in this PR's code; I'm reporting what changes when it is reverted and asking one question.

Symptom. With this PR in, the title stops about 22 s after start, before it presents a frame, with:

[gpu] driver failure: indirect register sentinel semantics are not implemented
terminate called after throwing an instance of 'std::runtime_error'

The message is the require(value != 0xffffffffu, ...) in registerOffset in Execution/src/Pm4.cpp, reached from an indirect register packet whose pair list holds the offset 0xffffffff.

What I compared (Windows 11, MinGW-w64 GCC 15.2, RTX 3070, driver 617.42; same relinked executable, game files and libs in both runs):

So reverting this PR is what removes the stop on this machine. I have not narrowed it to one of the two commits (the table type pairs, or sceAgcGetRegisterDefaults now returning the version 13 table).

Question. Does the console's version 13 register-defaults table contain entries a title turns into 0xffffffff offsets in an indirect register list, and if so what does the command processor do with them (skip the pair, write nowhere)? registerOffset throws on them as unimplemented, so a title that reads this table through the legacy call now stops there. I can't tell from the title's side which of those it is, and I'm not proposing to make the driver accept the sentinel without that.

AI-assisted: yes.

@ee6aba54

Copy link
Copy Markdown
Contributor

Since this PR, PPSA15304 aborts about 10 s after start:

[gpu] driver failure: indirect register sentinel semantics are not implemented
[videoout] presentation failed: indirect register sentinel semantics are not implemented
terminate called after throwing an instance of 'std::runtime_error'
  what():  indirect register sentinel semantics are not implemented

The message comes from registerOffset in Execution/src/Pm4.cpp, which rejects a register offset of 0xffffffff. main 07377573f with #7050 merged aborts like this; the same tree with only this PR reverted (git revert -m 1 2da2284de) loads the save and runs in game. The last build without this PR that I ran, main bf2053816, also runs.

Ubuntu 24.04, kernel 7.0.0, NVIDIA GeForce RTX 5080, driver 595.99.02, Release build.

AI-assisted: yes

@XairRrR

XairRrR commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

@YossiYad since this merged, Uncharted: Legacy of Thieves Collection (PPSA05684, #4001) stops before its first frame:

[videoout] presentation failed: indirect register sentinel semantics are not implemented

The failing packet is a context register list: opcode 0x9f, pairs mode (0x80000000), 232 entries. Several of its offsets are 0xffffffff, and guest memory holds the same bytes when the packet runs, so the read is not stale:

ffffffff=ff8ff8f8 ffffffff=ff000000 ffffffff=fefffed0 ffffffff=ffffffff ffffffff=a001e504 00000000=a001e504 00000001=a001e504 ... 00000318=145a5500 0000031b=00000000 0000031c=00040730 ...

I narrowed it down on my integration build (main 7732434 + my open PRs + local patches, all unchanged between runs). The title does not run on plain main without those PRs:

  • With f7750c7 (the 8-byte {key, index << 2 | bank} type pairs) reverted, the title reaches the menu and renders it as before.
  • With only dce1573 (the version 13 table) reverted, it still stops.

So this title appears to read the type table in the compact 12-byte form, while PPSA99011 reads pairs. Maybe the layout depends on the SDK the title was built with. I have not checked what the console's library hands out per SDK version. Happy to test a candidate fix on this title.

Linux, RTX 4070 Ti SUPER, NVIDIA 615.78.08.

AI-assisted: yes

@babyhuey

Copy link
Copy Markdown
Contributor

@YossiYad another regression from this merge, a different symptom from the indirect-register sentinel above, on a free catalog title anyone can run: Middle Ages Ping Pong (PPSA28319, a Unity homebrew from the homebrew catalog). Windows 11, RTX 3080, WinLibs GCC 15.2, Release.

  • Before: a62c724c3 runs the title at 59.95 FPS for the full 60 s.
  • Now: a6dd29b3d crashes at start-up, 2 of 2 runs: 0xc0000005, a read of 0x18 at app.exe+0x145782d, right after Unity's own todo: GfxDevicePS5SharedData::CreateWorkload() line (rcx/rbx = 0). Before this PR the same point goes on to start its worker threads and load "unity default resources".
  • First-parent bisect (30 s per step; good = reaches "unity default resources"): bd7cd1be1 (this merge's first parent) good, 2da2284de (this merge) bad, with the same crash at every later bad step. (fix(relinker): run guest preinit arrays before the module initializer #6112's start-up abort, reverted by revert(relinker): run guest preinit arrays before the module initializer (#6112) #7646, was told apart separately.)
  • Revert: a6dd29b3d with only git revert -m 1 2da2284de reaches "unity default resources" again, with no fault.

Like the NHL 27 report, this isn't narrowed to one of the two changes yet: the types array becoming {key, bankIndex} pairs, or sceAgcGetRegisterDefaults() now returning the version 13 table. I'll add which one once I've tried each on its own.

AI-assisted: yes

@babyhuey

Copy link
Copy Markdown
Contributor

Narrowed the Middle Ages Ping Pong crash to the types layout change in Command/src/RegisterDefaults.cpp; the version 13 return in Misc/src/RegisterDefaults.cpp isn't involved. Same machine, a6dd29b3d, 30 s per run, each variant one file restored from 2da2284de^1 (neither file has changed on main since this PR):

  • Only Misc/src/RegisterDefaults.cpp reverted (legacy sceAgcGetRegisterDefaults() back to version 0, {key, bankIndex} pairs kept): crashes as on main, 0xc0000005 reading 0x18 at app.exe+0x145782d.
  • Only Command/src/RegisterDefaults.cpp reverted (types passed through from the source table again, version 13 kept): runs past GfxDevicePS5SharedData::CreateWorkload() to unity default resources with no fault.

So with this title, Unity reads RegisterDefaults.types (offset 0x30) expecting the source's 3-word entries; given the 8-byte pairs, it ends up with a null bank pointer. I didn't run the new Commands.cpp register-defaults test with that file reverted, since it checks the pairs layout.

Could the indirect register sentinel reports above have the same cause? A guest stepping through the pairs with a 12-byte stride would read keys from the wrong offsets. I haven't checked those titles, so I can't say.

@babyhuey

Copy link
Copy Markdown
Contributor

Correction to my two comments above: they were tested on a6dd29b3d, which predates #7681 (fea306dff, for #7678). Middle Ages Ping Pong imports sceAgcGetRegisterDefaults2 and sceAgcGetRegisterDefaults2Internal, not the legacy sceAgcGetRegisterDefaults, so it is the #7678 case: the versioned tables handing out 8-byte pairs. On main fd423f9b2, which includes #7681, it runs again: 2 of 2 runs for the full 60 s at 59.92 and 59.93 FPS, with no fault, on the same machine. Nothing more is needed here for this title. #7681's Sword of the Sea run also answers my question about the sentinel reports.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants