On NVIDIA's proprietary driver, function arrays that live in local memory
and are read under a data-dependent branch return wrong values in
subgroups 1-3 of a mesh workgroup; subgroup 0 is correct. A wave64
geometry program whose relative addressing keeps its VGPRs in the vector
window therefore loses register writes in every wave but the first. In
the clipping geometry program of Sony's PS5 PSP emulator (PPSA07550) the
empty waves never cleared their candidate primitive slots and wrote
degenerate triangles over wave 0's, so the 3D menu drew spikes.
On that driver a mesh program's window now lives in a device-local
storage buffer of 256 workgroup slots of 65536 dwords, claimed through a
per-slot lock and released after the final barrier. The pool is created
before the first draw that uses it records anything, and its header is
zeroed in a batch of its own that is submitted and waited for; the pool
is published only after that wait, and a failure releases it. Direct
register accesses keep one function variable per register; a direct
store also writes the buffer when a buffer read can see it, relative
reads load the buffer, relative writes store only to it, and a direct
read that may follow a relative write loads the buffer and refreshes the
variable. A buffer entry is zeroed only when a buffer read can see it
before a write. APS5_NO_MESH_WINDOW_BUFFER=1 keeps the arrays.
The opening paragraph of docs/dev/ENVIRONMENT.md lists the calls through
which the code reads environment names, and so the reads that the
Conventions check holds to a row. It omits _wgetenv with a wide literal.
tools/env_reference.py has recognised that form since 65da8ac106, and the
code reads nine names through it on Windows, among them
ANYPS5_SHADER_CACHE_DIR, ANYPS5_SCREENSHOTS and APS5_DUMP_SUBMITS. The
paragraph now names _wgetenv beside GetEnvironmentVariableW.
AI-assisted: yes
List the controller vibration strength variable in the libScePad table with its default of 100, the paths it covers, the adaptive trigger effects it does not, and the error an invalid value raises.
cacheDrawStages never admitted a draw an indirect packet runs on the CPU
path, so a title whose draws are mostly DRAW_INDEX_INDIRECT packets with
a patched user word the vertex program reads as a value missed the draw
cache on nearly every lookup whatever its size. On Demon's Souls 97 % of
the draw packets are such draws and the hit rate stayed at 9 % with
APS5_DRAW_CACHE_ENTRIES=32768.
APS5_DRAW_CACHE_INDIRECT admits them. A hit reuses the fragment stage
and skips the first vertex pass; the stage whose user word the record
patches is still re-materialized per record, as the hit path already
provided for.
Tested: agc_driver_draw_cache_reuse_indirect (three DRAW_INDIRECT draws
patching an unfolded user word: one insert and one hit with the knob,
none without), the other draw_cache_reuse, key_miss, register_key and
draw_cache_relocation tests on lavapipe and an RTX 5070 Ti.
A comma-separated list of hex program addresses (optional 0x prefix) now
writes only those programs' requests, so one expensive shader can be
captured without serializing every request a title prepares. Any other
value, such as 1, keeps writing every request. A filtered-out program
returns no name, so diagnostics never point at a file that was not
written, and it stays eligible for a later list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
libSceSystemService reads the variable when it prepares a launch, and an
empty value counts as unset; GuestSystemExit.cpp only checks that it is
not defined.
FrameRecorder reads the variable with a fallback of 0 and raises it to 1
when it picks the boundary, so the effect of the default is the same as
1 but the default is 0; a value that is not a number throws.
HostVolumes records ReadOnly from the mount options (or the Windows
read-only volume flag) and the export reports it, so passthrough is not
always writable.
APS5_NO_PERF_FRONTEND_PAIR and APS5_NO_PERF_LAYOUT_PAIR are read by the
shader recompiler itself, which the shader disk cache lists among its
output-neutral switches, not only by the shader-memory test the rows
pointed at.
TextureFormat.cpp compares the value with "1" and "copy" and ignores
anything else, so setting it to an arbitrary value does not behave like
a presence switch.