Files
amane-config/src/curve.wgsl
T
server2 0457830aba Performance: surfaces, the candle ring and the equalizer curve as shaders, the pill's bars as quads; tools/perf.sh
A playing pill cost the iGPU about 2.9 ms and the CPU 3.7 ms a frame, 60 frames a
second: two vello passes over the whole island window for the surface's shadow and
gradients (vello keeps no gradient between frames) and one for the bars. Now 0.4 ms
GPU and 1.2 ms CPU. The open card went from 5.2 to 2.6 ms GPU and 6.2 to 3.4 ms CPU
a frame. surface.wgsl draws every design's shadow, fill and inner gradient in one
pass, with vello's own blurred-rectangle formula
2026-10-19 12:46:27 +02:00

80 lines
2.5 KiB
WebGPU Shading Language

// the equalizer's curve: a line through the sliders' knobs, eased between neighbours,
// shivering up and down with the music, a faint wide stroke under a thin bright one.
// island.rs fills the values:
// 0: x of the first point, width the points span, steps, bands
// 1: levels given, glow width, line width, unused
// 2: glow colour, 3: line colour (plain, 0 to 1)
// 4-6: the knobs' heights, one per band
// 7-15: the live levels, low to high, 0 to 1
const PI: f32 = 3.14159265359;
fn knob(band: u32) -> f32 {
return values[4u + band / 4u][band % 4u];
}
fn loudness(along: f32) -> f32 {
let count = u32(values[1].x);
if count == 0u {
return 0.0;
}
let index = min(u32(round(along * f32(count - 1u))), count - 1u);
return values[7u + index / 4u][index % 4u];
}
// the curve's point number `step`, as island.rs placed it on the old canvas
fn point(step: u32) -> vec2<f32> {
let steps = values[0].z;
let bands = u32(values[0].w);
let along = f32(step) / steps;
let place = along * f32(bands - 1u);
let band = min(u32(floor(place)), bands - 2u);
let eased = (1.0 - cos((place - f32(band)) * PI)) / 2.0;
let shiver = loudness(along) * 9.0 * select(0.6, -1.0, step % 2u == 0u);
return vec2<f32>(values[0].x + along * values[0].y, mix(knob(band), knob(band + 1u), eased) + shiver);
}
fn segment(p: vec2<f32>, a: vec2<f32>, b: vec2<f32>) -> f32 {
let ab = b - a;
let t = clamp(dot(p - a, ab) / max(dot(ab, ab), 1e-5), 0.0, 1.0);
return length(p - a - ab * t);
}
fn stroke(d: f32, width: f32, pixel: f32) -> f32 {
return clamp((width / 2.0 - d) / pixel + 0.5, 0.0, 1.0);
}
@fragment
fn main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
let p = uv * size;
let pixel = max(fwidth(p.x), 1e-4);
let steps = u32(values[0].z);
// the segment under this pixel and its neighbours: a stroke can't reach further
let here = i32(floor((p.x - values[0].x) / max(values[0].y, 1e-5) * values[0].z));
var d = 1e9;
for (var offset = -1; offset <= 1; offset++) {
let first = clamp(here + offset, 0, i32(steps) - 1);
d = min(d, segment(p, point(u32(first)), point(u32(first) + 1u)));
}
let glow = vec4<f32>(values[2].rgb * values[2].a, values[2].a) * stroke(d, values[1].y, pixel);
let line = vec4<f32>(values[3].rgb * values[3].a, values[3].a) * stroke(d, values[1].z, pixel);
let color = line + glow * (1.0 - line.a);
if color.a <= 0.0 {
return vec4<f32>(0.0);
}
return vec4<f32>(color.rgb / color.a, color.a);
}