Commit Graph
14017 Commits
Author SHA1 Message Date
Dean GalvinandClaude Opus 5.5 cf721a2794 feat(agc): persistent shader disk cache and Vulkan pipeline cache
Every run recompiled every shader: the recompiler's variant cache and
the device's VkPipelineCache only lived in memory, and the first frames
of a title spent hundreds of milliseconds per large program in the
front end, the emitter and the optimizer.

Compiled variants (the SPIR-V plus the compiled shader info and binding
allocation that per-snapshot binding population needs) are now stored
on disk, one file per key, written atomically by a background thread,
and a later run loads them instead of compiling. The key holds every
input the in-memory variant cache matches on: the in-memory recompile
key (stage, code hash, stage registers, user data count, target), every
code word, the host subgroup size, the binding layout, the resource
specialization, and the values of the APS5_* switches the recompiler
sources read (found at build time; switches that only report are left
out). Entries live under a directory per source version, a build-time
hash of the recompiler and AGC driver sources and the build
configuration, and carry their key and checksums: an entry whose
format, version, key, size or checksum does not match, or whose payload
does not decode exactly, is ignored and recompiled. Directories no
build used for two weeks and stale temporary files are removed. Requests
under the debug probe bypass the disk cache.

The device's VkPipelineCache is loaded at creation when the file's
checksum and Vulkan header match the device (vendor, device and
pipelineCacheUUID; the driver version is in the file name), saved every
10 s while it grew, and saved at teardown.

Location: a shader_cache folder beside the running executable (not the
working directory), so the relinked game stays a self-contained folder;
ANYPS5_SHADER_CACHE_DIR overrides it. ANYPS5_NO_SHADER_CACHE=1 turns both caches off; the AGC
tests run with it set so they never write to the user's cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01ButHENCbXXt8h4EZuWUabJ
2026-09-30 12:01:25 +03:00
STL1te 5693c77efb fix(shader): BDA lookup functions are not inlined
NVIDIA's compiler inlines every call of the BDA lookup functions, each with its binary-search
loop, and then spends minutes compiling shaders with a few hundred guest memory accesses. Marked
DontInline, they compile in seconds. Pipeline creation without the driver cache, NVIDIA RTX PRO
6000 Blackwell:

| Shader       | SPIR-V words | Inlined  | DontInline |
|--------------|--------------|----------|------------|
| 0x25b1e8600  | 280168       | 104.4 s  | 4.8 s      |
| 0x7ff6df189d00 | 95698      | 86.3 s   | 1.5 s      |
| 0x25b27ce00  | 39959        | 6.0 s    | 0.45 s     |
| 0x25b27e400  | 40640        | 6.6 s    | 0.48 s     |
| 0x7ff6df2a8000 | 69179      | 5.7 s    | 1.1 s      |
| 0x7ff6df253400 | 90491      | 4.1 s    | 0.74 s     |
| 0x7ff6df256700 | 92459      | 4.0 s    | 0.69 s     |

The menu renders 3311 frames in 120 s against 3325 without the change; the story mode load takes
the same time and GPU dispatch time in both builds.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
(cherry picked from commit 7615af4d4dd31581f52aa5d88118c73435fd6ae9)
2026-09-30 12:00:40 +03:00
carman24r b9955d8e53 Update Registers.cpp
fix conflict I think
2026-09-30 04:55:24 -04:00
MichonandClaude Opus 5.5 962fc34e5a fix(libkernel): unmap ranges that span several mappings
As on FreeBSD, a munmap range covering more than one registered allocation (or gaps between them)
is unmapped allocation by allocation instead of throwing "unmap crosses allocation boundaries".
GTA V unmaps such ranges in its Story Mode prologue.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-30 10:54:47 +02:00
boykopovar 0f6205aa36 Merge pull request #153 from STL1te/feat/boot2
feat: boot PPSA04263 with its own sce_module, into the story mode load (supersedes #144)
2026-09-30 11:33:29 +03:00
STL1teandClaude Fable 5.1 3a76b18646 fix(libc): the process starts in the root directory again
Starting in app0 when the folder exists made the working directory, and with it the tests, depend
on the layout next to the executable. The title opens its files by absolute /app0 paths, so the
fallback was never used: the route to story mode runs the same without it.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-30 11:15:29 +03:00
STL1teandClaude Fable 5.1 9cfdbff17c refactor(libc, kernel): drop the libc exports the title takes from its own libc.prx
The C11 thread layer (_Mtx_*, _Cnd_*, _Thrd_*, _Xtime_get_ticks, std::_Pad) and the libc additions
_FNan, _FDtest, _FExp, __udivti3, _Throw_Cpp_error, generic_category, localeconv, wcsncpy_s and the
aligned operator new came from the --skip-sce-module route, where our libc stood in for the title's.
With the title's sce_module loaded, its libc.prx exports every one of these NIDs and the relinker
lists the guest modules ahead of the host prx, so the imports never reach our copies. Checked both
by the NID tables (the executable imports them from libc, the guest libc.prx exports them) and by
making the four std::_Pad functions throw: the title boots, reaches the menu and loads story mode
without calling them.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-30 10:56:03 +03:00
boykopovar 5e08eb4cce perf(memory): skip redundant page scans in shared write tracking 2026-09-30 09:59:51 +03:00
boykopovar f7c53a4481 fix(memory): restore write-watch tracking for shared mappings 2026-09-30 09:59:51 +03:00
boykopovar abb9c85266 fix(kernel): share direct memory mappings and track physical offsets 2026-09-30 09:59:51 +03:00
STL1teandClaude Opus 5.5 ee942805cd test(libc): guest_formatting runs only on Windows
It checks the Windows formatter (the guest va_list must stay untouched).
On Linux the guest variadic path hands the call to glibc, which advances
the va_list, and snprintf_nid_postfix with arguments on the stack
crashes; upstream never registered this test.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 09:34:57 +03:00
STL1teandClaude Opus 5.5 5cf8ffa090 ci(windows): the WinLibs CMake gets a CA bundle for the FFmpeg download
WinLibs puts its own cmake.exe (4.3, OpenSSL) first in PATH and ships no
CA certificates, so file(DOWNLOAD) of the FFmpeg package failed with
curl error 60. SSL_CERT_FILE points it at the bundle of Git for Windows.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 09:34:56 +03:00
STL1teandClaude Opus 5.5 d252a3c72c fix(build): FFmpeg links into the Linux prx and without Windows libraries there
The MinGW FFmpeg package needs bcrypt, ole32 and user32; linking them
unconditionally broke libSceAjm.native.prx on Linux. The Linux package's
x86 assembly references its constants PC32, which ld refuses in a shared
object unless the archives' symbols stay local: --exclude-libs,ALL keeps
them out of the prx's dynamic symbols.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 09:34:56 +03:00
STL1teandClaude Fable 5.1 97443fd108 fix(savedata): the native module keeps save directory params and mounts at /savedata<N>
sceSaveDataSetParam dropped the params, and sceSaveDataGetParam and
sceSaveDataDirNameSearch returned zeros, so a title that checks the params of
the save it found took its profile for a new one and rewrote it with defaults
on every boot. The params now live in a <directory>.param sidecar next to the
save directory, written by SetParam (mtime refreshed) and returned by GetParam
and by the directory search, with the directory's write time when no param
was stored yet.

The mount point is /savedata<N>, as on the console: a title read its profile
through /host//_sm/0/Profile when the mount point had another shape, and that
path does not exist.

The module logs the first three calls of each entry point, like the core
module; APS5_SAVEDATA_TRACE lifts the limit.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-30 08:23:02 +03:00
asimplestray c6d098d487 feat(kernel): implement Sleep, sigprocmask and pthread finalize 2026-09-29 23:40:48 +00:00
MartinCajiao 671b8c6291 feat(relinker): support Intel trampolines in guest modules 2026-09-29 18:24:00 -05:00
asimplestray 064006b617 feat(kernel): implement pthread semaphore lifecycle 2026-09-29 22:13:07 +00:00
Dean Galvin 5a257bee52 feat(recompiler): conditional debug branches translate as never taken
S_CBRANCH_CDBGSYS, S_CBRANCH_CDBGUSER, S_CBRANCH_CDBGSYS_OR_USER and S_CBRANCH_CDBGSYS_AND_USER
(SOPP 0x17-0x1a) branch only while a debugger sets the conditional debug bits in the wave's
status, which never happens on a retail console, so they decode to one no-op opcode.
2026-09-30 10:56:31 +13:00
asimplestray 9f81bddf6d fix(libc): normalize thread-atexit module handle on Windows 2026-09-29 21:38:52 +00:00
MichonandClaude Opus 5.5 8cf2f65806 perf(agc): write each skip report in one call and cap its request dumps
mingw's fprintf writes an unbuffered stderr a character at a time: a title skipping many draws
spent seconds per frame printing 17 KB RecompileRequest dumps (GTA V ran at 0.1 fps). Reports are
built into one string and written once; dumps stop after 8 per reason (APS5_SKIP_REQUEST_DUMPS,
-1 for all).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 23:36:02 +02:00
MichonandClaude Opus 5.5 4ea1209d04 fix(recompiler): read f16 inline constants as half values and honour VOP3 f16 op_sel
A float inline constant (4.0, 2.0, -4.0...) read by an f16 operation is the half value in the low
16 bits (LLVM getInlineEncodingV216); it was the low half of the f32 bits, 0. op_sel[0..2] now
selects the high half of v_fma/min3/max3/med3_f16 sources. GTA V's CAS sharpen computed
rcp(1 + 4w) as rcp(0) and stored black.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-29 23:36:01 +02:00
asimplestray 40eeb4be40 fix(libc): use guest calling convention for atexit callbacks 2026-09-29 21:33:44 +00:00
asimplestray 85a0ec2e69 fix(libc): convert sysv va_list for fscanf on Windows 2026-09-29 21:32:30 +00:00
asimplestray 3bf115585c feat(libc): implement cxa lifecycle aliases and init_env 2026-09-29 21:29:13 +00:00
asimplestray 77194e4561 feat(libc): implement fputwc, fputws and fscanf 2026-09-29 21:25:07 +00:00
carman24r a2c639ff2d Update CMakeLists.txt
Missing a required Windows runtime dependency, causing a __gxx_personality_v0 linker error with windows build.
2026-09-29 16:43:10 -04:00
carman24r bf25ec499e Update CMakeLists.txt
test build target and CTest registration.
2026-09-29 16:23:15 -04:00
carman24r 883bb7ad13 Update CMakeLists.txt
Restore to original CMake
2026-09-29 16:22:12 -04:00
carman24r 0dcc9f0605 Update CMakeLists.txt
test build target and CTest registration.
2026-09-29 16:09:38 -04:00
carman24r 6032ba4af9 Create RegisterCommands.cpp
new regression tests.
2026-09-29 16:08:47 -04:00
carman24r 5afbeca23c Update Packet.cpp
validated count-replacement helper.
2026-09-29 16:06:41 -04:00
carman24r 94035e6367 Update Packet.hpp
declaration for SetIndirectCount.
2026-09-29 16:06:07 -04:00
carman24r 01f6311bd3 Update IndirectRegisters.cpp
three register-count setters.
2026-09-29 16:05:27 -04:00
carman24r 1f1e39dccf Update Registers.cpp
eight register-write and size-query implementations.
2026-09-29 16:04:50 -04:00
PacukaandClaude Opus 5.5 a2520aa071 fix(sysmodule): unload unknown module ids as no-ops
sceSysmoduleLoadModule and sceSysmoduleIsLoaded already accept ids the
project does not know and log them; unloading such an id threw, so a title
that loads and later unloads an unknown module aborted at the unload.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:32:15 +02:00
PacukaandClaude Opus 5.5 c618f53301 fix(userservice): report invalid arguments as 0x80960005
SCE_USER_SERVICE_ERROR_INVALID_ARGUMENT is 0x80960005; 0x80960002 is
NOT_INITIALIZED (the neighbouring NO_EVENT 0x80960007 and BUFFER_TOO_SHORT
0x8096000A already match the real sequence). The privacy getter returned the
raw value and now uses the constant.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:31:53 +02:00
STL1teandClaude Opus 5.5 03465e769c test(kernel): a second view of direct memory shares the page
Since direct memory mapped at two addresses is shared read-only instead of
refused, the guest memory test expected an exception that no longer comes.
It now maps the second view, reads the first view's bytes through it, and
writes through the first view once the second is gone, which faults unless
its protection came back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:28:49 +03:00
STL1teandClaude Opus 5.5 6b36c050fe style: drop the branch's code comments per CONVENTIONS.md
Only the lines this branch added lose their comments; the commits before
keep them, with the reasoning behind each change, for whoever digs into
that code later.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:25:29 +03:00
STL1teandClaude Opus 5.5 119560206d feat(agc-driver): heap ranges the host imports did not take are mirrored
An address-based build maps every readable registered range. A heap
range past APS5_HOST_IMPORT_MIB (or refused by the driver) was copied by
every such build: about 3200 ranges, 670 MiB and 500 ms per build on the
story-mode load, which then stalled. Such ranges are now served by
persistent host-cached mirrors, as the main image is, and the load
reaches its next failure.

- a heap mirror keeps no shadow; it keeps a write-tracking generation
  per 64 KiB block and a build copies only the blocks stamped since. The
  generation is stored by the copy, so a build that throws before it
  leaves no block marked current with stale bytes
- the build gathers its heap mirrors, sorts them and collects each run
  of adjacent ranges once (a collect costs about 4 us whatever its size;
  ~3000 mirrors took 16 ms per build, now about 3 ms)
- writable ranges are mirrored too: each build keeps the bytes its merged
  written ranges held at upload and write-back stores only what the GPU
  changed from them, so a CPU store made meanwhile stays; recorded work
  writing a range completes before a refresh reads it
- the mirrors are limited by APS5_HEAP_MIRROR_MIB (24 GiB by default); a
  mirror past it, or one whose buffer the driver refuses, stops the
  process. Heap mirrors are not given to descriptor sub-ranges, image
  mirror serials or reusable direct regions
- past APS5_ADDRESS_COPY_MAX_MIB (64 MiB by default) of committed bytes
  still copied, a build stops the process, naming the largest ranges and
  why each is copied (unreadable pages, an image mirror the driver
  refused, a range outside the write-watched arena, mirrors disabled)
- a direct memory page mapped at several addresses is read-only in every
  view while its range stays writable: copies, descriptor regions and
  mirrors take every readable page, write-back stores the writable ones
  and fails on a GPU change inside a read-only page instead of dropping it
- a store through the BDA table into a mirrored or copied range faults
  as before (only writable ranges imported in place are writable there);
  the fault now names the table range and that rule
- the BDA resource tests run with a storage range limit that holds the
  fault buffer and cover a mirror's fill, reuse, per-block refill, an
  interrupted refresh, a descriptor store beside a concurrent CPU store,
  and the copy limit

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:23 +03:00
STL1teandClaude Opus 5.5 c8913314bc test(agc-driver): the graphics and BDA device tests link and run to the end
Both suites stopped at their first stale expectation, so nothing after
it ran; the BDA device tests did not link.

- the BDA device tests define the one recorder entry Resources.cpp
  reaches (no recorder) and fail loudly on the others
- Buffer::Invalidate refuses memory that is not host mapped again (lost
  in 8a69fef); the device tests expect it for a device-local buffer
- APS5_PIN_WAIT_MS (60000 by default, unchanged) bounds the wait before
  a mutation of a leased range is refused, and the refusal names it; the
  graphics tests set 200 ms, as they expect about a dozen refusals
- the expectations follow the behaviour the code chose since: a buffer's
  four-DWORD V# is refused as a T#, a sampler exceeds the mock's zero
  sampler slots, a V# at unmapped memory binds its committed pages only,
  a rect-list fragment input without a vertex export reads as zero,
  detiled rows span the padded block width, mip ranges are checked in
  address order, block-compressed and 128/129/132 formats use render
  target tiling, an XOR swizzle is refused only over a base that is not
  64 KiB aligned, DCC and partial mip chain views decode, a cube reads as
  a 2D array, the sampler fixture's max LOD fits the 4.8 field, a BDA
  program with barriers is accepted, a tessellation control stage is
  refused, a corrupt request fails on its signature and a newer one on
  its version
- the DCC comment in the texture decoder no longer claims metadata
  compression is refused

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:23 +03:00
STL1teandClaude Opus 5.5 ee1d34230d feat(kernel): sceKernelDebugWriteCppExceptionInfo prints the uncaught C++ exception
A title libc from sce_module calls it from __cxa_throw/__cxa_rethrow when
no handler is found, right before std::terminate, with the thrown object,
0x400 (meaning unknown), the demangled type name and what() (or null).
The console keeps it for the core dump; there are no guest core dumps
here, so it is printed to stderr. It was the only import such a libc
needed beyond the existing libs.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:23 +03:00
STL1teandClaude Opus 5.5 8dbff97486 chore(agc-driver): agc_shader_replay --spv writes the recompiled SPIR-V
--spv saves <request>.spv in the working directory for spirv-val; the usage
line lists --asm and --mem too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:22 +03:00
STL1teandClaude Opus 5.5 cd158c6b02 fix(shader): a BDA fault in a continue target does not stop the invocation
SPIR-V wants the back-edge block to structurally post-dominate the continue
target, but the continue target is the guest latch, and a BDA access among its
instructions returned from the invocation on a fault: spirv-val rejected
such a loop ("The continue construct ... is not structurally post dominated by
the back-edge block"), which only builds without SPIRV-Tools accepted. While
the continue target is emitted, a BDA fault is recorded without stopping the
invocation, as in a program with wave LDS barriers.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:22 +03:00
STL1teandClaude Opus 5.5 9bb94163ea feat(shader): image_bvh_intersect_ray tests the ray against the BVH node
Every ray query returned a miss (a stub). The node test is now the GFX10
(RT IP 1.1) one AMD's GPURT software fallback defines (IntersectCommon.hlsl)
with the TriangleNode1_0 and BoxNode1_0 layouts, as SharpEmu ports it: the T#
(base, size, box grow and sort, triangle return mode) and the node pointer
are run-time values, triangle nodes return t over its denominator with the
barycentric numerators or the triangle id, float16/float32 box nodes the hit
children (clipped slabs, NaN misses, optionally sorted by entry time); an
inactive lane, a null BVH, an out-of-range node or another type returns four
invalid dwords.

The decoder addresses 11 dwords (8 with A16, the directions as halves), the
translation emits ImageBvhIntersectRay with the T#, MakeImageAddress and exec,
and the program takes the BDA path: one probe covers the node and direct loads
read it (a node the table does not map as one range records an Unmapped
fault); EmitBdaDwordReads can read every dword for the per-byte fallback.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:22 +03:00
STL1teandClaude Opus 5.5 74454ee9ca feat(shader): scalar buffer loads through a GPU-selected V#
A traversal builds the V# of a bottom-level structure from a pointer it loads
from an instance record, and reads it with s_buffer_load: no binding holds
such a V#, and TakeGpuDescriptor took vector raw dword loads and stores only,
so the resource analysis failed on the SGPR chain.

A ReadConstBuffer through a V# the SRT walker cannot compute is a
MemoryInfo::gpuDescriptor access too: the dword at the V#'s base plus the
dword-aligned offset, read through the BDA page table, zero past
STRIDE * NUM_RECORDS bytes (NUM_RECORDS for stride 0).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:21 +03:00
STL1teandClaude Opus 5.5 4622efb4bb chore(agc-driver): APS5_WATCH_MEMORY watches several ranges
APS5_WATCH_MEMORY takes <hex address>:<hex bytes> ranges separated by commas;
a snapshot holds them back to back, in order, and is kept when any changed.
Each range matches its own heap base above bit 36, and watching starts once
all are mapped.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:21 +03:00
STL1teandClaude Opus 5.5 d76ecfac06 fix(shader): DPP row and bank masks gate the instruction's VGPR write
The decoder puts an instruction's DPP fields on its source, and only a
destination carrying them has writeRawU32 keep the lanes the masks leave out
(and, without bound_ctrl, the lanes with no source lane). destinationOperand
copied them over, but no write used it, so every row was written: a wave64
scan adding the other row's total into the odd rows only (row_mask:0xa) added
it into every row, the prefix sums a key pass indexes primitives by came out
wrong, and the tree built from the keys held cycles its walkers looped on.

As in the Kyty recompiler (writes through DestinationOperand):
TranslateInstruction gives the destination its source's DPP fields once, and
a mac accumulator reads the destination without them (accumulatorOperand).
agc_dpp_write_tests checks a masked DPP add writes through DppUpdateU32.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:21 +03:00
STL1teandClaude Opus 5.5 f98b28d0d7 chore(agc-driver): debug aids for looping shaders
- The loop guard's report names the shader (its code address when its plan
  was built, now the translator's shader hash), and LoopGuardTripped() tells
  whether any dispatch reported one.
- APS5_WATCH_MEMORY=<hex address>:<hex bytes> (with APS5_SYNC_DISPATCH=1)
  keeps the last 64 contents of a range with the dispatch that left each, and
  writes them to watch_<n>_<program>.bin when the loop guard first trips; the
  address matches any heap base above bit 36.
- agc_shader_replay --mem prints the guest buffer V#s and which the shader
  writes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:21 +03:00
STL1teandClaude Opus 5.5 28bfe7747a fix(shader): accept SDWA source selectors on V_BFREV_B32 and V_FFBH_U32
Operand reads already extract SDWA source fields for every instruction, so
bit reversal and find-first-bit-high only need the decoder rule, as for
V_FFBL_B32. The rejection names the selector, the opcode and the pc.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:20 +03:00
STL1teandClaude Opus 5.5 28b09d4da2 feat(shader): GLC/DLC buffer loads and stores are coherent
A buffer load with GLC/DLC bypasses the caches another workgroup's stores
may be stale in; a radix sort polling a partition's published prefix with
one read stale flags on the host, handed overlapping ranges to two
partitions, and lost half its keys. A tree built from the duplicates held
cycles, and the shaders walking it looped until the loop guard stopped them.

As in the Kyty recompiler (GLC/DLC buffer cache policy support): the decoder
reads DLC on MUBUF/MTBUF, a buffer load or store with GLC or DLC (atomics use
GLC only to return data) is MemoryInfo::coherent, the guest storage buffers of
a program with one are decorated Coherent, and its accesses are Volatile.
Through the BDA table (GPU-selected V#s) the loads and stores are Volatile.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 21:22:20 +03:00