Skip to content

fix(libSceAgc): preserve versioned register defaults layout - #7681

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
MarcZX10:codex/fix-agc-register-defaults2-layout-7678
Oct 19, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
MarcZX10:codex/fix-agc-register-defaults2-layout-7678

Conversation

@MarcZX10

@MarcZX10 MarcZX10 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What

Fixes #7678 by keeping the legacy no-argument sceAgcGetRegisterDefaults() key/bank pair table while restoring the original three-word compact entries for sceAgcGetRegisterDefaults2(version) and its internal counterpart. This preserves the homebrew layout measured for the legacy path without changing the versioned SDK tables used by retail titles. Adds regression coverage for public versions 0 and 10 and internal version 0.

Tested

Linux, GCC 12.2: syntax-checked the changed command source, export source, AGC command test source and guest_shader_link test source. A focused standalone runtime smoke test confirmed legacy public pairs, versioned public compact entries for versions 0 and 10, and the internal version 0 compact entry. git diff --check, conventions against current upstream/main, and the open-PR overlap check passed. Full CMake/CTest was not run locally because configuration is blocked by the uninitialized SDL2 submodule and missing XRandR development files.

Windows contributor report on the prior code-identical head: Sword of the Sea (PPSA25344) reached the title screen in one 180-second run with no indirect-register sentinel abort. The full build passed agc_initialization and agc_commands; guest_shader_link initially failed on its pointer-equality assertion. Replacing that assertion with a comparison of the four bank register counts (the test-only correction now in this PR) made all three tests pass in the contributor's local build. The retail-title run preceded that test-only correction. No local retail-title or GPU runtime test is claimed.

Checklist

@m335671

m335671 commented Oct 18, 2026

Copy link
Copy Markdown

Full build and title run of this PR's head cf386a16b on Windows 11 Pro 26200, Ryzen 9 7900, RTX 4070 12 GB (NVIDIA 616.92), WinLibs GCC 15.2.0, Release, BUILD_TESTING=ON.

Tests. agc_initialization and agc_commands pass. guest_shader_link fails (exit 0xc0000409, a Require abort): core/libs/tests/GuestShaderLink.cpp:39 still requires the pointer this PR splits:

Require(defaults == sceAgcGetRegisterDefaults2(13));

One way to keep that test's intent: compare the table contents instead of the pointer, i.e. the four bank register counts at 0x20. The test already includes <cstring>:

-    Require(defaults == sceAgcGetRegisterDefaults2(13));
+    Require(std::memcmp(defaults + 0x20, static_cast<unsigned char*>(sceAgcGetRegisterDefaults2(13)) + 0x20, 16) == 0);

With that line applied locally on top of cf386a16b, all three pass (agc_initialization, agc_commands, guest_shader_link). Could you fold it into this PR?

Title. Sword of the Sea (PPSA25344, 01.510.000, imports sceAgcGetRegisterDefaults2), the second title in #7678, relinked with this head's relinker and every library built at it. A 180 s boot from a cold shader cache (1,324 misses) has no indirect register sentinel abort and no other abort. It reaches the title screen and holds 124–125 presents per 10 s there, the same rate as before #6270. One run. On main 0bc3003f8 the same boot stops a few seconds in (details in #7678).

AI-assisted: yes (Claude Code). The build, the tests, the run and this comment were done and written by Claude Code on my machine.

@github-actions

github-actions Bot commented Oct 18, 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

@MarcZX10
MarcZX10 force-pushed the codex/fix-agc-register-defaults2-layout-7678 branch from cf386a1 to 4fbec1e Compare October 18, 2026 22:55
@MarcZX10
MarcZX10 force-pushed the codex/fix-agc-register-defaults2-layout-7678 branch from 4fbec1e to 0c2260e Compare October 18, 2026 22:58
@WhiteAssassins

Copy link
Copy Markdown

Additional Linux title confirmation: ASTRO BOT (PPSA21567, v01.019.000) reaches in-game with this fix. Thanks for addressing this regression.

Host: CachyOS/Arch Linux, Ryzen 7 5700G, Radeon RX 6750 XT, RADV/Mesa 26.2.4, GCC 16.2.1.

Controlled comparison

Base: 8b8edc56195f2ff6ec8e1a87b2463dc408580c54. Same relinked executable, bundled guest modules, assets and other runtime libraries in both runs. We rebuilt libSceAgc.prx with the production change captured from this PR, including the callers affected by the GetRegisterDefaults signature change, and selected the library per process. This was an isolated test of the PR's change on that base, not a full build of the latest PR head.

Library Result
Original libSceAgc.prx SIGSEGV after 7.00 s
Library with this fix Ran for the full 60 s test window and rendered; stopped deliberately at the time limit and exited with code 0

The actual loaded libSceAgc.prx path was checked through /proc/<pid>/maps in each run. The focused agc_initialization and agc_commands tests also passed. The full test suite was not run.

Original failure

