Commit Graph

1757 Commits

Author SHA1 Message Date
Dean 417c7d02a7
Merge pull request #4586 from ee6aba54/fix/agc-lookahead-write-data
fix(agc): count every CP data write of a dispatch group as a write of the run
2026-10-13 15:37:38 +13:00
Dean 255a91abd6
Merge pull request #4809 from MarcZX100/codex/unaligned-shader-abi-4782
fix(agc): accept unaligned shader headers in ABI resolution
2026-10-13 15:36:40 +13:00
Dean 834de1ab10
Merge pull request #4199 from sanxchep/fix/msaa-guest-guard
fix(agc): size multisample guest guards by fragment blocks
2026-10-13 15:36:14 +13:00
Dean 37dd483084
Merge pull request #4468 from GustavoGamarra95/perf/agc-capture-one-sync-per-run
perf(agc): wait once per unit run when a storage image captures its guest bytes
2026-10-13 14:54:24 +13:00
Dean 2a55cfe1e8
Merge pull request #2491 from oneandonlydean/up639/depth-plane-unchanged-copy-on-425
perf(agc): skip the depth plane copy when no slice changed
2026-10-13 14:49:05 +13:00
Dean 26f8047889
Merge pull request #2490 from oneandonlydean/up639/depth-plane-guest-layers-on-425
perf(agc): keep unchanged guest layers of a depth plane copy
2026-10-13 14:48:43 +13:00
Dean 048206e18c
Merge pull request #4410 from MarcZX100/codex/idle-results-debounce-4398
fix(agc-driver): delay storage writeback until queue activity settles
2026-10-13 14:48:00 +13:00
Dean c3625c4789
Merge pull request #4120 from tibzejoker/fix/vector-window-direct-ssa
fix(shader): keep direct VGPR accesses in SSA when the window has only relative reads
2026-10-13 14:47:39 +13:00
Dean Galvin 50e5f088c9 perf(agc): skip the depth plane copy when no slice changed
Sampling a depth array, cube or R16_UINT view of depth surfaces went
through DepthPlaneCopy::Refresh on every use. Each use copied every slice
a depth surface holds into the staging buffer and the whole staging buffer
into the copy image, with three ALL_COMMANDS barriers, although nothing
had written the slices since the previous use.

Each depth surface now carries a write serial, bumped when a draw binds it
as its depth target and when a pending fast clear is applied. The copy
keeps the image and serial of every slice it copied. A use copies only the
slices whose surface or serial changed and the guest layers that changed
(as before), and records nothing when none did. A slice whose surface is
the depth target of the latest depth draw is copied at every use until
another depth target is bound, since that draw may still be recorded after
the copy.

Test: agc_driver_depth_plane_guest_layers counts the recorded slice copies
and copy fills; a second use with nothing written records none (it records
one of each without this change), a draw to a slice's surface or a CPU write
to a guest layer copies only that layer.
2026-10-13 14:33:40 +13:00
Dean 0c3dc9ca2b
Merge pull request #4381 from Celegans12/fix/agc-aliased-buffer-views
fix(agc): accept aliased views of the guest buffers in graphics shaders
2026-10-13 14:31:53 +13:00
Dean Galvin d9c0407391 test(shader): make the read-only window tests pass in the full Linux build
With SPIRV-Tools enabled the optimizer replaces a window that is only
indexed by constants with scalars, so the sampler spill case found no
array. It now reads M0 from the thread group id, as the first case does.

The direct access test reads and writes lanes across a wave32, which a
device with 8-lane subgroups cannot hold; it skips there like the
v_readlane and v_writelane tests.
2026-10-13 14:09:05 +13:00
thibaut c87f3771d1 test(shader): let direct SSA mirrors of a read-only window pass the stage input check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 13:59:35 +13:00
thibaut efa96f73bc test(shader): check direct and relative reads of a read-only vector window on the GPU
Write window registers directly (VALU, v_writelane_b32, buffer load,
v_movrels_b32 results), read them back directly and through
v_movrels_b32 at several M0 values, including after a direct rewrite,
and feed a v_readlane_b32 of the written lane into the output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 13:59:35 +13:00
thibaut ebf939ff10 test(shader): cover a sampler spilled through a read-only vector window
A compute program reads v4 relatively with v_movrels_b32, spills sampler dword
0 into v4 with v_writelane_b32, restores it with v_readlane_b32 and samples
with it. On main the restore reads the window array and resource analysis
refuses GetSamplerResource with ReadLane(LoadVectorWindow(Immediate),
Immediate).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 13:59:35 +13:00
Sanxchep Sharma 8bcf1cc0df fix(agc): size multisample guest guards by fragment blocks 2026-10-13 02:52:51 +02:00
Gustavo Ariel Gamarra Rojas e260f24939 test(agc): refuse committed reads of a multisampled target's memory
The multisampled target guest access test now also reads the target's
memory through ReadCommitted and ReadCommittedUnsynced and expects both
to throw.
2026-10-12 21:41:23 -03:00
Dean Galvin c52321fab5 fix(shader): move the primitive class to request version 19 and compose the ancillary word with render target index 0
main reads target.vertexSubgroups at request version 18 since #4470, so
reading the pixel primitive class at 18 as well read a version 18
request written by main one byte short. The class is now written and
read at version 19. agc_shader_memory loads a version 18 request as
main writes it, and it fails with "truncated data" when the class is
read at 18.

