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