Commit Graph

65 Commits

Author SHA1 Message Date
MarcZX100 a0e3d2098d fix(shader): omit sRGB variants for comparisons 2026-10-11 13:09:21 +00:00
jweidenh 82c04e4027 fix(shader): keep sRGB comparisons in the emulated runtime mode
(cherry picked from commit 5377951c552f2313c8f7a52b57bd7a54c5f7fb7f)
2026-10-11 12:54:54 +00:00
MarcZX100 9e8b5a21bc fix(shader): allow constant-zero sRGB comparisons 2026-10-11 12:49:23 +00: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
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à 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à Franch 6ac5cad133 fix(agc): compare the red channel of a swizzled view 2026-10-11 11:10:41 +02:00
Gjon Gojcevic d32a7881f0 feat(agc): apply the view's channel selects to a color compare
Comparison sampling of a color texture required the view's X channel to select red. A title runs image_sample_c_lz on a 1x1 R8 unorm placeholder whose dst_sel is constant 0 on all four channels.

On hardware the compare result is placed in every channel and each returned channel then applies its own dst_sel: 0 gives 0, 1 gives 1, R/G/B/A give the compare result. Return each dmask register from its own select, and throw only for the reserved selects 2 and 3.
2026-10-11 11:10:41 +02:00
Adrià Franch 60a14b5be6 fix(recompiler): bind a 2D texture to a 1D image access
A 1D sample or load of a 2D T# had no runtime image mode after the mode table landed, so it was still refused at materialization. Sampled 1D accesses now also accept a 2D view; the address completion in the emitter reads the missing row as 0.
2026-10-11 11:07:41 +02:00
Celegans12 d0d127b17d feat(agc): accept texel offsets with unnormalized sampler coordinates
Explicit-LOD offset samples through FORCE_UNNORMALIZED samplers now add
sign-extended texel offsets to f32 coordinates before clamping, using
OpFAdd with NoContraction and a sampler specialization selector.

Without native nonconstant-offset support, retain only runtime image
modes valid for unnormalized sampling and reject normalized samplers.
Preserve constant-swizzle handling, indirect-image rejection and main's
flat-volume and flat-line compatibility rules when selecting those modes.

Serialize image compatibility and folding flags and the sampler folding
flag in cache format 34, with roundtrip, preparation and execution coverage.
Retain main's request format 15 and runtime ABI 11.
2026-10-11 10:08:25 +02:00
overlordxrz-source c447b289ca fix(shader): read 2D-array T#s through 3D image accesses
Since #2133, a 3D image_sample_lz or image_load bound to a 2D-array T#
decodes as 2D and matches the new 2D mode, so it reads layer 0 for any
r and returns zero for z != 0. adriadam10 measured on RDNA2 (gfx1035)
that dim 3D on a 2D-array T# behaves exactly like dim 2D-array, with r
or z as the layer. Decode a 2D-array T# bound to a 3D access as 2D-array
and add a 2D-array mode next to the 2D one.

The same measurement covers image_load_mip on both T# types and
image_get_resinfo, which returns (w, h, 1, levels) on a 2D T# and
(w, h, layers, levels) on a 2D-array T#. Allow those accesses and return
1 as the third resinfo component through the 2D mode. image_get_lod is
not measured and keeps throwing.
2026-10-10 23:42:50 +00:00
Adrià 9d792ae477
Merge pull request #2989 from CnRJay/fix/srt-null-root-reads
fix(recompiler): read SRT words through a null user-data pointer as zero
2026-10-11 01:49:13 +02:00
Adrià ad3369236d
Merge pull request #2920 from XairRrR/fix/runtime-image-mode-diagnostic
fix(recompiler): describe the descriptor and modes when no runtime image mode matches
2026-10-11 01:30:51 +02:00
Chad 27e1ddca99 fix(recompiler): read SRT words through a null user-data pointer as zero 2026-10-10 14:51:20 -05:00
Dean Galvin 27353e16b7 fix(shader): give an emulated comparison of a 2D-array access a plain 2D mode
A comparison sample with a 2D-array address on a plain 2D color T# takes the emulated comparison, whose runtime modes were only the 2D-array and cube ones. No mode matched, and materialization threw that the image descriptor was incompatible with the static runtime image interface. The emulated comparison now also gets a 2D mode, as the native modes of a 2D-array access already have; the slice is ignored.
2026-10-11 07:32:16 +13:00
XairRrR 4adca33251 fix(recompiler): describe the descriptor and modes when no runtime image mode matches 2026-10-10 19:59:15 +02:00
Adrià 85d16bcc23
Merge pull request #2347 from Pipodi/fix/sampled-heap-capacity
fix(recompiler): hold 64 images in a sampled image heap
2026-10-10 10:23:23 +02:00
Alex Collini ea22c8286d fix(recompiler): hold 32 images in a sampled image heap
A compute program of PPSA26344 binds 29 sampled images of one class, more
than the 16 a typed sampled heap held, and failed with "shader image heap
capacity exceeded". Heap arrays are declared with their compact length,
so shaders that fitted before keep their layout and SPIR-V.

