@babylonjs/lite-gl 1.3.0 → 1.4.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 CHANGED
@@ -95,7 +95,8 @@ use.
95
95
  - **Meshes / buffers** — `createVertexBuffer`, `updateVertexBuffer`,
96
96
  `createIndexBuffer`, `bindAttributes`, `drawIndexed` (instanced), `disposeBuffer`.
97
97
  - **Depth / stencil / scissor** — `setDepthState`, `setStencilState`,
98
- `setCullState`, `setColorMask`, `clearEngine`; `setScissor`, `disableScissor`.
98
+ `setStencilOpSeparate` (independent front/back operations), `setCullState`,
99
+ `setColorMask`, `clearEngine`; `setScissor`, `disableScissor`.
99
100
  - **Blend** — `setBlendMode` + `GLBlendMode` (`DISABLE` / `ADD` / `ALPHA` /
100
101
  `PREMULTIPLIED`), matching Babylon's `setAlphaMode` parameters.
101
102
 
@@ -0,0 +1,7 @@
1
+ import type { GLEngineContext } from "./context.js";
2
+ /** Enable or disable WebGL2 sample alpha-to-coverage. Repeated state is elided. */
3
+ export declare function setAlphaToCoverage(engine: GLEngineContext, enabled: boolean): void;
4
+ /** Return the requested alpha-to-coverage state. Defaults to false. */
5
+ export declare function getAlphaToCoverage(engine: GLEngineContext): boolean;
6
+ /** Return the current draw framebuffer's sample count, normalized to at least one. */
7
+ export declare function getCurrentSampleCount(engine: GLEngineContext): number;
@@ -0,0 +1,63 @@
1
+ import { onContextRestored } from "./context.js";
2
+ // Optional feature state is allocated only after the first setter call. The weak key prevents this
3
+ // module from extending engine lifetime, and non-importing consumers allocate no feature state.
4
+ let _states = null;
5
+ /** Enable or disable WebGL2 sample alpha-to-coverage. Repeated state is elided. */
6
+ export function setAlphaToCoverage(engine, enabled) {
7
+ if (engine._disposed) {
8
+ return;
9
+ }
10
+ const state = getOrCreateState(engine);
11
+ state.enabled = enabled;
12
+ applyState(engine, state);
13
+ }
14
+ /** Return the requested alpha-to-coverage state. Defaults to false. */
15
+ export function getAlphaToCoverage(engine) {
16
+ return _states?.get(engine)?.enabled ?? false;
17
+ }
18
+ /** Return the current draw framebuffer's sample count, normalized to at least one. */
19
+ export function getCurrentSampleCount(engine) {
20
+ if (engine._isLost || engine._disposed) {
21
+ return 1;
22
+ }
23
+ const samples = Number(engine.gl.getParameter(engine.gl.SAMPLES));
24
+ return Number.isFinite(samples) ? Math.max(1, samples) : 1;
25
+ }
26
+ function getOrCreateState(engine) {
27
+ let states = _states;
28
+ if (!states) {
29
+ states = _states = new WeakMap();
30
+ }
31
+ let state = states.get(engine);
32
+ if (state) {
33
+ return state;
34
+ }
35
+ state = {
36
+ enabled: false,
37
+ applied: null,
38
+ restore: () => {
39
+ state.applied = null;
40
+ applyState(engine, state);
41
+ },
42
+ };
43
+ states.set(engine, state);
44
+ (engine._stateCacheInvalidators ??= []).push(() => {
45
+ state.applied = null;
46
+ });
47
+ onContextRestored(engine, state.restore);
48
+ return state;
49
+ }
50
+ function applyState(engine, state) {
51
+ if (engine._isLost || engine._disposed || state.applied === state.enabled) {
52
+ return;
53
+ }
54
+ const gl = engine.gl;
55
+ if (state.enabled) {
56
+ gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);
57
+ }
58
+ else {
59
+ gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);
60
+ }
61
+ state.applied = state.enabled;
62
+ }
63
+ //# sourceMappingURL=alpha-to-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alpha-to-coverage.js","sourceRoot":"","sources":["../src/alpha-to-coverage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AASjD,mGAAmG;AACnG,gGAAgG;AAChG,IAAI,OAAO,GAA0D,IAAI,CAAC;AAE1E,mFAAmF;AACnF,MAAM,UAAU,kBAAkB,CAAC,MAAuB,EAAE,OAAgB;IACxE,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACnB,OAAO;IACX,CAAC;IACD,MAAM,KAAK,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACvC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB,CAAC,MAAuB;IACtD,OAAO,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,OAAO,IAAI,KAAK,CAAC;AAClD,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,qBAAqB,CAAC,MAAuB;IACzD,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO,CAAC,CAAC;IACb,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAClE,OAAO,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAuB;IAC7C,IAAI,MAAM,GAAG,OAAO,CAAC;IACrB,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,GAAG,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IACrC,CAAC;IACD,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,IAAI,KAAK,EAAE,CAAC;QACR,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,KAAK,GAAG;QACJ,OAAO,EAAE,KAAK;QACd,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,GAAG,EAAE;YACV,KAAM,CAAC,OAAO,GAAG,IAAI,CAAC;YACtB,UAAU,CAAC,MAAM,EAAE,KAAM,CAAC,CAAC;QAC/B,CAAC;KACJ,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC1B,CAAC,MAAM,CAAC,uBAAuB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;QAC9C,KAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IAC1B,CAAC,CAAC,CAAC;IACH,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,MAAuB,EAAE,KAA2B;IACpE,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QACxE,OAAO;IACX,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;IACrB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAChB,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACJ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IACD,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;AAClC,CAAC","sourcesContent":["import { onContextRestored } from \"./context.js\";\nimport type { GLEngineContext } from \"./context.js\";\n\ninterface AlphaToCoverageState {\n enabled: boolean;\n applied: boolean | null;\n restore: () => void;\n}\n\n// Optional feature state is allocated only after the first setter call. The weak key prevents this\n// module from extending engine lifetime, and non-importing consumers allocate no feature state.\nlet _states: WeakMap<GLEngineContext, AlphaToCoverageState> | null = null;\n\n/** Enable or disable WebGL2 sample alpha-to-coverage. Repeated state is elided. */\nexport function setAlphaToCoverage(engine: GLEngineContext, enabled: boolean): void {\n if (engine._disposed) {\n return;\n }\n const state = getOrCreateState(engine);\n state.enabled = enabled;\n applyState(engine, state);\n}\n\n/** Return the requested alpha-to-coverage state. Defaults to false. */\nexport function getAlphaToCoverage(engine: GLEngineContext): boolean {\n return _states?.get(engine)?.enabled ?? false;\n}\n\n/** Return the current draw framebuffer's sample count, normalized to at least one. */\nexport function getCurrentSampleCount(engine: GLEngineContext): number {\n if (engine._isLost || engine._disposed) {\n return 1;\n }\n const samples = Number(engine.gl.getParameter(engine.gl.SAMPLES));\n return Number.isFinite(samples) ? Math.max(1, samples) : 1;\n}\n\nfunction getOrCreateState(engine: GLEngineContext): AlphaToCoverageState {\n let states = _states;\n if (!states) {\n states = _states = new WeakMap();\n }\n let state = states.get(engine);\n if (state) {\n return state;\n }\n state = {\n enabled: false,\n applied: null,\n restore: () => {\n state!.applied = null;\n applyState(engine, state!);\n },\n };\n states.set(engine, state);\n (engine._stateCacheInvalidators ??= []).push(() => {\n state!.applied = null;\n });\n onContextRestored(engine, state.restore);\n return state;\n}\n\nfunction applyState(engine: GLEngineContext, state: AlphaToCoverageState): void {\n if (engine._isLost || engine._disposed || state.applied === state.enabled) {\n return;\n }\n const gl = engine.gl;\n if (state.enabled) {\n gl.enable(gl.SAMPLE_ALPHA_TO_COVERAGE);\n } else {\n gl.disable(gl.SAMPLE_ALPHA_TO_COVERAGE);\n }\n state.applied = state.enabled;\n}\n"]}
package/context.d.ts CHANGED
@@ -17,7 +17,8 @@ export interface GLEngineOptions {
17
17
  /** Default: false. */
18
18
  failIfMajorPerformanceCaveat?: boolean;
19
19
  }
