Commit graph

3 commits

Author SHA1 Message Date
catbot
ac9180076c perf(ui): hoist loop-invariant per-item math out of the per-pixel loop (#125)
The cooperative-load section already streams each item into shared memory
once per workgroup, but the per-pixel inner loop then recomputed values
that are constant for the whole item/glyph. The compiler can't hoist them
itself — they read shared memory at a varying index.

Precompute them once per item at load time into new shared slots:

  - ui-images / ui-text / ui-fused (images+text phases): invRectSize =
    1.0/rect.zw, turning the per-pixel `(sp-rect.xy)/rect.zw` vec2 divide
    into a multiply.
  - ui-text / ui-fused (text phase): the SDF AA `band` (a vec2 divide +
    two maxes depending only on the glyph's uv span and rect size).

In ui-fused the new slots (s_inv, s_band) are reused across phases like
the existing s_v* scratch, so the LDS bump is per-category, not summed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 13:22:07 +00:00
catbot
e8f1c7cff9 Merge remote-tracking branch 'origin/master' into claude/issue-47
# Conflicts:
#	interfaces/Crafter.Graphics-UI.cppm
#	project.cpp
2026-06-16 17:09:06 +00: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