Bindless image tables keep 16 slots: they were sized by the same constant,
and each slot costs an image entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 10:09:42 +02:00
authurtabo-byte a76b0dc461 fix(agc): reject unsupported color comparison forms
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-10 01:08:04 -07:00
authurtabo-byte e50bd2f824 fix(agc): retain anisotropic compare rejection and test sRGB forms 2026-10-10 01:08:03 -07:00
authurtabo-byte f7ca33cb9d fix(agc): accept additional color comparison image forms 2026-10-10 01:08:03 -07:00
Adrià Franch c77a6f14f3 fix(shader): keep both image mode blocks after rebase 2026-10-10 09:55:37 +02:00
Alex Collini 14d9b13cd8 fix(shader): write 1D storage images through 2D image stores
A compute program of PPSA26344 writes a 1D storage image with image_store
DIM 2D, and no runtime image mode matched its T#. A write-only 2D storage
access now also gets a 1D mode. A store through it is dropped when y is not
0, as a one-texel-high image bounds it; this mirrors the measured 3D loads
through 2D T#s of #2023 and is listed in TechnicalDebt as unmeasured.

Loads, atomics and image_store_mip through such a pair still throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 09:55:37 +02:00
Nahuel Genchi eb06284c78 fix(shader): read 2D image accesses through 1D T#s as measured
A compute shader in Astro Bot samples an image as 2D while the bound
T# is 1D, and the recompiler refused it. Offer a 1D runtime mode for
sampled images used only by image_sample_lz and mip-less image_load,
which reads the texel the first coordinate selects, as measured on
RDNA2 in #2237.
2026-10-10 09:35:34 +02:00
MathiasDevelopes 83e0a01540 fix(recompiler): size a typed storage heap for several images, not one dynamic image
A typed storage-image heap held four elements, and a dynamic-mip storage
image takes four of them for its mip slots. A shader with a dynamic-mip
storage image and any other storage image of the same class needed five
and failed with "shader image heap capacity exceeded". The mip slots
per dynamic image stay four (StorageMipSlots); a storage heap now holds
sixteen elements (StorageHeapCapacity). Heap arrays are declared with
their compact length, so shaders that fitted before are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 08:40:41 +02:00
Daan Dehing 70d190cdd5 fix(shader): bind 2D textures to 3D sample and load accesses as measured
A 3D image access bound to a 2D T# matched no runtime image mode and
threw "image descriptor is incompatible with the static runtime image
interface" (Sonic Origins, #2023). CouchRiv measured RDNA2 (gfx1036)
with the hardware oracle: image_sample_lz with dim 3D ignores r and
reads the (u,v) texel, and image_load reads the (x,y) texel when z is
0 and returns zero otherwise. Add a 2D mode for 3D accesses whose only
uses are those two forms, and make the load return zero for z != 0.
Every other access keeps throwing.

Fixes #2023

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 05:15:30 +02:00
Gjon Gojcevic e8b24afecd feat(agc): fold samples of views whose channels all select constants
When every dst_sel of a sampled view selects constant 0 or 1, a sample
or gather returns that constant whatever the texel, filter, LOD or
coordinates. Such placeholder views can have a dimension the static
runtime image interface has no mode for (a 3D view under a 2D sample,
a 1D view under a 2D gather), and a 3D one paired with an unnormalized
sampler fails the unnormalized proof.

