Commit Graph

1787 Commits

Author SHA1 Message Date
Dean Galvin d9afe1078e test(agc): enter whole quad mode in the implicit-LOD 1D sample test
A pixel wave starts with only its covered pixels in EXEC, so a shader that
takes an implicit LOD has to run s_wqm first or the helper lanes of a partly
covered quad hold no coordinate. The test's pixel shader did not, which goes
unnoticed while its one triangle covers every quad whole. llvmpipe clips that
triangle at twice the viewport into three, and in the quads on the two inner
edges the helpers' zero coordinate gave the last mip instead of the expected
one.

The shader now starts with s_wqm_b64 exec, exec, as a compiled pixel shader
that samples with an implicit LOD does.
2026-10-12 07:35:46 +13:00
Adrià 2220effbe4
Merge pull request #3482 from MarcZX100/codex/rx-cross-block-3475
fix(agc): retain cross-block R_X coordinate XORs
2026-10-11 14:02:02 +02:00
Adrià a5d75aa24e
Merge pull request #3490 from MarcZX100/codex/image-dimension-boundary-3460
test(agc): remove ambiguous 1D V wrap expectation
2026-10-11 13:54:57 +02:00
Adrià 2e2c531441
Merge pull request #2258 from krishalivaibhav/codex/spirv13-image-regression
test(shader): cover SPIR-V 1.3 image compilation without a GPU
2026-10-11 13:53:14 +02:00
Adrià 544b38d094
Merge pull request #3474 from 014-code/fix/agc-resource-cache-storage-eviction
fix(agc): evict resource-cache images with storage entries
2026-10-11 13:50:49 +02:00
Adrià daa13905cb
Merge pull request #3434 from Yuva1024/fix/agc-nvidia-pipeline-link-optimization
fix(agc): skip the background pipeline relink on NVIDIA's proprietary driver
2026-10-11 13:49:43 +02:00
Adrià 6fd350f075
Merge pull request #3464 from GustavoGamarra95/test/agc-a2c-normalized-exports
test(agc): cover UNORM16 and SNORM16 exports with alpha-to-coverage
2026-10-11 13:49:40 +02:00
MarcZX100 8a390b06df fix(agc): XOR overlapping R_X coordinate contributions 2026-10-11 11:49:23 +00:00
Adrià b33b1b8e66
Merge pull request #3458 from alfalfa-star/fix/agc-dcc-writeback-keys-reused
fix(agc): store a storage image's DCC keys at write-back only when not stored yet
2026-10-11 13:49:15 +02:00
Adrià e38b5e95d2
Merge pull request #3300 from lendinglibrary/fix/3212-dispatch-split
fix(agc): split a compute dispatch over the workgroup count limit
2026-10-11 13:46:20 +02:00
Adrià cafc074e1a
Merge pull request #3454 from GustavoGamarra95/refactor/share-procmap-query
refactor(agc): share the PROCMAP_QUERY lookup with GuestMemory
2026-10-11 13:45:42 +02:00
Adrià 83929de897
Merge pull request #3444 from Pipodi/fix/fused-hull-header
fix(agc): select the registered back half for a fused hull header
2026-10-11 13:44:59 +02:00
Adrià e5d4f59dac
Merge pull request #3438 from ivica00/fix/srgb-bilinear-cube-compare
fix(shader): return 0 for 8-bit sRGB color comparison
2026-10-11 13:43:27 +02:00
MarcZX100 668a6db50d test(agc): avoid ambiguous row-boundary filtering 2026-10-11 11:34:47 +00:00
MarcZX100 a6cb65e481 fix(agc): retain cross-block R_X coordinate XORs 2026-10-11 11:23:47 +00:00
Donson 16cfe85a38 fix(agc): evict resource-cache images with storage entries 2026-10-11 19:08:29 +08:00
Gustavo Ariel Gamarra Rojas a07ac32a77 test(agc): cover UNORM16 and SNORM16 exports in the alpha-to-coverage GPU test
The pixel program packs its color for the export format, and the test now draws with FP16, UNORM16 and SNORM16 exports: an alpha of 0.75 keeps the pixel, 0.25 drops it, and so does -0.5 with SNORM16. Passes on an RTX 3050, a Radeon 660M and lavapipe.
2026-10-11 08:00:05 -03:00
Gustavo Ariel Gamarra Rojas 109eb81d62 test(agc): check the MRT0 export formats alpha-to-coverage accepts
Formats 4, 5, 6 and 9 (FP16, UNORM16, SNORM16 and 32-bit float ABGR) decode with alpha-to-coverage; formats 1, 2 and 3, which carry no floating-point alpha, still throw.
2026-10-11 08:00:05 -03:00
lendinglibrary 288ee89d2d fix(agc): split a compute dispatch over the workgroup count limit
A dispatch whose group count exceeds maxComputeWorkGroupCount aborted the
process; a software device (lavapipe) reports the spec minimum 65535, and
Ghost of Yotei (PPSA26344) dispatches 129199x1x1 in its presentation path.
The count is now recorded as consecutive vkCmdDispatchBase chunks of at
most the limit per dimension, each continuing the grid at its base offset
(a plain vkCmdDispatch would restart group ids at 0 and re-execute the
first chunk's workgroups), with a barrier between chunks so a later chunk
sees an earlier one's writes. Compute pipelines are created with
VK_PIPELINE_CREATE_DISPATCH_BASE_BIT, which a nonzero base requires.

A split on NVIDIA throws instead of dispatching: measured on an RTX 5070
Ti, the driver drops the first workgroups of a vkCmdDispatchBase dispatch
with a nonzero base (reproduced with a standalone Vulkan program outside
this repository), so its 65535 Y/Z limits cannot be split through. A split
of a dispatch whose LDS lives in device memory also throws: its buffer
slot base is computed from gl_NumWorkGroups, which inside a chunk is the
chunk's count, so workgroups of different chunks would alias (a 1D X split
only works because the base degenerates to the group id).

