Main refuses every storage view of a depth or stencil plane that is not
2D. A 2D array view is now taken as the plane only at layer 0 with one
layer; a view with more layers or another base layer still throws.
A one-layer 2D array view replaces a 2D view of the plane like any other
view, so a store back through the replaced 2D view throws. The depth
storage dispatch test runs one-layer views of both planes and checks
that the depth and stencil tests see what they stored, and that a
two-layer view still throws.
A storage view of a depth or stencil plane was taken for the depth surface
only as a 2D image. A 2D array view of layer 0 with one layer and the
plane's extent and format is the same plane, and now gets the plane's
storage image too; a view with more layers still takes the ordinary path.
The plane's storage image is recreated when the view's dimension changes.
PPSA01289 (Sackboy: A Big Adventure) binds the stencil plane of its
2880x1620 D32S8 surface as a one-layer R8_UINT 2D array from a compute
shader during presentation, and read guest memory instead of the stencil.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since #1463, every f16 operand read decodes sign, exponent and mantissa
with integer operations, 8 to 12 instructions more per read than
UnpackHalf2x16 on NVIDIA. The decode works around an AMD Windows driver
that folds the mask of a byte-selected half into UnpackHalf2x16.
A new target flag, float16DenormPreserve, is set when the device reports
shaderDenormPreserveFloat16 and denormBehaviorIndependence ALL, the same
rule as float64DenormPreserve. With it, EmitF16BitsToF32 uses
UnpackHalf2x16 and declares DenormPreserve for 16-bit floats, under
which SPV_KHR_float_controls keeps unpacked subnormal halves. NVIDIA
returns the default NaN for every NaN half, so the NaN bits are rebuilt
from the half: its sign at bit 31, its payload at bits 22:13. The
DenormPreserve 16 mode is declared once, shared with the native f32 to
f16 conversion. Without the flag (lavapipe; RADV reports 32_BIT_ONLY)
the integer decode stays. Halves of fields narrower than 16 bits, such
as SDWA byte selects, keep the integer decode on every device.
The flag is serialized (request version 17) and part of the cache key.
New test agc_driver_f16_to_f32_unpack converts all 65,536 halves, with
nonzero high bits, through v_cvt_f32_f16, and byte-selected halves
through v_add_f16 SDWA, with each decode the device supports, and
compares the bits with an exact decode. agc_driver_f16_to_f32_unpack_spirv
checks which decode the SPIR-V uses with and without the flag.
agc_driver_layered_volume_submit submits a DCB whose context registers
alone describe a 3D render target (CB_COLOR_ATTRIB3 resource type 3D,
CB_COLOR_VIEW slices 1-3) and set PA_CL_VS_OUT_CNTL USE_VTX_RENDER_
TARGET_INDX, with both shaders registered before the draw. It checks
that each band lands in its own slice. Removing the layer export from
the vertex stage info in Driver::draw, or the bit-18 read in
DecodeState, fails it. Two more runs draw the volume under DCC keys:
uncompressed keys stay uncompressed, and 0000 fast-clear keys leave
every texel the draw does not write at the clear color and end
uncompressed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A color view of several depth slices of a 3D target (CB_COLOR_VIEW
SLICE_START < SLICE_MAX) was rejected as "color views of several array
slices are unsupported". Titles render such volumes with one draw: an
instanced draw whose vertex stage picks each primitive's slice through
the misc position export (PA_CL_VS_OUT_CNTL USE_VTX_RENDER_TARGET_INDX
with VS_OUT_MISC_VEC_ENA).
Depends on #423, which carries PA_CL_VS_OUT_CNTL to the recompiler.
- DecodeColorBuffer takes the slice range of a 3D target
(ColorTarget::layers, clamped to the surface's last slice), allows DCC
on 3D targets, and sizes a tiled 3D target by its whole surface
(SurfaceForTarget moves to State.cpp for that). DecodeState sets
State::renderLayers and, for a layered draw, USE_VTX_RENDER_TARGET_INDX
(State::layerExports); color targets of different slice counts and
layered draws with a depth target throw.
- The driver adds State::layerExports to the PA_CL_VS_OUT_CNTL bits #423
passes (it already passes VS_OUT_MISC_VEC_ENA), so the render target
index reaches gl_Layer only for layered draws and stays ignored
elsewhere as before.
- A vertex stage writing gl_Layer declares ShaderViewportIndexLayerEXT
and SPV_EXT_shader_viewport_index_layer when the device offers them
(the device enables VK_EXT_shader_viewport_index_layer when present),
instead of SPIR-V 1.5 ShaderLayer, which a Vulkan 1.1 device cannot
take. Graphics validation accepts the capability, the extension and a
vertex Layer output when it is enabled, and a per-primitive Layer
output of a mesh stage.
- The resident target's attachment view is a 2D array view of the
slices, and the framebuffer has that many layers (Framebuffer and
AcquireFramebuffer take the layer count).
The execution test agc_driver_layered_volume_draw draws three bands,
one per layer, into slices 1..3 of a 4-slice 64 KiB-tiled volume and
reads every slice back: slice 0 stays untouched and slice 1 + L holds
only band L. It also recompiles a mesh program with the layer export and
validates it against the layered state. The mesh case does not draw:
lavapipe's LLVM backend aborts compiling that mesh pipeline ("Cannot
emit physreg copy instruction"), which a test cannot catch. On RADV the
same mesh draw renders the bands into the right slices.
PPSA14632 (LEGO Horizon Adventures) renders its 64x64x64 RGBA16F
translucency lighting volumes (two DCC targets, 64 instances) this way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DecodeShaderStages accepted only clockwise triangle patches and no HOS
level clamp narrower than [1, 63], so terrain drawn as TRI_CCW quad
patches with VGT_HOS_MAX_TESS_LEVEL 9 aborted. Quad domains and TRI_CCW
now decode and emit Quads and VertexOrderCcw; isolines, other
partitionings and point or line output still throw.
VGT_HOS_MIN/MAX_TESS_LEVEL are read per draw and pushed in the last 8
bytes of the pre-rasterization push constant slot, where the hull reads
them to clamp its factors; they stay out of every key and the recompile
request, so one hull serves every level. Zero, negative and NaN outer
factors still cull; clamps outside 0 <= min <= 63, max >= 1,
min <= max throw. Quad hulls write four outer and two inner factors at
a 24-byte stride, the layout Mesa's TF ring store uses.
On memory outside the write watch a write-back compares guest memory with
the bytes saved at the upload and leaves the 64 KiB blocks that differ to
the CPU. A store of some of an image's units marked the whole saved copy
invalid, and a fill turned into an image clear or an image copy never
saved the bytes under it, so the next store had nothing to compare and
wrote its layers whole over what the CPU had written since.
The saved copy is now valid per tracked unit. A store, a drop or a stale
eviction invalidates only its own units, a CPU-path store makes the units
it stored valid again, and the write-back compares each run of selected
units that still has saved bytes. FillClear and CopyFrom save the bytes
of the units they fill when the surface is not watched, as an upload does.
Results over a range that left the watch while they were pending still
have no saved bytes; storing them now throws instead of overwriting the
CPU's writes (technical debt).
The hardware ignores the LOD for unnormalized coordinates and samples the
base level. A sampler used only by explicit-LOD, implicit-LOD, bias and
gradient samples gets a pipeline specialization constant set from its
unnormalized bit when the binding plan is prepared. When the constant is
set, each of its samples is emitted as OpImageSampleExplicitLod with LOD 0,
which is what Vulkan requires of unnormalized samplers. Otherwise the
sample keeps its own form. Such shaders are no longer refused.
A sampled copy of a depth plane is kept while the surface's write
serial is unchanged. Storing a written storage view back into the
depth image did not bump the serial, so a later draw or dispatch that
sampled the plane as an array or cube kept the values from before the
store. The store-back bumps it now.
The dispatch test samples the plane through a copy, stores to it in a
dispatch and samples it again.
Draw depth and stencil, read and rewrite each plane in compute
dispatches, and check the stored values with depth-tested and
stencil-tested draws, a following dispatch and the flushed guest bytes.
The array view, multisampled and two-view refusals are checked through
dispatches as well.
Compute shaders bind the depth and stencil planes of a depth surface as
storage images, to read the depth or to write it before later draws
test against it. The driver kept depth on the GPU and refused such
bindings.
A depth surface now owns one storage image per plane (R32_SFLOAT or
R16_UNORM for depth, R8_UINT for stencil). Binding copies the plane
into it through a staging buffer; a written element is copied back into
the depth image during write-back, so the next draw sees the shader's
depth.
A view whose extent differs from the surface's means the memory was
reused for another texture and takes the ordinary texture path; this
applies to sampled views of a depth surface too.
PPSA15304 copies depth and stencil through storage images every frame.
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.
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.
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
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>
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>
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.
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>