A direct sampled image whose accesses are all non-compare samples or
gathers gets three constant runtime modes (float, uint, sint). The mode
is selected when all four dst_sel are 0 or 1, unless the view is FMASK,
depth bits, converted or sRGB-decoded. In it the emitter returns each
channel's constant (the dmask channel's, in all four texels, for a
gather) from the image's swizzle specialization constants, and the
image is left unbound. ProveUnnormalized skips the mode after the
run-time-selected check, so table roots still throw.
2026-10-10 01:22:21 +02:00
Celegans12 56f8a62428 feat(agc): implement deferred faults for reads through null SRT pointers
Capturing a shader threw "null or misaligned address" when the flat
SRT walk read through a pointer loaded from memory that is null or
unmapped, even when the program skips that s_load. Such a read now
yields zero and a record with its pc and address, as does a slot whose
address needs it. The registered artifact checks one flag word per
guarded slot at each ReadConst; the capture appends the flags and the
records to the flattened SRT, and a set flag raises an Unmapped BDA
fault through the fault buffer and stops the invocation as a BDA read
does. Nothing per capture enters a key: poisoned and clean captures
share one variant and specialization. Descriptors, branch conditions,
uniform fills, table bases and user-data pointers that need such a
value still throw, and poisoned results stay out of the dispatch and
draw caches. The shader disk cache format becomes 21.
2026-10-09 00:20:59 +02:00
Adrià Franch 57d4be33bb feat(shader): implement MIMG PCK2/PCK4 loads as measured on hardware
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-08 21:54:10 +02:00
Adrià Franch d5a3a4321c fix(shader): follow measured hardware for MIMG BY2/BY4 loads
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-08 21:28:22 +02:00
Petre 88f7a5e0e4 feat(shader): lower full-width MIMG BY2 and BY4 transfers 2026-10-08 21:10:51 +02:00
boykopovar 6741e90a1c fix(shader): restore prepared image compatibility and update regression fixtures 2026-10-08 18:32:12 +03:00
boykopovar eb68ad3b02 fix(shader): preserve main buffer alignment and image formats in prepared shaders 2026-10-08 17:51:05 +03:00
boykopovar 082f371758 fix(shader): correct emulated color comparison specialization 2026-10-08 17:40:12 +03:00
boykopovar 6a4c5b5ded fix(shader): support non-array image views and packed unorm runtime modes 2026-10-08 17:38:11 +03:00
boykopovar 6c858dab2c fix(shader): validate unsupported sRGB sampling and SINT d16 writes at materialization 2026-10-08 17:37:35 +03:00
boykopovar 2d93308780 fix(shader): preserve main behavior in cached specialization 2026-10-08 17:37:25 +03:00
boykopovar dc62c432b4 perf(shader): eliminate redundant prepared invocation work 2026-10-08 17:27:26 +03:00
boykopovar b2a4813307 refactor(shader): remove resource specialization from artifact key 2026-10-08 17:26:38 +03:00
boykopovar ab0f9985fa feat(shader): resolve bindless resources through runtime heap metadata 2026-10-08 17:26:36 +03:00
boykopovar 1174069239 feat(shader): select image and sampler resources at runtime 2026-10-08 17:26:34 +03:00
boykopovar b08c410b97 feat(shader): move buffer accesses to runtime descriptors via bda 2026-10-08 17:25:14 +03:00
Gjon Gojcevic 71851d66a3 fix(recompiler): take point-filtered sampler copies' descriptors from their source sampler
When one guest sampler is paired both with an image that needs point
filtering (sint, depth-bits or converted format) and with an ordinary
image, ResourceMaterializer::Apply appends a point-filtered copy of the
sampler after the original samplers. The resource snapshot only holds
the original samplers, so DescriptorBindingBuilder::Populate threw
"guest sampler index is out of range" for the copy, both in the sampler
binding and in the unnormalized-coordinate check.

SamplerResource::copyOf records the sampler a copy was made from, and
the guest sampler descriptor of a copy is read from that sampler; the
point-filter rewrite of word 2 still applies to the copy. The field is
part of the disk cache encoding: cache format version 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 14:55:18 +02:00
Celegans12 38e89df1a5 fix(agc): bind guest buffer views that start off a DWORD boundary
A V# whose base is not a multiple of 4 bytes from the start of its GPU
owner threw in GuestBufferMemory::Descriptor, because the shader added
the offset below the view only in whole dwords.

Descriptor now binds from the offset alignment below the view and
passes the whole remainder. Such a view's range covers the dwords
around it, and its descriptor region starts and ends on dword
boundaries, so a lone view copied to its own buffer binds the same way.

The buffer specialization records the V# base's low two bits. Raw
vector loads through such a V# join two dwords, glc dwordx2 loads
included, stores merge their bytes into both with compare-exchange
loops, and bounds checks include the second dword. s_buffer_load
ignores the low two bits, as SrtEvaluator does. Formatted accesses
whose element alignment (min(element bytes, 4)) the base breaks throw,
as they fault the wave on RDNA2, and so do atomics.
2026-10-08 13:01:27 +02:00
Adrià a0fa346f40
Merge pull request #944 from Celegans12/feat/agc-sint-storage-images
feat(agc): implement sint storage images through uint views
2026-10-07 13:56:17 +02:00
Celegans12 6e34e8614e feat(agc): implement sint storage images through uint views
Storage images of a SINT format threw "storage image descriptor uses
an unsupported format"; only write-only 32_SINT was accepted.

