Skip to content

fix(shader): act on the containing dword for unaligned 32-bit buffer atomics - #6094

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
n-delic:pr/unaligned-atomics-32
Oct 18, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
n-delic:pr/unaligned-atomics-32

Conversation

@n-delic

@n-delic n-delic commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

What

A 32-bit buffer atomic whose address is not a multiple of 4 now acts on the dword that holds the address. On main it records an unaligned BDA fault and is dropped. 64-bit buffer atomics are unchanged: off an 8-byte boundary they still fault.

Source: section 8.1.7 ("Alignment") of the RDNA2 ISA:

For Dword or larger reads or writes of non-formatted ops (such as BUFFER_LOAD_DWORD), the two LSBs of the byte-address are ignored, thus forcing Dword alignment.

That is mode DWORD of SH_MEM_CONFIG.alignment_mode. The console's mode is unknown (as the existing "V# bases off a DWORD boundary" entry already notes).

  • MXGP 26 (PPSA30839, Unreal Engine 5) sets bits with buffer_atomic_or (MUBUF 0x3a) at odd byte offsets in a compute shader. In the strict modes, section 3.11 reports a misaligned atomic as a memory violation, so the console is not in one of those.
  • What an atomic does in mode UNALIGNED is not documented. This is the part a hardware-oracle measurement could settle, if someone can run one.
  • For 64-bit atomics, mode DWORD aligns only to 4 bytes ("the smaller of element size or a dword"), which a host 64-bit atomic cannot address, so they keep the fault.

All of this is recorded in docs/dev/debt/shader.md.

Tested

Linux (Pop!_OS, kernel 7.1.5), GCC, Release.

  • New agc_driver_buffer_atomic_unaligned: 32 lanes each run buffer_atomic_add (glc) of 1 at byte offset lane * 4 + k, for k = 0 to 3. The test checks that:

    • each lane gets its dword's previous value;
    • each dword is incremented once;
    • the dword after the last lane is untouched.

    On main it fails at k = 1 (lane 0 returned 3735928559, expected 0).

  • agc_shader_memory: the specialized 32-bit buffer_atomic_inc no longer emits a record_bda_fault call.

  • agc_driver_buffer_atomics, _float, _inc_dec64, _or_swap64: pass.

  • ctest -R 'agc|shader': 563 of 563 pass. agc_driver_preparation_scope_settle failed once and passed on reruns; it also failed intermittently on the rebased feat(shader): add GDS ordered counts and ordered-append dispatches #5176 branch, which does not touch it.

  • MXGP 26: with this change (on a branch with other local fixes), the title runs with 0 BDA faults, where it reported reason=7 (unaligned) faults before.

  • tools/check_conventions.py --base upstream/main passes.

Not tested: AMD hardware, the hardware oracle, Windows.

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: PPSA30839 (MXGP 26), no bring-up discussion yet
  • GPUs tested: NVIDIA RTX 3060, driver 595.104.02, Linux
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

@github-actions github-actions Bot added shader core/shader docs agc libSceAgc* labels Oct 14, 2026
@fma-rc

fma-rc commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

I ran the unaligned case on real RDNA2 with tools/hw-oracle: gfx1036 (Ryzen 7 7800X3D iGPU), Linux 7.2 KFD, wave32, raw buffer V# (word3 0x31016fac), ieee 0, dx10_clamp 1, denorm32 0, denorm16 3, round 0, coarse memory. KFD programs SH_MEM_CONFIG.alignment_mode as UNALIGNED for gfx10 (kfd_device_queue_manager_v10.c).

buffer_atomic_or / buffer_atomic_add (with and without glc) at byte offsets 1, 2 and 3 abort the dispatch: the queue dies and the oracle exits with no result. Offsets 0, 4 and 8 act on that dword (or 0xff at 4 turns 0x55667788 into 0x556677ff, returns the old dword). As a control on the same queue, buffer_load_dword at offsets 1/2/3/5 reads byte-shifted (0x88112233, 0x77881122, 0x66778811, 0xcc556677), so the queue really is in UNALIGNED mode.

So on gfx1036 a misaligned atomic faults in UNALIGNED mode too, not only in the strict modes. If gfx1013 behaves the same, DWORD mode is the only one where MXGP's atomics would run, which is what this PR models. With this PR on main ab9a476, agc_shader_memory and agc_driver_buffer_atomic_unaligned pass on NVIDIA RTX 5080, RADV and lavapipe.

@milescoviello

Copy link
Copy Markdown
Contributor

MXGP 26 (PPSA30839), the title in the description, on Linux: Ubuntu 24.04, GCC 13.3, Release, RTX 4070 Ti (NVIDIA 595.99), main d92a8da with #6160 merged (it needs that to get past its s_swappc_b64 stop at 135 s), relinked with --to-intel, 180 s runs with pad input.

  • Without this PR: BDA access failed: ... instruction=0xa08 bytes=4 stage=4 reason=7 36 and 70 times in two runs.
  • With this head 6c188638a added (clean merge): 2 of 2 runs reach the 180 s limit with none of those lines. A run with screenshots reaches the title screen and the "Welcome to MXGP26" account prompt.

Not checked on Windows, and nothing here says what the console does with the misaligned atomic.

AI-assisted: yes

@zaryabzip

Copy link
Copy Markdown
Contributor

Windows run, for the "Not tested: ... Windows" line. Windows 11 (26200), RTX 3060 Laptop GPU (NVIDIA 617.42), WinLibs GCC 15.2.0 r7 (UCRT), Release, BUILD_TESTING=ON. Head 6c188638a merged onto main c0be1f69b (clean); ANYPS5_GPU=3060, and the test log shows the RTX 3060 selected.

  • agc_driver_buffer_atomic_unaligned, agc_shader_memory, agc_driver_buffer_atomics, _float, _inc_dec64, _or_swap64: pass.
  • With SpirvRuntimeBuffer.cpp from main and the PR's tests (2 of 2 runs):
    • agc_driver_buffer_atomic_unaligned fails: buffer_atomic_add at byte offset 1: lane 0 returned 3735928559, expected 0.
    • agc_shader_memory fails: specialized buffer atomic: a 32-bit atomic records an unaligned fault instead of acting on its dword.
  • ctest -R "agc|shader|guest_playgo" -j4: 580/580 pass with the PR. main: 578/579. Both skip the same 3 tests (agc_host_import_sparse, agc_driver_sparse_dma_buf_import, agc_driver_stencil_ref_export).
  • The one main failure was agc_driver_resource_cache_storage_admission (numerical exception, once under -j4). It passed 2 of 2 when run alone.

Not run: MXGP 26, the hardware oracle.

