Skip to main content

runmat_plot/gpu/shaders/
line.rs

1pub const F32: &str = r#"const WORKGROUP_SIZE: u32 = {{WORKGROUP_SIZE}}u;
2
3struct VertexRaw {
4    data: array<f32, 12u>,
5};
6
7struct LineParams {
8    color: vec4<f32>,
9    count: u32,
10    line_style: u32,
11    half_width_px: f32,
12    _pad0: f32,
13    viewport_width_px: f32,
14    viewport_height_px: f32,
15    x_min: f32,
16    x_span: f32,
17    y_min: f32,
18    y_span: f32,
19    _pad1: vec2<f32>,
20};
21
22@group(0) @binding(0)
23var<storage, read> buf_x: array<f32>;
24
25@group(0) @binding(1)
26var<storage, read> buf_y: array<f32>;
27
28@group(0) @binding(2)
29var<storage, read_write> out_vertices: array<VertexRaw>;
30
31@group(0) @binding(3)
32var<uniform> params: LineParams;
33
34fn should_draw(segment: u32, style: u32) -> bool {
35    switch(style) {
36        case 0u: { return true; } // Solid
37        case 1u: { return (segment % 4u) < 2u; } // Dashed: on,on,off,off
38        case 2u: { return (segment % 4u) < 2u; } // Dotted approximated via dashed pattern
39        case 3u: {
40            let m = segment % 6u;
41            return (m < 2u) || (m == 3u); // DashDot: on,on,off,on,off,off
42        }
43        case 4u: { return false; } // None: markers only
44        default: { return true; }
45    }
46}
47
48fn write_vertex(index: u32, pos: vec2<f32>, color: vec4<f32>) {
49    var vertex: VertexRaw;
50    vertex.data[0u] = pos.x;
51    vertex.data[1u] = pos.y;
52    vertex.data[2u] = 0.0;
53    vertex.data[3u] = color.x;
54    vertex.data[4u] = color.y;
55    vertex.data[5u] = color.z;
56    vertex.data[6u] = color.w;
57    vertex.data[7u] = 0.0;
58    vertex.data[8u] = 0.0;
59    vertex.data[9u] = 1.0;
60    vertex.data[10u] = 0.0;
61    vertex.data[11u] = 0.0;
62    out_vertices[index] = vertex;
63}
64
65fn data_to_px(p: vec2<f32>) -> vec2<f32> {
66    return vec2<f32>(
67        (p.x - params.x_min) * (params.viewport_width_px / max(params.x_span, 1e-12)),
68        (p.y - params.y_min) * (params.viewport_height_px / max(params.y_span, 1e-12))
69    );
70}
71
72fn px_to_data(p: vec2<f32>) -> vec2<f32> {
73    return vec2<f32>(
74        params.x_min + p.x * (max(params.x_span, 1e-12) / params.viewport_width_px),
75        params.y_min + p.y * (max(params.y_span, 1e-12) / params.viewport_height_px)
76    );
77}
78
79@compute @workgroup_size(WORKGROUP_SIZE)
80fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
81    if (params.count < 2u) {
82        return;
83    }
84    let segments = params.count - 1u;
85    let idx = gid.x;
86    if (idx >= segments) {
87        return;
88    }
89
90    let p0_data = vec2<f32>(buf_x[idx], buf_y[idx]);
91    let p1_data = vec2<f32>(buf_x[idx + 1u], buf_y[idx + 1u]);
92    let p0 = data_to_px(p0_data);
93    let p1 = data_to_px(p1_data);
94    let delta = p1 - p0;
95    let len = length(delta);
96    let draw = should_draw(idx, params.line_style) && (len != 0.0);
97    var color = params.color;
98    if (!draw) {
99        color.w = 0.0;
100    }
101
102    if (!draw) {
103        let base = idx * 6u;
104        // Emit fully transparent degenerate geometry for skipped/degenerate segments.
105        write_vertex(base + 0u, p0, color);
106        write_vertex(base + 1u, p0, color);
107        write_vertex(base + 2u, p0, color);
108        write_vertex(base + 3u, p0, color);
109        write_vertex(base + 4u, p0, color);
110        write_vertex(base + 5u, p0, color);
111        return;
112    }
113
114    var half_width = params.half_width_px;
115    if (half_width < 0.0001) {
116        half_width = 0.0001;
117    }
118    let dir = normalize(delta);
119    let normal = vec2<f32>(-dir.y, dir.x);
120    let offset = normal * half_width;
121    let v0 = p0 + offset;
122    let v1 = p1 + offset;
123    let v2 = p1 - offset;
124    let v3 = p0 - offset;
125
126    let base = idx * 6u;
127    write_vertex(base + 0u, px_to_data(v0), params.color);
128    write_vertex(base + 1u, px_to_data(v1), params.color);
129    write_vertex(base + 2u, px_to_data(v2), params.color);
130    write_vertex(base + 3u, px_to_data(v0), params.color);
131    write_vertex(base + 4u, px_to_data(v2), params.color);
132    write_vertex(base + 5u, px_to_data(v3), params.color);
133}
134"#;
135
136pub const F64: &str = r#"const WORKGROUP_SIZE: u32 = {{WORKGROUP_SIZE}}u;
137
138struct VertexRaw {
139    data: array<f32, 12u>,
140};
141
142struct LineParams {
143    color: vec4<f32>,
144    count: u32,
145    line_style: u32,
146    half_width_px: f32,
147    _pad0: f32,
148    viewport_width_px: f32,
149    viewport_height_px: f32,
150    x_min: f32,
151    x_span: f32,
152    y_min: f32,
153    y_span: f32,
154    _pad1: vec2<f32>,
155};
156
157@group(0) @binding(0)
158var<storage, read> buf_x: array<f64>;
159
160@group(0) @binding(1)
161var<storage, read> buf_y: array<f64>;
162
163@group(0) @binding(2)
164var<storage, read_write> out_vertices: array<VertexRaw>;
165
166@group(0) @binding(3)
167var<uniform> params: LineParams;
168
169fn should_draw(segment: u32, style: u32) -> bool {
170    switch(style) {
171        case 0u: { return true; }
172        case 1u: { return (segment % 4u) < 2u; }
173        case 2u: { return (segment % 4u) < 2u; }
174        case 3u: {
175            let m = segment % 6u;
176            return (m < 2u) || (m == 3u);
177        }
178        case 4u: { return false; } // None: markers only
179        default: { return true; }
180    }
181}
182
183fn write_vertex(index: u32, pos: vec2<f32>, color: vec4<f32>) {
184    var vertex: VertexRaw;
185    vertex.data[0u] = pos.x;
186    vertex.data[1u] = pos.y;
187    vertex.data[2u] = 0.0;
188    vertex.data[3u] = color.x;
189    vertex.data[4u] = color.y;
190    vertex.data[5u] = color.z;
191    vertex.data[6u] = color.w;
192    vertex.data[7u] = 0.0;
193    vertex.data[8u] = 0.0;
194    vertex.data[9u] = 1.0;
195    vertex.data[10u] = 0.0;
196    vertex.data[11u] = 0.0;
197    out_vertices[index] = vertex;
198}
199
200fn data_to_px(p: vec2<f32>) -> vec2<f32> {
201    return vec2<f32>(
202        (p.x - params.x_min) * (params.viewport_width_px / max(params.x_span, 1e-12)),
203        (p.y - params.y_min) * (params.viewport_height_px / max(params.y_span, 1e-12))
204    );
205}
206
207fn px_to_data(p: vec2<f32>) -> vec2<f32> {
208    return vec2<f32>(
209        params.x_min + p.x * (max(params.x_span, 1e-12) / params.viewport_width_px),
210        params.y_min + p.y * (max(params.y_span, 1e-12) / params.viewport_height_px)
211    );
212}
213
214@compute @workgroup_size(WORKGROUP_SIZE)
215fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
216    if (params.count < 2u) {
217        return;
218    }
219    let segments = params.count - 1u;
220    let idx = gid.x;
221    if (idx >= segments) {
222        return;
223    }
224
225    let p0_data = vec2<f32>(f32(buf_x[idx]), f32(buf_y[idx]));
226    let p1_data = vec2<f32>(f32(buf_x[idx + 1u]), f32(buf_y[idx + 1u]));
227    let p0 = data_to_px(p0_data);
228    let p1 = data_to_px(p1_data);
229    let delta = p1 - p0;
230    let len = length(delta);
231    let draw = should_draw(idx, params.line_style) && (len != 0.0);
232    var color = params.color;
233    if (!draw) {
234        color.w = 0.0;
235    }
236
237    if (!draw) {
238        let base = idx * 6u;
239        write_vertex(base + 0u, p0, color);
240        write_vertex(base + 1u, p0, color);
241        write_vertex(base + 2u, p0, color);
242        write_vertex(base + 3u, p0, color);
243        write_vertex(base + 4u, p0, color);
244        write_vertex(base + 5u, p0, color);
245        return;
246    }
247
248    var half_width = params.half_width_px;
249    if (half_width < 0.0001) {
250        half_width = 0.0001;
251    }
252    let dir = normalize(delta);
253    let normal = vec2<f32>(-dir.y, dir.x);
254    let offset = normal * half_width;
255    let v0 = p0 + offset;
256    let v1 = p1 + offset;
257    let v2 = p1 - offset;
258    let v3 = p0 - offset;
259
260    let base = idx * 6u;
261    write_vertex(base + 0u, px_to_data(v0), params.color);
262    write_vertex(base + 1u, px_to_data(v1), params.color);
263    write_vertex(base + 2u, px_to_data(v2), params.color);
264    write_vertex(base + 3u, px_to_data(v0), params.color);
265    write_vertex(base + 4u, px_to_data(v2), params.color);
266    write_vertex(base + 5u, px_to_data(v3), params.color);
267}
268"#;
269
270pub const MARKER_F32: &str = r#"const WORKGROUP_SIZE: u32 = {{WORKGROUP_SIZE}}u;
271
272struct VertexRaw {
273    data: array<f32, 12u>,
274};
275
276struct MarkerParams {
277    color: vec4<f32>,
278    count: u32,
279    size: f32,
280    _pad: vec2<u32>,
281};
282
283@group(0) @binding(0)
284var<storage, read> buf_x: array<f32>;
285
286@group(0) @binding(1)
287var<storage, read> buf_y: array<f32>;
288
289@group(0) @binding(2)
290var<storage, read_write> out_vertices: array<VertexRaw>;
291
292@group(0) @binding(3)
293var<uniform> params: MarkerParams;
294
295@compute @workgroup_size(WORKGROUP_SIZE)
296fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
297    let idx = gid.x;
298    if (idx >= params.count) {
299        return;
300    }
301
302    let px = buf_x[idx];
303    let py = buf_y[idx];
304
305    let base = idx * 6u;
306    let corners = array<vec2<f32>, 6u>(
307        vec2<f32>(-1.0, -1.0),
308        vec2<f32>( 1.0, -1.0),
309        vec2<f32>( 1.0,  1.0),
310        vec2<f32>(-1.0, -1.0),
311        vec2<f32>( 1.0,  1.0),
312        vec2<f32>(-1.0,  1.0)
313    );
314    for (var i: u32 = 0u; i < 6u; i = i + 1u) {
315    var vertex: VertexRaw;
316    vertex.data[0u] = px;
317    vertex.data[1u] = py;
318    vertex.data[2u] = 0.0;
319    vertex.data[3u] = params.color.x;
320    vertex.data[4u] = params.color.y;
321    vertex.data[5u] = params.color.z;
322    vertex.data[6u] = params.color.w;
323    vertex.data[7u] = 0.0;
324    vertex.data[8u] = 0.0;
325    vertex.data[9u] = params.size;
326        vertex.data[10u] = corners[i].x;
327        vertex.data[11u] = corners[i].y;
328        out_vertices[base + i] = vertex;
329    }
330}
331"#;
332
333pub const MARKER_F64: &str = r#"const WORKGROUP_SIZE: u32 = {{WORKGROUP_SIZE}}u;
334
335struct VertexRaw {
336    data: array<f32, 12u>,
337};
338
339struct MarkerParams {
340    color: vec4<f32>,
341    count: u32,
342    size: f32,
343    _pad: vec2<u32>,
344};
345
346@group(0) @binding(0)
347var<storage, read> buf_x: array<f64>;
348
349@group(0) @binding(1)
350var<storage, read> buf_y: array<f64>;
351
352@group(0) @binding(2)
353var<storage, read_write> out_vertices: array<VertexRaw>;
354
355@group(0) @binding(3)
356var<uniform> params: MarkerParams;
357
358@compute @workgroup_size(WORKGROUP_SIZE)
359fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
360    let idx = gid.x;
361    if (idx >= params.count) {
362        return;
363    }
364
365    let px = f32(buf_x[idx]);
366    let py = f32(buf_y[idx]);
367
368    let base = idx * 6u;
369    let corners = array<vec2<f32>, 6u>(
370        vec2<f32>(-1.0, -1.0),
371        vec2<f32>( 1.0, -1.0),
372        vec2<f32>( 1.0,  1.0),
373        vec2<f32>(-1.0, -1.0),
374        vec2<f32>( 1.0,  1.0),
375        vec2<f32>(-1.0,  1.0)
376    );
377    for (var i: u32 = 0u; i < 6u; i = i + 1u) {
378    var vertex: VertexRaw;
379    vertex.data[0u] = px;
380    vertex.data[1u] = py;
381    vertex.data[2u] = 0.0;
382    vertex.data[3u] = params.color.x;
383    vertex.data[4u] = params.color.y;
384    vertex.data[5u] = params.color.z;
385    vertex.data[6u] = params.color.w;
386    vertex.data[7u] = 0.0;
387    vertex.data[8u] = 0.0;
388    vertex.data[9u] = params.size;
389        vertex.data[10u] = corners[i].x;
390        vertex.data[11u] = corners[i].y;
391        out_vertices[base + i] = vertex;
392    }
393}
394"#;