Skip to content

fix(shader): gather a 2D image's image_gather4_l at its LOD - #12790

Open
yinx080 wants to merge 7 commits into
boykopovar:mainfrom
yinx080:fix/shader-gather4-l-lod
Open

yinx080 wants to merge 7 commits into
boykopovar:mainfrom
yinx080:fix/shader-gather4-l-lod

Conversation

@yinx080

@yinx080 yinx080 commented Oct 29, 2026 •

Copy link
Copy Markdown

What

image_gather4_l on a 2D image gathered the base level: EmitGatherOp emitted OpImageGather without the instruction's LOD, because SPIR-V gathers take no Lod operand without SPV_AMD_texture_gather_bias_lod. Such a gather now picks its level in the shader and fetches the four footprint texels there (EmitTwoDimensionalGatherLod, OpImageFetch). The level follows the sampler: samplers used by image_gather4_l are specialized on their S# LOD state (MIN_LOD, MAX_LOD, LOD_BIAS, MIP_FILTER none, CLAMP_X/CLAMP_Y; PipelineSpecialization::GatherLodBase/GatherLodBiasBase, new use SamplerUseGatherLod); λ = lod + bias, raised to MIN_LOD and lowered to MAX_LOD, raised to the T# MIN_LOD above the base level (the view's own minimum LOD; images specialized through GatherImageMinLodBase), clamped to the view's levels in floating point, nearest level with ties down as in Vulkan (ceil(λ + 0.5) - 1). For MIP_FILTER none the level is the T# MIN_LOD above the base level rounded down, clamped to the view's levels, so the fetch never reads below the view's minimum LOD. The wrap modes are applied to the footprint as GuestSamplerResource maps them. Fetches do no filtering, so integer and unfilterable formats are fine with any guest filter. Images or samplers from a table and the border modes (4, 6) keep the base-level gather.

Semantics: RDNA2 ISA, MIMG opcodes (IMAGE_GATHER4_L: gather 4 single-component elements (2x2) with a user LOD); the 2x2 footprint and its order follow the Vulkan specification, Texel Gathering: i0 = floor(u * width - 0.5), j0 = floor(v * height - 0.5), results (i0, j1), (i1, j1), (i1, j0), (i0, j0); level selection follows Vulkan's LOD operation for a nearest mip filter. Not measured on hardware; the unmeasured parts are in docs/dev/debt/shader.md.

Effect on PPSA23276: UE5's Nanite instance and cluster culling tests occlusion with four image_gather4_l at the HZB mip that covers an object's screen rectangle and takes the minimum. Reading 2x2 texels of mip 0 instead made the test non-conservative: wall panels behind narrow gaps were culled in both the main and the post pass, so the recess in the right wall and part of the end of the first hallway rendered black, as reported in #8587.