AI-assisted: yes (Claude Code)

This was referenced Oct 15, 2026
@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 1341 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 5021 docs/dev/debt/shader.md
PR 5371 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 5426 docs/dev/debt/shader.md
PR 5500 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 5941 docs/dev/debt/shader.md
PR 5958 docs/dev/debt/shader.md
PR 6009 docs/dev/debt/shader.md
PR 6177 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp
PR 6222 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 6232 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 6235 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 6412 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp
docs/dev/debt/shader.md
PR 6488 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
docs/dev/debt/shader.md
PR 6503 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 6645 core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
PR 6767 docs/dev/debt/shader.md
PR 6977 core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp
docs/dev/debt/shader.md
PR 7223 docs/dev/debt/shader.md
PR 7426 docs/dev/debt/shader.md

@erm14254

Copy link
Copy Markdown

Code Violet (PPSA26528, Unreal Engine 5.5) on Windows: Windows 11 (10.0.22631), RTX PRO 6000 Blackwell Workstation Edition (NVIDIA 597.16), WinLibs GCC 15.2.0 r7, Release. main d92a8da with #6258 and the three other fixes listed in #6256 (the title needs them to reach its title screen), then this PR's head 6c18863 cherry-picked on top (clean).

  • Without this PR: deferred write-back failed: BDA access failed: ... instruction=0x7bc bytes=4 stage=4 reason=7, once per present at the title screen (a compute shader).
  • With it: no such line in a cold run and a warm run to the title screen. agc_driver_buffer_atomic_unaligned, agc_shader_memory and agc_driver_gds_append_consume pass on that build.
  • The picture is unchanged, so the dropped atomic is not behind the title's missing geometry (tracked in Code Violet (PPSA26528) #6256).

Nothing here says what the console does with the misaligned atomic either.

AI-assisted: yes

@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the change against the RDNA2 ISA §8.1.7 (DWORD alignment mode), built and ran agc_shader_memory, agc_driver_buffer_atomic_unaligned and the agc_driver_buffer_atomics* tests on RADV, and confirmed the two new tests fail without the SpirvRuntimeBuffer.cpp change. Windows, NVIDIA and title runs from fma-rc, zaryabzip, milescoviello and erm14254 agree; merging.

Minor, not blocking: the docs/dev/debt/shader.md line says the UNALIGNED-mode behaviour is not measured, but fma-rc measured above that misaligned atomics abort the dispatch there. Worth updating in a follow-up.

@keravyn-ops

Copy link
Copy Markdown

AMD / Windows run of this PR at 6c18863: Radeon 780M (RDNA 3, 0x15bf), AMD driver 25.10.20.01 (32.0.21020.1007), Windows 11 Home 10.0.26100, WinLibs GCC 15.2.0 r7, Release.

  • agc_driver_buffer_atomic_unaligned: pass (buffer atomic unaligned tests passed, 0.69 s)
  • agc_shader_memory: pass (0.50 s)

AI-assisted: yes (runs made on the device above).

n-delic and others added 2 commits October 18, 2026 17:54
…atomics

The RDNA2 ISA (8.1.7) ignores the two low address bits of dword or wider
non-formatted accesses in alignment mode DWORD. MXGP 26 (PPSA30839) sets
bits with buffer_atomic_or at odd byte offsets, which recorded an
unaligned BDA fault and dropped the atomic. A 32-bit buffer atomic now
acts on the dword that holds its address; 64-bit atomics off an 8-byte
boundary still fault.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n their containing dword

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@adriadam10
adriadam10 force-pushed the pr/unaligned-atomics-32 branch from 6c18863 to 96c692c Compare October 18, 2026 16:20
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 18, 2026
@adriadam10
adriadam10 merged commit dc2aae4 into boykopovar:main Oct 18, 2026
9 checks passed
RINNECODER added a commit to RINNECODER/AnyPS5 that referenced this pull request Oct 18, 2026
…lation, and AGC resource fixes (#565)

* fix(libkernel): take NOTE_LOWAT on socket read and write events

kevent threw for NOTE_LOWAT. On a socket's EVFILT_READ it now sets the
low-water mark as FreeBSD 11's filt_soread does: the event fires once the
buffered bytes reach it, or at end of file or on a listening socket, and
a later change of the knote replaces it. EVFILT_WRITE takes a mark of 1,
where the host's writability stands in for a free byte; other write marks
and marks below 1 still throw. A pipe ignores the mark, as filt_piperead
does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit e1d4ccb)

* fix(libkernel): read ITIMER_VIRTUAL and ITIMER_PROF as disarmed in getitimer

setitimer refuses to arm the CPU interval timers and already reads and
disarms them as zero, so they are never armed. getitimer threw for them
instead of reporting that state, as FreeBSD's kern_getitimer does for a
timer that is not set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 9f704dc)

* fix(libkernel): return EFAULT from fstat for a null stat buffer

FreeBSD's fstat looks the descriptor up before it copies the result
out, so a bad descriptor still fails with EBADF and an open one fails
with EFAULT when sb is null. fstat and sceKernelFstat threw
std::invalid_argument instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit be17961)

* fix(libkernel): return EINVAL from scePthreadCondattrSetclock and Destroy for a null attribute

Both threw std::invalid_argument for a null or uninitialized attribute.
FreeBSD's libthr returns EINVAL for one, as the other scePthread
attribute calls here already do. The clock id is still stored as
given.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 62ac8d6)

* fix(libkernel): keep refusing a read low-water mark above 1 on non-stream sockets

On Linux, FIONREAD on a datagram socket reports the next datagram only,
where FreeBSD's sbavail counts every queued byte, so a read mark above 1
there could never fire. kevent now throws for it as before; stream
sockets and marks of 1 are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 91fbafd)

* fix(libkernel): end guest thread, fiber and context frame chains at a zero record

Unreal Engine walks the frame-pointer chain until it reads a return
address of 0 and never checks the frame pointer itself. Guest threads
started with whatever rbp the host code held, so the walk ran into host
start-up frames (glibc's saved rbp 0x21 in a LEGO Batman core dump,
address 8 in Returnal on Windows) and faulted. Fibers ended their chain
with a null frame, and makecontext started its function with rbp 0, so
the same walker read address 8 there too.

Guest threads now start through a thunk that points rbp at a
{saved rbp 0, return address 0} record, as the relinker's entry stub
does for the main thread; the fiber trampolines push the same record,
and the makecontext start function is built with frame pointers so its
frame, over the zero return address makecontext stores, ends the chain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ff9bf22)

* fix(libkernel): return EFAULT from sceKernelStat and sceKernelUnlink for a null path

