Physical device selection took the first Vulkan device with graphics,
compute and presentation support, so on systems with an integrated GPU
(e.g. Ryzen 7000 desktop CPUs) the enumeration order decided whether the
integrated or the discrete GPU was used. Rank candidates by device type.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Bn7CrhG7pTPR6DT13tN6Me
Every draw copied its index and vertex ranges from guest memory into
fresh buffers and scanned the indices, although the draws of a frame read
the same meshes again: about 10 GiB of vertex copies per 10 s at
PPSA21564's title.
CopyDrawInput now keeps the copy in the recorder's draw snapshot cache
and binds it again while the write tracker proves the range unchanged
since the copy was taken, with the same rules as the storage-buffer
snapshots: UnchangedSince on the collect made right before the copy and
an unchanged allocation registry. As GuestMemory::Read does, the flush
hook runs first, so pending GPU results over the range are stored and
stamped before the check. Unwatched memory is copied every time. An index
copy keeps its highest index; the device limit is still checked for every
draw.
The cache key includes the snapshot's use (storage, vertex, 16-bit or
32-bit index), so each use keeps its own buffer with its own usage. A
vertex copy runs from the buffer's start to its highest fetched element,
so draws over one shared vertex pool ask for many sizes: a vertex
snapshot also serves a shorter read at the same address while that
read's bytes are unchanged, and a new one replaces the shorter ones.
Vertex and index snapshots have their own least recently used pool
(1 GiB, 16384 entries), so storage copies, most of which change every
frame, do not push them out before their next use.
Tests: agc_driver_recorder_tests copies index and vertex ranges of
write-watched memory and requires a reuse (with the stored highest index)
while the bytes are unchanged, a new copy with the current bytes after a
CPU store, a MarkWritten store and a registry mutation, separate copies
per use, no reuse without a recorder, prefix reuse for vertex reads only,
and storage snapshots not evicting vertex snapshots.
A recorded draw binds a CPU copy of each read-only guest buffer it reads
in place, made for every draw. PPSA21564's title-stage draws bind the
same large light-list buffers written by compute passes (up to 16 MiB
each) hundreds of times per frame, so about 4 ms of every draw went into
copying about 30 MiB into freshly allocated host buffers.
The recorder now keeps the snapshots, least recently used first, under a
256 MiB budget and 1024 entries. A later draw of the same range binds the
kept, immutable copy while GuestMemory::UnchangedSince holds for the
write generation collected right before the copy and the guest allocation
registry generation is unchanged. A CPU store (stamped by a collect), a
driver or GPU store (MarkWritten) or a registry mutation makes the next
draw copy again. Unwatched memory has no generation and is copied for
every draw, as before.
A recency list (each entry holding its own position) gives the eviction
victim and moves a used entry to the back in O(1).
Tests: agc_driver_recorder_tests binds a write-watched, host-imported
range twice and requires the same snapshot, then a new copy with the new
bytes after a CPU store, a MarkWritten store and a registry mutation; and
checks the least recently used eviction order at the entry cap, the byte
budget and a CPU store dropping a kept snapshot.
A conditional inside a loop with one target in the loop body and the
other outside it was taken for a break and emitted without a merge. When
the outside target is not the loop's merge but a tail of blocks that
reaches it, SPIR-V has no construct for that branch: spirv-val rejects
the module ("Selection must be structured") and the driver is free to
compile it into anything.
Such a branch now merges at its in-body target when the tail is private
to it (every tail block is dominated by the tail's first block, stays in
the loop construct and leaves only for the loop's merge), so the tail is
an "if (c) { tail; break; }" the loop already allows. Merges are given
to these after every other construct's, and one already taken leaves the
branch as before.
All 13 PPSA21564 programs whose V#s are read from GPU memory (the
deferred lighting and ray-traced passes) had two such branches in their
traversal loops. With this change none of the 74 dumped title programs
has one, and the 209 modules of a title-to-cinematic run validate apart
from three unrelated image-offset and entry-point errors.
Tests: agc_structurizer_tests structures a loop that exits through one
tail block and a loop with two exit tails, and requires every merge-less
conditional to target its loop's merge or continue.
An MRT slot below the highest written one that the draw does not write is a VK_ATTACHMENT_UNUSED
reference with a disabled blend; color targets keep their slot for export mappings, pipeline and
pass keys. DB_DEPTH_VIEW SLICE_START moves the depth and stencil planes by whole slices, so each
slice of a depth array is its own depth surface.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Sign-in, web browser and player dialogs finish cancelled; the message dialog finishes at once;
NpAuth, entitlement consumption, reachability, account age and the NpUtility bandwidth test
report signed out; sceKernelMtypeprotect changes the protection (the memory type has no host
equivalent); the APR ForEach resolvers resolve every path and report each failure in its own
result entry. vswprintf's stub is dropped in favor of the existing wide-formatter
implementation, which already covers it on every platform.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Co-Authored-By: Claude Sonnet 5 <[email protected]>
The VOP1, VOP2 and VOPC SDWA and DPP decoders read the modifier word at
wordIndex + 1 before SetRdnaRawWords checked that it exists, so a code
span ending at such an instruction was read one word past its end.
AddressSanitizer reports a heap-buffer-overflow in decodeVop2Sdwa on a
synthetic program. The six decoders now throw "truncated <encoding>
instruction" first, as DecodeRdnaVop3 and DecodeRdnaVop3p already do.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01R5WJS4ZdQynAJesCZKH5hb
VOP3 compares have a scalar destination, so checkNativeVop3Modifiers sent
them through the carry/scalar-destination branch, which rejects neg. abs
already passed and both are applied to the sources by the VOPC path, so
v_cmp_lt_f32_e64 vcc_lo, -v1, v2 threw "VOP3 source modifiers are not
implemented". Float compares now accept neg there; integer ones still don't.
Co-Authored-By: Claude <[email protected]>
Fifty VOPC opcodes threw in the decoder: cmpx f/o/u/tru for f32, cmpx f/t
for i32/u32, the 64-bit integer compares except the seven already present,
and the f16 f/o/u/nge/nlg/ngt/nle/nlt/tru compares and their cmpx forms.
64-bit gt/le/ge are built from SLessThan64/ULessThan64 by swapping and
negating, as the IR has no other 64-bit orderings. emitFloatOrderedCompare
gains the f16 and cmpx forms.
Co-Authored-By: Claude <[email protected]>
These VOP3 opcodes threw in the decoder. v_add_nc_i32 and v_sub_nc_i32 are
what compilers emit for signed saturating add/sub, so their clamp saturates
to the int32 range. The 16-bit mads honour op_sel on their 16-bit sources;
the u32_u16/i32_i16 forms take a 32-bit addend and write all 32 bits, so
they do not get the 16-bit destination select.
Co-Authored-By: Claude <[email protected]>
s_icache_inv, s_incperflevel, s_decperflevel, s_ttracedata_imm, s_wait_idle,
s_gl1_inv, s_dcache_inv, s_dcache_wb, buffer_gl0_inv and buffer_gl1_inv threw
in the decoder. SPIR-V has no cache control, so they translate like s_clause
and s_waitcnt, which already emit nothing. The SMEM and MUBUF forms carry no
operands, so no pass sees a phantom base or destination register.
Co-Authored-By: Claude <[email protected]>
The VOP2/VOP1 forms already translate; the VOP3 encodings were rejected by the
decoder. VOP3 f16 add/sub/subrev/mul/min/max now take abs and neg, which
readF16AsF32 already applies. Clamp and omod stay rejected for them because
writeF16 does not apply result modifiers.
Co-Authored-By: Claude <[email protected]>
MixPrecision and SdwaDestination dispatch hand-assembled RDNA through the
recompiler and compare every lane with a CPU model, but were never in ctest.
Move them to tests/execution/, which is globbed into one test per file with
the same target names, so new execution tests add a file and no CMake lines.
They exit 77 (skipped) when the host has no Vulkan device; the Linux runner
installs lavapipe and sets ANYPS5_REQUIRE_VULKAN=1 so they cannot skip there.
Co-Authored-By: Claude <[email protected]>
Explicit v_interp_p1/p2 interpolation (fragment-shader barycentrics) reads every interpolated
parameter per vertex, and the rect-list shaders refused every per-vertex parameter. Their
tessellation stage emits each corner's parameter value, which is what a per-vertex read of the
generated triangles needs. Only custom parameters (custom interpolation or passthrough), whose
fourth vertex has no value of its own, stay refused; FragmentParameter carries that flag.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A sampled read through a surface's keys after a uniform key fill with a clear code reads the
fill's clear value, not the image's pending results (cachedTexture's clear-through-keys). The
stage-A fast path returned the record's view of the pending image without that check, so a
read between the fill and the image's next refresh saw the previous results.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
An image's filledKeys stands for "the pending results predate the title's
key fill": sampled reads through those keys bind the clear value, and
(with APS5_KEYS_FILL_WRITEBACK_KEEP=1) a write-back leaves the keys at the
fill's code. A refresh that clears the image under the new keys ends that
hold (upload resets it), but the immediate clear of APS5_KEYS_FILL_CLEAR=1
did not. Results drawn after such a fill were then treated as dead: reads
through the keys saw the clear and a write-back kept the keys clear over
them. The immediate clear now ends the hold as the refresh does.
The linker stores a dynamic string that is the suffix of another once
(for example "cos" inside "acos"). The patcher wrote every string
separately, so a library whose renamed exports do not shrink the table
enough failed with "rebuilt .dynstr exceeds original section size".
Strings are now sorted by their reversed text and a string that ends
another one points into it.
Co-Authored-By: Claude <[email protected]>
scePadOpen now records that the pad is open and scePadClose clears it;
scePadGetHandle returns the handle for a port scePadOpen accepts while
the pad is open, and DEVICE_NO_HANDLE otherwise. No embedded microphone
is emulated, so the option that weakens trigger vibration while it is
in use is accepted with no effect.
Co-Authored-By: Claude <[email protected]>
Ten libSceVideoOut and two libScePad error constants had values that
belong to other errors of the same library (for example INVALID_HANDLE
was 0x80290003 and RESOURCE_BUSY / SLOT_OCCUPIED were swapped). They now
match KytyPS5, shadPS4 and fpPS4, which agree on all of them.
Co-Authored-By: Claude <[email protected]>
The title is fully installed, so scePlayGoGetLanguageMask reports every
language. scePlayGoSetToDoList validates the handle, entries, chunk IDs
and loci; nothing is pending, so the list is not kept.
Co-Authored-By: Claude <[email protected]>
BAD_POINTER, BAD_HANDLE and BAD_SIZE were 0x80B20004, 0x80B20005 and
0x80B2000D, which are INVALID_ARGUMENT, NOT_INITIALIZED and BAD_SPEED.
They are 0x80B2000A, 0x80B20009 and 0x80B2000B.
Co-Authored-By: Claude <[email protected]>
sceFiberStartContextSizeCheck and sceFiberStopContextSizeCheck toggle a
mode in which new fibers get their context filled with a pattern, so
sceFiberGetInfo can report the unused stack margin.
sceFiberGetThreadFramePointerAddress returns the frame pointer of the
thread that called sceFiberRun.
Co-Authored-By: Claude <[email protected]>
Keyboard input follows libSceMouse: the video out event loop forwards SDL
key and focus events, whose scancodes are USB HID usages, and the library
keeps the pressed keys, modifiers and lock LEDs in a 16-entry history for
sceKeyboardRead and a snapshot for sceKeyboardReadState.
sceKeyboardGetKey2Char translates the 101-key arrangement; the 106-key
one throws and is listed as technical debt.
KeyboardData holds 16 key codes (96 bytes), not 6.
Co-Authored-By: Claude <[email protected]>
A file read into guest memory is a host write: before the I/O it pins the shared pages it
covers, which reopens any page armed for write tracking and keeps write tracking from arming
it again until the read ends, when the pages count as written. A destination the guest cannot
write fails with EFAULT before any I/O; read and pread throw as for any failed read, AIO
reports SCE_KERNEL_ERROR_EFAULT, and APR throws. APR also throws when a read ends before the
requested size.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The arena is always [0x200000000, 0x7200000000). Every free, allocation-granularity-aligned
piece of the range is reserved as a placeholder; what lies between the pieces (thread stacks
and other reservations Windows made before libc starts, unaligned slivers) and the
system-reserved range are holes, used ranges a release never frees. A failed query or
reservation of the range throws.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
sceCesRefersUcsProfileCp1252 returns the CP1252 profile, and
sceCesSbcToUtf8 / sceCesUtf8ToSbc convert one character in each
direction. The five bytes CP1252 leaves undefined map to the matching
C1 controls, so every byte round-trips. Malformed UTF-8 and code points
outside CP1252 return the library's error codes.
Co-Authored-By: Claude <[email protected]>
sceSslGetCaCerts reports that no CA certificates are available and
sceSslFreeCaCerts clears the structure, in line with the library's
no-network behaviour. sceHttp2AbortRequest succeeds like
sceHttpAbortRequest. sceSslClose and sceSslGetSerialNumber keep throwing
and are listed as unknown signatures.
Co-Authored-By: Claude <[email protected]>
sceHttpUriParse, sceHttpUriBuild and sceHttpUriEscape work on strings
only and are implemented. sceHttpCreateRequestWithURL, sceHttpReadData,
sceHttpSetChunkedTransferEnabled and sceHttpCreateEpoll follow the
existing no-network behaviour.
Co-Authored-By: Claude <[email protected]>