New execution test ImageGatherLod: a 16x16 RGBA8 image with five mips, image_gather4_l with 32 LODs per variant (integers, 0.49/0.5/0.51/1.49/1.5/2.5, negatives, 4294967296.0, +inf, NaN) in twelve variants (clamp to edge, wrap, mirror, bilinear x/y, MIN_LOD 1.25/MAX_LOD 2.75, MIN_LOD = MAX_LOD = 2, LOD_BIAS -0.75, MIP_FILTER none, 8_8_8_8_UINT with a bilinear sampler, T# MIN_LOD 2.5, MIP_FILTER none with T# MIN_LOD 2.5, border modes 6 and 4 on the base-level gather), against the footprint computed on the CPU. The two T# MIN_LOD variants run only on devices with VK_EXT_image_view_min_lod, because without it the texture path refuses such a descriptor, or on the shader-clamp lane a gather at that clamp. Mirror-once coverage is left for the samplerMirrorClampToEdge fix: GuestSamplerResource creates MIRROR_CLAMP_TO_EDGE without that feature, which adds validation errors of its own.

Tested

Windows 11 Pro 10.0.26200, NVIDIA GeForce RTX 3060 Laptop GPU, driver 592.82, GCC 15.2.0 (WinLibs UCRT).

Rebased onto main 086c0686c8. main now gives 57536 to MirrorUnnormalizedBase, 57600 to MinLodBase and, since #9183, 57664 to VolumeGatherBase (64 ids each), so the gather constants moved above them. main also raised ShaderInfo::MaxSamplers to 64 (12b8923), so the sampler ranges are 64 wide: GatherLodBase 57856 and GatherLodBiasBase 57920 (64 samplers each), GatherImageMinLodBase 57984 (room for 128 images, up to 58111). They also start above the ids open PRs take: #10489 raises MaxImages to 128 and moves FmaskFormatBase to 57728-57855, #14621 adds SamplerLodBiasBase at 57664; DeviceSamplerClassBase (#7642 and others) starts at 58368. static_asserts in DescriptorBindingBuilder.cpp tie each range to ShaderInfo::MaxSamplers or MaxImages, from VolumeGatherBase up to ImageComponentBase. Otherwise the diff is the one reviewed before; SpirvImageEmitter.cpp keeps #9183's 3D gather and this PR's 2D explicit-LOD gather side by side. GatherImageMinLodBase carries the same T# clamp as MinLodBase from fe18496, but in 1/256 steps and on every device, where MinLodBase is a float written only on the shader-clamp lane, so I kept them apart. That lane still refuses a gather at a clamp of a level or more, so the two T# MIN_LOD variants stay skipped without VK_EXT_image_view_min_lod.

  • With ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1 and VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, the 31 image gather, sample and table tests (ctest -R '^(agc_driver_image_(gather|sample|texel_status|table|packed)|image_sample_gather)'): 30/31 on this branch at 086c0686c8, all twelve ImageGatherLod variants and fix(shader): gather a 3D image's image_gather4_lz from one slice as measured #9183's agc_driver_image_gather_volume pass, and both lanes of agc_driver_image_sample_lod_clamp pass. The one failure is agc_driver_image_table on its SYNC-HAZARD, which fails on main too. Validation IDs: VUID-VkDeviceCreateInfo-pNext-pNext, and VUID-VkSamplerCreateInfo-addressMode* from agc_driver_image_gather_volume alone, whose mirror-once sampler is created without samplerMirrorClampToEdge (main's code; this PR creates no samplers).
  • agc_driver_image_gather_lod with APS5_NO_IMAGE_VIEW_MIN_LOD=1 APS5_NO_GPL=1 (the shader-clamp lane): passes, the two T# MIN_LOD variants skipped.
  • The skip without VK_EXT_image_view_min_lod was run here by forcing the check off; this GPU has the extension. On lavapipe, the same change passed 3/3 in jotitadev's run.
  • python3 tools/check_conventions.py --base origin/main: passed.
  • Cost in PPSA23276 (The Exit 8) 01.004.000 at 1080p, first hallway, no input, measured on the previous rebase onto main 9090972b7e (the same diff except the three ids and the asserts) against main at that commit, both built the same way:
    • Variants, 150 s from an empty shader cache: 1,430 and 1,431 shader disk cache writes against 1,430 on main, 251 compute pipelines on both, cache 563 MiB against 562 MiB, recompile time 3.2-3.4 s on both. The culling samplers keep one LOD state in this scene, so the specialization adds no pipeline here.
    • GPU time, 180 s with a warm cache, APS5_PROFILE_GPU=1, last 110 s: 26.1 and 26.6 ms per present against 26.0 and 27.0 on main, 60.5 presents per 10 s against 58.8-60.2.
    • Culling dispatches (the 9x1x1 instance cull, the two 1440x1x1 cluster culls and the two indirect culls that sample the 512x512 R16F HZB), per-program GPU timestamps over 50 s, two runs each: 1.06-1.10 ms per frame against 0.91-0.92 ms on main. One cluster cull went from 226-237 us to 452-457 us, the other from 272-280 us to 226-228 us; the instance cull (108 us) and the indirect culls are unchanged. I have not separated the extra fetches from the different culling work: with the right mip the culls keep instances main drops, so the later passes get different inputs.
  • In-game result (before the rebase, on main + fix(shader): load and store 16-bit data of converted packed images #11580 + a local depth-plane fix that goes in its own PR, 300 s): the panels at the far right of the first hallway were black in every shot without this change (60-75 % of the gap region near-black) and render in every shot with it (0 %); near-black share of the whole frame 1.6-1.7 % -> 0.4-0.5 %; frame rate unchanged (55-57 presents per 10 s).

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: PPSA23276 The Exit 8 (PPSA23276) #8587
  • GPUs tested: NVIDIA GeForce RTX 3060 Laptop GPU, driver 592.82, Windows 11 Pro 10.0.26200. Reported in the comments on this PR: AMD Radeon RX 9070 XT on Windows (bunbeans2-arch), AMD Radeon RX 9060 XT on Linux with RADV (RNK123), lavapipe on Linux (jotitadev), NVIDIA RTX 2000 Ada on Linux (TechLevelZero's test run)
  • AI-assisted: yes (Claude Code); the change, the test and the results were reviewed by me

🤖 Generated with Claude Code

@yinx080

yinx080 commented Oct 29, 2026

Copy link
Copy Markdown
Author
before-200s after-200s

@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 29, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

@yinx080 thanks. Two independent reviews agree the approach is right: the footprint order matches OpImageGather's, the guard leaves every other gather variant untouched, and no FormatVersion bump is needed because the generated SourceVersion hashes SpirvImageEmitter.cpp. Two correctness cases need changes in EmitTwoDimensionalGatherLod, plus some test coverage:

  1. The mip the samples read can differ from the mip the footprint was computed for. The extent and footprint use the rounded shader LOD, but the four OpImageSampleExplicitLod samples go through the existing sampler, which applies its minLod/maxLod clamp. Example: a 16-wide texture, LOD 0, sampler minLod 2, u = 0.4: both horizontal samples land on column 1 of mip 2 instead of gathering columns 1 and 2. Please make the effective mip selection consistent with the footprint (apply the same clamp before computing it), or restrict the new path to sampler states it handles. Please add min/max-LOD tests, including fractional clamps and the mip-filter NONE case.
  2. Clamp the LOD in floating point before OpConvertFToU. Converting a value outside the u32 range (e.g. a finite 4294967296.0, infinity, or NaN) is undefined in SPIR-V, so the UMin after it can't be relied on. Please take FMin(rounded, float(levels - 1)) (keeping the FMax with 0) before the conversion, and add a large-positive-LOD regression.
  3. Integer and unfilterable formats. OpImageSampleExplicitLod on a uint/sint image, or a float32 format without linear filtering, needs a nearest sampler in Vulkan, unlike OpImageGather. Can a guest sampler with a linear filter reach this path with such an image? If that's handled, say where; if not, please add an integer-format case with a linear sampler to ImageGatherLod.cpp (the existing 1D gather has the same question).
  4. Rounding and filter coverage. All test LODs are integers and the sampler is point-filtered in x/y. Please add fractional LODs around the rounding point (0.49, 0.5, 1.5, and a negative one) and one bilinear x/y sampler, so the floor(lod + 0.5) choice and exact texel-centre sampling are pinned.

Please keep the non-zero-LOD control that fails on main and passes on the head, and include generated-SPIR-V/Vulkan validation-layer results for the added cases. For the debt entry: the sampler's LOD bias, and a descriptor min-LOD above the chosen level, also affect the explicit-LOD samples. If they aren't modelled, please say so there.

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 10489 core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp L21
core/shader/recompiler/PipelineSpecialization.hpp L55
PR 14692 docs/dev/debt/shader.md L255
PR 14871 docs/dev/debt/shader.md L255
PR 14964 core/shader/recompiler/PipelineSpecialization.hpp L55

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 907 core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 5021 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 5371 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 5609 core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Recompiler.cpp
PR 6235 docs/dev/debt/shader.md
PR 6280 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 6412 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 6488 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 6924 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 7085 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 7410 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 7642 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 7684 docs/dev/debt/shader.md
PR 7689 core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
PR 8004 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 9356 core/shader/recompiler/Recompiler.cpp
PR 9387 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 9508 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 9748 core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp
core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 9852 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
PR 10193 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 10233 docs/dev/debt/shader.md
PR 10359 core/shader/recompiler/Recompiler.cpp
PR 10652 core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
docs/dev/debt/shader.md
PR 10757 core/shader/recompiler/Recompiler.cpp
PR 11483 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 12324 core/shader/recompiler/Recompiler.cpp
PR 12619 core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp
PR 12828 core/shader/recompiler/Recompiler.cpp
PR 13486 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 13847 docs/dev/debt/shader.md
PR 13928 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 13929 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/PipelineSpecialization.hpp
core/shader/recompiler/Recompiler.cpp
core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
PR 14212 docs/dev/debt/shader.md
PR 14218 core/shader/recompiler/Recompiler.cpp
docs/dev/debt/shader.md
PR 14377 docs/dev/debt/shader.md
PR 14944 core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp
core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
core/shader/recompiler/Recompiler.cpp
PR 15548 core/shader/recompiler/Recompiler.cpp

@yinx080
yinx080 force-pushed the fix/shader-gather4-l-lod branch from fbe13f0 to 8502ef1 Compare October 29, 2026 10:56
@yinx080

yinx080 commented Oct 29, 2026

Copy link
Copy Markdown
Author

Thanks for the careful review. Pushed a revision (rebased on f7e84c4); all four points are addressed:

  1. Mip selection. The S# LOD state now reaches the shader as per-sampler specialization constants (GatherLodBase/GatherLodBiasBase, set in DescriptorBindingBuilder::Prepare for samplers with the new SamplerUseGatherLod use): MIN_LOD, MAX_LOD, LOD_BIAS, MIP_FILTER none, CLAMP_X/CLAMP_Y. The shader takes λ = clamp(lod + bias, MIN_LOD, MAX_LOD), clamps it to the view's levels and picks the nearest level with ties going down as in Vulkan's nearest mip selection (ceil(λ + 0.5) - 1), or the base level for MIP_FILTER none. The extent and footprint come from that level, and the four texels are fetched there with OpImageFetch, so the sampler can no longer move the samples to another mip. Wrap modes are applied to the footprint as GuestSamplerResource maps them (wrap, mirror, clamp to edge, the mirror-once modes as mirror-clamp to edge). Samplers from a sampler table and the border modes (4, 6) keep the base-level gather, selected through the same constant; both are in the debt entry.
  2. Float clamps before conversion. The LOD is clamped with NClamp (a NaN takes the lower bound) to [MIN_LOD, MAX_LOD] and then to [0, levels - 1] before OpConvertFToS; the floored coordinates are clamped to ±2^24 before theirs. The test covers 4294967296.0, +inf and NaN.
  3. Integer and unfilterable formats. Fetches do no filtering, so the guest filter no longer matters on this path. The test has an 8_8_8_8_UINT image with a bilinear sampler. I did not change the 1D gather (EmitOneDimensionalGatherLz); it has the same question and is better a separate PR.
  4. Rounding and filter coverage. LODs 0.49, 0.5, 0.51, 1.49, 1.5, 2.5, -0.4, -0.7 and -3.0 among 32 per variant; variants: clamp to edge, wrap, mirror, bilinear x/y, MIN_LOD 1.25/MAX_LOD 2.75, MIN_LOD = MAX_LOD = 2, LOD_BIAS -0.75, MIP_FILTER none, UINT with a bilinear sampler. Ties pinned: 0.5 -> level 0, 1.5 -> 1, 2.5 -> 2.

Results (Windows 11 Pro 10.0.26200, NVIDIA GeForce RTX 3060 Laptop GPU, driver 592.82; ctest -R "agc_driver_image|image_sample|image_" -V with ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, the loader confirmed inserting the layer):

  • main f7e84c4 + the new test: 76/78; the control fails as before (clamp to edge, point: thread 1 (LOD 1.000000, level 1) component 0 is 108, expected 113), plus agc_driver_image_table (a validation SYNC-HAZARD, on main too).
  • This branch: 77/78; agc_driver_image_gather_lod passes all nine variants, agc_driver_image_table as on main. VUID- counts are identical to main (no new validation errors, the generated SPIR-V of the added cases included).
  • PPSA23276, first hallway, 300 s (main + fix(shader): load and store 16-bit data of converted packed images #11580 + a local depth-plane fix, as before): the panels render in every shot (0 % of the gap region near-black, 60-75 % without this PR), frame rate unchanged.

Debt entry: LOD_BIAS is modelled now; a T# MIN_LOD above the chosen level, LOD_BIAS_SEC, the tie rounding and a linear MIP_FILTER (taken as nearest) are listed as unmeasured.

Mirror-once is not in the test: GuestSamplerResource maps it to MIRROR_CLAMP_TO_EDGE without enabling samplerMirrorClampToEdge, which the validation layer reports on sampler creation (VUID-VkSamplerCreateInfo-addressModeU-01079) independent of this change; worth its own fix.

The overlap bot lists #9183, which also changes EmitGatherOp; I'm happy to rebase on it if it lands first.

@oneandonlydean oneandonlydean self-assigned this Oct 29, 2026

@XairRrR XairRrR left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — approved

The PR merges cleanly onto main and builds. The new ImageGatherLod test passes with the validation layer loaded and no VUID- lines, and it fails on main's production code with the error the description quotes. The neighbouring gather and sample tests pass on both trees. I found nothing blocking. Two small follow-ups are below.

What I ran

  • PR head: 8502ef15f7557f7972c066f5b24e07ee6e5ee9ba
  • main: ba5b5330b37724bca4b99f30faab504653635a04
  • Tree under test: main with the head merged in using --no-ff (no conflicts), in a clean worktree.
  • Machine: Arch Linux 7.2.11-zen, GCC 16.2.1, Intel Core i9-14900KF, NVIDIA GeForce RTX 4070 Ti SUPER (driver 615.78.08), Release build with Ninja.
git fetch origin main pull/12790/head:refs/review/12790
git checkout --detach origin/main
git merge --no-ff --no-edit refs/review/12790
git diff --check origin/main...refs/review/12790
python3 tools/check_conventions.py --base origin/main
cmake -S . -B build
cmake --build build -j10 --target \
  agc_driver_image_gather_lod_tests agc_driver_image_gather_one_dimensional_tests \
  agc_driver_image_gather_one_dimensional_array_tests agc_driver_image_packed_horizontal_gather_tests \
  agc_driver_image_sample_lod_clamp_tests agc_driver_image_sample_unnormalized_tests \
  agc_driver_image_sample_shared_sampler_tests agc_driver_image_sample_border_color_tests \
  image_sample_gather_tests agc_driver_image_texel_status_tests agc_driver_image_converted_unorm_tests \
  agc_driver_image_srgb_load_tests agc_driver_image_sint8_tests agc_driver_image_cache_bits_tests
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1 \
VK_LOADER_DEBUG=error,warn,layer ctest --test-dir build -j4 -V --output-on-failure -R \
  '^(agc_driver_image_gather_lod|agc_driver_image_gather_one_dimensional|agc_driver_image_gather_one_dimensional_array|agc_driver_image_packed_horizontal_gather|agc_driver_image_sample_border_color|agc_driver_image_sample_lod_clamp|agc_driver_image_sample_shared_sampler|agc_driver_image_sample_unnormalized|image_sample_gather|agc_driver_image_texel_status|agc_driver_image_converted_unorm|agc_driver_image_srgb_load|agc_driver_image_sint8|agc_driver_image_cache_bits)$'
# baseline / negative control: the same build and ctest command after
git checkout origin/main -- <the five changed files under core/shader>

The changed test file only adds a new test, so the negative-control tree is main's production code with the new test added. It was also the main baseline for the other 13 tests.

Results

Check Result
Merge onto main Clean (docs/dev/debt/shader.md auto-merged)
git diff --check Clean
check_conventions.py --base origin/main 0 errors, 1 warning (debt-length, see Non-blocking)
Build of the 14 test targets (merged tree) OK
Focused tests, merged tree, validation layer 14/14 passed
Focused tests, main production code + new test 13/14; agc_driver_image_gather_lod fails, the rest pass
VUID- lines 0 on the merged tree, 0 on main
was not found but was requested by env var VK_INSTANCE_LAYERS Absent from both logs; the loader logged inserting VK_LAYER_KHRONOS_validation 13 times (merged) and 14 times (main)
Test duration agc_driver_image_gather_lod 0.44–0.72 s
Merged tree
1164: image 2D explicit-LOD gather tests passed
 3/14 Test #1164: agc_driver_image_gather_lod .....................   Passed    0.72 sec
...
100% tests passed out of 14
main production code with the new test
1164: AGC graphics: clamp to edge, point: thread 1 (LOD 1.000000, level 1) component 0 is 108, expected 113
 1/14 Test #1164: agc_driver_image_gather_lod .....................***Failed    0.70 sec
...
93% tests passed, 1 tests failed out of 14
check_conventions.py
docs/dev/debt/shader.md:213: warning: [debt-length] Technical debt entry over 1000 characters: state the gap first (what differs from the console, what is unverified, what throws) and keep only the behaviour it needs: 1186 characters
1 findings, 0 errors

Negative control

I restored Resources.hpp, DescriptorBindingBuilder.cpp, ShaderInfoCollector.cpp, PipelineSpecialization.hpp and SpirvImageEmitter.cpp from origin/main and kept the new test. agc_driver_image_gather_lod then failed on its first non-zero LOD: thread 1, LOD 1, read 108 (mip 0) where 113 (mip 1) was expected. The author reports the same message. After I put the merged files back, the test passed again.

Findings

Blocking

None.

Non-blocking

  1. The debt entry is inaccurate about 1D images and leaves out 2D arrays. docs/dev/debt/shader.md:213 (line 212 on the head) says that image_gather4_l "on 1D, 3D or cube images" still gathers the base level. Two parts of that do not match the code:

    • On 1D, 1D-array and remapped-depth-1D images, image_gather4_l does not fall back to a base-level gather. It is refused: SpirvImageEmitter.cpp:1437-1439 calls ctx.Fail(... "has an unsupported 1D gather variant") whenever RdnaImageSampleFlagLevelZero is not set.
    • The new path's guard at SpirvImageEmitter.cpp:1481 requires dimension == RdnaImageDimension::Dim2D. So image_gather4_l on a 2D array also keeps the base-level gather, and the entry does not mention that case.

    This matters because a reader of the debt file would expect 1D titles to render wrong rather than abort, and would not look for 2D-array HZB reads. Suggested fix: write "on 2D array, 3D or cube images", and say that 1D explicit-LOD gathers throw. Shortening the entry at the same time would also clear the debt-length warning (1186 characters against a limit of 1000).

  2. The graphics-pipeline-library prebuild specializes on an all-zero sampler. This is a question about the prebuild path; I did not measure it. RegistrationResult gives up on shaders whose specialization depends on sampler words (FoldsTexelOffsets), and otherwise materializes the shader with zeroed sampler descriptors. The new block at DescriptorBindingBuilder.cpp:316 is not gated on that path. For a fragment shader that uses image_gather4_l, the prebuild therefore gets GatherLodBase = wrap, MAX_LOD 0 and MIP_FILTER none. With specialization present, Recompiler.cpp:1232 builds a specialized module rather than the registration module. A draw with a real sampler therefore gets a different module, so the prebuild looks wasted rather than wrong. If that is the case, adding SamplerUseGatherLod samplers to the early return next to FoldsTexelOffsets would avoid the useless prebuild. This only applies where graphicsPipelineLibrary is enabled.

Other things I checked and found correct:

  • The S# fields are extracted from the right bits: MIN_LOD/MAX_LOD from word 1 [23:0] as u4.8, CLAMP_X/CLAMP_Y from word 0 [5:0], LOD_BIAS from word 2 [13:0] as s5.8, MIP_FILTER from word 2 [27:26].
  • The specialization defaults (4 << 24, which reads as unsupported) mean that a sampler-table sampler, or a sampler that never got the constant, keeps the old gather.
  • GatherAddress matches the address-mode mapping in GuestSamplerResource.cpp, and Vulkan's integer wrap, mirrored-repeat and mirror-clamp-to-edge formulas.
  • GatherLodBase + 32 = GatherLodBiasBase, and the bias range also has 32 IDs. Both fit ShaderInfo::MaxSamplers = 32 without overlapping another range.
  • Sampler words are fully part of the binding-plan key (preparedBindingPlan) and of the result-memo key, so a sampler with a different LOD state cannot reuse a stale specialization.
  • With an unnormalized sampler, the gather is still refused through UnnormalizedUseReason, because SamplerUseGather is the lower bit.

The maintainer's four points from the earlier comment are covered by the test cases (fractional and tie LODs, bilinear, MIN/MAX_LOD, MIP_FILTER none, a large/+inf/NaN LOD, UINT with a bilinear sampler), and all of them pass on this NVIDIA host.

Not verified

  • I ran nothing on AMD or Windows. The behaviour the debt entry lists as unmeasured on hardware (tie rounding, linear MIP_FILTER, LOD_BIAS_SEC, T# MIN_LOD) was not checked on a console.
  • I did not run PPSA23276, so the in-game result is the author's.
  • I did not exercise image_gather4_l from a pixel shader, or the graphics-pipeline-library path (finding 2 comes from reading the code).
  • I did not check the textual overlap with #9183 beyond confirming that it is a different feature (3D image_gather4_lz).

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@yinx080 thanks, this round answers all four points: fetching the footprint at a level chosen in the shader removes the sampler's clamp and filter from the result, the LOD is clamped in floating point before any conversion, the integer-format case needs no sampler at all, and the new variants (fractional and tie LODs, NaN/inf/huge, bilinear, MIN/MAX_LOD, bias, MIP_FILTER none, UINT with a bilinear sampler) pin it.

One case the switch to OpImageFetch makes worse rather than unmeasured:

  1. Image descriptor MIN_LOD. When the T# has a non-zero minimum LOD, the view is created with VkImageViewMinLodCreateInfoEXT (ChainMinLod, Graphics/src/Texture.cpp:272-279). For integer texel operations the Vulkan spec says that if the level is below the view's minLodInteger, the fetched values are zero with robustImageAccess2 and undefined otherwise, and the driver doesn't enable robustImageAccess2. So when floor(MIN_LOD) is above the level picked from the sampler state, the four texels are undefined, where the previous OpImageGather was clamped by the view. Please either raise the fetched level to at least the image's integer minimum LOD (the hardware applies the T# MIN_LOD as an LOD clamp too), or keep the base gather when the T# has a minimum LOD, and add a variant with a T# MIN_LOD above the requested level. The debt entry should then describe what is done instead of listing it as unmeasured.

Smaller points, not blocking:

  • A sampler with MIN_LOD above MAX_LOD makes NClamp undefined (minVal > maxVal). If you want to pin it, choose an order explicitly.
  • The mirror-once modes (3, 5, 7) and the border fallback (4, 6) have no variant. One each would cover GatherAddress's last branch and the Select fallback.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 8502ef1 merged onto main ba5b533 · tested 2026-10-29 12:56 UTC · 28 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,218 passed · 5 failed (0 new, 5 also fail on main) · 39 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,262 of 2,891 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (860 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) none
Intermittent validation messages (1, seen once in the parallel run, not reproduced when the test was re-run alone 2 times; not counted against this PR)
agc_driver_depth_clear_hle: VUID-vkCmdCopyBuffer-commonparent (6x)
Failing on main too (5, not counted against this PR)
agc_compute_pipeline_inspection
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_depth_clear_hle: SEGFAULT in the parallel run; also passed only on retry in 27 other runs in the last 7 days
Validation IDs already present on main (4 IDs in 407 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (407 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alpha_to_coverage, agc_driver_array_layer_target_cpu, agc_driver_bc_block_copy, and 399 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (39)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_msaa_dcc_compared_import_2: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_4: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_8: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_main: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_fresh: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_labels: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SD
Reproduce
git fetch origin 8502ef15f7557f7972c066f5b24e07ee6e5ee9ba ba5b5330b37724bca4b99f30faab504653635a04
git checkout ba5b5330b37724bca4b99f30faab504653635a04 && git merge --no-ff 8502ef15f7557f7972c066f5b24e07ee6e5ee9ba
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@yinx080
yinx080 force-pushed the fix/shader-gather4-l-lod branch from 8502ef1 to 813217f Compare October 29, 2026 14:54
@yinx080

yinx080 commented Oct 29, 2026

Copy link
Copy Markdown
Author

Thanks. Pushed a third commit (rebased on 899ae3d):

  1. T# MIN_LOD. The images of explicit-LOD gathers are now specialized on their T# MIN_LOD above the base level (GatherImageMinLodBase, the same quantity EffectiveMinLod gives the view), and the shader raises the LOD to it after the sampler's clamp, before the clamp to the view's levels and the rounding. Since ceil(λ + 0.5) - 1 >= floor(λ), the fetched level is never below the view's minLodInteger. New variant: T# MIN_LOD 2.5 under LODs from -3 to 6, NaN and inf. The debt entry now describes the clamp instead of listing it as unmeasured.

Smaller points:

  • S# MIN_LOD > MAX_LOD: the clamp is explicit now, NMin(NMax(λ, MIN_LOD), MAX_LOD), so MAX_LOD wins. (GuestSamplerResource refuses such a sampler whenever MIP_FILTER is not none, so it only matters with MIP_FILTER none, where the level is the base level anyway.)
  • Coverage: new variants for mirror-once (modes 3 on X, 7 on Y) and the border fallback (6 on X, 4 on Y, expected: the base-level gather with transparent-black border texels).
  • Images or samplers read from a table now take the base-level gather when the shader is built, not only through the specialization default.

Results (same machine and settings as before, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation):

  • main 899ae3d + the new test: 76/78; agc_driver_image_gather_lod fails at its first variant (thread 1 (LOD 1.000000, level 1) component 0 is 108, expected 113), and agc_driver_image_table (validation SYNC-HAZARD, as on main before).
  • This branch: 77/78; all twelve ImageGatherLod variants pass; agc_driver_image_table as on main.
  • VUID- differences: the mirror-once variant adds VUID-VkSamplerCreateInfo-addressModeU-01079, -addressModeU-parameter and -addressModeV-parameter, raised when GuestSamplerResource creates its MIRROR_CLAMP_TO_EDGE sampler without samplerMirrorClampToEdge; main never reaches that variant, and this change does not create samplers. agc_driver_image_sample_derivatives logged VUID-StandaloneSpirv-Offset-04663/VUID-VkShaderModuleCreateInfo-pCode-08737 (its computed offsets under VK_KHR_maintenance8) in some parallel runs on both trees and in none of four standalone runs on either, so it is unrelated. Nothing else differs.
  • python3 tools/check_conventions.py --base origin/main: passed.
  • PPSA23276, first hallway, 300 s (main + fix(shader): load and store 16-bit data of converted packed images #11580 + the local depth-plane fix): the panels render in every shot (0 % of the gap region near-black), frame rate unchanged (56-57 presents per 10 s).

The samplerMirrorClampToEdge sampler creation seems worth its own fix; I've left it out of this PR.

@XairRrR XairRrR left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked — changes still requested

The new commit 813217f0b5 raises the fetched level to the T# MIN_LOD, keeps table images and samplers on the base-level gather at build time, and adds three test variants. One of those variants makes agc_driver_image_gather_lod log three new VUID- validation errors. The production code that raises them is already on main, but AGENTS.md asks that a GPU change add no new VUID- errors.

What changed since my last review

  • The branch was rebased onto 899ae3d1a. git range-diff shows the two commits I reviewed are unchanged (5f2c39f214 → f68dbf0702, 8502ef15f7 → 9d29fb18fe).
  • New commit 813217f0b5 "fix(shader): keep an explicit-LOD gather's fetch at the view's minimum LOD". It does five things:
    • adds the GatherImageMinLodBase specialization: the T# MIN_LOD above BASE_LEVEL, set per image in DescriptorBindingBuilder::Prepare;
    • applies the S# clamp in a fixed order, NMin(NMax(λ, MIN_LOD), MAX_LOD), and then raises λ to the image's minimum LOD;
    • gates the new path on table == NoTable for both the image and the sampler;
    • adds three test variants: T# MIN_LOD 2.5, mirror-once (modes 3 and 7), and the border fallback (modes 6 and 4);
    • rewrites the debt entry.
  • The author's 14:54 UTC comment replies to oneandonlydean's T# MIN_LOD point and reports results, including the new VUID- lines from the mirror-once variant.

What I ran

  • PR head: 813217f0b543b4a61f5449ed66e0bbea444b40b1
  • main: bb04a4f1ea0175e9e4f149f0359a764d62ff3296
  • Tree under test: main with the head merged in using --no-ff (no conflicts), in a clean worktree.
  • Machine: Arch Linux 7.2.11-zen, GCC 16.2.1, Intel Core i9-14900KF, NVIDIA GeForce RTX 4070 Ti SUPER (driver 615.78.08), Release build with Ninja.
git fetch origin main pull/12790/head:refs/review/12790
git range-diff ba5b5330b3..8502ef15f7 899ae3d1ad..refs/review/12790
git checkout --detach origin/main
git merge --no-ff --no-edit refs/review/12790
git diff --check origin/main...refs/review/12790
python3 tools/check_conventions.py --base origin/main
cmake -S . -B build
cmake --build build -j10 --target \
  agc_driver_image_gather_lod_tests agc_driver_image_gather_one_dimensional_tests \
  agc_driver_image_gather_one_dimensional_array_tests agc_driver_image_packed_horizontal_gather_tests \
  agc_driver_image_sample_lod_clamp_tests agc_driver_image_sample_unnormalized_tests \
  agc_driver_image_sample_shared_sampler_tests agc_driver_image_sample_border_color_tests \
  image_sample_gather_tests agc_driver_image_texel_status_tests agc_driver_image_table_tests
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1 \
VK_LOADER_DEBUG=error,warn,layer ctest --test-dir build -j4 -V --output-on-failure -R \
  '^(agc_driver_image_gather_lod|agc_driver_image_gather_one_dimensional|agc_driver_image_gather_one_dimensional_array|agc_driver_image_packed_horizontal_gather|agc_driver_image_sample_border_color|agc_driver_image_sample_lod_clamp|agc_driver_image_sample_shared_sampler|agc_driver_image_sample_unnormalized|image_sample_gather|agc_driver_image_texel_status|agc_driver_image_table)$'
# main baseline and negative control: the same build and ctest after
git checkout origin/main -- <the five changed files under core/shader>
# probes: one extra Run(...) line inserted temporarily into ImageGatherLod.cpp,
# built and run with the same environment, on both trees

Results

Check Result
git range-diff against the previously reviewed commits The first two commits are identical; one new commit
Merge onto main Clean
git diff --check Clean
check_conventions.py --base origin/main All checks passed. The debt-length warning from my last review is gone
Build of the 11 targets, merged tree and main production code OK
Focused tests, merged tree, validation layer 11/11 passed, including all 12 ImageGatherLod variants (0.50–0.65 s)
Focused tests, main production code + the PR's test 10/11; only agc_driver_image_gather_lod fails, with the message the author quotes
VUID- IDs, merged tree 3 new: VUID-VkSamplerCreateInfo-addressModeU-01079, -addressModeU-parameter, -addressModeV-parameter, all from agc_driver_image_gather_lod
VUID- IDs, main None
was not found but was requested by env var VK_INSTANCE_LAYERS Absent from both logs; the loader inserted VK_LAYER_KHRONOS_validation 10 times (merged) and 11 times (main)
agc_driver_image_table Passed on both trees with no SYNC- lines on this host. The author reports a SYNC-HAZARD failure on both trees on their machine
Merged tree: the new validation errors
1172: Validation Error: [ VUID-VkSamplerCreateInfo-addressModeU-01079 ] | MessageID = 0x1aa95c2b
1172: The Vulkan spec states: If the samplerMirrorClampToEdge feature is not enabled, and if the VK_KHR_sampler_mirror_clamp_to_edge extension is not enabled, addressModeU, addressModeV and addressModeW must not be VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE
1172: Validation Error: [ VUID-VkSamplerCreateInfo-addressModeU-parameter ] | MessageID = 0x3fcec07
1172: Validation Error: [ VUID-VkSamplerCreateInfo-addressModeV-parameter ] | MessageID = 0xf8361085
1172: image 2D explicit-LOD gather tests passed
10/11 Test #1172: agc_driver_image_gather_lod .....................   Passed    0.65 sec
100% tests passed out of 11
main production code with the PR's test
1172: AGC graphics: clamp to edge, point: thread 1 (LOD 1.000000, level 1) component 0 is 108, expected 113
 1/11 Test #1172: agc_driver_image_gather_lod .....................***Failed    1.02 sec
91% tests passed, 1 tests failed out of 11

Previous findings

  1. Debt entry: 1D images and 2D arrays. Partly resolved, still open.

    • Resolved: the entry was rewritten and no longer trips debt-length.
    • Still open: it still says 1D images "still gather the base level". On 1D, 1D-array and remapped-depth-1D images, image_gather4_l is refused at SpirvImageEmitter.cpp:1444-1446 (ctx.Fail(... "has an unsupported 1D gather variant")).
    • Still open: 2D arrays also keep the base-level gather because of the Dim2D guard, and the fallback list does not name them.

    Non-blocking.

  2. The graphics-pipeline-library prebuild specializes on an all-zero sampler. Still open and unanswered; the code is unchanged. The early return at Recompiler.cpp:2070 still covers only FoldsTexelOffsets. The new image MIN_LOD constant takes 0 from zeroed T# words, so the new commit adds nothing to this. Non-blocking question.

New findings

Blocking

  1. The mirror-once variant adds three new VUID- validation errors. The variant is at ImageGatherLod.cpp:234.
    • What I see. I confirm the author's report: on the merged tree, agc_driver_image_gather_lod logs VUID-VkSamplerCreateInfo-addressModeU-01079, -addressModeU-parameter and -addressModeV-parameter. main's log has none of them.
    • Where they come from. I moved a mirror-once variant to the front of the test and ran it against main's production code, and the same three IDs appeared. So they come from code that is already on main, not from the shader change. GuestSamplerResource.cpp:37-41 maps clamp modes 3, 5 and 7 to VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE, and nothing under core/ enables samplerMirrorClampToEdge.
    • Why it blocks. AGENTS.md asks that an AGC or shader recompiler change add no new VUID- errors. Once this merges, agc_driver_image_gather_lod reports Passed while it logs invalid sampler creation on every run.
    • Suggested fix. Either option works:
      • Land the sampler fix the author mentions first, and list it under Depends on. That fix would enable samplerMirrorClampToEdge at device creation when it is supported, and refuse or emulate the mode when it is not.
      • Drop the mirror-once variant here and add it with that fix.

Non-blocking

  1. With MIP_FILTER none, the fetch ignores the T# MIN_LOD.
    • The code. SpirvImageEmitter.cpp:823-831 raises nearest to the image's minimum LOD, but level takes the constant 0 when mipNone is set.
    • Why it matters. When the T# MIN_LOD is at least 1 above BASE_LEVEL, the view gets a minLod through ChainMinLod. The four OpImageFetches then read below minLodInteger. The Vulkan 1.4.330 spec says such values are zero with robustImageAccess2 and undefined otherwise.
    • Probe. I added {MipNone, T# MIN_LOD 640}, the T# MIN_LOD 2.5 variant with MIP_FILTER none. On the merged tree, thread 0 read 0 where any mip level holds a non-zero texel: component 0 is 0, expected 108.
    • Not a regression. The same probe on main also reads 0. The gather section of the spec makes OpImageGather below minLodInteger undefined as well, so the view does not clamp the old base-level gather either. That also qualifies the premise of oneandonlydean's point 1.
    • Why it is still worth fixing. The new commit's message says "the fetched level never falls below the view's integer minimum LOD". That is not true for this branch. The test's reference model encodes the same gap: Level() returns 0 for MipNone before it looks at textureMinLodRaw (ImageGatherLod.cpp:150).
    • Suggested fix. For mipNone, use floor of the image's minimum LOD, clamped to the last level, rather than 0. Add that variant to the test, and name the case as unmeasured in the debt entry. If the console's behaviour for MIP_FILTER none with a T# MIN_LOD is unknown, the debt entry is the place to say so.

Other things I checked in the new commit and found correct:

  • The constant matches the view's minimum LOD.
    • EffectiveMinLod gives the view an absolute MIN_LOD, or none when MIN_LOD <= BASE_LEVEL·256. The constant is the same value rebased to the view (MIN_LOD - BASE_LEVEL·256, else 0), and OpImageFetch's Lod is view-relative.
    • So for mip filters other than none, the fetched level BASE_LEVEL + nearest is at least the view's minLodInteger, because ceil(λ + 0.5) - 1 >= floor(λ).
    • EffectiveMinLod's cap at lastLevel matches the shader's clamp to levels - 1.
  • GatherImageMinLodBase + 64 (ShaderInfo::MaxImages) does not overlap another specialization range. The dedupe on equal constants is safe because only this loop writes those IDs, and each value depends on the image alone.
  • The image constant is set only for non-table images, and the shader now also takes the base-level path for table images and table samplers, so the default constant is never relied on there.

Not verified

  • Nothing ran on AMD or Windows, and I did not run PPSA23276. The in-game result in the author's comment is author-reported.
  • The hardware behaviour of the T# MIN_LOD clamp and of the mirror-once modes is unmeasured, as the debt entry says.
  • I did not reproduce the author's agc_driver_image_table SYNC-HAZARD: the test passed on both trees here.
  • I did not exercise the graphics-pipeline-library prebuild path.

To get this to a good state

  1. Remove the three new VUID- errors: land the samplerMirrorClampToEdge fix first and list it under Depends on, or drop the mirror-once variant from this PR.
  2. Optionally, apply the T# MIN_LOD to the MIP_FILTER none branch as well, and add a variant for it.
  3. Optionally, correct the debt entry for 1D (it throws) and 2D arrays (base-level gather).

@yinx080

yinx080 commented Oct 29, 2026

Copy link
Copy Markdown
Author

Thanks. Pushed a fourth commit:

  1. New VUID- errors. I dropped the mirror-once variant. It goes in with the samplerMirrorClampToEdge fix, which I'll open once this PR is settled. The mapping in the shader (GatherAddress mode 3/5/7) is unchanged, and the debt entry still lists it as unmeasured.
  2. MIP_FILTER none with a T# MIN_LOD. The fetch level for MIP_FILTER none is now floor of the image's minimum LOD above the base level, clamped to the view's last level, instead of 0. It never falls below the view's minLodInteger now. The test's reference model does the same, and there's a new variant, MIP_FILTER none with T# MIN_LOD 2.5 (level 2). The debt entry names this as unmeasured.
  3. Debt entry. It now says 1D image_gather4_l throws, and lists 2D arrays among the base-level gathers.
  4. Pipeline-library prebuild. Sorry, I missed this one last round. RegistrationResult now gives up on shaders with a SamplerUseGatherLod sampler, next to FoldsTexelOffsets, so the zero-sampler prebuild isn't built for them.

Results (same machine and settings as before, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation):

  • Your 11 focused tests, this branch: 10/11. All twelve ImageGatherLod variants pass, including the new one. agc_driver_image_table fails with the SYNC-HAZARD it also shows on main on this machine.
  • VUID- IDs: no VkSamplerCreateInfo IDs any more. The only ID left is VUID-VkDeviceCreateInfo-pNext-pNext (a pNext structure the older validation layer on this machine doesn't know), which main logs the same way here.
  • python3 tools/check_conventions.py --base origin/main: passed. git diff --check: clean.

@XairRrR XairRrR left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked — approved

The new commit 01b2319b7d drops the mirror-once variant, fetches MIP_FILTER none at the floor of the T# MIN_LOD, keeps gather-LOD shaders out of the pipeline-library prebuild and corrects the debt entry. The focused tests now pass on the merged tree with no VUID- lines.

What changed since my last review

  • One new commit, 01b2319b7d "fix(shader): fetch an explicit-LOD gather with MIP_FILTER none at the view's minimum level". It changes 4 files, +9/−7:
    • SpirvImageEmitter.cpp: for MIP_FILTER none the level is now floor(imageMinLod), clamped to [0, levels - 1], instead of 0.
    • Recompiler.cpp: RegistrationResult returns nullptr for a shader with a SamplerUseGatherLod sampler, as well as for FoldsTexelOffsets.
    • ImageGatherLod.cpp: removes the mirror-once variant, adds MIP_FILTER none, T# MIN_LOD 2.5, and updates the reference Level() to match.
    • docs/dev/debt/shader.md: rewrites the entry.
  • The first three commits are unchanged.
  • The author's 15:29 UTC comment answers all four points from my last review.

What I ran

  • PR head: 01b2319b7dc07947ed5bf1a3005e31da622d0410
  • main: bb04a4f1ea0175e9e4f149f0359a764d62ff3296, the same main as last time.
  • Tree under test: main with the head merged in using --no-ff (no conflicts), in a clean worktree.
  • Machine: Arch Linux 7.2.11-zen, GCC 16.2.1, Intel Core i9-14900KF, NVIDIA GeForce RTX 4070 Ti SUPER (driver 615.78.08), Release build with Ninja.
git fetch origin main pull/12790/head:refs/review/12790
git diff 813217f0b543 refs/review/12790
git checkout --detach origin/main
git merge --no-ff --no-edit refs/review/12790
git diff --check origin/main...refs/review/12790
python3 tools/check_conventions.py --base origin/main
cmake --build build -j10 --target \
  agc_driver_image_gather_lod_tests agc_driver_image_gather_one_dimensional_tests \
  agc_driver_image_gather_one_dimensional_array_tests agc_driver_image_packed_horizontal_gather_tests \
  agc_driver_image_sample_lod_clamp_tests agc_driver_image_sample_unnormalized_tests \
  agc_driver_image_sample_shared_sampler_tests agc_driver_image_sample_border_color_tests \
  image_sample_gather_tests agc_driver_image_texel_status_tests agc_driver_image_table_tests
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1 \
VK_LOADER_DEBUG=error,warn,layer ctest --test-dir build -j4 -V --output-on-failure -R \
  '^(agc_driver_image_gather_lod|agc_driver_image_gather_one_dimensional|agc_driver_image_gather_one_dimensional_array|agc_driver_image_packed_horizontal_gather|agc_driver_image_sample_border_color|agc_driver_image_sample_lod_clamp|agc_driver_image_sample_shared_sampler|agc_driver_image_sample_unnormalized|image_sample_gather|agc_driver_image_texel_status|agc_driver_image_table)$'
cmake --build build -j10 --target agc_driver_recorder_tests
ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1 ctest --test-dir build -j4 --output-on-failure -R '^agc_pipeline_library'
# negative control: the previous commit's emitter with the new test
git checkout 813217f0b543 -- core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp
# build agc_driver_image_gather_lod_tests, run it with the same environment
# main baseline: main's production code with the PR's test
git checkout origin/main -- core/shader
# same build and validation ctest as above, then the merged files were restored

Results

Check Result
Merge onto main Clean
git diff --check Clean
check_conventions.py --base origin/main All conventions checks passed
Build: the 11 focused targets and agc_driver_recorder_tests OK
Focused tests, merged tree, validation layer 11/11 passed. agc_driver_image_gather_lod passed in 1.30 s and printed image 2D explicit-LOD gather tests passed
agc_pipeline_library* tests, merged tree 6/6 passed
VUID- IDs, merged tree None
VUID- IDs, main production code None
was not found but was requested by env var VK_INSTANCE_LAYERS Absent from both logs. The loader inserted VK_LAYER_KHRONOS_validation 10 times (merged) and 11 times (main)
main production code + the PR's test 10/11. Only agc_driver_image_gather_lod fails: clamp to edge, point: thread 1 (LOD 1.000000, level 1) component 0 is 108, expected 113
agc_driver_image_table Passed on both trees, with no SYNC- lines on this host

Negative control

I restored SpirvImageEmitter.cpp from 813217f0b5, the commit I reviewed last time, and kept the new test. The new variant fails:

Output
1/1 Test #1172: agc_driver_image_gather_lod ......***Failed    0.84 sec
AGC graphics: MIP_FILTER none, T# MIN_LOD 2.5: thread 0 (LOD 0.000000, level 2) component 0 is 0, expected 146

With all of main's production code, the test fails on its first variant, as it did last time.

Previous findings

  1. New VUID- errors from the mirror-once variant (blocking): Resolved. The variant and its two constants are gone from ImageGatherLod.cpp. The merged-tree log has no VUID- lines, and neither has main's. The author plans to add the variant back with the samplerMirrorClampToEdge fix.
  2. MIP_FILTER none ignored the T# MIN_LOD: Resolved. See SpirvImageEmitter.cpp:831-834.
    • imageMinLod is the view-relative constant, MIN_LOD - BASE_LEVEL·256, or 0.
    • BASE_LEVEL + floor(imageMinLod), capped at the last level, equals the view's minLodInteger, so the fetch no longer reads below it.
    • The reference model at ImageGatherLod.cpp:149 matches, because every variant uses BASE_LEVEL 0.
    • The new variant at line 233 covers the change; see the negative control.
  3. Debt entry for 1D and 2D arrays: Resolved. shader.md:214 now says that image_gather4_l throws on 1D images. It lists 2D arrays among the base-level gathers, and names MIP_FILTER none with a T# MIN_LOD as unverified.
  4. Pipeline-library prebuild specialized on an all-zero sampler: Resolved. Recompiler.cpp:2070 now returns nullptr for any sampler with SamplerUseGatherLod, so those shaders get no zero-sampler registration module. The six agc_pipeline_library* tests pass. None of them contains an image_gather4_l shader, so the new condition itself is not exercised.

New findings

Blocking

None.

Non-blocking

  1. Parts of the PR description no longer match the code. Each of these could be updated:

    • What still says "base level for MIP_FILTER none".
    • The test list still names "mirror once (3, 7)" and not the new MIP_FILTER none, T# MIN_LOD 2.5 variant.
    • Tested still describes the mirror-once VkSamplerCreateInfo IDs.

    The 15:29 UTC comment does have the current results.

Not verified

  • Nothing ran on AMD or Windows, and I did not run PPSA23276. The author's in-game and Windows results are author-reported.
  • The hardware behaviour of MIP_FILTER none combined with a T# MIN_LOD is unmeasured. The debt entry says so.
  • No test runs an image_gather4_l shader through the pipeline-library registration path.
  • I did not reproduce the author's agc_driver_image_table SYNC-HAZARD: the test passed on both trees here.

@github-actions github-actions Bot added shader core/shader docs agc libSceAgc* labels Oct 29, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 01b2319 merged onto main bb04a4f · tested 2026-10-30 00:09 UTC · 23 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,242 passed · 5 failed (0 new, 5 also fail on main) · 40 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,287 of 2,918 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (861 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_bda_device 62.4 s
Failing on main too (5, not counted against this PR)
agc_compute_pipeline_inspection
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_job_manager_compute_capacity: Failed in the parallel run; also passed only on retry in 59 other runs in the last 7 days
Validation IDs already present on main (4 IDs in 401 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (401 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_array_layer_target, agc_driver_array_layer_target_cpu, agc_driver_bda_anchor_reads, and 393 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (40)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_msaa_dcc_compared_import_2: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_4: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_8: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_main: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_fresh: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_labels: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not
Reproduce
git fetch origin 01b2319b7dc07947ed5bf1a3005e31da622d0410 bb04a4f1ea0175e9e4f149f0359a764d62ff3296
git checkout bb04a4f1ea0175e9e4f149f0359a764d62ff3296 && git merge --no-ff 01b2319b7dc07947ed5bf1a3005e31da622d0410
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@bunbeans2-arch

Copy link
Copy Markdown
Contributor

An AMD RDNA 4 run, since the description lists an RTX 3060 Laptop GPU only.

Windows 11 Pro 10.0.22631, AMD Radeon RX 9070 XT (1002:7550, driver 32.0.32015.7029), MSYS2 UCRT64 g++ 16.1.0, Release. ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation; with VK_LOADER_DEBUG=layer the loader logs Insert instance layer "VK_LAYER_KHRONOS_validation" in every run of the Vulkan tests below (image_sample_gather creates no Vulkan instance). 3 runs each:

  • This head (01b2319b7d) merged onto main 673fa865f4: agc_driver_image_gather_lod, agc_driver_image_gather_one_dimensional, agc_driver_image_gather_one_dimensional_array, agc_driver_image_packed_horizontal_gather and image_sample_gather all pass 3/3, no VUID- line.
  • Control, main 673fa865f4 with only ImageGatherLod.cpp added: agc_driver_image_gather_lod fails 3/3 with clamp to edge, point: thread 1 (LOD 1.000000, level 1) component 0 is 108, expected 113, no VUID- line.

AI-assisted: yes (Claude Code)

@jotitadev jotitadev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linux lavapipe run of head 01b2319b7d: agc_driver_image_gather_lod fails on any device without VK_EXT_image_view_min_lod, and lavapipe (CI's linux device) is one of them. The linux job is skipped for this head, and the runs posted so far are on NVIDIA and AMD, so no report shows it yet.

Setup: i7-8700, Ubuntu 26.04, GCC 15.2, Release, lavapipe (Mesa 26.0.3, LLVM 21.1.8), ANYPS5_NO_SHADER_CACHE=1. Runs on the head alone and on the head merged --no-ff onto main 2a1ae91175, 3 runs each:

AGC graphics: guest texture descriptor uses a minimum LOD clamp, which needs VK_EXT_image_view_min_lod

Cause. The two T# MIN_LOD 2.5 variants put a T# minimum LOD on the image. Without the extension, main's texture path refuses that descriptor (Texture.cpp, Require(context.imageViewMinLod, ...)) before the gather runs. The test has no skip for that, so the whole test fails on the first of the two variants. MoltenVK has no VK_EXT_image_view_min_lod either.

Fix I checked: run the two variants only when the device has the extension, through the same test accessor #12662 uses:

+namespace AgcDriver {
+
+struct VulkanDeviceTestAccess {
+    static bool ImageViewMinLod(const VulkanDevice& device) { return device.graphicsContext().imageViewMinLod; }
+};
+
+}
+
@@ int main() {
-        Run(*device, {Format8888UNorm, ClampEdge, ClampEdge, FilterPoint, MipPoint, 0u, AllLevels, 0, 640u, "T# MIN_LOD 2.5"});
-        Run(*device, {Format8888UNorm, ClampEdge, ClampEdge, FilterPoint, MipNone, 0u, AllLevels, 0, 640u, "MIP_FILTER none, T# MIN_LOD 2.5"});
+        if (AgcDriver::VulkanDeviceTestAccess::ImageViewMinLod(*device)) {
+            Run(*device, {Format8888UNorm, ClampEdge, ClampEdge, FilterPoint, MipPoint, 0u, AllLevels, 0, 640u, "T# MIN_LOD 2.5"});
+            Run(*device, {Format8888UNorm, ClampEdge, ClampEdge, FilterPoint, MipNone, 0u, AllLevels, 0, 640u, "MIP_FILTER none, T# MIN_LOD 2.5"});
+        } else {
+            std::puts("T# MIN_LOD cases skipped, the device has no VK_EXT_image_view_min_lod");
+        }

With it, agc_driver_image_gather_lod passes 3/3 on lavapipe, on the head alone and merged onto main. The other variants, including border modes, still run there. Devices that have the extension run the same cases as before.

Also: this PR and #12662 conflict in PipelineSpecialization.hpp. #12662 also rejects gathers at a T# clamp of one level or more in its shader lane, which is the lane MoltenVK uses. I haven't checked how these two variants behave with both PRs applied.

GPUs tested: lavapipe (Mesa 26.0.3, Linux).

Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.

@RNK123

RNK123 commented Nov 1, 2026

Copy link
Copy Markdown

Linux / AMD RDNA 4 (RADV) run of head 01b2319b7dc0 merged --no-ff onto main 53fbe59956. This adds the Linux RADV result next to the existing NVIDIA and Windows AMD runs.

Host & GPU: CachyOS Linux (7.3.1-1-cachyos), 12th Gen Intel Core i5-12400F, GCC 16.2.1 (Release, -DBUILD_TESTING=ON), AMD Radeon RX 9060 XT (1002:7590, RADV GFX1200, Mesa 26.2.5-1, Vulkan 1.4.354, subgroup 64). The device exposes VK_EXT_image_view_min_lod, so the two T# MIN_LOD 2.5 variants run here and are not refused.

Environment: ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation (Khronos validation layer 1.4.363). VK_LOADER_DEBUG=layer logs Insert instance layer "VK_LAYER_KHRONOS_validation", and no log reports was not found but was requested by env var VK_INSTANCE_LAYERS. ctest --repeat until-fail:3 -V.

Test main + #12790 (01b2319b7dc0), 3 serial runs
agc_driver_image_gather_lod Pass 3/3 (0.10 s, 0.08 s, 0.09 s)
agc_driver_image_gather_one_dimensional Pass 3/3
agc_driver_image_gather_one_dimensional_array Pass 3/3
agc_driver_image_packed_horizontal_gather Pass 3/3
image_sample_gather Pass 3/3
  • Result: 5/5 tests passed (3/3 each), 0 skipped, 0 failed.
  • Vulkan validation: 0 VUID- or SYNC-HAZARD- diagnostics.
  • Not run: no control on main's production code here (the Windows AMD run above already reports agc_driver_image_gather_lod failing there), and no game or title was run. These are the automated tests only.

AI-assisted hardware test run (Antigravity).

@yinx080

yinx080 commented Nov 1, 2026

Copy link
Copy Markdown
Author

Thanks, all, and thanks RNK123 for the RADV run. Pushed a fifth commit with the change jotitadev checked: the two T# MIN_LOD 2.5 variants now run only on devices with VK_EXT_image_view_min_lod, and other devices print that they were skipped. It only touches the test.

I also updated the description, as XairRrR suggested: MIP_FILTER none, the variant list and the validation results now match the code.

Results on the RTX 3060 Laptop (same settings as before): your 11 focused tests are 10/11, with all twelve ImageGatherLod variants passing. agc_driver_image_table fails on its SYNC-HAZARD, as on main here. VUID-VkDeviceCreateInfo-pNext-pNext is the only VUID-, also as on main here. Conventions check passed. This GPU has the extension, so I ran the skip by forcing the check off: the message printed and the other variants passed. I haven't run it on a device that really lacks the extension; jotitadev's lavapipe run covers that.

On #12662 and #12980, which builds on it: I haven't tried them with this PR. The PipelineSpecialization.hpp conflict is more than text: GatherLodBase here and MinLodBase there both use 57536, so whichever merges second has to move its constant rather than keep both lines. The T# MIN_LOD variants should be rechecked then.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 3a6f213 merged onto main 3741ca9 · tested 2026-11-01 11:06 UTC · 30 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,432 passed · 5 failed (0 new, 3 also fail on main, 2 unstable on main) · 44 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,481 of 3,121 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (932 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 40.8 s, ⚠️ agc_driver_flat_store 37.6 s, ⚠️ agc_driver_graphics_maps_fallback 31.1 s
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)
agc_submit_progress: failed in 6 of the last 10 main runs
agc_submit_progress_no_timeline: failed in 8 of the last 10 main runs
Failing on main too (3, not counted against this PR)
agc_driver_image_table
guest_mmap_executable
guest_socket_options_multicast
Validated but not compared (1, not in the main baseline's validation pass)
agc_driver_image_gather_lod
Validation IDs already present on main (5 IDs in 480 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (480 tests): agc_driver, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, agc_driver_bda_span_reads, and 472 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (1 test): agc_driver_prepared_exports
VUID-VkShaderModuleCreateInfo-pCode-08737 (1 test): agc_driver_image_sample_derivatives
Skipped (44)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_dcc: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_deferred: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_main: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_fresh: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_f
Reproduce
git fetch origin 3a6f2133f2639bfea717f921c3998e44446529c6 3741ca9327246d0df81638726a986cef862b6db5
git checkout 3741ca9327246d0df81638726a986cef862b6db5 && git merge --no-ff 3a6f2133f2639bfea717f921c3998e44446529c6
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@CnRJay CnRJay left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantics are carefully argued. The part I would want measured is the cost on the shader side:

  1. More variants. Samplers used by image_gather4_l are now specialized on their S# LOD state (MIN_LOD, MAX_LOD, LOD_BIAS, MIP_FILTER, CLAMP_X/CLAMP_Y) and images on the T# MIN_LOD. Each distinct sampler or T# state used by a gather becomes its own pipeline/shader variant. For UE5 Nanite culling, which gathers the HZB at many mip levels with whichever samplers the title binds, how many extra variants does PPSA23276 compile per frame, and what is the effect on shader-cache size and first-hit compile time? Please attach a count from a run with the change against main.
  2. Larger SPIR-V on every gather. The path replaces one OpImageGather with a LOD computation, four clamped coordinate computations and four OpImageFetch plus the wrap-mode handling. For a culling shader that issues four gathers per instance, that is a lot more ALU per invocation than the single hardware gather. Do you have the dispatch time of the Nanite culling pass before and after, from tools/trace_digest.py on the same scene?
  3. Images or samplers from a table, and border modes 4 and 6, keep the base-level gather, so the result stays wrong for those. Is that recorded in the debt file as a known divergence?

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Nov 2, 2026

@XairRrR XairRrR left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — changes requested

This pull request does not merge cleanly into main (c955a63): head 3a6f213 conflicts in

  • core/shader/recompiler/Optimization/src/DescriptorBindingBuilder.cpp
  • core/shader/recompiler/PipelineSpecialization.hpp
  • docs/dev/debt/shader.md

Please rebase it onto current main. I will review the change once it merges cleanly.

@yinx080
yinx080 force-pushed the fix/shader-gather4-l-lod branch from 702dee4 to 48e75dd Compare November 3, 2026 20:10
@yinx080

yinx080 commented Nov 3, 2026

Copy link
Copy Markdown
Author

@XairRrR thanks for the approval. #9183 has since landed and the branch conflicted, so it is rebased onto main 086c0686c8:

  • SpirvImageEmitter.cpp: fix(shader): gather a 3D image's image_gather4_lz from one slice as measured #9183's EmitVolumeGatherLz and this PR's EmitTwoDimensionalGatherLod were added at the same place, and their branches in EmitGatherOp too. Both are kept, the 3D branch first.
  • PipelineSpecialization.hpp: main now has VolumeGatherBase at 57664. Commits 2-5 used to define GatherLodBase/GatherLodBiasBase/GatherImageMinLodBase at 57664/57696/57728, which would collide with it in those intermediate commits, so they now use 57856/57888/57920 (the old 32-wide spacing). The last commit still widens them to 57856/57920/57984; the final values are unchanged.
  • The first static_assert now chains from VolumeGatherBase + MaxImages instead of MinLodBase + MaxImages, since VolumeGatherBase is the range right below.

Apart from that assert, git diff main...head is the diff you approved, line for line.

With the validation layer on the RTX 3060 Laptop: the 31 image gather, sample and table tests pass 30/31, including agc_driver_image_gather_lod (all twelve variants), #9183's agc_driver_image_gather_volume, and agc_driver_image_gather_lod again in the forced shader-clamp lane. The failure is agc_driver_image_table's SYNC-HAZARD, as on main. agc_driver_image_gather_volume reports VUID-VkSamplerCreateInfo-addressModeU-01079 and the related address-mode IDs on this machine: its mirror-once sampler is created without samplerMirrorClampToEdge, which is in main's sampler code, not this PR. Conventions check passed.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 3, 2026

@XairRrR XairRrR left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked — approved

The branch was rebased onto main 086c0686c8 after #9183 landed. Both 2D and 3D gather branches stay in EmitGatherOp, and the first static_assert now starts from VolumeGatherBase. Checks pass, the negative control fails, and the PR adds no new validation IDs.

What changed since my last review

  • Rebase onto main 086c0686c8, which now has #9183 (VolumeGatherBase 57664, EmitVolumeGatherLz).
    • In commits 2–5, the intermediate ids are now 57856, 57888 and 57920.
    • The final ids are unchanged: GatherLodBase 57856, GatherLodBiasBase 57920, GatherImageMinLodBase 57984.
  • DescriptorBindingBuilder.cpp:22: the first assert is now VolumeGatherBase + MaxImages <= GatherLodBase. This is 57664 + 64 = 57728 ≤ 57856.
  • SpirvImageEmitter.cpp:1695-1702: #9183's 3D branch returns before the 2D explicit-LOD branch at lines 1739-1753, and the two don't interact.
    • EmitTwoDimensionalGatherLod is identical to what I reviewed before.
    • Its guard still requires Dim2D.
  • The description and the 20:10 reply describe this rebase. Both say that apart from the assert the diff is the one I approved. delta.patch agrees: commits 4 and 5 are =, and the other changes are context lines and the id lines above.

What I ran

Head 48e75dd9d096d738f9318d49329ad050081e4b09, main 086c0686c8adff3ee648fdbe34e0b0b647e1d511, merged with --no-ff into tree be2973f389, in a clean worktree. Debian 13 VM, GCC 14.2.0, lavapipe (Mesa 26.1.6). This device has no VK_EXT_image_view_min_lod.

git diff --check 086c0686c8...48e75dd9d0
python3 tools/check_conventions.py --base 086c0686c8
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1 VK_LOADER_DEBUG=error,warn,layer \
  ctest --test-dir build -V -R '^agc_driver_image_gather_(lod|volume)$'
# negative control: restore the six production files from 086c0686c8
ninja -C build -j32 -l32 agc_driver_image_gather_lod_tests agc_driver_image_gather_volume_tests
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1 \
  ctest --test-dir build -V -R '^agc_driver_image_gather_(lod|volume)$'
# restore from be2973f389, rebuild, rerun
ninja -C build -j32 -l32 agc_driver_image_gather_lod_tests agc_driver_image_gather_volume_tests
ANYPS5_NO_SHADER_CACHE=1 ANYPS5_REQUIRE_VULKAN=1 ctest --test-dir build --output-on-failure -R '^agc_driver_image_gather_(lod|volume)$'
git status --porcelain
Check Result
git diff --check Clean
check_conventions.py All conventions checks passed
Build (script) OK
Selected tests (script) 2188 run, no new failures, no timeouts. The 7 failures are all known on main
Script's validation diff 784 tests; the layer loaded in 783 and was missing in none. One test has "new" VUID-s: agc_driver_image_gather_volume, which was not in the older baseline (see below)
agc_driver_image_gather_lod, with the validation layer Passed (0.95 s). The layer was inserted, there were no VUID- lines, and the T# MIN_LOD cases were skipped (no extension)
agc_driver_image_gather_volume, with the validation layer Passed (9.6 s). It logs four VUID-VkSamplerCreateInfo-addressMode* IDs
Same two tests with main's production files gather_lod fails. gather_volume passes and logs the same four VUID-s
After restoring the merged files Both pass, and git status --porcelain is empty
Negative control output (main's production files)
1234: AGC graphics: clamp to edge, point: thread 1 (LOD 1.000000, level 1) component 0 is 108, expected 113
1/2 Test #1234: agc_driver_image_gather_lod ......***Failed    0.19 sec
1237: Validation Error: [ VUID-VkSamplerCreateInfo-addressModeU-parameter ]
1237: Validation Error: [ VUID-VkSamplerCreateInfo-addressModeV-parameter ]
1237: Validation Error: [ VUID-VkSamplerCreateInfo-addressModeW-parameter ]
1237: Validation Error: [ VUID-VkSamplerCreateInfo-addressModeU-01079 ]
2/2 Test #1237: agc_driver_image_gather_volume ...   Passed    9.61 sec

The script's validation baseline is main 1e658521a0, which predates #9183, so it marked agc_driver_image_gather_volume as "test not run on main". With this PR's production files reverted to 086c0686c8, the test logs the same four IDs. They come from main's mirror-once sampler created without samplerMirrorClampToEdge, as the author says, and do not count against this PR.

Previous findings

  1. Specialization ids collide for sampler indices 32–63 (blocking, resolved in my last review): still resolved. The bases are 57856, 57920 and 57984, each sampler range is 64 wide, and the image range is now chained from VolumeGatherBase (57664–57727). The four asserts compile on the merged tree.

New findings

Blocking

None.

Non-blocking

  • The debt entry is out of date for 3D images after the rebase. docs/dev/debt/shader.md:252 lists "3D and cube images" among those that "still gather the base level". On main since #9183, though, a 3D gather without the LZ flag fails compilation with "has an unsupported 3D gather variant" (SpirvImageEmitter.cpp:1594-1597 on main; SpirvImageEmitter.cpp:1695-1698 in the merged tree). So a 3D image_gather4_l throws; it does not gather the base level. Suggested fix: drop "3D" from the base-level list, and end the entry with "on 1D and 3D images image_gather4_l throws".

Not verified

  • The T# MIN_LOD variants were skipped, because lavapipe has no VK_EXT_image_view_min_lod. Results on devices that have it are author- or contributor-reported, and the contributor runs were on earlier heads.
  • No shader with more than 32 samplers that gathers at an explicit LOD was compiled or run.
  • I did not check the id use of the open PRs named in the description (#10489, #14621); only main is in this tree.
  • The PPSA23276 in-game result and the cost numbers are author-reported and predate this rebase.

@github-actions github-actions Bot added the review queue Next in the maintainer review queue: checks pass, no open change request, no conflict label Nov 3, 2026
@adam25136

Copy link
Copy Markdown

Windows and NVIDIA Blackwell run, for head 48e75dd9d0 merged into main 04a9eb4fa2 (git merge --no-ff, clean merge).

GPU NVIDIA GeForce RTX 5080 (10de:2c02), driver 617.42 (32.0.16.1742), Vulkan 1.4.351
Host Windows 10 Pro 22H2 (19045), i9-12900KF, WinLibs GCC 15.2.0 r7, Release, BUILD_TESTING=ON
Selection 2,202 of 3,853 tests, chosen by tools/select_tests.py select from main's build graph for the 8 changed files
Run select_tests.py ctest build --parallel 4 --timeout 120 with ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1; main ran the full suite the same way on this machine
Result 2,189 passed, 12 skipped (the same 12 as on main), 1 failed, 0 new

The failure, agc_driver_direct_write_completion, fails on main on this machine as well, also when run on its own (33 of 34 runs). agc_driver_image_gather_lod and the other gather tests (agc_driver_image_gather_one_dimensional, _one_dimensional_array, _volume, agc_driver_image_packed_horizontal_gather, image_sample_gather) passed. The only test over 30 s was agc_driver_pipeline_journal, 31.6 s against 31.5 s on main.

Not run: the Khronos validation layer is not installed on this machine, so this says nothing about VUIDs. PPSA23276 was not run.

AI-assisted: yes. Claude Code ran the builds and tests and drafted this comment; the numbers are from the runs on this machine.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 48e75dd merged onto main 1c8c0ef · tested 2026-11-04 02:14 UTC · 32 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,734 passed · 7 failed (0 new, 3 also fail on main, 4 unstable on main) · 50 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,791 of 3,842 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (954 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.3 s
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 4 of the last 10 main runs
agc_driver_submit_progress: failed in 4 of the last 10 main runs
agc_submit_progress: failed in 5 of the last 10 main runs
agc_submit_progress_no_timeline: failed in 4 of the last 10 main runs
Failing on main too (3, not counted against this PR)
agc_driver_image_table
guest_mmap_executable
guest_socket_options_multicast
Validated but not compared (1, not in the main baseline's validation pass)
agc_driver_image_gather_lod
Validation IDs already present on main (8 IDs in 527 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (527 tests): agc_driver, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, agc_driver_bda_span_reads, and 519 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkSamplerCreateInfo-addressModeU-01079 (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeU-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeV-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeW-parameter (1 test): agc_driver_image_gather_volume
Skipped (50)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_dcc: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_deferred: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
vide
Reproduce
git fetch origin 48e75dd9d096d738f9318d49329ad050081e4b09 1c8c0efc8c41314402e6966da5bef6ea3d639d10
git checkout 1c8c0efc8c41314402e6966da5bef6ea3d639d10 && git merge --no-ff 48e75dd9d096d738f9318d49329ad050081e4b09
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 48e75dd merged onto main 1c8c0ef · tested 2026-11-04 03:10 UTC · 40 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,727 passed · 7 failed (0 new, 5 also fail on main, 2 unstable on main) · 57 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,791 of 3,842 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (1010 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s, ⚠️ public_headers 77.8 s, ⚠️ agc_driver_image_sample_unnormalized 31.3 s
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_depth_fast_clear_submit: failed in 2 of the last 10 main runs
agc_driver_pipeline_journal: failed in 8 of the last 10 main runs
Failing on main too (5, not counted against this PR)
agc_driver_image_table
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
trace_digest_TraceDigestTests_test_cli_rejects_invalid_event_fields_without_partial_output: Timeout in the parallel run; also passed only on retry in 42 other runs in the last 7 days
Validated but not compared (57, not in the main baseline's validation pass)
agc_driver_image_gather_lod
public_headers
public_headers_compile_0
public_headers_compile_10
public_headers_compile_12
public_headers_compile_14
public_headers_compile_16
public_headers_compile_18
public_headers_compile_2
public_headers_compile_20
public_headers_compile_21
public_headers_compile_22
public_headers_compile_23
public_headers_compile_24
public_headers_compile_25
public_headers_compile_26
public_headers_compile_27
public_headers_compile_28
public_headers_compile_29
public_headers_compile_30
public_headers_compile_31
public_headers_compile_32
public_headers_compile_33
public_headers_compile_34
public_headers_compile_35
public_headers_compile_36
public_headers_compile_37
public_headers_compile_38
public_headers_compile_39
public_headers_compile_4
public_headers_compile_40
public_headers_compile_41
public_headers_compile_42
public_headers_compile_43
public_headers_compile_44
public_headers_compile_45
public_headers_compile_46
public_headers_compile_47
public_headers_compile_48
public_headers_compile_49
public_headers_compile_50
public_headers_compile_51
public_headers_compile_52
public_headers_compile_53
public_headers_compile_54
public_headers_compile_55
public_headers_compile_56
public_headers_compile_57
public_headers_compile_58
public_headers_compile_59
public_headers_compile_6
public_headers_compile_60
public_headers_compile_61
public_headers_compile_62
public_headers_compile_63
public_headers_compile_8
trace_digest_TraceDigestTests_test_cli_rejects_invalid_event_fields_without_partial_output
Validation IDs already present on main (8 IDs in 518 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (518 tests): agc_driver, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, agc_driver_bda_span_reads, and 510 more
VUID-vkCmdCopyImage-srcImage-01548 (4 tests): agc_driver_depth_clear_hle, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (4 tests): agc_driver_depth_clear_hle, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkSamplerCreateInfo-addressModeU-01079 (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeU-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeV-parameter (1 test): agc_driver_image_gather_volume
VUID-VkSamplerCreateInfo-addressModeW-parameter (1 test): agc_driver_image_gather_volume
Skipped (57)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_idle_publish_wait: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a forced watch of the import reads its submissions as CPU stores
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_storage_pending_key_refresh: skipped, refreshes over a pending key store are not testable here
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not av
Reproduce
git fetch origin 48e75dd9d096d738f9318d49329ad050081e4b09 1c8c0efc8c41314402e6966da5bef6ea3d639d10
git checkout 1c8c0efc8c41314402e6966da5bef6ea3d639d10 && git merge --no-ff 48e75dd9d096d738f9318d49329ad050081e4b09
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed review queue Next in the maintainer review queue: checks pass, no open change request, no conflict labels Nov 4, 2026
@DonRyuzaki

Copy link
Copy Markdown
Contributor

Independent RDNA 3 / Windows check of head 48e75dd9d096: I built agc_driver_image_gather_lod_tests after merging the PR locally with upstream main pinned at d63781e9ce82 (local merge 59d9da1c2699). The only merge conflict was the shader debt entry; I retained both entries without changing production code. This is a pinned integration test, not a claim that the PR currently merges cleanly into the latest main.

On the selected AMD Radeon RX 7800 XT (1002:747e, subgroup 64) with ANYPS5_GPU=7800 XT, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, and VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, agc_driver_image_gather_lod passed 3/3 independent CTest runs (1/1 in each). The current test source runs two T# MIN_LOD 2.5 variants when VK_EXT_image_view_min_lod is available; none of the three logs printed its explicit “T# MIN_LOD cases skipped” line, and each reached the final pass line. Thus both optional variants ran here.

This exercises the four-component gather/readback comparison against a CPU 2×2 footprint across 32 LOD inputs and the test's clamp, wrap, mirror, fractional LOD, sampler LOD clamp/bias, mip-filter-none, UINT-with-bilinear-sampler, and border-fallback cases. It adds a current-head RX 7800 XT Windows result alongside the existing RDNA 4 Windows, RADV, and NVIDIA reports. I did not run PPSA23276 or a paired main baseline in this check.

The installed validation layer uses Vulkan header version 284 and emitted VUID-VkDeviceCreateInfo-pNext-pNext for two unknown device-creation pNext structures in each run. The test still passed; I cannot attribute those VUIDs to this PR without a same-environment main baseline. Build and verbose CTest logs are retained locally. AI-assisted local testing.

yinx080 and others added 6 commits November 5, 2026 10:57
SPIR-V gathers take no LOD without SPV_AMD_texture_gather_bias_lod, so
image_gather4_l gathered the base level. Sample the four footprint texels
at their centres with the explicit LOD instead. UE5's Nanite HZB occlusion
test reads mip 0 otherwise and culls geometry behind narrow gaps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Specialize samplers of image_gather4_l on their LOD state (MIN_LOD,
MAX_LOD, LOD_BIAS, MIP_FILTER none, CLAMP_X/Y), select the level in the
shader with float clamps before any conversion, and fetch the footprint
texels at that level instead of sampling them, so the sampler's clamp
and filter cannot move or blend them. Border clamp modes and sampler
tables keep the base-level gather.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m LOD

Specialize the images of explicit-LOD gathers on their T# MIN_LOD above
the base level and raise the chosen LOD to it after the sampler's clamp,
so the fetched level never falls below the view's integer minimum LOD.
Clamp the sampler LOD in an explicit order (MIN_LOD, then MAX_LOD) and
keep images and samplers read from a table on the base-level gather.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… view's minimum level

With MIP_FILTER none the fetch took level 0, below the view's integer
minimum LOD when the T# MIN_LOD is at least one level above the base
level. It now takes the T# MIN_LOD above the base level rounded down,
clamped to the view's last level.

The graphics-pipeline-library prebuild no longer builds shaders with an
explicit-LOD gather sampler: it specializes on zeroed sampler words, so
a draw never uses that module.

The mirror-once test variant is dropped until the sampler creates
MIRROR_CLAMP_TO_EDGE with samplerMirrorClampToEdge enabled; the debt
entry now says 1D image_gather4_l throws and 2D arrays gather the base
level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…view_min_lod

Without the extension the texture path refuses a descriptor with a
minimum LOD clamp before the gather runs, so the two T# MIN_LOD variants
failed the whole test on such devices (lavapipe, MoltenVK). They now run
only when the device has it; the other variants run everywhere.

Reported, with this change, by jotitadev in the PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main raised ShaderInfo::MaxSamplers to 64 (12b8923), but the
GatherLodBase and GatherLodBiasBase ranges stayed 32 wide, so sampler
32's LOD state shared an id with sampler 0's bias, and sampler 32's
bias with image 0's GatherImageMinLodBase value. Each sampler range is
now 64 wide, and DescriptorBindingBuilder asserts that each range fits
before the next base, from VolumeGatherBase up to ImageComponentBase.

The ranges start at 57856 (GatherLodBase 57856, GatherLodBiasBase
57920, GatherImageMinLodBase 57984, with room for 128 images up to
58111), above VolumeGatherBase (57664) and the ids open pull requests
take (FmaskFormatBase at 57728-57855 with 128 images, SamplerLodBiasBase
at 57664), and below DeviceSamplerClassBase at 58368.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yinx080
yinx080 force-pushed the fix/shader-gather4-l-lod branch from 48e75dd to 51d4bda Compare November 5, 2026 10:31
@github-actions github-actions Bot added review queue Next in the maintainer review queue: checks pass, no open change request, no conflict and removed needs rebase Conflicts with main, rebase needed labels Nov 5, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ AMD GPU test: passed

PR head 51d4bda merged onto main e772fb2 · tested 2026-11-05 15:37 UTC · 42 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,887 passed · 9 failed (0 new, 6 also fail on main, 3 unstable on main) · 68 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,964 of 4,074 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (1036 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s, ⚠️ public_headers 85.9 s, ⚠️ agc_driver_image_sample_unnormalized 47.6 s, ⚠️ agc_driver_lazy_resolved_abi 37.8 s, ⚠️ agc_shader_sampler_limits 37.1 s
Unstable on main (3, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 9 of the last 10 main runs
agc_submit_progress: failed in 9 of the last 10 main runs
agc_submit_progress_no_timeline: failed in 9 of the last 10 main runs
Failing on main too (6, not counted against this PR)
agc_driver_image_table
agc_driver_native_depth_bda_clear
agc_driver_native_multisample_depth
agc_driver_submit_progress
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
agc_driver_store_pending_identity: Failed in the parallel run; also passed only on retry in 3 other runs in the last 7 days
Validated but not compared (55, not in the main baseline's validation pass)
agc_driver_image_gather_lod
public_headers
public_headers_compile_1
public_headers_compile_11
public_headers_compile_13
public_headers_compile_15
public_headers_compile_17
public_headers_compile_19
public_headers_compile_21
public_headers_compile_22
public_headers_compile_23
public_headers_compile_24
public_headers_compile_25
public_headers_compile_26
public_headers_compile_27
public_headers_compile_28
public_headers_compile_29
public_headers_compile_3
public_headers_compile_30
public_headers_compile_31
public_headers_compile_32
public_headers_compile_33
public_headers_compile_34
public_headers_compile_35
public_headers_compile_36
public_headers_compile_37
public_headers_compile_38
public_headers_compile_39
public_headers_compile_40
public_headers_compile_41
public_headers_compile_42
public_headers_compile_43
public_headers_compile_44
public_headers_compile_45
public_headers_compile_46
public_headers_compile_47
public_headers_compile_48
public_headers_compile_49
public_headers_compile_5
public_headers_compile_50
public_headers_compile_51
public_headers_compile_52
public_headers_compile_53
public_headers_compile_54
public_headers_compile_55
public_headers_compile_56
public_headers_compile_57
public_headers_compile_58
public_headers_compile_59
public_headers_compile_60
public_headers_compile_61
public_headers_compile_62
public_headers_compile_63
public_headers_compile_7
public_headers_compile_9
Validation IDs already present on main (4 IDs in 551 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (551 tests): agc_driver, agc_driver_address_draw_space_reuse, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, and 543 more
VUID-vkCmdCopyImage-srcImage-01548 (4 tests): agc_driver_depth_clear_hle, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (4 tests): agc_driver_depth_clear_hle, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (68)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_idle_publish_wait: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a forced watch of the import reads its submissions as CPU stores
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_storage_pending_key_refresh: skipped, refreshes over a pending key store are not testable here
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_net_ctl_disconnected: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_device_capability: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_disabled: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_four_k: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_height_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_limit: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 n
Reproduce
git fetch origin 51d4bda0b153bd6cae92991248da6718a2aefe81 e772fb25cc72ee5faf77847f06bbc69533016e82
git checkout e772fb25cc72ee5faf77847f06bbc69533016e82 && git merge --no-ff 51d4bda0b153bd6cae92991248da6718a2aefe81
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 51d4bda merged onto main 080dbd0 · tested 2026-11-05 17:56 UTC · 27 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,903 passed · 4 failed (0 new, 3 also fail on main, 1 unstable on main) · 56 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,963 of 4,077 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (980 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 114.2 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_submit_progress: failed in 1 of the last 10 main runs
Failing on main too (3, not counted against this PR)
agc_driver_image_table
guest_mmap_executable
guest_socket_options_multicast
Validated but not compared (1, not in the main baseline's validation pass)
agc_driver_image_gather_lod
Validation IDs already present on main (9 IDs in 592 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (592 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_draw_space_reuse, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, and 584 more
VUID-VkShaderModuleCreateInfo-pCode-08737 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08739 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08741 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
VUID-VkPipelineShaderStageCreateInfo-module-parameter (1 test): agc_driver_float16_unused_nan_payloads
VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849 (1 test): agc_driver_float16_unused_nan_payloads
Skipped (56)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl_disconnected: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_device_capability: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_disabled: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_four_k: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_height_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_limit: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_malformed: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_width_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_fl
Reproduce
git fetch origin 51d4bda0b153bd6cae92991248da6718a2aefe81 080dbd08c6f83be5a1768af7d3342d701bf34d21
git checkout 080dbd08c6f83be5a1768af7d3342d701bf34d21 && git merge --no-ff 51d4bda0b153bd6cae92991248da6718a2aefe81
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed review queue Next in the maintainer review queue: checks pass, no open change request, no conflict labels Nov 5, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 6, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head 7c07be9 merged onto main 82fcf46 · tested 2026-11-06 05:48 UTC · 27 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,932 passed · 4 failed (0 new, 3 also fail on main, 1 unstable on main) · 55 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,991 of 4,158 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (989 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.1 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 1 of the last 10 main runs
Failing on main too (3, not counted against this PR)
agc_driver_image_table
guest_mmap_executable
guest_socket_options_multicast
Validated but not compared (1, not in the main baseline's validation pass)
agc_driver_image_gather_lod
Validation IDs already present on main (9 IDs in 593 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (593 tests): agc_driver, agc_driver_address_draw_space_reuse, agc_driver_address_passes, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, agc_driver_bda_anchor_reads, and 585 more
VUID-VkShaderModuleCreateInfo-pCode-08737 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08739 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08741 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (1 test): agc_driver_prepared_exports
VUID-VkPipelineShaderStageCreateInfo-module-parameter (1 test): agc_driver_float16_unused_nan_payloads
VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849 (1 test): agc_driver_float16_unused_nan_payloads
Skipped (55)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_net_ctl_disconnected: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_device_capability: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_disabled: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_four_k: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_height_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_limit: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_malformed: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_width_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in curre
Reproduce
git fetch origin 7c07be968624872fbf48c9b9542649640d524186 82fcf46d250e0ec2f436ceffd607dd2ff04c62d7
git checkout 82fcf46d250e0ec2f436ceffd607dd2ff04c62d7 && git merge --no-ff 7c07be968624872fbf48c9b9542649640d524186
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@github-actions github-actions Bot added the review queue Next in the maintainer review queue: checks pass, no open change request, no conflict label Nov 6, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs review queue Next in the maintainer review queue: checks pass, no open change request, no conflict shader core/shader unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants