Commit Graph

66 Commits

Author SHA1 Message Date
Niko 664ad77037 test(recompiler): pin the field counts of the structures the plan codec writes
The plan and ShaderInfo encoders write these structures field by field,
and the sizeof asserts are their only reminder to update the encoders. A
field that fits in existing padding keeps the size: main's layerExported
landed in ShaderInfo with sizeof still 232, and the three bools #5089
adds to DescriptorSource::IndirectImage keep it at 28. A warm cache
would then hand back entries without the new field and nothing would
fail.

Count the fields of every aggregate the plan codec reaches (IrResourcePlan,
GuestRegister, MemoryInfo, DescriptorSource and IndirectImage,
ResourceBlock, SrtRead, UniformFill, UniformFillPlan, and ShaderInfo with
its resource and stage I/O records) by brace-initializing each with more
and more convertible-to-anything values until the initializer no longer
compiles, and assert the counts. Unlike the size asserts this does not
depend on the ABI, so it is checked on every platform. IrValue is a
class with private state and keeps only its size assert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:32:19 +02:00
Niko ed3f2776ed fix(recompiler): reject a stored plan whose image table names a missing source
DecodePlan checked every descriptor source index a plan holds except the
two inside an indirect image table: the heap source and the material
source. A damaged entry could therefore decode with a table that points
past the plan's descriptor sources, and only the materializer's own
bounds checks stood between it and an out-of-range read. The front end
interns both sources before the image source that names them, so a plan
it builds always passes the new check.

agc_shader_disk_cache now requires a plan whose table heap or material
source lies past the sources not to decode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko 1ed6c21067 perf(recompiler): load the resource plan from the disk cache before the front end
getSource built ResourceProgram (PrepareResourceProgram and ExtractPlan)
for the first request of every shader a process saw, before
findOrCompileVariant looked in the disk cache. With a warm cache every
launch still decoded, built the CFG, structurized, translated and ran
SSA, the fold passes, SrtWalker and ResourceTracker for every shader;
only SPIR-V emission was skipped.

getSource now builds the plan key and tries LoadPlan first. On a hit it
sets the plan and leaves the program null: findOrCompileVariant already
rebuilds the program when no stored variant fits the layout. The loaded
plan takes the request's code address as its shader hash, as a plan
built in this process would. On a miss it builds the plan as before and
stores it. A plan the front end refuses is not stored. The disk is not
used when the cache is off, under the debug probe, with APS5_DUMP_IR
(which prints the front-end passes), or with APS5_NO_PERF_DISK_PLANS=1,
a cache-neutral switch for comparisons.

FrontEndBuilds() counts PrepareResourceProgram calls. The new
cross-process test prepares, captures and materializes 14 shader
fixtures in one process and repeats it in a second process sharing the
cache directory: the second process must run the front end 0 times, hit
14 plans and 14 variants, and produce byte-identical SPIR-V, bindings,
push constants, snapshots, read traces and plans. A damaged plan entry
is rejected and rebuilt alone with the same output and stored again; a
changed code word and another switch value miss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko 0c144502e5 feat(recompiler): store front-end resource plans in the shader disk cache
The disk cache keeps compiled variants (SPIR-V, shader info, binding
layout) but not the IrResourcePlan the front end extracts for the
resource walk, so a process can only get a plan by running the whole
front end. Since #471 the plan owns its values (ownPlanValues clones the
graph behind every root into valueStorage and blockStorage), so it can
be written out and rebuilt.

