GPU effects
argui-render validates effect definitions and executes their passes.
argui-effects provides optional presets. Applications register only the
definitions they use.
[dependencies]
argui-effects = { version = "0.4.0", features = ["artistic"] }let renderer = argui_render::RendererConfig::default()
.effects(argui_effects::registry()?);Using an unregistered effect is a rendering error. Registration is explicit and
local to RendererConfig; there is no global registry.
Element composition
filterprocesses an element's rendered content.backdrop_filterprocesses the scene behind the element.masksets the layer alpha mask.opacitysets group opacity.clipapplies the element's rounded geometric clip without creating an offscreen layer by itself.layerreplaces the complete layer configuration.
The renderer creates offscreen passes only when layer analysis requires them. Filters do not change layout.
Custom definition
An effect has a stable namespaced ID, typed parameter schema, and one or more fragment passes:
use argui_paint::EffectId;
use argui_render::{
EffectDamage, EffectDefinition, EffectParameter, EffectParameterType,
EffectPassDefinition, EffectRegistry,
};
const TINT: EffectId = EffectId::new("acme.color.tint");
let tint = EffectDefinition::new(
TINT,
&[EffectParameter::new("amount", EffectParameterType::F32)],
&[EffectPassDefinition::fragment("tint", r#"
fn argui_effect(
_uv: vec2<f32>,
source: vec4<f32>,
_backdrop: vec4<f32>,
) -> vec4<f32> {
let amount = argui_param_f32(0u);
let tint = argui_srgb_to_linear(vec3<f32>(1.0, 0.2, 0.1));
return mix(source, vec4<f32>(tint, source.a), amount);
}
"#)],
)
.damage(EffectDamage::Bounded);
let registry = EffectRegistry::new([tint])?;The registry rejects invalid WGSL, duplicate IDs or parameter names, empty names, and invalid pass divisors. Instances provide exact names and types:
use argui_paint::{EffectInstance, EffectValue, Filter};
let filter = Filter::Effect(EffectInstance::new(
TINT,
[("amount", EffectValue::F32(0.35))],
));Parameters support scalars, booleans, vectors, matrices, colors, and logical pixels. The adapter packs them into bounded storage; custom definitions never receive raw WGPU handles.
Custom effects are damage-safe by default: EffectDamage::Unbounded makes any
scene change choose a full effect composition. Declare Bounded only when all
passes keep their texture reads inside the layer allocation. The renderer then
gets the output rectangle from the laid-out LayerStyle, expands backdrop
dependencies by each filter's sampling radius, and repaints the complete effect
output only when those regions intersect. For a shader with a dynamic sampling
radius, attach the conservative logical-pixel distance to its instance with
EffectInstance::expansion; DPI scaling and physical clipping remain automatic.
The scroll edge-fade and edge-shadow presets are bounded: their shader samples
only the scroll viewport layer. Changes in scroll position update the effect
parameters and damage that viewport, rather than forcing a full-window effect
composition. Register the preset definitions in the application's renderer with
RendererConfig::effects(argui_effects::registry()?) when using them.
source, backdrop, sampling helpers, and color parameters use straight
alpha extended linear sRGB. The generated ABI unpremultiplies layer textures
before custom code and premultiplies the result afterward. Use
argui_srgb_to_linear for shader literals authored in sRGB.
Passes can request downsampling. EffectQuality::{Normal, Balanced, Performance, Custom} lets the application combine that request with a global quality choice.
Liquid glass
Enable the dedicated preset:
argui-effects = { version = "0.4.0", features = ["liquid-glass"] }use argui_effects::LiquidGlass;
let glass = LiquidGlass::new()
.refraction(38.0)
.edge_width(19.0)
.blur(8.0)
.saturation(2.25)
.brightness(0.05)
.highlight(0.5)
.tint([0.0, 0.0, 0.0, 0.272]);
let pane = pane.backdrop_filter(glass.filter());The preset uses two separable Gaussian blur passes followed by the lens pass. Refraction, rim, dispersion, color controls, tint, and optional radial depth are bounded and sanitized. Its definition declares bounded damage, and each filter instance carries the refraction-plus-blur sampling expansion. Logical lengths scale with DPI. No animation frame is requested while the scene and parameters stay unchanged.
Use backdrop_filter for glass over existing content and filter to process
the element itself. Apply a rounded mask when the effect must stay inside a
specific shape.
The lens shader derives from Kyant's Apache-2.0 Backdrop implementation. The
source attribution, license, and Argui modifications are recorded in
crates/argui-effects/LICENSE-android-liquid-glass and the shader header.
Profiling
With renderer profiling enabled, every pass records a timestamp named
effect.<effect-id>.<pass-name> plus duration, processed pixels, and render
object identity. This keeps custom effects visible in DevTools traces.
The Widget Gallery's Effects pages exercise presets, live parameters, scrolling content, and fallback behavior.