Expand description
backdrop-blur-core — the backend-agnostic heart of backdrop-blur.
This crate owns the vocabulary of frosted glass and the seam every GPU backend
implements, and nothing else. It has no GPU dependency, is fully headless-testable,
and forbids unsafe. That makes it the one crate in the workspace that cannot break a
backend: the material/geometry types, the BlurError model, the liveness policy, and
the backdrop-blur seam trait all live here, while wgpu/glow resource types stay out.
§The shape of a blur
A caller describes a frosted surface with a BlurRequest — where the backdrop lives
and the surface goes (Regions in physical pixels), and what kind of glass it is
(BlurStrength, Tint, CornerRadius). Core resolves the algorithm-agnostic parts
(a physical blur radius via BlurRequest::physical_blur_radius; a clamped
ResolvedMask); the backend resolves the algorithm-specific parts (kernel offsets,
pipelines) and does the GPU work.
See docs/DESIGN.md (§4 is the load-bearing type design) and docs/IMPL.md for the
rationale and build sequence.
Structs§
- Blur
Request - The one backend-agnostic bundle that crosses the seam.
source_regionsays where the backdrop lives in the source texture;target_rectsays where to composite the frosted surface in the target. Both carry independent sizes and scales (DESIGN §4.1/§4.3). - Blur
Strength - Blur radius in logical points.
- Corner
Radius - Corner radius in logical points. Resolves (× the target region’s
Scale) to a physical-pixel radius, clamped so it can never overshoot the surface (the clamp lives incrate::ResolvedMask::from_target). Non-negative by construction. - Gaussian
Kernel - A resolved separable-Gaussian kernel: the standard deviation and the half-width (taps each
side of center).
tap_radius == 0is a single-tap pass-through (no blur). - GlRegion
- A physical-pixel rectangle in GL bottom-left coordinates (origin at the framebuffer’s
bottom-left corner,
yincreasing upward). The glow backend’s read coordinates, compositerect_origin, SDF, andbackdrop_uv_remapall operate in this one consistent system, so acopyTexSubImage2Dfrom the bottom-left framebuffer lines up withrect_uv.yincreasing upward and nothing is rendered upside-down (DESIGN §5). - Linear
Rgba - A straight-alpha color in linear light. RGB are linear (already gamma-decoded) and may
exceed
1.0(HDR over-bright); alpha is coverage in[0, 1](never gamma-encoded). The blur convolution runs in linear light, so a tint authored in sRGB must be decoded first — that is exactly whatSelf::from_srgb_unmultiplieddoes, so callers never hand the backend gamma-encoded tint values (DESIGN §4.2). - Opacity
- Surface-global fade coverage in
[0, 1]— how present the whole frosted surface is, distinct fromTint’s alpha (which is the film mix, blur vs tint color) and fromBlurStrength(the radius). It scales the composite’s final blend weight:1.0is the surface fully composited (the default — every existing caller and golden is unchanged),0.0leaves the destination untouched (the surface absent), and a fractional value blends the frosted result over the destination by that factor. A consumer animating a surface in/out (a modal scrim fading with its dialog) drives this per frame. - Ping
Pong Key - Keys a ping-pong scratch chain.
levelsis1for the separable Gaussian (no downsampling); the field exists so the dual-Kawase path keys its mip depth without a new type. - Region
- A physical-pixel rectangle carrying its own logical→physical
Scale. Construct with a struct literal so the two[u32; 2]fields are named at the call site (no positional origin/size swap is possible). - Resolved
Mask - What core computes for the shader: the target surface’s half-extents (physical px) and the
physical corner radius, clamped to
min(half_extent)so the rounded-rect SDF can never overshoot into a malformed shape. The per-pixel SDF is the shader’s job; this is its resolved, GPU-agnostic input — which is exactly what makes the clamp headless-testable (DESIGN §4.3, §11). - Scale
- A logical→physical scale factor (DPI) for one region. Strictly positive by construction:
a zero or negative factor would make the resolution math degenerate (every resolved radius
collapses to
0), soSelf::newfloors it at the smallest positivef32. - Tint
- The glass film painted over the blurred backdrop. The wrapped color is linear-light; its alpha is the film opacity (how much of the tint shows over the blur).
Enums§
- Blur
Error - Everything that can go wrong producing a frosted surface. Each
Displayis a complete sentence;ResourceCreation.stagelocalizes a 3 AM kiosk failure to the exact resource that died. - Blur
Stage - Which GPU resource a
BlurError::ResourceCreationrefers to — named so a failure points at one resource, not “something in prepare”. - Repaint
Policy - How often the frosted surface’s backdrop must be re-grabbed and re-blurred. The adapter —
not core — drives the host’s
request_repaintfrom this; core only names the obligation. - Target
Encoding - Whether the composite shader writes a linear target (hardware encodes, or a float target)
or must manually re-encode linear→sRGB for a gamma
Unormtarget. The shared encode vocabulary: the wgpu backend derives it from awgpu::TextureFormatallowlist, the glow backend from the live context’s sRGB-framebuffer state — but both speak this type so the composite shader’s “encode?” branch reads the same on either path.
Constants§
- KAWASE_
THRESHOLD_ PX - At/above this physical-pixel radius, dual-Kawase wins (downsampled, near-constant cost); below it the separable Gaussian is just as good and cheaper to set up (research: the dual-filter advantage needs a reasonably large radius, ≥ ~7px). The kept Gaussian path is the small-radius fallback; dual-Kawase is the production algorithm for large/animated blur.
- MAX_
GAUSSIAN_ RADIUS - Upper bound on the separable-Gaussian tap radius, so a huge
BlurStrengthcannot blow up the per-fragment loop. Tooltip/dialog blur sits far below this. - MAX_
KAWASE_ LEVELS - Max dual-Kawase mip depth, so a huge radius cannot build an unbounded pyramid (1/64 downscale).
- RETENTION_
FRAMES - How many frames a scratch chain may go untouched before it is evicted. Small: a surface that stops being frosted should not pin its memory for long, but a chain must survive a few frames of churn (a drag resizes every frame) without thrashing.
Traits§
- Backdrop
Blur - Implemented once per GPU backend (
WgpuBlurin v1;GlowBlurlater). The implementor holds the backend’s cached resources — per-(size, levels)ping-pong chains and the pipelines — across frames, so repeated frosted surfaces do not rebuild them. - Grab
Pass - The grab-pass socket, implemented in addition to
BackdropBluronly by backends that must extract a sampleable backdrop from a live framebuffer (glow; the deferred mainstream-egui path). Own-loop backends (wgpu) do not implement this — they receive an already-sampleable source — which is why it is a separate trait rather than a method every backend must stub.
Functions§
- backdrop_
uv_ remap - Map target-rect uv
[0,1]onto the blurred scratch, which holds the clipped source region. Returns(offset, scale)so thatscratch_uv = offset + target_uv * scale. It is the identity when the source region was fully in-bounds (clipped == source_region), and an inset otherwise — the composite samples through this withClampToEdge, so a source region clipped at a screen edge still registers 1:1 with the content behind the glass instead of being stretched. - evict_
decision - Given each cached chain’s key and the frame it was last used, the current frame, and the
retention window, return the keys whose age
now - last_usedhas reachedretentionand so should be evicted. Uses wrapping-aware distance, so au64frame counter that rolls over does not spuriously evict a fresh chain; a chain touched this frame (distance 0) is never evicted. - kawase_
halfpixel - The half-texel sampling offset for a dual-Kawase pass that samples a texture of
size(KWin’shalfpixelconvention:0.5 / size). - kawase_
level_ size - The size of mip level
levelfor a pyramid whose level 0 isbase(each level halves, floored at 1px so a tall/thin region never collapses to zero). - resolve_
gaussian - Resolve a physical-pixel blur radius to a Gaussian kernel.
sigma ≈ radius / 3(three sigma spans the visual radius), floored at0.5so the shader’si / sigmais always finite even attap_radius == 0; the tap radius is the rounded physical radius, clamped to the max. - resolve_
kawase_ levels - Dual-Kawase mip depth
Nfor a physical radius. Each down/up pass ~doubles the effective radius, soN ≈ log2(radius), clamped to[1, MAX_KAWASE_LEVELS]. The pyramid then hasN + 1levels (level 0 = full, leveli=base >> i). - use_
dual_ kawase - Whether
physical_radiusshould use dual-Kawase (vs the Gaussian fallback).
Type Aliases§
- Backend
Error - The boxed, typed source a backend attaches to a
BlurError. Core cannot namewgpu::Error/glowerrors, so it accepts anySend + Syncstandard error.