- EncodePlan writes the value graph in storage order, each value as
  LEB128 integers: opcode, type, id, flags, register, operand count,
  the immediate if it has one, each argument as the zigzag distance to
  the value's own index (0 for null) and, for phis, the index of each
  predecessor block. The roots (descriptor source dwords, SRT reads,
  control-flow conditions, uniform-fill values) are value indices; the
  plain data (memory info, indirect image tables, control-flow edges,
  materialization sources, guarded, clean and pure flat slots, the
  flags, the ShaderInfo through the variant's encoder) follows. A plan
  whose roots or arguments point outside its storage is not encoded.
  Control-flow conditions can pull most of a program into the plan:
  a synthetic 2,038-dword compute shader with a branch every 20 dwords
  has 42,775 plan values, which take 518 KB this way and 2.5 MB with
  fixed-width fields; its compiled variant entry is 935 KB.
- DecodePlan rebuilds the graph the way ownPlanValues does and rejects
  an unknown opcode, an overlong integer, an index past the values or
  blocks, a descriptor source, image, buffer or sampler past the
  sources, a branch without two successors, an edge past the blocks,
  and a uniform fill of more than four words.
- BuildPlanKey is the in-memory source key (RecompileCacheKey::Build and
  the host subgroup size), every code word and the switch key, after a
  plan entry tag. It leaves out the binding layout, so all layouts of a
  shader share one plan.
- EncodePlanEntry/DecodePlanEntry use the variant's header and hashes;
  LoadPlan and StorePlan use the same directory and background writer
  and count plan hits, misses, writes and load failures on their own.
- Size asserts cover the plan and the structures it is written from.
- FormatVersion 30.

agc_shader_disk_cache now round-trips the plans of 14 in-tree shader
fixtures (SRT reads, guarded and chained pointers, control-flow
conditions, an indirect image table, a loop phi, a fragment shader) and
a hand-built plan with a uniform fill. For each it materializes the
original and the decoded plan over 1000 random user-data and memory
runtimes and requires the same snapshot, poison, null-root count and
read trace, or the same error. Truncated, bit-flipped and
other-format entries are rejected, malformed plans do not decode, and
the key follows the code, the target and the host subgroup size but not
the layout, addresses or user data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko 5c802bbf10 refactor(recompiler): split the ShaderInfo encoder and the entry envelope
encodeInfo and decodeInfo wrote the ShaderInfo fields inline between the
compiled-shader header and the binding layout, and EncodeEntry and
DecodeEntry built and checked the file header (magic, format, source
version, key and payload hashes) inline. Move both into helpers,
encodeShaderInfo/decodeShaderInfo and wrapEntry/unwrapEntry, so that a
second kind of entry can reuse them.

The bytes of a compiled variant entry do not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:23:35 +02:00
TideOPL 7015c0443e fix(shader): read the ancillary render target index from Layer when the draw exports it 2026-10-13 15:15:55 +01:00
Dean 3a0642e131
Merge pull request #4019 from ahir061/fix/macos-shader-cache-directory
fix(shader): find the shader cache directory on macOS
2026-10-13 16:52:01 +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 acb13d1b39
Merge pull request #4030 from AlxcBTW/fix/recompiler-flat-p10-provoking-v2-2
fix(recompiler): read P10 of a flat input as the provoking vertex
2026-10-13 03:41:23 +13:00
Adrià Franch 361c7e2024 perf(agc): keep oversized shaders specialized and prebuild libraries on their own thread
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch baf3437465 perf(recompiler): read depth-compare state and export mappings at run time when modules are built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:33 +02:00
AlxcBTW a7ed14c74b Merge branch 'main' into fix/recompiler-flat-p10-provoking-v2-2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:09:22 +02:00
Ahir Barman Maji 52c7470b15 fix(shader): find the shader cache directory on macOS
ShaderCacheDirectory located the executable through /proc/self/exe,
which macOS does not have, so it returned an empty path there and
the shader and pipeline disk caches were silently disabled unless
ANYPS5_SHADER_CACHE_DIR was set. Use _NSGetExecutablePath and
resolve the path with std::filesystem::canonical on macOS. Linux
and Windows are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:56:34 +05:30
AlxcBTW 99edf9435c fix(recompiler): read P10 of a flat input as the provoking vertex
A FLAT_SHADE input without pass-through that a pixel shader reads
through v_interp_mov_f32 P10 became an explicit per-vertex input and
read vertex 1 minus vertex 0. With the last provoking vertex the
pipeline then threw ("last provoking vertex with explicit per-vertex
flat interpolation is unsupported"), which stopped all three
ps5-opengl titles in the catalog.

ps5-opengl reads every flat input through P10 when GL's last-vertex
convention is in effect, and its driver passes the GL 4.6 CTS on a PS5
(#1413). Such a read now returns the provoking vertex's value, as P0
does, and only a P20 read keeps a flat input per vertex. The lane was
not measured on the console itself, so FragmentParameter records it
and a pipeline using the first provoking vertex throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:09:55 +02:00
Celegans12 76fa2beceb perf(recompiler): reuse sorted sets for iteration selection merges
Seed each iteration-selection update from its first eligible successor
instead of rebuilding and intersecting the universe of block IDs.
Reuse the existing in-place intersection and sorted-insertion helpers
while preserving the original empty-successor and fixed-point behavior.
Keep a cache-neutral switch for same-binary output and timing comparisons.
Run the existing structurizer and control-flow fixtures in a separate
process with this optimization disabled as well as in their default mode.
2026-10-11 16:06:57 +02:00
Adrià Franch ac1c3b775b fix(agc): read the base level of a mipmapped view through unnormalized coordinates only where the hardware does
Multi-level views, and views starting above mip 0, are bound as their base
level only when every use of the image is image_sample_lz through an
unnormalized S# without minimum LOD or LOD bias, the case measured exact on
RDNA2. Selected-LOD, implicit-LOD and offset uses keep throwing.

The recompiler records selected-LOD uses (SamplerUseNonZeroLod) and proves
the case per image (imageUnnormalizedBaseLevel); the shader disk cache
format goes to 27.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:07:25 +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
Adrià Franch 05b3f6e40d fix(agc): update the DescriptorBinding size assert for samplerIntegerBorder
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 03:59:03 +02:00
krishnaraya 1ce9790481 fix(agc): pick float or integer sampler border colours from the paired images
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 21:57:09 -04:00
Adrià 4f3acc7b02
Merge pull request #2797 from Celegans12/perf/dominance-intersection-storage
perf(recompiler): reuse dominance intersection storage
2026-10-11 00:45:47 +02:00
Celegans12 48693920aa perf(recompiler): reuse dominance intersection storage
Intersect copied fixed-point sets without allocating each intermediate.
Preserve sorted results and the existing analysis iteration order.
Compact oversized storage only when publishing a changed set.
Keep an allocating control switch outside the cache key.
Compare complete fixed points over empty intersections, loops and DAGs.
Register the same equivalence fixtures for the allocating control.
2026-10-10 16:08:00 +02:00
Dean Galvin 2fb7a77acc feat(shader): note which guest buffer elements a shader loads from
DescriptorBinding gains bufferRead next to bufferWritten and bufferAtomic,
filled from the resource tracker's per-buffer read flag and kept in the
shader disk cache (format 24).
2026-10-10 23:45:38 +13:00
Celegans12 7144bb57ee feat(shader): implement compute LDS beyond the device's shared memory limit
Route oversized compute LDS through a per-workgroup storage buffer.
Retain native shared memory for programs that fit the device limit.
Carry the spill stride through prepared shader results and dispatches.
Preserve the stride in the full-result codec and its round-trip fixture.
Keep the Linux RecompileResult size guard aligned with the added field.
Exercise interleaved workgroups and the device-limit boundary.
This topic contains only the LDS change and its reviewer-requested repair.
The base includes upstream's separately merged NaN result correction.
2026-10-10 10:56:56 +02:00
Adrià Franch 89a115283d fix(shader): update the ImageResource size check for flatLineCompatible
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-10 09:35:34 +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
Adrià 1a668689d6
Merge pull request #2133 from queeeef/fix/agc-3d-access-on-2d-texture
fix(shader): bind 2D textures to 3D sample and load accesses as measured
2026-10-10 08:25:22 +02:00
Adrià 03bf8ff335
Merge pull request #2526 from Celegans12/perf/agc-registration-frontend-pair
perf(agc): reuse frontend within shader registration pairs
2026-10-10 08:20:52 +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
russhil 4a2e60546c feat(shader): emulate fragment barycentrics with a generated geometry stage
Without VK_KHR_fragment_shader_barycentric the recompiler dropped
v_interp_p1_f32 and made v_interp_p2_f32 read the attribute Vulkan
interpolated, but never wrote the I/J VGPRs: a pixel shader that read
them for anything else got 0, and v_interp_mov_f32 of P10/P20 (or P0 of
an interpolated input) needed PerVertexKHR and failed pipeline
validation.

The fallback translation now loads the I/J VGPRs and marks v_interp_p1
and v_interp_p2 with witnesses. After the front-end simplification
(SSA, folding, DCE, read-lane and masked-select elimination),
HostInterpolationChecker requires every p2 to read its J, and its p1's
I, from a barycentric of the parameter's kind, every p1 result to reach
only the matching p2 (through a write masked by the p2's EXEC, whose
inactive lanes keep the old register), no parameter read as raw
vertices and no slot shared by different interpolations. It then
lowers the witnesses to the host-interpolated attribute as before,
reruns DCE and masked-select elimination, and accepts the program only
when no barycentric read remains, so reusing the I/J VGPRs after
interpolating keeps host interpolation and the previous SPIR-V. Any other
program is translated with the barycentric path and marked as emulated:
its barycentrics become vec3 inputs (NoPerspective for the linear
weights) and its per-vertex parameters flat vec4[3] inputs, placed in
the free locations by LayoutBarycentricEmulation. The driver then
inserts a generated geometry stage between the vertex and fragment
shaders that copies the position and parameters, writes the one-hot
weights of each vertex and the three vertices' parameter values, so the
rasterizer interpolates the same weights the extension provides.

The stage needs geometryShader and a vertex shader before it;
tessellation, mesh and rect-list pipelines, other primitive types and
interfaces beyond 32 locations or the device's geometry limits throw.
CompiledShaderArtifact carries the emulation (shader disk cache format 21).

Hardware snaps window corners to 1/256 pixel before interpolating
(measured by yahya308 on an RTX 4070 Laptop and a Radeon 780M in #1547);
lavapipe snaps only for coverage (lp_rast.h FIXED_ORDER 8), so the test
accepts either reference per pixel at the original 1e-4 margin.

Sources:
- v_interp_p1_f32/v_interp_p2_f32/v_interp_mov_f32 semantics (P0,
  P10, P20, I/J): AMD RDNA2 ISA, "Parameter Interpolation".
- BaryCoordKHR/BaryCoordNoPerspKHR weights and PerVertexKHR vertex order:
  SPV_KHR_fragment_shader_barycentric, VK_KHR_fragment_shader_barycentric.
- Geometry shader triangle input order and provoking vertex: Vulkan
  specification, "Primitive Topologies" and "Geometry Shading".
- Interpolation decorations decided by the fragment input: Vulkan
  specification, "Interpolation Decorations".
- lavapipe interpolation: Mesa src/gallium/drivers/llvmpipe/lp_rast.h
  (FIXED_ORDER 8), lp_setup_tri.c (subpixel_snap for coverage only),
  lp_state_setup.c (init_args, plane setup from float positions).
2026-10-10 03:24:50 +02:00
Celegans12 0eebe23a12 perf(agc): reuse frontend within shader registration pairs
Retain an immutable decoded program and structured control flow while
preparing the registration variants for one shader.
Match owned code, stage and the complete SWAPPC inputs before reuse.
Translate and plan each variant separately using its current inputs.
Keep cache keys and persisted artifact layouts unchanged.
Provide a cache-neutral off switch for controlled comparisons.
Cover identity, lifetime, failed replacements and variant equivalence.
2026-10-10 02:53:50 +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 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 e4c604d64e fix(shader): update linux shader cache structure size assertions 2026-10-08 18:43:06 +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 2d93308780 fix(shader): preserve main behavior in cached specialization 2026-10-08 17:37:25 +03:00
boykopovar ca40b70b60 fix(shader): correct descriptor size check in disk cache decoder 2026-10-08 17:29:46 +03:00
boykopovar 05fa426112 perf(shader): eliminate prepared-path overhead in data, descriptors, dispatch, bda 2026-10-08 17:29:17 +03:00
boykopovar 7431e5a6e1 perf(shader): restore prepared vertex inputs + harden buffer specialization 2026-10-08 17:28:55 +03:00
boykopovar 7f56e04157 fix(shader): validate and serialize pipeline specialization constants (wip) 2026-10-08 17:28:35 +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 1174069239 feat(shader): select image and sampler resources at runtime 2026-10-08 17:26:34 +03:00
boykopovar 27439c43cc feat(shader): version runtime abi + freeze device capabilities 2026-10-08 17:26:01 +03:00
boykopovar b08c410b97 feat(shader): move buffer accesses to runtime descriptors via bda 2026-10-08 17:25:14 +03:00
boykopovar f26c8b6112 refactor(shader): separate compiled artifacts from invocation data 2026-10-08 17:21:17 +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
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