Orgnaization & cleanup

This commit is contained in:
Zachary Levy
2026-04-30 16:52:55 -07:00
parent 16989cbb71
commit fd64bc01bf
13 changed files with 269 additions and 258 deletions
+22 -23
View File
@@ -1,19 +1,18 @@
#version 450 core
// Unified backdrop blur fragment shader.
// Handles both H-blur (mode 0, blurs the ¼-resolution downsample texture into
// the ¼-resolution h_blur texture) and V-blur+composite (mode 1, blurs h_blur
// vertically, masks via RRect SDF, applies tint, composites outline, and writes
// to the main render target with premultiplied alpha).
// Handles both the 1D separable blur passes (mode 0, used for BOTH the H-pass and V-pass;
// `direction` picks the axis) and the composite pass (mode 1, reads the fully-blurred
// working texture, masks via RRect SDF, applies tint, and writes to source_texture with
// premultiplied-over blending). Working textures are sized at the full swapchain resolution;
// downsampled content occupies only a sub-rect at downsample factor > 1 (set via viewport).
//
// Following RAD's pattern, V-mode replaces a separate composite pass: the SDF
// discard limits V-blur work to the masked region, and the per-primitive tint
// is folded in. Output blends with the main render target via the standard
// premultiplied-over blend state (ONE, ONE_MINUS_SRC_ALPHA).
// The composite blends with source_texture via the standard premultiplied-over blend state
// (ONE, ONE_MINUS_SRC_ALPHA).
//
// Backdrop primitives are tint-only — there is no outline. A specialized edge
// effect (e.g. liquid-glass-style refraction outlines) would be implemented
// as a dedicated primitive type with its own pipeline.
// Backdrop primitives are tint-only — there is no outline. A specialized edge effect
// (e.g. liquid-glass-style refraction outlines) would be implemented as a dedicated
// primitive type with its own pipeline.
//
// Two modes, structurally distinct:
//
@@ -30,11 +29,11 @@
// (gl_FragCoord.xy * inv_downsample_factor) * inv_working_size.
// No kernel is applied here — the blur is already complete.
//
// Splitting V-blur out of the composite pass (an earlier version combined them) was needed
// to avoid a horizontal-vs-vertical asymmetry artifact: when the V-blur sampled the H-blur
// output through the bilinear-upsample/SDF-mask/tint pipeline in one shader invocation,
// horizontal source features ended up looking sharper than vertical ones. Running V-blur as
// its own working→working pass (matching H's structure exactly) restores symmetry.
// V-blur is run as its own working→working pass rather than folded into the composite. The
// folded variant produced a horizontal-vs-vertical asymmetry artifact: when V-blur sampled
// the H-blur output through the bilinear-upsample/SDF-mask/tint pipeline in one shader
// invocation, horizontal source features ended up looking sharper than vertical ones.
// Matching V's structure exactly to H's restores symmetry.
const uint MAX_KERNEL_PAIRS = 32;
@@ -140,16 +139,16 @@ void main() {
vec2 uv = (gl_FragCoord.xy * inv_downsample_factor) * inv_working_size;
vec3 color = texture(blur_input_tex, uv).rgb;
// Tint composition (Option B semantics): inside the masked region the panel is fully
// opaque — it completely hides the original framebuffer content, just like real frosted
// glass and like iOS UIBlurEffect / CSS backdrop-filter. f_color.rgb specifies the tint
// color; f_color.a specifies the tint *mix strength* (NOT panel opacity). At alpha=0 we
// see the pure blur; at alpha=255 we see the blur fully multiplied by the tint color.
// Tint composition: inside the masked region the panel is fully opaque — it completely
// hides the original framebuffer content, just like real frosted glass and like iOS
// UIBlurEffect / CSS backdrop-filter. f_color.rgb specifies the tint color; f_color.a
// specifies the tint *mix strength* (NOT panel opacity). At alpha=0 we see the pure
// blur; at alpha=255 we see the blur fully multiplied by the tint color.
//
// Output is premultiplied to match the ONE, ONE_MINUS_SRC_ALPHA blend state. Coverage
// (the SDF mask's edge AA) modulates only the alpha channel, never the panel-vs-source
// blend; that way edge pixels still feather correctly without re-introducing the bug
// where mid-panel pixels became semi-transparent.
// blend; that way edge pixels still feather correctly while mid-panel pixels stay fully
// opaque.
mediump vec3 tinted = mix(color, color * f_color.rgb, f_color.a);
mediump float coverage = sdf_alpha(d_n, h_n);
out_color = vec4(tinted * coverage, coverage);
+12 -11
View File
@@ -1,18 +1,19 @@
#version 450 core
// Unified backdrop blur vertex shader.
// Handles both H-blur (fullscreen triangle, mode 0) and V-blur+composite (instanced
// unit-quad over Backdrop_Primitive storage buffer, mode 1) for the second PSO of
// the backdrop bracket. The first PSO (downsample) uses backdrop_fullscreen.vert.
// Handles both the 1D separable blur passes (fullscreen triangle, mode 0; used for
// BOTH the H-pass and V-pass) and the composite pass (instanced unit-quad over
// Backdrop_Primitive storage buffer, mode 1) for the second PSO of the backdrop bracket.
// The first PSO (downsample) uses backdrop_fullscreen.vert.
//
// No vertex buffer for either mode. Mode 0 uses gl_VertexIndex 0..2 for a single
// fullscreen triangle; mode 1 uses gl_VertexIndex 0..5 for a unit-quad (two
// triangles, TRIANGLELIST topology) and gl_InstanceIndex to select the primitive.
//
// Mode 0 viewport+scissor are CPU-set per layer-bracket to the work region (union
// AABB of backdrop primitives + 3*max_sigma, clamped to swapchain bounds). Mode 1
// renders into the main render target with the screen-space orthographic projection;
// the per-primitive bounds drive the quad in screen space.
// Mode 0 viewport+scissor are CPU-set per sigma group to the work region (union AABB
// of that group's backdrop primitives + halo, clamped to swapchain bounds). Mode 1
// renders into source_texture with the screen-space orthographic projection; the
// per-primitive bounds drive the quad in screen space.
//
// Backdrop primitives have NO rotation — backdrop sampling is in screen space, so
// a rotated mask over a stationary blur sample would look wrong.
@@ -46,11 +47,11 @@ layout(set = 1, binding = 0) uniform Uniforms {
// vec2 and scalar tail packs tight to land the struct at a clean 48-byte
// stride (a multiple of 16, so the array stride needs no rounding either).
// Field semantics match the CPU-side Backdrop_Primitive declared in
// levlib/draw/pipeline_2d_backdrop.odin; keep both in sync.
// levlib/draw/backdrop.odin; keep both in sync.
//
// Backdrop primitives are tint-only in v1: outline is intentionally absent.
// Future specialized effects (e.g. liquid-glass-style edges) would be a
// dedicated primitive type with its own pipeline rather than a flag bit here.
// Backdrop primitives are tint-only: outline is intentionally absent. Specialized
// edge effects (e.g. liquid-glass-style refraction outlines) would be a dedicated
// primitive type with its own pipeline rather than a flag bit here.
struct Backdrop_Primitive {
vec4 bounds; // 0-15: min_xy, max_xy (world-space)
vec4 radii; // 16-31: per-corner radii (physical px)
+9 -12
View File
@@ -2,9 +2,9 @@
// Backdrop downsample fragment shader.
// Reads source_texture (full-resolution snapshot of pre-bracket framebuffer contents) and
// writes a downsampled copy at factor 1, 2, 4, 8, or 16. The output is the working texture
// (sized at full swapchain resolution); larger factors only fill a sub-rect of it via the
// CPU-set viewport. See backdrop.odin for the factor selection table (Flutter-style).
// writes a downsampled copy at factor 1, 2, or 4. The output is the working texture (sized
// at full swapchain resolution); larger factors only fill a sub-rect of it via the CPU-set
// viewport. See backdrop.odin for the factor selection table (Flutter-style).
//
// Shader paths by factor:
//
@@ -15,15 +15,12 @@
// factor=2: each output covers a 2×2 source block. Single bilinear tap at the shared
// corner reads all 4 source pixels with 0.25 weight.
//
// factor>=4: each output covers a (factor)×(factor) source block. We use 4 bilinear taps,
// each at the shared corner of a (factor/2)×(factor/2) sub-block. Each tap reads
// 4 source pixels uniformly; combined, the 4 taps sample 16 source pixels arranged
// uniformly across the block. This is an approximation of a true (factor)² box
// filter — exact at factor=4 (16 pixels = full coverage), undersampled at factor=8
// (16 pixels of 64) and factor=16 (16 of 256). Flutter uses a richer 13-tap COD-
// style downsample shader at high factors; we accept the simpler 4-tap pattern
// for now since the high-factor cases come with large kernels that mask any
// residual aliasing.
// factor=4: each output covers a 4×4 source block. We use 4 bilinear taps, each at the
// shared corner of a 2×2 sub-block. Each tap reads 4 source pixels uniformly;
// combined, the 4 taps sample 16 source pixels arranged uniformly across the
// block (full coverage at factor=4). The factor>=4 path is structured so the
// same shader code would extend to factor=8 (16 pixels of 64) or factor=16 (16
// of 256) if the CPU-side cap is ever raised, though the current cap is 4.
//
// The viewport+scissor are set by the CPU to limit output to the layer's work region in
// working-texture coords (work_region_phys / factor), clamped to the texture bounds.
+1 -1
View File
@@ -45,7 +45,7 @@ layout(std430, set = 0, binding = 0) readonly buffer Base_2D_Primitives {
// ---------- Entry point ----------
void main() {
if (mode == 0u) {
// ---- Mode 0: Tessellated (legacy) ----
// ---- Mode 0: Tessellated (used for text and arbitrary user geometry) ----
f_color = v_color;
f_local_or_uv = v_uv;
f_params = vec4(0.0);