The console's DCC of a single-level 4-byte SW_64KB_R_X surface is
ceil(W/512) * ceil(H/512) * 4096 bytes, 49152 for 1920x1080, but the
driver stored and read one key per 256 surface bytes (34560).
PPSA26344 retiles its render DCC into the displayable copy it
registers (cs_dcc_retile_32bpp); a retile of the stale tail gives the
mixed keys PPSA12544 stops on.
DccKeyCount gives that extent. Color target and storage image key
stores cover it when the DCC is pipe-aligned (CB_COLOR_ATTRIB3 bit 30,
T# word 6 bit 19), whose CB keys reach its last meta block, and throw
where it is not writable; unaligned DCC keeps one key per 256 bytes,
as do texture and color target key reads. DisplayBufferKeys reads the
display DCC over the extent, presents only when every key agrees,
names the first differing key's pixels and checks a pending image
against those keys.
On GFX10.3 to GFX11.7, ACO lowers a subgroup-scope OpReadClockKHR to
s_getreg_b32 of SHADER_CYCLES, 20 bits zero-extended to 64, so s_memtime
wrapped every 2^20 cycles and went backwards. Detect those families from
the RADV device name and read the device clock there instead.
Fixes#985
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve ASCII case differences in guest paths and mount aliases while preferring exact names and rejecting ambiguous fallback matches. Preserve new filenames and host roots. Five filesystem tests pass; Skyrim now loads its INI and shader archives, initializes Vulkan and uploads intro textures instead of dereferencing null shader metadata. Written with AI assistance.
A draw whose PA_SC_CONSERVATIVE_RASTERIZATION_CNTL (context 0x313) was
not the default 0x6000 threw in DecodeState and the draw precheck.
0x6001 sets OVER_RAST_ENABLE and now decodes to Vulkan's overestimation
mode, chained to the pipeline's rasterization state and its key.
The device enables VK_EXT_conservative_rasterization only when its
primitiveOverestimationSize is at most 1/256 pixel and it rasterizes
degenerate triangles; elsewhere the pipeline throws. Overestimation is
accepted only when triangles reach the rasterizer: VGT_GS_OUT_PRIM_TYPE
behind a geometry shader, VGT_TF_PARAM's output topology behind
tessellation, VGT_PRIMITIVE_TYPE otherwise. Other 0x313 values and
centroid interpolation still throw.
Adds the agc_driver_conservative_rasterization GPU test and extends
agc_driver_graphics.
Skip CCP and Simplification for storage-only narrow types while keeping
the remaining passes and arithmetic-capable folding.
Add compiler and Vulkan readback regressions for default/full/none modes.
Tested: Linux/llvmpipe, full CTest with SPIRV-Tools on/off, ASan/UBSan
optimizer checks, and cold-cache Vulkan wave32/wave64 readback.
Fixesboykopovar/AnyPS5#1276
Invoke the title callback on planar mixed samples and silence. Preserve per-voice callback data, reset registration on setup, and release the render guard on callback failure. Include behavioral tests and source references. Written with AI assistance.
A V# whose base is not a multiple of 4 bytes from the start of its GPU
owner threw in GuestBufferMemory::Descriptor, because the shader added
the offset below the view only in whole dwords.
Descriptor now binds from the offset alignment below the view and
passes the whole remainder. Such a view's range covers the dwords
around it, and its descriptor region starts and ends on dword
boundaries, so a lone view copied to its own buffer binds the same way.
The buffer specialization records the V# base's low two bits. Raw
vector loads through such a V# join two dwords, glc dwordx2 loads
included, stores merge their bytes into both with compare-exchange
loops, and bounds checks include the second dword. s_buffer_load
ignores the low two bits, as SrtEvaluator does. Formatted accesses
whose element alignment (min(element bytes, 4)) the base breaks throw,
as they fault the wave on RDNA2, and so do atomics.
Preserve files across unmount, block new lookups until remount, and validate mount-dependent operations. Keep prior-run storage inaccessible after initialization.
Written with AI assistance.
Translate the FreeBSD pollfd layout and flags to WSAPoll on Windows and
poll on Linux. Closed socket descriptors report POLLNVAL, a timeout below
-1 fails with EINVAL and a null array with EFAULT. Non-socket
descriptors, unknown event bits, POLLPRI on Windows and an infinite wait
without sockets throw; these are recorded in TechnicalDebt.
DOOM (PPSA99666) imports poll for its upload server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GFX10 lowering of a wave64 reduction scans rows with DPP row_shr
1/2/4/8 and v_permlanex16_b32 under EXEC -1, then joins lanes 31 and 63
with a scalar op. The console fills lanes outside the entry EXEC with
the identity, but on the host they can be helpers that sit out subgroup
operations or invocations that were never launched: on 64 lanes (RADV)
the emulated chain reads 0 or stale values from them, and on 32 lanes
lane 63 throws since #997.
EmitReadLane now recognises the sequence on any host for UMin, UMax,
SMin, SMax, IAdd, AND and OR when the keys are the identity outside the
entry EXEC. On 64 lanes, lanes 31 and 63 become subgroup reductions of
the keys of invocations 0-31 and 32-63; on 32 lanes or fewer, lane 31
reduces all keys and lane 63 is the identity. It throws without
subgroup arithmetic or on a subgroup wider than the wave. VulkanDevice
reports GroupNonUniformArithmetic and ShaderValidation checks it.
#1398 and #1399 both rewrite the sceRtcParseDateTime entry that the s_sethalt entry followed. It now follows the libSceAudio3d entries, which no open pull request edits, so either can merge first without a conflict.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1372 adds its test after the vop1 decode tests and #1314 its debt entry after s_setkill, the places where this branch added its own. The wave control decode test now precedes the vop1 decode tests and the s_sethalt entry follows sceRtcParseDateTime, so either pull request can merge first without a conflict.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
s_sethalt with SIMM16[0] clear is a no-op; s_sethalt 1, s_sendmsghalt, a reached s_code_end, s_rfe_b64, the ds_gws_* instructions and ds_ordered_count throw with the reason at translation. Linear decoding stops at s_code_end like at s_endpgm, so code padded with s_code_end after a final backward branch decodes. Encodings from LLVM SOPInstructions.td and DSInstructions.td, checked against the gfx10 MC tests; semantics from the RDNA 2 ISA guide.
DecodeState and the draw precheck rejected DB_SHADER_CONTROL bit 16,
PRIMITIVE_ORDERED_PIXEL_SHADER (POPS), which runs the ordered section
of overlapping pixels' shaders in primitive order. The bit now reaches
the recompiler as a pixel stage field, and such a pixel shader declares
PixelInterlockOrderedEXT and runs its whole body between
OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT; an
invocation that a BDA read fault kills ends the interlock first. The
POPS collision SGPRs read 0, so a title skips its POPS wait and runs
its per-collision loop once. An image_load still reads a sampled
binding, which misses an earlier overlapping primitive's image_store in
the same draw. The device enables VK_EXT_fragment_shader_interlock when
it has fragmentShaderPixelInterlock; without it the recompile throws,
as does a nonzero POPS_OVERLAP_NUM_SAMPLES. The field is part of the
recompile cache key, the draw key and the serialized request (version 10).
SHA-1 and SHA-256 instructions with a [rip+disp32] operand, and
instructions with one that a short site absorbs, now move into the
stub. The stub records each displacement as a relocation relative to
the site, and the Linux, guest and Windows placements resolve it once
the stub address is known. Indirect branches through [rip+disp32]
still fail the relink.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use the shared atomic bounds check instead of wrapping at 64 KiB.
Expect zero returns and unchanged LDS for aligned out-of-range accesses,
and record the gfx1035 hardware measurements.
No request reaches the network, so sceHttpsSetSslVersion has no TLS
connection to configure, sceHttpSetRedirectCallback and
sceHttpSetCookieRecvCallback register callbacks that are never called,
and sceHttpsGetSslError reports error 0 with detail 0. A null errNum or
detail returns SCE_HTTP_ERROR_INVALID_VALUE like sceHttpGetLastErrno.
Signatures follow shadPS4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A condition wait on a recursive mutex locked more than once unlocked the
native mutex only once, so the thread that should signal blocked forever
on it. Unlock the remaining levels before the wait and lock them again
after it, as FreeBSD libthr does in _mutex_cv_unlock and _mutex_cv_lock.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
A thread that sceKernelRaiseException redirects resumes through
RtlRestoreContext with a CONTEXT that has no XSTATE, so the upper halves
of ymm0-ymm15 came back as the handler and the delivery path left them.
A handler or host routine that runs vzeroupper or any VEX instruction
(Zen 2 guest code, the CRT's memcpy) cleared the upper lanes of a thread
interrupted in the middle of AVX code. The entry stub now stores
ymm0-ymm15 next to the delivery when the CPU and the OS enable AVX, and
the exit loads them back right before RtlRestoreContext, which restores
only the low halves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
sceKernelGetCurrentCpu returned the raw host CPU index. Guest affinities
are bookkeeping only and sceKernelGetAvailableCpumask advertises CPUs 0
to 12, so on a host with more than 13 logical CPUs a title could get 13
or higher, and a thread pinned to one CPU could get another one.
The host index now selects a CPU of the calling thread's affinity
clipped to the available mask, so the result is always an advertised
CPU and a thread pinned to one CPU reports that CPU.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
Opening a volume needs administrator rights, so the volume flush skipped
every volume in a normal user session. The libc, libkernel, save data and
app content file functions now record each host file and directory they
change. On Windows sceKernelSync flushes the recorded paths that changed
since the previous call, plus their changed parent directories, through
handles a normal user can open. Linux still calls sync().
wcstod, wcstof, wcstold, wcstol, wcstoll, wcstoul, wcstoull, wcscoll and wcsxfrm still forwarded to the host's wchar_t functions, which use 32-bit units on Linux. The wcsto* functions now parse the ASCII prefix of the guest string with the narrow parsers, as the C locale accepts only ASCII white space and digits. wcscoll compares like wcscmp and wcsxfrm copies, as strcoll and strxfrm do in the C locale.
BnMAMrsfVWo is _ZSt19_Throw_bad_weak_ptrv. Replace the unknown stub
with a throw of a guest std::bad_weak_ptr, and add its type info,
vtable, destructors and what().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Treat equal BASE_ARRAY and DEPTH as a six-face cube. Measured with image_load, image_sample and image_get_resinfo on RX 6600 gfx1032 using base slices 0, 5 and 6. Partial cube ranges still throw because filtered sampling differs from load bounds.
Graphics tests and GPU snapshot/storage sampling tests pass for all six faces at each tested base.
Assisted-by: OpenAI Codex
sceKernelReadv, sceKernelWritev, sceKernelPreadv and sceKernelPwritev threw on Windows. They now gather or scatter through one host buffer and issue a single read or write, with the same argument checks and error codes as the Linux path. Positioned transfers restore the file offset afterwards, as pread and pwrite do on POSIX, and report ESPIPE for handles that are not disk files. guest_file_vector now runs on Windows as well.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
COPY_DATA rejected source select 9 (GPU clock count) and any cache
policy bits. PPSA16064 (Minecraft Dungeons II, Unreal Engine)
calibrates its GPU timer at start-up with a submission of five
COPY_DATA packets, control 0x06016209: GPU clock source, memory
destination, 64-bit count, source and destination cache policy 3. The
submission was rejected and the title aborted.
The clock source now writes the same counter as a RELEASE_MEM
timestamp (the host steady clock in 10 ns ticks), read when the packet
runs, through the direct execute path and through ResolveStore for
stores recorded on the GPU. A 32-bit count writes its low half. The
source and destination cache policy fields are accepted and ignored,
as for WRITE_DATA and DMA_DATA. The reference clock (source select 10)
and the other unimplemented sources still throw.
agc_driver_pm4 runs the title's packet twice and checks a nonzero,
non-decreasing counter and an 8-byte resolved store, and that source
select 10 is still rejected.
MinGW's thread_local goes through emutls and winpthreads' pthread_getspecific, which holds a per-thread spinlock. A raise that redirected a thread holding it ran the handler on that same thread, and a handler reading any thread_local, scePthreadSelf for one, spun on the lock forever. A raise that finds the target inside libwinpthread-1.dll now resumes it and suspends it again, as one inside NtContinue does.
guest_raise_exception's new phase raises at a thread that calls scePthreadSelf in a loop and checks that the handler sees its own thread. It hung in every run without the retry. TechnicalDebt records that a handler can still reenter other host code the thread was interrupted in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guest's wchar_t is 16 bits, but wcslen, wcscmp, wcsncmp, wcscpy, wcsncpy, wcschr, wcsrchr, wcsstr, wcspbrk, wcsspn and the wmem functions forwarded to the host's wchar_t functions, which use 32-bit units on Linux. Implement them on char16_t. The wide conversions that still use the host's wchar_t are listed in TechnicalDebt.
NVIDIA (595.91.07) refuses VK_EXT_external_memory_host imports of
read-only or executable mappings with VK_ERROR_OUT_OF_DEVICE_MEMORY and
then fails every later vkAllocateMemory on the device. Check the range
is mapped read-write before importing on Linux, as the Windows path
already does for read-only memory, and copy it otherwise.
Refs #1022
A CMASK holds 4 KiB per 1024x512-pixel metablock, not one nibble per 8x8
tile of the target: 1920x1080 spans 0x6000 bytes, as titles clear it,
not 16200. The swizzled nibbles of the lower-right tiles lie past the
packed size, so the fast-clear check missed them and the expanded store
left them cleared.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Detach the device cache buffer attached by sceFontAttachDeviceCacheBuffer. The signature is taken from the IDA type database of Orbital: it takes the library, a pointer to receive the buffer and a pointer to receive its size. When no cache buffer is attached, it reports NOT_ATTACHED_CACHE_BUFFER. If the library allocated the buffer itself and no buffer pointer is passed, the memory is freed. The owned bit is cleared and device_cache_buf is reset to null.
The driver copies a submission's packets when it is submitted, but
SET_SH_REG_INDIRECT, SET_UCONFIG_REG_INDIRECT and SET_CONTEXT_REG_INDIRECT
read their (offset, value) lists from guest memory only when the queue
worker reaches the packet. A title that reuses its per-frame memory once
it expects the GPU to be done with it can rewrite a list in between, and
the worker then throws on the data it finds there ("extended register
semantics are not implemented (offset dword 0x3a888889)").
The lists are now read with the packets, after validation, in Submit and
for a rewind tail, and the worker writes the registers from that copy.
Pm4::Execute keeps reading the list itself for direct callers.
PPSA21402 (Balatro) keeps three frame slots and does not wait for the GPU
before reusing one: its slot labels start at 0xffffffff and the GPU only
ever writes 0xffffffff to them, so the poll before a slot is reused never
blocks. When the worker falls more than two frames behind, the title
writes per-draw constants over a SET_CONTEXT_REG_INDIRECT list the worker
has not read yet.
The console's PFP reads the list when it reaches the packet; the
difference is recorded in TechnicalDebt.md.
guest_formatting_tests was built on Linux but registered with ctest on
Windows only, so the Linux printf and scanf exports had no coverage: on main
the test fails there at its first vsnprintf check and then crashes in
snprintf with eight integers.
It is registered on every platform now. The %ls checks stay Windows-only,
as glibc reads %ls as 32-bit wchar_t; that gap is recorded in TechnicalDebt.
A color target with both DCC and CB_COLOR_INFO.FAST_CLEAR keeps its fast
clear in the DCC keys and fills its CMASK with 0xF (expanded), as radv
does (radv_get_cmask_fast_clear_value) and as PPSA21567 does
(CB_COLOR_INFO 0x10042730, reported on #419). Such targets now decode;
their clear is materialized by the existing DCC path. A DCC target's
CMASK that is not all expanded throws.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Draws into a color target with CB_COLOR_INFO FAST_CLEAR (bit 13) were
rejected. The CMASK keeps one nibble per 8x8 tile: 0 means the tile is
fast-cleared and reads as CB_COLOR_CLEAR_WORD, 0xF means expanded (the
texels hold the color). The texture units and display scanout do not read
it, so a title runs a fast-clear eliminate pass before sampling or
flipping such a target.
DecodeColorBuffer now takes the CMASK address (CB_COLOR0_CMASK and
CB_COLOR0_CMASK_BASE_EXT) and the bytes one nibble per 8x8 tile spans.
Before a draw renders into the target, and in a fast-clear eliminate
pass, a CMASK whose nibbles are all 0 is materialized: the target is
filled with the clear texel (FillClear on its resident image, else
guest memory on the CPU) and the CMASK bytes are stored as expanded,
through the same GPU-ordered store as the DCC "uncompressed" keys
(0xFF either way). The result equals the draw followed by the eliminate
pass. An all-expanded CMASK changes nothing; a mixed one throws, as do
fast clears of DCC, mipmapped, 3D and array targets.
PPSA14632 (LEGO Horizon Adventures) fast-clears its two 3840x2160
display buffers through CMASK, so its final composite draw was dropped
on every frame and only black was presented.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Read the module layout from the PE image that holds the address. A
relinked image reports its .elf sections as segments and the
eh_frame_hdr recorded in .ehmeta; any other image reports its sections
with memory access and its .ehfram section. The eh_frame_hdr and
eh_frame helpers are shared with the Linux path, and the guest module
info test now also runs on Windows.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A 16-bit PCM block whose num_samples exceed its data starts a stream, and waveform blocks with flag 0x2 append more data to it. The voice outputs silence while it has played all appended data and CONTINUE is set, and ends at num_samples or when CONTINUE is cleared.
A raised exception redirects the target thread to a stub that runs the handler and returns through RtlRestoreContext. On Windows 10 (19045) RtlRestoreContext restores in user mode only when the context flags are exactly CONTEXT_FULL | CONTEXT_SEGMENTS (CONTEXT_XSTATE aside); for the CONTEXT_FULL | CONTEXT_FLOATING_POINT context it was given, it calls NtContinue. When the next raise suspended the thread inside that system call, NtContinue then replaced the redirected context with the one it was restoring, so the handler never ran although sceKernelRaiseException had returned 0. guest_raise_exception lost such a delivery to its busy thread in about one run in five and hung until the ctest timeout.
The context is now read with CONTEXT_SEGMENTS, so the handler returns through the user-mode restore, and a raise that finds the thread inside NtContinue or NtContinueEx (an APC or an exception handler returning) resumes it and suspends it again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on a PS5 (firmware 13.60) through dlopen of
libSceVoiceQoS.sprx: a null block, any size other than exactly 0x40000,
and an app type other than 0x20000000 or 0x10000000 all return
0x804E0902 without touching the block, instead of throwing. A second
initialization returns 0x804E0905, as prosper reads from the module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add scePadOpenExt for special ports, which return DEVICE_NOT_CONNECTED because the host has no special controller, and the extended read and report exports, which throw.
The LOD warning lwe adds to the texel status is texture-streaming feedback,
which is not reported for the T# MIN_LOD_WARN either. Accept lwe on sample
and gather instructions as it already is on loads and stores; lwe on
image_get_lod still throws.
Once an image has .ehmeta, an eh_frame_hdr version other than 1, an
eh_frame_ptr encoding other than absptr or pcrel, a 64-bit record length,
an eh_frame_ptr outside the image or a missing terminator now throw
instead of returning the empty answer. The test queries a fixture DLL
whose .ehmeta it can break, since the unwinder reads the same section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a save progress dialog immediately reported FINISHED. Skyrim
waited for RUNNING before writing SAVEDATA.DAT and never resumed. Keep
progress-bar mode running through polling and progress updates until
Close, retaining the existing automatic responses for other dialog modes.
Add lifecycle regression coverage for repeated polling, completion,
reopening, result metadata and ordinary selection dialogs. The test
failed on the previous implementation and passes with the fix on Fedora
44 Release. Correcting the live dialog state recovered a blocked Skyrim
save without restarting its session.
Written with AI assistance.
Measured on a PS5 (firmware 13.60) through dlopen of libSceRtc.sprx.
The weekday is required in any case or length, month names may be
full, RFC 2822 and RFC 850 share one lenient grammar with 2-digit years
split at 50, and the asctime zone is ignored. Zones come from a name
table, a military letter, or +HHMM without a range check. Text the
console rejects returns a parse error with the tick untouched, and an
invalid calendar date returns 0 with only the offset applied to the
tick, as the console does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PS5 (firmware 13.60) every input that starts with a digit gets the
same result and tick from sceRtcParseDateTime as from
sceRtcParseRFC3339, except that leading spaces and tabs are skipped
first. A string without a zone, or with a space instead of T, is a
parse error rather than UTC.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A year before 1980 or after 2107 cannot be packed into the DOS time, so
GetDosTime threw. Following the disassembly-derived fpPS4
reimplementation of the PS4 library, it now returns INVALID_YEAR after
writing 0 for an earlier year and 0xff9fbf7d, the largest encodable
time, for a later one. The TechnicalDebt entry now records fpPS4 as the
source for the SetDosTime year decode as well.
A captured grain stored only the port slot and data pointer, so a port
created in a destroyed port's slot before the next push mixed the old
pointer with its own format. Ports now carry a generation that the
capture records. TechnicalDebt notes that the last grain pushed before a
title stops is never played.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wave64 graphics program on a 32-lane host runs one guest lane per
invocation, yet an EXEC written as a value keeps all 64 bits: the NGG
prologue's s_lshr_b64 exec, -1, 64 - count reads count 0 and sets -1.
A waterfall loop that copies EXEC with s_mov_b64 and retires one key
per pass then never empties its copy, since no ballot sets the lanes
the host lacks, and its v_readlane picks lane 32 and up.
readRawU32 now ANDs EXEC with a ballot of true when it is read as a
value (s_mov_b64, ALU sources, src_execz), and s_*_saveexec and
s_*_wrexec store that masked copy. EXEC's own words, threadBit and the
saveexec combination keep the raw words, so s_orn2_saveexec_b64 vcc,
exec still sets all 64 bits. On subgroups under 32 lanes the EXEC_LO
copies of agc_driver_scalar_sop1 now hold only the subgroup's lanes.
The Smurfs Dreams imports both and no library exports them, so eager
binding stops the title before it starts. sceAudioInAsyncOpen throws
like the other AudioIn opens that have no implementation;
sceHttp2SetMinSslVersion returns success like the other Http2 SSL
setters, as no request reaches the network.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host mixes without the console's mastering stage, so the mastering
parameters have nothing to configure, as with sceAudioOut2MasteringInit.
Accept them instead of aborting, keep throwing for a null parameter
block, and record SetParam next to MasteringInit in TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Return the id a graphics event was registered with in
sceAgcDriverAddEqEvent, which titles derive from their context
(PPSA12544: 0 or 1 for graphics, 0x20 + 8 * pipe + queue for compute) and
read back to dispatch the event. Other event filters, a null or
misaligned event and an id above 32 bits throw. The meaning is an
assumption and recorded in the technical debt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GuestAppContent now writes a param.json that declares downloadDataSize 0
and checks that sceAppContentDownloadDataGetAvailableSpaceKb returns OK
with 0 KB for /download0 (it threw before), and PARAMETER for a null
result. TechnicalDebt records that the console's behaviour for such a
title is unverified.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review: read a grain's buffers at the next push without spinning
under the context lock, keep the no-device model mixing at push time,
size the pad queue from the unplayed main grains again, use generic
names in comments and TechnicalDebt, and add audio_out2_timing (next-push
read, queue level beyond the cushion, priming after running dry).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The APC delivery was allocated after SuspendThread, so a target holding
the allocator lock deadlocked the raising thread; it is now allocated
before the suspend. The wait count a raise checks lived in the target's
thread_local storage and dangled once the thread exited; it is now a
member of PthreadPrivate, which TimedWait counts into through a
thread-local pointer bound when the thread starts, and a raise on a
thread that already finished returns ESRCH. TechnicalDebt gets one line
per unverified choice: Windows only, the FreeBSD ucontext layout, the
signal mask, the YMM upper halves, and delivery to threads outside
TimedWait.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PPSA03306 (Silt) loads the Unity plugin Media/Plugins/PSNCore.prx, which
imports scePlayerReviewDialogInitialize, Open, UpdateStatus, GetResult and
Terminate from libSceCdlgPlayerReview. No library provided it, so loading
the plugin failed with "Failed to load module ...\libs\libSceCdlgPlayerReview.prx".
The library follows libSceWebBrowserDialog: the dialog shows nothing, Open
finishes at once and GetResult reports that the user canceled. Terminate
also accepts a finished dialog. GetStatus and Close are added as well.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OpenAGC's include/agc_nids.h names three more of the raw-NID exports, and
each name hashes to its NID:
gQkqkLttcpw sceAgcAcb_gQkqkLttcpw -> sceAgcAcbAtomicGds_0900
dolOmWH+huQ sceAgcUnknownGetFusedShaderSize -> sceAgcGetFusedShaderSize_0080
V++UgBtQhn0 sceAgcGetDataPacketPayloadAddressUnk -> sceAgcGetDataPacketPayloadAddress_0090
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eight libSceAgc exports were published under a raw NID with a made-up
name. Each NID is the hash of a versioned SDK name (checked with
tools/nid_names.py compute_nid), so the functions now carry that name and
are exported from it directly:
23LRUSvYu1M sceAgcUnknownInitState -> sceAgcInit_0090
-KRzWekV120 sceAgcUnknown__MKRzWekV120 -> sceAgcDcbSetIndexSize_0600
Ikfdt-rIqCE sceAgcUnknown_Ikfdt_MrIqCE -> sceAgcJumpPatchSetTarget_0300
7Wa3aeJgeVU sceAgcUnknown_7Wa3aeJgeVU -> sceAgcBranchPatchSetThenTarget_0300
rP5xLdOf26k sceAgcUnknown_rP5xLdOf26k -> sceAgcBranchPatchSetElseTarget_0300
fd5Bp5tGTgo sceAgcUnknownFuseShaderHalves -> sceAgcFuseShaderHalves_0200
qj7QZpgr9Uw sceAgcDcbContextStateAnotherOp -> sceAgcDcbContextStateOp_0100
k0E7vkgqAuE sceAgcCreateInterpolantMappingVsPs -> sceAgcUpdateInterpolantMapping_0100
The exported NIDs are unchanged; the extra exports under the hashes of the
made-up names are gone. Behaviour is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PPSA01325 stops on sceFontGlyphDefineAttribute right after generating a character glyph: it sets attribute 0x11 to 0 on that glyph, deletes it without using it, and draws the character with sceFontRenderCharGlyphImageHorizontal. The meaning of the attributes is unknown, so the call checks the glyph and ignores the rest; TechnicalDebt lists it under silent stubs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
Astro's Playroom (PPSA01325) fails eager binding on three imports. The two voice chat group requests follow the offline model of the other sceVoiceChatRequest* calls and return SIGNED_OUT; sceRudpActivate, whose signature is unknown, is declared as a throwing stub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
PPSA16064 (Minecraft Dungeons II, Unreal Engine) failed to load because
these imports had no export:
- libkernel: sceCoredumpWriteUserString
- libSceSaveData: sceSaveDataConvert, sceSaveDataGetConvertProgress
- libSceVideoOut: sceVideoOutVrrPegToFixedRate,
sceVideoOutVrrUnpegFromFixedRate
None of them is called during the title's start-up, so they are added
as unimplemented exports that throw with their name if a title calls
them. Their signatures are unknown; TechnicalDebt records them. The
built libraries export the NIDs the title imports.
Every command writer checked bottom <= cursor_up <= cursor_down <= top.
PPSA16064 (Minecraft Dungeons II, Unreal Engine) builds small one-packet
buffers (8 dwords) with cursor_down left null, and the first
sceAgcDcbCopyData into one threw "invalid command buffer cursors".
Nothing in the library allocates downward, so cursor_down only bounds
the space above cursor_up. A null cursor_down now means that bound is
top. Non-null cursors are checked as before. TechnicalDebt records the
assumption.
agc_commands writes six dwords into such a buffer and checks that a
further allocation past top fails without moving the cursor.
NID fCWdlnmB1Ks was exported as an unknown function that threw. The
name scePadIsRemoteController hashes to it. PPSA16064 (Minecraft
Dungeons II) calls it once per controller after
scePadGetControllerInformation, with the pad handle and the address of a
byte it has just zeroed, and ignores the result.
It reports whether the controller is connected through Remote Play.
Controllers here are local devices, so the answer is always false. An
invalid handle returns INVALID_HANDLE and a null output INVALID_ARG, as
the other pad calls do. Without it the title aborted at the main menu.
scePadSetAngularVelocityBiasCorrectionState, which the title imports but
does not call during start-up, is added as an unimplemented export.
guest_pad checks the handle and null-output errors and the result.
sceHttp2SendRequestAsync no longer fails synchronously: it queues a
completion carrying the network error and triggers the user event named
by the kqueue option. sceHttp2WaitAsync returns queued completions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BREAKING CHANGE: CLI and Python callers must supply all seven float
controls. Add clamp, rounding and FP16 overflow settings so hardware
measurements cannot silently use an unintended mode.
Refs #981
Storage images of a SINT format threw "storage image descriptor uses
an unsupported format"; only write-only 32_SINT was accepted.
The recompiler now writes 16_SINT, 8_8_SINT, 32_SINT, 16_16_SINT,
8_8_8_8_SINT, 32_32_SINT, 16_16_16_16_SINT and 32_32_32_32_SINT
storage images with the Uint numeric class. 8- and 16-bit components
are saturated (EmitFormatStoreComponent) and 32-bit ones stored
unchanged, as measured on RDNA2 (gfx1035); image_store_pck stores
32-bit components unchanged too. The driver binds these surfaces
through a cached view of the same-size UINT format.
8_SINT, 32_32_32_SINT, the packed SINT formats (11_11_10, 10_11_11,
2_10_10_10, 10_10_10_2), D16 stores, packed stores of 8- and 16-bit
components and atomics other than 32-bit ones on 32_SINT and 64-bit
ones on 32_32_SINT still throw. New test: agc_driver_image_store_sint,
with DST_SEL X,Y,0,1 on the two-channel formats, as with X,Y,Z,W the
hardware writes channel 1 from the W data slot (TechnicalDebt).
NVIDIA 610.62 cannot sample R8G8_SRGB, and lavapipe can sample neither
it nor R8_SRGB, so an 8_8 sRGB T# made an image the device does not
support: creating it crashes the NVIDIA driver and lavapipe loads zeros.
The device's format support is read once. An 8 or 8_8 sRGB texture the
device cannot sample is created and viewed as R8_UNORM or R8G8_UNORM,
and SpirvTarget::srgbDecodeFormats makes the materializer mark the
image, so image loads map every channel through a 256-entry table of
the sRGB EOTF rounded to nearest even at bf16 precision, which is what
RDNA2 (gfx1035) returns; both channels of 8_8 are sRGB, and DST_SEL 0
and 1 map to themselves. Samples and gathers of such an image throw,
and its bindless table entries are unmapped like converted formats.
APS5_SRGB_SHADER_DECODE=1 takes this path on every device. Shader disk
cache entries and keys carry the mark: cache format version 7.
DS opcodes 0x6e and 0x6f exchange two qwords at offset0 and offset1
scaled by 8 (st64: 512) and return both old values. Each address is
exchanged with its own 64-bit LDS exchange on the locked 64-bit path.
Equal offsets and a destination past v255 throw, as for the b32 forms;
64-bit GDS throws like the other 64-bit DS atomics.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comparison sampling of a color texture threw on the _o, _b and _cl
forms and on a sampler LOD bias. The emulation fetches the texels
itself, so the 6-bit offsets of the address are added to the point or
bilinear texel indices, as on the hardware and in Vulkan. The emulation
runs only at level zero or on a single-level view, where an LOD bias or
clamp cannot select another level, so they are accepted with no effect.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LookBackScan runs a 43-instruction decoupled look-back written for the
test: one aggregate per block, the block index from an atomic counter,
{flag, value} published with buffer_atomic_swap_x2 and read back with
glc buffer_load_dwordx2. Seven single look-backs and 50 scans of 4096
blocks are checked against a CPU prefix sum; without the recompiler
change the first whole scan fails (3 of 3 runs).
TechnicalDebt records that only glc, aligned, in-bounds dwordx2 loads
in programs with the 64-bit storage view are one atomic load: loads
without glc, and loads in programs without 64-bit buffer atomics, can
still tear against a concurrent swap_x2.
image_atomic_* with a two-dword data mask (four for cmpswap) is the
64-bit form of an integer image atomic. The decoder rejected it with
"invalid MIMG data mask". PPSA14632 (Unreal Engine) uses
image_atomic_umax this way in a compute shader during its first frames.
Swap, add, sub, signed and unsigned min/max, and, or, xor and cmpswap
now decode with 64-bit data and translate to new ImageAtomic*64 IR
opcodes. An image updated by them gets its own descriptor binding
range, declared as an R64ui image of 64-bit integers, and the module
uses Int64ImageEXT from SPV_EXT_shader_image_int64. The driver enables
shaderImageInt64Atomics when the device offers it and binds an
R64_UINT view of the 32_32 UINT, SINT or FLOAT surface. Without the
feature, recompiling such a shader throws.
64-bit inc, dec and the float atomics still throw, as does any other
access in the same shader to an image it updates with 64-bit atomics.
The new agc_driver_image_atomics64 test runs every operation on 32_32
UINT, SINT and FLOAT surfaces and checks the returned and stored values
against edge cases across the dword boundary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The resource tracker refused a program's 33rd distinct V# with "buffer
resource limit exceeded". The 32 was a recompiler constant, not a
Vulkan limit: every buffer is one constant-indexed element of the guest
buffers binding, which ShaderResources checks against the device's
storage-buffer limits.
Drop ShaderInfo::MaxBuffers and the tracker's refusal, and size the
SPIR-V emitter's per-buffer offset list from the binding layout. When
the user data and offsets do not fit in the push constants, they all go
to the shader data buffer as before. ShaderResources counted only
storage buffers against maxPerStageResources (200 on Intel UHD) and now
adds the stage's sampled and storage images.
agc_driver_many_buffers reads and writes 52 and 124 buffers, and
agc_driver_graphics rejects buffers plus an image above the limit.
An S# with FILTER_MODE (word0 bits 29-30) set threw in
DecodeSamplerResource. Modes 1 and 2 now map to Vulkan's MIN and MAX
sampler reduction through VK_EXT_sampler_filter_minmax, which the device
enables when it reports both filterMinmax properties; mode 3 throws.
Min/max still throws with anisotropy, a linear mip filter (lavapipe and
NVIDIA disagree there), depth comparison, color-texture compare
emulation, and, in PointFilteredSamplerWord (S# words 0 and 2), when it
filters between texels or mips of an image the recompiler point-samples.
A texture sampled through a linear min/max sampler needs
SAMPLED_IMAGE_FILTER_MINMAX on its view format; the image-sampler pairs
travel in DescriptorBinding::imageSamplers (shader disk cache format 5).
Adds the agc_driver_image_sample_reduction GPU test and extends the
sampler, emulated color compare and disk cache tests.
RADV (Mesa 26.0 and later) initialises a FREE_DESCRIPTOR_SET pool's
allocator with bo_size + RADV_POOL_HEAP_OFFSET as its size, so a set can
be placed up to 32 bytes past the pool's buffer. The chain pools are
0x26000 bytes, a page multiple, so a set at the end of a full pool has
its last descriptors on an unmapped page: the CPU descriptor write
faults, or the shader's scalar load raises a GPU page fault and the ring
is reset.
Two more storage buffer descriptors make the pool 0x26020 bytes; the
buffer's last page then holds those 32 bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CB_COLOR_INFO format 1 with number type 6 (8_SRGB) threw at decode.
It now decodes as R8_SRGB with component swap 0; the other swaps and
8_8_SRGB still throw.
A device that cannot attach and blend R8_SRGB (NVIDIA, lavapipe)
renders a resident 8_SRGB target through an RGBA8_SRGB view of an
RGBA8 copy of its R8 image: the image is blitted in before the pass
and back when the pass ends, and the write mask keeps R only, so the
encode, decode and blend are the device's sRGB attachment rules.
APS5_SRGB_ATTACHMENT_PROXY=1 forces the copy. Linear, mipmapped and
3D 8_SRGB targets throw on such a device.
PPSA26344 renders its G-buffer slot 5 and a multiply-blend pass into
8_SRGB targets.
LEGO Horizon Adventures imports 19 functions that no library exports
and no open pull request adds, so it fails eager binding. They are
declared as throwing stubs. The two libSceAjmWVorbis imports have no
known name and are exported by NID from libSceAjm.native.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A title maps direct memory with protection 0x3f2, which adds bits
0x100 and 0x200 to the CPU, GPU and AMPR bits, and the mapping threw.
They are the audio coprocessor (ACP) read and write bits, as named in
the KytyPS5 and etaHEN headers. The coprocessor work runs on the host
CPU, so they map to host read and read/write access like the GPU bits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Return the version 0 internal register defaults, the internal
counterpart of sceAgcGetRegisterDefaults.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A stage's user data registers are read for the count in its RSRC2
(COMPUTE_PGM_RSRC2 for compute). A register the title never wrote no
longer refuses the draw or dispatch: it reads 0, as Kyty and SharpEmu
read it and as AMD PAL and Mesa initialize the user data registers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mixing stage threw when a mastering voice and its render buffer had different channel counts. A 2-channel voice is now mixed into the first two channels of a 6- or 8-channel buffer; other mismatches still throw.
Driver::RegisterShader required the Shader header at an 8-byte
boundary (alignof(Shader)) and read its fields through the pointer.
PPSA26344 uses its shader packs in place: its compute shader objects
point at headers 4 and 1 bytes past an 8-byte boundary, so the first
sceAgcCreateShader of such a header threw "null or misaligned address".
The header is now checked for null and readable memory at any
address, and its fixed fields and the registers the trace prints are
copied out with memcpy. The code address must still be 256-byte
aligned, and the header and size checks are unchanged. The console's
rule is unknown and is listed in TechnicalDebt.
The call site of PPSA21402 sets only three argument registers, so the function has no size parameter. A data chunk that runs past the buffer or ends inside a frame returns INVALID_WAVEFORM_DATA instead of being shortened. The test no longer writes padding from a string literal.
Write the 2-dword CLEAR_STATE (0x12) packet with a 4-bit command, the
packet the driver already validates and executes by restoring the
initial context registers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Write SET_CONFIG_REG (0x68) packets like the context, shader and user
config register setters: one register from a ShaderRegister, or a range
of values at an offset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Patch the mask of a wait packet like sceAgcWaitRegMemPatchReference
patches its reference: a 32-bit value written to the low mask word of a
32- or 64-bit WAIT_REG_MEM, rejected above 32 bits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The end-of-shader action is written by an inline SDK builder as a
RELEASE_MEM packet, so the size is the 32 bytes that
sceAgcCbReleaseMem writes and that sceAgcAcbQueueEndOfShaderActionGetSize
already returns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reverb was a Freeverb-style approximation. It now follows the PS4
firmware 11.00 libSceNgs2.sprx, read and checked against its output: the
input mixed to mono with the library's fixed gains, a 14400-sample buffer
with four reflection taps from the reflection pattern tables and a late
feed, sixteen damped comb filters with a pre-emphasis stage, a signed
selection of combs and four allpasses per output channel, a two-pole room
HF filter, and the library's parameter clamps, crossfades and setup
defaults. The test values are the PS4 library's own output.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>