Skip to content

fix(test): sample the anisotropic min/max rows without a mip filter - #7364

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
SandstromPL:fix/test-aniso-reduction-no-mip
Oct 18, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
SandstromPL:fix/test-aniso-reduction-no-mip

Conversation

@SandstromPL

Copy link
Copy Markdown
Contributor

What

This follows #5821, which accepts a min or max reduction sampler with anisotropic filtering and added two anisotropic rows to agc_driver_image_sample_reduction (tests/execution/ImageSampleReduction.cpp). On an RTX 4090 (NVIDIA 616.92, Windows 11), the first of them fails on main, as @frankea reported on #5821:

AGC graphics: min, 2x anisotropic bilinear, point mip: thread 0 sampled 5.000000, expected 17.000000

The problem is in the test, not the decoder. The two rows sample a two-level texture with image_sample_l at an explicit LOD of 0.25 (even threads) or 0.75 (odd threads) through a point mip filter, and expect only the nearest level. At thread 0 (LOD 0.25), level 0's bilinear footprint is 17, 17, 20 and 20, and level 1 there is 5, so the 4090's 5 comes from level 1. #5821 expected these samples to stay isotropic, and so on the nearest level, because a Lod operand sets the derivatives to zero (textures.adoc L1376-L1378). But Vulkan leaves the anisotropic filtering scheme to the implementation. It only says that implementations should consider magFilter, minFilter, mipmapMode, minLod and maxLod (textures.adoc L2777-L2783). So which levels an anisotropic sample reads through a point mip filter is up to the host driver, and on the 4090 it reads level 1 at LOD 0.25.

The two rows now use no mip filter (MipNone, named "no mip"), like the two existing "no mip" rows above them:

  • For an S# without a mip filter, the decoder leaves the host sampler's minLod and maxLod at 0 (GuestSamplerResource.cpp L126-L132), and Sampler passes them to vkCreateSampler (Sampler.cpp L41-L42).
  • The test's S# keeps MAX_LOD at 0xfff (ImageSampleReduction.cpp L95); without a mip filter the decoder ignores it.
  • Vulkan clamps the LOD to [minLod, maxLod] (textures.adoc L1697-L1703, L1726), so it is 0 on every thread. Level 1 then gets no weight even under a linear mip filter (δ = 0, L1824-L1826), and a min or max reduction only takes values with non-zero weight (L2761-L2765). So level 0 is the only level read, whichever mip mode the anisotropic scheme applies, as long as it honours maxLod. The anisotropic paragraph above asks for that, and the 4090 run and the negative control below show that NVIDIA and lavapipe do.
  • The test's expected value already takes level 0 for a sampler without a point mip filter (ImageSampleReduction.cpp L180), so only the two rows change.

This matches what was measured on RDNA2 without derivatives, where the sample reads level 0:

  • Tamalero on #5156 (gfx1030): image_sample_l at LOD 0 through an S# without a mip filter, over a single-level texture.
  • @adriadam10 on #5821 (gfx1035): image_sample_lz.

In both, min or max with anisotropic bilinear returned exactly the bilinear minimum or maximum, which is what the two rows expect.

The rows no longer check mip level selection under anisotropy, which Vulkan leaves to the host driver. The four point-mip rows without anisotropy still check it. The rows still check the reduction: at thread 0 a weighted average would be 17.45, not 17. They also still fail if the decoder refuses a min or max reduction with anisotropy again. Neither before nor after did these explicit-LOD rows check anisotropic filtering itself, since they expect the plain bilinear minimum or maximum; agc_driver_graphics (tests/GuestSamplerResource.cpp L180-L197) checks that anisotropyEnable, maxAnisotropy and the reduction reach vkCreateSampler. Only the test changes.

Tested

Ubuntu 24.04, GCC 13.3, Release with BUILD_TESTING=ON, on main 195d0d0. Locally, the libusb lines were removed from CMakeLists.txt, because the libusb submodule isn't checked out here.

  • agc_driver_image_sample_reduction passes 5 of 5 runs on the RTX 3050 Ti and 3 of 3 on lavapipe.
  • All 46 agc_driver_image* tests pass on the RTX 3050 Ti.
  • @frankea ran this change on the RTX 4090 (NVIDIA 616.92, Windows 11, MinGW-w64 GCC 15.2.0, Release, main 922dc89, the same two-row diff; the test, GuestSamplerResource.cpp and Sampler.cpp are the same there as on 195d0d0):
    • with the change, the test passes 3 of 3 runs, and 5 of 5 in a second build of the same tree;
    • with the unchanged rows, in the same build, with the shader cache kept and with it cleared, it fails every time with thread 0 sampled 5.000000, expected 17.000000;
    • main 31cbb55 (the tree of his first report), rebuilt from scratch, fails 3 of 3.

Negative control: the unchanged rows pass on the RTX 3050 Ti and on lavapipe, so neither shows the 4090's result by itself. I tried a temporary local change that gives the host sampler a linear mip filter whenever anisotropy is on (mipFilter == 2u || aniso at GuestSamplerResource.cpp L148; it is not part of this PR), so that the sample at LOD 0.25 also reads level 1, as on the 4090. With it:

  • The unchanged rows fail 3 of 3 on the RTX 3050 Ti with the 4090's exact line, min, 2x anisotropic bilinear, point mip: thread 0 sampled 5.000000, expected 17.000000.
  • The unchanged rows fail 3 of 3 on lavapipe with thread 0 sampled 14.000000, expected 17.000000, which is 0.75 × 17 + 0.25 × 5: lavapipe's anisotropic path blends the two levels' minima, where the RTX 3050 Ti returns 5, the minimum over both levels that Vulkan's mipmap filtering gives for a min reduction (L2761-L2765).
  • The rows in this PR pass 3 of 3 on both, because maxLod 0 still keeps them on level 0.

With the local change reverted, the test passes again on both.

tools/check_conventions.py from main passes with this title and body.

@frankea, thanks for both 4090 runs: the report and the check of this change.

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: none
  • GPUs tested: NVIDIA GeForce RTX 3050 Ti Laptop GPU, driver 580.178.04, Ubuntu 24.04; lavapipe (Mesa 25.2.8, LLVM 20.1.2), Ubuntu 24.04; NVIDIA GeForce RTX 4090, driver 616.92, Windows 11 (by @frankea, results)
  • AI-assisted: yes (Claude Code); I reviewed and tested every line

🤖 Generated with Claude Code

The two anisotropic rows of agc_driver_image_sample_reduction sampled
a two-level texture at explicit LOD 0.25 and 0.75 through a point mip
filter, and expected only the nearest level in the min or max. Vulkan
leaves the anisotropic filtering scheme to the implementation, and
only recommends that it consider mipmapMode, minLod and maxLod. On an
RTX 4090 (NVIDIA 616.92) the min row returns 5, a level-1 texel, where
level 0's bilinear minimum is 17, so the test fails on main there.

The rows now use no mip filter, which the decoder turns into a host
sampler with minLod and maxLod 0, so they read level 0 only, as the
RDNA2 measurements without derivatives did (gfx1030 with no mip filter
over a single-level texture, gfx1035 with image_sample_lz). They still
throw if the min or max reduction with anisotropy is refused again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the agc libSceAgc* label Oct 18, 2026
@oneandonlydean
oneandonlydean merged commit 6313b1f into boykopovar:main Oct 18, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants