1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
use *;
/// A GPU buffer holding vertex, index, or uniform data.
///
/// Unlike the WebGPU backend, a WebGL buffer carries its own allocation
/// size, so this wrapper remembers how many bytes were last written and
/// can answer [`GlBuffer::fits`](super::GlBuffer::fits) without asking the
/// driver. That is what lets
/// [`GlBuffer::upload`](super::GlBuffer::upload) re-issue `bufferData` on
/// a resize instead of creating a new `WebGlBuffer` object and leaving
/// the old one to the garbage collector.
/// A GPU texture holding sampled image data or render-target contents.
///
/// Carries the dimensions and mip count the driver was told about so a
/// resize re-allocates instead of stretching stale texels, and records
/// whether a mip chain exists so a caller cannot accidentally select a
/// minification filter that samples across levels the texture does not
/// have, which samples as solid black and is the single most common "my
/// texture is invisible" WebGL bug.
/// A vertex array object: the recorded bundle of buffer bindings and
/// vertex attribute formats.
///
/// Binding a VAO replaces up to a dozen `bindBuffer` /
/// `enableVertexAttribArray` / `vertexAttribPointer` calls with one,
/// which is what makes per-mesh draw state cheap enough to change on
/// every draw call.
/// A framebuffer: a color attachment texture plus an optional depth (and
/// stencil) renderbuffer.
///
/// The color attachment is always a [`GlTexture`] rather than a
/// renderbuffer, so the result can be sampled by a later pass or read
/// back without a resolve blit.
/// A linked GLSL program together with its cached uniform locations.
///
/// Uniform locations are stable for the lifetime of a linked program, but
/// resolving one costs a round trip into the GL frontend that walks the
/// program's uniform table. Caching them in a name-keyed map means a
/// per-frame `set_uniform_1f` is a hash lookup plus the upload with no
/// re-resolution, and a name the GLSL compiler optimized out caches the
/// `None` so the miss is paid exactly once rather than every frame.
/// A uniform buffer bound to a numbered binding point.
///
/// WebGL 2 has no bind groups: the equivalent of a `var<uniform>` block
/// is a `WebGlBuffer` bound to `UNIFORM_BUFFER` at a binding index the
/// shader agrees on. This holds both halves of that agreement, the buffer
/// and the binding point, so swapping programs cannot leave the two out
/// of sync.
/// The blend and color-write state of one draw.
///
/// Mirrors the alpha/color pair of [`BlendState`] exactly, so the WebGL
/// path can consume the same blend the WebGPU path builds, but carries
/// the extra `enabled` flag WebGL needs: WebGPU expresses "no blend" as
/// `blend: None` on the target, whereas WebGL expresses it as leaving
/// `BLEND` disabled, which is a different call with a different cost.
/// The depth-test state of one draw.
///
/// WebGL splits "test" from "write" into two independent calls
/// (`DEPTH_TEST` plus `depthFunc`, and `depthMask`), whereas WebGPU's
/// [`DepthStencilState`] derives both from `depth_write_enabled` plus the
/// presence of an attachment. Keeping the two axes separate here is what
/// lets a transparent overlay bind a depth buffer it must read but must
/// not write.
/// The culling and winding state of one draw.
/// Which channels the fragment stage may write, as a raw four-bit mask.
///
/// Red is `0x1`, green `0x2`, blue `0x4`, alpha `0x8`, and all four is
/// `0xf`. WebGL's `colorMask` is the only place a per-channel write mask
/// can be expressed, and keeping it a bitmask rather than four booleans
/// matches the [`ColorTargetState`] write-mask convention the WebGPU path
/// already uses.
/// A scissor rectangle.
///
/// A zero-sized box is legal and clips everything away, which is
/// meaningfully different from scissoring being off; the off case is
/// therefore expressed by the `Option` on [`GlRenderState::scissor`]
/// rather than by a sentinel rectangle.
/// The viewport rectangle, in framebuffer pixels.
///
/// Stored as signed values because GL accepts a negative origin for
/// flipped-coordinate passes even though the default is the origin.
/// The whole fixed-function state of one draw, as a single `Copy` value.
///
/// The point of bundling these six fields into one struct is that it can
/// be diffed field by field against the state currently bound, so
/// [`WebGl2Backend::apply_state`](super::WebGl2Backend::apply_state)
/// issues a GL call only for what actually changed. A renderer that
/// calls `enable`, `depthFunc`, `depthMask`, `blendFunc`,
/// `blendEquation`, `cullFace`, `frontFace`, `colorMask`, `scissor`, and
/// `viewport` unconditionally pays for a dozen redundant calls per draw,
/// each one a validation check inside the driver.
/// The complete WebGL 2 backend: a context plus the three shadows that
/// make a frame cheap.
///
/// # Performance
///
/// Three things are shadowed rather than re-queried. The fixed-function
/// state is shadowed by a [`GlRenderState`] copy, so applying a state
/// costs only the calls for fields that actually changed. The current
/// texture unit is shadowed by a `u32`, so binding a texture to the unit
/// that is already active costs no `activeTexture` at all. The current
/// program is shadowed by a `JsValue` compared with `Object.is`, so
/// rebinding the same program costs no `useProgram`. None of the three
/// can be answered by asking the driver without a synchronous
/// round trip, which is exactly why they are tracked here instead.
/// Constructor inputs for [`WebGl2Backend`].
///
/// `WebGl2Backend::init` is the only constructor, and it needs a live
/// document: it queries the canvas by selector and acquires a `webgl2`
/// context. Every field on the backend is `pub(crate)`, so without this
/// struct there is no way to build one from outside the crate and the
/// state-diffing, draw-call and resize surface is untestable. Naming the
/// inputs lets a caller that already holds a context construct a backend
/// without going through DOM lookup.
/// Constructor inputs for [`GlRenderState`].
///
/// `GlRenderState::context_defaults` is the only way to build one today, and
/// it hard-codes the state a *freshly created* GL context happens to be in.
/// Every other state — a depth-tested draw, a scissored viewport, a masked
/// render target — has to be assembled field by field to reach
/// `WebGl2Backend::apply_state`, which means it cannot be built from outside
/// the crate. Naming the inputs closes that gap.