The import probe only checked whether a GPU read of an imported range
reports its pages as written. lavapipe runs command buffers on CPU
threads, so its writes into an import go through the page tables and
are collected as CPU stores. In agc_unit_shadow the whole publish's
copy into the import is then collected as a CPU store over unit 3, and
the next partial publish drops the slab's newer bytes as stale: "the
partial publish did not copy exactly the partly covered unit".
The probe now also fills the scratch range on the GPU and collects
again. When the read or the fill reports pages as written, host and
dma-buf imports are compared. lavapipe reports 0 of 4 pages after the
read and 4 of 4 after the fill; NVIDIA reports 0 after the fill.
The probe change is the first commit of #1046.
The title id now comes from GetAppTitleId, so param.json has one parser and
its validation (a missing file or titleId throws). Another pid returns EPERM
and a null info returns 0 without writing, as shadPS4 does. app_id stays 1,
the running title's id in libSceSystemService, which is inferred, not
measured.
guest_app_info runs the call without param.json in a child process and
checks that the field between app_id and the title id is left alone.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceKernelGetAppInfo fills the app id and the title id of the calling
process: app id 1, the running title's id in libSceSystemService, and the
titleId of /app0/sce_sys/param.json at offset 0x10, up to 9 characters and a
terminator. Only those fields are written, since callers declare the
structure with different sizes. Another pid returns ESRCH and a null info
EFAULT.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The AMM ranges were reserved address-less on first use, so they took the
lowest free address of the arena, directly after the title's last mapping,
where the title's next hinted maps go. They are now reserved at a fixed
place, the last 64 GiB of the guest arena, which maps without MAP_FIXED and
the hints derived from them do not reach; a reservation that cannot be made
there throws.
Fixes#3146
Split at the first delimiter like FreeBSD's strsep: empty fields are
returned, and the cursor becomes null after the last token.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Share rand's generator step with rand_r, which advances the caller's
state instead of the global one, as FreeBSD's rand and rand_r share
do_rand.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Measured on a PS5 (firmware 13.60):
- sceRudpInit rejects a null pool or a size of 0 or less with
INVALID_ARGUMENT, and a pool under 0xC58 bytes or not 8-byte aligned
with MEMORY (0x80770007).
- sceRudpGetStatus rejects a null pointer or a byte count of 0 or above
0xF8 with INVALID_ARGUMENT and reports the live context count at 0x64.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Export sinh, cosh, coshf, asinh, asinhf, acosh, acoshf and atanh,
forwarding to the C++ standard library like tanh and tanhf.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
InitialContextRegisters built the clear-state context registers on every
call: about 200 emplace calls from its list and loops, with the register
vectors growing several times on the way to offset 0x3b8. Each
QueueState starts from it, so it ran for every CLEAR_STATE, and
ResolveShaderAbi and ResolveGraphicsStagesAbi construct a QueueState on
each call, which sceAgcLinkShaders, sceAgcCreatePrimState and
sceAgcCreateInterpolantMapping make while a title draws.
The registers are now built once into a table that is never destroyed,
so a queue cleared while the process exits still finds it, and each call
returns a copy of the table.
A call takes 71 ns instead of 746 ns, and constructing a QueueState 885
ns instead of 1378 ns (200000 calls each, GCC 13 -O2). On the title
screen of PPSA21402 (Balatro) the two functions took 44 to 47 of about
2300 CPU samples per 10 s and no longer appear in the profile.
sceNgs2ParseWaveformFile read everything from the offset to the end of
the file into a zero-filled buffer and parsed that, although parsing
uses only the RIFF chunk headers and the fmt, fact and smpl payloads.
A waveform at an offset in a sound bank cost a read of the rest of the
bank on each call.
ReadChunks now takes the bytes through an accessor. Waveform data passes
its buffer; a waveform file seeks to and reads the headers and those
payloads alone, with the same checks against the length from the offset
to the end of the file. The format dispatch both use moves to
ParseWaveform.
ngs2_parse adds a bank of two waveforms in one file, the second with a
chunk after its samples, and parses each at its offset.
PPSA21402 (Balatro) parses its five 33 MB music tracks at start: one
call took 8.3 to 26.7 ms and allocated 33 MB, and now takes 0.01 to
0.04 ms with the same result.
An unregistered graphics program is read raw only while the shader registry
is empty; with shaders registered it throws, as before. A draw that uses a
raw program is noted, and RegisterShader throws once that has happened,
since a title that registers shaders after drawing unregistered programs is
not understood. The draw cache revalidates a raw program by reading it raw.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The POSIX alias takes (attr, int), returns EINVAL (22) for a null or
uninitialized attribute like the other pthread_attr setters, and
stores the value through scePthreadAttrSetsolosched, returning its
result in errno form. The signature and the stored int follow the SCE
twin in KytyPS5 and sharpemu; PS5PCEM implements the alias the same
way. The value is not applied to scheduling, recorded in TechnicalDebt.
Returnal (PPSA01285 v03.000.005) stops on it after eager binding
(#2872, #1155):
what(): pthread_attr_setsolosched_np_nid_postfix not implemented
LEGO Horizon Adventures (PPSA14632, #734) and Palworld (PPSA23126,
#2598) import it.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
The register draw key identifies an unregistered program only by its
address, and a key hit reuses the entry's decode without reading the
program again. A different program uploaded to the same address with
the same registers would run the old compiled shader. A hit now checks
the decode's unregistered programs against guest memory and drops the
entry when one changed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Use the upstream CMASK implementation and remove the import requirement from image clears. Propagate storage-image creation failures. Test repeated clears, copied write-back, and failure without changing guest data.
AI-assisted: yes.
guest_filesystem creates symbolic links for the Windows lstat checks.
Without administrator rights or Developer Mode, CreateSymbolicLinkW
fails with ERROR_PRIVILEGE_NOT_HELD even with
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, and the test aborted there
(reported as a stale guest errno). Skip those checks with a note in that
case; any other failure to create the first link still fails the test.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#2839 decides f32 product tininess after rounding, in all four f32
round modes, from the exponents and a UMulHi and IMul of the
significands, and builds the flushed value from them. To nearest even,
the mode of shaders without a known float mode and of the 0xc0 most
titles use, one multiply decides it: one factor times 2^64, which is
exact below 2^64, times the other factor rounds the exact product once
to 24 bits in the normal range, and 0 < |that| < 2^-62 is tininess
after rounding. A product that rounds up to 2^-126 is then not tiny and
keeps the emitted product, which is 2^-126 there, so nothing has to be
built from the significands.
The flush of v_mul_f32 v5, v6, v7 takes 12 IR instructions instead of
34, counted as the instructions the flush select needs beyond the
product. vector_constant_factor_tininess pins 12 to nearest and 36 for
the directed path of the same instruction, and checks that the
directed modes and v_mad_legacy_f32 (before rounding) keep #2839's
check. Its round-to-nearest branches are removed as unreachable.
The f32 denormals entry says which rounding the check inherits: the
multiply is a plain OpFMul like the product, with no f32 rounding mode
requested, so it relies on the driver rounding f32 to nearest even, as
the products do. F32ConstantFactorTininess names the row a check before
rounding flushes, 0x3f7ffffe * 0x00800001, which keeps the emitted
0x00800000.
Against #2839 to nearest even, a model of both checks agrees on
1,074,977,546 operand pairs (every 24-bit significand at both rounding
thresholds, random pairs, specials) with each other and with an exact
reference. On an RTX 5070 the outputs of #2839's own tininess and
mixed-source shaders are bit-identical with either lowering over
165,298 and 164,008 generated rows in all six float modes tested.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A wait on an equeue that holds socket events now blocks in poll/WSAPoll on
those sockets plus a per-thread loopback wake socket, instead of rechecking
every millisecond. Adding, triggering or deleting an event and closing the
equeue wake the blocked waiters. Closing a socket removes its read and write
events, as on FreeBSD. A read event also reports EV_EOF once the peer has
shut down its sending side.
sceKernelAddReadEvent/DeleteReadEvent and sceKernelAddWriteEvent/
DeleteWriteEvent register EVFILT_READ and EVFILT_WRITE events on guest
sockets. The equeue polls the host socket for readiness on each wait
and reports the readable byte count or the send buffer size in data,
with EV_EOF on hang-up or error.
pthread_cond_wait and pthread_cond_timedwait, and their sce counterparts, threw a
host exception when the mutex was unlocked, locked by another thread, or not yet
initialized, and dereferenced the adaptive static initializer. FreeBSD returns
EPERM for these (EINVAL for a destroyed mutex) before touching the condition.
(cherry picked from commit a78bb8a855)
The elected lane's inner spin loop never ended on Mesa ANV with the LDS in
a storage buffer. It now tries once and the outer loop elects again; the
update is unchanged.
(cherry picked from commit e894f90923)
sceVideoOutVrrPegToFixedRate and sceVideoOutVrrUnpegFromFixedRate were
NotImplemented without arguments. Both take the output handle; the peg
also takes two further arguments whose meaning is unknown. VRR is not
modelled and the output always runs at a fixed rate, so both validate
the handle and return 0; non-zero peg arguments throw. Recorded in
TechnicalDebt.
Uncharted: Legacy of Thieves Collection (PPSA05684) stops on main with
"sceVideoOutVrrUnpegFromFixedRate not implemented"; it passes its
output handle to both (and zeros to the peg) and ignores the results.