1use pfx_post::look::shader::COLOUR_WGSL;
2
3pub const SDF_WGSL: &str = r#"
4const FLAT_RECT: u32 = 0u;
5const FLAT_CIRCLE: u32 = 1u;
6const FLAT_RING: u32 = 2u;
7const FLAT_ARC: u32 = 3u;
8const FLAT_ICON_FILL: u32 = 4u;
9const FLAT_ICON_LINE: u32 = 5u;
10const FLAT_TAU: f32 = 6.283185307179586;
11
12fn flat_sign(value: f32) -> f32 {
13 return select(-1.0, 1.0, value >= 0.0);
14}
15
16fn flat_direction(p: vec2f) -> vec2f {
17 let length_p = length(p);
18 return select(vec2f(1.0, 0.0), p / length_p, length_p > 0.0);
19}
20
21fn flat_corner(p: vec2f, radii: vec4f) -> f32 {
22 let right = p.x >= 0.0;
23 if (p.y >= 0.0) {
24 return select(radii.w, radii.z, right);
25 }
26 return select(radii.x, radii.y, right);
27}
28
29fn flat_rect(p: vec2f, half: vec2f, radii: vec4f) -> vec3f {
30 let radius = flat_corner(p, radii);
31 let s = vec2f(flat_sign(p.x), flat_sign(p.y));
32 let q = abs(p) - half + vec2f(radius);
33 if (q.x > 0.0 && q.y > 0.0) {
34 let l = length(q);
35 return vec3f(l - radius, s * q / l);
36 }
37 if (q.x > q.y) {
38 return vec3f(q.x - radius, s.x, 0.0);
39 }
40 return vec3f(q.y - radius, 0.0, s.y);
41}
42
43fn flat_circle(p: vec2f, radius: f32) -> vec3f {
44 return vec3f(length(p) - radius, flat_direction(p));
45}
46
47fn flat_ring(p: vec2f, radius: f32, width: f32) -> vec3f {
48 let offset = length(p) - radius;
49 return vec3f(abs(offset) - width * 0.5, flat_sign(offset) * flat_direction(p));
50}
51
52fn flat_turn(p: vec2f, angle: f32) -> vec2f {
53 let c = cos(angle);
54 let s = sin(angle);
55 return vec2f(c * p.x - s * p.y, s * p.x + c * p.y);
56}
57
58fn flat_arc(p: vec2f, radius: f32, width: f32, start: f32, sweep: f32) -> vec3f {
59 if (sweep >= FLAT_TAU) {
60 return flat_ring(p, radius, width);
61 }
62 let half = sweep * 0.5;
63 let middle = start + half;
64 let q = flat_turn(p, -middle);
65 let angle = atan2(q.y, q.x);
66 if (abs(angle) <= half) {
67 return flat_ring(p, radius, width);
68 }
69 let end = radius * vec2f(cos(half), flat_sign(q.y) * sin(half));
70 let away = q - end;
71 return vec3f(length(away) - width * 0.5, flat_turn(flat_direction(away), middle));
72}
73
74struct FlatAlong {
75 s: f32,
76 tangent: vec2f,
77}
78
79fn flat_rect_along(p: vec2f, half: vec2f, radii: vec4f) -> FlatAlong {
80 let arc = radii * (FLAT_TAU * 0.25);
81 let top = (half.x - radii.x) + (half.x - radii.y);
82 let right = (half.y - radii.y) + (half.y - radii.z);
83 let bottom = (half.x - radii.w) + (half.x - radii.z);
84 let left = (half.y - radii.x) + (half.y - radii.w);
85 let at_right = top + arc.y;
86 let at_bottom_arc = at_right + right;
87 let at_bottom = at_bottom_arc + arc.z;
88 let at_left_arc = at_bottom + bottom;
89 let at_left = at_left_arc + arc.w;
90 let at_top_arc = at_left + left;
91 let radius = flat_corner(p, radii);
92 let inner = half - vec2f(radius);
93 let q = abs(p) - inner;
94 if (q.x > 0.0 && q.y > 0.0) {
95 let s = vec2f(flat_sign(p.x), flat_sign(p.y));
96 let centre = s * inner;
97 let offset = p - centre;
98 let rho = max(length(offset), 1e-6);
99 let tangent = vec2f(-offset.y, offset.x) / rho * (radius / rho);
100 var start = top;
101 var angle = atan2(offset.y, offset.x) + FLAT_TAU * 0.25;
102 if (s.x > 0.0 && s.y > 0.0) {
103 start = at_bottom_arc;
104 angle = atan2(offset.y, offset.x);
105 } else if (s.x < 0.0 && s.y > 0.0) {
106 start = at_left_arc;
107 angle = atan2(offset.y, offset.x) - FLAT_TAU * 0.25;
108 } else if (s.x < 0.0 && s.y < 0.0) {
109 start = at_top_arc;
110 angle = atan2(-offset.y, -offset.x);
111 }
112 return FlatAlong(start + radius * clamp(angle, 0.0, FLAT_TAU * 0.25), tangent);
113 }
114 if (q.x > q.y) {
115 if (p.x > 0.0) {
116 return FlatAlong(at_right + p.y + (half.y - radii.y), vec2f(0.0, 1.0));
117 }
118 return FlatAlong(at_left + (half.y - radii.w) - p.y, vec2f(0.0, -1.0));
119 }
120 if (p.y < 0.0) {
121 return FlatAlong(p.x + (half.x - radii.x), vec2f(1.0, 0.0));
122 }
123 return FlatAlong(at_bottom + (half.x - radii.z) - p.x, vec2f(-1.0, 0.0));
124}
125
126fn flat_circle_along(p: vec2f, radius: f32) -> FlatAlong {
127 let rho = max(length(p), 1e-6);
128 var angle = atan2(p.y, p.x);
129 if (angle < 0.0) {
130 angle += FLAT_TAU;
131 }
132 return FlatAlong(radius * angle, vec2f(-p.y, p.x) / rho * (radius / rho));
133}
134
135fn flat_dash(along: FlatAlong, on: f32, off: f32, phase: f32) -> vec3f {
136 let period = on + off;
137 let u = along.s + phase - on * 0.5;
138 let centred = u - period * floor(u / period + 0.5);
139 return vec3f(abs(centred) - on * 0.5, flat_sign(centred) * along.tangent);
140}
141
142fn flat_coverage(field: vec3f, inverse: mat2x2f) -> f32 {
143 let screen = vec2f(dot(inverse[0], field.yz), dot(inverse[1], field.yz));
144 return clamp(0.5 - field.x / max(length(screen), 1e-6), 0.0, 1.0);
145}
146
147fn flat_coverage_even(distance: f32, inverse: mat2x2f) -> f32 {
148 let unit = sqrt(abs(determinant(inverse)));
149 return clamp(0.5 - distance / max(unit, 1e-6), 0.0, 1.0);
150}
151
152const FLAT_SHADOW: u32 = 6u;
153const FLAT_SHADOW_STEPS: u32 = 8u;
154const FLAT_SHADOW_REACH: f32 = 4.0;
155
156fn flat_erf(x: f32) -> f32 {
157 let t = 1.0 / (1.0 + 0.3275911 * abs(x));
158 let y = 1.0 - (((((1.0614054 * t - 1.4531521) * t) + 1.4214138) * t - 0.28449674) * t + 0.2548296) * t * exp(-x * x);
159 return select(-y, y, x >= 0.0);
160}
161
162fn flat_shadow_reach(half: vec2f, radius: f32, y: f32) -> f32 {
163 let delta = min(half.y - radius - abs(y), 0.0);
164 return half.x - radius + sqrt(max(radius * radius - delta * delta, 0.0));
165}
166
167fn flat_shadow_row(x: f32, y: f32, half: vec2f, radii: vec4f, sigma: f32) -> f32 {
168 var left = radii.w;
169 var right = radii.z;
170 if (y < 0.0) {
171 left = radii.x;
172 right = radii.y;
173 }
174 let k = 0.70710678 / sigma;
175 return 0.5 * (flat_erf((x + flat_shadow_reach(half, left, y)) * k) - flat_erf((x - flat_shadow_reach(half, right, y)) * k));
176}
177
178fn flat_rounded_shadow(p: vec2f, half: vec2f, radii: vec4f, sigma: vec2f) -> f32 {
179 let k = 0.70710678 / sigma.y;
180 let low = max(-half.y, p.y - FLAT_SHADOW_REACH * sigma.y);
181 let high = min(half.y, p.y + FLAT_SHADOW_REACH * sigma.y);
182 if (high <= low) {
183 return 0.0;
184 }
185 let first = asin(clamp(low / half.y, -1.0, 1.0));
186 let last = asin(clamp(high / half.y, -1.0, 1.0));
187 let step = (last - first) / f32(FLAT_SHADOW_STEPS);
188 var total = 0.0;
189 var edge = flat_erf((p.y - low) * k);
190 for (var index = 0u; index < FLAT_SHADOW_STEPS; index += 1u) {
191 let a = first + step * f32(index);
192 let b = a + step;
193 let next = flat_erf((p.y - half.y * sin(b)) * k);
194 let weight = 0.5 * (edge - next);
195 edge = next;
196 total += weight * flat_shadow_row(p.x, half.y * sin((a + b) * 0.5), half, radii, sigma.x);
197 }
198 return total;
199}
200
201fn flat_stripe(u: f32, width: f32) -> vec2f {
202 let c = u - floor(u + 0.5);
203 return vec2f(abs(c) - width * 0.5, flat_sign(c));
204}
205
206fn flat_pattern_field(kind: u32, params: vec4f, p: vec2f) -> vec3f {
207 let scale = params.x;
208 let width = params.z;
209 let q = flat_turn(p, -params.y) / scale;
210 var d = 0.0;
211 var g = vec2f(1.0, 0.0);
212 if (kind == 1u) {
213 let s = flat_stripe(q.x, width);
214 d = s.x;
215 g = vec2f(s.y, 0.0);
216 } else if (kind == 2u) {
217 let f = q - floor(q + vec2f(0.5));
218 d = length(f) - width * 0.5;
219 g = flat_direction(f);
220 } else if (kind == 3u) {
221 let a = flat_stripe(q.x, width);
222 let b = flat_stripe(q.y, width);
223 if (a.x <= b.x) {
224 d = a.x;
225 g = vec2f(a.y, 0.0);
226 } else {
227 d = b.x;
228 g = vec2f(0.0, b.y);
229 }
230 } else {
231 let t = q.y - floor(q.y + 0.5);
232 let s = flat_stripe(q.x + abs(t), width);
233 d = s.x * 0.70710678;
234 g = vec2f(s.y, s.y * flat_sign(t)) * 0.70710678;
235 }
236 return vec3f(d * scale, flat_turn(g, params.y));
237}
238
239fn flat_pattern_over(base: vec4f, kind: u32, params: vec4f, packed: u32, p: vec2f, inverse: mat2x2f) -> vec4f {
240 let colour = unpack4x8unorm(packed);
241 let cover = flat_coverage(flat_pattern_field(kind, params, p), inverse);
242 let paint = vec4f(colour.rgb * colour.a, colour.a) * cover;
243 return paint + base * (1.0 - paint.a);
244}
245"#;
246
247pub const PASS_WGSL: &str = r#"
248const FLAT_FILL_SHIFT: u32 = 4u;
249const FLAT_DASHED: u32 = 256u;
250
251struct FlatView {
252 size: vec4f,
253 light: vec4f,
254 environment: vec4f,
255}
256
257@group(0) @binding(0) var<uniform> flat_view: FlatView;
258@group(0) @binding(1) var flat_icons: texture_2d<f32>;
259@group(0) @binding(2) var flat_icon_sampler: sampler;
260@group(0) @binding(3) var flat_environment: texture_2d<f32>;
261@group(0) @binding(4) var flat_environment_sampler: sampler;
262@group(0) @binding(5) var flat_gradients: texture_2d<f32>;
263@group(0) @binding(6) var flat_sprites: texture_2d<f32>;
264@group(0) @binding(7) var flat_sprite_sampler: sampler;
265
266struct FlatInstance {
267 @location(0) axes: vec4f,
268 @location(1) origin: vec4f,
269 @location(2) bounds: vec4f,
270 @location(3) shape: vec4f,
271 @location(4) stroke: vec4f,
272 @location(5) fill_top: vec4f,
273 @location(6) fill_bottom: vec4f,
274 @location(7) stroke_colour: vec4f,
275 @location(8) icon: vec4f,
276 @location(9) info: vec4u,
277 @location(10) material: vec4f,
278 @location(11) frame_x: vec4f,
279 @location(12) frame_y: vec4f,
280 @location(13) frame_z: vec4f,
281 @location(14) corners: vec4f,
282}
283
284struct FlatVaryings {
285 @builtin(position) position: vec4f,
286 @location(0) local: vec2f,
287 @location(1) @interpolate(flat) inverse: vec4f,
288 @location(2) @interpolate(flat) origin: vec4f,
289 @location(3) @interpolate(flat) shape: vec4f,
290 @location(4) @interpolate(flat) stroke: vec4f,
291 @location(5) @interpolate(flat) fill_top: vec4f,
292 @location(6) @interpolate(flat) fill_bottom: vec4f,
293 @location(7) @interpolate(flat) stroke_colour: vec4f,
294 @location(8) @interpolate(flat) icon: vec4f,
295 @location(9) @interpolate(flat) info: vec4u,
296 @location(10) @interpolate(flat) material: vec4f,
297 @location(11) @interpolate(flat) frame_x: vec4f,
298 @location(12) @interpolate(flat) frame_y: vec4f,
299 @location(13) @interpolate(flat) frame_z: vec4f,
300 @location(14) @interpolate(flat) corners: vec4f,
301}
302
303fn flat_place(instance: FlatInstance, index: u32) -> FlatVaryings {
304 let corner = vec2f(f32(index & 1u), f32((index >> 1u) & 1u)) * 2.0 - 1.0;
305 let axes = mat2x2f(instance.axes.xy, instance.axes.zw);
306 let inverse = mat2x2f(vec2f(axes[1].y, -axes[0].y), vec2f(-axes[1].x, axes[0].x))
307 * (1.0 / determinant(axes));
308 let margin = 1.5 * vec2f(
309 length(vec2f(inverse[0].x, inverse[1].x)),
310 length(vec2f(inverse[0].y, inverse[1].y)),
311 );
312 let local = instance.bounds.xy + corner * (instance.bounds.zw + margin);
313 let pixel = axes * local + instance.origin.xy;
314 var out: FlatVaryings;
315 out.position = vec4f(
316 pixel.x * flat_view.size.z * 2.0 - 1.0,
317 1.0 - pixel.y * flat_view.size.w * 2.0,
318 0.0,
319 1.0,
320 );
321 out.local = local;
322 out.inverse = vec4f(inverse[0], inverse[1]);
323 out.origin = instance.origin;
324 out.shape = instance.shape;
325 out.stroke = instance.stroke;
326 out.fill_top = instance.fill_top;
327 out.fill_bottom = instance.fill_bottom;
328 out.stroke_colour = instance.stroke_colour;
329 out.icon = instance.icon;
330 out.info = instance.info;
331 out.material = instance.material;
332 out.frame_x = instance.frame_x;
333 out.frame_y = instance.frame_y;
334 out.frame_z = instance.frame_z;
335 out.corners = instance.corners;
336 return out;
337}
338
339@vertex
340fn flat_vertex(instance: FlatInstance, @builtin(vertex_index) index: u32) -> FlatVaryings {
341 return flat_place(instance, index);
342}
343
344@vertex
345fn flat_id_vertex(instance: FlatInstance, @builtin(vertex_index) index: u32) -> FlatVaryings {
346 var out = flat_place(instance, index);
347 if ((instance.info.x & 15u) == FLAT_SHADOW) {
348 out.position = vec4f(2.0, 2.0, 2.0, 1.0);
349 }
350 return out;
351}
352
353struct FlatPaint {
354 colour: vec4f,
355 footprint: f32,
356}
357
358fn flat_field(v: FlatVaryings, kind: u32, p: vec2f) -> vec3f {
359 if (kind == FLAT_RECT) {
360 return flat_rect(p, v.shape.xy, v.corners);
361 } else if (kind == FLAT_CIRCLE) {
362 return flat_circle(p, v.shape.x);
363 } else if (kind == FLAT_RING) {
364 return flat_ring(p, v.shape.x, v.shape.y);
365 } else if (kind == FLAT_ARC) {
366 return flat_arc(p, v.shape.x, v.shape.y, v.shape.z, v.shape.w);
367 }
368 return vec3f(textureSampleLevel(flat_icons, flat_icon_sampler, v.icon.xy + p * v.icon.zw, 0.0).r, 0.0, 0.0);
369}
370
371fn flat_stroke_cover(v: FlatVaryings, kind: u32, p: vec2f, field: vec3f, inverse: mat2x2f) -> f32 {
372 let icon = kind == FLAT_ICON_FILL || kind == FLAT_ICON_LINE;
373 let band = vec3f(abs(field.x) - v.stroke.x * 0.5, flat_sign(field.x) * field.yz);
374 var cover = select(flat_coverage(band, inverse), flat_coverage_even(band.x, inverse), icon);
375 if ((v.info.x & FLAT_DASHED) != 0u) {
376 var along = flat_circle_along(p, v.shape.x);
377 if (kind == FLAT_RECT) {
378 along = flat_rect_along(p, v.shape.xy, v.corners);
379 }
380 cover *= flat_coverage(flat_dash(along, v.stroke.y, v.stroke.z, v.stroke.w), inverse);
381 }
382 return cover;
383}
384
385fn flat_gradient_offset(row: i32, index: u32) -> f32 {
386 return textureLoad(flat_gradients, vec2i(8 + i32(index / 4u), row), 0)[index % 4u];
387}
388
389fn flat_gradient_stop(row: i32, index: u32) -> vec4f {
390 return textureLoad(flat_gradients, vec2i(i32(index), row), 0);
391}
392
393fn flat_gradient(v: FlatVaryings, p: vec2f) -> vec4f {
394 let row = i32(v.fill_bottom.x);
395 let count = u32(v.fill_bottom.y);
396 let mode = u32(v.fill_bottom.z);
397 let oklab = (mode & 2u) != 0u;
398 let t = look_gradient_position(f32(mode & 1u), v.fill_top, p);
399 var colour = flat_gradient_stop(row, count - 1u);
400 if (t <= flat_gradient_offset(row, 0u)) {
401 colour = flat_gradient_stop(row, 0u);
402 } else {
403 for (var i = 0u; i + 1u < count; i += 1u) {
404 let a = flat_gradient_offset(row, i);
405 let b = flat_gradient_offset(row, i + 1u);
406 if (t < b) {
407 colour = look_stop_mix(flat_gradient_stop(row, i), flat_gradient_stop(row, i + 1u), (t - a) / (b - a), oklab);
408 break;
409 }
410 }
411 }
412 return vec4f(colour.rgb * colour.a, colour.a);
413}
414
415fn flat_fill_base(v: FlatVaryings, fill_mode: u32, p: vec2f) -> vec4f {
416 if (fill_mode == 3u) {
417 return flat_gradient(v, p);
418 }
419 if (fill_mode == 4u) {
420 let texel = textureSampleLevel(flat_sprites, flat_sprite_sampler, v.icon.xy + p * v.icon.zw, 0.0) * v.fill_top;
421 return vec4f(texel.rgb * texel.a, texel.a);
422 }
423 var t = 0.0;
424 if (fill_mode == 2u) {
425 t = clamp((p.y - v.origin.z) / (v.origin.w - v.origin.z), 0.0, 1.0);
426 }
427 let colour = mix(v.fill_top, v.fill_bottom, t);
428 return vec4f(colour.rgb * colour.a, colour.a);
429}
430
431fn flat_fill_alpha(v: FlatVaryings, fill_mode: u32) -> f32 {
432 return select(max(v.fill_top.a, v.fill_bottom.a), v.fill_bottom.a, fill_mode == 3u);
433}
434
435fn flat_paint(v: FlatVaryings) -> FlatPaint {
436 let p = v.local;
437 let inverse = mat2x2f(v.inverse.xy, v.inverse.zw);
438 let kind = v.info.x & 15u;
439 if (kind == FLAT_SHADOW) {
440 let cover = flat_rounded_shadow(p - v.icon.zw, v.shape.xy, v.corners, v.icon.xy);
441 return FlatPaint(vec4f(v.fill_top.rgb * v.fill_top.a, v.fill_top.a) * cover, 0.0);
442 }
443 let fill_mode = (v.info.x >> FLAT_FILL_SHIFT) & 15u;
444 let opacity = bitcast<f32>(v.info.w);
445 let icon = kind == FLAT_ICON_FILL || kind == FLAT_ICON_LINE;
446 let field = flat_field(v, kind, p);
447 var fill = vec4f(0.0);
448 var fill_cover = 0.0;
449 if (fill_mode != 0u) {
450 fill_cover = select(flat_coverage(field, inverse), flat_coverage_even(field.x, inverse), icon);
451 var base = flat_fill_base(v, fill_mode, p);
452 let pattern = (v.info.x >> 12u) & 7u;
453 if (pattern != 0u) {
454 base = flat_pattern_over(base, pattern, v.icon, v.info.z, p, inverse);
455 }
456 fill = base * fill_cover;
457 fill = flat_bevel_over(v, kind, p, field, inverse, fill, fill_cover);
458 }
459 var stroke = vec4f(0.0);
460 var stroke_cover = 0.0;
461 if (v.stroke.x > 0.0) {
462 stroke_cover = flat_stroke_cover(v, kind, p, field, inverse);
463 stroke = vec4f(v.stroke_colour.rgb * v.stroke_colour.a, v.stroke_colour.a) * stroke_cover;
464 }
465 let colour = (stroke + fill * (1.0 - stroke.a)) * opacity;
466 let fill_seen = select(0.0, fill_cover, fill_mode != 0u && flat_fill_alpha(v, fill_mode) > 0.0);
467 let stroke_seen = select(0.0, stroke_cover, v.stroke_colour.a > 0.0);
468 let footprint = select(0.0, max(fill_seen, stroke_seen), opacity > 0.0);
469 return FlatPaint(colour, footprint);
470}
471
472@fragment
473fn flat_colour(v: FlatVaryings) -> @location(0) vec4f {
474 return flat_paint(v).colour;
475}
476
477@fragment
478fn flat_id(v: FlatVaryings) -> @location(0) u32 {
479 if (flat_paint(v).footprint < 0.5) {
480 discard;
481 }
482 return v.info.y;
483}
484
485struct FlatLayerVaryings {
486 @builtin(position) position: vec4f,
487 @location(0) @interpolate(flat) opacity: f32,
488}
489
490@vertex
491fn flat_layer_vertex(@location(0) rect: vec4f, @location(1) opacity: vec4f, @builtin(vertex_index) index: u32) -> FlatLayerVaryings {
492 let corner = vec2f(f32(index & 1u), f32((index >> 1u) & 1u));
493 let pixel = mix(rect.xy, rect.zw, corner);
494 var out: FlatLayerVaryings;
495 out.position = vec4f(pixel.x * flat_view.size.z * 2.0 - 1.0, 1.0 - pixel.y * flat_view.size.w * 2.0, 0.0, 1.0);
496 out.opacity = opacity.x;
497 return out;
498}
499
500@group(1) @binding(0) var flat_layer: texture_2d<f32>;
501
502@fragment
503fn flat_layer_clear(v: FlatLayerVaryings) -> @location(0) vec4f {
504 return vec4f(0.0);
505}
506
507@fragment
508fn flat_layer_composite(v: FlatLayerVaryings) -> @location(0) vec4f {
509 return textureLoad(flat_layer, vec2i(v.position.xy), 0) * v.opacity;
510}
511"#;
512
513pub const LIT_WGSL: &str = r#"
514const FLAT_SLAB: u32 = 512u;
515const FLAT_LIT: u32 = 1024u;
516const FLAT_PI: f32 = 3.141592653589793;
517
518fn flat_decode(c: vec3f) -> vec3f {
519 return select(pow((c + 0.055) / 1.055, vec3f(2.4)), c / 12.92, c <= vec3f(0.04045));
520}
521
522fn flat_encode(c: vec3f) -> vec3f {
523 let x = clamp(c, vec3f(0.0), vec3f(1.0));
524 return select(1.055 * pow(x, vec3f(1.0 / 2.4)) - 0.055, x * 12.92, x <= vec3f(0.0031308));
525}
526
527fn flat_footprint(p: vec2f, v: FlatVaryings, kind: u32) -> f32 {
528 if (kind == FLAT_RECT) {
529 return flat_rect(p, v.shape.xy, v.corners).x;
530 } else if (kind == FLAT_ICON_FILL || kind == FLAT_ICON_LINE) {
531 return textureSampleLevel(flat_icons, flat_icon_sampler, v.icon.xy + p * v.icon.zw, 0.0).r
532 - v.stroke.x * 0.5;
533 }
534 return length(p) - v.shape.x;
535}
536
537fn flat_footprint_field(p: vec2f, v: FlatVaryings, kind: u32, unit: f32) -> vec3f {
538 if (kind == FLAT_RECT) {
539 return flat_rect(p, v.shape.xy, v.corners);
540 } else if (kind == FLAT_CIRCLE) {
541 return flat_circle(p, v.shape.x);
542 }
543 let e = 0.5 * unit;
544 let d = flat_footprint(p, v, kind);
545 let gradient = vec2f(
546 flat_footprint(p + vec2f(e, 0.0), v, kind) - flat_footprint(p - vec2f(e, 0.0), v, kind),
547 flat_footprint(p + vec2f(0.0, e), v, kind) - flat_footprint(p - vec2f(0.0, e), v, kind),
548 );
549 return vec3f(d, flat_direction(gradient));
550}
551
552fn flat_slab(q: vec3f, v: FlatVaryings, kind: u32, thickness: f32, bevel: f32) -> f32 {
553 let w = vec2f(flat_footprint(q.xy, v, kind) + bevel, abs(q.z + thickness * 0.5) - thickness * 0.5 + bevel);
554 return min(max(w.x, w.y), 0.0) + length(max(w, vec2f(0.0))) - bevel;
555}
556
557fn flat_ggx(n: vec3f, l: vec3f, v: vec3f, roughness: f32) -> f32 {
558 let h = normalize(l + v);
559 let nl = max(dot(n, l), 0.0);
560 let nv = max(dot(n, v), 1e-4);
561 let nh = max(dot(n, h), 0.0);
562 let vh = max(dot(v, h), 0.0);
563 let a = roughness * roughness;
564 let a2 = a * a;
565 let denominator = nh * nh * (a2 - 1.0) + 1.0;
566 let d = a2 / (FLAT_PI * denominator * denominator);
567 let k = a * 0.5;
568 let g = nl / (nl * (1.0 - k) + k) * nv / (nv * (1.0 - k) + k);
569 let f = 0.04 + 0.96 * pow(1.0 - vh, 5.0);
570 return d * g * f / (4.0 * nv);
571}
572
573fn flat_reflected(direction: vec3f, roughness: f32) -> vec3f {
574 let u = 0.5 + atan2(direction.x, -direction.y) / (2.0 * FLAT_PI);
575 let w = acos(clamp(direction.z, -1.0, 1.0)) / FLAT_PI;
576 let level = roughness * max(flat_view.environment.x - 1.0, 0.0);
577 return textureSampleLevel(flat_environment, flat_environment_sampler, vec2f(u, w), level).rgb
578 * flat_view.environment.y;
579}
580
581fn flat_lit(v: FlatVaryings) -> FlatPaint {
582 let inverse = mat2x2f(v.inverse.xy, v.inverse.zw);
583 let kind = v.info.x & 15u;
584 let fill_mode = (v.info.x >> FLAT_FILL_SHIFT) & 15u;
585 let opacity = bitcast<f32>(v.info.w);
586 let icon = kind == FLAT_ICON_FILL || kind == FLAT_ICON_LINE;
587 let slab = (v.info.x & FLAT_SLAB) != 0u;
588 let unit = sqrt(abs(determinant(inverse)));
589 let local_from_world = mat3x3f(v.frame_x.xyz, v.frame_y.xyz, v.frame_z.xyz);
590 let world_from_local = transpose(local_from_world);
591 let down = -v.frame_z.xyz;
592 var hit = vec3f(v.local, 0.0);
593 var normal = vec3f(0.0, 0.0, 1.0);
594 var cover = 1.0;
595 var settled = false;
596 if (slab && down.z < -0.99999) {
597 let footprint = flat_footprint_field(v.local, v, kind, unit);
598 if (footprint.x >= 1.5 * unit) {
599 return FlatPaint(vec4f(0.0), 0.0);
600 }
601 let bevel = v.material.y;
602 let across = footprint.x + bevel;
603 if (across > 0.0) {
604 let x = min(across, bevel);
605 let height = sqrt(max(bevel * bevel - x * x, 0.0));
606 normal = normalize(vec3f(footprint.yz * max(x, 1e-6), height));
607 hit = vec3f(v.local, height - bevel);
608 }
609 cover = clamp(0.5 - footprint.x / unit, 0.0, 1.0);
610 settled = true;
611 }
612 if (slab && !settled) {
613 let thickness = v.material.x;
614 let bevel = v.material.y;
615 let reach = thickness / max(-down.z, 0.05) + 2.0 * unit;
616 let start = hit;
617 var s = 0.0;
618 var best = 1e9;
619 var closest = hit;
620 var found = false;
621 for (var step = 0u; step < 32u; step += 1u) {
622 let q = start + down * s;
623 let distance = flat_slab(q, v, kind, thickness, bevel);
624 if (distance < best) {
625 best = distance;
626 closest = q;
627 }
628 if (distance < 0.02 * unit) {
629 found = true;
630 break;
631 }
632 s += max(distance, 0.1 * unit);
633 if (s > reach) {
634 break;
635 }
636 }
637 hit = closest;
638 if (found) {
639 let probe = min(unit, thickness * 0.5);
640 let inside = flat_slab(hit + down * probe, v, kind, thickness, bevel);
641 cover = clamp(0.5 - inside / max(probe, 1e-6), 0.0, 1.0);
642 } else {
643 cover = clamp(0.5 - best / unit, 0.0, 1.0);
644 }
645 let e = 0.25 * unit;
646 normal = normalize(vec3f(
647 flat_slab(hit + vec3f(e, 0.0, 0.0), v, kind, thickness, bevel)
648 - flat_slab(hit - vec3f(e, 0.0, 0.0), v, kind, thickness, bevel),
649 flat_slab(hit + vec3f(0.0, e, 0.0), v, kind, thickness, bevel)
650 - flat_slab(hit - vec3f(0.0, e, 0.0), v, kind, thickness, bevel),
651 flat_slab(hit + vec3f(0.0, 0.0, e), v, kind, thickness, bevel)
652 - flat_slab(hit - vec3f(0.0, 0.0, e), v, kind, thickness, bevel),
653 ));
654 }
655 let p = hit.xy;
656 let field = flat_field(v, kind, p);
657 var fill = vec4f(0.0);
658 var fill_cover = 0.0;
659 if (fill_mode != 0u) {
660 fill_cover = select(select(flat_coverage(field, inverse), flat_coverage_even(field.x, inverse), icon), 1.0, slab);
661 var base = flat_fill_base(v, fill_mode, p);
662 let pattern = (v.info.x >> 12u) & 7u;
663 if (pattern != 0u) {
664 base = flat_pattern_over(base, pattern, v.icon, v.info.z, p, inverse);
665 }
666 fill = base * fill_cover;
667 }
668 var stroke = vec4f(0.0);
669 var stroke_cover = 0.0;
670 if (v.stroke.x > 0.0) {
671 stroke_cover = select(flat_stroke_cover(v, kind, p, field, inverse), 1.0, slab && kind == FLAT_ICON_LINE);
672 stroke = vec4f(v.stroke_colour.rgb * v.stroke_colour.a, v.stroke_colour.a) * stroke_cover;
673 }
674 let painted = stroke + fill * (1.0 - stroke.a);
675 let alpha = painted.a;
676 if (alpha <= 0.0) {
677 return FlatPaint(vec4f(0.0), 0.0);
678 }
679 let albedo = flat_decode(painted.rgb / alpha);
680 let world_normal = normalize(world_from_local * normal);
681 let key = flat_view.light.xyz;
682 let view = vec3f(0.0, 0.0, 1.0);
683 let lean = length(world_normal.xy);
684 var shade = 1.0;
685 if (slab) {
686 let ambient = flat_view.light.w;
687 let ratio = clamp(dot(world_normal, key) / max(key.z, 0.05), 0.0, 1.5);
688 shade = 1.0 + (1.0 - ambient) * (ratio - 1.0);
689 }
690 var linear = albedo * shade;
691 let roughness = v.frame_y.w;
692 if (lean > 0.0) {
693 linear += vec3f(lean * v.material.z * flat_ggx(world_normal, key, view, roughness) * max(dot(world_normal, key), 0.0));
694 if (v.frame_x.w > 0.0 && flat_view.environment.z > 0.0) {
695 let nv = max(dot(world_normal, view), 0.0);
696 let fresnel = 0.04 + 0.96 * pow(1.0 - nv, 5.0);
697 linear += lean * v.frame_x.w * fresnel * flat_reflected(reflect(-view, world_normal), roughness);
698 }
699 }
700 let coverage = alpha * cover * opacity;
701 var colour = painted * (cover * opacity);
702 if (any(linear != albedo)) {
703 colour += vec4f((flat_encode(linear) - flat_encode(albedo)) * coverage, 0.0);
704 }
705 let footprint = select(0.0, select(max(fill_cover, stroke_cover), cover, slab), opacity > 0.0);
706 return FlatPaint(colour, footprint);
707}
708
709fn flat_rich(v: FlatVaryings) -> FlatPaint {
710 var paint: FlatPaint;
711 if ((v.info.x & FLAT_BLUR) != 0u) {
712 paint = flat_blur(v);
713 } else if ((v.info.x & FLAT_LIT) != 0u) {
714 paint = flat_lit(v);
715 } else {
716 paint = flat_paint(v);
717 }
718 if ((v.info.x & FLAT_ADDITIVE) != 0u) {
719 paint.colour.a = 0.0;
720 }
721 return paint;
722}
723
724@fragment
725fn flat_colour_lit(v: FlatVaryings) -> @location(0) vec4f {
726 return flat_rich(v).colour;
727}
728
729@fragment
730fn flat_id_lit(v: FlatVaryings) -> @location(0) u32 {
731 if ((v.info.x & FLAT_UNPICKED) != 0u) {
732 discard;
733 }
734 var footprint = 0.0;
735 if ((v.info.x & FLAT_BLUR) != 0u) {
736 footprint = flat_paint(v).footprint;
737 } else {
738 footprint = flat_rich(v).footprint;
739 }
740 if (footprint < 0.5) {
741 discard;
742 }
743 return v.info.y;
744}
745"#;
746
747pub const FX_WGSL: &str = r#"
748const FLAT_BLUR: u32 = 2048u;
749const FLAT_ADDITIVE: u32 = 32768u;
750const FLAT_UNPICKED: u32 = 65536u;
751const FLAT_BLUR_ROWS: u32 = 4u;
752const FLAT_BLUR_STEPS: u32 = 48u;
753const FLAT_BLUR_FLOOR: f32 = 0.35;
754
755@vertex
756fn flat_id_vertex_rich(instance: FlatInstance, @builtin(vertex_index) index: u32) -> FlatVaryings {
757 var out = flat_place(instance, index);
758 if ((instance.info.x & 15u) == FLAT_SHADOW || (instance.info.x & FLAT_UNPICKED) != 0u) {
759 out.position = vec4f(2.0, 2.0, 2.0, 1.0);
760 }
761 return out;
762}
763
764fn flat_blur_distance(v: FlatVaryings, kind: u32, p: vec2f) -> f32 {
765 if (kind == FLAT_ICON_FILL || kind == FLAT_ICON_LINE) {
766 let c = clamp(p, v.shape.xy - v.shape.zw, v.shape.xy + v.shape.zw);
767 let outside = length(p - c);
768 if (outside > 0.0) {
769 return outside + 0.5 * v.stroke.x;
770 }
771 return textureSampleLevel(flat_icons, flat_icon_sampler, v.icon.xy + c * v.icon.zw, 0.0).r;
772 }
773 return flat_field(v, kind, p).x;
774}
775
776fn flat_blur_band(v: FlatVaryings, kind: u32, stroke: bool, p: vec2f) -> f32 {
777 let d = flat_blur_distance(v, kind, p);
778 if (!stroke) {
779 return d;
780 }
781 let band = abs(d) - v.stroke.x * 0.5;
782 if ((v.info.x & FLAT_DASHED) == 0u) {
783 return band;
784 }
785 var along = flat_circle_along(p, v.shape.x);
786 if (kind == FLAT_RECT) {
787 along = flat_rect_along(p, v.shape.xy, v.corners);
788 }
789 return max(band, flat_dash(along, v.stroke.y, v.stroke.z, v.stroke.w).x);
790}
791
792fn flat_blur_cdf(x: f32, span: f32) -> f32 {
793 let hi = 0.5 * (span + 1.0);
794 let lo = 0.5 * abs(span - 1.0);
795 let ramp = min(span, 1.0);
796 let height = 1.0 / max(span, 1.0);
797 if (x <= -hi) {
798 return 0.0;
799 }
800 if (x >= hi) {
801 return 1.0;
802 }
803 if (x < -lo) {
804 let u = x + hi;
805 return 0.5 * height / ramp * u * u;
806 }
807 if (x > lo) {
808 let u = hi - x;
809 return 1.0 - 0.5 * height / ramp * u * u;
810 }
811 return 0.5 * height * ramp + height * (x + lo);
812}
813
814fn flat_blur_line(v: FlatVaryings, kind: u32, stroke: bool, start: vec2f, along: vec2f, unit: f32, span: f32) -> f32 {
815 let hi = 0.5 * (span + 1.0);
816 let least = max(FLAT_BLUR_FLOOR, 2.0 * hi / f32(FLAT_BLUR_STEPS - 8u));
817 var x = -hi;
818 var s0 = flat_blur_band(v, kind, stroke, start + along * x) / unit;
819 var total = 0.0;
820 for (var step = 0u; step < FLAT_BLUR_STEPS; step += 1u) {
821 if (x >= hi) {
822 break;
823 }
824 let x1 = min(x + max(abs(s0), least), hi);
825 let s1 = flat_blur_band(v, kind, stroke, start + along * x1) / unit;
826 if (s0 <= 0.0 && s1 <= 0.0) {
827 total += flat_blur_cdf(x1, span) - flat_blur_cdf(x, span);
828 } else if (s0 <= 0.0) {
829 let r = x + (x1 - x) * s0 / (s0 - s1);
830 total += flat_blur_cdf(r, span) - flat_blur_cdf(x, span);
831 } else if (s1 <= 0.0) {
832 let r = x + (x1 - x) * s0 / (s0 - s1);
833 total += flat_blur_cdf(x1, span) - flat_blur_cdf(r, span);
834 }
835 x = x1;
836 s0 = s1;
837 }
838 if (x < hi && s0 <= 0.0) {
839 total += 1.0 - flat_blur_cdf(x, span);
840 }
841 return total;
842}
843
844fn flat_line_empty() -> vec2f {
845 return vec2f(1.0, -1.0);
846}
847
848fn flat_line_merge(a: vec2f, b: vec2f) -> vec2f {
849 if (a.x >= a.y) {
850 return b;
851 }
852 if (b.x >= b.y) {
853 return a;
854 }
855 return vec2f(min(a.x, b.x), max(a.y, b.y));
856}
857
858fn flat_line_disc(start: vec2f, along: vec2f, centre: vec2f, radius: f32) -> vec2f {
859 if (radius <= 0.0) {
860 return flat_line_empty();
861 }
862 let o = start - centre;
863 let aa = dot(along, along);
864 let b = dot(o, along);
865 let c = dot(o, o) - radius * radius;
866 let d = b * b - aa * c;
867 if (d <= 0.0) {
868 return flat_line_empty();
869 }
870 let root = sqrt(d);
871 return vec2f((-b - root) / aa, (-b + root) / aa);
872}
873
874fn flat_line_box(start: vec2f, along: vec2f, half: vec2f) -> vec2f {
875 if (half.x < 0.0 || half.y < 0.0) {
876 return flat_line_empty();
877 }
878 var low = -1e30;
879 var high = 1e30;
880 for (var axis = 0; axis < 2; axis += 1) {
881 if (abs(along[axis]) < 1e-12) {
882 if (abs(start[axis]) > half[axis]) {
883 return flat_line_empty();
884 }
885 } else {
886 let a = (-half[axis] - start[axis]) / along[axis];
887 let b = (half[axis] - start[axis]) / along[axis];
888 low = max(low, min(a, b));
889 high = min(high, max(a, b));
890 }
891 }
892 return vec2f(low, high);
893}
894
895fn flat_line_rounded(start: vec2f, along: vec2f, half: vec2f, radius: f32) -> vec2f {
896 if (half.x <= 0.0 || half.y <= 0.0) {
897 return flat_line_empty();
898 }
899 let r = clamp(radius, 0.0, min(half.x, half.y));
900 var hit = flat_line_box(start, along, vec2f(half.x, half.y - r));
901 hit = flat_line_merge(hit, flat_line_box(start, along, vec2f(half.x - r, half.y)));
902 if (r > 0.0) {
903 let c = half - vec2f(r);
904 hit = flat_line_merge(hit, flat_line_disc(start, along, c, r));
905 hit = flat_line_merge(hit, flat_line_disc(start, along, vec2f(-c.x, c.y), r));
906 hit = flat_line_merge(hit, flat_line_disc(start, along, vec2f(c.x, -c.y), r));
907 hit = flat_line_merge(hit, flat_line_disc(start, along, -c, r));
908 }
909 return hit;
910}
911
912fn flat_blur_weight(hit: vec2f, span: f32) -> f32 {
913 if (hit.x >= hit.y) {
914 return 0.0;
915 }
916 return flat_blur_cdf(hit.y, span) - flat_blur_cdf(hit.x, span);
917}
918
919fn flat_blur_closed(v: FlatVaryings, kind: u32, stroke: bool) -> bool {
920 if (kind == FLAT_RING) {
921 return !stroke;
922 }
923 if (kind != FLAT_RECT && kind != FLAT_CIRCLE) {
924 return false;
925 }
926 if (kind == FLAT_RECT && any(v.corners != vec4f(v.corners.x))) {
927 return false;
928 }
929 return !stroke || (v.info.x & FLAT_DASHED) == 0u;
930}
931
932fn flat_blur_row(v: FlatVaryings, kind: u32, stroke: bool, start: vec2f, a: vec2f, span: f32) -> f32 {
933 let w = select(0.0, v.stroke.x * 0.5, stroke);
934 var outer = flat_line_empty();
935 var inner = flat_line_empty();
936 if (kind == FLAT_RECT) {
937 outer = flat_line_rounded(start, a, v.shape.xy + vec2f(w), v.corners.x + w);
938 if (stroke) {
939 inner = flat_line_rounded(start, a, v.shape.xy - vec2f(w), max(v.corners.x - w, 0.0));
940 }
941 } else if (kind == FLAT_CIRCLE) {
942 outer = flat_line_disc(start, a, vec2f(0.0), v.shape.x + w);
943 if (stroke) {
944 inner = flat_line_disc(start, a, vec2f(0.0), v.shape.x - w);
945 }
946 } else {
947 outer = flat_line_disc(start, a, vec2f(0.0), v.shape.x + 0.5 * v.shape.y);
948 inner = flat_line_disc(start, a, vec2f(0.0), v.shape.x - 0.5 * v.shape.y);
949 }
950 return flat_blur_weight(outer, span) - flat_blur_weight(inner, span);
951}
952
953fn flat_blur_cover(v: FlatVaryings, kind: u32, stroke: bool, p: vec2f, inverse: mat2x2f) -> f32 {
954 let axes = mat2x2f(vec2f(inverse[1].y, -inverse[0].y), vec2f(-inverse[1].x, inverse[0].x))
955 * (1.0 / determinant(inverse));
956 let screen = axes * v.material.xy;
957 let span = max(length(screen), 1e-6);
958 let direction = screen / span;
959 let along = inverse * direction;
960 let across = inverse * vec2f(-direction.y, direction.x);
961 let unit = max(length(along), 1e-12);
962 let closed = flat_blur_closed(v, kind, stroke);
963 var total = 0.0;
964 for (var row = 0u; row < FLAT_BLUR_ROWS; row += 1u) {
965 let u = (f32(row) + 0.5) / f32(FLAT_BLUR_ROWS) - 0.5;
966 if (closed) {
967 total += flat_blur_row(v, kind, stroke, p + across * u, along, span);
968 } else {
969 total += flat_blur_line(v, kind, stroke, p + across * u, along, unit, span);
970 }
971 }
972 return clamp(total / f32(FLAT_BLUR_ROWS), 0.0, 1.0);
973}
974
975fn flat_blur(v: FlatVaryings) -> FlatPaint {
976 let p = v.local;
977 let inverse = mat2x2f(v.inverse.xy, v.inverse.zw);
978 let kind = v.info.x & 15u;
979 let fill_mode = (v.info.x >> FLAT_FILL_SHIFT) & 15u;
980 let opacity = bitcast<f32>(v.info.w);
981 var fill = vec4f(0.0);
982 var fill_cover = 0.0;
983 if (fill_mode != 0u) {
984 fill_cover = flat_blur_cover(v, kind, false, p, inverse);
985 var base = flat_fill_base(v, fill_mode, p);
986 let pattern = (v.info.x >> 12u) & 7u;
987 if (pattern != 0u) {
988 base = flat_pattern_over(base, pattern, v.icon, v.info.z, p, inverse);
989 }
990 fill = base * fill_cover;
991 }
992 var stroke = vec4f(0.0);
993 var stroke_cover = 0.0;
994 if (v.stroke.x > 0.0) {
995 stroke_cover = flat_blur_cover(v, kind, true, p, inverse);
996 stroke = vec4f(v.stroke_colour.rgb * v.stroke_colour.a, v.stroke_colour.a) * stroke_cover;
997 }
998 let colour = (stroke + fill * (1.0 - stroke.a)) * opacity;
999 let fill_seen = select(0.0, fill_cover, fill_mode != 0u && flat_fill_alpha(v, fill_mode) > 0.0);
1000 let stroke_seen = select(0.0, stroke_cover, v.stroke_colour.a > 0.0);
1001 let footprint = select(0.0, max(fill_seen, stroke_seen), opacity > 0.0);
1002 return FlatPaint(colour, footprint);
1003}
1004"#;
1005
1006pub const FINISH_WGSL: &str = r#"
1007@group(0) @binding(0) var flat_source: texture_2d<f32>;
1008
1009@vertex
1010fn flat_finish_vertex(@builtin(vertex_index) index: u32) -> @builtin(position) vec4f {
1011 let xy = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));
1012 return vec4f(xy[index], 0.0, 1.0);
1013}
1014
1015fn flat_finish_decode(c: vec3f) -> vec3f {
1016 return select(pow((c + 0.055) / 1.055, vec3f(2.4)), c / 12.92, c <= vec3f(0.04045));
1017}
1018
1019@fragment
1020fn flat_finish_copy(@builtin(position) position: vec4f) -> @location(0) vec4f {
1021 return textureLoad(flat_source, vec2i(position.xy), 0);
1022}
1023
1024@fragment
1025fn flat_finish_linear(@builtin(position) position: vec4f) -> @location(0) vec4f {
1026 let c = textureLoad(flat_source, vec2i(position.xy), 0);
1027 if (c.a <= 0.0) {
1028 return vec4f(0.0);
1029 }
1030 return vec4f(flat_finish_decode(c.rgb / c.a) * c.a, c.a);
1031}
1032"#;
1033
1034pub use super::style::{STYLE_CALL, STYLE_WGSL};
1035
1036pub fn flat_source() -> String {
1037 format!(
1038 "{COLOUR_WGSL}
1039{SDF_WGSL}
1040{STYLE_WGSL}
1041{PASS_WGSL}"
1042 )
1043}
1044
1045pub fn source() -> String {
1046 format!(
1047 "{COLOUR_WGSL}
1048{SDF_WGSL}
1049{STYLE_WGSL}
1050{PASS_WGSL}
1051{LIT_WGSL}
1052{FX_WGSL}"
1053 )
1054}
1055
1056pub fn unstyled_source() -> String {
1057 format!(
1058 "{COLOUR_WGSL}
1059{SDF_WGSL}
1060{}
1061{LIT_WGSL}
1062{FX_WGSL}",
1063 PASS_WGSL.replace(STYLE_CALL, "")
1064 )
1065}