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
|
|
@ -170,14 +170,20 @@ int main() {
|
|||
0, 0, 0, 0,
|
||||
};
|
||||
|
||||
// Flush + dispatch. The library inserts the inter-dispatch barriers.
|
||||
// Flush the buffers the GPU is about to read, then composite all three
|
||||
// categories — background quads, the mouse circle, and the button/label
|
||||
// text — in ONE fused dispatch (issue #47). The frame runs these passes
|
||||
// back-to-back with no custom shader interleaved, so DispatchFused loads
|
||||
// and stores the swapchain image once instead of three times and skips
|
||||
// the two inter-pass barriers DispatchQuads→Circles→Text would insert.
|
||||
// Canonical order is quads → circles → images → text; here there are no
|
||||
// images, so that category is a free no-op.
|
||||
if (qc > 0) {
|
||||
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
quadsBuf.FlushDevice(cmd, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||
#else
|
||||
quadsBuf.FlushDevice();
|
||||
#endif
|
||||
ui.DispatchQuads(cmd, quadsSlot, qc);
|
||||
}
|
||||
if (cc > 0) {
|
||||
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
|
@ -185,7 +191,6 @@ int main() {
|
|||
#else
|
||||
circlesBuf.FlushDevice();
|
||||
#endif
|
||||
ui.DispatchCircles(cmd, circlesSlot, cc);
|
||||
}
|
||||
if (gc > 0) {
|
||||
#ifndef CRAFTER_GRAPHICS_WINDOW_DOM
|
||||
|
|
@ -193,8 +198,12 @@ int main() {
|
|||
#else
|
||||
glyphsBuf.FlushDevice();
|
||||
#endif
|
||||
ui.DispatchText(cmd, glyphsSlot, gc);
|
||||
}
|
||||
ui.DispatchFused(cmd,
|
||||
{quadsSlot, qc},
|
||||
{circlesSlot, cc},
|
||||
{}, // no images this frame
|
||||
{glyphsSlot, gc});
|
||||
});
|
||||
|
||||
window.FinishInit();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue