sceHttp2GetMemoryPoolStats, sceSslGetMemoryPoolStats and
sceNetGetMemoryPoolStats threw. PPSA16064 (Minecraft Dungeons II,
Unreal Engine) queries all three while its HTTP module starts, and
aborted there.
Each library now records the pool size given at sceHttp2Init,
sceSslInit or sceNetPoolCreate and reports it with a peak and current
use of 0: the offline Http2 and Ssl models and the host-socket Net
library allocate nothing from these pools. The layouts follow the PS4
SDK's *MemoryPoolStats structures. Net returns EBADF for an unknown
pool and EINVAL for a null output, like its other pool calls; Http2 and
Ssl throw for both, as their error codes are unknown. Terminating a
context or destroying a pool forgets its size.
sceHttp2SetResolveRetry, which the title calls next, accepts and
ignores its value like the other Http2 settings. The four Http2
WebSocket exports the title imports are added as unimplemented.
guest_http2, guest_ssl and guest_sce_net check the reported sizes;
guest_sce_net also checks the null-output and unknown-pool errors.
A program's entry EXEC is the ballot of !gl_HelperInvocation in a pixel program and the ballot of true in vertex, local and tessellation programs (emitEntryPrologue). The half-wave reduction matcher only accepted the pixel form, so a wave64 vertex program that scans keys selected under its EXEC and reads lane 63 threw that the lane is outside the 32-lane host subgroup.
EntryLaneWalk now also accepts a ballot of true as within the entry EXEC for those stages. Every invocation is live at their entry, and a later ballot of true holds only invocations that are still active. Pixel programs keep the !gl_HelperInvocation form, because a ballot of true there includes helper invocations. Compute and mesh programs keep their own form too, because their entry EXEC can bound the local invocation index.
A draw's guest buffer region inside a host import at an offset off
minStorageBufferOffsetAlignment was copied per build (a new buffer, an
input snapshot, a vkCmdCopyBuffer that ends the open render pass), and
DirectRegions refused such builds as templates.
Descriptor already binds any DWORD-aligned view from the aligned offset
below it and passes the remainder through the shader's memory offset
slot. A draw build that is not address-based and whose every guest buffer
element has such a slot (a push constant byte, or a shader data byte
inside its data buffer) now binds a region whose offset in its import is
DWORD aligned in place, like an aligned one: its read-only views are
snapshotted per draw by PrepareDrawBindings at address - adjustment, the
import serial keys the template, and Revalidate proves it per use.
Dispatches keep the copy (they read in-place inputs at execution, not at
record).
agc_driver_recorder_tests misalignedRegionTests: a misaligned read-only
draw region is read in place, the build is reusable, the push constant
carries the adjustment, and the per-draw snapshot starts at the aligned
offset below the view.
CachedPipeline keeps at most 256 pipelines. A level with a larger working
set thrashes the LRU: every miss creates a new VkPipeline (modules,
layout, render pass and a driver compile from the pipeline cache). The
bound is now 1024. A cached pipeline is a pure function of its key, so
the bound changes only memory and time.
The half-wave reduction read (v_readlane_b32 of lane 31 or 63 after a DPP row scan) is exact on a narrower host subgroup only when the scan's keys are the identity outside the entry EXEC. EntryLaneWalk::ZeroBit now also proves that through a constant false, a SelectU1 whose two values both prove it, and a bool Phi whose inputs all do. A mask written inside a divergent branch reaches the key select in that form, so such a scan threw that lane 63 is outside the 32-lane host subgroup.
A draw with DB_RENDER_CONTROL.RESUMMARIZE_ENABLE recomputes the HTILE summary of the bound depth surface from its depth values. HTILE is not modeled, so such a draw with depth and stencil tests off and no color writes changes no modeled state. It now passes the maintenance check and draws nothing, instead of throwing. Resummarize draws with depth, stencil or color work, and copy and decompress draws, still throw.
ctest loads the libraries from libs/unpatched, so an import that nid_patcher
renames out of another library's exports only shows when a title loads the
patched prx. patched_libraries loads each patched .prx with LoadLibraryEx on
Windows and dlopen(RTLD_NOW) on Linux, and depends on the libs target so the
patched files are current.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The unified and service entitlement info list requests and polls threw.
They now return SCE_NP_ERROR_SIGNED_OUT, like the consume requests in
the same library. Their signatures are unknown, so they stay declared
without parameters.
PPSA15657 calls the unified request during start-up and continues past
the error.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0134RWCyiyydJBbfKz8g9Bwe
Sackboy: A Big Adventure (PPSA01289) imports nine functions that no library
exports, so eager binding fails before it starts.
- libSceVoiceChat: sceVoiceChatRequestLeaveVoiceChatGroup and
sceVoiceChatRequestDeleteVoiceChatGroup, offline requests like the other
sceVoiceChatRequest* calls
- libSceNpSessionSignaling: sceNpSessionSignalingGetConnectionFromPeerAddress,
a throwing stub; the signature is unknown
- libSceCdlgG2P, a new library: the six sceG2PDialog* functions, throwing
stubs declared without parameters
TechnicalDebt records the assumed and unknown signatures.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The param is void*, as RommPS declares it. The test also passes a null and an empty URI and a param that must stay untouched, and compares against 0x8002002D itself. The debt entries now say the error is returned for any arguments and that the param layout and the console's argument checks are unknown.
PPSA01289 (Sackboy: A Big Adventure) calls sceAudioOut2MasteringTerm when it
replaces its world, which threw. Like Init and SetParam it has no effect, as
there is no mastering stage. The title creates its next world's audio device
before it destroys the previous one, so Init runs twice before Term runs
twice; whether the console reports an error for either is unknown, so both
return 0 and the case is recorded in TechnicalDebt.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceSystemServiceLaunchWebBrowser fails with the kernel's EOPNOTSUPP: there is no web browser to open. RommPS PPSA76677 stops on main at this import and shows a QR code instead when the browser does not open.
sceAmprCommandBufferClearBuffer detaches a command buffer's memory and
returns it so the caller can free it. It returned 0. PPSA01289 (Sackboy: A
Big Adventure) frees the result after every streaming read, so each read
leaked its 16 KiB command buffer until the allocator ran out of memory
during the first world load.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With depth bias enabled, RADV sets POLY_OFFSET_PARA_ENABLE in
PA_SU_SC_MODE_CNTL next to the front and back enables, whatever the
topology; vkQuake's draws carry 0x3a46. The bit applies the offset to
points and lines, so it changes nothing for triangles, and Vulkan applies
depth bias only to polygons. It is now accepted with depth bias on
triangle topologies; point and line draws with it are still rejected.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A label in memory the device has no host import of was stored when its
batch was reaped, after later labels the GPU had already made visible.
A title that reused that memory saw its next command buffer overwritten
by the stale value (#1458). Wait for the recorded work and store it then.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A title that replaces the allocator through its libc parameters also
supplies malloc_stats_fast in that table, and PPSA28997 calls it. It is
now forwarded to the title's function. Without a replacement allocator
that provides it, the call throws.
Without TZ, glibc's localtime and mktime run tzset on every call and
re-check /etc/localtime each time. PPSA28997 calls them every frame
during its intro, which then spent most of its time there. TZ is set
to :/etc/localtime when unset, so the zone is read once.
With f32 denormals flushed, as in the 0xc0 float mode titles use, the
hardware flushes the denominator and numerator of v_div_fixup_f32 but
keeps a denormal quotient and result. The recompiler kept all three,
so a denormal divisor gave the quotient back instead of an infinity.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
PrepareMultisampledStorage only inspects the SPIR-V that the recompiler
emits for multisampled storage writes and atomics, but it took its
target from the device, so on a device without
shaderStorageImageMultisample, such as MoltenVK, the emission failed
and agc_driver_prepared_shaders failed in all its variants. The target
now has the StorageImageMultisample capability, and the check that a
target without it is refused still removes it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PixelProgramSkipped only treated SPI_SHADER_PGM_LO/HI_PS written as 0 as
a draw without a pixel program, while the draw decoder already reads an
unwritten address as 0 and binds the null pixel program for it. A
depth/stencil-only draw whose pixel program address was never written
was instead rejected by the precheck with "depth/stencil-only draws
without a pixel shader are not implemented".
An unwritten address is now unset in PixelProgramSkipped too, so such a
draw goes through the null pixel program like one with the address
written as 0, and one that writes color is rejected as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With IEEE=1, v_pk_min_f16 and v_pk_max_f16 return a quieted NaN for a
signaling NaN operand, and DX10_CLAMP=1 clamps it to +0. The clamped
form skipped the bit-level NaN selection and saturated the driver's
min/max, which returns the other operand, so it gave that operand.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Sonic Origins (PPSA05324) imports sce::Json::Object::empty() const
(i2l3IYvQ9UE) and fails to load without it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sceSystemServiceKillApp now ends the title as guest exit() does, so driver calls that wait for the exit park instead of throwing ProcessShutdown, and a second exit waits for the first. The test now fails when the kill returns, exits with a status other than 0 or bypasses the coordinator, and checks why a rejected call throws.
Report an app ID for the running title from sceSystemServiceGetAppIdOfRunningBigApp, and shut the guest down and exit when sceSystemServiceKillApp receives that ID with the arguments -1, 0 and 0. SwanStationPS5 (PPSA98510) 1.0.6 closes itself this way and stops on both imports on main.
ReadRawComputeShader decodes a growing prefix of guest memory and reads
more when the decoder throws std::out_of_range, which the vector, scalar
and instruction-length checks throw for an instruction cut off by the end
of the span. The memory (SMEM, DS, MUBUF, MTBUF, FLAT), image and export
decoders threw std::runtime_error instead, so a two-word instruction
across a window boundary failed the read: vkQuake terminated on "truncated
FLAT instruction". These now throw std::out_of_range too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The hardware findings give 0xF_F000_0000, which is 0xFF0000000, not 0xFF00000000. Check the ring across threads, at ps5-opengl's size of 0x1b000 and with a canary after the outputs, and say in the debt entry that nothing is written to the ring.
Record the tessellation factor ring that sceAgcDriverSetTFRing receives and report it from sceAgcDriverGetTFRing, with the console's default of 0xFF00000000 and 128 KiB before a title sets one. VITA5 (PPSA39410) and Homebrew UI Lab (PPSA99050) stop on this import once sceVideoOutGetResolutionStatus resolves.
v_sin_f32 and v_cos_f32 take their input in cycles, and the SPIR-V
backend reduced it with GLSL Fract before scaling by 2*pi. For x in
(-0.5, 0), fract(x) = 1 + x is rounded to a multiple of 2^-24, which
drops the low bits of x, and for |x| <= 2^-25 it rounds to 1.0, which
RADV's v_fract_f32 clamps to 0x3f7fffff. On RADV, v_sin_f32(-2^-30)
returned 0xb4c90fde (-3.7e-7) where RDNA3 returns 0xb1c90fd5 (-5.9e-9).
The cycle is now x - roundEven(x). It lies in [-0.5, 0.5] and is exact
for every finite x: it is x when roundEven(x) is 0, and Sterbenz's
lemma applies otherwise. Finite inputs of magnitude 2^23 or more still
reduce to +0, so the select that forced them to 0 is removed. Infinite
and NaN inputs are unaffected: the translation selects their results
on the input bits.
Measured on RDNA3 (gfx1101) with the hardware oracle, with the same
results in all 16 combinations of IEEE, DX10_CLAMP and the f32 and f16
denormal modes: over 544 inputs, v_sin_f32(x - v_rndne_f32(x)) returns
the bits of v_sin_f32(x) for 519 and is at most 11 ulp off, while
v_sin_f32(v_fract_f32(x)) matches 403 and is more than 16 ulp off for
80, all of them in (-0.5, 0). For v_cos_f32 the counts are 523 (at
most 7 ulp off) and 484 (6 more than 16 ulp off). On RADV the
recompiled shader returns the bits of the v_fract_f32 sequence before
this change and of the v_rndne_f32 sequence after it, for all 544.
v_sin_f16 and v_cos_f16 share the reduction. On RADV, with f16
denormals kept, v_sin_f16 now returns the RDNA3 bits for all 65536
inputs, where it was 1 ulp off for 6 small negative ones, and
v_cos_f16 matches for all of them before and after.
An image with several runtime modes is emitted as a switch whose default
and disabled modes end in OpUnreachable. In a loop latch that exit breaks
the continue construct, and spirv-val rejected the shader. Inside a
continue target those cases now join the merge with an undefined result.
Fixes#1921
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
v_ldexp_f16 read src1 with readU32, so a float inline constant gave
the low 16 bits of its f32 pattern: 0 for 0.5, 1.0, 2.0, 4.0 and
their negatives, which returned src0 unchanged, and 0xf983 for
1/(2*pi), which clamped to -64 and returned a signed zero. The
hardware treats src1 as an f16 operand, as LLVM's
LDEXP_F16_VOPProfile in VOP2Instructions.td records, so it receives
the f16 encoding: 0.5 is 0x3800, an exponent of 14336. Measured on
gfx1101, ldexp(1.0, 0.5) is +inf and ldexp(1.0, -0.5) is +0.
Read VOP3 src1 through readF16SourceBits, which maps the float inline
constants to their f16 bits and reads other operands as before.
Registers, integer inline constants and literals already matched the
sign-extended low 16 bits the hardware uses. SDWA src1 keeps its old
read, as gfx11 has no SDWA to measure it on.
With VCC set and a src2 exponent field of 128 or more (scale 2^64),
v_div_fmas_f32 recomputed an fma result with exponent field 0 as an
fma of the smaller multiplicand times 2^64, the other multiplicand and
src2 * 2^64. For |src2| >= 2^64, src2 * 2^64 overflows to infinity, so
an fma that cancels exactly returned -inf or +inf. The hardware
returns +0: measured on gfx1101 for (1.0, FLT_MAX, -FLT_MAX) and
(1.0, -2^100, 2^100) in all 16 combinations of IEEE mode, f32 and
f16/f64 denormal mode and DX10_CLAMP.
Only exact cancellations reach that path. With |src2| >= 2, a nonzero
sum below 2^-126 needs a product above 1, which is a multiple of
2^-47, while src2 is a multiple of 2^-22, so a nonzero sum is at least
2^-47 and the fma result is normal. The scaled result is then the fma
result times 2^64, +0 for a cancellation. The recomputation is now
done only for the 2^-64 scale, when the fma result overflows.
FmaRounding checks v_fma_f32, v_fmac_f32, v_fmaak_f32 and v_fmamk_f32
against the fused result on the GPU, but not v_fma_mix_f32. It now
also runs v_fma_mix_f32 v20, v4, v5, v6 with all sources f32 on its 64
rows and expects the fused value, in the group that is skipped when the
device splits fma.
fastTexture took the Textures() cache entry of a stage-A image record
without asking whether a depth surface is resident at the address. The
full lookup in cachedTexture returns DepthSurfaceTexture first; the fast
path skipped it, so once a guest-memory entry existed for a depth
target's descriptor, every later sampled read got that entry and saw
the guest memory under the target, which a depth target never writes.
The fast path now returns nothing while a depth surface is resident
there, and the lookup falls back to cachedTexture.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UTjpbMX7BUFMZZaxJE98zV
Use the decoded opcode offset when checking forbidden system calls.
Add synthetic regressions for prefixed forms, immediate decoys, and the skip option.
AI-assisted: Codex. The submitting author is responsible for every changed line.