[perf] Single-lane serial stream compaction stalls 63/64 threads — use subgroup prefix-sum (ui-fused.comp.glsl) #124
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Catcrafts/Crafter.Graphics#124
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found by an adversarially-verified performance audit (confirmed real after a skeptic pass).
Location:
shaders/ui-fused.comp.glsl:79-87(same pattern inlined in ui-quads/circles/images/text)Severity: medium · Effort: moderate · Category: gpu
Problem
uiCompactChunkruns the survivor scan entirely ongl_LocalInvocationIndex == 0between two barriers — ~64 serial iterations with 63/64 lanes idle, 4× per chunk in the fused kernel. It must produce a stable in-order exclusive prefix of the survivors (s_order[]in buffer order) so the per-pixel inner loop still sees items in draw order.Fix — subgroup exclusive prefix-sum (primary)
Enable
GL_KHR_shader_subgroup_arithmetic(+_ballot) and replace the lane-0 scan withsubgroupExclusiveAdd(s_keep[k])to compute each survivor's output slot ins_order[]at register speed. This beats the shared-memory fallback on both barrier count and shared-memory traffic.Safe to enable here:
additional/dom-webgpu.js); these GLSL files compile to SPIR-V (ui-fused.comp.spv) for the Vulkan path only.apiVersion = 1.4with a hard requirement onVK_EXT_descriptor_heap, and every GPU that clears that bar supports Vulkan 1.1 compute subgroup ballot+arithmetic. No realistic device is excluded, so no correctness fallback is required.Implementation caveats (why this stays "moderate"):
descriptor_heaptarget, up to 8 on 8-wide Intel. So it's per-subgroupsubgroupExclusiveAdd→ write each subgroup's total to a shared slot → tiny scan over the ≤8 partial totals → add each subgroup's base as a carry. (Could instead pin width viaVK_EXT_subgroup_size_control+requiredSubgroupSize, but the general carry avoids that machinery.)atomicAdd(s_count, 1)per survivor — atomics yield arbitrary order and would break draw order. Exclusive prefix-add preserves it; atomics don't.Magnitude
The win is the compaction bubble only — SSBO traffic and the surrounding barriers are unchanged, and the per-pixel survivor loop (SDF /
texture()fetches) dominates frame cost. This tightens a per-chunk serial bubble; it does not transform frame time. Medium severity is correct.[perf] Single-lane serial stream compaction stalls 63/64 threads (ui-fused.comp.glsl)to [perf] Single-lane serial stream compaction stalls 63/64 threads — use subgroup prefix-sum (ui-fused.comp.glsl)