The composed word no longer reads the Layer builtin for bits 16-28.
Draws render to a single layer and guest layer exports are ignored, so
no stage writes Layer, and AMD's Windows driver drops the writes of a
fragment shader that reads it unwritten (#4800). The field is the
constant 0 and pixel_ancillary checks that the composed program has no
Layer read. The debt entry names the bits that have no source and are
assumed zero (2-7, 12-15, 29-31), and says that a change which makes
draws export a layer must restore the read.
2026-10-13 13:18:51 +13:00
Celegans12 d644c31855 fix(agc): accept aliased views of the guest buffers in graphics shaders
The SPIR-V emitter declares extra views of the guest buffers binding:
buffers_coherent for GLC or DLC accesses (#2369) and buffers_u64 for
64-bit atomics. Each is another StorageBuffer variable on the same set
and binding, decorated Aliased, which Vulkan allows.

ShaderValidation, which checks the stages of every draw, accepted one
StorageBuffer variable per binding with u32 elements only, so such a
graphics shader was refused with "duplicate SPIR-V resource binding".

A second variable is now accepted when the binding is the guest buffers
array and every variable on it is Aliased; an aliased view may hold u64
elements. Other duplicates are still refused. agc_driver_graphics
validates pixel shaders with a GLC store and a 64-bit atomic, and checks
that the same modules without Aliased are refused.
2026-10-13 02:15:38 +02:00
AlxcBTW d0aca69eff test(agc): a relative read of the whole ancillary word gets the composed word
The relative-ancillary test expected the whole-word read through
v_movrels to be refused, which was main's behaviour before the word was
composed. It now converts that read to float and exports it: the
composed word of a triangle draw is 2 (primitive class 2, sample 0,
layer 0), so the pixel is 255, with and without the implicit position
inputs in front of the ancillary VGPR. TechnicalDebt's relative
addressing entry no longer says that whole-word uses throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:06:27 +13:00
AlxcBTW 18d9874a06 feat(shader): give reads the masked-select pass keeps the whole pixel ancillary word
A packed ancillary word that still reaches an export, or any other read
the masked-select pass cannot drop, no longer stops the recompile. After
that pass every remaining use gets the composed word: the primitive class
at bits 0-1, SampleId at bits 8-11 and Layer at bits 16-28, with every
other bit zero. Those lanes do export the ancillary VGPR on the console,
so the word has to be right, not just present.

The primitive class (0 point, 1 line, 2 triangle, 3 rectangle) comes from
the draw. RasterPrimitiveOf maps rect lists to 3, line lists on the vertex
path to 1 and everything else the driver accepts to 2, because its
geometry and tessellation paths only output triangles. It reaches the
shader as pipeline specialization constant 8233, the way dual-source
blending does: a request field that is folded into the specialized
module and is part of the result memo, the draw key and the serialized
request (version 17).

Only shaders that read the class pay for it. ShaderInfo records the read
(disk cache format 28), and only those shaders put the class in their
module key, so a shader that reads just the sample or layer field keeps
one module for every primitive class. The draw sets the class only when
the pixel shader loads the ancillary VGPR, so other draws keep the draw
cache entries they have today.

The VRS rates (bits 2-5) and the eye id (bit 7) are 0 because the driver
supports neither VRS nor multiview. SampleId is 0 because the driver
rejects MSAA and per-sample shading, and Layer is 0 while it ignores
vertex layer exports. TechnicalDebt.md records the layout sources and
these assumptions.

Tests:
- pixel_ancillary builds an export to target 0 whose two channels select
  between a packed value and a phi carrying the word. It checks the word
  for each primitive class, sample and layer, checks that the three
  shapes the masked-select pass keeps now compile with the composed word,
  and that a program whose select the pass drops reads no class.
- agc_driver_pixel_ancillary_word exports the word from a draw and reads
  back 2, 3 and 1 for triangle, rectangle and line. A shader that reads
  only the sample field shares one module across the classes, and the
  whole-word shader gets one per class. It also checks RasterPrimitiveOf.
- agc_shader_memory covers the version 17 round trip, and
  agc_shader_disk_cache the new flag.

Both ancillary tests fail with the old refusal when the composition is
disabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:06:21 +13:00
Dean Galvin af7160475b perf(agc): keep unchanged guest layers of a depth plane copy
Sampling a depth surface as a 2D array, a cube or its integer bits
copies its planes into a per-use image. Each use read every slice no
depth surface holds from guest memory and detiled it again, even when
nothing had written it since: in one level of a heavier title that was
about 165,000 layer reads, about 650 GiB, in a five-minute run.

A slice read from guest memory now stays in the copy's staging buffer
with the write generation it was read at. The next use reads and
detiles it again only when its address, size or tiling changed, the
write tracker reports a write since, or recorded GPU work writes the
range; a slice a depth surface held in between is read again. Without
write watching every use reads, as before.
2026-10-13 12:59:26 +13:00
Dean 1c5e62878a
Merge pull request #425 from MichonGoddijn231849/feat/depth-surface-reads
feat(agc): depth planes sampled as 2D arrays, cubes and integer views
2026-10-13 12:58:34 +13:00
alfalfa-star fd98327ba7 fix(agc): take a memory value fill only when the hardware stores the raw value
The kernel reads its value with s_buffer_load_dword through the V# in
user[4..7] and stores it with buffer_store_format_x through the V# in
user[0..3]. When the value V# covers less than a dword (NUM_RECORDS
times its stride, or NUM_RECORDS bytes for stride 0) the load returns 0,
and a store format other than 32_UINT, 32_SINT or 32_FLOAT converts the
value, so in both cases the HTILE does not receive the dword read from
memory. The match now requires both. Kena's own dispatch (format
32_UINT, a value V# of one 16-byte record) still matches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:40:31 +13:00
alfalfa-star d31b13f491 fix(agc): take an HTILE fill from a value in memory as a depth fast clear
PPSA01802 (Kena: Bridge of Spirits) clears its depth HTILE every frame
with its own compute kernel: a 9-word dword fill that stores, through
the stride-4 V# in user[0..3], a value loaded from the pointer in
user[4..5]. Neither the DMA_DATA hook nor the fill kernels the driver
recognizes saw it, so the depth kept the first frames' contents and the
base pass and sky failed their depth test almost everywhere.

The kernel is matched exactly when the dispatch is decoded, the value is
read from guest memory, and the range is passed to NoteDepthMetadataFill
like a fill, so the whole-HTILE and ZMask rules apply unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:40:24 +13:00
Dean e6ecece94e
Merge pull request #4267 from EagleBearerYS/fix/shader-color-compare-8bit-unorm-zero
fix(shader): return 0 for 8-bit UNORM color comparison
2026-10-13 12:38:49 +13:00
Dean Galvin 90d899d982 fix(agc): first-slice view of depth plane arrays, refuse plane copies of another host format
A 2D-array T# over a depth surface bound to a shader image declared 2D had
no first-layer view, so the descriptor got a null view and the shader read 0.
The depth plane texture now creates the 2D view of its first slice when its
view is a 2D array, for the depth image itself and for the plane copy.

A 16-bit depth surface with stencil may be stored as D24_UNORM_S8_UINT or
D32_SFLOAT_S8_UINT. The plane copy sizes and copies slices by the guest
format, so several slices or an R16_UINT view of such a surface now throw.
2026-10-13 12:18:50 +13:00
Michon dc0c2cf8c6 test(agc): agc_driver_depth_plane_sampling pins the sampled plane values
image_load of row 0 through each view, against a live surface drawn at a
known depth and guest-memory slices filled with known values: a 3-slice
D32 2D array, a D32 cube (all six faces), a D16 plane as R16_UINT, and a
D16 depth-bits R16_UINT array (both slices' raw values). A plane array
from base slice 1 is still refused. Fails on main (the array is refused)
and without the depth-bits format gate (GTA V's refusal message).
2026-10-13 12:11:02 +13:00
MarcZX100 1988607409 test(agc): cover unaligned shader ABI headers 2026-10-12 22:28:55 +00:00
Dean 8a67eb2e68
Merge pull request #4456 from Mouhand-Kaddo/fix/agc-library-used-descriptor-sets
fix(agc): retain used descriptor sets in pipeline libraries
2026-10-13 10:41:32 +13:00
Dean 16cdf635e2
Merge pull request #4746 from oneandonlydean/up4/fix-arena-hole-test-flake
test(agc): reserve the arena host hole the write-tracking checks query
2026-10-13 10:08:48 +13:00
Dean 6f438a1c2e
Merge pull request #4318 from Amoenus/codex/fix-color-target-d64-x
feat(agc): support 64 KiB display XOR color targets
2026-10-13 10:01:57 +13:00
Dean 7ac3f460e5
Merge pull request #3952 from xXPBSXx/fix/recompiler-resource-control-flow
fix(recompiler): leave out descriptor sources behind a branch the capture decides
2026-10-13 10:00:59 +13:00
Mouhand Kaddo 4f5f6d671c test(agc): ignore null shader modules in graphics mock
Pipeline libraries take ownership of shader modules during prebuild. Treat null handles as a no-op so moved-from modules do not decrement the live-object count twice.
2026-10-13 00:43:16 +04:00
Adrià 84659e16d0
Merge pull request #4632 from AlxcBTW/feat/trace-gpu-track
feat(agc): add the GPU, GPU mutex waits and shader preparation to the trace
2026-10-12 22:39:23 +02:00
Mouhand-Kaddo 9aeeea848f fix(agc): retain used descriptor sets in pipeline libraries 2026-10-13 00:38:10 +04:00
Adrià Franch b3c8be5887 test(agc): run the integer storage clear check from ctest 2026-10-12 22:22:48 +02:00
Adrià Franch 09aa34d063 test(agc): pin the integer storage clear to DCC code 0000
StorageClearAvailable takes code 0000 on R8/R16/R32_UINT and refuses every
other code; drop the header comment that said integer formats were refused.
2026-10-12 22:22:48 +02:00
AlxcBTW eb8a7255d2 feat(agc): add the GPU, GPU mutex waits and shader preparation to the trace
The trace gets a gpu track: one slice per recorder batch from its timestamp
pair, and with APS5_PROFILE_GPU=1 one per command class range under the
[gputime] class names. The GPU clock is mapped onto the CPU clock from
calibrated timestamps (VK_KHR_calibrated_timestamps or the EXT version,
enabled only when a trace switch is set) taken when the window opens and
when it closes. A traced thread's wait for the GPU mutex becomes a
GpuMutex.wait.<site> slice, and the shader preparation threads are traced
with smaller buffers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:13:44 +02:00
Lexroid 7d34a6aa8b fix(agc): store D16 depth with stencil as D24 or D32 where the device lacks it
A 16-bit depth target with stencil maps to VK_FORMAT_D16_UNORM_S8_UINT,
which NVIDIA devices do not support as an attachment, so creating it threw.
The surface, its views and the pipelines now use the first of D16_UNORM_S8_UINT,
D24_UNORM_S8_UINT and D32_SFLOAT_S8_UINT the device can attach and sample.
Guest-side choices (16-bit sampled views, bias units) still follow the guest
format. A constant depth bias is scaled by 256 for D24 storage; on D32 float
storage it throws, as its unit depends on the depth value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:13:24 +02:00
AlxcBTW 5c7da226df fix(agc): write the trace's gzip framing around a raw deflate stream
The zlib in the tree (libjpeg-turbo's) is built with NO_GZIP, so
deflateInit2 refuses the gzip wrapper (windowBits 31) and no trace was
written. The trace file now writes the gzip header, a raw deflate stream,
and the CRC-32 and size trailer itself. The writer's result line is flushed,
since stderr is buffered in a title process. The file writing moves to
TraceFile, and a test inflates a written file and checks its CRC-32 and
size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:42:59 +02:00
AlxcBTW 5668e0d0e7 feat(agc): record a present window as a Chrome trace and a summary line
APS5_TRACE_FRAMES=<start>:<count> (or APS5_TRACE_SECONDS=<start>:<length>)
records every PerformanceTimer scope and stage of the queue workers and the
presenter inside the window as complete events, named after their frame
timing keys, into a preallocated buffer per thread. Slices under 20 us are
counted per thread instead of written; a full buffer stops that thread and
sets trace_dropped. When the window closes, a ProfileOutput writer thread
writes aps5-trace-<commit>-<pid>.json.gz and appends one line to
aps5-summary.jsonl. Without either switch a PerformanceTimer costs one more
predictable branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:42:59 +02:00
Adrià cf4557e5d8
Merge pull request #4570 from AlxcBTW/feat/sampler-collapsed
feat(agc): write the worker sampler's stacks as collapsed stacks
2026-10-12 21:42:52 +02:00
Romans Pokrovskis b6f1a4a69d test(agc): verify display XOR array attachment transport
Run the existing array-layer draw, recompiled shader load, untouched-layer and guest-publication checks with D_X as well as R_X. This exercises the resident array path that carries slice-dependent address XOR; a direct per-slice copy is not an equivalent model.

AI-assisted implementation and validation: Codex.
2026-10-12 22:38:59 +03:00
Romans Pokrovskis 4326c7c212 fix(agc): validate display XOR bits before resident lookup
Apply the source-configured D_X XOR refusal during color decoding as well as direct layout construction. Regression tests reject each raw column-bit combination before storage or rendering is selected. Existing resident-array and packed-mip refusal paths remain in force.

AI-assisted implementation and validation: Codex.
2026-10-12 22:38:59 +03:00
Romans Pokrovskis f27bdf4d73 feat(agc): support 64 KiB display XOR color targets
Map SW_64KB_D_X color surfaces through the existing source-backed texture equations and mip geometry. Keep display and render-target equation caches separate. Reject unresolved raw column bits for the new mode instead of accepting them.

Cover the captured PPSA99902 descriptor, independent source offsets, pipe/bank tags, transfers, array/mip placement and resident/non-resident GPU draws. Record unmeasured console behavior in TechnicalDebt.

CPU validation on main e013f8c9: captured-descriptor failure 5/5; changed passes 5/5; pinned AMD addrlib reference 536940 addresses per run, five runs. Full build and GPU validation are tracked externally. AI-assisted implementation and validation: Codex.
2026-10-12 22:38:59 +03:00
Dean 2f4cc8983b
Merge pull request #4431 from AlxcBTW/refactor/spirv-specializer-debris
refactor(shader): drop unused folded constants and unreachable blocks after specialization
2026-10-13 08:30:39 +13:00
AlxcBTW 428963d3b3 feat(agc): write the worker sampler's stacks as collapsed stacks
APS5_SAMPLE_WORKER=<file> now also writes <file stem>.collapsed.txt: one
"frame;...;frame count" line per distinct stack of queue worker 0, root
first, frames as module+0xRVA, which speedscope.app and flamegraph.pl
open. Walks go up to 64 frames, and nothing allocates while the worker is
suspended.

The libraries are built with -fno-asynchronous-unwind-tables, so most of
their functions have no .pdata entry, only a DWARF record in .ehfram, and a
walk ended at the first such frame. The worker's walk now steps those
frames through their record, with the CIE/FDE decoding and the call frame
instructions of libc's unwinder, which move from Unwind.cpp into its
header unchanged. A module is indexed after the worker resumes, the first
time a walk meets it, and every register the step restores is read inside
the worker's stack. The process sampler (APS5_SAMPLE_THREADS) keeps its 12
frames through .pdata.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:23:11 +02:00
xXPBSXx 7d508cf779 fix(recompiler): evaluate pruning conditions without throwing on null roots
A branch condition that reads through a null user-data pointer is now
treated as not evaluable instead of being caught, so every successor
stays active.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:18 +02:00
xXPBSXx 37003b7a38 fix(recompiler): track which branch uses each image, not only each descriptor source
Several image instructions of different dimensions on the same T# address share
one descriptor source, so a source stays active as soon as one of them is taken
and the others are still checked against the T#. Record the images a block uses
as well, deactivate the images used only behind a branch the capture does not
take, and bind them as null descriptors.

PPSA15246 (Teardown) then passes the "incompatible with the static runtime
image interface" check of the three image_store variants that are not taken.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:18 +02:00
xXPBSXx 7b9c7ca582 fix(recompiler): leave out descriptor sources behind a branch the capture decides
A compute kernel that picks a buffer or an image path from an SRT word reads
the same address as a V# in one path and as a T# in another. The resource
tracker now records which block uses each descriptor source and builds the
resource control flow from the structured block terminators, so a source whose
only uses sit behind a conditional branch with a clean, evaluable condition is
left inactive (bound as zero words, not validated) when the capture takes the
other path. A condition that reads inaccessible memory is not evaluable instead
of throwing.

PPSA15246 (Teardown) stopped on "runtime image descriptor is invalid" for the
untaken path of such a kernel.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:18 +02:00