euv_chart/backend/webgl/struct.rs
1use super::*;
2
3/// The WebGL2 rendering backend for the chart.
4///
5/// This is the GPU fallback for browsers that do not expose WebGPU. It
6/// replays the same display list as the WebGPU backend and the Canvas2D
7/// backend, so the picture it produces is the same; only the plumbing
8/// differs.
9///
10/// # Performance
11///
12/// The display list is retained between frames, and the backend takes
13/// advantage of that in two ways.
14///
15/// The first is caching: the backend fingerprints the display list and
16/// only re-tessellates and re-uploads when the fingerprint changes. An
17/// unchanged chart therefore costs no vertex work at all — the GPU
18/// buffers are simply drawn again.
19///
20/// The second is batching: every shape in the display list ends up in
21/// one vertex buffer, in paint order, and is drawn with a handful of
22/// draw calls. A steady-state frame of an ordinary chart issues two or
23/// three draw calls, and none of them crosses the JavaScript boundary
24/// more than a handful of times, because the vertex data is uploaded in
25/// one go rather than one vertex at a time.
26#[derive(Data)]
27pub struct WebGl2ChartBackend {
28 /// The canvas the chart draws into.
29 #[get(pub(crate))]
30 #[get_mut(pub(crate))]
31 #[set(pub(crate))]
32 pub(crate) canvas: HtmlCanvasElement,
33 /// The WebGL2 rendering context every call goes through.
34 #[get(pub(crate))]
35 #[get_mut(pub(crate))]
36 #[set(pub(crate))]
37 pub(crate) context: WebGl2RenderingContext,
38 /// The linked shader program, or `None` before it is built.
39 #[get(pub(crate))]
40 #[get_mut(pub(crate))]
41 #[set(pub(crate))]
42 pub(crate) program: Option<WebGl2Program>,
43 /// The vertex array object that records the vertex layout.
44 #[get(pub(crate))]
45 #[get_mut(pub(crate))]
46 #[set(pub(crate))]
47 pub(crate) vertex_array: Option<WebGlVertexArrayObject>,
48 /// The buffer holding the vertices of the whole frame.
49 #[get(pub(crate))]
50 #[get_mut(pub(crate))]
51 #[set(pub(crate))]
52 pub(crate) vertex_buffer: Option<WebGlBuffer>,
53 /// The buffer holding the indices of the whole frame.
54 #[get(pub(crate))]
55 #[get_mut(pub(crate))]
56 #[set(pub(crate))]
57 pub(crate) index_buffer: Option<WebGlBuffer>,
58 /// The texture the glyph atlas is uploaded into.
59 #[get(pub(crate))]
60 #[get_mut(pub(crate))]
61 #[set(pub(crate))]
62 pub(crate) atlas_texture: Option<WebGlTexture>,
63 /// The texture holding the colour ramp of every gradient.
64 #[get(pub(crate))]
65 #[get_mut(pub(crate))]
66 #[set(pub(crate))]
67 pub(crate) ramp_texture: Option<WebGlTexture>,
68 /// The texture holding the geometry of every gradient.
69 #[get(pub(crate))]
70 #[get_mut(pub(crate))]
71 #[set(pub(crate))]
72 pub(crate) parameter_texture: Option<WebGlTexture>,
73 /// The images the display list refers to, in the order they appear.
74 #[get(pub(crate))]
75 #[get_mut(pub(crate))]
76 #[set(pub(crate))]
77 pub(crate) images: Vec<WebGl2Image>,
78 /// The tessellator, reused across frames so its scratch buffers are
79 /// allocated once.
80 #[get(pub(crate))]
81 #[get_mut(pub(crate))]
82 #[set(pub(crate))]
83 pub(crate) tessellator: Tessellator,
84 /// The vertices of the whole frame, interleaved.
85 #[get(pub(crate))]
86 #[get_mut(pub(crate))]
87 #[set(pub(crate))]
88 pub(crate) vertices: Vec<f32>,
89 /// The indices of the whole frame.
90 #[get(pub(crate))]
91 #[get_mut(pub(crate))]
92 #[set(pub(crate))]
93 pub(crate) indices: Vec<u32>,
94 /// The batches to draw, in paint order.
95 #[get(pub(crate))]
96 #[get_mut(pub(crate))]
97 #[set(pub(crate))]
98 pub(crate) batches: Vec<WebGl2Batch>,
99 /// The gradients the frame uses, one row each in the ramp texture.
100 #[get(pub(crate))]
101 #[get_mut(pub(crate))]
102 #[set(pub(crate))]
103 pub(crate) gradients: Vec<WebGl2Gradient>,
104 /// The fingerprint of the display list the buffers currently hold.
105 ///
106 /// The fingerprint is a hash over everything in the list that can
107 /// change what is drawn — the commands, the paths, the paints, the
108 /// text, and the positions. A chart whose data changed but whose
109 /// shape count stayed the same therefore still invalidates the
110 /// cache.
111 #[get(pub(crate), type(copy))]
112 #[get_mut(pub(crate))]
113 #[set(pub(crate))]
114 pub(crate) fingerprint: u64,
115 /// Whether the GPU buffers currently hold the display list.
116 #[get(pub(crate), type(copy))]
117 #[get_mut(pub(crate))]
118 #[set(pub(crate))]
119 pub(crate) built: bool,
120 /// The logical width of the canvas, in CSS pixels.
121 #[get(pub(crate), type(copy))]
122 #[get_mut(pub(crate))]
123 #[set(pub(crate))]
124 pub(crate) width: f64,
125 /// The logical height of the canvas, in CSS pixels.
126 #[get(pub(crate), type(copy))]
127 #[get_mut(pub(crate))]
128 #[set(pub(crate))]
129 pub(crate) height: f64,
130 /// The device pixel ratio the canvas renders at.
131 #[get(pub(crate), type(copy))]
132 #[get_mut(pub(crate))]
133 #[set(pub(crate))]
134 pub(crate) dpr: f64,
135 /// The scissor rectangle currently in effect, in CSS pixels.
136 ///
137 /// WebGL2 has no path clipping, so a clip path is approximated by
138 /// the rectangle that bounds it. The rectangle is tracked so that a
139 /// frame only issues a scissor call when the clip actually changes.
140 #[get(pub(crate))]
141 #[get_mut(pub(crate))]
142 #[set(pub(crate))]
143 pub(crate) scissor: Option<WebGl2Scissor>,
144 /// Whether the scissor rectangle has to be re-applied.
145 ///
146 /// A resize moves the framebuffer the rectangle is expressed in, so
147 /// the rectangle is re-applied after one even when it did not
148 /// change.
149 #[get(pub(crate), type(copy))]
150 #[get_mut(pub(crate))]
151 #[set(pub(crate))]
152 pub(crate) scissor_dirty: bool,
153 /// Which texture is bound to the primary texture unit.
154 ///
155 /// The atlas and the images share one unit, so the backend has to
156 /// know what is bound to avoid rebinding it for every batch. The
157 /// second component names the image, for an image binding.
158 #[get(pub(crate))]
159 #[get_mut(pub(crate))]
160 #[set(pub(crate))]
161 pub(crate) primary_texture: Option<(WebGl2BatchKind, usize)>,
162}
163
164/// The linked shader program and the uniform locations it exposes.
165///
166/// The uniform locations are resolved once, when the program is built:
167/// resolving one costs a round trip into the driver that walks the
168/// program's uniform table, and the names never change afterwards.
169#[derive(Clone, Data)]
170pub(crate) struct WebGl2Program {
171 /// The linked program.
172 #[get(pub(crate))]
173 #[get_mut(pub(crate))]
174 #[set(pub(crate))]
175 pub(crate) program: WebGlProgram,
176 /// The location of canvas size, in CSS pixels.
177 #[get(pub(crate))]
178 #[get_mut(pub(crate))]
179 #[set(pub(crate))]
180 pub(crate) resolution: Option<WebGlUniformLocation>,
181 /// The location of the sampler that reads the atlas or the image.
182 #[get(pub(crate))]
183 #[get_mut(pub(crate))]
184 #[set(pub(crate))]
185 pub(crate) atlas: Option<WebGlUniformLocation>,
186 /// The location of the sampler that reads the gradient ramps.
187 #[get(pub(crate))]
188 #[get_mut(pub(crate))]
189 #[set(pub(crate))]
190 pub(crate) ramp: Option<WebGlUniformLocation>,
191 /// The location of the sampler that reads the gradient parameters.
192 #[get(pub(crate))]
193 #[get_mut(pub(crate))]
194 #[set(pub(crate))]
195 pub(crate) parameters: Option<WebGlUniformLocation>,
196 /// The location of the number of gradient rows.
197 #[get(pub(crate))]
198 #[get_mut(pub(crate))]
199 #[set(pub(crate))]
200 pub(crate) gradient_count: Option<WebGlUniformLocation>,
201}
202
203/// The shading a run of vertices shares.
204///
205/// The kind, the colour, and the gradient row are the same for every
206/// vertex of a shape, so they are carried together instead of being
207/// passed around one by one.
208#[derive(Clone, Copy, Data, Debug)]
209pub(crate) struct WebGl2Style {
210 /// The colour of the vertices, as normalised channels.
211 #[get(pub(crate))]
212 #[get_mut(pub(crate))]
213 #[set(pub(crate))]
214 pub(crate) color: [f32; 4],
215 /// The shading kind of the vertices.
216 #[get(pub(crate), type(copy))]
217 #[get_mut(pub(crate))]
218 #[set(pub(crate))]
219 pub(crate) kind: f32,
220 /// The gradient row the vertices belong to.
221 #[get(pub(crate), type(copy))]
222 #[get_mut(pub(crate))]
223 #[set(pub(crate))]
224 pub(crate) gradient: f32,
225}
226
227/// The rectangle of a texture a quad covers.
228///
229/// The rectangle is expressed in texture coordinates: the top-left
230/// corner and the size of the area the quad reads.
231#[derive(Clone, Copy, Data, Debug)]
232pub(crate) struct WebGl2TextureRect {
233 /// The horizontal coordinate of the top-left corner.
234 #[get(pub(crate), type(copy))]
235 #[get_mut(pub(crate))]
236 #[set(pub(crate))]
237 pub(crate) u: f32,
238 /// The vertical coordinate of the top-left corner.
239 #[get(pub(crate), type(copy))]
240 #[get_mut(pub(crate))]
241 #[set(pub(crate))]
242 pub(crate) v: f32,
243 /// The width of the rectangle.
244 #[get(pub(crate), type(copy))]
245 #[get_mut(pub(crate))]
246 #[set(pub(crate))]
247 pub(crate) u_size: f32,
248 /// The height of the rectangle.
249 #[get(pub(crate), type(copy))]
250 #[get_mut(pub(crate))]
251 #[set(pub(crate))]
252 pub(crate) v_size: f32,
253}
254
255/// One contiguous run of triangles that costs a single draw call.
256///
257/// The batches are kept in paint order. A batch ends when the texture it
258/// samples changes, or when the clip rectangle changes, so an ordinary
259/// chart holds one or two of them.
260#[derive(Clone, Copy, Data)]
261pub(crate) struct WebGl2Batch {
262 /// The texture the batch samples.
263 #[get(pub(crate))]
264 #[get_mut(pub(crate))]
265 #[set(pub(crate))]
266 pub(crate) kind: WebGl2BatchKind,
267 /// The index of the batch's first vertex.
268 #[get(pub(crate), type(copy))]
269 #[get_mut(pub(crate))]
270 #[set(pub(crate))]
271 pub(crate) first_index: u32,
272 /// The number of indices in the batch.
273 #[get(pub(crate), type(copy))]
274 #[get_mut(pub(crate))]
275 #[set(pub(crate))]
276 pub(crate) index_count: u32,
277 /// The image the batch samples, for an image batch.
278 #[get(pub(crate), type(copy))]
279 #[get_mut(pub(crate))]
280 #[set(pub(crate))]
281 pub(crate) image: usize,
282 /// The scissor rectangle the batch is clipped to, in CSS pixels.
283 #[get(pub(crate))]
284 #[get_mut(pub(crate))]
285 #[set(pub(crate))]
286 pub(crate) clip: Option<WebGl2Scissor>,
287}
288
289/// A scissor rectangle, in CSS pixels, with the origin at the top left.
290///
291/// The rectangle is stored in the chart's own coordinate system; the
292/// backend converts it into the framebuffer's bottom-left origin when
293/// it hands it to WebGL.
294#[derive(Clone, Copy, Data, Debug, PartialEq)]
295pub(crate) struct WebGl2Scissor {
296 /// The left edge of the rectangle.
297 #[get(pub(crate), type(copy))]
298 #[get_mut(pub(crate))]
299 #[set(pub(crate))]
300 pub(crate) x: f64,
301 /// The top edge of the rectangle.
302 #[get(pub(crate), type(copy))]
303 #[get_mut(pub(crate))]
304 #[set(pub(crate))]
305 pub(crate) y: f64,
306 /// The width of the rectangle.
307 #[get(pub(crate), type(copy))]
308 #[get_mut(pub(crate))]
309 #[set(pub(crate))]
310 pub(crate) width: f64,
311 /// The height of the rectangle.
312 #[get(pub(crate), type(copy))]
313 #[get_mut(pub(crate))]
314 #[set(pub(crate))]
315 pub(crate) height: f64,
316}
317
318/// The GPU-side state of one image.
319///
320/// The image is uploaded into a texture of its own and drawn with the
321/// same program the glyphs use, so an image costs exactly one draw call
322/// on top of the frame.
323#[derive(Data)]
324pub(crate) struct WebGl2Image {
325 /// The image element the texture was made from.
326 #[get(pub(crate))]
327 #[get_mut(pub(crate))]
328 #[set(pub(crate))]
329 pub(crate) element: HtmlImageElement,
330 /// The texture holding the image, or `None` before the first upload.
331 #[get(pub(crate))]
332 #[get_mut(pub(crate))]
333 #[set(pub(crate))]
334 pub(crate) texture: Option<WebGlTexture>,
335}
336
337/// A small hash used to fingerprint the display list.
338///
339/// The hash is an FNV-1a variant over machine words. It is not a
340/// cryptographic hash and does not need to be: it only has to notice
341/// that the display list changed, and it has to do so without allocating
342/// or crossing into JavaScript, because it runs on every frame.
343#[derive(Data)]
344pub(crate) struct DisplayHasher {
345 /// The hash accumulated so far.
346 #[get(pub(crate), type(copy))]
347 #[get_mut(pub(crate))]
348 #[set(pub(crate))]
349 pub(crate) hash: u64,
350}