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>
This commit is contained in:
parent
c58b68ec40
commit
574242dd30
8 changed files with 720 additions and 8 deletions
30
project.cpp
30
project.cpp
|
|
@ -204,6 +204,7 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
cfg.shaders.emplace_back(fs::path("shaders/ui-circles.comp.glsl"), std::string("main"), ShaderType::Compute);
|
||||
cfg.shaders.emplace_back(fs::path("shaders/ui-images.comp.glsl"), std::string("main"), ShaderType::Compute);
|
||||
cfg.shaders.emplace_back(fs::path("shaders/ui-text.comp.glsl"), std::string("main"), ShaderType::Compute);
|
||||
cfg.shaders.emplace_back(fs::path("shaders/ui-fused.comp.glsl"), std::string("main"), ShaderType::Compute);
|
||||
cfg.buildFiles.emplace_back(fs::path("shaders/ui-shared.glsl"));
|
||||
|
||||
// Regression test for issue #18: drive the NVIDIA descriptor-heap
|
||||
|
|
@ -413,6 +414,35 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
|||
memImpls.emplace_back("tests/MemoryTypeFallback/main");
|
||||
mc.GetInterfacesAndImplementations(ifaces, memImpls);
|
||||
cfg.tests.push_back(std::move(memTest));
|
||||
|
||||
// Issue #47: the fused UI uber-kernel (shaders/ui-fused.comp.glsl) and
|
||||
// its C++ push-constant mirror UIFusedHeader. Compiles the real shader
|
||||
// with glslang, validates with spirv-val, and pins the push-constant
|
||||
// member offsets to UIFusedHeader's layout so a GLSL/C++ drift can't
|
||||
// slip through (the C++ static_assert only guards the C++ side). No GPU
|
||||
// device at runtime, but glslang + spirv-val are required tools.
|
||||
Test fusedTest;
|
||||
Configuration& fc = fusedTest.config;
|
||||
fc.path = cfg.path;
|
||||
fc.name = "UIFusedShader";
|
||||
fc.outputName = "UIFusedShader";
|
||||
fc.type = ConfigurationType::Executable;
|
||||
fc.target = cfg.target;
|
||||
fc.march = cfg.march;
|
||||
fc.mtune = cfg.mtune;
|
||||
fc.debug = cfg.debug;
|
||||
fc.sysroot = cfg.sysroot;
|
||||
fc.dependencies = cfg.dependencies;
|
||||
fc.externalDependencies = cfg.externalDependencies;
|
||||
fc.compileFlags = cfg.compileFlags;
|
||||
fc.linkFlags = cfg.linkFlags;
|
||||
fc.defines = cfg.defines;
|
||||
fc.cFiles = cfg.cFiles;
|
||||
std::vector<fs::path> fusedImpls(impls.begin(), impls.end());
|
||||
fusedImpls.emplace_back("tests/UIFusedShader/main");
|
||||
fc.GetInterfacesAndImplementations(ifaces, fusedImpls);
|
||||
fusedTest.requires_ = { "tool:glslang", "tool:spirv-val" };
|
||||
cfg.tests.push_back(std::move(fusedTest));
|
||||
}
|
||||
|
||||
return cfg;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue