PPSA21564's final composite renders into 4K 10-bit scanout buffers
(COLOR_2_10_10_10 with the alternate swap, A2R10G10B10). No guest texture
format matched that color format, so the target stayed non-resident:
every frame's composite draw ran synchronously (about 11 ms, 143 per 10 s
at the title) and the flip read the frame back through guest memory.
The blue-low 10-bit color target now keeps its bits in the storage image
of its surface's red-low 2-10-10-10 format, as B8G8R8A8 targets map to
R8G8B8A8.
Presenting it: DisplayTexelFormat names a display buffer's texel order as
the guest-memory path reads it (R8G8B8A8 base: red low, else blue low;
the 10-bit variant: A2B10G10R10 or A2R10G10B10). ResidentPresentPath
blits a resident image only when it holds the display's 8-bit texel
format (an exact copy). A 10-bit image (whose blit would round where the
guest path drops the low two bits), the other 8-bit order or an sRGB type
is converted instead: GpuColorTransfer::DetileImage copies the image's
texels raw and runs the guest path's own conversion shader on them, so
both paths present the same pixels.
The image's write-back is unchanged: its results stay pending like any
resident target's and are stored when a reader of the memory flushes
them.
Tests: agc_driver_recorder_tests checks the format decision for both
8-bit and both 10-bit orders, and converts an image in each order,
comparing it with the CPU decode and the GPU detile of the same words in
guest memory.
ColorTargetLayout built its SW_64KB_R_X offset tables in every
constructor, and every draw constructs a layout per color target while
decoding and again while setting the target up. The tables depend only on
the element size; they are now built once per size under std::call_once
and the layout points into them.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Replace the decoder-less AvPlayer with a player that demuxes MP4 sources
with FFmpeg (opened by path or through the title's file replacement),
decodes H.264 and AAC on their own threads, and hands out NV12 frames and
interleaved 16-bit PCM in buffers from the title's allocators, presented
on a media clock that audio drives when it is enabled.
Both libSceAvPlayer and libSceAvPlayer.native are built from the same
sources and link the shared ffmpeg target. The struct size checks stay,
next to the types in SceTypes.hpp. ChangeStream and GetStreamInfoEx are
implemented; Init without allocators, StartEx, SetAvailableBandwidth,
SetLogCallback and negative trick speeds succeed as before and are listed
in TechnicalDebt. HLS sources throw.
guest_avplayer plays an embedded 6 KB MP4 (H.264 plus two AAC tracks).
Every guest memory read checks the pending storage images for results it
must store first (FlushPending, overlappingPending and the overlap test of
shader resources), each by a scan of the whole registry, and each scan read
the thread-local `refreshing` once per image (an emutls call with MinGW).
The registry now keeps a sorted, merged index of the ranges its images
cover, rebuilt on the first query after a change: a query that overlaps
none of them returns without a scan. The scans read `refreshing` once.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
sceSaveDataDelete appended the title's directory name to the save root
without any check and passed the result to std::filesystem::remove_all.
"../victim" removed a directory beside the save root, ".." the directory
holding the save root, and an empty name or "." every save. The name
now has to be terminated within its field, non-empty, not "." or "..",
and free of "/", "\" and ":", as sceSaveDataMount3 already requires;
otherwise the call returns SAVE_DATA_ERROR_PARAMETER and deletes nothing.
The native library gains its own CMakeLists.txt, with the same library
definition as the generic one, to register guest_savedata_delete.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01R5WJS4ZdQynAJesCZKH5hb
Take scanned every retained slot of its tier for one with the same size,
usage and memory properties, so the slot bound stayed at 512. With 512
slots GTA V's in-game builds overflowed the small tier: each draw evicted
and recreated buffers, and the submission thread spent most of its time
in the driver's allocation and free calls. The slots are now kept in
lists per key, oldest first: Take pops the newest of its key, eviction
takes the oldest front. The bound is 4096 slots per tier, held to a sixth
of maxMemoryAllocationCount.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
MIMG opcode 0x33 samples with explicit derivatives, an LOD clamp and a
texel offset. The three address flags already exist; only the opcode was
missing from the decoder table.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The LOD clamp of an image sample looked for the MinLod capability in the
emitter's own requirement list, which nothing fills, so every sample with
a clamp failed to compile on every device. The check now reads the
capabilities of the target, and a sample that takes the MinLod operand
declares the capability in its module.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Decode and translate v_dot2_i32_i16, v_dot2_u32_u16, v_dot4_i32_i8,
v_dot4_u32_u8, v_dot8_i32_i4, v_dot8_u32_u4 (VOP3P 0x14-0x19) and
v_dot4c_i32_i8 (VOP2 0x0d).
Clamp, neg and the op_sel combinations whose behaviour is not
established throw.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Decode VOP3 0x14d and translate it as the per-byte average of S0 and S1,
rounded up where bit 0 of the matching S2 byte is set and truncated
otherwise.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Decode VOP3 0x15a, 0x15b, 0x15c and 0x171 and translate them as the
unsigned sum of absolute differences over the byte or word fields of
the first two sources, accumulated into the third. v_sad_hi_u8 shifts
the sum into the high half first, v_msad_u8 skips the fields whose
reference byte in the second source is zero.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
open, _open, stat, unlink and rmdir forwarded the result of their sceKernel
twin, so a failure returned 0x8002xxxx instead of -1 and errno was never set
to the guest error. They now return -1 with errno set to the code carried by
the sce error, and report a null pointer as EFAULT and an empty path as
ENOENT.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
s_wakeup (SOPP 0x03) threw "unsupported SOPP opcode 3" in the decoder. It
pings the other waves of the thread group so they leave s_sleep early.
s_sleep already translates to nothing, so no wave is ever asleep and the
ping has nothing to wake: s_wakeup translates like s_sleep. It carries no
operand.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
SMEM opcode 0x2a threw in the decoder. It carries only a scalar destination
and returns the index of the current wave in its workgroup. A compute
workgroup fills its waves in local invocation index order, the mapping the
compute prologue already uses for the tg_size wave id, so the result is the
local invocation index divided by the wave size. Other stages throw.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
scePthreadAttrGet wrote priority 700 and left the affinity of the
destination untouched, whatever the thread was created with or set to
afterwards. It now reports the values the thread holds, as
pthread_attr_get_np already does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
When the windowless device is replaced at the first present, its unit
shadows were published but stayed registered. Lookups take the shadow
whose import starts nearest below an address, so such a stale entry
could hide the live import's shadow. The write-back results it held then
looked absent, and an upload read the import's stale bytes instead.
The replaced device's shadows are now dropped once it is idle.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The VOP3 form of v_nop now decodes without operands, and no test looked at
it. agc_vop1_control_decode decodes the three instructions in VOP1 and VOP3
form and checks the opcode, the length, the null destination and the absence
of sources. agc_driver_vop1_control also runs v_nop in both forms.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Decode MIMG image_atomic_cmpswap, sub, smin, smax, inc and dec and
recompile them on the image texel pointer. sub, smin and smax map to
the SPIR-V atomic of the same name. cmpswap takes the new value from
VDATA[0] and the comparand from VDATA[1] (dmask 0x3) and returns the
previous value in VDATA[0]. inc and dec use the compare-exchange loop
of ds_inc/ds_dec, whose replacement helpers are now shared.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
v_pipeflush (VOP1 0x1b) and v_clrexcp (VOP1 0x41) threw in the decoder, as did
their VOP3 forms. SPIR-V has no ALU pipeline to flush, and the recompiler keeps
no exception flags for v_clrexcp to clear, so both translate like v_nop, which
emits nothing. Neither has operands: they decode with a null destination and no
sources. The VOP3 form of v_nop decoded a destination and a source it does not
have and now takes the same path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Decode v_cmp_*_f64, v_cmpx_*_f64 and v_cmp(x)_class_f64 in their VOPC and VOP3 encodings and translate them as exact IEEE-754 compares on the 64-bit pattern with the existing 64-bit integer IR, so no Float64 capability is needed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
pthread_barrier_init threw for any non-null attributes object, and no
function could create one. Add pthread_barrierattr_init, _destroy,
_getpshared and _setpshared after FreeBSD libthr, and let
pthread_barrier_init take the object.
PTHREAD_PROCESS_SHARED still throws in pthread_barrierattr_setpshared:
FreeBSD 9.0 rejects it and 11.0 accepts it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>