Conflicts resolved:
- libSceAgcDriver CMakeLists.txt: main moved test registration into tests/cmake/<exe>.cmake (tools/agc_test_split.py resolve); this branch's nested image table tests now sit in agc_driver_image_table_tests.cmake, and its nested snapshot retry test sits next to main's descriptor phis test in agc_shader_memory_tests.cmake.
- ResourceMaterializer.cpp: table equality compares both this branch's nested/pointers and main's resourceViews; the footprint counts main's resource views and this branch's pointers.
- ShaderDiskCache.cpp: both sides grew the descriptor source record (this branch: TableColumn::buffer, 1 byte; main: requiredBlocks, an 8-byte count), so the decoder minimum is 72 + 1 + 8 = 81, and the field-count asserts are DescriptorSource 4 and TableColumn 19.
- ShaderDiskCache.hpp: FormatVersion 56, an unclaimed number (main has 52; 53, 54 and 55 are already set by other open PRs).
- ResourceMaterializer.cpp (semantic, no text conflict): main added the per-image resourceViews loop to Snapshot(source, table), and it uses the top-level table index; this branch moved that copying into CopyRecords(), which nested tables share and which has no table index. The loop now runs in Snapshot right after CopyRecords, as on main; nested tables carry no resource views, as before.
A 3D sample of the same image through another sampler no longer feeds the gather's
state, z modes 4 and 5 no longer count as border addressing, a null image no
longer divides by a zero extent, and the test covers per-axis modes, rounding
ties, denormals, null images and the vertex stage.
NVIDIA's front end spends tens of seconds per large compute program on the
dynamically indexed Function arrays that held the per-lane scratch. A compute
program that uses scratch (scratch_* or a FLAT access that the aperture split
routes to the private aperture) now keeps it in a storage buffer instead, so
no Function array is declared or indexed. FLAT routing is unchanged.
The driver allocates one buffer per compute pipeline on its first dispatch,
capped by APS5_SCRATCH_POOL_MIB (default 64), holding a lock word and an
invocation counter per slot followed by the slots, and binds it in descriptor
set 2 (the layout of the device-memory LDS buffer). The shader takes the slot
count from the array length. Invocation 0 of each workgroup claims a slot with
a compare-exchange scan that starts at its linear workgroup id and spins until
it wins one; a barrier publishes the slot. Every invocation decrements the
slot's counter on every compute exit and the last one frees the lock, so
divergent exits and concurrent dispatches of one pipeline share the buffer
safely. The emitter fails the compile if a compute return lacks the release.
Programs without scratch keep their SPIR-V and pipeline layout.
The buffer is decorated Coherent: without it NVIDIA lost earlier byte stores
of a sequence of byte read-modify-writes to one dword. Lane-private
read-modify-writes no longer use atomics.
Tests: a new execution test covers the private aperture reached through a
table load, a literal and a computed address (store, atomic, and both ways
through scratch_*), a callee reached through s_swappc_b64, isolation of 64
workgroups over one or two slots, divergent wave exits with a second dispatch
reusing the slots, and the module shape (no Function array, no binding in a
program without scratch). The guarded memory load fixture skips the slot
acquisition.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Null buffer descriptors still split modules on unused specialization
fields after buffer presence moved to run time. Learn the first valid
bound fields per slot and use them for absent descriptors, retaining
actual runtime presence and independent bound formats.
Snapshot the learned fields once for result memo keys and materialization
so identical null captures cannot retain a module made before learning.
Cover direct, captured and prepared calls, null stores, and changed formats.
Keep the process-local table and an off switch for main's module selection.
AI-assisted: yes
The conflict in core/libs/prx/libSceAgcDriver/CMakeLists.txt from #12862's split is resolved with tools/agc_test_split.py resolve, which moves this PR's test registrations into tests/cmake/.
ResourceTracker masks the adjust bit before deciding cubeFaceSample, so the 2D
MSAA plane read takes image_sample_a. That predicate also feeds the cube-face and
2D-array-through-3D modes, which nothing has measured with _a. An image now
records adjustedSample when any of its samples sets the bit, and those two modes
refuse it. Disk cache format 50 for the new ImageResource field.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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 MinGW std::filesystem::last_write_time reads and sets file times
through the UCRT stat and utime calls, which convert through local time
using TZ. With a TZ other than the Windows zone, including the one libc
applies from the guest environment, times shifted by the difference:
under TZ=IST-5:30 on an Eastern host a temporary written that second
was 9.5 hours old, so housekeeping deleted it, and a TZ west of the host
set the active cache stamp hours in the past. Housekeeping now reads
times with GetFileAttributesExW, refreshes the stamp with SetFileTime
and compares in system_clock. The housekeeping test runs its child
with a TZ 12 hours from the host zone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A sampled 1D array view with at least maxImageArrayLayers slices, read
only by image_sample_lz or image_sample_l, gets a runtime image mode that
binds a 2D image holding each viewed slice in two adjacent rows. The
shader rounds and clamps the layer as Vulkan array layer selection does
and samples on the boundary between the two copies, so filtering never
reaches another slice. Other uses keep throwing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A shader with an image table map read its fault buffer back only when a
table record was poisoned, an SRT read was poisoned, it had window lane
guards or the loop guard was on. A DescriptorFault only set the fault
buffer, so with a valid table and no copied write nothing ran the fault
check, and a lane that took a folded descriptor web path or selected an
undefined arm read arm 0 or zeros with no report.
The compiled artifact now counts its DescriptorFault instructions
(descriptorFaults, stored in the shader disk cache, format 48), and the
readback gate requests the fault check when it is nonzero. The result a
capture materializes copies the count, and also windowLaneGuards, which
that copy had dropped, so the gate's window lane guard condition never
saw it. sizeof(CompiledShaderArtifact) is 288 and sizeof(RecompileResult)
552 with the new field.
agc_driver_descriptor_web_faults adds a wave32 shader that reads an image
table and then the descriptor web, storing only to LDS so no copied write
forces completion: it binds an image table and no BDA address table, a
valid arm and an unselected undefined arm stay clean, the folded path and
a selected undefined arm fault at the read, and nothing faults with EXEC
empty. Without the gate change the folded case does not fault.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Resolve the overlap with main's WindowLaneGuard and image-resource changes
by keeping both sides: DescriptorFault sits next to WindowLaneGuard in the
opcode list, metadata and side-effect switch, and its emitter next to
EmitWindowLaneGuard; optionalDescriptor is serialized after
remappedDepth1D; undefinedImageWords follows the new image helpers; the
descriptor web debt entry follows main's new entries.
The ImageResource field-count assertion is derived from the merged
structure (44 fields: main's 43 plus optionalDescriptor). sizeof
(ImageResource) stays 96 and sizeof(MemoryInfo) stays 80, so those
assertions are unchanged. The undefined-format test keeps its captured
regions in a variable, as MemoryRegionView no longer binds a temporary.
Co-Authored-By: Claude Opus 5.5 <[email protected]>