Fixes #3212
2026-10-11 05:58:29 -05:00
Gustavo Ariel Gamarra Rojas 94f05530d9 refactor(agc): use the shared PROCMAP_QUERY lookup in GuestMemory
GuestMemory.cpp carried its own procmap_query fallback and a ProcMapsQueryFd that opened /proc/self/maps once, probed PROCMAP_QUERY and honoured APS5_NO_PROCMAP_QUERY, the same as HostMapping::QueryFd. It now includes HostMapping.hpp and calls HostMapping::QueryFd.
2026-10-11 07:55:11 -03:00
alfalfa-star 00648f71b0 test(agc): keep key bytes reused after a first write through the write-back
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:51:36 +02:00
alfalfa-star 63f06f2935 fix(agc): store a storage image's DCC keys at write-back only when not stored yet
MarkDirty stores uncompressed keys at an image's first write and marks
its uploaded keys uncompressed. The write-back stored them again,
whenever the key bytes did not read uncompressed. By then the title may
have reused the metadata memory: RIDE 4 (PPSA01599) frees its 1920x1088
render targets after the legal screen and Unreal Engine's small-block
allocator takes the 64 KiB key block that followed one of them. The
deferred write-back then filled the allocator's free lists with 0xff
and the title crashed popping one.

The write-back now stores keys only when the image's uploaded keys are
not uncompressed already, that is when no store at the first write or
an earlier write-back made them so since the keys were last read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:51:36 +02:00
Adrià 9a9bd3fb3c
Merge pull request #2369 from karanvora2599/perf/shader-coherent-alias
perf(shader): load from buffers without Coherent when the load has no GLC or DLC
2026-10-11 12:47:46 +02:00
ivica00 cb52ca10c9 fix(shader): return 0 from 8-bit sRGB color comparison
image_sample_c_lz on gfx1035 returns 0 for 8-bit sRGB, so the unorm clamp was the wrong result. Bilinear cube comparison without DISABLE_CUBE_WRAP keeps throwing because that filter blends across faces.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-11 12:44:53 +02:00
Yuvaraj b29133c74e fix(agc): skip the background pipeline relink on NVIDIA's proprietary driver
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>
2026-10-11 12:44:46 +02:00
Alex Collini 687f73c951 fix(agc): select the registered back half for a fused hull header
sceAgcFuseShaderHalves returns a fused hull as a copy of its back half
(type 7) that is not registered: type 3, no user data, and shader
registers copied to scratch memory with the checksum, RSRC1/RSRC2 and
the LS program address rewritten. sceAgcCreatePrimState accepts only that
type-3 header, and graphics ABI resolution refused it as a replaced
shader header.