(cherry picked from commit 274f092)

* fix(libkernel): report EINVAL for getdirentries on a non-directory

sceKernelGetdirentries on a descriptor that is not a directory returned
ENOTDIR from the host getdents64 on Linux, and ENOTDIR for every descriptor
missing from the directory table on Windows, including invalid ones.
FreeBSD 11.4's kern_getdirentries returns EINVAL when the vnode is not a
directory.

Return EINVAL for a regular file on both hosts and EBADF for an invalid
descriptor on Windows. Test getdents and getdirentries on a regular file and
list the pipe case, which still fails with ESPIPE on Linux, in the library's
technical debt file.

Written with AI assistance (Claude).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 9dc6542)

* fix(libkernel): return EPROTOTYPE for a socket type inet does not have

socket() returned EPROTONOSUPPORT for every socket type it does not create.
FreeBSD 11.4's socreate returns EPROTOTYPE when no protocol is found, the
protocol is 0 and the type is not 0, and EPROTONOSUPPORT when a protocol was
given. Its inet domain (in_proto.c) has SOCK_RAW entries in every kernel and
a SOCK_SEQPACKET entry only with SCTP, and no SOCK_RDM or other type.

Return EPROTOTYPE for a type the inet domain never has, such as SOCK_RDM,
with protocol 0. SOCK_RAW and SOCK_SEQPACKET keep EPROTONOSUPPORT, since
whether the console creates them is not known. Test the unknown types and
mismatched protocols for AF_INET and AF_INET6, and list the socket types and
families that are not created in the library's technical debt file.

Written with AI assistance (Claude).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit a6c123c)

* fix(libkernel): return EFAULT from truncate, utimes, mkdir, rmdir, and rename for a null path

(cherry picked from commit 7bb7608)

* feat(libkernel): implement pthread_condattr_setpshared

PSFlyCast (PPSA99247) imports pthread_condattr_setpshared (3BpP850hBT4)
from libkernel and fails to load without it:
"./app.elf: symbol lookup error: ./app.elf: undefined symbol: 3BpP850hBT4".

The attribute stores PTHREAD_PROCESS_PRIVATE (0) or
PTHREAD_PROCESS_SHARED (1); a null or uninitialized attribute and any
other value return EINVAL without changing it, following FreeBSD 11
libthr thr_condattr.c and pthread_mutexattr_setpshared here. Sharing
across processes is not modeled, as for mutexes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sovthwyu4H63RMbRVha2Sy
(cherry picked from commit 05b457d)

* fix(libkernel): export sysarch so modules that import it load

The Wine port (PPSA99995) ships an ntdll module that imports sysarch
(b7uXQmnfB2s). libkernel did not export it, so loading stopped with
"undefined symbol: b7uXQmnfB2s" before the title ran.

Export sysarch as a stub that reports that it is not implemented: the
title loads and runs its launcher without calling it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 162f4af)

* feat(libkernel): read and set the GS base through sysarch

sysarch's AMD64_GET_FSBASE, AMD64_GET_GSBASE and AMD64_SET_GSBASE now
follow FreeBSD 11's amd64 sysarch on Linux x86-64 through arch_prctl: the
GS base is the calling thread's, a base at or above VM_MAXUSER_ADDRESS
returns EINVAL and a null argument EFAULT. AMD64_SET_FSBASE, whose base
guest and host code share for their TLS, other operations and other hosts
still throw. Wine sets its TEB this way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d7c83db)

* feat(libkernel): implement three coredump exports from the console module

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 70ff799)

* feat(libkernel): add pthread_rwlockattr_getpshared and pthread_rwlockattr_setpshared

Measured on a PS5 (firmware 12.70): the getter reports 0, and of the
values 0, 1, 2 and -1 the setter accepts only 0 and checks the value
before the attribute. A null or uninitialized attribute, or a null
output, crashes the console; here it throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bdf3adb)

* fix(libkernel): reject PTHREAD_PROCESS_SHARED in pthread_condattr_setpshared and add pthread_condattr_getpshared

On a PS5 (firmware 12.70), of the values 0, 1, 2 and -1,
pthread_condattr_setpshared accepts only 0 and leaves the attribute
unchanged otherwise. pthread_condattr_getpshared reports 0 and returns
EINVAL for a null, uninitialized or destroyed attribute, also with a
null output. With an initialized attribute a null output crashes the
console; here it throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c00c58a)

* feat(libkernel): poll regular files and directories as vop_stdpoll does

poll threw for any descriptor that is neither a socket nor a pipe, so a title polling a file it also reads stopped there. FreeBSD 11.4 hands a vnode to VOP_POLL, and both vop_stdpoll and vop_nopoll return POLLIN, POLLRDNORM and POLLOUT of the requested events for every event poll accepts and never POLLPRI, POLLRDBAND or POLLWRBAND. A file, a directory or a file read from app0.zar now answers that without a host call. File::IsVnode tells such a descriptor apart from the host file type without throwing, so a host socket lent by libSceNet still reaches its own path.

(cherry picked from commit 7e26c1a)

* feat(libkernel): poll the random device as randomdev_poll does

poll threw for /dev/random and /dev/urandom, which select already treats as ready. FreeBSD 11.4's randomdev_poll, once the device is seeded, narrows a request that has POLLIN or POLLRDNORM to those bits and returns any other request whole, POLLOUT and the band bits included. poll now answers that, and the test that relied on the random device throwing uses a kqueue descriptor, which poll still does not support.

(cherry picked from commit d0c3385)

* fix(libkernel): answer select on files and the random device as FreeBSD's poll routines do

selscan asks a descriptor once, with the flags of every set it is in (POLLRDNORM for the read set, POLLWRNORM for the write set, POLLRDBAND for the exception set, plus POLLHUP and POLLERR), and marks each set whose flags the answer shares. A seeded random device's randomdev_poll narrows a request with POLLRDNORM to that bit and returns any other request whole, so in the read set it is readable and in no other set, and otherwise it is in each write and exception set it was asked in; select reported it readable and writable together and never exceptional. A regular file or directory went to the host poll, which on Windows threw; vop_stdpoll makes it readable and writable and never exceptional, which select now answers in process on every host, as poll does.

(cherry picked from commit aabd0e7)

* feat(libkernel): poll descriptors 0 to 2 with the console model select uses

select takes descriptors 0 to 2 as a console title's terminal with no input: 0 is never readable and 1 and 2 are always writable. poll threw for them unless the host happened to attach a pipe, so the same title could select on its standard streams but not poll them. poll now gives the same answer, with POLLOUT, the one bit ttydev_poll reports for a writable terminal, for 1 and 2 and nothing for 0.

