test(ui): the descriptor-set fallback draws the same frame as the heap

UIFallbackParity renders one scene through both binding models and
compares the frames. It starts itself twice, once as is and once with
CRAFTER_GRAPHICS_NO_DESCRIPTOR_HEAP, so the fallback cannot drift from
the heap path on a machine that never takes it. The scene reaches what
HelloUI does not: the four standalone kernels next to the fused one, a
sampled image with two samplers indexed per item, an image slot and a
sampler slot that are freed and registered again, a resize, an image
created with ImageVulkan::Create's default consumer stage, and a
generic Dispatch whose push payload is not a whole number of words.

On a driver without the heap both runs take the fallback, and the test
then only checks that the scene renders. Validation errors raised
while rendering fail a run, which only bites when
CRAFTER_GRAPHICS_VALIDATION is set. They are counted before the frame
capture because Window::SaveFrame raises one itself (#157).

UIFusedShader now also compiles and validates the descriptor-set build
of the fused kernel and pins its push-constant layout: both builds are
fed the same UIFusedHeader bytes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-06 17:28:46 +02:00
commit 2b6f90b12c
3 changed files with 513 additions and 43 deletions

View file

@ -373,6 +373,40 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
setupReuseImpls.emplace_back("tests/SetupCmdBufferReuse/main");
sr.GetInterfacesAndImplementations(ifaces, setupReuseImpls);
cfg.tests.push_back(std::move(setupReuseTest));
// The UI path has two binding models: VK_EXT_descriptor_heap, and
// a bindless descriptor set for drivers without it (Mesa Turnip).
// The test renders one scene through each — the second by
// re-running itself with CRAFTER_GRAPHICS_NO_DESCRIPTOR_HEAP — and
// compares the frames, so the fallback cannot drift from the heap
// path unnoticed on a machine that never takes it. Unlike the
// other tests it loads the real UI kernels, so it carries the
// library's shaders (both variants) and a font into its own bin
// dir. Needs the Wayland backend + a real compositor.
Test parityTest;
Configuration& up = parityTest.config;
up.path = cfg.path;
up.name = "UIFallbackParity";
up.outputName = "UIFallbackParity";
up.type = ConfigurationType::Executable;
up.target = cfg.target;
up.march = cfg.march;
up.mtune = cfg.mtune;
up.debug = cfg.debug;
up.sysroot = cfg.sysroot;
up.dependencies = cfg.dependencies;
up.externalDependencies = cfg.externalDependencies;
up.compileFlags = cfg.compileFlags;
up.linkFlags = cfg.linkFlags;
up.defines = cfg.defines;
up.cFiles = cfg.cFiles;
up.shaders = cfg.shaders;
up.buildFiles = cfg.buildFiles;
up.files.emplace_back(fs::path("examples/HelloUI/font.ttf"));
std::vector<fs::path> parityImpls(impls.begin(), impls.end());
parityImpls.emplace_back("tests/UIFallbackParity/main");
up.GetInterfacesAndImplementations(ifaces, parityImpls);
cfg.tests.push_back(std::move(parityTest));
}
// Issue #36: BLAS build options. Drives the real hardware AS-build