The recompiler now writes 16_SINT, 8_8_SINT, 32_SINT, 16_16_SINT,
8_8_8_8_SINT, 32_32_SINT, 16_16_16_16_SINT and 32_32_32_32_SINT
storage images with the Uint numeric class. 8- and 16-bit components
are saturated (EmitFormatStoreComponent) and 32-bit ones stored
unchanged, as measured on RDNA2 (gfx1035); image_store_pck stores
32-bit components unchanged too. The driver binds these surfaces
through a cached view of the same-size UINT format.

8_SINT, 32_32_32_SINT, the packed SINT formats (11_11_10, 10_11_11,
2_10_10_10, 10_10_10_2), D16 stores, packed stores of 8- and 16-bit
components and atomics other than 32-bit ones on 32_SINT and 64-bit
ones on 32_32_SINT still throw. New test: agc_driver_image_store_sint,
with DST_SEL X,Y,0,1 on the two-channel formats, as with X,Y,Z,W the
hardware writes channel 1 from the W data slot (TechnicalDebt).
2026-10-07 11:56:04 +02:00
Celegans12 91852719bf feat(agc): implement 8 and 8_8 srgb image loads through unorm views
NVIDIA 610.62 cannot sample R8G8_SRGB, and lavapipe can sample neither
it nor R8_SRGB, so an 8_8 sRGB T# made an image the device does not
support: creating it crashes the NVIDIA driver and lavapipe loads zeros.

The device's format support is read once. An 8 or 8_8 sRGB texture the
device cannot sample is created and viewed as R8_UNORM or R8G8_UNORM,
and SpirvTarget::srgbDecodeFormats makes the materializer mark the
image, so image loads map every channel through a 256-entry table of
the sRGB EOTF rounded to nearest even at bf16 precision, which is what
RDNA2 (gfx1035) returns; both channels of 8_8 are sRGB, and DST_SEL 0
and 1 map to themselves. Samples and gathers of such an image throw,
and its bindless table entries are unmapped like converted formats.
APS5_SRGB_SHADER_DECODE=1 takes this path on every device. Shader disk
cache entries and keys carry the mark: cache format version 7.
2026-10-07 11:52:08 +02:00
Claude 2eebe84f25 feat(agc): accept texel offsets, LOD biases and LOD clamps in color comparison sampling
Comparison sampling of a color texture threw on the _o, _b and _cl
forms and on a sampler LOD bias. The emulation fetches the texels
itself, so the 6-bit offsets of the address are added to the point or
bilinear texel indices, as on the hardware and in Vulkan. The emulation
runs only at level zero or on a single-level view, where an LOD bias or
clamp cannot select another level, so they are accepted with no effect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 11:30:00 +02:00
alfalfa-star 9df592926b feat(recompiler): implement 64-bit image atomics
image_atomic_* with a two-dword data mask (four for cmpswap) is the
64-bit form of an integer image atomic. The decoder rejected it with
"invalid MIMG data mask". PPSA14632 (Unreal Engine) uses
image_atomic_umax this way in a compute shader during its first frames.

Swap, add, sub, signed and unsigned min/max, and, or, xor and cmpswap
now decode with 64-bit data and translate to new ImageAtomic*64 IR
opcodes. An image updated by them gets its own descriptor binding
range, declared as an R64ui image of 64-bit integers, and the module
uses Int64ImageEXT from SPV_EXT_shader_image_int64. The driver enables
shaderImageInt64Atomics when the device offers it and binds an
R64_UINT view of the 32_32 UINT, SINT or FLOAT surface. Without the
feature, recompiling such a shader throws.

64-bit inc, dec and the float atomics still throw, as does any other
access in the same shader to an image it updates with 64-bit atomics.

The new agc_driver_image_atomics64 test runs every operation on 32_32
UINT, SINT and FLOAT surfaces and checks the returned and stored values
against edge cases across the dword boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 10:21:32 +02:00
Celegans12 1afcd20281 feat(agc): implement min and max sampler reduction modes
An S# with FILTER_MODE (word0 bits 29-30) set threw in
DecodeSamplerResource. Modes 1 and 2 now map to Vulkan's MIN and MAX
sampler reduction through VK_EXT_sampler_filter_minmax, which the device
enables when it reports both filterMinmax properties; mode 3 throws.

Min/max still throws with anisotropy, a linear mip filter (lavapipe and
NVIDIA disagree there), depth comparison, color-texture compare
emulation, and, in PointFilteredSamplerWord (S# words 0 and 2), when it
filters between texels or mips of an image the recompiler point-samples.
A texture sampled through a linear min/max sampler needs
SAMPLED_IMAGE_FILTER_MINMAX on its view format; the image-sampler pairs
travel in DescriptorBinding::imageSamplers (shader disk cache format 5).

Adds the agc_driver_image_sample_reduction GPU test and extends the
sampler, emulated color compare and disk cache tests.
2026-10-07 09:04:26 +02:00