(cherry picked from commit 7462945)

* fix(libkernel): select on kqueues as kqueue_poll answers

A kqueue descriptor is readable while one of its events is triggered and
is never writable or exceptional, as FreeBSD's kqueue_poll answers, on
both hosts. On Windows select threw for it, and on Linux its /dev/null
placeholder was reported readable and writable.

A descriptor that stops being a socket, a kqueue or a pipe during the
wait now fails the call with EBADF when it was closed and is selected
again with the rest of the timeout when its number was reused, as
FreeBSD's kern_select looks every descriptor up again in each pass,
instead of throwing. On Windows a host descriptor that is not a pipe
throws when select starts, before waiting.

Depends on boykopovar#7139.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 6d4c7c3)

* fix(relinker): keep test artifacts in the build tree

The tests build their working directories with tempfile.TemporaryDirectory()
and std::filesystem::temp_directory_path(), so every executable they generate
lands in the user's system temp directory. On Windows, Microsoft Defender
quarantines those generated binaries as Trojan:Win32/Wacatac.H!ml, and the
nine tests that run one fail with WinError 5 (access denied) on a clean
checkout: autorun_exit_status, optional_plt, empty_tls, tls_function_coverage,
executable_segments, windows_icon, guest_needed_modules,
guest_module_directories and windows_import_modules.

Point TMPDIR, TEMP and TMP at a build-local directory for every test
registered here, so generated executables stay inside the build tree. Test
artifacts are then removed with the build directory, and a contributor who
needs an antivirus exclusion can scope it to the build tree instead of the
system temp directory.

(cherry picked from commit 39dd854)

* fix(relinker): append the test temp directory instead of replacing the environment

set_tests_properties replaced the ASAN_OPTIONS that add_asan_test_options appends to
guest_module_args, guest_deferred_dependencies and guest_module_fini in ASan builds.
Append the TMPDIR, TEMP and TMP settings instead.

(cherry picked from commit 5e340fa)

* fix(libkernel): report the accept queue length for a listening socket's read event

A read event on a listening socket took its data from FIONREAD, which a listening socket refuses on Linux, so kevent and sceKernelAddReadEvent reported 0 connections while saying one was ready. FreeBSD 11.4 gives a listening socket filt_solisten, which reports so_qlen, the completed connections waiting for accept. Ready now checks SO_ACCEPTCONN and on Linux reads a TCP listener's accept backlog from TCP_INFO, where tcp_get_info puts it in tcpi_unacked; with no host call for the count (Windows, macOS, a listener that is not TCP) it reports 1, the lower bound the readiness already implies.

(cherry picked from commit b40e0bf)

* fix(relinker): report processor 0 from RDTSCP under --to-rosetta

Rosetta sets RDTSCP's ECX to its constant TSC_AUX, 0xAAAAAAAB, which is
not a processor index. --to-rosetta already makes RDPID return processor
0 for the same reason, so code reading the processor through RDTSCP saw
a different value from code reading it through RDPID.

--to-rosetta now moves RDTSCP into an optional stub that runs the
instruction unchanged and then loads 0 into ECX with a MOV, which
leaves the counter in EDX:EAX and the flags as they were. A site with no
movable instruction after it for the jump is kept, as for RDPID. The
Intel target and a plain relink are unchanged.

(cherry picked from commit 57e52c1)

* fix(relinker): run guest preinit arrays before the module initializer

Converted guest modules never ran their DT_PREINIT_ARRAY entries: the
reader validated the array and the unused-NID filter kept its targets,
but no writer emitted or ran it. ld.so runs a shared object's preinit
array only for the main executable, so the Linux output runs the
entries from the DT_INIT stub before the module initializer; the macOS
and Windows outputs run them before the initializer in their startup
stubs, and the deferred Windows output exports a thunk that runs them
before the initializer for runtime-loaded modules. A module with a
preinit array and no initializer is still rejected, since the Linux
stub exists only with one.

(cherry picked from commit 9c43211)

* fix(relinker): pop the guest preinit thunk registers in reverse order

The Windows deferred preinit thunk pushed r12, r13 and r14 and then
popped r12, r13 and r14, so the caller got r12 and r14 back swapped.
InitializeDeferredModule keeps the module base, table and count live
across the call to the thunk, so a compiler that keeps any of them in
r12 or r14 sees corrupted loader state after opening a deferred module
that has a preinit array. Pop in reverse push order instead and pin the
new bytes in test_guest_preinit.

(cherry picked from commit 6574ade)

* test(relinker): provide libkernel.prx to the native guest preinit load

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit e751268)

* test(libkernel): drop the guest socket fstat case, which needs GuestSockets::Stat

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj

* test(agc): cover Intel single-queue devices in recorder fixtures

(cherry picked from commit 418d385)

* fix(libSceSystemService): park guest threads during LoadExec's shutdown as exit does

Metal Gear Solid 4 (PPSA18194) aborts on main when its launcher starts
the game: sceSystemServiceLoadExec runs the guest shutdown, a render
thread that then calls into the stopped AGC driver gets ProcessShutdown
back from LibcAwaitExit, and the exception unwinds into title code,
which terminates the process before the new executable starts.

LibcAwaitExit parks a thread only once an exit has been requested, and
LoadExec ran the shutdown without requesting one. It now starts the
shutdown the way exit does, so such threads wait until the process is
replaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5b072bc)

* fix(agc): retain submitted batches when profiling fails

(cherry picked from commit 4076fce)

* fix(agc): wake an idle queue 0 for completions the draw pipeline adds

With pipelined draws, queue 0 hands the end-of-pipe labels of a
submission to the draw pipeline and can enter its idle wait before the
pipeline commits them. When such a label raises an end-of-pipe
interrupt behind recorded work, the pipeline attaches the delivery to
the batch as a completion and submits it, but queue 0 only reaps when
it saw completions pending before it went to sleep, and nothing woke
it. The batch was then reaped, and the interrupt delivered, only when
the next submission arrived, so a title that waits for that interrupt
before submitting more work stopped.

Queue 0's idle wait now also ends when completions are pending, and
the pipeline's end of a submission (and the REWIND path's open-work
submit) wakes it when they are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit bd2197e)

* fix(agc): suppress the flip end-of-pipe event on queues that raise release interrupts

(cherry picked from commit 2aa6bfc)

* test(agc): order the late interrupt's commit after queue 0 waits

The test relied on queue 0 reaching its idle wait before the draw
pipeline committed the end-of-pipe label. It now forces that order: a
first submission runs the dispatch and leaves its batch unreaped
(APS5_NO_OPPORTUNISTIC_REAP), queue 0 must be waiting, then the label
is submitted while the test holds the GPU mutex, so the pipeline commits
it only after queue 0 handed it over and waits again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ff4ecc6)

