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"#;