Goat Simulator 3 draws a triangle list whose indices are all 0xffffffff
with primitive restart off. Each fetch past a descriptor's records reads
zeros (out-of-bounds select 0 or 1), but the zero-padded vertex copies
were sized to the highest index: 160 GiB, which vkAllocateMemory refused.
When the highest index is at least 64Ki vertices past the largest
per-vertex record count, the draw now uses a copy of its indices with
every index past that count moved to it. Each attribute reads zeros
there as at the original index; only the vertex index the shader sees
changes, so indices closer to the records are left as they are.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An image whose results stay on the GPU is never written back, so
APS5_DUMP_STORAGE saw nothing; APS5_STORE_AT_FLIP=1 stores every pending
image and ran a 4 GB device out of memory at 3840x2160. The selected
images alone are now stored at each flip.
An indexed draw that binds its index buffer in place from a host import
(#2253) scans the bound range for its highest index on every draw, where
the copied path reuses the value kept with its snapshot. A draw of many
indices pays for the scan each time, and an indirect draw, which binds
the whole declared index buffer, scans the whole buffer per draw.
The recorder now keeps the highest index of an in-place index range of
4096 indices or more with the write generation collected before the
scan, and a draw takes it while the range is unchanged since
(GuestMemory::UnchangedSince and the allocation registry generation, as
for a reused snapshot). A smaller range is scanned at every draw as
before: the scan costs 0.4 ns per index and the write collect and lookup
0.2 to 1.4 us per draw, so the scan is the cheaper of the two below
about 3,000 indices, as #2253 measured on small ranges.
A multisampled target's samples are only on the host, so its guest memory
never holds them: a CPU read through GuestMemory::Read/ReadCommitted, a
DMA_DATA copy from it and a texture or storage image lookup at its address
throw instead of returning bytes that were never written there. Asked by
oneandonlydean; the test reads a resolved-only target's memory each way.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The duplicate ColorTarget::cmaskAddress, decode and size helper are gone: multisampled targets
use main's cmaskAddress/cmaskBytes (whole metablocks). Single-sample targets keep main's rule
(CMASK under FAST_CLEAR); a multisampled one takes it under FAST_CLEAR or COMPRESSION. A zero
fill of the whole CMASK clears at the next use, 0xFFFFFFFF marks it expanded, any other or
partial fill refuses the next use; eliminate/DCC passes over a multisampled target and a CB
resolve into a fast-clear destination are refused. The draw key adds CMASK_BASE_EXT (0x398),
which main decodes but did not key.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Draws with PA_SC_AA_CONFIG multisampling (2, 4 or 8 samples, every target with as many
fragments, the standard sample locations, MSAA_ENABLE) render into host-only multisampled
color images and multisampled depth surfaces; the pipeline rasterizes with that sample
count. A CB_COLOR_CONTROL resolve over a multisampled CB0 runs as a render pass whose
resolve attachment is CB1's resident image. A zero fill over a target's CMASK fast-clears
it to the CB_COLOR_CLEAR_WORD values of its next use. EQAA, coverage conversion,
nonstandard sample locations, sampling a multisampled surface and multisampled mips stay
refused.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A pixel input whose SPI_PS_INPUT_CNTL offset names a parameter the
previous stage does not export failed validation ("graphics interfaces
disagree"), so the draw threw. No export writes that parameter slot for
the draw. radeonsi programs a pixel input without a matching vertex
output as OFFSET 0x20 with DEFAULT_VAL 0, so that it reads (0, 0, 0, 0)
(SI_PS_INPUT_CNTL_UNUSED in si_shader.h, applied in si_shader.c), and
rect-list draws already pass zero for such inputs.
Before the pixel stage of a draw is prepared, inputs that read a
parameter the previous stage's program does not export now read zero:
OFFSET bit 5 is set, DEFAULT_VAL and FLAT_SHADE are cleared, and an FP16
input also reads zero in its high half (USE_DEFAULT_ATTR1 set,
DEFAULT_VAL_ATTR1 cleared). Inputs that already read a default value
are unchanged.
Fixes#2975.
A color view of slice N of a 2D array (CB_COLOR_VIEW SLICE_START, with
CB_COLOR_ATTRIB3 MIP0_DEPTH as the last slice) was rendered as a 2D
surface at the slice's address. The swizzle equations of the XOR tile
modes take the slice index (the detiler's slice bits), so the texels of
slice N of an array are not where a 2D surface at that address puts
them: a title that renders into array slices and then samples the array
read them scrambled.
The decoded target now keeps the array's address, its last slice and the
view's slice. A resident target that is a slice of an array attaches
that layer of the array's storage image, so the texels follow the
array's layout; storage images of 2D arrays are color attachments for
this, and an attachment view selects a layer. A slice whose array has
DCC, CMASK or an attachment proxy has no resident array image, and a
non-resident slice of an XOR-tiled array now throws instead of being
written in the 2D layout.
In #3338, Ratchet & Clank: Rift Apart (PPSA01474) hits a 36 GB / 5 s
memcpy stall at frame ~380 right after intro logos because the GPU
worker thread is blocked copying multi-gigabyte storage buffers on every
draw snapshot miss (specifically a 3.01 GiB streaming range
0x118f800000+0xbc200000 and a 232 MiB range 0x1171258040+0xe7fffc0
across 130,000 draws).
ShaderResources::PrepareDrawBindings now caps both per-allocation and
cumulative draw input buffer snapshot copies at 64 MiB by default,
overridable via APS5_DRAW_SNAPSHOT_MAX_MIB. Inputs exceeding the threshold
skip snapshotting and bind directly in place, preventing multi-gigabyte
memory copies while preserving standard small uniforms and draw arguments.
Budget allocation prioritises earlier descriptor bindings in deterministic
pipeline layout order.
Adds drawSnapshotCapTests in core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
verifying individual and cumulative capping, cross-platform cache behavior on
watched and compared hosts, and direct observation of subsequent CPU writes
on in-place bound ranges exceeding the cap. Exercised via --draw-snapshot-cap-only
registered as agc_draw_snapshot_cap in libSceAgcDriver/CMakeLists.txt.
A merged (NGG geometry path) program's user data starts with eight
hidden words, so the vertex buffer and attribute table offsets of its
AGC header, which count from the first user SGPR, were read eight words
too early and found zeros. Killzone: Liberation (PPSA11142) stopped on
`AGC graphics: null vertex buffer table address` at the first frame of
its PSP game. The decoder now adds the hidden word count to the table
reads; the registers reported to the recompiler are unchanged, as it
already maps them from the first user SGPR. `agc_driver_graphics` gains
a merged and a plain layout case.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review follow-up to the 8_8 SWAP_ALT blending. The second channel of
such a target holds the guest's alpha. Vulkan applies the green blend
constant to it for CONSTANT_COLOR and ONE_MINUS_CONSTANT_COLOR, where the
guest's alpha equation uses the alpha constant. Decoding now throws for
these factors on a SWAP_ALT 8_8 target, unless the equation is MIN or
MAX, which ignore factors.
The second pass of a split draw runs every stage again, not only the
pixel shader, so a vertex or geometry stage that writes memory would
write twice. The memory-write check (now ShadersWriteMemory) covers all
the draw's stages.
On an RTX 4060 (Ada, driver 617.42) the optimized link of a pipeline built
from libraries loses the device (VK_ERROR_DEVICE_LOST) as soon as it is
bound, which fails agc_driver_pixel_valid_mask in 16 of 20 runs. With
APS5_NO_PIPELINE_LTO=1 it passes 20 of 20. Reporters see the same loss on an
Ampere card with a depth attachment; two Blackwell cards do not lose it.
A device whose driver is VK_DRIVER_ID_NVIDIA_PROPRIETARY keeps the
fast-linked pipeline and no longer queues the link-time-optimized relink.
Other drivers are unchanged.
Refs #2866
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>