@babylonjs/lite-gl 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -34
- package/apply-states.d.ts +1 -0
- package/apply-states.js +30 -0
- package/apply-states.js.map +1 -0
- package/blend.d.ts +123 -0
- package/blend.js +194 -0
- package/blend.js.map +1 -0
- package/context.d.ts +130 -0
- package/context.js +354 -0
- package/context.js.map +1 -0
- package/depth-stencil.d.ts +155 -231
- package/depth-stencil.js +399 -262
- package/depth-stencil.js.map +1 -1
- package/dynamic-texture.d.ts +59 -149
- package/dynamic-texture.js +123 -69
- package/dynamic-texture.js.map +1 -1
- package/effect-renderer.d.ts +65 -0
- package/effect-renderer.js +132 -0
- package/effect-renderer.js.map +1 -0
- package/effect.d.ts +142 -0
- package/effect.js +465 -0
- package/effect.js.map +1 -0
- package/html-texture.d.ts +44 -143
- package/html-texture.js +87 -81
- package/html-texture.js.map +1 -1
- package/index.d.ts +23 -1482
- package/index.js +42 -263
- package/index.js.map +1 -1
- package/mesh.d.ts +210 -307
- package/mesh.js +436 -311
- package/mesh.js.map +1 -1
- package/package.json +5 -29
- package/render-loop.d.ts +8 -0
- package/render-loop.js +62 -0
- package/render-loop.js.map +1 -0
- package/render-target.d.ts +233 -290
- package/render-target.js +534 -335
- package/render-target.js.map +1 -1
- package/scissor.d.ts +30 -72
- package/scissor.js +47 -33
- package/scissor.js.map +1 -1
- package/shader.d.ts +22 -0
- package/shader.js +65 -0
- package/shader.js.map +1 -0
- package/sprites.d.ts +182 -263
- package/sprites.js +426 -10
- package/sprites.js.map +1 -1
- package/state.d.ts +126 -0
- package/state.js +153 -0
- package/state.js.map +1 -0
- package/texture.d.ts +142 -0
- package/texture.js +433 -0
- package/texture.js.map +1 -0
- package/effect-BxxwfB_O.js +0 -737
- package/effect-BxxwfB_O.js.map +0 -1
- package/sprites--1oyVtJ3.js +0 -437
- package/sprites--1oyVtJ3.js.map +0 -1
- package/state--j_ncWIi.js +0 -155
- package/state--j_ncWIi.js.map +0 -1
- package/texture-DaMd1gGm.js +0 -329
- package/texture-DaMd1gGm.js.map +0 -1
package/context.js
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { createGLState, resetGLState, resetGLStateCache } from "./state.js";
|
|
2
|
+
/** Acquire a WebGL2 context on the canvas and build the pure-state handle.
|
|
3
|
+
* Accepts an `HTMLCanvasElement` or an `OffscreenCanvas` (worker render paths —
|
|
4
|
+
* e.g. the Lottie player — which size the drawing buffer via `setGLEngineSize`).
|
|
5
|
+
* Throws if WebGL2 is unsupported. */
|
|
6
|
+
export function createGLEngine(canvas, options) {
|
|
7
|
+
const o = options ?? {};
|
|
8
|
+
const attrs = {
|
|
9
|
+
alpha: o.alpha ?? true,
|
|
10
|
+
premultipliedAlpha: o.premultipliedAlpha ?? true,
|
|
11
|
+
antialias: o.antialias ?? false,
|
|
12
|
+
preserveDrawingBuffer: o.preserveDrawingBuffer ?? false,
|
|
13
|
+
depth: o.depth ?? false,
|
|
14
|
+
stencil: o.stencil ?? false,
|
|
15
|
+
powerPreference: o.powerPreference ?? "default",
|
|
16
|
+
failIfMajorPerformanceCaveat: o.failIfMajorPerformanceCaveat ?? false,
|
|
17
|
+
};
|
|
18
|
+
const gl = canvas.getContext("webgl2", attrs);
|
|
19
|
+
if (gl === null) {
|
|
20
|
+
throw new Error("lite-gl: WebGL2 is not supported on this canvas");
|
|
21
|
+
}
|
|
22
|
+
const parallelExt = gl.getExtension("KHR_parallel_shader_compile");
|
|
23
|
+
// Probe color-buffer-float support. `getExtension` both queries AND enables
|
|
24
|
+
// the extension, so this call is what makes float/half-float attachments
|
|
25
|
+
// renderable for the render-target module.
|
|
26
|
+
const colorBufferFloat = gl.getExtension("EXT_color_buffer_float") !== null;
|
|
27
|
+
const colorBufferHalfFloat = gl.getExtension("EXT_color_buffer_half_float") !== null;
|
|
28
|
+
const floatLinear = gl.getExtension("OES_texture_float_linear") !== null;
|
|
29
|
+
const caps = {
|
|
30
|
+
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
|
|
31
|
+
maxTextureUnits: gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS),
|
|
32
|
+
parallelShaderCompile: parallelExt,
|
|
33
|
+
textureFloatRender: colorBufferFloat,
|
|
34
|
+
textureFloatLinearFiltering: floatLinear,
|
|
35
|
+
// In WebGL2, EXT_color_buffer_float also makes RGBA16F renderable; the
|
|
36
|
+
// half-float-only extension is the fallback for drivers exposing just it.
|
|
37
|
+
textureHalfFloatRender: colorBufferFloat || colorBufferHalfFloat,
|
|
38
|
+
// Half-float linear filtering is core in WebGL2 (no extension needed).
|
|
39
|
+
textureHalfFloatLinearFiltering: true,
|
|
40
|
+
// NPOT textures are core in WebGL2 — mips/wrap work at any size.
|
|
41
|
+
needPOTTextures: false,
|
|
42
|
+
};
|
|
43
|
+
const engine = {
|
|
44
|
+
canvas,
|
|
45
|
+
gl,
|
|
46
|
+
caps,
|
|
47
|
+
_hsl: 1,
|
|
48
|
+
_rafId: 0,
|
|
49
|
+
_loops: [],
|
|
50
|
+
_prevNow: 0,
|
|
51
|
+
_state: createGLState(caps.maxTextureUnits),
|
|
52
|
+
_effects: [],
|
|
53
|
+
_effectCache: new Map(),
|
|
54
|
+
_textures: [],
|
|
55
|
+
_renderTargets: [],
|
|
56
|
+
_currentRenderTarget: null,
|
|
57
|
+
_buffers: [],
|
|
58
|
+
_onLost: [],
|
|
59
|
+
_onRestored: [],
|
|
60
|
+
_isLost: false,
|
|
61
|
+
_disposed: false,
|
|
62
|
+
_lostHandler: () => { },
|
|
63
|
+
_restoredHandler: () => { },
|
|
64
|
+
_wasLoopActive: false,
|
|
65
|
+
_scheduleFrame: null,
|
|
66
|
+
};
|
|
67
|
+
engine._lostHandler = (e) => handleContextLost(engine, e);
|
|
68
|
+
engine._restoredHandler = () => handleContextRestored(engine);
|
|
69
|
+
canvas.addEventListener("webglcontextlost", engine._lostHandler, false);
|
|
70
|
+
canvas.addEventListener("webglcontextrestored", engine._restoredHandler, false);
|
|
71
|
+
return engine;
|
|
72
|
+
}
|
|
73
|
+
/** Stops the render loop, removes DOM listeners, releases all known effects
|
|
74
|
+
* and textures, then marks the context disposed. The browser-owned canvas
|
|
75
|
+
* is left intact. */
|
|
76
|
+
export function disposeGLEngine(engine) {
|
|
77
|
+
if (engine._disposed) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
engine._disposed = true;
|
|
81
|
+
if (engine._rafId !== 0) {
|
|
82
|
+
cancelAnimationFrame(engine._rafId);
|
|
83
|
+
engine._rafId = 0;
|
|
84
|
+
}
|
|
85
|
+
engine._loops.length = 0;
|
|
86
|
+
engine.canvas.removeEventListener("webglcontextlost", engine._lostHandler, false);
|
|
87
|
+
engine.canvas.removeEventListener("webglcontextrestored", engine._restoredHandler, false);
|
|
88
|
+
const gl = engine.gl;
|
|
89
|
+
// Iterate snapshots — the dispose paths splice into the registries.
|
|
90
|
+
const effects = engine._effects.slice();
|
|
91
|
+
for (const eff of effects) {
|
|
92
|
+
if (!eff._disposed) {
|
|
93
|
+
eff._disposed = true;
|
|
94
|
+
eff.isReady = false;
|
|
95
|
+
gl.deleteProgram(eff.program);
|
|
96
|
+
gl.deleteShader(eff._vs);
|
|
97
|
+
gl.deleteShader(eff._fs);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
engine._effects.length = 0;
|
|
101
|
+
engine._effectCache.clear();
|
|
102
|
+
const textures = engine._textures.slice();
|
|
103
|
+
for (const tex of textures) {
|
|
104
|
+
if (!tex._disposed) {
|
|
105
|
+
tex._disposed = true;
|
|
106
|
+
gl.deleteTexture(tex.handle);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
engine._textures.length = 0;
|
|
110
|
+
// Render targets own their FBO + renderbuffer + color texture; free them via
|
|
111
|
+
// the per-RT closure so context.ts needs no runtime import of render-target.
|
|
112
|
+
const rts = engine._renderTargets.slice();
|
|
113
|
+
for (const rt of rts) {
|
|
114
|
+
if (!rt._disposed) {
|
|
115
|
+
rt._disposed = true;
|
|
116
|
+
rt._deleteGpu(gl);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
engine._renderTargets.length = 0;
|
|
120
|
+
// Mesh vertex/index buffers own a single GL buffer each; free via closure.
|
|
121
|
+
const buffers = engine._buffers.slice();
|
|
122
|
+
for (const buf of buffers) {
|
|
123
|
+
if (!buf._disposed) {
|
|
124
|
+
buf._disposed = true;
|
|
125
|
+
buf._deleteGpu(gl);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
engine._buffers.length = 0;
|
|
129
|
+
resetGLState(engine._state);
|
|
130
|
+
engine._currentRenderTarget = null;
|
|
131
|
+
engine._onLost.length = 0;
|
|
132
|
+
engine._onRestored.length = 0;
|
|
133
|
+
}
|
|
134
|
+
/** Match drawing-buffer size to (clientSize × devicePixelRatio / _hsl). No-op
|
|
135
|
+
* if size already matches. Never touches viewport — `setViewport` owns that. */
|
|
136
|
+
export function resizeGLEngine(engine) {
|
|
137
|
+
if (engine._disposed || engine._isLost) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const canvas = engine.canvas;
|
|
141
|
+
// An OffscreenCanvas has no CSS box (no clientWidth/clientHeight); it is sized
|
|
142
|
+
// explicitly via setGLEngineSize, so CSS-derived auto-resize is a no-op for it.
|
|
143
|
+
if (!("clientWidth" in canvas)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const dpr = typeof devicePixelRatio === "number" ? devicePixelRatio : 1;
|
|
147
|
+
const w = Math.max(1, Math.floor((canvas.clientWidth * dpr) / engine._hsl));
|
|
148
|
+
const h = Math.max(1, Math.floor((canvas.clientHeight * dpr) / engine._hsl));
|
|
149
|
+
if (canvas.width !== w) {
|
|
150
|
+
canvas.width = w;
|
|
151
|
+
}
|
|
152
|
+
if (canvas.height !== h) {
|
|
153
|
+
canvas.height = h;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Set the drawing-buffer size to an EXPLICIT width/height in physical pixels
|
|
158
|
+
* (each divided by `_hsl`), independent of the canvas client/CSS size — the
|
|
159
|
+
* counterpart of Babylon's `ThinEngine.setSize`. Use when the render resolution
|
|
160
|
+
* is computed directly (e.g. animation dimensions × scale × dpr) rather than
|
|
161
|
+
* derived from CSS layout via {@link resizeGLEngine}. No-op on a disposed
|
|
162
|
+
* engine. Never touches the viewport — `setViewport` owns that.
|
|
163
|
+
*
|
|
164
|
+
* @param engine - The engine to resize.
|
|
165
|
+
* @param width - Target drawing-buffer width in physical pixels.
|
|
166
|
+
* @param height - Target drawing-buffer height in physical pixels.
|
|
167
|
+
*/
|
|
168
|
+
export function setGLEngineSize(engine, width, height) {
|
|
169
|
+
if (engine._disposed) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const canvas = engine.canvas;
|
|
173
|
+
const w = Math.max(1, Math.floor(width / engine._hsl));
|
|
174
|
+
const h = Math.max(1, Math.floor(height / engine._hsl));
|
|
175
|
+
if (canvas.width !== w) {
|
|
176
|
+
canvas.width = w;
|
|
177
|
+
}
|
|
178
|
+
if (canvas.height !== h) {
|
|
179
|
+
canvas.height = h;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Invalidate lite-gl's cached GL state so the next state-setting call in each
|
|
184
|
+
* category (program / buffer / texture / VAO / framebuffer bindings, blend,
|
|
185
|
+
* depth, stencil, scissor, color-mask, viewport, unpack) is re-issued instead
|
|
186
|
+
* of elided.
|
|
187
|
+
*
|
|
188
|
+
* Use this when a host application that SHARES this WebGL2 context mutates raw
|
|
189
|
+
* `gl.*` state outside lite-gl — e.g. a save/restore wrapper that resets the
|
|
190
|
+
* context to GL defaults around each render scope, or any interop layer that
|
|
191
|
+
* issues GL calls directly. Without it, lite-gl's redundant-call elision would
|
|
192
|
+
* skip the state changes needed to re-establish the scope, producing corrupted
|
|
193
|
+
* output. Owned GPU resources (the shared quad, render targets, meshes,
|
|
194
|
+
* effects, textures) are preserved — only the cached "current GL state" is
|
|
195
|
+
* reset. Mirrors Babylon's `Engine.wipeCaches()`.
|
|
196
|
+
*
|
|
197
|
+
* No-op while the context is lost or the engine is disposed.
|
|
198
|
+
*/
|
|
199
|
+
export function wipeGLStateCache(engine) {
|
|
200
|
+
if (engine._disposed || engine._isLost) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
resetGLStateCache(engine._state);
|
|
204
|
+
}
|
|
205
|
+
/** Drawing-buffer width in physical pixels (`canvas.width`). */
|
|
206
|
+
export function getRenderWidth(engine) {
|
|
207
|
+
return engine.canvas.width;
|
|
208
|
+
}
|
|
209
|
+
/** Drawing-buffer height in physical pixels (`canvas.height`). */
|
|
210
|
+
export function getRenderHeight(engine) {
|
|
211
|
+
return engine.canvas.height;
|
|
212
|
+
}
|
|
213
|
+
/** Current hardware-scaling factor — drawing-buffer = clientSize × dpr / level. */
|
|
214
|
+
export function getHardwareScalingLevel(engine) {
|
|
215
|
+
return engine._hsl;
|
|
216
|
+
}
|
|
217
|
+
/** Updates the hardware-scaling factor and triggers a resize. */
|
|
218
|
+
export function setHardwareScalingLevel(engine, level) {
|
|
219
|
+
if (level <= 0 || !isFinite(level)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
engine._hsl = level;
|
|
223
|
+
resizeGLEngine(engine);
|
|
224
|
+
}
|
|
225
|
+
/** The backing canvas element (an `HTMLCanvasElement`, or an `OffscreenCanvas`
|
|
226
|
+
* for worker render paths). */
|
|
227
|
+
export function getRenderingCanvas(engine) {
|
|
228
|
+
return engine.canvas;
|
|
229
|
+
}
|
|
230
|
+
/** Register a `webglcontextlost` callback. Duplicate registrations are ignored. */
|
|
231
|
+
export function onContextLost(engine, cb) {
|
|
232
|
+
if (engine._onLost.indexOf(cb) === -1) {
|
|
233
|
+
engine._onLost.push(cb);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/** Remove a previously-registered context-lost callback. */
|
|
237
|
+
export function offContextLost(engine, cb) {
|
|
238
|
+
const i = engine._onLost.indexOf(cb);
|
|
239
|
+
if (i !== -1) {
|
|
240
|
+
engine._onLost.splice(i, 1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Register a `webglcontextrestored` callback. Duplicate registrations are ignored. */
|
|
244
|
+
export function onContextRestored(engine, cb) {
|
|
245
|
+
if (engine._onRestored.indexOf(cb) === -1) {
|
|
246
|
+
engine._onRestored.push(cb);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/** Remove a previously-registered context-restored callback. */
|
|
250
|
+
export function offContextRestored(engine, cb) {
|
|
251
|
+
const i = engine._onRestored.indexOf(cb);
|
|
252
|
+
if (i !== -1) {
|
|
253
|
+
engine._onRestored.splice(i, 1);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/* ───────────────────────── internal: loss / restore ───────────────────── */
|
|
257
|
+
function handleContextLost(engine, e) {
|
|
258
|
+
// Opt-in to restore. Without preventDefault() the browser will NOT fire
|
|
259
|
+
// webglcontextrestored later.
|
|
260
|
+
e.preventDefault();
|
|
261
|
+
if (engine._isLost || engine._disposed) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
engine._isLost = true;
|
|
265
|
+
engine._wasLoopActive = engine._rafId !== 0;
|
|
266
|
+
if (engine._rafId !== 0) {
|
|
267
|
+
cancelAnimationFrame(engine._rafId);
|
|
268
|
+
engine._rafId = 0;
|
|
269
|
+
}
|
|
270
|
+
resetGLState(engine._state);
|
|
271
|
+
engine._currentRenderTarget = null;
|
|
272
|
+
for (const eff of engine._effects) {
|
|
273
|
+
eff.isReady = false;
|
|
274
|
+
eff._samplersAssigned = false;
|
|
275
|
+
eff.uniformLocations = {};
|
|
276
|
+
eff.attributeLocations = {};
|
|
277
|
+
// Clear value caches so the first frame after restore re-uploads
|
|
278
|
+
// every uniform into the freshly-linked program.
|
|
279
|
+
clearObject(eff._lastF1);
|
|
280
|
+
clearObject(eff._lastVec);
|
|
281
|
+
clearObject(eff._lastI1);
|
|
282
|
+
// Do NOT gl.deleteProgram — handle is already dead per WebGL spec.
|
|
283
|
+
}
|
|
284
|
+
for (const tex of engine._textures) {
|
|
285
|
+
tex._wasReady = tex.isReady;
|
|
286
|
+
tex.isReady = false;
|
|
287
|
+
}
|
|
288
|
+
const cbs = engine._onLost.slice();
|
|
289
|
+
for (const cb of cbs) {
|
|
290
|
+
try {
|
|
291
|
+
cb();
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
console.error("lite-gl: onLost callback threw", err);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function handleContextRestored(engine) {
|
|
299
|
+
if (engine._disposed) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
for (const eff of engine._effects) {
|
|
303
|
+
if (!eff._disposed) {
|
|
304
|
+
eff._restore(engine);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
for (const tex of engine._textures) {
|
|
308
|
+
if (!tex._disposed) {
|
|
309
|
+
const newHandle = engine.gl.createTexture();
|
|
310
|
+
if (newHandle !== null) {
|
|
311
|
+
tex.handle = newHandle;
|
|
312
|
+
tex._upload(engine);
|
|
313
|
+
tex.isReady = tex._wasReady;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Clear the lost flag BEFORE the render-target / buffer rebuilds: their
|
|
318
|
+
// `_restore` closures route through the same guarded build/upload paths used
|
|
319
|
+
// at create time (which no-op while `_isLost`), whereas the effect / texture
|
|
320
|
+
// replay above is unguarded and unaffected by the flag's value.
|
|
321
|
+
engine._isLost = false;
|
|
322
|
+
// Render targets rebuild their own color texture + FBO + renderbuffer AFTER
|
|
323
|
+
// the standalone texture replay above (an RT's color texture is owned by the
|
|
324
|
+
// RT, not the `_textures` registry).
|
|
325
|
+
for (const rt of engine._renderTargets) {
|
|
326
|
+
if (!rt._disposed) {
|
|
327
|
+
rt._restore(engine);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Mesh buffers re-upload their retained CPU data into fresh GL buffers.
|
|
331
|
+
for (const buf of engine._buffers) {
|
|
332
|
+
if (!buf._disposed) {
|
|
333
|
+
buf._restore(engine);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (engine._wasLoopActive && engine._loops.length > 0 && engine._scheduleFrame !== null) {
|
|
337
|
+
engine._scheduleFrame(engine);
|
|
338
|
+
}
|
|
339
|
+
const cbs = engine._onRestored.slice();
|
|
340
|
+
for (const cb of cbs) {
|
|
341
|
+
try {
|
|
342
|
+
cb();
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
console.error("lite-gl: onRestored callback threw", err);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function clearObject(o) {
|
|
350
|
+
for (const key in o) {
|
|
351
|
+
delete o[key];
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
//# sourceMappingURL=context.js.map
|
package/context.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GLEffect } from \"./effect.js\";\nimport type { GLTexture } from \"./texture.js\";\nimport type { GLRenderTarget } from \"./render-target.js\";\nimport { createGLState, resetGLState, resetGLStateCache, type GLState } from \"./state.js\";\n\n/**\n * Minimal shape of a context-restorable GL buffer (vertex / index), kept local\n * to this module so the public `GLEngineContext` type carries no dependency on\n * the tree-shakeable `mesh` entry. `mesh.ts`'s `GLVertexBuffer` /\n * `GLIndexBuffer` satisfy it structurally.\n * @internal\n */\nexport interface GLManagedBuffer {\n handle: WebGLBuffer;\n /** @internal */\n _disposed: boolean;\n /** @internal */\n _deleteGpu: (gl: WebGL2RenderingContext) => void;\n /** @internal */\n _restore: (engine: GLEngineContext) => void;\n}\n\n/** Constructor options forwarded to `canvas.getContext('webgl2', …)`. */\nexport interface GLEngineOptions {\n /** Default: true. */\n alpha?: boolean;\n /** Default: true. */\n premultipliedAlpha?: boolean;\n /** Default: false. */\n antialias?: boolean;\n /** Default: false. */\n preserveDrawingBuffer?: boolean;\n /** Default: false — disabled for fullscreen-quad workloads. */\n depth?: boolean;\n /** Default: false. */\n stencil?: boolean;\n /** Default: \"default\". */\n powerPreference?: WebGLPowerPreference;\n /** Default: false. */\n failIfMajorPerformanceCaveat?: boolean;\n}\n\n/** Read-only WebGL2 capability limits, queried once at context creation. */\nexport interface GLEngineCaps {\n /** `gl.MAX_TEXTURE_SIZE` — largest supported texture dimension, in texels. */\n readonly maxTextureSize: number;\n /** `gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS` — number of sampler binding slots. */\n readonly maxTextureUnits: number;\n /** The `KHR_parallel_shader_compile` extension used for async link polling,\n * or null when unsupported — linking is then treated as synchronous. */\n readonly parallelShaderCompile: { COMPLETION_STATUS_KHR: number } | null;\n /** True when 32-bit float color attachments are renderable\n * (`EXT_color_buffer_float`). Mirrors Babylon's `caps.textureFloatRender`. */\n readonly textureFloatRender: boolean;\n /** True when 32-bit float textures support linear filtering\n * (`OES_texture_float_linear`). Mirrors `caps.textureFloatLinearFiltering`. */\n readonly textureFloatLinearFiltering: boolean;\n /** True when 16-bit half-float color attachments are renderable\n * (`EXT_color_buffer_float` or `EXT_color_buffer_half_float`). Mirrors\n * `caps.textureHalfFloatRender`. */\n readonly textureHalfFloatRender: boolean;\n /** Half-float linear filtering — always `true` in WebGL2 (it is core).\n * Kept as a field to mirror Babylon's `caps.textureHalfFloatLinearFiltering`. */\n readonly textureHalfFloatLinearFiltering: boolean;\n /** Whether non-power-of-two textures need POT dimensions for mips / wrap.\n * Always `false` in WebGL2 (NPOT is core). Mirrors `engine.needPOTTextures`. */\n readonly needPOTTextures: boolean;\n}\n\n/**\n * Pure-state handle for a WebGL2 canvas + its cached GL state.\n *\n * INVARIANT: consumers MUST NOT mutate GL state directly through `engine.gl`.\n * Doing so silently corrupts the cache in `_state`. The package owns every\n * GL call. (`engine.gl` is exposed only so downstream code that already has the\n * pattern of poking `engine._gl.getExtension(...)` can do that, but must NOT\n * call `bindTexture`/`useProgram`/`bindBuffer`/`viewport`/etc.)\n */\nexport interface GLEngineContext {\n /** The canvas the WebGL2 context was acquired from. An `OffscreenCanvas` is\n * supported for worker render paths (e.g. the Lottie player); it has no CSS\n * box, so it must be sized explicitly via `setGLEngineSize` rather than the\n * CSS-derived `resizeGLEngine`. */\n readonly canvas: HTMLCanvasElement | OffscreenCanvas;\n /** The raw WebGL2 context. Do NOT mutate GL state through it — see the\n * type-level invariant above; the package owns every state-changing call. */\n readonly gl: WebGL2RenderingContext;\n /** Queried capability limits for this context. */\n readonly caps: GLEngineCaps;\n /**\n * Hardware-scaling-level — drawingBufferWidth = clientWidth * dpr / _hsl.\n * @internal\n */\n _hsl: number;\n /**\n * rAF id when a render loop is active, 0 otherwise.\n * @internal\n */\n _rafId: number;\n /**\n * Per-frame callbacks. `runRenderLoop` is a no-op if `fn` is already\n * registered (matches Babylon `AbstractEngine.runRenderLoop`).\n * @internal\n */\n _loops: ((dt: number) => void)[];\n /**\n * Timestamp of last frame for delta computation.\n * @internal\n */\n _prevNow: number;\n /**\n * Cached GL state. See §4 of 00-lite-gl.md.\n * @internal\n */\n _state: GLState;\n /**\n * Live effect registry — populated by `createEffect`, removed by\n * `disposeEffect`. Used by the context-restored protocol to rebuild\n * programs.\n * @internal\n */\n _effects: GLEffect[];\n /**\n * Per-engine effect cache keyed by source descriptor (vertex+fragment\n * source, defines, attribute/uniform/sampler names). `createEffect` returns\n * (and ref-counts) the cached `GLEffect` when an identical descriptor is\n * requested, so identical shaders share ONE WebGLProgram — letting\n * `useEffect`'s current-program cache elide redundant `gl.useProgram`.\n * @internal\n */\n _effectCache: Map<string, GLEffect>;\n /**\n * Live texture registry — populated by `createRawTexture` /\n * `loadTexture2D` / `createHtmlElementTexture`. Used by the\n * context-restored protocol to replay uploads.\n * @internal\n */\n _textures: GLTexture[];\n /**\n * Live render-target registry — populated by `createRenderTarget`. Used by\n * the context-restored protocol to rebuild framebuffers + attachments after\n * their color textures have been replayed. Empty (and tree-shaken) when the\n * render-target module is unused.\n * @internal\n */\n _renderTargets: GLRenderTarget[];\n /**\n * The render target currently bound for drawing (`bindRenderTarget`), or\n * `null` for the default canvas framebuffer. Tracked so that leaving a\n * mipmapped target (`bindRenderTarget(engine, null)` or switching to\n * another target) can regenerate its mip chain — mirroring Babylon's\n * auto-mipmap on `unBindFramebuffer`. Reset to `null` on context-lost.\n * @internal\n */\n _currentRenderTarget: GLRenderTarget | null;\n /**\n * Live vertex / index buffer registry — populated by `createVertexBuffer` /\n * `createIndexBuffer`. Used by the context-restored protocol to re-upload\n * the retained CPU data into fresh GL buffers. Empty (and tree-shaken) when\n * the mesh module is unused.\n * @internal\n */\n _buffers: GLManagedBuffer[];\n /** @internal */\n _onLost: (() => void)[];\n /** @internal */\n _onRestored: (() => void)[];\n /**\n * True between `webglcontextlost` and `webglcontextrestored`. While\n * true, every `setEffect*` / `bindTexture` / `drawEffect` is a no-op.\n * @internal\n */\n _isLost: boolean;\n /**\n * True once the context has been disposed; subsequent calls become no-ops.\n * @internal\n */\n _disposed: boolean;\n /**\n * DOM handlers — retained so dispose can `removeEventListener` them.\n * @internal\n */\n _lostHandler: (e: Event) => void;\n /** @internal */\n _restoredHandler: () => void;\n /**\n * True when a render loop was active at the moment of `webglcontextlost`,\n * so we can resume it from the restored handler.\n * @internal\n */\n _wasLoopActive: boolean;\n /**\n * Installed by `runRenderLoop` on first call. Lets the context-restored\n * handler resume a loop without a circular runtime import from render-loop.\n * Null if the render-loop module is tree-shaken out.\n * @internal\n */\n _scheduleFrame: ((engine: GLEngineContext) => void) | null;\n}\n\n/** Acquire a WebGL2 context on the canvas and build the pure-state handle.\n * Accepts an `HTMLCanvasElement` or an `OffscreenCanvas` (worker render paths —\n * e.g. the Lottie player — which size the drawing buffer via `setGLEngineSize`).\n * Throws if WebGL2 is unsupported. */\nexport function createGLEngine(canvas: HTMLCanvasElement | OffscreenCanvas, options?: GLEngineOptions): GLEngineContext {\n const o = options ?? {};\n const attrs: WebGLContextAttributes = {\n alpha: o.alpha ?? true,\n premultipliedAlpha: o.premultipliedAlpha ?? true,\n antialias: o.antialias ?? false,\n preserveDrawingBuffer: o.preserveDrawingBuffer ?? false,\n depth: o.depth ?? false,\n stencil: o.stencil ?? false,\n powerPreference: o.powerPreference ?? \"default\",\n failIfMajorPerformanceCaveat: o.failIfMajorPerformanceCaveat ?? false,\n };\n const gl = canvas.getContext(\"webgl2\", attrs) as WebGL2RenderingContext | null;\n if (gl === null) {\n throw new Error(\"lite-gl: WebGL2 is not supported on this canvas\");\n }\n\n const parallelExt = gl.getExtension(\"KHR_parallel_shader_compile\") as { COMPLETION_STATUS_KHR: number } | null;\n // Probe color-buffer-float support. `getExtension` both queries AND enables\n // the extension, so this call is what makes float/half-float attachments\n // renderable for the render-target module.\n const colorBufferFloat = gl.getExtension(\"EXT_color_buffer_float\") !== null;\n const colorBufferHalfFloat = gl.getExtension(\"EXT_color_buffer_half_float\") !== null;\n const floatLinear = gl.getExtension(\"OES_texture_float_linear\") !== null;\n const caps: GLEngineCaps = {\n maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) as number,\n maxTextureUnits: gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS) as number,\n parallelShaderCompile: parallelExt,\n textureFloatRender: colorBufferFloat,\n textureFloatLinearFiltering: floatLinear,\n // In WebGL2, EXT_color_buffer_float also makes RGBA16F renderable; the\n // half-float-only extension is the fallback for drivers exposing just it.\n textureHalfFloatRender: colorBufferFloat || colorBufferHalfFloat,\n // Half-float linear filtering is core in WebGL2 (no extension needed).\n textureHalfFloatLinearFiltering: true,\n // NPOT textures are core in WebGL2 — mips/wrap work at any size.\n needPOTTextures: false,\n };\n\n const engine: GLEngineContext = {\n canvas,\n gl,\n caps,\n _hsl: 1,\n _rafId: 0,\n _loops: [],\n _prevNow: 0,\n _state: createGLState(caps.maxTextureUnits),\n _effects: [],\n _effectCache: new Map(),\n _textures: [],\n _renderTargets: [],\n _currentRenderTarget: null,\n _buffers: [],\n _onLost: [],\n _onRestored: [],\n _isLost: false,\n _disposed: false,\n _lostHandler: () => {},\n _restoredHandler: () => {},\n _wasLoopActive: false,\n _scheduleFrame: null,\n };\n\n engine._lostHandler = (e: Event) => handleContextLost(engine, e);\n engine._restoredHandler = () => handleContextRestored(engine);\n canvas.addEventListener(\"webglcontextlost\", engine._lostHandler as EventListener, false);\n canvas.addEventListener(\"webglcontextrestored\", engine._restoredHandler, false);\n\n return engine;\n}\n\n/** Stops the render loop, removes DOM listeners, releases all known effects\n * and textures, then marks the context disposed. The browser-owned canvas\n * is left intact. */\nexport function disposeGLEngine(engine: GLEngineContext): void {\n if (engine._disposed) {\n return;\n }\n engine._disposed = true;\n if (engine._rafId !== 0) {\n cancelAnimationFrame(engine._rafId);\n engine._rafId = 0;\n }\n engine._loops.length = 0;\n engine.canvas.removeEventListener(\"webglcontextlost\", engine._lostHandler as EventListener, false);\n engine.canvas.removeEventListener(\"webglcontextrestored\", engine._restoredHandler, false);\n\n const gl = engine.gl;\n // Iterate snapshots — the dispose paths splice into the registries.\n const effects = engine._effects.slice();\n for (const eff of effects) {\n if (!eff._disposed) {\n eff._disposed = true;\n eff.isReady = false;\n gl.deleteProgram(eff.program);\n gl.deleteShader(eff._vs);\n gl.deleteShader(eff._fs);\n }\n }\n engine._effects.length = 0;\n engine._effectCache.clear();\n const textures = engine._textures.slice();\n for (const tex of textures) {\n if (!tex._disposed) {\n tex._disposed = true;\n gl.deleteTexture(tex.handle);\n }\n }\n engine._textures.length = 0;\n // Render targets own their FBO + renderbuffer + color texture; free them via\n // the per-RT closure so context.ts needs no runtime import of render-target.\n const rts = engine._renderTargets.slice();\n for (const rt of rts) {\n if (!rt._disposed) {\n rt._disposed = true;\n rt._deleteGpu(gl);\n }\n }\n engine._renderTargets.length = 0;\n\n // Mesh vertex/index buffers own a single GL buffer each; free via closure.\n const buffers = engine._buffers.slice();\n for (const buf of buffers) {\n if (!buf._disposed) {\n buf._disposed = true;\n buf._deleteGpu(gl);\n }\n }\n engine._buffers.length = 0;\n\n resetGLState(engine._state);\n engine._currentRenderTarget = null;\n engine._onLost.length = 0;\n engine._onRestored.length = 0;\n}\n\n/** Match drawing-buffer size to (clientSize × devicePixelRatio / _hsl). No-op\n * if size already matches. Never touches viewport — `setViewport` owns that. */\nexport function resizeGLEngine(engine: GLEngineContext): void {\n if (engine._disposed || engine._isLost) {\n return;\n }\n const canvas = engine.canvas;\n // An OffscreenCanvas has no CSS box (no clientWidth/clientHeight); it is sized\n // explicitly via setGLEngineSize, so CSS-derived auto-resize is a no-op for it.\n if (!(\"clientWidth\" in canvas)) {\n return;\n }\n const dpr = typeof devicePixelRatio === \"number\" ? devicePixelRatio : 1;\n const w = Math.max(1, Math.floor((canvas.clientWidth * dpr) / engine._hsl));\n const h = Math.max(1, Math.floor((canvas.clientHeight * dpr) / engine._hsl));\n if (canvas.width !== w) {\n canvas.width = w;\n }\n if (canvas.height !== h) {\n canvas.height = h;\n }\n}\n\n/**\n * Set the drawing-buffer size to an EXPLICIT width/height in physical pixels\n * (each divided by `_hsl`), independent of the canvas client/CSS size — the\n * counterpart of Babylon's `ThinEngine.setSize`. Use when the render resolution\n * is computed directly (e.g. animation dimensions × scale × dpr) rather than\n * derived from CSS layout via {@link resizeGLEngine}. No-op on a disposed\n * engine. Never touches the viewport — `setViewport` owns that.\n *\n * @param engine - The engine to resize.\n * @param width - Target drawing-buffer width in physical pixels.\n * @param height - Target drawing-buffer height in physical pixels.\n */\nexport function setGLEngineSize(engine: GLEngineContext, width: number, height: number): void {\n if (engine._disposed) {\n return;\n }\n const canvas = engine.canvas;\n const w = Math.max(1, Math.floor(width / engine._hsl));\n const h = Math.max(1, Math.floor(height / engine._hsl));\n if (canvas.width !== w) {\n canvas.width = w;\n }\n if (canvas.height !== h) {\n canvas.height = h;\n }\n}\n\n/**\n * Invalidate lite-gl's cached GL state so the next state-setting call in each\n * category (program / buffer / texture / VAO / framebuffer bindings, blend,\n * depth, stencil, scissor, color-mask, viewport, unpack) is re-issued instead\n * of elided.\n *\n * Use this when a host application that SHARES this WebGL2 context mutates raw\n * `gl.*` state outside lite-gl — e.g. a save/restore wrapper that resets the\n * context to GL defaults around each render scope, or any interop layer that\n * issues GL calls directly. Without it, lite-gl's redundant-call elision would\n * skip the state changes needed to re-establish the scope, producing corrupted\n * output. Owned GPU resources (the shared quad, render targets, meshes,\n * effects, textures) are preserved — only the cached \"current GL state\" is\n * reset. Mirrors Babylon's `Engine.wipeCaches()`.\n *\n * No-op while the context is lost or the engine is disposed.\n */\nexport function wipeGLStateCache(engine: GLEngineContext): void {\n if (engine._disposed || engine._isLost) {\n return;\n }\n resetGLStateCache(engine._state);\n}\n\n/** Drawing-buffer width in physical pixels (`canvas.width`). */\nexport function getRenderWidth(engine: GLEngineContext): number {\n return engine.canvas.width;\n}\n\n/** Drawing-buffer height in physical pixels (`canvas.height`). */\nexport function getRenderHeight(engine: GLEngineContext): number {\n return engine.canvas.height;\n}\n\n/** Current hardware-scaling factor — drawing-buffer = clientSize × dpr / level. */\nexport function getHardwareScalingLevel(engine: GLEngineContext): number {\n return engine._hsl;\n}\n\n/** Updates the hardware-scaling factor and triggers a resize. */\nexport function setHardwareScalingLevel(engine: GLEngineContext, level: number): void {\n if (level <= 0 || !isFinite(level)) {\n return;\n }\n engine._hsl = level;\n resizeGLEngine(engine);\n}\n\n/** The backing canvas element (an `HTMLCanvasElement`, or an `OffscreenCanvas`\n * for worker render paths). */\nexport function getRenderingCanvas(engine: GLEngineContext): HTMLCanvasElement | OffscreenCanvas {\n return engine.canvas;\n}\n\n/** Register a `webglcontextlost` callback. Duplicate registrations are ignored. */\nexport function onContextLost(engine: GLEngineContext, cb: () => void): void {\n if (engine._onLost.indexOf(cb) === -1) {\n engine._onLost.push(cb);\n }\n}\n\n/** Remove a previously-registered context-lost callback. */\nexport function offContextLost(engine: GLEngineContext, cb: () => void): void {\n const i = engine._onLost.indexOf(cb);\n if (i !== -1) {\n engine._onLost.splice(i, 1);\n }\n}\n\n/** Register a `webglcontextrestored` callback. Duplicate registrations are ignored. */\nexport function onContextRestored(engine: GLEngineContext, cb: () => void): void {\n if (engine._onRestored.indexOf(cb) === -1) {\n engine._onRestored.push(cb);\n }\n}\n\n/** Remove a previously-registered context-restored callback. */\nexport function offContextRestored(engine: GLEngineContext, cb: () => void): void {\n const i = engine._onRestored.indexOf(cb);\n if (i !== -1) {\n engine._onRestored.splice(i, 1);\n }\n}\n\n/* ───────────────────────── internal: loss / restore ───────────────────── */\n\nfunction handleContextLost(engine: GLEngineContext, e: Event): void {\n // Opt-in to restore. Without preventDefault() the browser will NOT fire\n // webglcontextrestored later.\n e.preventDefault();\n if (engine._isLost || engine._disposed) {\n return;\n }\n engine._isLost = true;\n engine._wasLoopActive = engine._rafId !== 0;\n if (engine._rafId !== 0) {\n cancelAnimationFrame(engine._rafId);\n engine._rafId = 0;\n }\n resetGLState(engine._state);\n engine._currentRenderTarget = null;\n for (const eff of engine._effects) {\n eff.isReady = false;\n eff._samplersAssigned = false;\n eff.uniformLocations = {};\n eff.attributeLocations = {};\n // Clear value caches so the first frame after restore re-uploads\n // every uniform into the freshly-linked program.\n clearObject(eff._lastF1);\n clearObject(eff._lastVec);\n clearObject(eff._lastI1);\n // Do NOT gl.deleteProgram — handle is already dead per WebGL spec.\n }\n for (const tex of engine._textures) {\n tex._wasReady = tex.isReady;\n tex.isReady = false;\n }\n const cbs = engine._onLost.slice();\n for (const cb of cbs) {\n try {\n cb();\n } catch (err) {\n console.error(\"lite-gl: onLost callback threw\", err);\n }\n }\n}\n\nfunction handleContextRestored(engine: GLEngineContext): void {\n if (engine._disposed) {\n return;\n }\n for (const eff of engine._effects) {\n if (!eff._disposed) {\n eff._restore(engine);\n }\n }\n for (const tex of engine._textures) {\n if (!tex._disposed) {\n const newHandle = engine.gl.createTexture();\n if (newHandle !== null) {\n tex.handle = newHandle;\n tex._upload(engine);\n tex.isReady = tex._wasReady;\n }\n }\n }\n // Clear the lost flag BEFORE the render-target / buffer rebuilds: their\n // `_restore` closures route through the same guarded build/upload paths used\n // at create time (which no-op while `_isLost`), whereas the effect / texture\n // replay above is unguarded and unaffected by the flag's value.\n engine._isLost = false;\n // Render targets rebuild their own color texture + FBO + renderbuffer AFTER\n // the standalone texture replay above (an RT's color texture is owned by the\n // RT, not the `_textures` registry).\n for (const rt of engine._renderTargets) {\n if (!rt._disposed) {\n rt._restore(engine);\n }\n }\n // Mesh buffers re-upload their retained CPU data into fresh GL buffers.\n for (const buf of engine._buffers) {\n if (!buf._disposed) {\n buf._restore(engine);\n }\n }\n if (engine._wasLoopActive && engine._loops.length > 0 && engine._scheduleFrame !== null) {\n engine._scheduleFrame(engine);\n }\n const cbs = engine._onRestored.slice();\n for (const cb of cbs) {\n try {\n cb();\n } catch (err) {\n console.error(\"lite-gl: onRestored callback threw\", err);\n }\n }\n}\n\nfunction clearObject(o: { [k: string]: unknown }): void {\n for (const key in o) {\n delete o[key];\n }\n}\n"]}
|