SameHeader now also takes a type-3 header as the registered type-7
header for the same code when every other field is equal and its shader
registers differ only in the fused ones. Graphics ABI resolution then
applies the bound header's shader registers over the registered state.

PPSA26344 fuses its hull halves during its loading screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:36:01 +02:00
Adrià c064e6898e
Merge pull request #3425 from oneandonlydean/up4/fix-3158-span-limit
perf(agc-driver): join host-import spans only over whole aligned windows
2026-10-11 12:35:57 +02:00
ivica00 fc7446590a fix(shader): compare sRGB colors and bilinear cube faces
Subnautica Below Zero stops while compiling a menu shader that compares an sRGB color texture or a bilinear cube face. sRGB uses the unorm reference clamp, and a bilinear cube sample stays on the face it hits.

Written with AI assistance.
2026-10-11 12:32:28 +02:00
Adrià 8fcd352f04
Merge pull request #3420 from gookie-dev/perf/agc-lazy-constant-ram
perf(agc): allocate a queue's constant RAM on its first write
2026-10-11 12:23:06 +02:00
Adrià 88636aaf02
Merge pull request #3419 from gookie-dev/perf/agc-registers-foreach
perf(agc): walk register sets by their bit words when resolving ABIs
2026-10-11 12:21:53 +02:00
Adrià e7827eb4d4
Merge pull request #3416 from AchrafReyani/fix/nodiscard-warnings
fix(shader): discard two nodiscard results explicitly
2026-10-11 12:16:54 +02:00
Adrià a7d80e7413
Merge pull request #2410 from karanvora2599/perf/shader-pack-conversions-f32
perf(shader): round v_cvt_pknorm and v_cvt_pk_u8 in f32
2026-10-11 12:16:07 +02:00
Adrià 3bf63fadce
Merge pull request #3401 from AchrafReyani/carry/pipodi-3350
fix(recompiler): split descriptor webs selected per lane
2026-10-11 12:08:57 +02:00
Adrià 6cee0bb02a
Merge pull request #3400 from AchrafReyani/carry/pipodi-3349
fix(recompiler): evaluate an out-of-range constant buffer read as zero
2026-10-11 12:08:15 +02:00
gookie 8b550b81fc perf(agc): allocate a queue's constant RAM on its first write
QueueState held the 48 KiB constant RAM inline, so every QueueState
cleared 48 KiB when constructed and copied it when copied.
ResolveShaderAbi and ResolveGraphicsStagesAbi construct one on each call
(sceAgcLinkShaders, sceAgcCreatePrimState, interpolant mapping),
resolveGroupAhead copies the live state, and a queue reset builds a new
one, although few command buffers write the constant RAM.

The constant RAM is now a vector allocated when WRITE_CONST_RAM first
writes it. ConstantRam returns a shared all-zero view until then, so
DUMP_CONST_RAM of unwritten words still stores zeros, and the bounds use
QueueState::ConstantRamWords. The store view ResolveStore returns is
copied by the recorder within the packet, as before.

testMemory dumps unwritten constant RAM and checks for zeros, and checks
that a copied queue state keeps the words written before the copy.

A QueueState is 336 bytes instead of 49464; constructing one takes 0.3
to 0.8 us instead of 1.5 to 2.1 us, and copying one 0.4 to 0.6 us
instead of 1.9 to 3.8 us (GCC 13 -O3). On the title screen of PPSA21402
(Balatro) the clears from the two resolvers took 30 to 129 of 3459 to
7838 CPU samples per 10 s.
2026-10-11 12:01:27 +02:00
gookie f596bde6f6 perf(agc): walk register sets by their bit words when resolving ABIs
Iterating a Registers advances with nextPresent, which recomputes the
word, mask and next bit from the index on every step, about 3.5 ns per
register. BuildRegisteredAbiKey walks all of a state's registers, about
300 with the context, and ResolveGraphicsStagesAbi walks each stage's
registered state to merge it, on every sceAgcLinkShaders,
sceAgcCreatePrimState and interpolant mapping call.

Registers::ForEach visits the set registers in ascending order straight
from the presence words, and both paths use it. The key and the merged
state are unchanged.

testRegisterFile checks that ForEach visits what iteration visits, in
order, and skips an erased register.

Building the key for 297 registers takes 200 ns instead of 1030 ns (GCC
13 -O3). On the title screen of PPSA21402 (Balatro) the two functions
took 0.18 to 0.29 CPU samples per sample of the title's own code, and
0.06 to 0.12 with this change.
2026-10-11 12:00:28 +02:00
Adrià b70a0e278f
Merge pull request #1491 from Celegans12/feat/shader-tessellation-triangle-patches
feat(shader): implement triangle-patch tessellation
2026-10-11 11:50:50 +02:00
Achraf Reyani 30a3cbea7d fix(shader): discard two nodiscard results explicitly
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:45:44 +09:00
Alex Collini f3e654a4f5 fix(recompiler): split descriptor webs selected per lane
A T# picked per lane with SelectU32 and made uniform with
v_readfirstlane, as in a waterfall loop, left a descriptor web without
Phis that the web walk could not split. CollectDescriptorPhis now steps
through ReadFirstLane, and a Phi-free tuple containing SelectU32 or
ReadFirstLane is split on the shared condition (SelectU32) or wrapped in
a matching ReadFirstLane. The new nodes are placed after the last
original node of the tuple in its block and inserted only when the web
is split; otherwise they are invalidated like the web's Phis.

PPSA26344 compute shader 0x800037f200 selects its T# per lane between
SRT reads or zero and reads the first lane of the result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:42:56 +09:00
Alex Collini 5a9d731540 fix(recompiler): evaluate an out-of-range constant buffer read as zero
s_buffer_load returns zero for dwords outside the V#'s range. The SRT
evaluator refused such a read instead, so evaluating a descriptor failed
as soon as one arm of a split T# read through a null or short V#, even
when that arm is never taken. The evaluator now returns zero for those
reads, as the hardware does.

PPSA26344 compute shader 0x800037f200 reads one arm's T# through a null
V# and stopped at "SrtWalker::EvaluateRuntimeSources failed to evaluate
runtime sources".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:42:47 +09:00
Adrià 946ffa08d4
Merge pull request #3372 from alfalfa-star/fix/agc-fast-clear-import-window
fix(agc): keep a fast-clear copy where an import window can stamp watched memory
2026-10-11 11:40:15 +02:00
Dean Galvin 8da461ba51 perf(agc-driver): fix the host-import window at 8 MiB
The window was read from APS5_HOST_IMPORT_WINDOW_MIB on the device.
It is now always 8 MiB, the value the previous commit was measured
with; SetHostImportWindow stays for the tests. TechnicalDebt records
the window as a choice.
2026-10-11 22:35:37 +13:00
Cesar Rodriguez a947303731 perf(agc-driver): join host-import spans only over whole aligned windows
one span. A title that grows a heap 16 KiB at a time then re-imports the
whole chain on every growth: Ratchet & Clank: Rift Apart re-imported about
1.5 TiB while loading its first scene, and NVIDIA refused grown spans with
VK_ERROR_OUT_OF_DEVICE_MEMORY, which leaves those ranges copied per draw.

Ranges are now joined only when a chain covers a whole aligned window (8 MiB
by default, APS5_HOST_IMPORT_WINDOW_MIB on the device); until the window is
complete each range is imported on its own, and a span never crosses a
window edge. Growth therefore re-imports a range once more at most when its
window completes, instead of the chain on every growth. Joining any part of
a window was tried first and still re-imported 2 GB per second in Ratchet,
as each growth re-imported the window's partial span. A single range larger
than the window is imported whole. SetHostImportWindow(0) keeps the
unbounded spans, which is what the existing tests use.

