Skip to content

feat(agc): run COND_INDIRECT_BUFFER branches whose comparison always passes - #898

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
alfalfa-star:feat/agc-cond-indirect-buffer
Oct 7, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
alfalfa-star:feat/agc-cond-indirect-buffer

Conversation

@alfalfa-star

Copy link
Copy Markdown
Contributor

What

Run COND_INDIRECT_BUFFER branches whose comparison always passes, and stop sceAgcCbBranch from rejecting fields that such a branch never reads.

PPSA14632 (LEGO Horizon Adventures, Unreal Engine) calls sceAgcCbBranch(cb, mode 1, compare function 0, compareAddress, mask 0, reference 0, 0, then, n, 0, nullptr, 0) from one place (eboot 0x5084ea4, through a wrapper at 0x50ff640). The caller never sets rcx, so the compare address is whatever the register holds: it was 0x300d5f4ac8 in one run and 0x300d5f4e84 (only 4-byte aligned) in another. On main:

  • The writer throws sceAgcCbBranch: null or misaligned address 0x0 for the null else buffer, and, once that is relaxed, for the misaligned compare address.
  • The driver accepts opcode 0x3f only as the 4-dword INDIRECT_BUFFER (invalid INDIRECT_BUFFER size), so the 14-dword branch would fail at submission.

Changes:

  • Writer: sceAgcCbBranch checks the compare address only when the compare function is not 0 (always), and a buffer address only when its size is not 0. The packet is written as before.
  • Driver: copySegment runs the first buffer of an always-pass COND_INDIRECT_BUFFER in modes 1 and 2 in place of the packet, as it does for a non-chained INDIRECT_BUFFER. Modes 1 and 2 are assumed to be AMD's if-then and if-then-else. A real comparison, a predicated branch and other modes throw. A comparison would have to read memory that earlier packets of the queue may still write, so it is left for a title that needs it.

TechnicalDebt lists both.

Tested

  • Arch Linux, GCC 16.2, Release, RX 9070 XT (RADV).
  • agc_branch_patch_tests: an always-pass branch with an unaligned compare address and a null else buffer of 0 dwords, and an empty branch, are written; a comparison with a misaligned address, or a null buffer with a size, still throws without advancing the buffer. Fails on main.
  • agc_driver_pm4: an always-pass if-then and if-then-else run only the first buffer, an empty branch skips to the next packet, and a comparison or mode 0 rejects the whole submission. Fails on main with invalid INDIRECT_BUFFER size.
  • Full ctest: 359/359.
  • PPSA14632: the title gets past the branch and runs on to an unrelated storage image access to depth/stencil surface ... is not implemented, which feat(agc): depth planes sampled as 2D arrays, cubes and integer views #425 may address.
  • Windows not built or tested.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes

🤖 Generated with Claude Code

…passes

PPSA14632 (Unreal Engine) writes sceAgcCbBranch(mode 1, compare
function 0, mask 0, reference 0) with a then buffer and a null else
buffer of 0 dwords. It leaves the compare address uninitialised, so it
is sometimes only 4-byte aligned. The writer rejected the null else
buffer and the misaligned compare address, and the driver accepted
opcode 0x3f only as the 4-dword INDIRECT_BUFFER, so the 14-dword branch
would have failed at submission.

sceAgcCbBranch no longer checks the compare address of an always-pass
branch, which reads nothing, or the address of a buffer of 0 dwords.
The driver runs the first buffer of an always-pass COND_INDIRECT_BUFFER
in modes 1 and 2 (if-then and if-then-else) in place of the packet. A
comparison, a predicated branch and other modes throw.

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

github-actions Bot commented Oct 7, 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
#1100 docs/dev/TechnicalDebt.md L231

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the packet layout, modes 1/2 and compare function 0 against AMD's si_ci_vi_merged_pm4defs.h (COND_INDIRECT_BUFFER_MODE_IF/IF_ELSE, FUNC_ALWAYS), build + full ctest on Linux (398/398), and that both new tests fail without the change; merging.

@adriadam10
adriadam10 merged commit d95ce1f into boykopovar:main Oct 7, 2026
3 checks passed
@alfalfa-star
alfalfa-star deleted the feat/agc-cond-indirect-buffer branch October 7, 2026 17:55
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.

2 participants