The data-hit verification keeps this branch's pending-span query on its ShaderMemory and takes #5958's CaptureInvocation, which retries a capture with converted samples.
Keeps this branch's tagVariantRuns declaration next to main's dumpRequest suffix parameter in Driver.hpp, and reads #6232's pointer-chain test words from its table's snapshot, where this branch keeps them.
Resolves the shader debt file bullet by bullet: main's EmitSetAttribute entry, and this branch's PERSP_SAMPLE/LINEAR_SAMPLE and pixel-position entries.
10_11_11_UNORM (format 30) images are read and written through an
R32_UINT view, so the hardware sampler cannot filter them. image_sample
of a 2D or 2D array view now fetches the one or four texels at LOD 0,
decodes them and blends them in the shader, with bilinear weights on
the 8-bit grid, for point or bilinear filtering with wrap or
clamp-to-edge addressing.
A shader gets the converted sample mode only after a converted
descriptor reaches one of its sampled images: that capture throws
ConvertedSampleRequired and the driver prepares the program again
with GuestContext::convertedSamples.
The R10_G11_B11 float format is sampled natively since #7271 and keeps
its refusal on the converted path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A remapped vertex shader reads the scale and offset from the push block, but
the pipeline's push constant stages only counted programs with push data of
their own, so a shader with none got no stage in a non-GPL layout and its
scale and offset never arrived. The result now says it reads them, and the
push stages include the vertex stage for it. The draw test gains a fetchless
remapped vertex shader with no push data.
The remap mode stays a specialization constant and cache key input. Scale and
offset are two push words at byte 104, patched per draw, so draws that differ
only in depth range share one pipeline. The recipe, journal and serializer
carry the mode only.
Only the remap mode changes what the vertex shader compiles to. Scale and
offset are specialization constants that each materialization takes from
its own request, so they no longer split the compiled variant per depth
transform. The result memo hashes them with the snapshot instead, so one
prepared vertex shader does not hand the module specialized for one
transform to a draw with another. sameVertexInfo repeated its first
comparison without paClVsOutCntl; the repeat is dropped.
A test pins the version 27 layout (the remap fields are the last bytes
of a vertex request), a version 25 replay, truncation and trailing-data
refusal, and that scale and offset leave the cache key alone while the
mode does not. agc_depth_range_remap materializes one prepared vertex
shader with two transforms and checks that each gets its own module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without VK_EXT_depth_range_unrestricted a viewport depth range outside
[0, 1] threw. Minecraft (PPSA17221) draws its world with scale 1 and
offset 0 in negative-one-to-one clip space, so its depth is the raw z/w
clamped by PA_SC_VPORT_ZMIN/ZMAX. On such a device the vertex shader now
scales clip z so z/w is the console's depth, the pipeline clamps depth at
the console's bounds, and two clip distances restore the near and far
planes that depth clamping removes. Mode, scale and offset travel as
pipeline specialization constants from the draw's vertex stage info, in
both the per-draw and the registration-module path; mesh and tessellation
draws with such a range still throw.
The console's depth clamp bounds are clipped to [0, 1]: exact for a
normalized depth target or none, and reported once for a tested or
written float depth target, where a depth below 0 is stored as 0
(Minecraft clamps its float depth target to [-1, 1]). The shader
validator accepts the remap's two extra clip distances next to the one
a program may already export.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Main's #6402 stores to image tables: a table with a written entry takes
the fresh path, as it does on main, and so does one without a sampled
table binding, whose entries the fresh path rejects. The shared place
step uses the binding as a pointer. Main's #6614 frees descriptor sets
from shared pools one by one, while this branch keeps only dedicated
pools, so both stay. #7101's bulk key read counts its unmapped-page
skips as unmapped, and its key words sit next to this branch's write
stamp and compared runs.
NVIDIA's compiler predicates a branched LDS load at no cost, while the
selected form costs about 2.5 instructions per dword and splits a
ds_read_b128 into four loads (RTX 4060 measurements by @karanvora2599 in
loads therefore keep the branch of main, in workgroup, function and device
memory alike, and the sentinel dword that only they read goes away: the LDS
array is the program's dwords plus the lock again, and the device-memory
decision reserves the lock alone. The sentinel budget tests become lock
budget tests at that boundary, and the guarded-load test pins the branch.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A load selected instead of branched still accesses memory, so its fallback
element must be one no write can race with: scratch keeps element 0, LDS in
workgroup or function memory reads a sentinel dword appended after the
program's LDS and lock, and storage buffers read element 0xfffffffe, which
robustBufferAccess2 turns into a zero load without a memory access. The
device enables robustBufferAccess2 when it has it and the target carries the
flag; without it, and for LDS in device memory, buffer loads keep their
selections. The aligned high word of a buffer read re-reads the low word's
element instead of word 0. agc_guarded_memory_loads traces the emitted loads
in both device modes.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Main took request serialization versions 31 (output primitive size) and
32 (spanned waves), and #6683 uses 33, so the pixel iteration fields move
to version 34. A version 32 request keeps its spanned-waves flag and reads
no pixel iteration.
Prepared captures pass their source handle to the plan reload. When the handle
matches the request, its artifact serves the draw, so the rebuilt resource
program is not kept even if the push layout differs from the artifact's.
A handle that does not match the source is rejected before the entry changes.
Same change as f1b42dae4 on top of c00b0dfb9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
APS5_SPANNED_WAVE is now read once by the driver, which sets
SpirvTarget::spannedWaves only on a device with 32-lane compute subgroup
control and computeFullSubgroups. The recompiler no longer reads the
environment. The field is part of the cache key and of the request
serialization (version 32), so a cached or replayed module keeps the mode
it was built in.
A spanned module numbers its lanes SubgroupId * 32 +
SubgroupLocalInvocationId, which needs two full 32-lane subgroups. The
artifact records this as fullSubgroups (disk cache and pipeline identity
included), and the driver creates those pipelines with
REQUIRE_FULL_SUBGROUPS. Compute pipeline captures accept the flag with a
required subgroup size.
The two-lane fallback now catches only SpannedWaveUnsupported, thrown for
a shuffle wider than 32 bits; other emit errors surface as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves#4943's draw relocation against this branch: tagVariantRuns, which now builds a draw variant's runs and words, also records the captured words it shares with a table's supplied blocks, per region as before. A variant whose runs relocation splits keeps sharing for the draw cache's word compare, and skips the vouched validation, which already requires one run per region.
The ancillary input's sample number (bits 11:8) was the SampleId
builtin in every pixel shader. Vulkan enables sample shading for a
shader that uses SampleId, so a shader that reads the sample field or
the whole ancillary word ran once per sample on a multisampled draw
even when the guest runs it once per pixel (PS_ITER_SAMPLE clear), and
FragCoord, and with it POS_X_FLOAT and POS_Y_FLOAT, moved to the
sample. The sample number is now SampleId only when the request says
the guest iterates the shader, and otherwise 0, the iterated sample
number of a pixel the hardware does not iterate.
The other inputs that depend on the sample follow where the console
evaluates them:
- PERSP_SAMPLE and LINEAR_SAMPLE are interpolated with
InterpolateAtOffset at sample 0's offset in a shader the guest does
not iterate, and in an iterated one at the offset of the host's
SampleId, read from a table of the guest's locations, because Vulkan
leaves plain barycentrics under sample shading at an
implementation-defined position (lavapipe uses the pixel centre).
- PERSP_CENTER and LINEAR_CENTER are interpolated at offset 0 in an
iterated shader.
- POS_X_FLOAT and POS_Y_FLOAT follow SPI_BARYC_CNTL: the centre is
floor(FragCoord) + 0.5 under sample shading, a sample position in a
shader the guest does not iterate is the centre plus sample 0's
offset, and POS_FLOAT_ULC gives the upper-left corner.
The offsets need InterpolationFunction (sampleRateShading) and the
fragment-shader barycentric path; devices without them keep the host's
default positions. The shader disk cache format goes to 39.
Brings the series onto main 4e9a2a871 (#1193, #1201, #1202, #6060,
#6333 and the descriptor word tracking of 682b07eb0).
Resolutions:
- ShaderMemory: the column memo's supplied and spilled blocks now sit
on #1193's page vector, page pool and word masks. Pages are found by
binary search, since a spilled block can add many pages, and a block
placed into a lazily read page fills the rest of the 16-word blocks it
touches, so a later lazy read never overwrites placed words.
- ResourceMaterializer and SharedValues: the selected-record path and
the words index carry availableDwordMask, as the snapshot hash does.
A selected record's dwords past the end of its buffer count as read
zeros, which is what a scalar buffer load returns there, so a key past
the heap still selects a null image without reading the heap.
- DescriptorCache: kept table sets live beside #1201's recycled sets.
- Tests and CMake: both sides' tests are kept.
A result memo miss resolves every image table of the snapshot again: ResolveImageTables decodes each
distinct T#, builds the element and poison indices and fills one map record per key, which is most
of a miss for a large table. A miss whose table did not change is common: another buffer, flat SRT
word or user data word changes the result key while the column is the one the column memo shares.
ResolveImageTablesShared keeps the last resolutions and hands one out by reference when the variant
(its id), the reducing state of the direct samplers paired with table images and every column match:
base, size, records, keys, fault, outside, and the column's codes and distinct T#s by identity. The
memo holds those shared values, so a block it compares cannot be freed and reused for other words;
a column read again is a new block and resolves again even when its words are equal.
It keeps at most 4 resolutions per variant and 256 in all within 8 Mi words, counting the columns
their keys keep alive, least recently used first out, under one mutex. A resolution is immutable
once made and is shared between threads read-only. APS5_NO_TABLE_RESOLUTION_REUSE=1 turns it off.
ImageTableReuseCounts gains the resolutions made and the ones shared.
A capture that reused a memoized image table column still copied it: each record's state and code
and the distinct T#s went from the memo into the snapshot, the snapshot rebuilt its codes from the
states, and the column's per-record vectors were allocated before the memo was even looked up. Every
Recompile then hashed the codes and the distinct T#s word by word for the result memo. On a
32,768-record column of distinct T#s that was about 155 us of copying per capture and 410 us of
hashing per Recompile here, for a column nothing wrote.
A column is now built once into immutable shared values (SharedValues: its codes, its distinct T#s
and its read ranges) and the memo keeps that column. A capture that reuses it takes a reference, so
two captures of an unchanged table share one column and copy nothing, and the per-record vectors
are allocated only when the column is walked. The codes index the column's own distinct T#s
(ImageTableColumnSnapshot::words) instead of a list shared by the shader's columns, so every column
of a shader is shared whole, not only the first. A column read again after a write is a new one:
the snapshots holding the old one keep it unchanged. The result memo hashes each shared value once
and keeps the hash in it; equal contents hash equal whether they are shared or not.
ImageTableReuseCounts gains the reuses that shared a column and the columns built.
The SRT evaluator records every raw read with the user-data word its address is
based on, and marks a word used as a plain value. The result also names the
user-data word behind each shader-data dword.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8_8_8_8 and 16_16_16_16 targets with COMP_SWAP 2 or 3 keep the exported
alpha in their first channel and red or blue in their fourth, so a plain
Vulkan blend would run the alpha equation on a color channel. Such draws
threw "blending into a color target with a reversed component order is
not implemented" (Gran Turismo 7, #4782).
The AMD SI and CIK register references describe CB_TARGET_MASK in the
order of components after blending and before COMP_SWAP is applied, and
RADV programs CB_BLENDn_CONTROL without regard to the swap while it
renders R4G4B4A4, B4G4R4A4, R5G5B5A1 and B5G5R5A1 through SWAP_STD_REV
and SWAP_ALT_REV and passes the CTS blend tests on Navi10. The color
block is therefore taken to blend in export order: the alpha equation
and source-alpha factors use the exported alpha and the destination
alpha is the first channel.
TranslateReversedBlend keeps the factor pairs Vulkan applies per channel
in the pipeline. Other source factors are computed by the pixel shader
from its MRT0 export (clamped for UNORM targets, as BLEND_CLAMP says)
and handed to the blend as a dual-source second color, which the
fragment output post-pass adds at location 0 index 1; when both
positions need it, the export is premultiplied by its source factor.
The code is a pipeline specialization, serialized in request version
26, and stays out of the prepared shader key.
sRGB, SNORM and integer targets, constant and second-source factors,
destination alpha in the color equation, destination factors the two
equations do not share, different color and alpha operations, MIN or
MAX with factors other than ONE, other color exports when the shader
computes factors, and devices without dualSrcBlend still throw, now
with the blend control, mapping and format in the message.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move bounded vertex folding and empty-image feedback into separate topics. Keep the replay coverage proof, stage contracts, finite-reference tests and mapped cache regressions; version the stencil layout separately as format 34.
Co-Authored-By: Codex <noreply@openai.com>
Preserve instance and triangle order with one dynamic-reference shader pipeline. Require bounded references, complete export coverage, replay-safe mask control and resources; refuse unsupported side effects and feedback.
Co-Authored-By: Codex <noreply@openai.com>
An s_swappc_b64 whose target is not resolved compiles to a faulting call
only at the fallback the table-call linker emits; the linker lists those
calls in the request (GuestContext::linkedCallFallbacks), and every other
computed call is refused at compile time again, as on main. The new
agc_driver_compute_table_calls test runs a linked two-record table on
Vulkan in wave32 and wave64, with SCC 0 and 1 reaching the callee, and a
record past the table reporting the UnlinkedCall fault.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With GE_CNTL.BREAK_WAVE_AT_EOI clear, the instances of a tessellated draw share
hull threadgroups: the relative patch index counts on through the instances,
while the host's PrimitiveID restarts with each one. The local stage now passes
its draw-relative instance (InstanceIndex - BaseInstance) to the hull, which
numbers its patches instance * patches-per-instance + PrimitiveID before taking
(mod PRIM_GRP_SIZE) mod NUM_PATCHES. Instances that start a threadgroup anyway
(BREAK_WAVE_AT_EOI, or patches per instance a multiple of the group) keep the
per-instance PrimitiveID.
This replaces #4201's refusal of a hull or domain that uses its relative index
as data while BREAK_WAVE_AT_EOI is clear, and builds on its decode of the bit
(breakAtInstance, request serialization and draw keys).
- Hull and domain cache keys hold the patches per instance instead of the bit;
an indirect draw recompiles its hull and domain for each record's instance
count.
- The device enables shaderDrawParameters when available. Without it, a hull
using its relative index as data in such a draw is refused, as is a domain
doing so (passing the instance on to the domain is not implemented).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plan runtime-indexed T# and S# columns read through a 64-bit pointer.
Preserve the console offset alignment, signed immediate and key bounds.
Track the descriptor dwords consumed by each table in its invocation.
Reject table reads when a writable buffer or storage image overlaps them.
Cover the overlapping-storage fault and isolated second-table behavior.
Materialize runtime-indexed image and sampler records into descriptor
tables while keeping descriptor selection in the shader. Report invalid
active entries through the existing deferred fault buffer.
Bind fragment tables in the fragment set when pipeline libraries split it,
and reserve a physical descriptor for empty tables. Reject unsupported
sampler, border, cube and anisotropic pairings per entry. Keep one column
per heap access shape, one T# shared by several image operations included
(milescoviello's #5550 case), and narrow it to the union of its keys'
domains when every key has one (SleglJan's key-domain changes). Plan
columns read at (key << n) & mask over their whole domain (tibzejoker's
fix). Read records in runs through SleglJan's bulk reader, and reject
entries whose surface has no mapped byte, as Tamalero proposed. Store
table columns in disk plans; update the runtime ABI and disk format. Bound
table-sized entries in the resource cache (65,536 table elements) and the
dispatch cache (256 MiB of captured words), evicting LRU.