20
- /** Read-only WebGL2 capability limits, queried once at context creation. */
20
+ /** Read-only WebGL2 capability limits, queried at creation and refreshed after
21
+ * context restoration because extension objects do not survive context loss. */
21
22
  export interface GLEngineCaps {
22
23
  /** `gl.MAX_TEXTURE_SIZE` — largest supported texture dimension, in texels. */
23
24
  readonly maxTextureSize: number;
package/context.js CHANGED
@@ -19,27 +19,7 @@ export function createGLEngine(canvas, options) {
19
19
  if (gl === null) {
20
20
  throw new Error("lite-gl: WebGL2 is not supported on this canvas");
21
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
- };
22
+ const caps = queryCaps(gl);
43
23
  const engine = {
44
24
  canvas,
45
25
  gl,
@@ -130,6 +110,7 @@ export function disposeGLEngine(engine) {
130
110
  engine._currentRenderTarget = null;
131
111
  engine._onLost.length = 0;
132
112
  engine._onRestored.length = 0;
113
+ engine._stateCacheInvalidators?.splice(0);
133
114
  }
134
115
  /** Match drawing-buffer size to (clientSize × devicePixelRatio / _hsl). No-op
135
116
  * if size already matches. Never touches viewport — `setViewport` owns that. */
@@ -201,6 +182,12 @@ export function wipeGLStateCache(engine) {
201
182
  return;
202
183
  }
203
184
  resetGLStateCache(engine._state);
185
+ const invalidators = engine._stateCacheInvalidators;
186
+ if (invalidators) {
187
+ for (const invalidate of invalidators) {
188
+ invalidate();
189
+ }
190
+ }
204
191
  }
205
192
  /** Drawing-buffer width in physical pixels (`canvas.width`). */
206
193
  export function getRenderWidth(engine) {
@@ -262,7 +249,9 @@ function handleContextLost(engine, e) {
262
249
  return;
263
250
  }
264
251
  engine._isLost = true;
265
- engine._wasLoopActive = engine._rafId !== 0;
252
+ // `tick` clears `_rafId` while callbacks run, so a loss event delivered in
253
+ // that window still represents an active loop when callbacks remain.
254
+ engine._wasLoopActive = engine._loops.length > 0;
266
255
  if (engine._rafId !== 0) {
267
256
  cancelAnimationFrame(engine._rafId);
268
257
  engine._rafId = 0;
@@ -299,6 +288,7 @@ function handleContextRestored(engine) {
299
288
  if (engine._disposed) {
300
289
  return;
301
290
  }
291
+ Object.assign(engine.caps, queryCaps(engine.gl));
302
292
  for (const eff of engine._effects) {
303
293
  if (!eff._disposed) {
304
294
  eff._restore(engine);
@@ -346,6 +336,24 @@ function handleContextRestored(engine) {
346
336
  }
347
337
  }
348
338
  }
339
+ function queryCaps(gl) {
340
+ const parallelExt = gl.getExtension("KHR_parallel_shader_compile");
341
+ // `getExtension` both queries and enables these capabilities, so they must
342
+ // be reacquired after context restoration before resources are rebuilt.
343
+ const colorBufferFloat = gl.getExtension("EXT_color_buffer_float") !== null;
344
+ const colorBufferHalfFloat = gl.getExtension("EXT_color_buffer_half_float") !== null;
345
+ const floatLinear = gl.getExtension("OES_texture_float_linear") !== null;
346
+ return {
347
+ maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
348
+ maxTextureUnits: gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS),
349
+ parallelShaderCompile: parallelExt,
350
+ textureFloatRender: colorBufferFloat,
351
+ textureFloatLinearFiltering: floatLinear,
352
+ textureHalfFloatRender: colorBufferFloat || colorBufferHalfFloat,
353
+ textureHalfFloatLinearFiltering: true,
354
+ needPOTTextures: false,
355
+ };
356
+ }
349
357
  function clearObject(o) {
350
358
  for (const key in o) {
351
359
  delete o[key];
package/context.js.map CHANGED
@@ -1 +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"]}
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 at creation and refreshed after\n * context restoration because extension objects do not survive context loss. */\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 /** Optional feature-owned state-cache invalidators. Allocated lazily by the first feature that needs one.\n * @internal */\n _stateCacheInvalidators?: (() => void)[];\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 caps = queryCaps(gl);\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 engine._stateCacheInvalidators?.splice(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 const invalidators = engine._stateCacheInvalidators;\n if (invalidators) {\n for (const invalidate of invalidators) {\n invalidate();\n }\n }\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 // `tick` clears `_rafId` while callbacks run, so a loss event delivered in\n // that window still represents an active loop when callbacks remain.\n engine._wasLoopActive = engine._loops.length > 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 Object.assign(engine.caps, queryCaps(engine.gl));\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 queryCaps(gl: WebGL2RenderingContext): GLEngineCaps {\n const parallelExt = gl.getExtension(\"KHR_parallel_shader_compile\") as { COMPLETION_STATUS_KHR: number } | null;\n // `getExtension` both queries and enables these capabilities, so they must\n // be reacquired after context restoration before resources are rebuilt.\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 return {\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 textureHalfFloatRender: colorBufferFloat || colorBufferHalfFloat,\n textureHalfFloatLinearFiltering: true,\n needPOTTextures: false,\n };\n}\n\nfunction clearObject(o: { [k: string]: unknown }): void {\n for (const key in o) {\n delete o[key];\n }\n}\n"]}
@@ -34,20 +34,19 @@ export interface GLDepthState {
34
34
  func?: GLenum;
35
35
  }
36
36
  /** Stencil configuration for {@link setStencilState}. Omitted fields are left
37
- * unchanged. The `func`/`ref`/`funcMask` triple and the
38
- * `opFail`/`opZFail`/`opZPass` triple are each applied as a unit (any member
39
- * present re-issues that GL call, merging the unspecified members from cache). */
37
+ * unchanged. Operation fields update both front and back faces; use
38
+ * {@link setStencilOpSeparate} for per-face values. */
40
39
  export interface GLStencilState {
41
40
  /** Enable/disable the stencil test (`gl.enable/disable(STENCIL_TEST)`). */
42
41
  test?: boolean;
43
- /** Stencil write mask (`gl.stencilMask`). */
44
- mask?: number;
45
42
  /** Comparison function (`gl.stencilFunc` arg 1), e.g. `gl.ALWAYS`. */
46
43
  func?: GLenum;
47
44
  /** Reference value (`gl.stencilFunc` arg 2). */
48
45
  ref?: number;
49
46
  /** Comparison mask (`gl.stencilFunc` arg 3). */
50
47
  funcMask?: number;
48
+ /** Stencil write mask (`gl.stencilMask`). */
49
+ mask?: number;
51
50
  /** Op when the stencil test fails (`gl.stencilOp` arg 1). */
52
51
  opFail?: GLenum;
53
52
  /** Op when the stencil test passes but depth fails (`gl.stencilOp` arg 2). */
@@ -55,6 +54,16 @@ export interface GLStencilState {
55
54
  /** Op when both stencil and depth pass (`gl.stencilOp` arg 3). */
56
55
  opZPass?: GLenum;
57
56
  }
57
+ /** Per-face stencil operations for {@link setStencilOpSeparate}. Omitted fields
58
+ * retain that face's desired value. */
59
+ export interface GLStencilOpState {
60
+ /** Op when the stencil test fails (`gl.stencilOpSeparate` arg 2). */
61
+ opFail?: GLenum;
62
+ /** Op when the stencil test passes but depth fails (`gl.stencilOpSeparate` arg 3). */
63
+ opZFail?: GLenum;
64
+ /** Op when both stencil and depth pass (`gl.stencilOpSeparate` arg 4). */
65
+ opZPass?: GLenum;
66
+ }
58
67
  /** Options for {@link clearEngine}. */
59
68
  export interface GLClearOptions {
60
69
  /** When set, clears the color buffer to this RGBA color (alpha default 1). */
@@ -98,6 +107,19 @@ export declare function setCullState(engine: GLEngineContext, enabled: boolean,
98
107
  * @param state - The stencil fields to change. Omitted fields are untouched.
99
108
  */
100
109
  export declare function setStencilState(engine: GLEngineContext, state: GLStencilState): void;
110
+ /**
111
+ * Buffer stencil operations for one face into the DESIRED state. This is the
112
+ * deferred lite-gl counterpart of `gl.stencilOpSeparate`; use it to apply
113
+ * different winding operations to front and back faces in one draw. A
114
+ * `FRONT_AND_BACK` update applies each provided operation to both faces while
115
+ * leaving omitted operations unchanged on each face.
116
+ *
117
+ * @param engine - The engine.
118
+ * @param face - `gl.FRONT`, `gl.BACK`, or `gl.FRONT_AND_BACK`.
119
+ * @param state - The operation fields to change for the selected face(s).
120
+ * @throws If `face` is not `gl.FRONT`, `gl.BACK`, or `gl.FRONT_AND_BACK`.
121
+ */
122
+ export declare function setStencilOpSeparate(engine: GLEngineContext, face: GLenum, state: GLStencilOpState): void;
101
123
  /**
102
124
  * Buffer the color write mask into the DESIRED state — the lite-gl equivalent of
103
125
  * Babylon's `setColorWrite` (which passes the same flag to all four channels).
package/depth-stencil.js CHANGED
@@ -12,6 +12,20 @@ const DEPTH_TEST = 0x0b71;
12
12
  const CULL_FACE = 0x0b44;
13
13
  /** GL `gl.STENCIL_TEST`. */
14
14
  const STENCIL_TEST = 0x0b90;
15
+ /** GL `gl.KEEP`. */
16
+ const KEEP = 0x1e00;
17
+ /** GL `gl.FRONT`. */
18
+ const FRONT = 0x0404;
19
+ /** GL `gl.BACK`. */
20
+ const BACK = 0x0405;
21
+ /** GL `gl.FRONT_AND_BACK`. */
22
+ const FRONT_AND_BACK = 0x0408;
23
+ // Two-sided mode appends actual slots 46..48 and desired slots 49..51 to the
24
+ // compact 46-slot state array. Base indices remain unchanged.
25
+ const BACK_OP_FAIL = 46;
26
+ const BACK_OP_ZFAIL = 47;
27
+ const BACK_OP_ZPASS = 48;
28
+ const BACK_DESIRED = 3;
15
29
  // ── Framebuffer / renderbuffer enums (used only by generateRenderTargetStencil).
16
30
  // Module-local consts mirror render-target.ts's constant style. Because the
17
31
  // render-target core no longer references the stencil/packed enums, they live
@@ -33,6 +47,77 @@ const STENCIL_ATTACHMENT = 0x8d20;
33
47
  const DEPTH_ATTACHMENT = 0x8d00;
34
48
  /** GL `gl.FRAMEBUFFER_COMPLETE`. */
35
49
  const FRAMEBUFFER_COMPLETE = 0x8cd5;
50
+ /** Seed the shared desired op tuple with WebGL's KEEP defaults. */
51
+ function ensureStencilOpCacheInitialized(rs) {
52
+ if (rs[RS_STENCIL_OP_FAIL + RS_DESIRED] !== -1) {
53
+ return;
54
+ }
55
+ rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = KEEP;
56
+ rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = KEEP;
57
+ rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = KEEP;
58
+ }
59
+ function setDesiredStencilOps(rs, state) {
60
+ if ((state.opFail ?? state.opZFail ?? state.opZPass) === undefined) {
61
+ return;
62
+ }
63
+ ensureStencilOpCacheInitialized(rs);
64
+ if (state.opFail !== undefined) {
65
+ rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = state.opFail;
66
+ }
67
+ if (state.opZFail !== undefined) {
68
+ rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = state.opZFail;
69
+ }
70
+ if (state.opZPass !== undefined) {
71
+ rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = state.opZPass;
72
+ }
73
+ }
74
+ /** Store one complete GL operation tuple starting at `opIndex`. */
75
+ function setStencilOps(rs, opIndex, fail, zfail, zpass) {
76
+ rs[opIndex] = fail;
77
+ rs[opIndex + 1] = zfail;
78
+ rs[opIndex + 2] = zpass;
79
+ }
80
+ function resetStencilBack(rs) {
81
+ ensureStencilOpCacheInitialized(rs);
82
+ setStencilOps(rs, BACK_OP_FAIL, rs[RS_STENCIL_OP_FAIL], rs[RS_STENCIL_OP_ZFAIL], rs[RS_STENCIL_OP_ZPASS]);
83
+ setStencilOps(rs, BACK_OP_FAIL + BACK_DESIRED, rs[RS_STENCIL_OP_FAIL + RS_DESIRED], rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED], rs[RS_STENCIL_OP_ZPASS + RS_DESIRED]);
84
+ }
85
+ function ensureStencilBackCacheInitialized(rs) {
86
+ if (rs[RS_STENCIL_OP_FAIL + RS_DESIRED] === -1) {
87
+ resetStencilBack(rs);
88
+ }
89
+ }
90
+ function setDesiredStencilBackOps(rs, state) {
91
+ if (state.opFail !== undefined) {
92
+ rs[BACK_OP_FAIL + BACK_DESIRED] = state.opFail;
93
+ }
94
+ if (state.opZFail !== undefined) {
95
+ rs[BACK_OP_ZFAIL + BACK_DESIRED] = state.opZFail;
96
+ }
97
+ if (state.opZPass !== undefined) {
98
+ rs[BACK_OP_ZPASS + BACK_DESIRED] = state.opZPass;
99
+ }
100
+ }
101
+ /** Mirror a shared stencil-op update to the lazily allocated back-face cache. */
102
+ function mirrorSharedStencilOpsToBack(s, state) {
103
+ if ((state.opFail ?? state.opZFail ?? state.opZPass) === undefined) {
104
+ return;
105
+ }
106
+ ensureStencilBackCacheInitialized(s.rs);
107
+ setDesiredStencilBackOps(s.rs, state);
108
+ }
109
+ /** Lazily install back-face storage and the two-sided reconciler. */
110
+ function enableSeparateStencil(s) {
111
+ if (s._setStencilBack !== undefined) {
112
+ return;
113
+ }
114
+ const rs = new Float64Array(52);
115
+ rs.set(s.rs);
116
+ s.rs = rs;
117
+ resetStencilBack(rs);
118
+ s._setStencilBack = mirrorSharedStencilOpsToBack;
119
+ s._flushStencil = flushStencilSeparate;
120
+ }
36
121
  /**
37
122
  * Buffer depth-buffer state (test enable, write mask, comparison function) into
38
123
  * the DESIRED state — the lite-gl equivalent of mutating Babylon's
@@ -96,9 +181,6 @@ export function setStencilState(engine, state) {
96
181
  if (state.test !== undefined) {
97
182
  s.rs[RS_STENCIL_TEST + RS_DESIRED] = state.test ? 1 : 0;
98
183
  }
99
- if (state.mask !== undefined) {
100
- s.rs[RS_STENCIL_MASK + RS_DESIRED] = state.mask;
101
- }
102
184
  if (state.func !== undefined) {
103
185
  s.rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED] = state.func;
104
186
  }
@@ -108,16 +190,56 @@ export function setStencilState(engine, state) {
108
190
  if (state.funcMask !== undefined) {
109
191
  s.rs[RS_STENCIL_FUNC_MASK + RS_DESIRED] = state.funcMask;
110
192
  }
111
- if (state.opFail !== undefined) {
112
- s.rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = state.opFail;
193
+ if (state.mask !== undefined) {
194
+ s.rs[RS_STENCIL_MASK + RS_DESIRED] = state.mask;
113
195
  }
114
- if (state.opZFail !== undefined) {
115
- s.rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = state.opZFail;
196
+ s._setStencilBack?.(s, state);
197
+ if ((state.opFail ?? state.opZFail ?? state.opZPass) !== undefined) {
198
+ ensureStencilOpCacheInitialized(s.rs);
199
+ if (state.opFail !== undefined) {
200
+ s.rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = state.opFail;
201
+ }
202
+ if (state.opZFail !== undefined) {
203
+ s.rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = state.opZFail;
204
+ }
205
+ if (state.opZPass !== undefined) {
206
+ s.rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = state.opZPass;
207
+ }
116
208
  }
117
- if (state.opZPass !== undefined) {
118
- s.rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = state.opZPass;
209
+ s._flushStencil ??= flushStencil;
210
+ s.statesDirty = true;
211
+ }
212
+ /**
213
+ * Buffer stencil operations for one face into the DESIRED state. This is the
214
+ * deferred lite-gl counterpart of `gl.stencilOpSeparate`; use it to apply
215
+ * different winding operations to front and back faces in one draw. A
216
+ * `FRONT_AND_BACK` update applies each provided operation to both faces while
217
+ * leaving omitted operations unchanged on each face.
218
+ *
219
+ * @param engine - The engine.
220
+ * @param face - `gl.FRONT`, `gl.BACK`, or `gl.FRONT_AND_BACK`.
221
+ * @param state - The operation fields to change for the selected face(s).
222
+ * @throws If `face` is not `gl.FRONT`, `gl.BACK`, or `gl.FRONT_AND_BACK`.
223
+ */
224
+ export function setStencilOpSeparate(engine, face, state) {
225
+ if (engine._isLost || engine._disposed) {
226
+ return;
227
+ }
228
+ const s = engine._state;
229
+ if (face !== FRONT && face !== BACK && face !== FRONT_AND_BACK) {
230
+ throw new Error("lite-gl: invalid face");
231
+ }
232
+ if ((state.opFail ?? state.opZFail ?? state.opZPass) === undefined) {
233
+ return;
234
+ }
235
+ enableSeparateStencil(s);
236
+ ensureStencilBackCacheInitialized(s.rs);
237
+ if (face !== BACK) {
238
+ setDesiredStencilOps(s.rs, state);
239
+ }
240
+ if (face !== FRONT) {
241
+ setDesiredStencilBackOps(s.rs, state);
119
242
  }
120
- s._flushStencil = flushStencil;
121
243
  s.statesDirty = true;
122
244
  }
123
245
  /**
@@ -237,9 +359,7 @@ function flushDepthCull(engine) {
237
359
  gl.cullFace(dCullFace);
238
360
  }
239
361
  }
240
- /** Reconcile the stencil test / mask / func-triple / op-triple (Babylon's
241
- * `_stencilState.apply`); each triple is issued as a unit. @internal */
242
- function flushStencil(engine) {
362
+ function flushStencilCommon(engine) {
243
363
  const gl = engine.gl;
244
364
  const rs = engine._state.rs;
245
365
  const dTest = rs[RS_STENCIL_TEST + RS_DESIRED];
@@ -266,14 +386,59 @@ function flushStencil(engine) {
266
386
  rs[RS_STENCIL_FUNC_MASK] = dFuncMask;
267
387
  gl.stencilFunc(dFuncFunc, dFuncRef, dFuncMask);
268
388
  }
269
- const dOpFail = rs[RS_STENCIL_OP_FAIL + RS_DESIRED];
270
- const dOpZFail = rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED];
271
- const dOpZPass = rs[RS_STENCIL_OP_ZPASS + RS_DESIRED];
272
- if (dOpFail !== rs[RS_STENCIL_OP_FAIL] || dOpZFail !== rs[RS_STENCIL_OP_ZFAIL] || dOpZPass !== rs[RS_STENCIL_OP_ZPASS]) {
273
- rs[RS_STENCIL_OP_FAIL] = dOpFail;
274
- rs[RS_STENCIL_OP_ZFAIL] = dOpZFail;
275
- rs[RS_STENCIL_OP_ZPASS] = dOpZPass;
276
- gl.stencilOp(dOpFail, dOpZFail, dOpZPass);
389
+ }
390
+ /** Reconcile the compact shared stencil state. @internal */
391
+ function flushStencil(engine) {
392
+ flushStencilCommon(engine);
393
+ const gl = engine.gl;
394
+ const rs = engine._state.rs;
395
+ const frontOpFail = rs[RS_STENCIL_OP_FAIL + RS_DESIRED];
396
+ if (frontOpFail === -1) {
397
+ return;
398
+ }
399
+ const frontOpZFail = rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED];
400
+ const frontOpZPass = rs[RS_STENCIL_OP_ZPASS + RS_DESIRED];
401
+ const frontChanged = frontOpFail !== rs[RS_STENCIL_OP_FAIL] || frontOpZFail !== rs[RS_STENCIL_OP_ZFAIL] || frontOpZPass !== rs[RS_STENCIL_OP_ZPASS];
402
+ if (frontChanged) {
403
+ rs[RS_STENCIL_OP_FAIL] = frontOpFail;
404
+ rs[RS_STENCIL_OP_ZFAIL] = frontOpZFail;
405
+ rs[RS_STENCIL_OP_ZPASS] = frontOpZPass;
406
+ gl.stencilOp(frontOpFail, frontOpZFail, frontOpZPass);
407
+ }
408
+ }
409
+ /** Reconcile independently cached front/back stencil operation tuples. */
410
+ function flushStencilSeparate(engine) {
411
+ flushStencilCommon(engine);
412
+ const gl = engine.gl;
413
+ const rs = engine._state.rs;
414
+ const frontOpFail = rs[RS_STENCIL_OP_FAIL + RS_DESIRED];
415
+ if (frontOpFail === -1) {
416
+ return;
417
+ }
418
+ const frontOpZFail = rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED];
419
+ const frontOpZPass = rs[RS_STENCIL_OP_ZPASS + RS_DESIRED];
420
+ const backOpFail = rs[BACK_OP_FAIL + BACK_DESIRED];
421
+ const backOpZFail = rs[BACK_OP_ZFAIL + BACK_DESIRED];
422
+ const backOpZPass = rs[BACK_OP_ZPASS + BACK_DESIRED];
423
+ const frontChanged = frontOpFail !== rs[RS_STENCIL_OP_FAIL] || frontOpZFail !== rs[RS_STENCIL_OP_ZFAIL] || frontOpZPass !== rs[RS_STENCIL_OP_ZPASS];
424
+ const backChanged = backOpFail !== rs[BACK_OP_FAIL] || backOpZFail !== rs[BACK_OP_ZFAIL] || backOpZPass !== rs[BACK_OP_ZPASS];
425
+ if (!frontChanged && !backChanged) {
426
+ return;
427
+ }
428
+ const facesMatch = frontOpFail === backOpFail && frontOpZFail === backOpZFail && frontOpZPass === backOpZPass;
429
+ if (facesMatch) {
430
+ setStencilOps(rs, RS_STENCIL_OP_FAIL, frontOpFail, frontOpZFail, frontOpZPass);
431
+ setStencilOps(rs, BACK_OP_FAIL, backOpFail, backOpZFail, backOpZPass);
432
+ gl.stencilOp(frontOpFail, frontOpZFail, frontOpZPass);
433
+ return;
434
+ }
435
+ if (frontChanged) {
436
+ setStencilOps(rs, RS_STENCIL_OP_FAIL, frontOpFail, frontOpZFail, frontOpZPass);
437
+ gl.stencilOpSeparate(FRONT, frontOpFail, frontOpZFail, frontOpZPass);
438
+ }
439
+ if (backChanged) {
440
+ setStencilOps(rs, BACK_OP_FAIL, backOpFail, backOpZFail, backOpZPass);
441
+ gl.stencilOpSeparate(BACK, backOpFail, backOpZFail, backOpZPass);
277
442
  }
278
443
  }
279
444
  /** Reconcile the packed color-write mask (Babylon's `setColorWrite`). @internal */
@@ -1 +1 @@
1
- {"version":3,"file":"depth-stencil.js","sourceRoot":"","sources":["../src/depth-stencil.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Depth, stencil, color-mask and clear state — the lite-gl counterpart of\n * Babylon's `_depthCullingState` / `_stencilState` / `setColorWrite` / `clear`.\n *\n * Like Babylon, these setters are DEFERRED: they buffer the requested values\n * into the DESIRED (`d*`) mirror fields of `GLState` and raise `statesDirty`,\n * issuing NO `gl.*` calls themselves. `applyGLStates` (apply-states.ts) flushes\n * the diff to GL right before each draw (and before `clearEngine`'s `gl.clear`,\n * since a clear respects the current write masks). Every field is reconciled\n * independently, so a flush that changes only one sub-state (e.g. just the\n * stencil op triple) issues only that GL call. Omitted setter fields leave the\n * corresponding desired value untouched (merge-from-desired).\n *\n * All setters are no-ops on a lost/disposed context.\n *\n * This module also hosts {@link generateRenderTargetStencil} — the tree-shakeable\n * opt-in that gives a `/render-target` {@link GLRenderTarget} a stencil (or packed\n * depth+stencil) attachment. Keeping the STENCIL_INDEX8 / DEPTH24_STENCIL8\n * renderbuffer code here (rather than in the render-target core) means a consumer\n * that only needs a depth buffer never ships it. The one-way type import below\n * (`depth-stencil` importing `render-target`) introduces NO cycle: render-target\n * must not import this module.\n */\nimport type { GLEngineContext } from \"./context.js\";\nimport type { GLRenderTarget } from \"./render-target.js\";\nimport { applyGLStates } from \"./apply-states.js\";\nimport {\n RS_CLEAR_A,\n RS_CLEAR_B,\n RS_CLEAR_G,\n RS_CLEAR_R,\n RS_COLOR_MASK,\n RS_CULL_ENABLED,\n RS_CULL_FACE,\n RS_DEPTH_FUNC,\n RS_DEPTH_MASK,\n RS_DEPTH_TEST,\n RS_DESIRED,\n RS_STENCIL_FUNC_FUNC,\n RS_STENCIL_FUNC_MASK,\n RS_STENCIL_FUNC_REF,\n RS_STENCIL_MASK,\n RS_STENCIL_OP_FAIL,\n RS_STENCIL_OP_ZFAIL,\n RS_STENCIL_OP_ZPASS,\n RS_STENCIL_TEST,\n} from \"./state.js\";\n\n// ── Clear bits (used by `clearEngine`).\n/** GL clear bits. */\nconst COLOR_BUFFER_BIT = 0x4000;\nconst DEPTH_BUFFER_BIT = 0x0100;\nconst STENCIL_BUFFER_BIT = 0x0400;\n\n// ── Test-enable enums for the per-category reconcilers below.\n/** GL `gl.DEPTH_TEST`. */\nconst DEPTH_TEST = 0x0b71;\n/** GL `gl.CULL_FACE`. */\nconst CULL_FACE = 0x0b44;\n/** GL `gl.STENCIL_TEST`. */\nconst STENCIL_TEST = 0x0b90;\n\n// ── Framebuffer / renderbuffer enums (used only by generateRenderTargetStencil).\n// Module-local consts mirror render-target.ts's constant style. Because the\n// render-target core no longer references the stencil/packed enums, they live\n// here and tree-shake away for consumers that never opt into a stencil buffer.\n/** GL `gl.FRAMEBUFFER`. */\nconst FRAMEBUFFER = 0x8d40;\n/** GL `gl.RENDERBUFFER`. */\nconst RENDERBUFFER = 0x8d41;\n/** GL `gl.DEPTH24_STENCIL8` — packed depth+stencil sized format. */\nconst DEPTH24_STENCIL8 = 0x88f0;\n/** GL `gl.STENCIL_INDEX8` — stencil-only sized format. */\nconst STENCIL_INDEX8 = 0x8d48;\n/** GL `gl.DEPTH_STENCIL_ATTACHMENT`. */\nconst DEPTH_STENCIL_ATTACHMENT = 0x821a;\n/** GL `gl.STENCIL_ATTACHMENT`. */\nconst STENCIL_ATTACHMENT = 0x8d20;\n/** GL `gl.DEPTH_ATTACHMENT` — where the render-target core attaches its depth-only\n * renderbuffer (re-established when rolling back a failed stencil attach). */\nconst DEPTH_ATTACHMENT = 0x8d00;\n/** GL `gl.FRAMEBUFFER_COMPLETE`. */\nconst FRAMEBUFFER_COMPLETE = 0x8cd5;\n\n/** Depth-buffer configuration for {@link setDepthState}. Omitted fields are\n * left unchanged. */\nexport interface GLDepthState {\n /** Enable/disable the depth test (`gl.enable/disable(DEPTH_TEST)`). */\n test?: boolean;\n /** Enable/disable depth writes (`gl.depthMask`). */\n write?: boolean;\n /** Depth comparison function (`gl.depthFunc`), e.g. `gl.LESS`. */\n func?: GLenum;\n}\n\n/** Stencil configuration for {@link setStencilState}. Omitted fields are left\n * unchanged. The `func`/`ref`/`funcMask` triple and the\n * `opFail`/`opZFail`/`opZPass` triple are each applied as a unit (any member\n * present re-issues that GL call, merging the unspecified members from cache). */\nexport interface GLStencilState {\n /** Enable/disable the stencil test (`gl.enable/disable(STENCIL_TEST)`). */\n test?: boolean;\n /** Stencil write mask (`gl.stencilMask`). */\n mask?: number;\n /** Comparison function (`gl.stencilFunc` arg 1), e.g. `gl.ALWAYS`. */\n func?: GLenum;\n /** Reference value (`gl.stencilFunc` arg 2). */\n ref?: number;\n /** Comparison mask (`gl.stencilFunc` arg 3). */\n funcMask?: number;\n /** Op when the stencil test fails (`gl.stencilOp` arg 1). */\n opFail?: GLenum;\n /** Op when the stencil test passes but depth fails (`gl.stencilOp` arg 2). */\n opZFail?: GLenum;\n /** Op when both stencil and depth pass (`gl.stencilOp` arg 3). */\n opZPass?: GLenum;\n}\n\n/** Options for {@link clearEngine}. */\nexport interface GLClearOptions {\n /** When set, clears the color buffer to this RGBA color (alpha default 1). */\n color?: { r: number; g: number; b: number; a?: number };\n /** Clear the depth buffer (respects the current depth write mask). */\n depth?: boolean;\n /** Clear the stencil buffer (respects the current stencil write mask). */\n stencil?: boolean;\n}\n\n/**\n * Buffer depth-buffer state (test enable, write mask, comparison function) into\n * the DESIRED state — the lite-gl equivalent of mutating Babylon's\n * `engine.depthCullingState.{depthTest,depthMask,depthFunc}`. Flushed to GL by\n * `applyGLStates` before the next draw / clear; omitted fields are untouched.\n *\n * @param engine - The engine.\n * @param state - The depth fields to change. Omitted fields are untouched.\n */\nexport function setDepthState(engine: GLEngineContext, state: GLDepthState): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const s = engine._state;\n if (state.test !== undefined) {\n s.rs[RS_DEPTH_TEST + RS_DESIRED] = state.test ? 1 : 0;\n }\n if (state.write !== undefined) {\n s.rs[RS_DEPTH_MASK + RS_DESIRED] = state.write ? 1 : 0;\n }\n if (state.func !== undefined) {\n s.rs[RS_DEPTH_FUNC + RS_DESIRED] = state.func;\n }\n s._flushDepthCull = flushDepthCull;\n s.statesDirty = true;\n}\n\n/**\n * Enable/disable face culling and (optionally) set the cull face — the lite-gl\n * equivalent of `engine.depthCullingState.cull` + `cullFace`.\n *\n * @param engine - The engine.\n * @param enabled - Enable (`true`) or disable (`false`) `gl.CULL_FACE`.\n * @param face - Optional cull face (`gl.BACK` / `gl.FRONT` / `gl.FRONT_AND_BACK`).\n */\nexport function setCullState(engine: GLEngineContext, enabled: boolean, face?: GLenum): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const s = engine._state;\n s.rs[RS_CULL_ENABLED + RS_DESIRED] = enabled ? 1 : 0;\n if (face !== undefined) {\n s.rs[RS_CULL_FACE + RS_DESIRED] = face;\n }\n s._flushDepthCull = flushDepthCull;\n s.statesDirty = true;\n}\n\n/**\n * Buffer stencil state (test enable, write mask, comparison func triple, op\n * triple) into the DESIRED state — the lite-gl equivalent of mutating Babylon's\n * `engine.stencilState.*`. Flushed by `applyGLStates` before the next draw /\n * clear; omitted fields are untouched (merge-from-desired).\n *\n * @param engine - The engine.\n * @param state - The stencil fields to change. Omitted fields are untouched.\n */\nexport function setStencilState(engine: GLEngineContext, state: GLStencilState): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const s = engine._state;\n if (state.test !== undefined) {\n s.rs[RS_STENCIL_TEST + RS_DESIRED] = state.test ? 1 : 0;\n }\n if (state.mask !== undefined) {\n s.rs[RS_STENCIL_MASK + RS_DESIRED] = state.mask;\n }\n if (state.func !== undefined) {\n s.rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED] = state.func;\n }\n if (state.ref !== undefined) {\n s.rs[RS_STENCIL_FUNC_REF + RS_DESIRED] = state.ref;\n }\n if (state.funcMask !== undefined) {\n s.rs[RS_STENCIL_FUNC_MASK + RS_DESIRED] = state.funcMask;\n }\n if (state.opFail !== undefined) {\n s.rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = state.opFail;\n }\n if (state.opZFail !== undefined) {\n s.rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = state.opZFail;\n }\n if (state.opZPass !== undefined) {\n s.rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = state.opZPass;\n }\n s._flushStencil = flushStencil;\n s.statesDirty = true;\n}\n\n/**\n * Buffer the color write mask into the DESIRED state — the lite-gl equivalent of\n * Babylon's `setColorWrite` (which passes the same flag to all four channels).\n * Flushed to GL (`gl.colorMask`) by `applyGLStates` before the next draw /\n * clear.\n *\n * @param engine - The engine.\n * @param r - Write red.\n * @param g - Write green.\n * @param b - Write blue.\n * @param a - Write alpha.\n */\nexport function setColorMask(engine: GLEngineContext, r: boolean, g: boolean, b: boolean, a: boolean): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const packed = (r ? 8 : 0) | (g ? 4 : 0) | (b ? 2 : 0) | (a ? 1 : 0);\n const s = engine._state;\n s.rs[RS_COLOR_MASK + RS_DESIRED] = packed;\n s._flushColorMask = flushColorMask;\n s.statesDirty = true;\n}\n\n/**\n * Clear the currently-bound framebuffer's color / depth / stencil buffers — the\n * lite-gl equivalent of Babylon's `clear(color, backBuffer, depth, stencil)`.\n * Depth/stencil clears respect the current write masks (set them first via\n * {@link setDepthState} / {@link setStencilState}). No-op when nothing is\n * requested or the context is lost/disposed.\n *\n * @param engine - The engine.\n * @param options - Which buffers to clear (and the color value).\n */\nexport function clearEngine(engine: GLEngineContext, options: GLClearOptions): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const gl = engine.gl;\n let mask = 0;\n if (options.color !== undefined) {\n const c = options.color;\n const a = c.a ?? 1;\n const rs = engine._state.rs;\n // Cached: gl.clearColor is per-context state GL retains, so only re-issue\n // it when the requested color actually changes (Babylon re-sets it every\n // clear; this elides the redundant JS↔native call for constant backgrounds).\n if (rs[RS_CLEAR_R] !== c.r || rs[RS_CLEAR_G] !== c.g || rs[RS_CLEAR_B] !== c.b || rs[RS_CLEAR_A] !== a) {\n gl.clearColor(c.r, c.g, c.b, a);\n rs[RS_CLEAR_R] = c.r;\n rs[RS_CLEAR_G] = c.g;\n rs[RS_CLEAR_B] = c.b;\n rs[RS_CLEAR_A] = a;\n }\n mask |= COLOR_BUFFER_BIT;\n }\n if (options.depth === true) {\n mask |= DEPTH_BUFFER_BIT;\n }\n if (options.stencil === true) {\n mask |= STENCIL_BUFFER_BIT;\n }\n if (mask !== 0) {\n // Babylon parity: a clear respects the current depth/stencil/color write\n // masks, so flush any deferred state before clearing.\n applyGLStates(engine);\n gl.clear(mask);\n }\n}\n\n/* ─────────────────── deferred render-state reconcilers ──────────────────────\n * The per-category half of `applyGLStates`. Each setter above installs the\n * matching reconciler onto its `_state._flush*` slot; `applyGLStates`\n * (apply-states.ts) dispatches ONLY through those slots. Co-locating them with\n * their setters makes each one reachable solely when its setter is in the bundle,\n * so a scene that never touches depth/cull, stencil, or color-mask tree-shakes\n * the corresponding reconciler — and its GL code — away entirely. Each reconciles\n * its DESIRED (`rs[RS_X + RS_DESIRED]`) slots against the ACTUAL twins (`rs[RS_X]`)\n * and issues only the `gl.*` calls that changed, in the same order and with the\n * same elision rules as the former monolithic flush. */\n\n/** Reconcile depth + cull (Babylon's `_depthCullingState.apply`). @internal */\nfunction flushDepthCull(engine: GLEngineContext): void {\n const gl = engine.gl;\n const rs = engine._state.rs;\n const dTest = rs[RS_DEPTH_TEST + RS_DESIRED]!;\n if (dTest !== rs[RS_DEPTH_TEST]) {\n rs[RS_DEPTH_TEST] = dTest;\n if (dTest === 1) {\n gl.enable(DEPTH_TEST);\n } else {\n gl.disable(DEPTH_TEST);\n }\n }\n const dMask = rs[RS_DEPTH_MASK + RS_DESIRED]!;\n if (dMask !== rs[RS_DEPTH_MASK]) {\n rs[RS_DEPTH_MASK] = dMask;\n gl.depthMask(dMask === 1);\n }\n const dFunc = rs[RS_DEPTH_FUNC + RS_DESIRED]!;\n if (dFunc !== rs[RS_DEPTH_FUNC]) {\n rs[RS_DEPTH_FUNC] = dFunc;\n gl.depthFunc(dFunc);\n }\n const dCull = rs[RS_CULL_ENABLED + RS_DESIRED]!;\n if (dCull !== rs[RS_CULL_ENABLED]) {\n rs[RS_CULL_ENABLED] = dCull;\n if (dCull === 1) {\n gl.enable(CULL_FACE);\n } else {\n gl.disable(CULL_FACE);\n }\n }\n const dCullFace = rs[RS_CULL_FACE + RS_DESIRED]!;\n if (dCullFace !== rs[RS_CULL_FACE]) {\n rs[RS_CULL_FACE] = dCullFace;\n gl.cullFace(dCullFace);\n }\n}\n\n/** Reconcile the stencil test / mask / func-triple / op-triple (Babylon's\n * `_stencilState.apply`); each triple is issued as a unit. @internal */\nfunction flushStencil(engine: GLEngineContext): void {\n const gl = engine.gl;\n const rs = engine._state.rs;\n const dTest = rs[RS_STENCIL_TEST + RS_DESIRED]!;\n if (dTest !== rs[RS_STENCIL_TEST]) {\n rs[RS_STENCIL_TEST] = dTest;\n if (dTest === 1) {\n gl.enable(STENCIL_TEST);\n } else {\n gl.disable(STENCIL_TEST);\n }\n }\n const dMask = rs[RS_STENCIL_MASK + RS_DESIRED]!;\n if (dMask !== rs[RS_STENCIL_MASK]) {\n rs[RS_STENCIL_MASK] = dMask;\n gl.stencilMask(dMask);\n }\n const dFuncFunc = rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED]!;\n const dFuncRef = rs[RS_STENCIL_FUNC_REF + RS_DESIRED]!;\n const dFuncMask = rs[RS_STENCIL_FUNC_MASK + RS_DESIRED]!;\n if (dFuncFunc !== rs[RS_STENCIL_FUNC_FUNC] || dFuncRef !== rs[RS_STENCIL_FUNC_REF] || dFuncMask !== rs[RS_STENCIL_FUNC_MASK]) {\n rs[RS_STENCIL_FUNC_FUNC] = dFuncFunc;\n rs[RS_STENCIL_FUNC_REF] = dFuncRef;\n rs[RS_STENCIL_FUNC_MASK] = dFuncMask;\n gl.stencilFunc(dFuncFunc, dFuncRef, dFuncMask);\n }\n const dOpFail = rs[RS_STENCIL_OP_FAIL + RS_DESIRED]!;\n const dOpZFail = rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED]!;\n const dOpZPass = rs[RS_STENCIL_OP_ZPASS + RS_DESIRED]!;\n if (dOpFail !== rs[RS_STENCIL_OP_FAIL] || dOpZFail !== rs[RS_STENCIL_OP_ZFAIL] || dOpZPass !== rs[RS_STENCIL_OP_ZPASS]) {\n rs[RS_STENCIL_OP_FAIL] = dOpFail;\n rs[RS_STENCIL_OP_ZFAIL] = dOpZFail;\n rs[RS_STENCIL_OP_ZPASS] = dOpZPass;\n gl.stencilOp(dOpFail, dOpZFail, dOpZPass);\n }\n}\n\n/** Reconcile the packed color-write mask (Babylon's `setColorWrite`). @internal */\nfunction flushColorMask(engine: GLEngineContext): void {\n const rs = engine._state.rs;\n const dColorMask = rs[RS_COLOR_MASK + RS_DESIRED]!;\n if (dColorMask !== rs[RS_COLOR_MASK]) {\n rs[RS_COLOR_MASK] = dColorMask;\n engine.gl.colorMask((dColorMask & 8) !== 0, (dColorMask & 4) !== 0, (dColorMask & 2) !== 0, (dColorMask & 1) !== 0);\n }\n}\n\n/**\n * Opt-in: give a `/render-target` {@link GLRenderTarget} a stencil attachment,\n * replacing the core's depth-only `DEPTH_COMPONENT16` renderbuffer with either a\n * packed **`DEPTH24_STENCIL8`** buffer (default — depth *and* stencil) or a\n * stencil-only **`STENCIL_INDEX8`** buffer.\n *\n * Stencil is intentionally NOT a {@link createRenderTarget} option: keeping this\n * helper in the depth-stencil module means the stencil/packed renderbuffer\n * code tree-shakes out of every bundle that only needs a color (and optional\n * depth) target.\n *\n * The attachment is **restore-correct**: it is rebuilt automatically — at the new\n * size on {@link resizeRenderTarget}, and into the fresh framebuffer after a\n * `webglcontextrestored` event — so the stencil survives for the life of the\n * target, and {@link disposeRenderTarget} releases it along with the target.\n *\n * No-op on a lost/disposed context or a disposed target.\n *\n * @param engine - The engine that owns `rt`.\n * @param rt - The render target to attach the stencil buffer to.\n * @param options - `depth` (default `true`): when `true` the attachment is a\n * packed depth+stencil buffer (`DEPTH24_STENCIL8` on `DEPTH_STENCIL_ATTACHMENT`)\n * — the common case, and the correct choice when the target was created with\n * `generateDepthBuffer: true`. When `false` the attachment is stencil-only\n * (`STENCIL_INDEX8` on `STENCIL_ATTACHMENT`).\n * @throws If a renderbuffer handle could not be allocated or the framebuffer is\n * incomplete after attaching.\n */\nexport function generateRenderTargetStencil(engine: GLEngineContext, rt: GLRenderTarget, options?: { depth?: boolean }): void {\n if (engine._isLost || engine._disposed || rt._disposed) {\n return;\n }\n const packDepth = options?.depth ?? true;\n const attachment = packDepth ? DEPTH_STENCIL_ATTACHMENT : STENCIL_ATTACHMENT;\n const format = packDepth ? DEPTH24_STENCIL8 : STENCIL_INDEX8;\n\n const build = (e: GLEngineContext): void => {\n const gl = e.gl;\n // Capture the caller's draw target so this helper is STATE-NEUTRAL: it must\n // not silently redirect subsequent draws to `rt` (during an internal\n // rebuild `prevFb` is `rt._framebuffer`, which the core re-checks next).\n const prevFb = e._state.boundFramebuffer;\n const newRb = gl.createRenderbuffer();\n if (newRb === null) {\n throw new Error(\"lite-gl: gl.createRenderbuffer returned null (render target stencil)\");\n }\n let committed = false;\n try {\n gl.bindFramebuffer(FRAMEBUFFER, rt._framebuffer);\n e._state.boundFramebuffer = rt._framebuffer;\n gl.bindRenderbuffer(RENDERBUFFER, newRb);\n gl.renderbufferStorage(RENDERBUFFER, format, rt.width, rt.height);\n gl.framebufferRenderbuffer(FRAMEBUFFER, attachment, RENDERBUFFER, newRb);\n gl.bindRenderbuffer(RENDERBUFFER, null);\n const status = gl.checkFramebufferStatus(FRAMEBUFFER);\n if (status !== FRAMEBUFFER_COMPLETE) {\n throw new Error(`lite-gl: render target framebuffer incomplete after stencil attach (status 0x${status.toString(16)})`);\n }\n // Commit only after a complete attachment: release the buffer we\n // replaced (the core depth-only one, or our own from a prior rebuild).\n if (rt._depthStencil !== null) {\n gl.deleteRenderbuffer(rt._depthStencil);\n }\n rt._depthStencil = newRb;\n committed = true;\n } finally {\n if (!committed) {\n // Any non-committed exit (incomplete framebuffer OR an unexpected\n // GL throw): detach + delete the buffer we couldn't adopt so it\n // never leaks, leaving the attachment point empty for the\n // caller-level rollback to re-establish the prior buffer.\n gl.framebufferRenderbuffer(FRAMEBUFFER, attachment, RENDERBUFFER, null);\n gl.deleteRenderbuffer(newRb);\n }\n // Restore the caller's draw target + the bound-framebuffer cache.\n if (e._state.boundFramebuffer !== prevFb) {\n gl.bindFramebuffer(FRAMEBUFFER, prevFb);\n e._state.boundFramebuffer = prevFb;\n }\n }\n };\n\n // Build once now, but COMMIT the resize/restore hook only if it succeeds — a\n // failed opt-in must leave the target exactly as it was. The packed attach\n // above can clear the core `DEPTH_ATTACHMENT`, so on failure restore the prior\n // hook AND re-establish the prior depth/stencil attachment (the prior hook's,\n // or the core depth-only buffer).\n const prevHook = rt._rebuildDepthStencil;\n const prevDepthStencil = rt._depthStencil;\n try {\n build(engine);\n } catch (err) {\n rt._rebuildDepthStencil = prevHook;\n try {\n if (prevHook !== undefined) {\n prevHook(engine);\n } else if (prevDepthStencil !== null) {\n reattachCoreDepthBuffer(engine, rt, prevDepthStencil);\n }\n } catch {\n // Best-effort restore; surface the original failure below.\n }\n throw err;\n }\n rt._rebuildDepthStencil = build;\n}\n\n/**\n * Re-attach a core depth-only renderbuffer at `DEPTH_ATTACHMENT` — used to roll a\n * render target back when a packed {@link generateRenderTargetStencil} attach\n * fails completeness (attaching at `DEPTH_STENCIL_ATTACHMENT` clears the core\n * `DEPTH_ATTACHMENT`). State-neutral: restores the caller's bound framebuffer.\n * @internal\n */\nfunction reattachCoreDepthBuffer(engine: GLEngineContext, rt: GLRenderTarget, depthBuffer: WebGLRenderbuffer): void {\n const gl = engine.gl;\n const prevFb = engine._state.boundFramebuffer;\n gl.bindFramebuffer(FRAMEBUFFER, rt._framebuffer);\n engine._state.boundFramebuffer = rt._framebuffer;\n gl.framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, depthBuffer);\n if (engine._state.boundFramebuffer !== prevFb) {\n gl.bindFramebuffer(FRAMEBUFFER, prevFb);\n engine._state.boundFramebuffer = prevFb;\n }\n}\n"]}
1
+ {"version":3,"file":"depth-stencil.js","sourceRoot":"","sources":["../src/depth-stencil.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EACH,UAAU,EACV,UAAU,EACV,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,GAClB,MAAM,YAAY,CAAC;AAEpB,yCAAyC;AACzC,qBAAqB;AACrB,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,+DAA+D;AAC/D,0BAA0B;AAC1B,MAAM,UAAU,GAAG,MAAM,CAAC;AAC1B,yBAAyB;AACzB,MAAM,SAAS,GAAG,MAAM,CAAC;AACzB,4BAA4B;AAC5B,MAAM,YAAY,GAAG,MAAM,CAAC;AAC5B,oBAAoB;AACpB,MAAM,IAAI,GAAG,MAAM,CAAC;AACpB,qBAAqB;AACrB,MAAM,KAAK,GAAG,MAAM,CAAC;AACrB,oBAAoB;AACpB,MAAM,IAAI,GAAG,MAAM,CAAC;AACpB,8BAA8B;AAC9B,MAAM,cAAc,GAAG,MAAM,CAAC;AAC9B,6EAA6E;AAC7E,8DAA8D;AAC9D,MAAM,YAAY,GAAG,EAAE,CAAC;AACxB,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,kFAAkF;AAClF,4EAA4E;AAC5E,8EAA8E;AAC9E,+EAA+E;AAC/E,2BAA2B;AAC3B,MAAM,WAAW,GAAG,MAAM,CAAC;AAC3B,4BAA4B;AAC5B,MAAM,YAAY,GAAG,MAAM,CAAC;AAC5B,oEAAoE;AACpE,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,0DAA0D;AAC1D,MAAM,cAAc,GAAG,MAAM,CAAC;AAC9B,wCAAwC;AACxC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,kCAAkC;AAClC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC;+EAC+E;AAC/E,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,oCAAoC;AACpC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AA8CpC,mEAAmE;AACnE,SAAS,+BAA+B,CAAC,EAAgB;IACrD,IAAI,EAAE,CAAC,kBAAkB,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAC7C,OAAO;IACX,CAAC;IACD,EAAE,CAAC,kBAAkB,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;IAC3C,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;IAC5C,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;AAChD,CAAC;AAED,SAAS,oBAAoB,CAAC,EAAgB,EAAE,KAAuB;IACnE,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;QACjE,OAAO;IACX,CAAC;IACD,+BAA+B,CAAC,EAAE,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7B,EAAE,CAAC,kBAAkB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACvD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9B,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IACzD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9B,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IACzD,CAAC;AACL,CAAC;AAED,mEAAmE;AACnE,SAAS,aAAa,CAAC,EAAgB,EAAE,OAAe,EAAE,IAAY,EAAE,KAAa,EAAE,KAAa;IAChG,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IACnB,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;IACxB,EAAE,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;AAC5B,CAAC;AAED,SAAS,gBAAgB,CAAC,EAAgB;IACtC,+BAA+B,CAAC,EAAE,CAAC,CAAC;IACpC,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,EAAE,CAAC,kBAAkB,CAAE,EAAE,EAAE,CAAC,mBAAmB,CAAE,EAAE,EAAE,CAAC,mBAAmB,CAAE,CAAC,CAAC;IAC7G,aAAa,CAAC,EAAE,EAAE,YAAY,GAAG,YAAY,EAAE,EAAE,CAAC,kBAAkB,GAAG,UAAU,CAAE,EAAE,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAE,EAAE,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAE,CAAC,CAAC;AACvK,CAAC;AAED,SAAS,iCAAiC,CAAC,EAAgB;IACvD,IAAI,EAAE,CAAC,kBAAkB,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAC7C,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;AACL,CAAC;AAED,SAAS,wBAAwB,CAAC,EAAgB,EAAE,KAAuB;IACvE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7B,EAAE,CAAC,YAAY,GAAG,YAAY,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;IACnD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9B,EAAE,CAAC,aAAa,GAAG,YAAY,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IACrD,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9B,EAAE,CAAC,aAAa,GAAG,YAAY,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;IACrD,CAAC;AACL,CAAC;AAED,iFAAiF;AACjF,SAAS,4BAA4B,CAAC,CAA4B,EAAE,KAAuB;IACvF,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;QACjE,OAAO;IACX,CAAC;IACD,iCAAiC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxC,wBAAwB,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;AAC1C,CAAC;AAED,qEAAqE;AACrE,SAAS,qBAAqB,CAAC,CAA4B;IACvD,IAAI,CAAC,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO;IACX,CAAC;IACD,MAAM,EAAE,GAAG,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC;IAChC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACb,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;IACV,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC,CAAC,eAAe,GAAG,4BAA4B,CAAC;IACjD,CAAC,CAAC,aAAa,GAAG,oBAAoB,CAAC;AAC3C,CAAC;AAYD;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,MAAuB,EAAE,KAAmB;IACtE,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO;IACX,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC5B,CAAC,CAAC,EAAE,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;IAClD,CAAC;IACD,CAAC,CAAC,eAAe,GAAG,cAAc,CAAC;IACnC,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC;AACzB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,MAAuB,EAAE,OAAgB,EAAE,IAAa;IACjF,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO;IACX,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,CAAC,CAAC,EAAE,CAAC,eAAe,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACrB,CAAC,CAAC,EAAE,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;IAC3C,CAAC;IACD,CAAC,CAAC,eAAe,GAAG,cAAc,CAAC;IACnC,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC;AACzB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,MAAuB,EAAE,KAAqB;IAC1E,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO;IACX,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC,eAAe,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC,oBAAoB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;IACzD,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC1B,CAAC,CAAC,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC;IACvD,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/B,CAAC,CAAC,EAAE,CAAC,oBAAoB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC7D,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC,eAAe,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC;IACpD,CAAC;IACD,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;QACjE,+BAA+B,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,CAAC,CAAC,EAAE,CAAC,kBAAkB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;QACzD,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC9B,CAAC,CAAC,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;QAC3D,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC9B,CAAC,CAAC,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;QAC3D,CAAC;IACL,CAAC;IACD,CAAC,CAAC,aAAa,KAAK,YAAY,CAAC;IACjC,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC;AACzB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAuB,EAAE,IAAY,EAAE,KAAuB;IAC/F,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO;IACX,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;QACjE,OAAO;IACX,CAAC;IACD,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACzB,iCAAiC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAChB,oBAAoB,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACjB,wBAAwB,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IACD,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC;AACzB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,MAAuB,EAAE,CAAU,EAAE,CAAU,EAAE,CAAU,EAAE,CAAU;IAChG,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO;IACX,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,CAAC,CAAC,EAAE,CAAC,aAAa,GAAG,UAAU,CAAC,GAAG,MAAM,CAAC;IAC1C,CAAC,CAAC,eAAe,GAAG,cAAc,CAAC;IACnC,CAAC,CAAC,WAAW,GAAG,IAAI,CAAC;AACzB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,MAAuB,EAAE,OAAuB;IACxE,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrC,OAAO;IACX,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;IACrB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QACxB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACnB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,0EAA0E;QAC1E,yEAAyE;QACzE,6EAA6E;QAC7E,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACrG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAChC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACrB,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACrB,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACrB,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,IAAI,gBAAgB,CAAC;IAC7B,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QACzB,IAAI,IAAI,gBAAgB,CAAC;IAC7B,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC3B,IAAI,IAAI,kBAAkB,CAAC;IAC/B,CAAC;IACD,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACb,yEAAyE;QACzE,sDAAsD;QACtD,aAAa,CAAC,MAAM,CAAC,CAAC;QACtB,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;AACL,CAAC;AAED;;;;;;;;;wDASwD;AAExD,+EAA+E;AAC/E,SAAS,cAAc,CAAC,MAAuB;IAC3C,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;IACrB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,EAAE,CAAC,aAAa,GAAG,UAAU,CAAE,CAAC;IAC9C,IAAI,KAAK,KAAK,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC;QAC1B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YACd,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACJ,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC;IACD,MAAM,KAAK,GAAG,EAAE,CAAC,aAAa,GAAG,UAAU,CAAE,CAAC;IAC9C,IAAI,KAAK,KAAK,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC;QAC1B,EAAE,CAAC,SAAS,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;IAC9B,CAAC;IACD,MAAM,KAAK,GAAG,EAAE,CAAC,aAAa,GAAG,UAAU,CAAE,CAAC;IAC9C,IAAI,KAAK,KAAK,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC;QAC1B,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IACD,MAAM,KAAK,GAAG,EAAE,CAAC,eAAe,GAAG,UAAU,CAAE,CAAC;IAChD,IAAI,KAAK,KAAK,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QAChC,EAAE,CAAC,eAAe,CAAC,GAAG,KAAK,CAAC;QAC5B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YACd,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACJ,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;IACL,CAAC;IACD,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,GAAG,UAAU,CAAE,CAAC;IACjD,IAAI,SAAS,KAAK,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,EAAE,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC;QAC7B,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;AACL,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAuB;IAC/C,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;IACrB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,EAAE,CAAC,eAAe,GAAG,UAAU,CAAE,CAAC;IAChD,IAAI,KAAK,KAAK,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QAChC,EAAE,CAAC,eAAe,CAAC,GAAG,KAAK,CAAC;QAC5B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YACd,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC5B,CAAC;aAAM,CAAC;YACJ,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC7B,CAAC;IACL,CAAC;IACD,MAAM,KAAK,GAAG,EAAE,CAAC,eAAe,GAAG,UAAU,CAAE,CAAC;IAChD,IAAI,KAAK,KAAK,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QAChC,EAAE,CAAC,eAAe,CAAC,GAAG,KAAK,CAAC;QAC5B,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IACD,MAAM,SAAS,GAAG,EAAE,CAAC,oBAAoB,GAAG,UAAU,CAAE,CAAC;IACzD,MAAM,QAAQ,GAAG,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAE,CAAC;IACvD,MAAM,SAAS,GAAG,EAAE,CAAC,oBAAoB,GAAG,UAAU,CAAE,CAAC;IACzD,IAAI,SAAS,KAAK,EAAE,CAAC,oBAAoB,CAAC,IAAI,QAAQ,KAAK,EAAE,CAAC,mBAAmB,CAAC,IAAI,SAAS,KAAK,EAAE,CAAC,oBAAoB,CAAC,EAAE,CAAC;QAC3H,EAAE,CAAC,oBAAoB,CAAC,GAAG,SAAS,CAAC;QACrC,EAAE,CAAC,mBAAmB,CAAC,GAAG,QAAQ,CAAC;QACnC,EAAE,CAAC,oBAAoB,CAAC,GAAG,SAAS,CAAC;QACrC,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACnD,CAAC;AACL,CAAC;AAED,4DAA4D;AAC5D,SAAS,YAAY,CAAC,MAAuB;IACzC,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;IACrB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,EAAE,CAAC,kBAAkB,GAAG,UAAU,CAAE,CAAC;IACzD,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,OAAO;IACX,CAAC;IACD,MAAM,YAAY,GAAG,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAE,CAAC;IAC3D,MAAM,YAAY,GAAG,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAE,CAAC;IAC3D,MAAM,YAAY,GAAG,WAAW,KAAK,EAAE,CAAC,kBAAkB,CAAC,IAAI,YAAY,KAAK,EAAE,CAAC,mBAAmB,CAAC,IAAI,YAAY,KAAK,EAAE,CAAC,mBAAmB,CAAC,CAAC;IACpJ,IAAI,YAAY,EAAE,CAAC;QACf,EAAE,CAAC,kBAAkB,CAAC,GAAG,WAAW,CAAC;QACrC,EAAE,CAAC,mBAAmB,CAAC,GAAG,YAAY,CAAC;QACvC,EAAE,CAAC,mBAAmB,CAAC,GAAG,YAAY,CAAC;QACvC,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IAC1D,CAAC;AACL,CAAC;AAED,0EAA0E;AAC1E,SAAS,oBAAoB,CAAC,MAAuB;IACjD,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;IACrB,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAC5B,MAAM,WAAW,GAAG,EAAE,CAAC,kBAAkB,GAAG,UAAU,CAAE,CAAC;IACzD,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,OAAO;IACX,CAAC;IACD,MAAM,YAAY,GAAG,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAE,CAAC;IAC3D,MAAM,YAAY,GAAG,EAAE,CAAC,mBAAmB,GAAG,UAAU,CAAE,CAAC;IAC3D,MAAM,UAAU,GAAG,EAAE,CAAC,YAAY,GAAG,YAAY,CAAE,CAAC;IACpD,MAAM,WAAW,GAAG,EAAE,CAAC,aAAa,GAAG,YAAY,CAAE,CAAC;IACtD,MAAM,WAAW,GAAG,EAAE,CAAC,aAAa,GAAG,YAAY,CAAE,CAAC;IACtD,MAAM,YAAY,GAAG,WAAW,KAAK,EAAE,CAAC,kBAAkB,CAAC,IAAI,YAAY,KAAK,EAAE,CAAC,mBAAmB,CAAC,IAAI,YAAY,KAAK,EAAE,CAAC,mBAAmB,CAAC,CAAC;IACpJ,MAAM,WAAW,GAAG,UAAU,KAAK,EAAE,CAAC,YAAY,CAAC,IAAI,WAAW,KAAK,EAAE,CAAC,aAAa,CAAC,IAAI,WAAW,KAAK,EAAE,CAAC,aAAa,CAAC,CAAC;IAC9H,IAAI,CAAC,YAAY,IAAI,CAAC,WAAW,EAAE,CAAC;QAChC,OAAO;IACX,CAAC;IACD,MAAM,UAAU,GAAG,WAAW,KAAK,UAAU,IAAI,YAAY,KAAK,WAAW,IAAI,YAAY,KAAK,WAAW,CAAC;IAC9G,IAAI,UAAU,EAAE,CAAC;QACb,aAAa,CAAC,EAAE,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QAC/E,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QACtE,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QACtD,OAAO;IACX,CAAC;IACD,IAAI,YAAY,EAAE,CAAC;QACf,aAAa,CAAC,EAAE,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;QAC/E,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,WAAW,EAAE,CAAC;QACd,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QACtE,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;IACrE,CAAC;AACL,CAAC;AAED,mFAAmF;AACnF,SAAS,cAAc,CAAC,MAAuB;IAC3C,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,GAAG,UAAU,CAAE,CAAC;IACnD,IAAI,UAAU,KAAK,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;QACnC,EAAE,CAAC,aAAa,CAAC,GAAG,UAAU,CAAC;QAC/B,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACxH,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAuB,EAAE,EAAkB,EAAE,OAA6B;IAClH,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,SAAS,EAAE,CAAC;QACrD,OAAO;IACX,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC;IACzC,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,kBAAkB,CAAC;IAC7E,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,cAAc,CAAC;IAE7D,MAAM,KAAK,GAAG,CAAC,CAAkB,EAAQ,EAAE;QACvC,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;QAChB,4EAA4E;QAC5E,qEAAqE;QACrE,yEAAyE;QACzE,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC;QACzC,MAAM,KAAK,GAAG,EAAE,CAAC,kBAAkB,EAAE,CAAC;QACtC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC;YACD,EAAE,CAAC,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;YACjD,CAAC,CAAC,MAAM,CAAC,gBAAgB,GAAG,EAAE,CAAC,YAAY,CAAC;YAC5C,EAAE,CAAC,gBAAgB,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YACzC,EAAE,CAAC,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;YAClE,EAAE,CAAC,uBAAuB,CAAC,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;YACzE,EAAE,CAAC,gBAAgB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;YACxC,MAAM,MAAM,GAAG,EAAE,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC;YACtD,IAAI,MAAM,KAAK,oBAAoB,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,gFAAgF,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;YAC5H,CAAC;YACD,iEAAiE;YACjE,uEAAuE;YACvE,IAAI,EAAE,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;gBAC5B,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;YAC5C,CAAC;YACD,EAAE,CAAC,aAAa,GAAG,KAAK,CAAC;YACzB,SAAS,GAAG,IAAI,CAAC;QACrB,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,SAAS,EAAE,CAAC;gBACb,kEAAkE;gBAClE,gEAAgE;gBAChE,0DAA0D;gBAC1D,0DAA0D;gBAC1D,EAAE,CAAC,uBAAuB,CAAC,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;gBACxE,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC;YACD,kEAAkE;YAClE,IAAI,CAAC,CAAC,MAAM,CAAC,gBAAgB,KAAK,MAAM,EAAE,CAAC;gBACvC,EAAE,CAAC,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;gBACxC,CAAC,CAAC,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC;YACvC,CAAC;QACL,CAAC;IACL,CAAC,CAAC;IAEF,6EAA6E;IAC7E,2EAA2E;IAC3E,+EAA+E;IAC/E,8EAA8E;IAC9E,kCAAkC;IAClC,MAAM,QAAQ,GAAG,EAAE,CAAC,oBAAoB,CAAC;IACzC,MAAM,gBAAgB,GAAG,EAAE,CAAC,aAAa,CAAC;IAC1C,IAAI,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,EAAE,CAAC,oBAAoB,GAAG,QAAQ,CAAC;QACnC,IAAI,CAAC;YACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACzB,QAAQ,CAAC,MAAM,CAAC,CAAC;YACrB,CAAC;iBAAM,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;gBACnC,uBAAuB,CAAC,MAAM,EAAE,EAAE,EAAE,gBAAgB,CAAC,CAAC;YAC1D,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,2DAA2D;QAC/D,CAAC;QACD,MAAM,GAAG,CAAC;IACd,CAAC;IACD,EAAE,CAAC,oBAAoB,GAAG,KAAK,CAAC;AACpC,CAAC;AAED;;;;;;GAMG;AACH,SAAS,uBAAuB,CAAC,MAAuB,EAAE,EAAkB,EAAE,WAA8B;IACxG,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC;IACrB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAC9C,EAAE,CAAC,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;IACjD,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,EAAE,CAAC,YAAY,CAAC;IACjD,EAAE,CAAC,uBAAuB,CAAC,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;IACrF,IAAI,MAAM,CAAC,MAAM,CAAC,gBAAgB,KAAK,MAAM,EAAE,CAAC;QAC5C,EAAE,CAAC,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC;IAC5C,CAAC;AACL,CAAC","sourcesContent":["/**\n * Depth, stencil, color-mask and clear state — the lite-gl counterpart of\n * Babylon's `_depthCullingState` / `_stencilState` / `setColorWrite` / `clear`.\n *\n * Like Babylon, these setters are DEFERRED: they buffer the requested values\n * into the DESIRED (`d*`) mirror fields of `GLState` and raise `statesDirty`,\n * issuing NO `gl.*` calls themselves. `applyGLStates` (apply-states.ts) flushes\n * the diff to GL right before each draw (and before `clearEngine`'s `gl.clear`,\n * since a clear respects the current write masks). Every field is reconciled\n * independently, so a flush that changes only one sub-state (e.g. just the\n * stencil op triple) issues only that GL call. Omitted setter fields leave the\n * corresponding desired value untouched (merge-from-desired).\n *\n * All setters are no-ops on a lost/disposed context.\n *\n * This module also hosts {@link generateRenderTargetStencil} — the tree-shakeable\n * opt-in that gives a `/render-target` {@link GLRenderTarget} a stencil (or packed\n * depth+stencil) attachment. Keeping the STENCIL_INDEX8 / DEPTH24_STENCIL8\n * renderbuffer code here (rather than in the render-target core) means a consumer\n * that only needs a depth buffer never ships it. The one-way type import below\n * (`depth-stencil` importing `render-target`) introduces NO cycle: render-target\n * must not import this module.\n */\nimport type { GLEngineContext } from \"./context.js\";\nimport type { GLRenderTarget } from \"./render-target.js\";\nimport { applyGLStates } from \"./apply-states.js\";\nimport {\n RS_CLEAR_A,\n RS_CLEAR_B,\n RS_CLEAR_G,\n RS_CLEAR_R,\n RS_COLOR_MASK,\n RS_CULL_ENABLED,\n RS_CULL_FACE,\n RS_DEPTH_FUNC,\n RS_DEPTH_MASK,\n RS_DEPTH_TEST,\n RS_DESIRED,\n RS_STENCIL_FUNC_FUNC,\n RS_STENCIL_FUNC_MASK,\n RS_STENCIL_FUNC_REF,\n RS_STENCIL_MASK,\n RS_STENCIL_OP_FAIL,\n RS_STENCIL_OP_ZFAIL,\n RS_STENCIL_OP_ZPASS,\n RS_STENCIL_TEST,\n} from \"./state.js\";\n\n// ── Clear bits (used by `clearEngine`).\n/** GL clear bits. */\nconst COLOR_BUFFER_BIT = 0x4000;\nconst DEPTH_BUFFER_BIT = 0x0100;\nconst STENCIL_BUFFER_BIT = 0x0400;\n\n// ── Test-enable enums for the per-category reconcilers below.\n/** GL `gl.DEPTH_TEST`. */\nconst DEPTH_TEST = 0x0b71;\n/** GL `gl.CULL_FACE`. */\nconst CULL_FACE = 0x0b44;\n/** GL `gl.STENCIL_TEST`. */\nconst STENCIL_TEST = 0x0b90;\n/** GL `gl.KEEP`. */\nconst KEEP = 0x1e00;\n/** GL `gl.FRONT`. */\nconst FRONT = 0x0404;\n/** GL `gl.BACK`. */\nconst BACK = 0x0405;\n/** GL `gl.FRONT_AND_BACK`. */\nconst FRONT_AND_BACK = 0x0408;\n// Two-sided mode appends actual slots 46..48 and desired slots 49..51 to the\n// compact 46-slot state array. Base indices remain unchanged.\nconst BACK_OP_FAIL = 46;\nconst BACK_OP_ZFAIL = 47;\nconst BACK_OP_ZPASS = 48;\nconst BACK_DESIRED = 3;\n\n// ── Framebuffer / renderbuffer enums (used only by generateRenderTargetStencil).\n// Module-local consts mirror render-target.ts's constant style. Because the\n// render-target core no longer references the stencil/packed enums, they live\n// here and tree-shake away for consumers that never opt into a stencil buffer.\n/** GL `gl.FRAMEBUFFER`. */\nconst FRAMEBUFFER = 0x8d40;\n/** GL `gl.RENDERBUFFER`. */\nconst RENDERBUFFER = 0x8d41;\n/** GL `gl.DEPTH24_STENCIL8` — packed depth+stencil sized format. */\nconst DEPTH24_STENCIL8 = 0x88f0;\n/** GL `gl.STENCIL_INDEX8` — stencil-only sized format. */\nconst STENCIL_INDEX8 = 0x8d48;\n/** GL `gl.DEPTH_STENCIL_ATTACHMENT`. */\nconst DEPTH_STENCIL_ATTACHMENT = 0x821a;\n/** GL `gl.STENCIL_ATTACHMENT`. */\nconst STENCIL_ATTACHMENT = 0x8d20;\n/** GL `gl.DEPTH_ATTACHMENT` — where the render-target core attaches its depth-only\n * renderbuffer (re-established when rolling back a failed stencil attach). */\nconst DEPTH_ATTACHMENT = 0x8d00;\n/** GL `gl.FRAMEBUFFER_COMPLETE`. */\nconst FRAMEBUFFER_COMPLETE = 0x8cd5;\n\n/** Depth-buffer configuration for {@link setDepthState}. Omitted fields are\n * left unchanged. */\nexport interface GLDepthState {\n /** Enable/disable the depth test (`gl.enable/disable(DEPTH_TEST)`). */\n test?: boolean;\n /** Enable/disable depth writes (`gl.depthMask`). */\n write?: boolean;\n /** Depth comparison function (`gl.depthFunc`), e.g. `gl.LESS`. */\n func?: GLenum;\n}\n\n/** Stencil configuration for {@link setStencilState}. Omitted fields are left\n * unchanged. Operation fields update both front and back faces; use\n * {@link setStencilOpSeparate} for per-face values. */\nexport interface GLStencilState {\n /** Enable/disable the stencil test (`gl.enable/disable(STENCIL_TEST)`). */\n test?: boolean;\n /** Comparison function (`gl.stencilFunc` arg 1), e.g. `gl.ALWAYS`. */\n func?: GLenum;\n /** Reference value (`gl.stencilFunc` arg 2). */\n ref?: number;\n /** Comparison mask (`gl.stencilFunc` arg 3). */\n funcMask?: number;\n /** Stencil write mask (`gl.stencilMask`). */\n mask?: number;\n /** Op when the stencil test fails (`gl.stencilOp` arg 1). */\n opFail?: GLenum;\n /** Op when the stencil test passes but depth fails (`gl.stencilOp` arg 2). */\n opZFail?: GLenum;\n /** Op when both stencil and depth pass (`gl.stencilOp` arg 3). */\n opZPass?: GLenum;\n}\n\n/** Per-face stencil operations for {@link setStencilOpSeparate}. Omitted fields\n * retain that face's desired value. */\nexport interface GLStencilOpState {\n /** Op when the stencil test fails (`gl.stencilOpSeparate` arg 2). */\n opFail?: GLenum;\n /** Op when the stencil test passes but depth fails (`gl.stencilOpSeparate` arg 3). */\n opZFail?: GLenum;\n /** Op when both stencil and depth pass (`gl.stencilOpSeparate` arg 4). */\n opZPass?: GLenum;\n}\n\n/** Seed the shared desired op tuple with WebGL's KEEP defaults. */\nfunction ensureStencilOpCacheInitialized(rs: Float64Array): void {\n if (rs[RS_STENCIL_OP_FAIL + RS_DESIRED] !== -1) {\n return;\n }\n rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = KEEP;\n rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = KEEP;\n rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = KEEP;\n}\n\nfunction setDesiredStencilOps(rs: Float64Array, state: GLStencilOpState): void {\n if ((state.opFail ?? state.opZFail ?? state.opZPass) === undefined) {\n return;\n }\n ensureStencilOpCacheInitialized(rs);\n if (state.opFail !== undefined) {\n rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = state.opFail;\n }\n if (state.opZFail !== undefined) {\n rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = state.opZFail;\n }\n if (state.opZPass !== undefined) {\n rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = state.opZPass;\n }\n}\n\n/** Store one complete GL operation tuple starting at `opIndex`. */\nfunction setStencilOps(rs: Float64Array, opIndex: number, fail: GLenum, zfail: GLenum, zpass: GLenum): void {\n rs[opIndex] = fail;\n rs[opIndex + 1] = zfail;\n rs[opIndex + 2] = zpass;\n}\n\nfunction resetStencilBack(rs: Float64Array): void {\n ensureStencilOpCacheInitialized(rs);\n setStencilOps(rs, BACK_OP_FAIL, rs[RS_STENCIL_OP_FAIL]!, rs[RS_STENCIL_OP_ZFAIL]!, rs[RS_STENCIL_OP_ZPASS]!);\n setStencilOps(rs, BACK_OP_FAIL + BACK_DESIRED, rs[RS_STENCIL_OP_FAIL + RS_DESIRED]!, rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED]!, rs[RS_STENCIL_OP_ZPASS + RS_DESIRED]!);\n}\n\nfunction ensureStencilBackCacheInitialized(rs: Float64Array): void {\n if (rs[RS_STENCIL_OP_FAIL + RS_DESIRED] === -1) {\n resetStencilBack(rs);\n }\n}\n\nfunction setDesiredStencilBackOps(rs: Float64Array, state: GLStencilOpState): void {\n if (state.opFail !== undefined) {\n rs[BACK_OP_FAIL + BACK_DESIRED] = state.opFail;\n }\n if (state.opZFail !== undefined) {\n rs[BACK_OP_ZFAIL + BACK_DESIRED] = state.opZFail;\n }\n if (state.opZPass !== undefined) {\n rs[BACK_OP_ZPASS + BACK_DESIRED] = state.opZPass;\n }\n}\n\n/** Mirror a shared stencil-op update to the lazily allocated back-face cache. */\nfunction mirrorSharedStencilOpsToBack(s: GLEngineContext[\"_state\"], state: GLStencilOpState): void {\n if ((state.opFail ?? state.opZFail ?? state.opZPass) === undefined) {\n return;\n }\n ensureStencilBackCacheInitialized(s.rs);\n setDesiredStencilBackOps(s.rs, state);\n}\n\n/** Lazily install back-face storage and the two-sided reconciler. */\nfunction enableSeparateStencil(s: GLEngineContext[\"_state\"]): void {\n if (s._setStencilBack !== undefined) {\n return;\n }\n const rs = new Float64Array(52);\n rs.set(s.rs);\n s.rs = rs;\n resetStencilBack(rs);\n s._setStencilBack = mirrorSharedStencilOpsToBack;\n s._flushStencil = flushStencilSeparate;\n}\n\n/** Options for {@link clearEngine}. */\nexport interface GLClearOptions {\n /** When set, clears the color buffer to this RGBA color (alpha default 1). */\n color?: { r: number; g: number; b: number; a?: number };\n /** Clear the depth buffer (respects the current depth write mask). */\n depth?: boolean;\n /** Clear the stencil buffer (respects the current stencil write mask). */\n stencil?: boolean;\n}\n\n/**\n * Buffer depth-buffer state (test enable, write mask, comparison function) into\n * the DESIRED state — the lite-gl equivalent of mutating Babylon's\n * `engine.depthCullingState.{depthTest,depthMask,depthFunc}`. Flushed to GL by\n * `applyGLStates` before the next draw / clear; omitted fields are untouched.\n *\n * @param engine - The engine.\n * @param state - The depth fields to change. Omitted fields are untouched.\n */\nexport function setDepthState(engine: GLEngineContext, state: GLDepthState): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const s = engine._state;\n if (state.test !== undefined) {\n s.rs[RS_DEPTH_TEST + RS_DESIRED] = state.test ? 1 : 0;\n }\n if (state.write !== undefined) {\n s.rs[RS_DEPTH_MASK + RS_DESIRED] = state.write ? 1 : 0;\n }\n if (state.func !== undefined) {\n s.rs[RS_DEPTH_FUNC + RS_DESIRED] = state.func;\n }\n s._flushDepthCull = flushDepthCull;\n s.statesDirty = true;\n}\n\n/**\n * Enable/disable face culling and (optionally) set the cull face — the lite-gl\n * equivalent of `engine.depthCullingState.cull` + `cullFace`.\n *\n * @param engine - The engine.\n * @param enabled - Enable (`true`) or disable (`false`) `gl.CULL_FACE`.\n * @param face - Optional cull face (`gl.BACK` / `gl.FRONT` / `gl.FRONT_AND_BACK`).\n */\nexport function setCullState(engine: GLEngineContext, enabled: boolean, face?: GLenum): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const s = engine._state;\n s.rs[RS_CULL_ENABLED + RS_DESIRED] = enabled ? 1 : 0;\n if (face !== undefined) {\n s.rs[RS_CULL_FACE + RS_DESIRED] = face;\n }\n s._flushDepthCull = flushDepthCull;\n s.statesDirty = true;\n}\n\n/**\n * Buffer stencil state (test enable, write mask, comparison func triple, op\n * triple) into the DESIRED state — the lite-gl equivalent of mutating Babylon's\n * `engine.stencilState.*`. Flushed by `applyGLStates` before the next draw /\n * clear; omitted fields are untouched (merge-from-desired).\n *\n * @param engine - The engine.\n * @param state - The stencil fields to change. Omitted fields are untouched.\n */\nexport function setStencilState(engine: GLEngineContext, state: GLStencilState): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const s = engine._state;\n if (state.test !== undefined) {\n s.rs[RS_STENCIL_TEST + RS_DESIRED] = state.test ? 1 : 0;\n }\n if (state.func !== undefined) {\n s.rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED] = state.func;\n }\n if (state.ref !== undefined) {\n s.rs[RS_STENCIL_FUNC_REF + RS_DESIRED] = state.ref;\n }\n if (state.funcMask !== undefined) {\n s.rs[RS_STENCIL_FUNC_MASK + RS_DESIRED] = state.funcMask;\n }\n if (state.mask !== undefined) {\n s.rs[RS_STENCIL_MASK + RS_DESIRED] = state.mask;\n }\n s._setStencilBack?.(s, state);\n if ((state.opFail ?? state.opZFail ?? state.opZPass) !== undefined) {\n ensureStencilOpCacheInitialized(s.rs);\n if (state.opFail !== undefined) {\n s.rs[RS_STENCIL_OP_FAIL + RS_DESIRED] = state.opFail;\n }\n if (state.opZFail !== undefined) {\n s.rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED] = state.opZFail;\n }\n if (state.opZPass !== undefined) {\n s.rs[RS_STENCIL_OP_ZPASS + RS_DESIRED] = state.opZPass;\n }\n }\n s._flushStencil ??= flushStencil;\n s.statesDirty = true;\n}\n\n/**\n * Buffer stencil operations for one face into the DESIRED state. This is the\n * deferred lite-gl counterpart of `gl.stencilOpSeparate`; use it to apply\n * different winding operations to front and back faces in one draw. A\n * `FRONT_AND_BACK` update applies each provided operation to both faces while\n * leaving omitted operations unchanged on each face.\n *\n * @param engine - The engine.\n * @param face - `gl.FRONT`, `gl.BACK`, or `gl.FRONT_AND_BACK`.\n * @param state - The operation fields to change for the selected face(s).\n * @throws If `face` is not `gl.FRONT`, `gl.BACK`, or `gl.FRONT_AND_BACK`.\n */\nexport function setStencilOpSeparate(engine: GLEngineContext, face: GLenum, state: GLStencilOpState): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const s = engine._state;\n if (face !== FRONT && face !== BACK && face !== FRONT_AND_BACK) {\n throw new Error(\"lite-gl: invalid face\");\n }\n if ((state.opFail ?? state.opZFail ?? state.opZPass) === undefined) {\n return;\n }\n enableSeparateStencil(s);\n ensureStencilBackCacheInitialized(s.rs);\n if (face !== BACK) {\n setDesiredStencilOps(s.rs, state);\n }\n if (face !== FRONT) {\n setDesiredStencilBackOps(s.rs, state);\n }\n s.statesDirty = true;\n}\n\n/**\n * Buffer the color write mask into the DESIRED state — the lite-gl equivalent of\n * Babylon's `setColorWrite` (which passes the same flag to all four channels).\n * Flushed to GL (`gl.colorMask`) by `applyGLStates` before the next draw /\n * clear.\n *\n * @param engine - The engine.\n * @param r - Write red.\n * @param g - Write green.\n * @param b - Write blue.\n * @param a - Write alpha.\n */\nexport function setColorMask(engine: GLEngineContext, r: boolean, g: boolean, b: boolean, a: boolean): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const packed = (r ? 8 : 0) | (g ? 4 : 0) | (b ? 2 : 0) | (a ? 1 : 0);\n const s = engine._state;\n s.rs[RS_COLOR_MASK + RS_DESIRED] = packed;\n s._flushColorMask = flushColorMask;\n s.statesDirty = true;\n}\n\n/**\n * Clear the currently-bound framebuffer's color / depth / stencil buffers — the\n * lite-gl equivalent of Babylon's `clear(color, backBuffer, depth, stencil)`.\n * Depth/stencil clears respect the current write masks (set them first via\n * {@link setDepthState} / {@link setStencilState}). No-op when nothing is\n * requested or the context is lost/disposed.\n *\n * @param engine - The engine.\n * @param options - Which buffers to clear (and the color value).\n */\nexport function clearEngine(engine: GLEngineContext, options: GLClearOptions): void {\n if (engine._isLost || engine._disposed) {\n return;\n }\n const gl = engine.gl;\n let mask = 0;\n if (options.color !== undefined) {\n const c = options.color;\n const a = c.a ?? 1;\n const rs = engine._state.rs;\n // Cached: gl.clearColor is per-context state GL retains, so only re-issue\n // it when the requested color actually changes (Babylon re-sets it every\n // clear; this elides the redundant JS↔native call for constant backgrounds).\n if (rs[RS_CLEAR_R] !== c.r || rs[RS_CLEAR_G] !== c.g || rs[RS_CLEAR_B] !== c.b || rs[RS_CLEAR_A] !== a) {\n gl.clearColor(c.r, c.g, c.b, a);\n rs[RS_CLEAR_R] = c.r;\n rs[RS_CLEAR_G] = c.g;\n rs[RS_CLEAR_B] = c.b;\n rs[RS_CLEAR_A] = a;\n }\n mask |= COLOR_BUFFER_BIT;\n }\n if (options.depth === true) {\n mask |= DEPTH_BUFFER_BIT;\n }\n if (options.stencil === true) {\n mask |= STENCIL_BUFFER_BIT;\n }\n if (mask !== 0) {\n // Babylon parity: a clear respects the current depth/stencil/color write\n // masks, so flush any deferred state before clearing.\n applyGLStates(engine);\n gl.clear(mask);\n }\n}\n\n/* ─────────────────── deferred render-state reconcilers ──────────────────────\n * The per-category half of `applyGLStates`. Each setter above installs the\n * matching reconciler onto its `_state._flush*` slot; `applyGLStates`\n * (apply-states.ts) dispatches ONLY through those slots. Co-locating them with\n * their setters makes each one reachable solely when its setter is in the bundle,\n * so a scene that never touches depth/cull, stencil, or color-mask tree-shakes\n * the corresponding reconciler — and its GL code — away entirely. Each reconciles\n * its DESIRED (`rs[RS_X + RS_DESIRED]`) slots against the ACTUAL twins (`rs[RS_X]`)\n * and issues only the `gl.*` calls that changed, in the same order and with the\n * same elision rules as the former monolithic flush. */\n\n/** Reconcile depth + cull (Babylon's `_depthCullingState.apply`). @internal */\nfunction flushDepthCull(engine: GLEngineContext): void {\n const gl = engine.gl;\n const rs = engine._state.rs;\n const dTest = rs[RS_DEPTH_TEST + RS_DESIRED]!;\n if (dTest !== rs[RS_DEPTH_TEST]) {\n rs[RS_DEPTH_TEST] = dTest;\n if (dTest === 1) {\n gl.enable(DEPTH_TEST);\n } else {\n gl.disable(DEPTH_TEST);\n }\n }\n const dMask = rs[RS_DEPTH_MASK + RS_DESIRED]!;\n if (dMask !== rs[RS_DEPTH_MASK]) {\n rs[RS_DEPTH_MASK] = dMask;\n gl.depthMask(dMask === 1);\n }\n const dFunc = rs[RS_DEPTH_FUNC + RS_DESIRED]!;\n if (dFunc !== rs[RS_DEPTH_FUNC]) {\n rs[RS_DEPTH_FUNC] = dFunc;\n gl.depthFunc(dFunc);\n }\n const dCull = rs[RS_CULL_ENABLED + RS_DESIRED]!;\n if (dCull !== rs[RS_CULL_ENABLED]) {\n rs[RS_CULL_ENABLED] = dCull;\n if (dCull === 1) {\n gl.enable(CULL_FACE);\n } else {\n gl.disable(CULL_FACE);\n }\n }\n const dCullFace = rs[RS_CULL_FACE + RS_DESIRED]!;\n if (dCullFace !== rs[RS_CULL_FACE]) {\n rs[RS_CULL_FACE] = dCullFace;\n gl.cullFace(dCullFace);\n }\n}\n\nfunction flushStencilCommon(engine: GLEngineContext): void {\n const gl = engine.gl;\n const rs = engine._state.rs;\n const dTest = rs[RS_STENCIL_TEST + RS_DESIRED]!;\n if (dTest !== rs[RS_STENCIL_TEST]) {\n rs[RS_STENCIL_TEST] = dTest;\n if (dTest === 1) {\n gl.enable(STENCIL_TEST);\n } else {\n gl.disable(STENCIL_TEST);\n }\n }\n const dMask = rs[RS_STENCIL_MASK + RS_DESIRED]!;\n if (dMask !== rs[RS_STENCIL_MASK]) {\n rs[RS_STENCIL_MASK] = dMask;\n gl.stencilMask(dMask);\n }\n const dFuncFunc = rs[RS_STENCIL_FUNC_FUNC + RS_DESIRED]!;\n const dFuncRef = rs[RS_STENCIL_FUNC_REF + RS_DESIRED]!;\n const dFuncMask = rs[RS_STENCIL_FUNC_MASK + RS_DESIRED]!;\n if (dFuncFunc !== rs[RS_STENCIL_FUNC_FUNC] || dFuncRef !== rs[RS_STENCIL_FUNC_REF] || dFuncMask !== rs[RS_STENCIL_FUNC_MASK]) {\n rs[RS_STENCIL_FUNC_FUNC] = dFuncFunc;\n rs[RS_STENCIL_FUNC_REF] = dFuncRef;\n rs[RS_STENCIL_FUNC_MASK] = dFuncMask;\n gl.stencilFunc(dFuncFunc, dFuncRef, dFuncMask);\n }\n}\n\n/** Reconcile the compact shared stencil state. @internal */\nfunction flushStencil(engine: GLEngineContext): void {\n flushStencilCommon(engine);\n const gl = engine.gl;\n const rs = engine._state.rs;\n const frontOpFail = rs[RS_STENCIL_OP_FAIL + RS_DESIRED]!;\n if (frontOpFail === -1) {\n return;\n }\n const frontOpZFail = rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED]!;\n const frontOpZPass = rs[RS_STENCIL_OP_ZPASS + RS_DESIRED]!;\n const frontChanged = frontOpFail !== rs[RS_STENCIL_OP_FAIL] || frontOpZFail !== rs[RS_STENCIL_OP_ZFAIL] || frontOpZPass !== rs[RS_STENCIL_OP_ZPASS];\n if (frontChanged) {\n rs[RS_STENCIL_OP_FAIL] = frontOpFail;\n rs[RS_STENCIL_OP_ZFAIL] = frontOpZFail;\n rs[RS_STENCIL_OP_ZPASS] = frontOpZPass;\n gl.stencilOp(frontOpFail, frontOpZFail, frontOpZPass);\n }\n}\n\n/** Reconcile independently cached front/back stencil operation tuples. */\nfunction flushStencilSeparate(engine: GLEngineContext): void {\n flushStencilCommon(engine);\n const gl = engine.gl;\n const rs = engine._state.rs;\n const frontOpFail = rs[RS_STENCIL_OP_FAIL + RS_DESIRED]!;\n if (frontOpFail === -1) {\n return;\n }\n const frontOpZFail = rs[RS_STENCIL_OP_ZFAIL + RS_DESIRED]!;\n const frontOpZPass = rs[RS_STENCIL_OP_ZPASS + RS_DESIRED]!;\n const backOpFail = rs[BACK_OP_FAIL + BACK_DESIRED]!;\n const backOpZFail = rs[BACK_OP_ZFAIL + BACK_DESIRED]!;\n const backOpZPass = rs[BACK_OP_ZPASS + BACK_DESIRED]!;\n const frontChanged = frontOpFail !== rs[RS_STENCIL_OP_FAIL] || frontOpZFail !== rs[RS_STENCIL_OP_ZFAIL] || frontOpZPass !== rs[RS_STENCIL_OP_ZPASS];\n const backChanged = backOpFail !== rs[BACK_OP_FAIL] || backOpZFail !== rs[BACK_OP_ZFAIL] || backOpZPass !== rs[BACK_OP_ZPASS];\n if (!frontChanged && !backChanged) {\n return;\n }\n const facesMatch = frontOpFail === backOpFail && frontOpZFail === backOpZFail && frontOpZPass === backOpZPass;\n if (facesMatch) {\n setStencilOps(rs, RS_STENCIL_OP_FAIL, frontOpFail, frontOpZFail, frontOpZPass);\n setStencilOps(rs, BACK_OP_FAIL, backOpFail, backOpZFail, backOpZPass);\n gl.stencilOp(frontOpFail, frontOpZFail, frontOpZPass);\n return;\n }\n if (frontChanged) {\n setStencilOps(rs, RS_STENCIL_OP_FAIL, frontOpFail, frontOpZFail, frontOpZPass);\n gl.stencilOpSeparate(FRONT, frontOpFail, frontOpZFail, frontOpZPass);\n }\n if (backChanged) {\n setStencilOps(rs, BACK_OP_FAIL, backOpFail, backOpZFail, backOpZPass);\n gl.stencilOpSeparate(BACK, backOpFail, backOpZFail, backOpZPass);\n }\n}\n\n/** Reconcile the packed color-write mask (Babylon's `setColorWrite`). @internal */\nfunction flushColorMask(engine: GLEngineContext): void {\n const rs = engine._state.rs;\n const dColorMask = rs[RS_COLOR_MASK + RS_DESIRED]!;\n if (dColorMask !== rs[RS_COLOR_MASK]) {\n rs[RS_COLOR_MASK] = dColorMask;\n engine.gl.colorMask((dColorMask & 8) !== 0, (dColorMask & 4) !== 0, (dColorMask & 2) !== 0, (dColorMask & 1) !== 0);\n }\n}\n\n/**\n * Opt-in: give a `/render-target` {@link GLRenderTarget} a stencil attachment,\n * replacing the core's depth-only `DEPTH_COMPONENT16` renderbuffer with either a\n * packed **`DEPTH24_STENCIL8`** buffer (default — depth *and* stencil) or a\n * stencil-only **`STENCIL_INDEX8`** buffer.\n *\n * Stencil is intentionally NOT a {@link createRenderTarget} option: keeping this\n * helper in the depth-stencil module means the stencil/packed renderbuffer\n * code tree-shakes out of every bundle that only needs a color (and optional\n * depth) target.\n *\n * The attachment is **restore-correct**: it is rebuilt automatically — at the new\n * size on {@link resizeRenderTarget}, and into the fresh framebuffer after a\n * `webglcontextrestored` event — so the stencil survives for the life of the\n * target, and {@link disposeRenderTarget} releases it along with the target.\n *\n * No-op on a lost/disposed context or a disposed target.\n *\n * @param engine - The engine that owns `rt`.\n * @param rt - The render target to attach the stencil buffer to.\n * @param options - `depth` (default `true`): when `true` the attachment is a\n * packed depth+stencil buffer (`DEPTH24_STENCIL8` on `DEPTH_STENCIL_ATTACHMENT`)\n * — the common case, and the correct choice when the target was created with\n * `generateDepthBuffer: true`. When `false` the attachment is stencil-only\n * (`STENCIL_INDEX8` on `STENCIL_ATTACHMENT`).\n * @throws If a renderbuffer handle could not be allocated or the framebuffer is\n * incomplete after attaching.\n */\nexport function generateRenderTargetStencil(engine: GLEngineContext, rt: GLRenderTarget, options?: { depth?: boolean }): void {\n if (engine._isLost || engine._disposed || rt._disposed) {\n return;\n }\n const packDepth = options?.depth ?? true;\n const attachment = packDepth ? DEPTH_STENCIL_ATTACHMENT : STENCIL_ATTACHMENT;\n const format = packDepth ? DEPTH24_STENCIL8 : STENCIL_INDEX8;\n\n const build = (e: GLEngineContext): void => {\n const gl = e.gl;\n // Capture the caller's draw target so this helper is STATE-NEUTRAL: it must\n // not silently redirect subsequent draws to `rt` (during an internal\n // rebuild `prevFb` is `rt._framebuffer`, which the core re-checks next).\n const prevFb = e._state.boundFramebuffer;\n const newRb = gl.createRenderbuffer();\n if (newRb === null) {\n throw new Error(\"lite-gl: gl.createRenderbuffer returned null (render target stencil)\");\n }\n let committed = false;\n try {\n gl.bindFramebuffer(FRAMEBUFFER, rt._framebuffer);\n e._state.boundFramebuffer = rt._framebuffer;\n gl.bindRenderbuffer(RENDERBUFFER, newRb);\n gl.renderbufferStorage(RENDERBUFFER, format, rt.width, rt.height);\n gl.framebufferRenderbuffer(FRAMEBUFFER, attachment, RENDERBUFFER, newRb);\n gl.bindRenderbuffer(RENDERBUFFER, null);\n const status = gl.checkFramebufferStatus(FRAMEBUFFER);\n if (status !== FRAMEBUFFER_COMPLETE) {\n throw new Error(`lite-gl: render target framebuffer incomplete after stencil attach (status 0x${status.toString(16)})`);\n }\n // Commit only after a complete attachment: release the buffer we\n // replaced (the core depth-only one, or our own from a prior rebuild).\n if (rt._depthStencil !== null) {\n gl.deleteRenderbuffer(rt._depthStencil);\n }\n rt._depthStencil = newRb;\n committed = true;\n } finally {\n if (!committed) {\n // Any non-committed exit (incomplete framebuffer OR an unexpected\n // GL throw): detach + delete the buffer we couldn't adopt so it\n // never leaks, leaving the attachment point empty for the\n // caller-level rollback to re-establish the prior buffer.\n gl.framebufferRenderbuffer(FRAMEBUFFER, attachment, RENDERBUFFER, null);\n gl.deleteRenderbuffer(newRb);\n }\n // Restore the caller's draw target + the bound-framebuffer cache.\n if (e._state.boundFramebuffer !== prevFb) {\n gl.bindFramebuffer(FRAMEBUFFER, prevFb);\n e._state.boundFramebuffer = prevFb;\n }\n }\n };\n\n // Build once now, but COMMIT the resize/restore hook only if it succeeds — a\n // failed opt-in must leave the target exactly as it was. The packed attach\n // above can clear the core `DEPTH_ATTACHMENT`, so on failure restore the prior\n // hook AND re-establish the prior depth/stencil attachment (the prior hook's,\n // or the core depth-only buffer).\n const prevHook = rt._rebuildDepthStencil;\n const prevDepthStencil = rt._depthStencil;\n try {\n build(engine);\n } catch (err) {\n rt._rebuildDepthStencil = prevHook;\n try {\n if (prevHook !== undefined) {\n prevHook(engine);\n } else if (prevDepthStencil !== null) {\n reattachCoreDepthBuffer(engine, rt, prevDepthStencil);\n }\n } catch {\n // Best-effort restore; surface the original failure below.\n }\n throw err;\n }\n rt._rebuildDepthStencil = build;\n}\n\n/**\n * Re-attach a core depth-only renderbuffer at `DEPTH_ATTACHMENT` — used to roll a\n * render target back when a packed {@link generateRenderTargetStencil} attach\n * fails completeness (attaching at `DEPTH_STENCIL_ATTACHMENT` clears the core\n * `DEPTH_ATTACHMENT`). State-neutral: restores the caller's bound framebuffer.\n * @internal\n */\nfunction reattachCoreDepthBuffer(engine: GLEngineContext, rt: GLRenderTarget, depthBuffer: WebGLRenderbuffer): void {\n const gl = engine.gl;\n const prevFb = engine._state.boundFramebuffer;\n gl.bindFramebuffer(FRAMEBUFFER, rt._framebuffer);\n engine._state.boundFramebuffer = rt._framebuffer;\n gl.framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, depthBuffer);\n if (engine._state.boundFramebuffer !== prevFb) {\n gl.bindFramebuffer(FRAMEBUFFER, prevFb);\n engine._state.boundFramebuffer = prevFb;\n }\n}\n"]}
package/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { createGLEngine, disposeGLEngine, resizeGLEngine, setGLEngineSize, wipeGLStateCache, getRenderWidth, getRenderHeight, getHardwareScalingLevel, setHardwareScalingLevel, getRenderingCanvas, onContextLost, offContextLost, onContextRestored, offContextRestored, } from "./context.js";
2
2
  export type { GLEngineOptions, GLEngineCaps, GLEngineContext } from "./context.js";
3
+ export { getAlphaToCoverage, getCurrentSampleCount, setAlphaToCoverage } from "./alpha-to-coverage.js";
3
4
  export { runRenderLoop, stopRenderLoop } from "./render-loop.js";
4
5
  export { createEffect, useEffect, isEffectReady, executeWhenCompiled, disposeEffect, setEffectFloat, setEffectFloat2, setEffectFloat3, setEffectFloat4, setEffectColor3, setEffectColor4, setEffectVector2, setEffectDirectColor4, setEffectInt, setEffectTexture, setEffectMatrix, setEffectMatrix3x3, setEffectFloatArray, setEffectFloatArray4, setEffectIntArray, } from "./effect.js";
5
6
  export type { GLEffectOptions, GLEffect } from "./effect.js";
@@ -14,8 +15,8 @@ export { createVertexBuffer, updateVertexBuffer, createIndexBuffer, disposeBuffe
14
15
  export type { GLVertexBuffer, GLIndexBuffer, GLAttributeDescriptor, GLMeshVertexBuffer, GLMeshVao } from "./mesh.js";
15
16
  export { GLBlendMode, GLBlendEquation, setBlendMode, setBlendState, disableBlend } from "./blend.js";
16
17
  export type { GLBlendState } from "./blend.js";
17
- export { setDepthState, setCullState, setStencilState, setColorMask, clearEngine, generateRenderTargetStencil } from "./depth-stencil.js";
18
- export type { GLDepthState, GLStencilState, GLClearOptions } from "./depth-stencil.js";
18
+ export { setDepthState, setCullState, setStencilState, setStencilOpSeparate, setColorMask, clearEngine, generateRenderTargetStencil } from "./depth-stencil.js";
19
+ export type { GLDepthState, GLStencilState, GLStencilOpState, GLClearOptions } from "./depth-stencil.js";
19
20
  export { setScissor, disableScissor } from "./scissor.js";
20
21
  export { createSpriteRenderer, renderSprites, setSpriteRendererTexture, disposeSpriteRenderer } from "./sprites.js";
21
22
  export type { GLSprite, GLSpriteColor, GLSpriteRendererOptions, GLSpriteRenderer } from "./sprites.js";
package/index.js CHANGED
@@ -12,6 +12,8 @@
12
12
  // else (sprites, html-texture, …) away.
13
13
  // ─── Engine / context ────────────────────────────────────────────────
14
14
  export { createGLEngine, disposeGLEngine, resizeGLEngine, setGLEngineSize, wipeGLStateCache, getRenderWidth, getRenderHeight, getHardwareScalingLevel, setHardwareScalingLevel, getRenderingCanvas, onContextLost, offContextLost, onContextRestored, offContextRestored, } from "./context.js";
15
+ // ─── Alpha-to-coverage (optional) ───────────────────────────────────
16
+ export { getAlphaToCoverage, getCurrentSampleCount, setAlphaToCoverage } from "./alpha-to-coverage.js";
15
17
  // ─── Render loop ─────────────────────────────────────────────────────
16
18
  export { runRenderLoop, stopRenderLoop } from "./render-loop.js";
17
19
  // ─── Effects ─────────────────────────────────────────────────────────
@@ -31,7 +33,7 @@ export { createVertexBuffer, updateVertexBuffer, createIndexBuffer, disposeBuffe
31
33
  // re-export carries both.
32
34
  export { GLBlendMode, GLBlendEquation, setBlendMode, setBlendState, disableBlend } from "./blend.js";
33
35
  // ─── Depth / stencil / color-mask / clear ────────────────────────────
34
- export { setDepthState, setCullState, setStencilState, setColorMask, clearEngine, generateRenderTargetStencil } from "./depth-stencil.js";
36
+ export { setDepthState, setCullState, setStencilState, setStencilOpSeparate, setColorMask, clearEngine, generateRenderTargetStencil } from "./depth-stencil.js";
35
37
  // ─── Scissor ─────────────────────────────────────────────────────────
36
38
  export { setScissor, disableScissor } from "./scissor.js";
37
39
  // ─── Sprites ─────────────────────────────────────────────────────────
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,gFAAgF;AAChF,EAAE;AACF,iFAAiF;AACjF,4EAA4E;AAC5E,2EAA2E;AAC3E,yEAAyE;AACzE,+DAA+D;AAC/D,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,wCAAwC;AAExC,wEAAwE;AACxE,OAAO,EACH,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,GACrB,MAAM,cAAc,CAAC;AAGtB,wEAAwE;AACxE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEjE,wEAAwE;AACxE,OAAO,EACH,YAAY,EACZ,SAAS,EACT,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,GACpB,MAAM,aAAa,CAAC;AAGrB,wEAAwE;AACxE,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG9H,wEAAwE;AACxE,OAAO,EACH,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,EACb,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,GAC1B,MAAM,cAAc,CAAC;AAGtB,wEAAwE;AACxE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAE7G,wEAAwE;AACxE,OAAO,EACH,kBAAkB,EAClB,uBAAuB,EACvB,gBAAgB,EAChB,2BAA2B,EAC3B,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,GACtB,MAAM,oBAAoB,CAAC;AAG5B,wEAAwE;AACxE,OAAO,EACH,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,cAAc,EACd,wBAAwB,EACxB,WAAW,EACX,aAAa,EACb,WAAW,EACX,QAAQ,EACR,cAAc,GACjB,MAAM,WAAW,CAAC;AAGnB,wEAAwE;AACxE,sEAAsE;AACtE,0BAA0B;AAC1B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGrG,wEAAwE;AACxE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAG1I,wEAAwE;AACxE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE1D,wEAAwE;AACxE,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAGpH,wEAAwE;AACxE,gFAAgF;AAChF,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["// @babylonjs/lite-gl — Public API\n// Function-based, tree-shakeable WebGL2 micro-engine. Import only what you use.\n//\n// Convention (mirrors @babylonjs/lite): the public API is re-exported EXPLICITLY\n// by name — never `export *`. This keeps the public surface intentional and\n// reviewable, lets `export type` mark type-only re-exports (required under\n// `isolatedModules`), and keeps internal helpers (e.g. `bindTextureRaw`,\n// `bindTextureForUpload`) out of consumers' bundles + `.d.ts`.\n//\n// The package is `sideEffects: false` and no module has top-level side effects,\n// so a consumer that imports only the symbols it needs tree-shakes everything\n// else (sprites, html-texture, …) away.\n\n// ─── Engine / context ────────────────────────────────────────────────\nexport {\n createGLEngine,\n disposeGLEngine,\n resizeGLEngine,\n setGLEngineSize,\n wipeGLStateCache,\n getRenderWidth,\n getRenderHeight,\n getHardwareScalingLevel,\n setHardwareScalingLevel,\n getRenderingCanvas,\n onContextLost,\n offContextLost,\n onContextRestored,\n offContextRestored,\n} from \"./context.js\";\nexport type { GLEngineOptions, GLEngineCaps, GLEngineContext } from \"./context.js\";\n\n// ─── Render loop ─────────────────────────────────────────────────────\nexport { runRenderLoop, stopRenderLoop } from \"./render-loop.js\";\n\n// ─── Effects ─────────────────────────────────────────────────────────\nexport {\n createEffect,\n useEffect,\n isEffectReady,\n executeWhenCompiled,\n disposeEffect,\n setEffectFloat,\n setEffectFloat2,\n setEffectFloat3,\n setEffectFloat4,\n setEffectColor3,\n setEffectColor4,\n setEffectVector2,\n setEffectDirectColor4,\n setEffectInt,\n setEffectTexture,\n setEffectMatrix,\n setEffectMatrix3x3,\n setEffectFloatArray,\n setEffectFloatArray4,\n setEffectIntArray,\n} from \"./effect.js\";\nexport type { GLEffectOptions, GLEffect } from \"./effect.js\";\n\n// ─── Effect renderer (fullscreen quad) ───────────────────────────────\nexport { createEffectWrapper, applyEffectWrapper, drawEffect, setViewport, disposeEffectWrapper } from \"./effect-renderer.js\";\nexport type { GLEffectWrapperOptions, GLEffectWrapper, GLViewport } from \"./effect-renderer.js\";\n\n// ─── Textures ────────────────────────────────────────────────────────\nexport {\n createRawTexture,\n createFloatTexture,\n generateTextureMipMaps,\n loadTexture2D,\n bindTexture,\n disposeTexture,\n updateRawTexture,\n updateTextureSamplingMode,\n updateTextureWrapMode,\n createTextureFromHandle,\n} from \"./texture.js\";\nexport type { GLTextureOptions, GLFloatTextureOptions, GLTexture } from \"./texture.js\";\n\n// ─── Dynamic textures ────────────────────────────────────────────────\nexport { createDynamicTexture, updateDynamicTexture, clearDynamicTextureSource } from \"./dynamic-texture.js\";\n\n// ─── Render targets ──────────────────────────────────────────────────\nexport {\n createRenderTarget,\n createFloatRenderTarget,\n bindRenderTarget,\n generateRenderTargetMipMaps,\n resizeRenderTarget,\n readRenderTargetPixels,\n disposeRenderTarget,\n} from \"./render-target.js\";\nexport type { GLRenderTarget, GLRenderTargetOptions, GLFloatRenderTargetOptions } from \"./render-target.js\";\n\n// ─── Meshes / buffers / instancing ───────────────────────────────────\nexport {\n createVertexBuffer,\n updateVertexBuffer,\n createIndexBuffer,\n disposeBuffer,\n bindIndexBuffer,\n bindAttributes,\n unbindInstanceAttributes,\n drawIndexed,\n createMeshVao,\n bindMeshVao,\n drawMesh,\n disposeMeshVao,\n} from \"./mesh.js\";\nexport type { GLVertexBuffer, GLIndexBuffer, GLAttributeDescriptor, GLMeshVertexBuffer, GLMeshVao } from \"./mesh.js\";\n\n// ─── Blend modes ─────────────────────────────────────────────────────\n// GLBlendMode / GLBlendEquation are const + same-name type; one value\n// re-export carries both.\nexport { GLBlendMode, GLBlendEquation, setBlendMode, setBlendState, disableBlend } from \"./blend.js\";\nexport type { GLBlendState } from \"./blend.js\";\n\n// ─── Depth / stencil / color-mask / clear ────────────────────────────\nexport { setDepthState, setCullState, setStencilState, setColorMask, clearEngine, generateRenderTargetStencil } from \"./depth-stencil.js\";\nexport type { GLDepthState, GLStencilState, GLClearOptions } from \"./depth-stencil.js\";\n\n// ─── Scissor ─────────────────────────────────────────────────────────\nexport { setScissor, disableScissor } from \"./scissor.js\";\n\n// ─── Sprites ─────────────────────────────────────────────────────────\nexport { createSpriteRenderer, renderSprites, setSpriteRendererTexture, disposeSpriteRenderer } from \"./sprites.js\";\nexport type { GLSprite, GLSpriteColor, GLSpriteRendererOptions, GLSpriteRenderer } from \"./sprites.js\";\n\n// ─── HTML-element textures ───────────────────────────────────────────\n// GLSamplingMode is a const + same-name type; one value re-export carries both.\nexport { createHtmlElementTexture, updateHtmlElementTexture, GLSamplingMode } from \"./html-texture.js\";\nexport type { GLHtmlElementTextureOptions } from \"./html-texture.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,gFAAgF;AAChF,EAAE;AACF,iFAAiF;AACjF,4EAA4E;AAC5E,2EAA2E;AAC3E,yEAAyE;AACzE,+DAA+D;AAC/D,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,wCAAwC;AAExC,wEAAwE;AACxE,OAAO,EACH,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,kBAAkB,GACrB,MAAM,cAAc,CAAC;AAGtB,uEAAuE;AACvE,OAAO,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEvG,wEAAwE;AACxE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEjE,wEAAwE;AACxE,OAAO,EACH,YAAY,EACZ,SAAS,EACT,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,GACpB,MAAM,aAAa,CAAC;AAGrB,wEAAwE;AACxE,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,UAAU,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAG9H,wEAAwE;AACxE,OAAO,EACH,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,EACb,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,GAC1B,MAAM,cAAc,CAAC;AAGtB,wEAAwE;AACxE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAE7G,wEAAwE;AACxE,OAAO,EACH,kBAAkB,EAClB,uBAAuB,EACvB,gBAAgB,EAChB,2BAA2B,EAC3B,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,GACtB,MAAM,oBAAoB,CAAC;AAG5B,wEAAwE;AACxE,OAAO,EACH,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,cAAc,EACd,wBAAwB,EACxB,WAAW,EACX,aAAa,EACb,WAAW,EACX,QAAQ,EACR,cAAc,GACjB,MAAM,WAAW,CAAC;AAGnB,wEAAwE;AACxE,sEAAsE;AACtE,0BAA0B;AAC1B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGrG,wEAAwE;AACxE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,WAAW,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAGhK,wEAAwE;AACxE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE1D,wEAAwE;AACxE,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAGpH,wEAAwE;AACxE,gFAAgF;AAChF,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["// @babylonjs/lite-gl — Public API\n// Function-based, tree-shakeable WebGL2 micro-engine. Import only what you use.\n//\n// Convention (mirrors @babylonjs/lite): the public API is re-exported EXPLICITLY\n// by name — never `export *`. This keeps the public surface intentional and\n// reviewable, lets `export type` mark type-only re-exports (required under\n// `isolatedModules`), and keeps internal helpers (e.g. `bindTextureRaw`,\n// `bindTextureForUpload`) out of consumers' bundles + `.d.ts`.\n//\n// The package is `sideEffects: false` and no module has top-level side effects,\n// so a consumer that imports only the symbols it needs tree-shakes everything\n// else (sprites, html-texture, …) away.\n\n// ─── Engine / context ────────────────────────────────────────────────\nexport {\n createGLEngine,\n disposeGLEngine,\n resizeGLEngine,\n setGLEngineSize,\n wipeGLStateCache,\n getRenderWidth,\n getRenderHeight,\n getHardwareScalingLevel,\n setHardwareScalingLevel,\n getRenderingCanvas,\n onContextLost,\n offContextLost,\n onContextRestored,\n offContextRestored,\n} from \"./context.js\";\nexport type { GLEngineOptions, GLEngineCaps, GLEngineContext } from \"./context.js\";\n\n// ─── Alpha-to-coverage (optional) ───────────────────────────────────\nexport { getAlphaToCoverage, getCurrentSampleCount, setAlphaToCoverage } from \"./alpha-to-coverage.js\";\n\n// ─── Render loop ─────────────────────────────────────────────────────\nexport { runRenderLoop, stopRenderLoop } from \"./render-loop.js\";\n\n// ─── Effects ─────────────────────────────────────────────────────────\nexport {\n createEffect,\n useEffect,\n isEffectReady,\n executeWhenCompiled,\n disposeEffect,\n setEffectFloat,\n setEffectFloat2,\n setEffectFloat3,\n setEffectFloat4,\n setEffectColor3,\n setEffectColor4,\n setEffectVector2,\n setEffectDirectColor4,\n setEffectInt,\n setEffectTexture,\n setEffectMatrix,\n setEffectMatrix3x3,\n setEffectFloatArray,\n setEffectFloatArray4,\n setEffectIntArray,\n} from \"./effect.js\";\nexport type { GLEffectOptions, GLEffect } from \"./effect.js\";\n\n// ─── Effect renderer (fullscreen quad) ───────────────────────────────\nexport { createEffectWrapper, applyEffectWrapper, drawEffect, setViewport, disposeEffectWrapper } from \"./effect-renderer.js\";\nexport type { GLEffectWrapperOptions, GLEffectWrapper, GLViewport } from \"./effect-renderer.js\";\n\n// ─── Textures ────────────────────────────────────────────────────────\nexport {\n createRawTexture,\n createFloatTexture,\n generateTextureMipMaps,\n loadTexture2D,\n bindTexture,\n disposeTexture,\n updateRawTexture,\n updateTextureSamplingMode,\n updateTextureWrapMode,\n createTextureFromHandle,\n} from \"./texture.js\";\nexport type { GLTextureOptions, GLFloatTextureOptions, GLTexture } from \"./texture.js\";\n\n// ─── Dynamic textures ────────────────────────────────────────────────\nexport { createDynamicTexture, updateDynamicTexture, clearDynamicTextureSource } from \"./dynamic-texture.js\";\n\n// ─── Render targets ──────────────────────────────────────────────────\nexport {\n createRenderTarget,\n createFloatRenderTarget,\n bindRenderTarget,\n generateRenderTargetMipMaps,\n resizeRenderTarget,\n readRenderTargetPixels,\n disposeRenderTarget,\n} from \"./render-target.js\";\nexport type { GLRenderTarget, GLRenderTargetOptions, GLFloatRenderTargetOptions } from \"./render-target.js\";\n\n// ─── Meshes / buffers / instancing ───────────────────────────────────\nexport {\n createVertexBuffer,\n updateVertexBuffer,\n createIndexBuffer,\n disposeBuffer,\n bindIndexBuffer,\n bindAttributes,\n unbindInstanceAttributes,\n drawIndexed,\n createMeshVao,\n bindMeshVao,\n drawMesh,\n disposeMeshVao,\n} from \"./mesh.js\";\nexport type { GLVertexBuffer, GLIndexBuffer, GLAttributeDescriptor, GLMeshVertexBuffer, GLMeshVao } from \"./mesh.js\";\n\n// ─── Blend modes ─────────────────────────────────────────────────────\n// GLBlendMode / GLBlendEquation are const + same-name type; one value\n// re-export carries both.\nexport { GLBlendMode, GLBlendEquation, setBlendMode, setBlendState, disableBlend } from \"./blend.js\";\nexport type { GLBlendState } from \"./blend.js\";\n\n// ─── Depth / stencil / color-mask / clear ────────────────────────────\nexport { setDepthState, setCullState, setStencilState, setStencilOpSeparate, setColorMask, clearEngine, generateRenderTargetStencil } from \"./depth-stencil.js\";\nexport type { GLDepthState, GLStencilState, GLStencilOpState, GLClearOptions } from \"./depth-stencil.js\";\n\n// ─── Scissor ─────────────────────────────────────────────────────────\nexport { setScissor, disableScissor } from \"./scissor.js\";\n\n// ─── Sprites ─────────────────────────────────────────────────────────\nexport { createSpriteRenderer, renderSprites, setSpriteRendererTexture, disposeSpriteRenderer } from \"./sprites.js\";\nexport type { GLSprite, GLSpriteColor, GLSpriteRendererOptions, GLSpriteRenderer } from \"./sprites.js\";\n\n// ─── HTML-element textures ───────────────────────────────────────────\n// GLSamplingMode is a const + same-name type; one value re-export carries both.\nexport { createHtmlElementTexture, updateHtmlElementTexture, GLSamplingMode } from \"./html-texture.js\";\nexport type { GLHtmlElementTextureOptions } from \"./html-texture.js\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babylonjs/lite-gl",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Function-based, tree-shakeable WebGL2 micro-engine for fullscreen effects, sprites and dynamic textures — the WebGL counterpart of @babylonjs/lite.",
5
5
  "keywords": [
6
6
  "babylon",
@@ -32,7 +32,7 @@
32
32
  }
33
33
  },
34
34
  "babylonLiteRelease": {
35
- "azureBuildId": "56773",
36
- "sourceVersion": "42534bbd8bd209a66fc3659d381ad6a0a9493628"
35
+ "azureBuildId": "57307",
36
+ "sourceVersion": "17779086faef2e3e6a107febd9e89ecfca21b573"
37
37
  }
38
38
  }
package/state.d.ts CHANGED
@@ -46,7 +46,7 @@ export interface GLState {
46
46
  boundFramebuffer: WebGLFramebuffer | null;
47
47
  /**
48
48
  * Deferred render-state (blend / depth / cull / stencil / color-mask) packed
49
- * into ONE flat `Float64Array(46)`. Slots `0..20` (indexed by the `RS_*`
49
+ * into a flat `Float64Array(46)`. Slots `0..20` (indexed by the `RS_*`
50
50
  * consts) are the ACTUAL applied GL state — what {@link applyGLStates} last
51
51
  * wrote to the context; slots `21..41` (`rs[RS_X + RS_DESIRED]`) are the
52
52
  * DESIRED twin the setters record. The setters write ONLY the desired half
@@ -59,13 +59,20 @@ export interface GLState {
59
59
  * Sentinels (identical for both halves): `-1` for the tri-state enables
60
60
  * (`RS_BLEND_ENABLED` / `RS_DEPTH_TEST` / `RS_DEPTH_MASK` / `RS_CULL_ENABLED`
61
61
  * / `RS_STENCIL_TEST`) and the mask caches (`RS_STENCIL_MASK` /
62
- * `RS_COLOR_MASK`); `0` for every func / equation / op / ref slot (no GL enum
63
- * is `0`). An unset desired equals its unset actual and is never flushed; the
64
- * `-1` blend/test sentinels guarantee the first applied state is never elided.
62
+ * `RS_COLOR_MASK`), plus the leading stencil-op slot
63
+ * (`RS_STENCIL_OP_FAIL`); `0` for every other func / equation / op / ref
64
+ * slot. The leading op sentinel lets the first op
65
+ * setter seed omitted fields to WebGL's `KEEP` defaults before merging. An
66
+ * unset desired equals its unset actual and is never flushed; the `-1`
67
+ * blend/test sentinels guarantee the first applied state is never elided.
65
68
  * The blend func/equation slots are only trusted while `RS_BLEND_ENABLED` is
66
69
  * `1` — the disabled→enabled transition re-issues both (matching Babylon's
67
70
  * `AlphaState`, which does not track them while blending is off).
68
71
  *
72
+ * `setStencilOpSeparate` lazily replaces this array with a 52-slot copy; the
73
+ * six appended slots cache actual and desired back-face operations without
74
+ * moving any base index.
75
+ *
69
76
  * Float64 (not Int32): `RS_STENCIL_MASK` / `RS_STENCIL_FUNC_MASK` can be
70
77
  * `0xFFFFFFFF`, which Int32 stores as `-1` — colliding with the `-1` unset
71
78
  * sentinel. Reset on context-lost. */
package/state.js CHANGED
@@ -1,8 +1,9 @@
1
1
  /* ─── Deferred render-state index-array layout ────────────────────────────────
2
2
  * The deferred render-state (blend / depth / cull / stencil / color-mask) lives
3
- * in a single flat `Float64Array(46)` on `GLState.rs`: slots `0..20` hold the
4
- * ACTUAL applied GL state, slots `21..41` (`RS_X + RS_DESIRED`) the DESIRED twin,
5
- * and slots `42..45` the standalone (no-desired-twin) cached `gl.clearColor` RGBA.
3
+ * in a flat `Float64Array(46)` on `GLState.rs`: slots `0..20` hold the ACTUAL
4
+ * applied GL state, slots `21..41` (`RS_X + RS_DESIRED`) the DESIRED twin, and
5
+ * slots `42..45` the standalone (no-desired-twin) cached `gl.clearColor` RGBA.
6
+ * `setStencilOpSeparate` lazily expands it to 52 slots for back-face operations.
6
7
  *
7
8
  * These `@internal` index consts are imported by blend.ts / depth-stencil.ts /
8
9
  * apply-states.ts. Because they are plain `const` integers, esbuild inlines each
@@ -44,13 +45,14 @@ export const RS_DESIRED = 21;
44
45
  /** @internal */ export const RS_CLEAR_G = 43;
45
46
  /** @internal */ export const RS_CLEAR_B = 44;
46
47
  /** @internal */ export const RS_CLEAR_A = 45;
47
- /** Indices in `rs` whose unset sentinel is `-1` (the tri-state enables + the
48
- * `stencilMask` / `colorMask` caches). Every other slot defaults to `0`. The
49
- * sentinel is written to BOTH the actual (`i`) and desired (`i + RS_DESIRED`)
50
- * halves. Kept as a function-local literal so it stays a runtime value (no
51
- * module-level allocation / side effect). */
48
+ /** Indices in `rs` whose unset sentinel is `-1` (the tri-state enables, the
49
+ * `stencilMask` / `colorMask` caches, and the leading shared stencil-op slot).
50
+ * Every other slot defaults to `0`. The sentinel is written to BOTH
51
+ * the actual (`i`) and desired (`i + RS_DESIRED`) halves. Kept as a
52
+ * function-local literal so it stays a runtime value (no module-level
53
+ * allocation / side effect). */
52
54
  function applyRenderStateSentinels(rs) {
53
- for (const i of [RS_BLEND_ENABLED, RS_DEPTH_TEST, RS_DEPTH_MASK, RS_CULL_ENABLED, RS_STENCIL_TEST, RS_STENCIL_MASK, RS_COLOR_MASK]) {
55
+ for (const i of [RS_BLEND_ENABLED, RS_DEPTH_TEST, RS_DEPTH_MASK, RS_CULL_ENABLED, RS_STENCIL_TEST, RS_STENCIL_MASK, RS_STENCIL_OP_FAIL, RS_COLOR_MASK]) {
54
56
  rs[i] = -1;
55
57
  rs[i + RS_DESIRED] = -1;
56
58
  }
package/state.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GLEngineContext } from \"./context.js\";\n\n/* ─── Deferred render-state index-array layout ────────────────────────────────\n * The deferred render-state (blend / depth / cull / stencil / color-mask) lives\n * in a single flat `Float64Array(46)` on `GLState.rs`: slots `0..20` hold the\n * ACTUAL applied GL state, slots `21..41` (`RS_X + RS_DESIRED`) the DESIRED twin,\n * and slots `42..45` the standalone (no-desired-twin) cached `gl.clearColor` RGBA.\n *\n * These `@internal` index consts are imported by blend.ts / depth-stencil.ts /\n * apply-states.ts. Because they are plain `const` integers, esbuild inlines each\n * to a 1–2 char literal (`rs[RS_BLEND_SRC_RGB + RS_DESIRED]` → `rs[22]`) when\n * bundling a scene — far smaller than the old cross-module `.dBlendSrcRGB` named\n * props, which could not be mangled and shipped verbatim in every bundle.\n *\n * Float64Array (NOT Int32Array) is required: `RS_STENCIL_MASK` /\n * `RS_STENCIL_FUNC_MASK` can be `0xFFFFFFFF`, which Int32 would store as `-1` and\n * collide with the `-1` \"unset\" sentinel; Float64 keeps `4294967295` distinct. */\n/** @internal */ export const RS_BLEND_ENABLED = 0;\n/** @internal */ export const RS_BLEND_SRC_RGB = 1;\n/** @internal */ export const RS_BLEND_DST_RGB = 2;\n/** @internal */ export const RS_BLEND_SRC_A = 3;\n/** @internal */ export const RS_BLEND_DST_A = 4;\n/** @internal */ export const RS_BLEND_EQ_RGB = 5;\n/** @internal */ export const RS_BLEND_EQ_A = 6;\n/** @internal */ export const RS_DEPTH_TEST = 7;\n/** @internal */ export const RS_DEPTH_MASK = 8;\n/** @internal */ export const RS_DEPTH_FUNC = 9;\n/** @internal */ export const RS_CULL_ENABLED = 10;\n/** @internal */ export const RS_CULL_FACE = 11;\n/** @internal */ export const RS_STENCIL_TEST = 12;\n/** @internal */ export const RS_STENCIL_MASK = 13;\n/** @internal */ export const RS_STENCIL_FUNC_FUNC = 14;\n/** @internal */ export const RS_STENCIL_FUNC_REF = 15;\n/** @internal */ export const RS_STENCIL_FUNC_MASK = 16;\n/** @internal */ export const RS_STENCIL_OP_FAIL = 17;\n/** @internal */ export const RS_STENCIL_OP_ZFAIL = 18;\n/** @internal */ export const RS_STENCIL_OP_ZPASS = 19;\n/** @internal */ export const RS_COLOR_MASK = 20;\n/** @internal Offset from an ACTUAL slot (`0..20`) to its DESIRED twin (`21..41`). */\nexport const RS_DESIRED = 21;\n/* Standalone cached gl.clearColor RGBA slots (`42..45`) — NO desired twin; a\n * simple apply-on-clear cache like viewport/scissor, stored in `rs` (not as named\n * GLState fields) so the index access mangles small instead of shipping\n * `.clearColorR` verbatim in every bundle. */\n/** @internal */ export const RS_CLEAR_R = 42;\n/** @internal */ export const RS_CLEAR_G = 43;\n/** @internal */ export const RS_CLEAR_B = 44;\n/** @internal */ export const RS_CLEAR_A = 45;\n\n/**\n * GL-state cache type. Owned by `GLEngineContext._state`.\n *\n * Two flavours of cached state coexist here:\n *\n * - **Eager bindings** (program / buffers / textures / VAO / framebuffer /\n * viewport / scissor / unpack). Each binding setter issues its `gl.*` call\n * immediately and updates the matching field in lock-step, eliding the call\n * when the cache already matches.\n * - **Deferred render-state** (blend / depth / cull / stencil / color-mask),\n * stored in the {@link GLState.rs} index-array. These follow Babylon's\n * `applyStates()` model: each setter records only the DESIRED slot\n * (`rs[RS_X + RS_DESIRED]`) and sets `statesDirty`; the real `gl.*` calls are\n * flushed by {@link applyGLStates} (apply-states.ts) immediately before each\n * draw / clear, reconciling DESIRED → ACTUAL and writing the actual slots\n * (`rs[RS_X]`) in lock-step. Both halves keep their `-1`/`0` unset sentinels.\n *\n * See `00-lite-gl.md` §4 for the full table of cached operations and the\n * deferred-state flush sites.\n *\n * INVARIANTS:\n * - The cache is the source of truth. If consumers poke `engine.gl.*` directly\n * they will silently corrupt this state.\n * - On `webglcontextlost` the whole `rs` array (both the actual and desired\n * halves) is reset to its initial sentinels (handles are dead anyway;\n * subsequent setters bail out on `engine._isLost`, and a post-restore setter\n * re-marks `statesDirty`).\n */\nexport interface GLState {\n currentProgram: WebGLProgram | null;\n activeTextureUnit: number;\n /** Per-unit binding; length === caps.maxTextureUnits. */\n boundTextures: (WebGLTexture | null)[];\n boundArrayBuffer: WebGLBuffer | null;\n boundElementBuffer: WebGLBuffer | null;\n boundVao: WebGLVertexArrayObject | null;\n viewportX: number;\n viewportY: number;\n viewportW: number;\n viewportH: number;\n /**\n * Currently-bound draw framebuffer, or `null` for the default (canvas)\n * framebuffer. Owned by the render-target module's `bindRenderTarget`\n * (binding `null` returns to the canvas). Reset to `null` on context-lost.\n */\n boundFramebuffer: WebGLFramebuffer | null;\n /**\n * Deferred render-state (blend / depth / cull / stencil / color-mask) packed\n * into ONE flat `Float64Array(46)`. Slots `0..20` (indexed by the `RS_*`\n * consts) are the ACTUAL applied GL state — what {@link applyGLStates} last\n * wrote to the context; slots `21..41` (`rs[RS_X + RS_DESIRED]`) are the\n * DESIRED twin the setters record. The setters write ONLY the desired half\n * (never `gl.*`, never the actual half) and raise `statesDirty`;\n * `applyGLStates` reconciles each desired → its actual twin right before a\n * draw / clear, issuing only the GL calls that changed and updating the\n * actual half in lock-step. Both preset and arbitrary blend paths feed the\n * same desired slots, so they can never desync.\n *\n * Sentinels (identical for both halves): `-1` for the tri-state enables\n * (`RS_BLEND_ENABLED` / `RS_DEPTH_TEST` / `RS_DEPTH_MASK` / `RS_CULL_ENABLED`\n * / `RS_STENCIL_TEST`) and the mask caches (`RS_STENCIL_MASK` /\n * `RS_COLOR_MASK`); `0` for every func / equation / op / ref slot (no GL enum\n * is `0`). An unset desired equals its unset actual and is never flushed; the\n * `-1` blend/test sentinels guarantee the first applied state is never elided.\n * The blend func/equation slots are only trusted while `RS_BLEND_ENABLED` is\n * `1` — the disabled→enabled transition re-issues both (matching Babylon's\n * `AlphaState`, which does not track them while blending is off).\n *\n * Float64 (not Int32): `RS_STENCIL_MASK` / `RS_STENCIL_FUNC_MASK` can be\n * `0xFFFFFFFF`, which Int32 stores as `-1` — colliding with the `-1` unset\n * sentinel. Reset on context-lost. */\n rs: Float64Array;\n /** `true` when at least one deferred-state setter ran since the last\n * {@link applyGLStates}. Gates the flush so an unchanged frame issues no\n * reconciliation work. */\n statesDirty: boolean;\n /* ─── Deferred render-state appliers (per-category, lazily installed) ──────\n * Each category's reconciler (blend / depth+cull / stencil / color-mask) is\n * installed onto its slot the first time the matching setter runs — a runtime\n * assignment, NOT a module-level side effect. `applyGLStates` dispatches ONLY\n * through these slots, so the reconcilers (and their GL reconciliation code)\n * are reachable only when their setter is in the bundle: a scene that never\n * touches a category tree-shakes its reconciler out entirely (e.g. a clear-\n * only scene drops all four). Left undefined until installed; never cleared on\n * context-lost (a post-restore setter re-installs idempotently). */\n /** @internal Blend reconciler (Babylon's `_alphaState`). */\n _flushBlend?: (engine: GLEngineContext) => void;\n /** @internal Depth + cull reconciler (Babylon's `_depthCullingState`). */\n _flushDepthCull?: (engine: GLEngineContext) => void;\n /** @internal Stencil reconciler (Babylon's `_stencilState`). */\n _flushStencil?: (engine: GLEngineContext) => void;\n /** @internal Color-write-mask reconciler (Babylon's `setColorWrite`). */\n _flushColorMask?: (engine: GLEngineContext) => void;\n /** Scissor-test enable tri-state (`-1` unset, `0` off, `1` on). */\n scissorEnabled: number;\n scissorX: number;\n scissorY: number;\n scissorW: number;\n scissorH: number;\n /** Cached `gl.pixelStorei(UNPACK_ALIGNMENT)`, or `-1` when unset. */\n unpackAlignment: number;\n /** Cached `gl.pixelStorei(UNPACK_FLIP_Y_WEBGL)`, or `-1` when unset. */\n unpackFlipY: number;\n /** Cached `gl.pixelStorei(UNPACK_PREMULTIPLY_ALPHA_WEBGL)`, or `-1` unset. */\n unpackPremultiplyAlpha: number;\n /**\n * Per-location vertex-attribute enable flags for the DEFAULT (null) VAO —\n * the mesh / instancing path (mirrors Babylon's `_vertexAttribArraysEnabled`).\n * Index is the attribute location. lite-gl's quad / sprite paths use their\n * own VAOs and do not touch this. Cleared on context-lost.\n */\n enabledAttribs: boolean[];\n /**\n * Attribute locations currently configured with a non-default vertex divisor\n * (instanced attributes), mirroring Babylon's `_currentInstanceLocations`.\n * `unbindInstanceAttributes` resets each back to divisor 0 and clears this.\n */\n instanceLocations: number[];\n /** Shared fullscreen quad — lazily created on first `applyEffectWrapper`. */\n quadVbo: WebGLBuffer | null;\n quadIbo: WebGLBuffer | null;\n quadVao: WebGLVertexArrayObject | null;\n}\n\n/** Indices in `rs` whose unset sentinel is `-1` (the tri-state enables + the\n * `stencilMask` / `colorMask` caches). Every other slot defaults to `0`. The\n * sentinel is written to BOTH the actual (`i`) and desired (`i + RS_DESIRED`)\n * halves. Kept as a function-local literal so it stays a runtime value (no\n * module-level allocation / side effect). */\nfunction applyRenderStateSentinels(rs: Float64Array): void {\n for (const i of [RS_BLEND_ENABLED, RS_DEPTH_TEST, RS_DEPTH_MASK, RS_CULL_ENABLED, RS_STENCIL_TEST, RS_STENCIL_MASK, RS_COLOR_MASK]) {\n rs[i] = -1;\n rs[i + RS_DESIRED] = -1;\n }\n // clearColor RGBA (standalone, no DESIRED twin) — `-1` forces the first clear\n // to issue gl.clearColor (valid components are 0..1).\n rs[RS_CLEAR_R] = rs[RS_CLEAR_G] = rs[RS_CLEAR_B] = rs[RS_CLEAR_A] = -1;\n}\n\n/** Allocate the 46-slot deferred render-state array (21 actual + 21 desired + 4\n * standalone clearColor) initialised to its unset sentinels — a fresh\n * `Float64Array` is already all `0`, so only the `-1` slots need writing. */\nfunction createRenderStateArray(): Float64Array {\n const rs = new Float64Array(46);\n applyRenderStateSentinels(rs);\n return rs;\n}\n\n/** Re-initialise a live render-state array in place: zero every slot, then\n * restore the `-1` sentinels. */\nfunction resetRenderStateArray(rs: Float64Array): void {\n rs.fill(0);\n applyRenderStateSentinels(rs);\n}\n\n/** Allocate a fresh, fully-null GLState sized for `maxTextureUnits`. */\nexport function createGLState(maxTextureUnits: number): GLState {\n return {\n currentProgram: null,\n activeTextureUnit: 0,\n boundTextures: new Array<WebGLTexture | null>(maxTextureUnits).fill(null),\n boundArrayBuffer: null,\n boundElementBuffer: null,\n boundVao: null,\n viewportX: 0,\n viewportY: 0,\n viewportW: 0,\n viewportH: 0,\n boundFramebuffer: null,\n rs: createRenderStateArray(),\n statesDirty: false,\n scissorEnabled: -1,\n scissorX: 0,\n scissorY: 0,\n scissorW: 0,\n scissorH: 0,\n unpackAlignment: -1,\n unpackFlipY: -1,\n unpackPremultiplyAlpha: -1,\n enabledAttribs: [],\n instanceLocations: [],\n quadVbo: null,\n quadIbo: null,\n quadVao: null,\n };\n}\n\n/** Reset only the cached \"current GL state\" — every binding (program / buffers /\n * textures / VAO / framebuffer) and render-state (blend / depth / stencil /\n * scissor / color-mask / viewport / unpack) field, including the whole `rs`\n * deferred render-state array (BOTH the actual applied values and the DESIRED\n * twins) plus `statesDirty` — to its unset sentinel, WITHOUT discarding owned\n * GPU resources (the shared quad). After this, the next setter in each category\n * is re-issued rather than elided.\n *\n * Used by `resetGLState` (context-lost) and by `wipeGLStateCache` (a host that\n * shares the GL context calling in after mutating raw `gl.*` state). The shared\n * quad's GL objects are still alive in the latter case, so they are preserved\n * here to avoid leaking + needlessly rebuilding them every render scope. */\nexport function resetGLStateCache(state: GLState): void {\n state.currentProgram = null;\n state.activeTextureUnit = 0;\n state.boundTextures.fill(null);\n state.boundArrayBuffer = null;\n state.boundElementBuffer = null;\n state.boundVao = null;\n state.viewportX = 0;\n state.viewportY = 0;\n state.viewportW = 0;\n state.viewportH = 0;\n state.boundFramebuffer = null;\n resetRenderStateArray(state.rs);\n state.statesDirty = false;\n state.scissorEnabled = -1;\n state.scissorX = 0;\n state.scissorY = 0;\n state.scissorW = 0;\n state.scissorH = 0;\n state.unpackAlignment = -1;\n state.unpackFlipY = -1;\n state.unpackPremultiplyAlpha = -1;\n state.enabledAttribs.length = 0;\n state.instanceLocations.length = 0;\n}\n\n/** Zero the cache in-place. Used by the context-lost handler — GL handles are\n * already dead per WebGL spec; we forget what we knew about them (including the\n * shared quad, whose GL objects are gone and must be rebuilt) so the next\n * bind/use after restore is NOT incorrectly elided. */\nexport function resetGLState(state: GLState): void {\n resetGLStateCache(state);\n state.quadVbo = null;\n state.quadIbo = null;\n state.quadVao = null;\n}\n"]}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GLEngineContext } from \"./context.js\";\n\n/* ─── Deferred render-state index-array layout ────────────────────────────────\n * The deferred render-state (blend / depth / cull / stencil / color-mask) lives\n * in a flat `Float64Array(46)` on `GLState.rs`: slots `0..20` hold the ACTUAL\n * applied GL state, slots `21..41` (`RS_X + RS_DESIRED`) the DESIRED twin, and\n * slots `42..45` the standalone (no-desired-twin) cached `gl.clearColor` RGBA.\n * `setStencilOpSeparate` lazily expands it to 52 slots for back-face operations.\n *\n * These `@internal` index consts are imported by blend.ts / depth-stencil.ts /\n * apply-states.ts. Because they are plain `const` integers, esbuild inlines each\n * to a 1–2 char literal (`rs[RS_BLEND_SRC_RGB + RS_DESIRED]` → `rs[22]`) when\n * bundling a scene — far smaller than the old cross-module `.dBlendSrcRGB` named\n * props, which could not be mangled and shipped verbatim in every bundle.\n *\n * Float64Array (NOT Int32Array) is required: `RS_STENCIL_MASK` /\n * `RS_STENCIL_FUNC_MASK` can be `0xFFFFFFFF`, which Int32 would store as `-1` and\n * collide with the `-1` \"unset\" sentinel; Float64 keeps `4294967295` distinct. */\n/** @internal */ export const RS_BLEND_ENABLED = 0;\n/** @internal */ export const RS_BLEND_SRC_RGB = 1;\n/** @internal */ export const RS_BLEND_DST_RGB = 2;\n/** @internal */ export const RS_BLEND_SRC_A = 3;\n/** @internal */ export const RS_BLEND_DST_A = 4;\n/** @internal */ export const RS_BLEND_EQ_RGB = 5;\n/** @internal */ export const RS_BLEND_EQ_A = 6;\n/** @internal */ export const RS_DEPTH_TEST = 7;\n/** @internal */ export const RS_DEPTH_MASK = 8;\n/** @internal */ export const RS_DEPTH_FUNC = 9;\n/** @internal */ export const RS_CULL_ENABLED = 10;\n/** @internal */ export const RS_CULL_FACE = 11;\n/** @internal */ export const RS_STENCIL_TEST = 12;\n/** @internal */ export const RS_STENCIL_MASK = 13;\n/** @internal */ export const RS_STENCIL_FUNC_FUNC = 14;\n/** @internal */ export const RS_STENCIL_FUNC_REF = 15;\n/** @internal */ export const RS_STENCIL_FUNC_MASK = 16;\n/** @internal */ export const RS_STENCIL_OP_FAIL = 17;\n/** @internal */ export const RS_STENCIL_OP_ZFAIL = 18;\n/** @internal */ export const RS_STENCIL_OP_ZPASS = 19;\n/** @internal */ export const RS_COLOR_MASK = 20;\n/** @internal Offset from an ACTUAL slot (`0..20`) to its DESIRED twin (`21..41`). */\nexport const RS_DESIRED = 21;\n/* Standalone cached gl.clearColor RGBA slots (`42..45`) — NO desired twin; a\n * simple apply-on-clear cache like viewport/scissor, stored in `rs` (not as named\n * GLState fields) so the index access mangles small instead of shipping\n * `.clearColorR` verbatim in every bundle. */\n/** @internal */ export const RS_CLEAR_R = 42;\n/** @internal */ export const RS_CLEAR_G = 43;\n/** @internal */ export const RS_CLEAR_B = 44;\n/** @internal */ export const RS_CLEAR_A = 45;\n\n/**\n * GL-state cache type. Owned by `GLEngineContext._state`.\n *\n * Two flavours of cached state coexist here:\n *\n * - **Eager bindings** (program / buffers / textures / VAO / framebuffer /\n * viewport / scissor / unpack). Each binding setter issues its `gl.*` call\n * immediately and updates the matching field in lock-step, eliding the call\n * when the cache already matches.\n * - **Deferred render-state** (blend / depth / cull / stencil / color-mask),\n * stored in the {@link GLState.rs} index-array. These follow Babylon's\n * `applyStates()` model: each setter records only the DESIRED slot\n * (`rs[RS_X + RS_DESIRED]`) and sets `statesDirty`; the real `gl.*` calls are\n * flushed by {@link applyGLStates} (apply-states.ts) immediately before each\n * draw / clear, reconciling DESIRED → ACTUAL and writing the actual slots\n * (`rs[RS_X]`) in lock-step. Both halves keep their `-1`/`0` unset sentinels.\n *\n * See `00-lite-gl.md` §4 for the full table of cached operations and the\n * deferred-state flush sites.\n *\n * INVARIANTS:\n * - The cache is the source of truth. If consumers poke `engine.gl.*` directly\n * they will silently corrupt this state.\n * - On `webglcontextlost` the whole `rs` array (both the actual and desired\n * halves) is reset to its initial sentinels (handles are dead anyway;\n * subsequent setters bail out on `engine._isLost`, and a post-restore setter\n * re-marks `statesDirty`).\n */\nexport interface GLState {\n currentProgram: WebGLProgram | null;\n activeTextureUnit: number;\n /** Per-unit binding; length === caps.maxTextureUnits. */\n boundTextures: (WebGLTexture | null)[];\n boundArrayBuffer: WebGLBuffer | null;\n boundElementBuffer: WebGLBuffer | null;\n boundVao: WebGLVertexArrayObject | null;\n viewportX: number;\n viewportY: number;\n viewportW: number;\n viewportH: number;\n /**\n * Currently-bound draw framebuffer, or `null` for the default (canvas)\n * framebuffer. Owned by the render-target module's `bindRenderTarget`\n * (binding `null` returns to the canvas). Reset to `null` on context-lost.\n */\n boundFramebuffer: WebGLFramebuffer | null;\n /**\n * Deferred render-state (blend / depth / cull / stencil / color-mask) packed\n * into a flat `Float64Array(46)`. Slots `0..20` (indexed by the `RS_*`\n * consts) are the ACTUAL applied GL state — what {@link applyGLStates} last\n * wrote to the context; slots `21..41` (`rs[RS_X + RS_DESIRED]`) are the\n * DESIRED twin the setters record. The setters write ONLY the desired half\n * (never `gl.*`, never the actual half) and raise `statesDirty`;\n * `applyGLStates` reconciles each desired → its actual twin right before a\n * draw / clear, issuing only the GL calls that changed and updating the\n * actual half in lock-step. Both preset and arbitrary blend paths feed the\n * same desired slots, so they can never desync.\n *\n * Sentinels (identical for both halves): `-1` for the tri-state enables\n * (`RS_BLEND_ENABLED` / `RS_DEPTH_TEST` / `RS_DEPTH_MASK` / `RS_CULL_ENABLED`\n * / `RS_STENCIL_TEST`) and the mask caches (`RS_STENCIL_MASK` /\n * `RS_COLOR_MASK`), plus the leading stencil-op slot\n * (`RS_STENCIL_OP_FAIL`); `0` for every other func / equation / op / ref\n * slot. The leading op sentinel lets the first op\n * setter seed omitted fields to WebGL's `KEEP` defaults before merging. An\n * unset desired equals its unset actual and is never flushed; the `-1`\n * blend/test sentinels guarantee the first applied state is never elided.\n * The blend func/equation slots are only trusted while `RS_BLEND_ENABLED` is\n * `1` — the disabled→enabled transition re-issues both (matching Babylon's\n * `AlphaState`, which does not track them while blending is off).\n *\n * `setStencilOpSeparate` lazily replaces this array with a 52-slot copy; the\n * six appended slots cache actual and desired back-face operations without\n * moving any base index.\n *\n * Float64 (not Int32): `RS_STENCIL_MASK` / `RS_STENCIL_FUNC_MASK` can be\n * `0xFFFFFFFF`, which Int32 stores as `-1` — colliding with the `-1` unset\n * sentinel. Reset on context-lost. */\n rs: Float64Array;\n /** `true` when at least one deferred-state setter ran since the last\n * {@link applyGLStates}. Gates the flush so an unchanged frame issues no\n * reconciliation work. */\n statesDirty: boolean;\n /* ─── Deferred render-state appliers (per-category, lazily installed) ──────\n * Each category's reconciler (blend / depth+cull / stencil / color-mask) is\n * installed onto its slot the first time the matching setter runs — a runtime\n * assignment, NOT a module-level side effect. `applyGLStates` dispatches ONLY\n * through these slots, so the reconcilers (and their GL reconciliation code)\n * are reachable only when their setter is in the bundle: a scene that never\n * touches a category tree-shakes its reconciler out entirely (e.g. a clear-\n * only scene drops all four). Left undefined until installed; never cleared on\n * context-lost (a post-restore setter re-installs idempotently). */\n /** @internal Blend reconciler (Babylon's `_alphaState`). */\n _flushBlend?: (engine: GLEngineContext) => void;\n /** @internal Depth + cull reconciler (Babylon's `_depthCullingState`). */\n _flushDepthCull?: (engine: GLEngineContext) => void;\n /** @internal Stencil reconciler (Babylon's `_stencilState`). */\n _flushStencil?: (engine: GLEngineContext) => void;\n /** @internal Lazy hook that mirrors shared stencil operations to the back face. */\n _setStencilBack?: (cache: GLState, state: { opFail?: GLenum; opZFail?: GLenum; opZPass?: GLenum }) => void;\n /** @internal Color-write-mask reconciler (Babylon's `setColorWrite`). */\n _flushColorMask?: (engine: GLEngineContext) => void;\n /** Scissor-test enable tri-state (`-1` unset, `0` off, `1` on). */\n scissorEnabled: number;\n scissorX: number;\n scissorY: number;\n scissorW: number;\n scissorH: number;\n /** Cached `gl.pixelStorei(UNPACK_ALIGNMENT)`, or `-1` when unset. */\n unpackAlignment: number;\n /** Cached `gl.pixelStorei(UNPACK_FLIP_Y_WEBGL)`, or `-1` when unset. */\n unpackFlipY: number;\n /** Cached `gl.pixelStorei(UNPACK_PREMULTIPLY_ALPHA_WEBGL)`, or `-1` unset. */\n unpackPremultiplyAlpha: number;\n /**\n * Per-location vertex-attribute enable flags for the DEFAULT (null) VAO —\n * the mesh / instancing path (mirrors Babylon's `_vertexAttribArraysEnabled`).\n * Index is the attribute location. lite-gl's quad / sprite paths use their\n * own VAOs and do not touch this. Cleared on context-lost.\n */\n enabledAttribs: boolean[];\n /**\n * Attribute locations currently configured with a non-default vertex divisor\n * (instanced attributes), mirroring Babylon's `_currentInstanceLocations`.\n * `unbindInstanceAttributes` resets each back to divisor 0 and clears this.\n */\n instanceLocations: number[];\n /** Shared fullscreen quad — lazily created on first `applyEffectWrapper`. */\n quadVbo: WebGLBuffer | null;\n quadIbo: WebGLBuffer | null;\n quadVao: WebGLVertexArrayObject | null;\n}\n\n/** Indices in `rs` whose unset sentinel is `-1` (the tri-state enables, the\n * `stencilMask` / `colorMask` caches, and the leading shared stencil-op slot).\n * Every other slot defaults to `0`. The sentinel is written to BOTH\n * the actual (`i`) and desired (`i + RS_DESIRED`) halves. Kept as a\n * function-local literal so it stays a runtime value (no module-level\n * allocation / side effect). */\nfunction applyRenderStateSentinels(rs: Float64Array): void {\n for (const i of [RS_BLEND_ENABLED, RS_DEPTH_TEST, RS_DEPTH_MASK, RS_CULL_ENABLED, RS_STENCIL_TEST, RS_STENCIL_MASK, RS_STENCIL_OP_FAIL, RS_COLOR_MASK]) {\n rs[i] = -1;\n rs[i + RS_DESIRED] = -1;\n }\n // clearColor RGBA (standalone, no DESIRED twin) — `-1` forces the first clear\n // to issue gl.clearColor (valid components are 0..1).\n rs[RS_CLEAR_R] = rs[RS_CLEAR_G] = rs[RS_CLEAR_B] = rs[RS_CLEAR_A] = -1;\n}\n\n/** Allocate the 46-slot deferred render-state array (21 actual + 21 desired + 4\n * standalone clearColor) initialised to its unset sentinels — a fresh\n * `Float64Array` is already all `0`, so only the `-1` slots need writing. */\nfunction createRenderStateArray(): Float64Array {\n const rs = new Float64Array(46);\n applyRenderStateSentinels(rs);\n return rs;\n}\n\n/** Re-initialise a live render-state array in place: zero every slot, then\n * restore the `-1` sentinels. */\nfunction resetRenderStateArray(rs: Float64Array): void {\n rs.fill(0);\n applyRenderStateSentinels(rs);\n}\n\n/** Allocate a fresh, fully-null GLState sized for `maxTextureUnits`. */\nexport function createGLState(maxTextureUnits: number): GLState {\n return {\n currentProgram: null,\n activeTextureUnit: 0,\n boundTextures: new Array<WebGLTexture | null>(maxTextureUnits).fill(null),\n boundArrayBuffer: null,\n boundElementBuffer: null,\n boundVao: null,\n viewportX: 0,\n viewportY: 0,\n viewportW: 0,\n viewportH: 0,\n boundFramebuffer: null,\n rs: createRenderStateArray(),\n statesDirty: false,\n scissorEnabled: -1,\n scissorX: 0,\n scissorY: 0,\n scissorW: 0,\n scissorH: 0,\n unpackAlignment: -1,\n unpackFlipY: -1,\n unpackPremultiplyAlpha: -1,\n enabledAttribs: [],\n instanceLocations: [],\n quadVbo: null,\n quadIbo: null,\n quadVao: null,\n };\n}\n\n/** Reset only the cached \"current GL state\" — every binding (program / buffers /\n * textures / VAO / framebuffer) and render-state (blend / depth / stencil /\n * scissor / color-mask / viewport / unpack) field, including the whole `rs`\n * deferred render-state array (BOTH the actual applied values and the DESIRED\n * twins) plus `statesDirty` — to its unset sentinel, WITHOUT discarding owned\n * GPU resources (the shared quad). After this, the next setter in each category\n * is re-issued rather than elided.\n *\n * Used by `resetGLState` (context-lost) and by `wipeGLStateCache` (a host that\n * shares the GL context calling in after mutating raw `gl.*` state). The shared\n * quad's GL objects are still alive in the latter case, so they are preserved\n * here to avoid leaking + needlessly rebuilding them every render scope. */\nexport function resetGLStateCache(state: GLState): void {\n state.currentProgram = null;\n state.activeTextureUnit = 0;\n state.boundTextures.fill(null);\n state.boundArrayBuffer = null;\n state.boundElementBuffer = null;\n state.boundVao = null;\n state.viewportX = 0;\n state.viewportY = 0;\n state.viewportW = 0;\n state.viewportH = 0;\n state.boundFramebuffer = null;\n resetRenderStateArray(state.rs);\n state.statesDirty = false;\n state.scissorEnabled = -1;\n state.scissorX = 0;\n state.scissorY = 0;\n state.scissorW = 0;\n state.scissorH = 0;\n state.unpackAlignment = -1;\n state.unpackFlipY = -1;\n state.unpackPremultiplyAlpha = -1;\n state.enabledAttribs.length = 0;\n state.instanceLocations.length = 0;\n}\n\n/** Zero the cache in-place. Used by the context-lost handler — GL handles are\n * already dead per WebGL spec; we forget what we knew about them (including the\n * shared quad, whose GL objects are gone and must be rebuilt) so the next\n * bind/use after restore is NOT incorrectly elided. */\nexport function resetGLState(state: GLState): void {\n resetGLStateCache(state);\n state.quadVbo = null;\n state.quadIbo = null;\n state.quadVao = null;\n}\n"]}