perf(ui): gate per-pixel clip compares behind a flags bit (#50)
uiResolveScreenPixel ran four float compares against hdr.clipRectPx for
every pixel of every UI dispatch, even though the clip rect is the
full-surface default {0,0,1e9,1e9} in the overwhelmingly common case.
Reserve the high bit of the header flags word (UI_FLAG_CLIP) and have
FillHeader set it only when the clip rect is narrower than the surface
(extracted into the unit-testable UIRenderer::ClipFlags). The shader
gates the compares on the bit; the branch is push-constant-uniform so it
never diverges. When the rect covers the surface the skipped compares
could never reject an in-surface pixel, so output is pixel-identical.
Mirrored on the WebGPU/WGSL path (dom-webgpu.js) for parity. Adds the
UIClipFlag CPU test covering the gate decision.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c58b68ec40
commit
65c19ea84a
6 changed files with 192 additions and 9 deletions
|
|
@ -27,10 +27,19 @@ struct UIDispatchHeader {
|
|||
vec4 clipRectPx; // (xy, wh) — every standard shader honors this
|
||||
uint itemCount;
|
||||
uint frameIdx;
|
||||
uint flags; // user-defined feature bits
|
||||
uint flags; // feature bits — high bit reserved by the renderer
|
||||
uint _pad; // reserved — keep zeroed
|
||||
};
|
||||
|
||||
// ─── header flag bits ───────────────────────────────────────────────────
|
||||
// `flags` is otherwise free for user-defined feature bits, but the renderer
|
||||
// reserves the high bit: UI_FLAG_CLIP is set by UIRenderer::FillHeader when
|
||||
// the clip rect does not already cover the whole surface. uiResolveScreenPixel
|
||||
// gates its four clipRectPx compares on this bit so the overwhelmingly common
|
||||
// full-surface case (clipRect == {0,0,1e9,1e9}) skips them entirely. The
|
||||
// branch is push-constant-uniform across the dispatch, so it never diverges.
|
||||
const uint UI_FLAG_CLIP = 0x80000000u;
|
||||
|
||||
// ─── standard item structs ──────────────────────────────────────────────
|
||||
// These match the C++ Crafter::QuadItem / CircleItem / ImageItem / GlyphItem
|
||||
// byte-for-byte under std430.
|
||||
|
|
@ -121,9 +130,11 @@ QuadItem LoadQuadItem(uint heap, uint i) {
|
|||
bool uiResolveScreenPixel(UIDispatchHeader hdr, out ivec2 screenPx) {
|
||||
uvec2 px = gl_GlobalInvocationID.xy;
|
||||
if (px.x >= hdr.surfaceSize.x || px.y >= hdr.surfaceSize.y) return false;
|
||||
if (float(px.x) < hdr.clipRectPx.x || float(px.y) < hdr.clipRectPx.y) return false;
|
||||
if (float(px.x) >= hdr.clipRectPx.x + hdr.clipRectPx.z) return false;
|
||||
if (float(px.y) >= hdr.clipRectPx.y + hdr.clipRectPx.w) return false;
|
||||
if ((hdr.flags & UI_FLAG_CLIP) != 0u) {
|
||||
if (float(px.x) < hdr.clipRectPx.x || float(px.y) < hdr.clipRectPx.y) return false;
|
||||
if (float(px.x) >= hdr.clipRectPx.x + hdr.clipRectPx.z) return false;
|
||||
if (float(px.y) >= hdr.clipRectPx.y + hdr.clipRectPx.w) return false;
|
||||
}
|
||||
screenPx = ivec2(px);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue