Commit graph Crafter.Graphics/tests/UIFusedShader
Author SHA1 Message Date
2b6f90b12c 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>
2026-10-06 17:28:46 +02:00
42a479572d SPDX license update 2026-07-22 18:09:06 +02:00
catbot
574242dd30 perf(ui): add additive DispatchFused to collapse consecutive UI passes (#47)
Add a fifth UI compute path — DispatchFused — alongside the four
per-element Dispatch* calls. It composites quads → circles → images →
text (canonical back-to-front order) in ONE dispatch that loads the
destination image once and stores once, eliminating the per-pass
load+store and the inter-pass VkMemoryBarriers a run of consecutive
Dispatch* calls would pay. The win materialises at >= 2 non-empty
categories; an absent category is a zero-trip, push-constant-uniform
loop (~free).

The new uber-kernel (shaders/ui-fused.comp.glsl) reuses each category's
exact cooperative shared-memory tile-cull + per-pixel accumulate, so a
fused category is pixel-identical to its standalone pass. Categories run
sequentially per thread and share one set of shared-memory scratch, so
the VGPR/shared high-water mark stays at the per-category level, not the
sum.

Additive and non-breaking: the per-element Dispatch* calls and the
frozen 48-byte UIDispatchHeader are untouched. DispatchFused gets its own
128-byte push-constant layout (UIFusedHeader: four uvec4 headers + four
per-category clip vec4s). To interleave a custom ui.Dispatch() between
standard passes, bracket it with two DispatchFused calls — the dispatch
boundary is the explicit, app-declared flush point.

WebGPU (Vulkan-second): DispatchFused falls back to the per-element
Dispatch* calls in canonical order — one texture/sampler per dispatch
there can't feed the bindless image phase — so the result matches; only
the load/store fusion is Vulkan-only.

HelloUI now composites its background quads, mouse circle, and label
text in a single DispatchFused. Verified rendering identical on a live
Vulkan/Wayland session.

Resolves #47

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 17:03:24 +00:00