* fix(agc): count pipeline jobs after queue admission

(cherry picked from commit 4f639d5)

* fix(libc): allocate the getifaddrs list on the application heap

FreeBSD's freeifaddrs is a plain free of the list, and Doom 3 BFG
(PPSA99313) releases the list with free. The list came from host malloc,
so the guest free stopped on an unregistered allocation; it now comes
from the application heap, and freeifaddrs returns it there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 18cf27a)

* feat(libc): implement getw and putw

(cherry picked from commit 7c07ebd)

* fix(libc): take sceLibcMspaceReallocalign's alignment before its size

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 3802f88)

* perf(agc): share unchanged shader registry chunks between published versions

PublishRegisteredShader copied the whole code-address map, with every
header vector and snapshot reference, for each registration under the
driver mutex, so a title registering its shaders while it loads paid
O(n^2). The registry now keeps its entries in address-ordered chunks of
at most 64 shared by every version that has not changed them; a write
clones only the chunk it touches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5b12f1c)

* fix(libSceJobManager): refuse shutdown with compute work in flight and check the handler wait

(cherry picked from commit 394ac01)

* test(agc): create fixture command pools with the reset flag

Seven test fixtures create their own command pool without
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, while the driver's own
pool sets it and Recorder begins a used command buffer again without an
explicit reset. The Khronos validation layer reports
VUID-vkBeginCommandBuffer-commandBuffer-00050 in depth_storage and
partially_resident_snapshot --pending-stores for this. The fixtures now
create the pool the way VulkanDevice does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 563cdd6)

* feat(libc): implement wcscspn, wcstoimax, wcstoumax, strtouq, rindex and flsl

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f68ab45)

* fix(agc): clear a queue's flip event latch once its own interrupts stop

A queue that delivered an end-of-pipe interrupt from its own release stopped
delivering the flip's event for the rest of the run, so a title that raises a
release interrupt once and then waits on the flip event never saw it again.

Count the flips a queue has taken since its last delivered release interrupt:
four consecutive flips keep the latch, a fifth clears it and raises the event,
so the queue recovers within five flips. Gran Turismo 7 (PPSA01317) pairs two
flips with one release interrupt per frame, which stays under the limit, and a
title that flips without further release interrupts recovers.

Extend agc_driver_eop_interrupts: a latched queue still delivers its own
releases, flips keep reaching the video output while their event is suppressed,
an event returns once the latch clears, and a release followed by two flips
raises none.

(cherry picked from commit 241429c)

* fix(libc): keep the host standard stream when a guest freopen of it fails

(cherry picked from commit 2d8d3b9)

* fix(libc): format %p the way the console's libc does

The console's libSceLibcInternal.sprx (measured on firmware 12.70 in
a null pointer, ignores #, + and the space flag, counts only the digits
towards the width, pads with spaces on the left (on the right for - and
with zeros for 0 when there is no precision), treats the precision as
the minimum number of digits, prints nothing for a null pointer under
%.0p, and accepts the l length. FormatPointer instead followed FreeBSD
and printed 0x..., which is what a title does not see on hardware.

FormatPointer now prints the digits alone, the narrow Windows formatter
accepts %lp, and Linux routes %p and %lp to the guest formatter instead
of glibc, which prints the FreeBSD form and (nil) for a null pointer.
The wide family still refuses %lp, because the console's swprintf was
not measured with it; it returns -1 as it does for any other conversion
the formatter rejects. The debt note records the pointer cases the
console was not measured with and what the Linux routing means for a
format that mixes %p with a refused conversion.

(cherry picked from commit 536fef3)

* feat(libc): implement fdim and fdimf

(cherry picked from commit c33cf11)

* feat(libc): implement erf, erfc, and erfcf

(cherry picked from commit 5509b6c)

* fix(libc): ilogb special cases match PS5 libc.prx (0 -> INT_MIN, inf/NaN -> INT_MAX)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 3b5c41b)

* feat(libc): implement fabsf and fabsl

(cherry picked from commit 831ecb0)

* fix(libc): use std::fabs overload for fabsl

(cherry picked from commit d5eeed0)

* test(agc): skip the emulated color compare clamp cases without MinLod

The offset cases compile `image_sample_cb_cl_o`, whose LOD clamp the emitter
reaches only through the device's `shaderResourceMinLod`; without it the
emitter refuses the shader (the gate #388 left in place). The test had no
capability guard, so on such a device every one of those cases failed at the
first of them, `point, wrap with negative offsets`, instead of skipping them
as `ImageSampleLodClamp` and `ImageSampleBiasA16` already do.

Found on llvmpipe (Mesa 25.0.7, Debian 13), which lacks the feature that
lavapipe reports from Mesa 25.2.8: `agc_driver_emulated_color_compare` and
its `_srgb_decode` variant both failed. The other cases still run; the note
names the cases that were skipped.

(cherry picked from commit 3855136)

* test(agc): accept any NaN where the f16 fma and fma_mix tests expect one

agc_driver_f16_fma_denormal_modes and
agc_driver_fma_mix_f16_denormal_modes compared NaN results (inf * 0)
bit for bit with the RX 6700 XT's, but the NaN bits of f16 arithmetic
and of v_fma_mix_f32 with f16 sources are left to the driver, so the
tests fail on NVIDIA (0x7fff for 0xfe00). Rows whose expected value is
a NaN now accept any NaN; every other row is still exact.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit e5b4b32)

* test(agc): check that 16-bit interpolation takes the barycentric emulation

Without barycentrics the test shader now recompiles with the emulation
active instead of throwing, and with them it does not take it. The
offline target declares Float64, which the exact interpolation sums use.

(cherry picked from commit f397af4)

* feat(agc): link calls through the AGC FS table

(cherry picked from commit 8f37cdd)

* perf(shader): judge first-lane and constant reads by what they read

MayEvaluate took ReadFirstLane and ReadConst as evaluable without looking at
their sources, so plans kept conditions behind per-lane values.

(cherry picked from commit 745cdb5)

* fix(shader): stop first-lane reads that reach themselves from recursing

MayEvaluate and the evaluator track the first-lane reads they are inside, so a read reached again through a loop-carried phi or a constant slot is not evaluable instead of recursing.

(cherry picked from commit e14a4e5)

* fix(main): unique directory for the large cache file test

Culprit: a03ccc6 (large cache files in chunks); failing run 39057904312.
The test used a fixed directory in the temp dir; the runners of one host
share it, so a concurrent run left a second entry behind.

(cherry picked from commit b54018e)

* fix(main): run the large cache file test alone with a longer hang limit

Windows run 39061030084: agc_shader_disk_cache_large_file wrote 2 GiB in 141 s next to 1399 parallel tests (limit 120 s), and conventions_function_bodies_shard_4 timed out at 30 s on the same loaded runner. The test is now RUN_SERIAL and its hang limit is 200 s (ctest timeout stays 240 s).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 28b3ce0)

* fix(shader): collect packed ancillary readers without recursion

lowerPackedAncillary follows the selects, phis and identities that
forward the packed ancillary word to its readers by recursing once per
link (collectReaders), with a linear scan of the visited list at each
step. A long forwarding chain overflowed the stack of the thread that
compiles the shader: PPSA01317 crashes about 30 s into boot in that
recursion on a guest job thread, with a 0xc00000fd stack overflow inside
malloc. The readers are now collected with an explicit stack and a
visited set, in the same order.

AI-assisted: yes
(cherry picked from commit dce7784)

* fix(shader): use 64-bit buffer atomics for device-memory LDS

Compute LDS beyond the device's shared-memory limit lives in a storage
buffer, and its 64-bit atomics took a lock in that buffer. AMD's Windows
driver compiles the lock's acquire and release to s_barrier, which inside
the divergent lock loop pairs with the program's own barriers, so the
device-memory case of agc_driver_lds_lock_budget read half its sum.

With shaderBufferInt64Atomics the device-memory LDS now uses a 64-bit view
of the buffer instead: 64-bit atomics are compare-exchange loops on it and
32-bit atomics stay native, so no lock is taken. Without the feature the
lock is kept. The per-workgroup slice is rounded up to an even dword count
so that the 64-bit view stays aligned.

(cherry picked from commit af0af83)

* fix(shader): fault on misaligned 64-bit device-memory LDS atomics

(cherry picked from commit cb28abe)

* fix(shader): bind an unnormalized S# normalized when it samples only null images

An S# that forces unnormalized coordinates was refused when it samples a
T# the shader declares 3D, 2D-array, cube or multisampled, since Vulkan
allows unnormalized coordinates only with a single-level 1D or 2D view.
A null T# reads zeros at any coordinate, and a T# whose channels all
select constants is emitted as those constants without a sample, so when
the S# samples no other T# its coordinates cannot change a result. Such
an S# is now marked in its binding (samplerCoordinatesIgnored) and the
driver creates its sampler without FORCE_UNNORMALIZED. The flag is part
of the binding content key and of the shader disk cache, whose format
version is raised to 38. An S# that also samples a real T# while a null
T# of such a shape shares it is still refused. PPSA01317 pairs an
unnormalized clamped bilinear S# with a null 3D T# and a 3D T# whose
channels all select 0 for image_sample_lz in a compute shader during
boot.

AI-assisted: yes
(cherry picked from commit a2e79fb)

* fix(shader): act on the containing dword for unaligned 32-bit buffer atomics

The RDNA2 ISA (8.1.7) ignores the two low address bits of dword or wider
non-formatted accesses in alignment mode DWORD. MXGP 26 (PPSA30839) sets
bits with buffer_atomic_or at odd byte offsets, which recorded an
unaligned BDA fault and dropped the atomic. A 32-bit buffer atomic now
acts on the dword that holds its address; 64-bit atomics off an 8-byte
boundary still fault.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7e4ce1a)

* test(agc): expect 32-bit buffer atomics off a DWORD boundary to act on their containing dword

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 96c692c)

* test(agc): cover native vertex aliases after index guards

(cherry picked from commit 8980d77)

* fix(agc): refuse native MSAA vertex buffer reads

(cherry picked from commit 15eb120)

* feat(agc): find the programs that store through the BDA table

A program writes guest memory through the BDA table only with a store or
an atomic through a PhysicalStorageBuffer pointer. SpirvStoresThroughAddresses
scans a module for such an instruction: it collects the PhysicalStorageBuffer
pointer types, every value of those types, and the stores, copies and
atomics whose pointer operand is one of them. A module it can't read to its
end counts as storing.

An address-based build now records whether its programs only read through
the table (ShaderResources::BdaReadOnly). Each module is scanned once; the
answer is kept by its content hash.

(cherry picked from commit 0300bb0)

* fix(libSceAgcDriver): accept unclamped normalized color target formats

(cherry picked from commit 9fb2820)

* fix(agc): count every physical pointer write form in the BDA store scan

The scan listed stores, memory copies and atomics, and took only typed
OpTypePointer declarations as physical pointer types. A module that
wrote guest memory through an untyped PhysicalStorageBuffer pointer
(SPV_KHR_untyped_pointers), through a GLSL.std.450 Modf or Frexp output
pointer, or with a cooperative matrix store would have been taken for
one that only reads through the BDA table, and its draws would have
continued a render pass they must end. The recompiler emits none of
these today, so no module's answer changes.

Untyped physical pointer types now count as physical pointer types, an
extended instruction with a physical pointer operand counts as a store,
and the cooperative matrix stores store through their pointer operand.
Pointer type declarations no longer add their storage class to the
pointer values.

The tests build modules for every write form the scan knows, the reads
it must not take for writes, and the three forms above.

(cherry picked from commit 4ff3b05)

* test(agc): order draws reading through the BDA table against buffer descriptor stores

A draw that only reads through the table now leaves its pass open to
the next draw, so its ordering against guest memory written through a
buffer descriptor rests on the pass hazard tracker's unknown-read rule
alone. Two cases cover it in agc_driver_address_passes: a draw that
reads through the table what the previous draw stored through a buffer
descriptor begins its own pass and sees the store, and a draw storing
through a buffer descriptor after one that read through the table
begins its own pass. Both fail when the tracker drops that rule.

(cherry picked from commit f063282)

* test(agc): check that a read through the BDA table sees a target as with its pass broken

Continuing a pass across a draw that only reads through the table is
right only if no pass end records a target write-back that the read
depends on. agc_driver_address_passes --targets renders a color whose
bytes spell 1.0f into the target, then reads the target's first dword
through the table into the same target, once with a tiled target and
once with a linear one.

The tiled read continues the rendering draw's pass and reads the bytes
from before it: tiled results reach guest memory only by explicit
write-backs, none of which falls between the two draws. The linear read
reads the rendered color: a linear target over a host import is stored
back right after its draw (the store ends the pass), and one without an
import is drawn synchronously. agc_driver_address_passes_targets_split
runs the same cases with APS5_NO_ADDRESS_READ_PASSES=1 and must read the
same bytes; a scratch build that writes the targets back whenever a
draw reading through the table breaks the pass fails it.

(cherry picked from commit 4796eac)

* feat(agc): implement 11_11_10 float color targets through 32-bit uint attachments

Color format 7 (COLOR_11_11_10, float) keeps its 10-bit component in the
low bits; with the reversed component order (COMP_SWAP 2) that is blue,
with red in the high 11 bits. Vulkan has no such layout, so the target was
refused at decode.

Render it the way 10_11_11 unorm targets are rendered: an R32_UINT
attachment, the reversed export mapping 0xc6, and a new
ColorExportPacking::Float11_11_10 that makes the pixel shader pack each
export with the format-43 image store conversion (toward zero, overflow to
the largest finite value, negatives to zero, NaN kept). Blending, partial
writes, 16-bit integer exports, DCC and the other component orders throw.

AI-assisted: yes
(cherry picked from commit 49b0e80)

* fix(agc): refuse rounding 11_11_10 float targets and resolves of packed color targets

An 11_11_10 float color target with CB_COLOR_INFO ROUND_MODE (bit 18) clear
asks the CB to round, but the export packing truncates toward zero, so such a
target now throws. A CB resolve whose source is a packed 10_11_11 unorm or
11_11_10 float target would resolve the raw R32_UINT words, which an integer
resolve cannot average, so it now throws at decode.

AI-assisted: yes
(cherry picked from commit a518a90)

* test(agc): refuse an additive blend into an 11_11_10 float target

The blend-refusal case used CB_BLEND_CONTROL 0x60010001, which is
ONE * source + ZERO * destination: a replace blend that writes the source
unchanged. A packed target can draw such a blend with blending disabled,
so the case now uses the additive blend 0x60010101, which a packed target
must still refuse.

AI-assisted: yes
(cherry picked from commit 45c78bb)

* refactor(shader): gather the emulated barycentric interface in one place

(cherry picked from commit f4b3f03)

* feat(shader): build the barycentric emulation stages from tessellation

On a device without geometry shaders, each triangle becomes a three-point
patch at tessellation level 1. The evaluation stage takes the corner
gl_TessCoord points at as its vertex and writes it as the weights, and
passes every vertex's parameters for raw reads. The vertex clip distances
are forwarded, so MoltenVK's stage interface matches the vertex output.

(cherry picked from commit 02a4bdf)

* feat(agc): emulate barycentrics through tessellation without geometry shaders

A triangle list without primitive restart whose pixel shader emulates
barycentrics draws through the generated tessellation stages when the
device has tessellation but no geometry shaders, as on MoltenVK. The
pipeline uses a three-point patch list for it, keeps it out of pipeline
libraries and validates the generated stages' modes and interface.

(cherry picked from commit 03f4f67)

* test(agc): cover the tessellation route of the barycentric emulation

(cherry picked from commit 48b7d3a)

* feat(agc): support thick 4 KiB standard XOR volumes

(cherry picked from commit b2d9880)

* test(agc): allow native depth fixture command buffer resets

(cherry picked from commit d6ad5b7)

* fix(shader): match barycentric tessellation clip interfaces

(cherry picked from commit 06379ce)

* fix(shader): preserve clip arrays for tessellation control points

(cherry picked from commit ba9aec5)

* fix(agc): draw geometry programs with the last provoking vertex

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 8f03282)

* fix(shader): keep clip distances in the blocks between the barycentric tessellation stages

boykopovar#7330 passes the vertex clip distances as separate ClipDistance arrays so
the control stage's input matches the vertex shader's output. SPIRV-Cross,
which MoltenVK uses, cannot translate a separate ClipDistance array in a
tessellation evaluation stage, so on MoltenVK every pipeline of the
barycentric tessellation route failed to compile. The control stage now
reads the separate array the vertex shader writes and passes it on inside
its per-vertex block, which the evaluation stage reads and writes.

(cherry picked from commit 6491e12)

* test(agc): check where the barycentric tessellation stages keep clip distances

(cherry picked from commit f982c1c)

* feat(libSceAgcDriver): support polygon primitive type 21

(cherry picked from commit f07df07)

* test(agc): honor the GPU selector in pooled buffer checks

(cherry picked from commit 3f1dcad)

* fix(agc): restrict lazy sampled view usage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 724b166)

* fix(agc): keep cached pipelines across lazy buffer pool creation

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 86b05a0)

* fix(agc): discard device buffers after memory binding fails

(cherry picked from commit 432fe5c)

* test(agc): count mock Vulkan objects atomically

(cherry picked from commit 5070322)

* fix(agc): release unpublished descriptor cache objects

(cherry picked from commit 256da96)

* perf(agc): replace the plane copies around a depth plane's UAV clear by a depth clear

A compute kernel that stores one colour to every texel of a storage image over a depth plane (a shadow atlas clear) copied
the whole plane depth image -> staging buffer -> mirror before the dispatch and mirror -> staging buffer -> depth image
after it, although the kernel reads nothing and writes every texel.

A dispatch of the 15-word 8x8x1 image clear kernel (direct, no partial last group) whose T# lies on a depth surface now
reads its colour from guest memory under the GPU mutex, the place the kernel reads it. When the dispatch covers the whole
plane, the plane is one layer of level 0, the colour is a value the plane's format holds exactly (0 or 1 for a 16-bit
unorm view, 0 or 0xffff for a 16-bit uint view, 0 or a normal float in (0, 1] for a 32-bit view) and nothing is waiting to
write the colour, the dispatch still writes the mirror but the copy-in is left out, and the copy-out becomes a clear of the
depth image to that value. The colour is read again when the copy-out is replaced; a changed value keeps the copy-out.
Every other dispatch, stencil planes and declined values keep both copies.

agc_driver_depth_clear_hle_tests runs the clear sequences with the clear taken and with the copies, and compares the depth
image texel by texel through the mirror, the guest memory and the depth tests.

(cherry picked from commit c05b256)

* fix(agc): retain import settle resources per device

(cherry picked from commit a3ed32e)

* perf(agc): let the resource cache bound come back down

Halve the grown bound (not below 1024 entries) once a window of the
bound's uses times a shrink window was served by at most a quarter of
it, and reset the bound on Clear(). The shrink window starts at 8 and
doubles (up to 1024) each time growth undoes the last shrink, so a set
of keys that comes back over a longer span stops the bound from
oscillating. The entries over the bound leave at the next insert, with
the other evictions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P
(cherry picked from commit bea4be8)

* test(agc): keep the resource cache growth test short

The test now sets a base bound of 32 entries and a first shrink window
of 2 (ResourceCache::SetBase, applied at the next Clear()) and turns
dispatch recipes off, so every dispatch goes through the cache: grow,
shrink, regrow, backoff and the Clear() reset take about 1.2k
dispatches instead of about 100k.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P
(cherry picked from commit f27ad68)