The new importWindowTests (agc_driver_graphics) checks that a full window is
one span, that a partly filled window imports its range alone, that a view
crossing a window edge gets no import, and that completing one window leaves
the other window's import in place; with the window ignored it fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:35:28 +13:00
Adrià Franch e53e1762e3 test(agc): adapt the tessellation tests to the fixed push slots and preparation at use
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:25:56 +02:00
Celegans12 77dcb74ccd feat(shader): implement triangle-patch tessellation
DecodeShaderStages accepts LS, HS and an NGG passthrough ES, reads the
3-bit VGT_TF_PARAM fields, refuses its other bits but DISTRIBUTION_MODE,
and needs HOS levels that keep [1, 63]; 0x286-0x287 join the draw key.
Local and domain programs are prepared with their stage group: its
headers are merged over one set of defaults, and a local program left
unbound by the hull header is prepared at its first draw.

The local program ends at its jump into the hull half.
TessellationLowering lowers LDS and ring addresses that are affine in
the control point and relative index to tessellation interfaces with
strides from the code, and refuses accesses outside the patch, per-patch
writes from other control points and a modified primitive export. Hull
lanes read their own saved exec bit, also of host-subgroup-masked
copies; other masks are refused, gl_InvocationID loads as a scalar and
factors clamp to 63.
2026-10-11 11:25:56 +02:00
Alex d809958b27 fix(shader): fold BitReverse32 of an immediate
A descriptor dword built by S_BREV_B32, V_BFREV_B32 or S_BREV_B64 from a
constant stayed a runtime value, so the resource could not be resolved
and the shader failed to prepare. The constant folder now reverses the
bits of an immediate the way the hardware does (D[31-i] = S0[i]), like
the existing BitCount32 fold. ConstantBitTest covers the fold.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-11 05:23:40 -04:00
Adrià 4c9686fe2e
Merge pull request #1388 from gjonn/feat/agc-color-compare-constant-swizzle
feat(agc): color compare through a view whose X channel is constant
2026-10-11 11:21:17 +02:00
Adrià 0490ca18dd
Merge pull request #1505 from Sanjaysrinivas/test/shader-half-expansion
test(shader): cover half expansion bit patterns
2026-10-11 11:18:31 +02:00
milescoviello ee720ee004 test(agc): reproduce an import window over a pending storage image
agc_driver_dcc_keys_first_write only exercises the import window when the
device's own host import resolves the write protection of the imported
pages, as the NVIDIA driver does on Linux. CI's devices do not, so the
comparison that #2984 adds for BlockMaybeWritten units is not run there.

RunImportWindow opens an import window itself over a pending storage
image with GuestMemory::ImportWatched and stores the byte already there,
which leaves the same stamp without depending on the driver: the image
must stay pending and unchanged. A store of a new byte inside a window
must still be seen. Devices whose imports are compared print that the
window is not tested, as the recorder's (w) test does. Linux only: the
Windows path compares imports.

TechnicalDebt records that an identical-byte CPU store inside a window
cannot be told apart from the import, and that images tracked by layer
still upload again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:18:04 +02:00
milescoviello f774e1cd98 fix(agc): keep a fast-clear copy where an import window can stamp watched memory
range is write-watched. Since #2984, Refresh turns a BlockMaybeWritten
unit into BlockUnchanged only through compareUntracked, which needs that
copy. Where a host import leaves its range watched and making it stamps
the imported pages (the probe's writtenAtImport, 4 of 4 on NVIDIA's
Linux driver), the import window stamps the surface, so a pending
fast-cleared image counted as changed again and
agc_driver_dcc_keys_first_write failed on main (#3179).

HostImports now keeps whether making each kind of import stamped the
probe's pages, and WatchedImportsStampPages reports whether an import
that leaves its range watched can stamp it. The clear path skips the
copy only when no such import can, as oneandonlydean proposed in #3179.
Devices whose probe saw no pages written at the import, devices without
host imports and Windows (whose alias imports don't disarm the guest
views) keep #2699's saving.
An unprobed watch (APS5_WRITE_WATCH_IMPORTS=watch) keeps the copy.

The fast-clear check in agc_driver_dcc_keys_first_write now expects the
copy exactly when the range is unwatched or such a stamp is possible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:18:04 +02:00