Repeated launches stopped in the title's bundled libc.prx.guest.prx. GDB confirmed:

instruction: vmovdqu XMMWORD PTR [rdi], xmm0
rdi = 0x4c0000000
rsi = 0
rdx = 0x3f800000

This is the first write of a memset-like operation over 1,016 MiB. The destination belonged to the reserved 0x40b800000–0x500000000 VMA with ---p permissions. The kernel's file offset 0x143c6 corresponds to ELF virtual offset 0x103c6 in this PRX. There was no preceding assert or unimplemented-function message explaining this stop.

A separate temporary GDB experiment restoring the original compact type tables for the public and internal version-13 calls also passed that failure point. Applying the real production change to the library then reproduced the improvement without GDB. We have not traced every instruction between reading the defaults table and the allocator's invalid write, so this does not claim a complete corruption trace.

Manual follow-up

After installing the corrected library for my normal Astro Bot launch, I confirmed that the game reaches in-game, beyond the automated startup test. I have not yet measured the duration of that manual session or established long-term stability, performance gains or complete visual correctness.

AI-assisted: Codex performed the controlled builds/debugging/tests and helped draft this report; I manually confirmed the in-game result and authorized posting it.

@Longziip

Copy link
Copy Markdown

Another retail title with the same regression: EA SPORTS UFC 6 (PPSA23566, SDK 9.04 per its process parameters).

On current main it stops right after loading its render module with
sceAgcCbSetShRegisterRangeDirect: invalid register range. The call comes from the title's
Engine.Render.Core2.PlatformPs5.prx with offset 0xffffffff, count 4. Tracing the title's code:
it calls sceAgcGetRegisterDefaults2(13) (NID 2JtWUUiYBXs), walks the root's type table at +0x30
in 12-byte steps (count at +0x38), compares the key at +0 and decodes bank = word & 3,
index = word & 0x3fc from +4. Over the 8-byte pairs it finds none of its keys, stores -1 as
the register offset, and later passes it to sceAgcCbSetShRegisterRangeDirect.

I tested a change equivalent to this PR (legacy sceAgcGetRegisterDefaults keeps the pairs,
sceAgcGetRegisterDefaults2/2Internal return the compact 12-byte entries over the same blocks), not this PR's head:
UFC 6 gets past the crash and plays its intro cinematic. agc_commands, agc_initialization
and guest_shader_link pass with the test updates.

Windows 11 Pro, Ryzen 7 5700X, RTX 4060 8 GB (driver 32.0.16.1664), 32 GB RAM, WinLibs GCC 15.2.0, Release.

@WhiteAssassins

Copy link
Copy Markdown

Another retail title with the same regression: EA SPORTS UFC 6 (PPSA23566, SDK 9.04 per its process parameters).

On current main it stops right after loading its render module with sceAgcCbSetShRegisterRangeDirect: invalid register range. The call comes from the title's Engine.Render.Core2.PlatformPs5.prx with offset 0xffffffff, count 4. Tracing the title's code: it calls sceAgcGetRegisterDefaults2(13) (NID 2JtWUUiYBXs), walks the root's type table at +0x30 in 12-byte steps (count at +0x38), compares the key at +0 and decodes bank = word & 3, index = word & 0x3fc from +4. Over the 8-byte pairs it finds none of its keys, stores -1 as the register offset, and later passes it to sceAgcCbSetShRegisterRangeDirect.

I tested a change equivalent to this PR (legacy sceAgcGetRegisterDefaults keeps the pairs, sceAgcGetRegisterDefaults2/2Internal return the compact 12-byte entries over the same blocks), not this PR's head: UFC 6 gets past the crash and plays its intro cinematic. agc_commands, agc_initialization and guest_shader_link pass with the test updates.

Windows 11 Pro, Ryzen 7 5700X, RTX 4060 8 GB (driver 32.0.16.1664), 32 GB RAM, WinLibs GCC 15.2.0, Release.

Nice catch. This lines up very well with the Astro Bot regression I reported above.
In my case the bad table layout did not fail immediately with invalid register range; Astro continued further and eventually crashed in its bundled libc.prx.guest.prx while writing to an unmapped guest range. Applying the #7681 layout fix removed that crash and the game reached in-game.
Your trace showing the 12-byte walk, failed key lookup, and resulting 0xffffffff register offset is very useful evidence for the underlying mechanism.

@github-actions github-actions Bot added the agc libSceAgc* label Oct 18, 2026
@hugodearaujo

hugodearaujo commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Tested this PR's head (0c2260e) on a retail SDK title, and the title's own code agrees with the layout it restores. EA SPORTS FC 25 (PPSA20049) imports sceAgcGetRegisterDefaults2 (2JtWUUiYBXs) and sceAgcGetRegisterDefaults2Internal (wRbq6ZjNop4) in Engine.Render.Core2.PlatformPs5.prx, and walks the type table at +0x30 in 12-byte steps after both calls:

