0457830aba
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
80 lines
2.5 KiB
WebGPU Shading Language
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);
|
|
}
|