Skip to main content

runmat_plot/gpu/shaders/
contour.rs

1pub const F32: &str = r#"const WORKGROUP_SIZE: u32 = {{WORKGROUP_SIZE}}u;
2
3const VERTICES_PER_INVOCATION: u32 = 4u;
4
5struct VertexRaw {
6    data: array<f32, 12u>,
7};
8
9struct ContourParams {
10    min_z: f32,
11    max_z: f32,
12    base_z: f32,
13    level_count: u32,
14    x_len: u32,
15    y_len: u32,
16    color_table_len: u32,
17    cell_count: u32,
18    level_z: u32,
19};
20
21@group(0) @binding(0)
22var<storage, read> buf_x: array<f32>;
23
24@group(0) @binding(1)
25var<storage, read> buf_y: array<f32>;
26
27@group(0) @binding(2)
28var<storage, read> buf_z: array<f32>;
29
30@group(0) @binding(3)
31var<storage, read> color_table: array<vec4<f32>>;
32
33@group(0) @binding(4)
34var<storage, read_write> out_vertices: array<VertexRaw>;
35
36@group(0) @binding(5)
37var<uniform> params: ContourParams;
38
39@group(0) @binding(6)
40var<storage, read> level_values: array<f32>;
41
42fn sample_color(t: f32) -> vec4<f32> {
43    let table_len = params.color_table_len;
44    if (table_len <= 1u) {
45        return color_table[0u];
46    }
47    let clamped = clamp(t, 0.0, 1.0);
48    let scaled = clamped * f32(table_len - 1u);
49    let lower = u32(scaled);
50    let upper = min(lower + 1u, table_len - 1u);
51    let frac = scaled - f32(lower);
52    return mix(color_table[lower], color_table[upper], frac);
53}
54
55fn encode_vertex(position: vec3<f32>, color: vec4<f32>) -> VertexRaw {
56    var vertex: VertexRaw;
57    vertex.data[0u] = position.x;
58    vertex.data[1u] = position.y;
59    vertex.data[2u] = position.z;
60    vertex.data[3u] = color.x;
61    vertex.data[4u] = color.y;
62    vertex.data[5u] = color.z;
63    vertex.data[6u] = color.w;
64    vertex.data[7u] = 0.0;
65    vertex.data[8u] = 0.0;
66    vertex.data[9u] = 1.0;
67    vertex.data[10u] = 0.0;
68    vertex.data[11u] = 0.0;
69    return vertex;
70}
71
72fn interpolate_edge(edge: u32, corners: array<vec2<f32>, 4>, values: array<f32, 4>, level: f32) -> vec2<f32> {
73    var a: vec2<f32>;
74    var b: vec2<f32>;
75    var va: f32;
76    var vb: f32;
77    switch edge {
78        case 0u: { a = corners[0u]; b = corners[1u]; va = values[0u]; vb = values[1u]; }
79        case 1u: { a = corners[1u]; b = corners[2u]; va = values[1u]; vb = values[2u]; }
80        case 2u: { a = corners[2u]; b = corners[3u]; va = values[2u]; vb = values[3u]; }
81        default: { a = corners[3u]; b = corners[0u]; va = values[3u]; vb = values[0u]; }
82    }
83    let delta = vb - va;
84    let t = if (abs(delta) <= 1e-6) { 0.5 } else { clamp((level - va) / delta, 0.0, 1.0) };
85    return mix(a, b, t);
86}
87
88fn add_ambiguous_segments(
89    case_index: u32,
90    corners: array<vec2<f32>, 4>,
91    values: array<f32, 4>,
92    level: f32,
93    io_segments: ptr<function, array<vec2<f32>, 4>>,
94    io_count: ptr<function, u32>,
95) {
96    let f00 = values[0u] - level;
97    let f10 = values[1u] - level;
98    let f11 = values[2u] - level;
99    let f01 = values[3u] - level;
100    let q = f00 * f11 - f10 * f01;
101    let use_default = q > 0.0 || (abs(q) <= 1e-6 && case_index == 5u);
102    if (use_default) {
103        add_segment(3u, 2u, corners, values, level, io_segments, io_count);
104        add_segment(0u, 1u, corners, values, level, io_segments, io_count);
105    } else {
106        add_segment(3u, 0u, corners, values, level, io_segments, io_count);
107        add_segment(1u, 2u, corners, values, level, io_segments, io_count);
108    }
109}
110
111fn write_vertex_range(base_index: u32, segment_points: array<vec2<f32>, 4>, segment_count: u32, color: vec4<f32>, level: f32) {
112    let z_value = if (params.level_z == 1u) { level } else { params.base_z };
113    for (var i: u32 = 0u; i < VERTICES_PER_INVOCATION; i = i + 1u) {
114        let idx = base_index + i;
115        let vertex = if (i < segment_count * 2u) {
116            let pt = segment_points[i];
117            encode_vertex(vec3<f32>(pt, z_value), color)
118        } else {
119            encode_vertex(vec3<f32>(0.0, 0.0, z_value), vec4<f32>(color.xyz, 0.0))
120        };
121        out_vertices[idx] = vertex;
122    }
123}
124
125fn add_segment(
126    edge_a: u32,
127    edge_b: u32,
128    corners: array<vec2<f32>, 4>,
129    values: array<f32, 4>,
130    level: f32,
131    io_segments: ptr<function, array<vec2<f32>, 4>>,
132    io_count: ptr<function, u32>,
133) {
134    if (*io_count) >= 2u {
135        return;
136    }
137    let idx = (*io_count) * 2u;
138    (*io_segments)[idx] = interpolate_edge(edge_a, corners, values, level);
139    (*io_segments)[idx + 1u] = interpolate_edge(edge_b, corners, values, level);
140    *io_count = *io_count + 1u;
141}
142
143@compute @workgroup_size(WORKGROUP_SIZE)
144fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
145    let total = params.cell_count * params.level_count;
146    let invocation = gid.x;
147    if (invocation >= total) {
148        return;
149    }
150
151    let level_idx = invocation % params.level_count;
152    let cell_idx = invocation / params.level_count;
153    let cells_x = params.x_len - 1u;
154    let row = cell_idx % cells_x;
155    let col = cell_idx / cells_x;
156
157    let base_index = row + col * params.x_len;
158    let idx00 = base_index;
159    let idx10 = idx00 + 1u;
160    let idx01 = idx00 + params.x_len;
161    let idx11 = idx01 + 1u;
162
163    let x0 = buf_x[row];
164    let x1 = buf_x[row + 1u];
165    let y0 = buf_y[col];
166    let y1 = buf_y[col + 1u];
167
168    let z00 = buf_z[idx00];
169    let z10 = buf_z[idx10];
170    let z11 = buf_z[idx11];
171    let z01 = buf_z[idx01];
172
173    let corners = array<vec2<f32>, 4>(
174        vec2<f32>(x0, y0),
175        vec2<f32>(x1, y0),
176        vec2<f32>(x1, y1),
177        vec2<f32>(x0, y1)
178    );
179    let values = array<f32, 4>(z00, z10, z11, z01);
180
181    let level = level_values[level_idx];
182
183    var case_index: u32 = 0u;
184    if (z00 > level) { case_index = case_index | 1u; }
185    if (z10 > level) { case_index = case_index | 2u; }
186    if (z11 > level) { case_index = case_index | 4u; }
187    if (z01 > level) { case_index = case_index | 8u; }
188
189    var segments: array<vec2<f32>, 4>;
190    var segment_count: u32 = 0u;
191
192    switch case_index {
193        case 0u, 15u: {}
194        case 1u, 14u: { add_segment(3u, 0u, corners, values, level, &segments, &segment_count); }
195        case 2u, 13u: { add_segment(0u, 1u, corners, values, level, &segments, &segment_count); }
196        case 3u, 12u: { add_segment(3u, 1u, corners, values, level, &segments, &segment_count); }
197        case 4u, 11u: { add_segment(1u, 2u, corners, values, level, &segments, &segment_count); }
198        case 5u, 10u: { add_ambiguous_segments(case_index, corners, values, level, &segments, &segment_count); }
199        case 6u, 9u: { add_segment(0u, 2u, corners, values, level, &segments, &segment_count); }
200        case 7u, 8u: { add_segment(3u, 2u, corners, values, level, &segments, &segment_count); }
201    }
202
203    let norm = (level - params.min_z) / max(params.max_z - params.min_z, 1e-6);
204    let color = sample_color(norm);
205    let base_vertex = invocation * VERTICES_PER_INVOCATION;
206    write_vertex_range(base_vertex, segments, segment_count, color, level);
207}
208"#;
209
210pub const F64: &str = r#"const WORKGROUP_SIZE: u32 = {{WORKGROUP_SIZE}}u;
211
212const VERTICES_PER_INVOCATION: u32 = 4u;
213
214struct VertexRaw {
215    data: array<f32, 12u>,
216};
217
218struct ContourParams {
219    min_z: f32,
220    max_z: f32,
221    base_z: f32,
222    level_count: u32,
223    x_len: u32,
224    y_len: u32,
225    color_table_len: u32,
226    cell_count: u32,
227    level_z: u32,
228};
229
230@group(0) @binding(0)
231var<storage, read> buf_x: array<f64>;
232
233@group(0) @binding(1)
234var<storage, read> buf_y: array<f64>;
235
236@group(0) @binding(2)
237var<storage, read> buf_z: array<f64>;
238
239@group(0) @binding(3)
240var<storage, read> color_table: array<vec4<f32>>;
241
242@group(0) @binding(4)
243var<storage, read_write> out_vertices: array<VertexRaw>;
244
245@group(0) @binding(5)
246var<uniform> params: ContourParams;
247
248@group(0) @binding(6)
249var<storage, read> level_values: array<f32>;
250
251fn sample_color(t: f32) -> vec4<f32> {
252    let table_len = params.color_table_len;
253    if (table_len <= 1u) {
254        return color_table[0u];
255    }
256    let clamped = clamp(t, 0.0, 1.0);
257    let scaled = clamped * f32(table_len - 1u);
258    let lower = u32(scaled);
259    let upper = min(lower + 1u, table_len - 1u);
260    let frac = scaled - f32(lower);
261    return mix(color_table[lower], color_table[upper], frac);
262}
263
264fn encode_vertex(position: vec3<f32>, color: vec4<f32>) -> VertexRaw {
265    var vertex: VertexRaw;
266    vertex.data[0u] = position.x;
267    vertex.data[1u] = position.y;
268    vertex.data[2u] = position.z;
269    vertex.data[3u] = color.x;
270    vertex.data[4u] = color.y;
271    vertex.data[5u] = color.z;
272    vertex.data[6u] = color.w;
273    vertex.data[7u] = 0.0;
274    vertex.data[8u] = 0.0;
275    vertex.data[9u] = 1.0;
276    vertex.data[10u] = 0.0;
277    vertex.data[11u] = 0.0;
278    return vertex;
279}
280
281fn interpolate_edge(edge: u32, corners: array<vec2<f32>, 4>, values: array<f32, 4>, level: f32) -> vec2<f32> {
282    var a: vec2<f32>;
283    var b: vec2<f32>;
284    var va: f32;
285    var vb: f32;
286    switch edge {
287        case 0u: { a = corners[0u]; b = corners[1u]; va = values[0u]; vb = values[1u]; }
288        case 1u: { a = corners[1u]; b = corners[2u]; va = values[1u]; vb = values[2u]; }
289        case 2u: { a = corners[2u]; b = corners[3u]; va = values[2u]; vb = values[3u]; }
290        default: { a = corners[3u]; b = corners[0u]; va = values[3u]; vb = values[0u]; }
291    }
292    let delta = vb - va;
293    let t = if (abs(delta) <= 1e-6) { 0.5 } else { clamp((level - va) / delta, 0.0, 1.0) };
294    return mix(a, b, t);
295}
296
297fn add_ambiguous_segments(
298    case_index: u32,
299    corners: array<vec2<f32>, 4>,
300    values: array<f32, 4>,
301    level: f32,
302    io_segments: ptr<function, array<vec2<f32>, 4>>,
303    io_count: ptr<function, u32>,
304) {
305    let f00 = values[0u] - level;
306    let f10 = values[1u] - level;
307    let f11 = values[2u] - level;
308    let f01 = values[3u] - level;
309    let q = f00 * f11 - f10 * f01;
310    let use_default = q > 0.0 || (abs(q) <= 1e-6 && case_index == 5u);
311    if (use_default) {
312        add_segment(3u, 2u, corners, values, level, io_segments, io_count);
313        add_segment(0u, 1u, corners, values, level, io_segments, io_count);
314    } else {
315        add_segment(3u, 0u, corners, values, level, io_segments, io_count);
316        add_segment(1u, 2u, corners, values, level, io_segments, io_count);
317    }
318}
319
320fn write_vertex_range(base_index: u32, segment_points: array<vec2<f32>, 4>, segment_count: u32, color: vec4<f32>, level: f32) {
321    let z_value = if (params.level_z == 1u) { level } else { params.base_z };
322    for (var i: u32 = 0u; i < VERTICES_PER_INVOCATION; i = i + 1u) {
323        let idx = base_index + i;
324        let vertex = if (i < segment_count * 2u) {
325            let pt = segment_points[i];
326            encode_vertex(vec3<f32>(pt, z_value), color)
327        } else {
328            encode_vertex(vec3<f32>(0.0, 0.0, z_value), vec4<f32>(color.xyz, 0.0))
329        };
330        out_vertices[idx] = vertex;
331    }
332}
333
334fn add_segment(
335    edge_a: u32,
336    edge_b: u32,
337    corners: array<vec2<f32>, 4>,
338    values: array<f32, 4>,
339    level: f32,
340    io_segments: ptr<function, array<vec2<f32>, 4>>,
341    io_count: ptr<function, u32>,
342) {
343    if (*io_count) >= 2u {
344        return;
345    }
346    let idx = (*io_count) * 2u;
347    (*io_segments)[idx] = interpolate_edge(edge_a, corners, values, level);
348    (*io_segments)[idx + 1u] = interpolate_edge(edge_b, corners, values, level);
349    *io_count = *io_count + 1u;
350}
351
352@compute @workgroup_size(WORKGROUP_SIZE)
353fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
354    let total = params.cell_count * params.level_count;
355    let invocation = gid.x;
356    if (invocation >= total) {
357        return;
358    }
359
360    let level_idx = invocation % params.level_count;
361    let cell_idx = invocation / params.level_count;
362    let cells_x = params.x_len - 1u;
363    let row = cell_idx % cells_x;
364    let col = cell_idx / cells_x;
365
366    let base_index = row + col * params.x_len;
367    let idx00 = base_index;
368    let idx10 = idx00 + 1u;
369    let idx01 = idx00 + params.x_len;
370    let idx11 = idx01 + 1u;
371
372    let x0 = f32(buf_x[row]);
373    let x1 = f32(buf_x[row + 1u]);
374    let y0 = f32(buf_y[col]);
375    let y1 = f32(buf_y[col + 1u]);
376
377    let z00 = f32(buf_z[idx00]);
378    let z10 = f32(buf_z[idx10]);
379    let z11 = f32(buf_z[idx11]);
380    let z01 = f32(buf_z[idx01]);
381
382    let corners = array<vec2<f32>, 4>(
383        vec2<f32>(x0, y0),
384        vec2<f32>(x1, y0),
385        vec2<f32>(x1, y1),
386        vec2<f32>(x0, y1)
387    );
388    let values = array<f32, 4>(z00, z10, z11, z01);
389
390    let level = level_values[level_idx];
391
392    var case_index: u32 = 0u;
393    if (z00 > level) { case_index = case_index | 1u; }
394    if (z10 > level) { case_index = case_index | 2u; }
395    if (z11 > level) { case_index = case_index | 4u; }
396    if (z01 > level) { case_index = case_index | 8u; }
397
398    var segments: array<vec2<f32>, 4>;
399    var segment_count: u32 = 0u;
400
401    switch case_index {
402        case 0u, 15u: {}
403        case 1u, 14u: { add_segment(3u, 0u, corners, values, level, &segments, &segment_count); }
404        case 2u, 13u: { add_segment(0u, 1u, corners, values, level, &segments, &segment_count); }
405        case 3u, 12u: { add_segment(3u, 1u, corners, values, level, &segments, &segment_count); }
406        case 4u, 11u: { add_segment(1u, 2u, corners, values, level, &segments, &segment_count); }
407        case 5u, 10u: { add_ambiguous_segments(case_index, corners, values, level, &segments, &segment_count); }
408        case 6u, 9u: { add_segment(0u, 2u, corners, values, level, &segments, &segment_count); }
409        case 7u, 8u: { add_segment(3u, 2u, corners, values, level, &segments, &segment_count); }
410    }
411
412    let norm = (level - params.min_z) / max(params.max_z - params.min_z, 1e-6);
413    let color = sample_color(norm);
414    let base_vertex = invocation * VERTICES_PER_INVOCATION;
415    write_vertex_range(base_vertex, segments, segment_count, color, level);
416}
417"#;