; after sceAgcGetRegisterDefaults2(13), module offset 0x6b36a
mov   ecx, [rax+0x38]            ; count
mov   rdx, [rax+0x30]            ; type table
cmp   dword [rdi], 0xe24f806d    ; key
add   rdi, 0xc                   ; next entry
; key found: second dword of the entry
lea   rsi, [rsi+rsi*2]
mov   esi, [rdx+rsi*4+4]
bextr edi, esi, 0x802            ; bits 2..9: index
and   esi, 3                     ; bank
; key not found: mov rsi, -1, stored as the register
; after sceAgcGetRegisterDefaults2Internal(13), module offset 0x6d2ef
lea   rcx, [rcx+rcx*2]
lea   rcx, [rcx*4-0xc]           ; (count - 1) * 12
cmp   dword [rax+rdx], 0x6ac156ef
lea   rsi, [rdx+0xc]

A key it can't find becomes -1, which fits the 0xffffffff offsets in the indirect register lists that #7678 and the other reports stop on.

Runs on Linux, RX 9070 XT, RADV (Mesa 26.2.5), main 8b8edc5 with #6343, #7606, #7413 and #6960; only libSceAgc.prx differs between them:

@Longziip

Copy link
Copy Markdown

@WhiteAssassins Thanks! That difference makes sense with the mechanism: when the 12-byte walk misses a key, UFC 6 stores -1 and the range check stops it right away, but if a misaligned read happens to match a key, the title gets a wrong block index instead and computes a plausible but wrong register or memory location, so it can fail much later and somewhere else, like your libc.prx write. That would explain why each title breaks differently with the same root cause.

I can also run UFC 6 on this PR's exact head if that helps the merge

@WhiteAssassins

Copy link
Copy Markdown

@WhiteAssassins Thanks! That difference makes sense with the mechanism: when the 12-byte walk misses a key, UFC 6 stores -1 and the range check stops it right away, but if a misaligned read happens to match a key, the title gets a wrong block index instead and computes a plausible but wrong register or memory location, so it can fail much later and somewhere else, like your libc.prx write. That would explain why each title breaks differently with the same root cause.

I can also run UFC 6 on this PR's exact head if that helps the merge

Yes, testing UFC 6 on the exact PR head would definitely help. Your current trace already gives strong evidence for the mechanism, but confirming the actual head would remove the only remaining difference from your current test.
If it gets past the same 0xffffffff / invalid-register failure on the PR head, that would be another very useful retail-title confirmation for the merge. Thanks!

@JJenkx JJenkx mentioned this pull request Oct 18, 2026
12 tasks done
@MarcZX10

Copy link
Copy Markdown
Contributor Author

Thanks for the Windows test report and the suggested assertion update. The pointer-identity check has already been replaced on the current PR head (0c2260e30) with a comparison of the four bank counts at offset 0x20, preserving the test intent while allowing the versioned layout to differ. Your local run reports all three focused tests passing with that change. The current PR head also has additional title evidence in this thread.

@oneandonlydean
oneandonlydean merged commit fea306d into boykopovar:main Oct 19, 2026
11 of 12 checks passed
PhialsBasement added a commit to PhialsBasement/AnyPS5 that referenced this pull request Oct 19, 2026
…aults

The versioned tables hand out compact 12-byte type entries since boykopovar#7681,
and the MRT0 checks on them were dropped with the pairs. The walk now
takes the entry size: MRT0 is block 78 of the legacy pairs and of the
compact version 13 table, block 73 of version 10, and every versioned
table from 4 holds it. The debt entry names the layout of each entry
point.

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

teepean commented Oct 19, 2026

Copy link
Copy Markdown

Stray (PPSA02100, Unreal Engine 4, retail) is another title #6270 stopped, and this PR fixes it there too. Stray imports the versioned getters sceAgcGetRegisterDefaults2 (2JtWUUiYBXs) and sceAgcGetRegisterDefaults2Internal (wRbq6ZjNop4), not the legacy one.

Build Result
main 0bc3003 (contains #6270) aborts 11 to 25 s after start: indirect register sentinel semantics are not implemented
main fc62332 + this PR at cf386a1 (the same libSceAgc code as the merged head 0c2260e) starts every time: a 45 s startup check and 11 runs of about 4 minutes each to the start of Dead City (5 of them with an unrelated change on top), none with the abort

Before this PR, a first-parent bisect from e1338ff to 0bc3003 ended at #6270's merge 2da2284. On 0bc3003, restoring only the compact type entries in Command/src/RegisterDefaults.cpp, with the rest of #6270 kept, also made Stray start.

Setup: Linux (CachyOS, kernel 7.2.10), Ryzen 9 9950X, RTX 3090 (NVIDIA 615.78.08), GCC 16.2.1, Release, stock engine.ini.

AI-assisted: yes (bisect, tests and text with Claude Code; the runs are from my machine).

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

Labels

agc libSceAgc* fix error Closes an issue (Fixes #N)

Projects

None yet

7 participants