* fix(agc): harden host import construction and mutation waits

Guard incomplete imports and preserve mutation waiter errors and progress.

Follow-up to boykopovar#2575; refs boykopovar#2352.

(cherry picked from commit 3047980)

* fix(libSceAgcDriver): normalize reserved bit 30 in guest buffer descriptors

(cherry picked from commit c3c400a)

* fix(agc): account recorded storage images in keep budget

(cherry picked from commit 902b051)

* fix(agc): account for batch-retained evicted storage images

(cherry picked from commit 27497f0)

* fix(agc): create legal sampled cache placeholders

(cherry picked from commit 5394684)

* fix(agc): omit unavailable storage MSAA placeholders

(cherry picked from commit fd87226)

* revert(libc): drop the duplicate wcstoimax/wcstoumax/strtouq pick

f68ab45 duplicates definitions the fork already has (redefinition errors).

This reverts commit 17ab20050c90dc27998251019972ea3df1de2dac.

* fix(shader): add the clip distance helper header the tessellation barycentric stages include

Brings only SpirvClipDistances.hpp from upstream 2b9c131 (its rect-list
stage changes stay skipped), and adds samplerCoordinatesIgnored to the Metal
ConversionKey now that DescriptorBinding carries it (size assert 448 -> 472).

* fix(agc): build the Metal driver and libkernel select against the Batch 40 picks

- Split the chunked ShaderRegistry container members into ShaderRegistryChunks.cpp
  so the Metal replay build (which does not compile ShaderRegistry.cpp) links them.
- MetalDriver rebuilds the filtered registry instead of erasing through the const
  entries the chunked registry hands out.
- Socket.cpp: declare GuestEbadf, used by the Unknown-revents select path.

* fix(agc): link the chunked shader registry into the Rosetta Metal driver

* revert(shader): drop the unaligned 32-bit buffer atomic picks (upstream boykopovar#6094)

cde860417 stops the direct 32-bit buffer atomic path from referencing the
BDA fault buffer, so modules keep a BDA ABI version without page-table or
fault-buffer metadata and the Metal shader bridge refuses them
(anyps5_metal_rect_replay and anyps5_metal_guest_draw_replay fail). Reverted
with its test d35a29a92 until the bridge's BDA contract accounts for it.

This reverts commits d35a29a92 and cde860417.

* fix(agc): apply the Batch 40 review findings

- tests: the "%p|%0" count is 5 characters, not 4.
- Metal: suppress the flip end-of-pipe event on a queue that raises its own
  RELEASE_MEM interrupts, as the Vulkan driver does since 2aa6bfc; the
  interrupt callback now reports whether it reached the title.
- tests: drop the includes and the declaration the review found unused.

* fix(agc): reconcile the Batch 40 rebase with main

The rebase kept the pre-rebase side of four conflicting files and re-added
one test block main already had. Restore the main-side content the picks
had replaced (the ADX lowering include and matcher, the shader cache
format version, the Metal driver's pending-write APIs) and keep one copy
of the pthread frame-chain test target.

* test(agc): report the end-of-pipe delivery from the replay interrupt callbacks

The Metal driver's interrupt callback now answers whether the event
reached the title, so every Configure call site in the Metal replays
returns true as the game's callback does.

---------

Co-authored-by: milescoviello <kitslayer29@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: thuanquang <ghoulbulldog@gmail.com>
Co-authored-by: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com>
Co-authored-by: mattogepeto <mattogepeto@gmail.com>
Co-authored-by: Nicolas Falesy <112726756+nicolasfalesy@users.noreply.github.com>
Co-authored-by: T3cm0 <srodriguez7604@gmail.com>
Co-authored-by: ionut27001 <194206737+ionut27001@users.noreply.github.com>
Co-authored-by: Alexej Szpevák <szpevakalexej@gmail.com>
Co-authored-by: HQasper <52220032+HQasper@users.noreply.github.com>
Co-authored-by: blu <131330772+ycbuzi@users.noreply.github.com>
Co-authored-by: Karim Masmoudi <125830216+Kernel-Hunter@users.noreply.github.com>
Co-authored-by: Ayamullah Khan <ayamullahkhan04@gmail.com>
Co-authored-by: Adrià Franch <adriadam10@gmail.com>
Co-authored-by: akkadbakkad89 <best.dv1996@gmail.com>
Co-authored-by: Ondra97 <47064087+Ondra97@users.noreply.github.com>
Co-authored-by: Ori Shamir <orishamir04@gmail.com>
Co-authored-by: Lberonio <103309975+Lberonio@users.noreply.github.com>
Co-authored-by: lorkydey <57268822+Lorkydey@users.noreply.github.com>
Co-authored-by: fma-rc <188743521+fma-rc@users.noreply.github.com>
Co-authored-by: babyhuey <7751607+babyhuey@users.noreply.github.com>
Co-authored-by: Slick Daddy <129640104+slick-daddy@users.noreply.github.com>
Co-authored-by: BIGFERRY999 <bigferry24@gmail.com>
Co-authored-by: David Sera <david.sera@proton.me>
Co-authored-by: Yossi Yadgar <yadgar360@gmail.com>
Co-authored-by: ThatsLeo <leonardorocco.monaco@studio.unibo.it>
Co-authored-by: Gjon Gojcevic <gjonn@hotmail.com>
Co-authored-by: Crimson <144462348+CrimsonMac@users.noreply.github.com>
Co-authored-by: Nahid Delic <nahe.delic84@gmail.com>
Co-authored-by: Romans Pokrovskis <motivated.it@gmail.com>
Co-authored-by: Yunus Shaikh <75296019+EagleBearerYS@users.noreply.github.com>
Co-authored-by: David <David.Samir.WD@gmail.com>
Co-authored-by: MarcZX100 <MarcZX100@users.noreply.github.com>
Co-authored-by: Sam <jb006d6855@gmail.com>
Co-authored-by: Sam <85789460+Sam-mon7@users.noreply.github.com>
Co-authored-by: Dextroid17 <217507631+Dextroid17@users.noreply.github.com>
Co-authored-by: AlxcBTW <187763517+AlxcBTW@users.noreply.github.com>
Co-authored-by: JJenkx <josh_jenkins@hotmail.com>
Co-authored-by: thibaut <thibaut-leaux-angeli@live.fr>
Co-authored-by: Nikhil Panchal <37629345+nikhilpanchal1@users.noreply.github.com>
Co-authored-by: MarcZX100 <marczxrr@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants