Skip to main content

Crate backdrop_blur_core

Crate backdrop_blur_core 

Source
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 BlurRequestwhere 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§

BlurRequest
The one backend-agnostic bundle that crosses the seam. source_region says where the backdrop lives in the source texture; target_rect says where to composite the frosted surface in the target. Both carry independent sizes and scales (DESIGN §4.1/§4.3).
BlurStrength
Blur radius in logical points.
CornerRadius
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 in crate::ResolvedMask::from_target). Non-negative by construction.
GaussianKernel
A resolved separable-Gaussian kernel: the standard deviation and the half-width (taps each side of center). tap_radius == 0 is 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, y increasing upward). The glow backend’s read coordinates, composite rect_origin, SDF, and backdrop_uv_remap all operate in this one consistent system, so a copyTexSubImage2D from the bottom-left framebuffer lines up with rect_uv.y increasing upward and nothing is rendered upside-down (DESIGN §5).
LinearRgba
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 what Self::from_srgb_unmultiplied does, 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 from Tint’s alpha (which is the film mix, blur vs tint color) and from BlurStrength (the radius). It scales the composite’s final blend weight: 1.0 is the surface fully composited (the default — every existing caller and golden is unchanged), 0.0 leaves 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.
PingPongKey
Keys a ping-pong scratch chain. levels is 1 for 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).
ResolvedMask
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), so Self::new floors it at the smallest positive f32.
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§

BlurError
Everything that can go wrong producing a frosted surface. Each Display is a complete sentence; ResourceCreation.stage localizes a 3 AM kiosk failure to the exact resource that died.
BlurStage
Which GPU resource a BlurError::ResourceCreation refers to — named so a failure points at one resource, not “something in prepare”.
RepaintPolicy
How often the frosted surface’s backdrop must be re-grabbed and re-blurred. The adapter — not core — drives the host’s request_repaint from this; core only names the obligation.
TargetEncoding
Whether the composite shader writes a linear target (hardware encodes, or a float target) or must manually re-encode linear→sRGB for a gamma Unorm target. The shared encode vocabulary: the wgpu backend derives it from a wgpu::TextureFormat allowlist, 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 BlurStrength cannot 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§

BackdropBlur
Implemented once per GPU backend (WgpuBlur in v1; GlowBlur later). 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.
GrabPass
The grab-pass socket, implemented in addition to BackdropBlur only 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 that scratch_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 with ClampToEdge, 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_used has reached retention and so should be evicted. Uses wrapping-aware distance, so a u64 frame 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’s halfpixel convention: 0.5 / size).
kawase_level_size
The size of mip level level for a pyramid whose level 0 is base (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 at 0.5 so the shader’s i / sigma is always finite even at tap_radius == 0; the tap radius is the rounded physical radius, clamped to the max.
resolve_kawase_levels
Dual-Kawase mip depth N for a physical radius. Each down/up pass ~doubles the effective radius, so N ≈ log2(radius), clamped to [1, MAX_KAWASE_LEVELS]. The pyramid then has N + 1 levels (level 0 = full, level i = base >> i).
use_dual_kawase
Whether physical_radius should use dual-Kawase (vs the Gaussian fallback).

Type Aliases§

BackendError
The boxed, typed source a backend attaches to a BlurError. Core cannot name wgpu::Error/glow errors, so it accepts any Send + Sync standard error.