vireglass 2.2.0 → 2.3.1

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/dist/index.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { V as VireGlassOptics } from './material-CnUmdKg1.cjs';
2
2
  export { A as ADAPT_RADIUS, b as ALL_EFFECTS_ON, D as DEBUG_MODES, E as EFFECTS, L as LEGACY_NAMES, c as LEGACY_OPTICS, d as LegacyOpticsName, M as MATERIAL_PRESETS, e as MATERIAL_RANGES, f as MaterialPresetName, P as PRESET_NAMES, g as PRODUCT_DIM, h as VIREGLASS_CLEAR_MATERIAL, i as VIREGLASS_CONTROL_MATERIAL, j as VIREGLASS_LYRICS_MATERIAL, k as VIREGLASS_MATERIAL, l as VIREGLASS_MATERIAL_V4, m as VIREGLASS_MATERIAL_V5, n as VIREGLASS_SHEET_MATERIAL, o as VireGlassDebugMode, p as VireGlassEffect, a as VireGlassMaterial, q as VireGlassNumericKey, r as VireGlassTint, s as VireGlassToggles, t as activeMaterial, u as applyToggles, v as debugIndex, w as materialForInk, x as resolveMaterial, y as resolveOptics } from './material-CnUmdKg1.cjs';
3
3
  export { M as MAX_BEVEL_FRACTION, a as MAX_STRETCH, R as RIM_FRACTION, V as VireGlassGeometry, b as bevelDp, c as bevelFraction, d as capsuleGeometry, e as circleGeometry, h as halfMinDp, l as lensPadDp, m as morphReachDp, r as rimDp, f as roundedRectGeometry, s as shadowOpacity, g as shadowOpacityFrom, i as shadowReachDp, j as sizeDp, k as sizeGain, n as surfacePadDp, t as thicknessDp } from './geometry-RhKd7CM5.cjs';
4
- import { V as VireGlassAccent } from './adapters-DugC6Qri.cjs';
5
- export { A as ACCENT_AMOUNT, D as DYNAMIC_UNIFORMS, I as ICON_UNIFORMS, O as OVERLAY_UNIFORMS, R as REST_LIGHT, S as SURFACE_LENGTH_UNIFORMS, a as SurfaceUniforms, b as VireGlassMorph, c as VireGlassTouch, l as lensMagnify, m as morphBetween, d as morphOutOf, t as toDeviceSurfaceUniforms, e as toLensProps, f as toSurfaceUniforms } from './adapters-DugC6Qri.cjs';
4
+ import { V as VireGlassAccent } from './adapters-BOPIRMnL.cjs';
5
+ export { A as ACCENT_AMOUNT, D as DYNAMIC_UNIFORMS, I as ICON_UNIFORMS, O as OVERLAY_UNIFORMS, R as REST_LIGHT, S as SURFACE_LENGTH_UNIFORMS, a as SurfaceUniforms, b as VireGlassMorph, c as VireGlassTouch, l as lensMagnify, m as morphBetween, d as morphOutOf, t as toDeviceSurfaceUniforms, e as toLensProps, f as toSurfaceUniforms } from './adapters-BOPIRMnL.cjs';
6
6
  export { a as GRADIENT, b as GroupDecision, c as GroupMember, G as GroupPlane, d as GroupSink, e as GroupState, M as MorphShape, N as NO_ACCESSIBILITY, f as SCROLL_EDGE_ENGAGE_DP, g as SETTLE, h as SMOOTH, S as ScrollEdgeStyle, T as TouchWarp, i as VG_FALLOFF, j as VG_SDF, V as VireGlassAccessibility, k as aggregate, l as applyAccessibility, m as contrastRimLuma, n as createGroupState, o as elasticAllowed, p as neckToBridge, q as probeValuesAt, s as sceneDistance, r as sceneGradient, t as scrollEdgeReachDp, u as scrollEdgeStrength, v as scrollEdgeStyle, w as sdfRoundedRect, x as sdfRoundedRectGradient, y as smin, z as smoothPlane, A as touchWarp } from './group-model-DwtixGhG.cjs';
7
7
  export { B as BackdropSample, C as CONFIRMATIONS, F as FADE_MS, a as FLIP_LUMA, I as INK_DARK, b as INK_LIGHT, R as RETURN_LUMA, c as ambientFrom, d as decisiveLuma, s as shouldInkBeLight } from './adaptation-BSbbdExK.cjs';
8
8
 
@@ -275,11 +275,16 @@ declare function convertShaderUniforms(src: string): string;
275
275
  * parentheses inside.
276
276
  */
277
277
  declare function convertEvalCalls(src: string): string;
278
+ /** Comments are prose, and the rewrites here are regexes over raw text: a comment mentioning
279
+ * "return" became one, and a brace in a comment ended the entry point early. Newlines inside
280
+ * block comments survive, so a compile error still points at the source line. */
281
+ declare function stripComments(src: string): string;
278
282
  /**
279
283
  * Early `return <expression>;` inside `main` → `fragColor = <expression>; return;`. Takes ONLY
280
284
  * the body of main (see `convertEntryPoint`) — ordinary `return`s in helper functions live
281
285
  * outside this text and are untouched. Expressions never contain a `;` of their own (shaders have
282
- * none — the only `;` lives inside `for(...)`, which isn't a return), so `[^;]+` is safe.
286
+ * none — the only `;` lives inside `for(...)`, which isn't a return), so `[^;]+` is safe once
287
+ * comments are gone (`stripComments`).
283
288
  */
284
289
  declare function convertReturns(mainBody: string): string;
285
290
  /**
@@ -288,6 +293,10 @@ declare function convertReturns(mainBody: string): string;
288
293
  * origin, y up. The flip via `u_resolution.y - gl_FragCoord.y` restores the original convention
289
294
  * (both coordinate systems center the pixel at `+0.5`, so no X shift or constant is needed).
290
295
  * `out vec4 fragColor` and `uniform vec2 u_resolution` are declared in the prologue (`addPrologue`).
296
+ *
297
+ * `xy` and `.eval()` are flipped together — but only inside a shader THIS function translates. A
298
+ * `VireGlassBackdropPass` (`web/renderer.ts`) that reads its own previous frame from an FBO is raw
299
+ * `gl_FragCoord`, unflipped; reusing AGSL-style helpers there needs an explicit flip of its own.
291
300
  */
292
301
  declare function convertEntryPoint(src: string): string;
293
302
  /** `#version 300 es` + `precision highp float;` — no equivalent in AGSL, WebGL2 requires them as
@@ -300,4 +309,4 @@ declare function addPrologue(src: string): string;
300
309
  * found. */
301
310
  declare function toGLSL(shaderSource: string): string;
302
311
 
303
- export { type DeformOptions, type DeformSample, FRESNEL_EXPONENT, GLASS_SCALE_DEFAULT, LENS_SHADER, SURFACE_SHADER, type ShapeParent, type ShapeSpec, VireGlassAccent, VireGlassOptics, absorption, accentAmount, accentTone, addPrologue, applyAppear, applyGlassScale, blur, bodyDensity, colorPickup, concentricFit, concentricInset, concentricRadius, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, createDeform, diffraction, dispersion, edgeDensity, edgeLight, fresnelF0, fresnelStrength, gatherRadius, iorSpread, iridescence, mediumTint, raiseIntoGlass, refractionScale, refractionStrength, resolveShape, shadowTint, specularPower, specularStrength, toAGSL, toGLSL };
312
+ export { type DeformOptions, type DeformSample, FRESNEL_EXPONENT, GLASS_SCALE_DEFAULT, LENS_SHADER, SURFACE_SHADER, type ShapeParent, type ShapeSpec, VireGlassAccent, VireGlassOptics, absorption, accentAmount, accentTone, addPrologue, applyAppear, applyGlassScale, blur, bodyDensity, colorPickup, concentricFit, concentricInset, concentricRadius, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, createDeform, diffraction, dispersion, edgeDensity, edgeLight, fresnelF0, fresnelStrength, gatherRadius, iorSpread, iridescence, mediumTint, raiseIntoGlass, refractionScale, refractionStrength, resolveShape, shadowTint, specularPower, specularStrength, stripComments, toAGSL, toGLSL };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { V as VireGlassOptics } from './material-CnUmdKg1.js';
2
2
  export { A as ADAPT_RADIUS, b as ALL_EFFECTS_ON, D as DEBUG_MODES, E as EFFECTS, L as LEGACY_NAMES, c as LEGACY_OPTICS, d as LegacyOpticsName, M as MATERIAL_PRESETS, e as MATERIAL_RANGES, f as MaterialPresetName, P as PRESET_NAMES, g as PRODUCT_DIM, h as VIREGLASS_CLEAR_MATERIAL, i as VIREGLASS_CONTROL_MATERIAL, j as VIREGLASS_LYRICS_MATERIAL, k as VIREGLASS_MATERIAL, l as VIREGLASS_MATERIAL_V4, m as VIREGLASS_MATERIAL_V5, n as VIREGLASS_SHEET_MATERIAL, o as VireGlassDebugMode, p as VireGlassEffect, a as VireGlassMaterial, q as VireGlassNumericKey, r as VireGlassTint, s as VireGlassToggles, t as activeMaterial, u as applyToggles, v as debugIndex, w as materialForInk, x as resolveMaterial, y as resolveOptics } from './material-CnUmdKg1.js';
3
3
  export { M as MAX_BEVEL_FRACTION, a as MAX_STRETCH, R as RIM_FRACTION, V as VireGlassGeometry, b as bevelDp, c as bevelFraction, d as capsuleGeometry, e as circleGeometry, h as halfMinDp, l as lensPadDp, m as morphReachDp, r as rimDp, f as roundedRectGeometry, s as shadowOpacity, g as shadowOpacityFrom, i as shadowReachDp, j as sizeDp, k as sizeGain, n as surfacePadDp, t as thicknessDp } from './geometry-GvXtt-_S.js';
4
- import { V as VireGlassAccent } from './adapters-BfeAopGO.js';
5
- export { A as ACCENT_AMOUNT, D as DYNAMIC_UNIFORMS, I as ICON_UNIFORMS, O as OVERLAY_UNIFORMS, R as REST_LIGHT, S as SURFACE_LENGTH_UNIFORMS, a as SurfaceUniforms, b as VireGlassMorph, c as VireGlassTouch, l as lensMagnify, m as morphBetween, d as morphOutOf, t as toDeviceSurfaceUniforms, e as toLensProps, f as toSurfaceUniforms } from './adapters-BfeAopGO.js';
4
+ import { V as VireGlassAccent } from './adapters-B_amRBqc.js';
5
+ export { A as ACCENT_AMOUNT, D as DYNAMIC_UNIFORMS, I as ICON_UNIFORMS, O as OVERLAY_UNIFORMS, R as REST_LIGHT, S as SURFACE_LENGTH_UNIFORMS, a as SurfaceUniforms, b as VireGlassMorph, c as VireGlassTouch, l as lensMagnify, m as morphBetween, d as morphOutOf, t as toDeviceSurfaceUniforms, e as toLensProps, f as toSurfaceUniforms } from './adapters-B_amRBqc.js';
6
6
  export { a as GRADIENT, b as GroupDecision, c as GroupMember, G as GroupPlane, d as GroupSink, e as GroupState, M as MorphShape, N as NO_ACCESSIBILITY, f as SCROLL_EDGE_ENGAGE_DP, g as SETTLE, h as SMOOTH, S as ScrollEdgeStyle, T as TouchWarp, i as VG_FALLOFF, j as VG_SDF, V as VireGlassAccessibility, k as aggregate, l as applyAccessibility, m as contrastRimLuma, n as createGroupState, o as elasticAllowed, p as neckToBridge, q as probeValuesAt, s as sceneDistance, r as sceneGradient, t as scrollEdgeReachDp, u as scrollEdgeStrength, v as scrollEdgeStyle, w as sdfRoundedRect, x as sdfRoundedRectGradient, y as smin, z as smoothPlane, A as touchWarp } from './group-model-z4N27qsC.js';
7
7
  export { B as BackdropSample, C as CONFIRMATIONS, F as FADE_MS, a as FLIP_LUMA, I as INK_DARK, b as INK_LIGHT, R as RETURN_LUMA, c as ambientFrom, d as decisiveLuma, s as shouldInkBeLight } from './adaptation-BSbbdExK.js';
8
8
 
@@ -275,11 +275,16 @@ declare function convertShaderUniforms(src: string): string;
275
275
  * parentheses inside.
276
276
  */
277
277
  declare function convertEvalCalls(src: string): string;
278
+ /** Comments are prose, and the rewrites here are regexes over raw text: a comment mentioning
279
+ * "return" became one, and a brace in a comment ended the entry point early. Newlines inside
280
+ * block comments survive, so a compile error still points at the source line. */
281
+ declare function stripComments(src: string): string;
278
282
  /**
279
283
  * Early `return <expression>;` inside `main` → `fragColor = <expression>; return;`. Takes ONLY
280
284
  * the body of main (see `convertEntryPoint`) — ordinary `return`s in helper functions live
281
285
  * outside this text and are untouched. Expressions never contain a `;` of their own (shaders have
282
- * none — the only `;` lives inside `for(...)`, which isn't a return), so `[^;]+` is safe.
286
+ * none — the only `;` lives inside `for(...)`, which isn't a return), so `[^;]+` is safe once
287
+ * comments are gone (`stripComments`).
283
288
  */
284
289
  declare function convertReturns(mainBody: string): string;
285
290
  /**
@@ -288,6 +293,10 @@ declare function convertReturns(mainBody: string): string;
288
293
  * origin, y up. The flip via `u_resolution.y - gl_FragCoord.y` restores the original convention
289
294
  * (both coordinate systems center the pixel at `+0.5`, so no X shift or constant is needed).
290
295
  * `out vec4 fragColor` and `uniform vec2 u_resolution` are declared in the prologue (`addPrologue`).
296
+ *
297
+ * `xy` and `.eval()` are flipped together — but only inside a shader THIS function translates. A
298
+ * `VireGlassBackdropPass` (`web/renderer.ts`) that reads its own previous frame from an FBO is raw
299
+ * `gl_FragCoord`, unflipped; reusing AGSL-style helpers there needs an explicit flip of its own.
291
300
  */
292
301
  declare function convertEntryPoint(src: string): string;
293
302
  /** `#version 300 es` + `precision highp float;` — no equivalent in AGSL, WebGL2 requires them as
@@ -300,4 +309,4 @@ declare function addPrologue(src: string): string;
300
309
  * found. */
301
310
  declare function toGLSL(shaderSource: string): string;
302
311
 
303
- export { type DeformOptions, type DeformSample, FRESNEL_EXPONENT, GLASS_SCALE_DEFAULT, LENS_SHADER, SURFACE_SHADER, type ShapeParent, type ShapeSpec, VireGlassAccent, VireGlassOptics, absorption, accentAmount, accentTone, addPrologue, applyAppear, applyGlassScale, blur, bodyDensity, colorPickup, concentricFit, concentricInset, concentricRadius, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, createDeform, diffraction, dispersion, edgeDensity, edgeLight, fresnelF0, fresnelStrength, gatherRadius, iorSpread, iridescence, mediumTint, raiseIntoGlass, refractionScale, refractionStrength, resolveShape, shadowTint, specularPower, specularStrength, toAGSL, toGLSL };
312
+ export { type DeformOptions, type DeformSample, FRESNEL_EXPONENT, GLASS_SCALE_DEFAULT, LENS_SHADER, SURFACE_SHADER, type ShapeParent, type ShapeSpec, VireGlassAccent, VireGlassOptics, absorption, accentAmount, accentTone, addPrologue, applyAppear, applyGlassScale, blur, bodyDensity, colorPickup, concentricFit, concentricInset, concentricRadius, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, createDeform, diffraction, dispersion, edgeDensity, edgeLight, fresnelF0, fresnelStrength, gatherRadius, iorSpread, iridescence, mediumTint, raiseIntoGlass, refractionScale, refractionStrength, resolveShape, shadowTint, specularPower, specularStrength, stripComments, toAGSL, toGLSL };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- export { SURFACE_SHADER, addPrologue, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, toGLSL } from './chunk-2XJJO7SC.js';
1
+ export { SURFACE_SHADER, addPrologue, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, stripComments, toGLSL } from './chunk-UDPJLNFW.js';
2
2
  export { GLASS_SCALE_DEFAULT, GRADIENT, NO_ACCESSIBILITY, SCROLL_EDGE_ENGAGE_DP, SETTLE, SMOOTH, accentAmount, accentTone, aggregate, applyAccessibility, applyAppear, applyGlassScale, concentricFit, concentricInset, concentricRadius, contrastRimLuma, createDeform, createGroupState, elasticAllowed, probeValuesAt, raiseIntoGlass, resolveShape, scrollEdgeReachDp, scrollEdgeStrength, scrollEdgeStyle, shadowTint, smoothPlane } from './chunk-CTTZNZFV.js';
3
3
  export { CONFIRMATIONS, FADE_MS, FLIP_LUMA, INK_DARK, INK_LIGHT, RETURN_LUMA, ambientFrom, decisiveLuma, shouldInkBeLight } from './chunk-JNRZNMJJ.js';
4
- export { ACCENT_AMOUNT, ADAPT_RADIUS, ALL_EFFECTS_ON, DEBUG_MODES, DYNAMIC_UNIFORMS, EFFECTS, FRESNEL_EXPONENT, ICON_UNIFORMS, LEGACY_NAMES, LEGACY_OPTICS, LENS_SHADER, MATERIAL_PRESETS, MATERIAL_RANGES, OVERLAY_UNIFORMS, PRESET_NAMES, PRODUCT_DIM, REST_LIGHT, SURFACE_LENGTH_UNIFORMS, VG_FALLOFF, VG_SDF, VIREGLASS_CLEAR_MATERIAL, VIREGLASS_CONTROL_MATERIAL, VIREGLASS_LYRICS_MATERIAL, VIREGLASS_MATERIAL, VIREGLASS_MATERIAL_V4, VIREGLASS_MATERIAL_V5, VIREGLASS_SHEET_MATERIAL, absorption, activeMaterial, applyToggles, blur, bodyDensity, colorPickup, debugIndex, diffraction, dispersion, edgeDensity, edgeLight, fresnelF0, fresnelStrength, gatherRadius, iorSpread, iridescence, lensMagnify, materialForInk, mediumTint, morphBetween, morphOutOf, neckToBridge, refractionScale, refractionStrength, resolveMaterial, resolveOptics, sceneDistance, sceneGradient, sdfRoundedRect, sdfRoundedRectGradient, smin, specularPower, specularStrength, toDeviceSurfaceUniforms, toLensProps, toSurfaceUniforms, touchWarp } from './chunk-H5QTBF67.js';
4
+ export { ACCENT_AMOUNT, ADAPT_RADIUS, ALL_EFFECTS_ON, DEBUG_MODES, DYNAMIC_UNIFORMS, EFFECTS, FRESNEL_EXPONENT, ICON_UNIFORMS, LEGACY_NAMES, LEGACY_OPTICS, LENS_SHADER, MATERIAL_PRESETS, MATERIAL_RANGES, OVERLAY_UNIFORMS, PRESET_NAMES, PRODUCT_DIM, REST_LIGHT, SURFACE_LENGTH_UNIFORMS, VG_FALLOFF, VG_SDF, VIREGLASS_CLEAR_MATERIAL, VIREGLASS_CONTROL_MATERIAL, VIREGLASS_LYRICS_MATERIAL, VIREGLASS_MATERIAL, VIREGLASS_MATERIAL_V4, VIREGLASS_MATERIAL_V5, VIREGLASS_SHEET_MATERIAL, absorption, activeMaterial, applyToggles, blur, bodyDensity, colorPickup, debugIndex, diffraction, dispersion, edgeDensity, edgeLight, fresnelF0, fresnelStrength, gatherRadius, iorSpread, iridescence, lensMagnify, materialForInk, mediumTint, morphBetween, morphOutOf, neckToBridge, refractionScale, refractionStrength, resolveMaterial, resolveOptics, sceneDistance, sceneGradient, sdfRoundedRect, sdfRoundedRectGradient, smin, specularPower, specularStrength, toDeviceSurfaceUniforms, toLensProps, toSurfaceUniforms, touchWarp } from './chunk-V47YU75P.js';
5
5
  export { MAX_BEVEL_FRACTION, MAX_STRETCH, RIM_FRACTION, bevelDp, bevelFraction, capsuleGeometry, circleGeometry, halfMinDp, lensPadDp, morphReachDp, rimDp, roundedRectGeometry, shadowOpacity, shadowOpacityFrom, shadowReachDp, sizeDp, sizeGain, surfacePadDp, thicknessDp } from './chunk-SLRPHDV7.js';
6
6
  import './chunk-MMPK6DCK.js';
7
7
 
@@ -1459,6 +1459,26 @@ function convertEvalCalls(src) {
1459
1459
  out += src.slice(cursor);
1460
1460
  return out;
1461
1461
  }
1462
+ function stripComments(src) {
1463
+ let out = "";
1464
+ let i = 0;
1465
+ while (i < src.length) {
1466
+ if (src.startsWith("//", i)) {
1467
+ const end = src.indexOf("\n", i);
1468
+ i = end < 0 ? src.length : end;
1469
+ } else if (src.startsWith("/*", i)) {
1470
+ const end = src.indexOf("*/", i + 2);
1471
+ if (end < 0) throw new Error("vireglass/targets/glsl: unterminated block comment");
1472
+ const stop = end + 2;
1473
+ out += src.slice(i, stop).replace(/[^\n]/g, "");
1474
+ i = stop;
1475
+ } else {
1476
+ out += src[i];
1477
+ i += 1;
1478
+ }
1479
+ }
1480
+ return out;
1481
+ }
1462
1482
  function convertReturns(mainBody) {
1463
1483
  return mainBody.replace(/\breturn\s+([^;]+);/g, "fragColor = $1; return;");
1464
1484
  }
@@ -1489,7 +1509,7 @@ function addPrologue(src) {
1489
1509
  return "#version 300 es\nprecision highp float;\n\nout vec4 fragColor;\nuniform vec2 u_resolution;\n\n" + src;
1490
1510
  }
1491
1511
  function toGLSL(shaderSource) {
1492
- let out = shaderSource;
1512
+ let out = stripComments(shaderSource);
1493
1513
  out = convertEntryPoint(out);
1494
1514
  out = convertShaderUniforms(out);
1495
1515
  out = convertEvalCalls(out);
@@ -1546,6 +1566,7 @@ function createTexture(gl, options) {
1546
1566
  const internalFormat = options.internalFormat ?? gl.RGBA8;
1547
1567
  const format = options.format ?? gl.RGBA;
1548
1568
  const type = options.type ?? gl.UNSIGNED_BYTE;
1569
+ const wrap = options.wrap ?? gl.CLAMP_TO_EDGE;
1549
1570
  gl.bindTexture(gl.TEXTURE_2D, texture);
1550
1571
  gl.texImage2D(
1551
1572
  gl.TEXTURE_2D,
@@ -1560,11 +1581,45 @@ function createTexture(gl, options) {
1560
1581
  );
1561
1582
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
1562
1583
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
1563
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1564
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1584
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap);
1585
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap);
1565
1586
  gl.bindTexture(gl.TEXTURE_2D, null);
1566
1587
  return texture;
1567
1588
  }
1589
+ function locationCache(gl, program) {
1590
+ const cache = /* @__PURE__ */ new Map();
1591
+ return (name) => {
1592
+ let loc = cache.get(name);
1593
+ if (loc === void 0) {
1594
+ loc = gl.getUniformLocation(program, name);
1595
+ cache.set(name, loc);
1596
+ }
1597
+ return loc;
1598
+ };
1599
+ }
1600
+ function setUniform(gl, loc, value) {
1601
+ if (!loc) return;
1602
+ if (typeof value === "number") {
1603
+ gl.uniform1f(loc, value);
1604
+ return;
1605
+ }
1606
+ switch (value.length) {
1607
+ case 1:
1608
+ gl.uniform1f(loc, value[0]);
1609
+ break;
1610
+ case 2:
1611
+ gl.uniform2f(loc, value[0], value[1]);
1612
+ break;
1613
+ case 3:
1614
+ gl.uniform3f(loc, value[0], value[1], value[2]);
1615
+ break;
1616
+ case 4:
1617
+ gl.uniform4f(loc, value[0], value[1], value[2], value[3]);
1618
+ break;
1619
+ default:
1620
+ throw new Error(`vireglass/web: unsupported uniform size (${value.length})`);
1621
+ }
1622
+ }
1568
1623
  function createFramebuffer(gl, texture) {
1569
1624
  const fbo = gl.createFramebuffer();
1570
1625
  if (!fbo) throw new Error("vireglass/web: gl.createFramebuffer returned null");
@@ -1584,6 +1639,62 @@ function bindTextureAt(gl, unit, texture, program, uniformName) {
1584
1639
  gl.uniform1i(loc, unit);
1585
1640
  }
1586
1641
 
1642
+ // src/web/gpu-timer.ts
1643
+ var GPU_TIMER_RING_SIZE = 3;
1644
+ function createGpuTimer(gl) {
1645
+ const ext = gl.getExtension("EXT_disjoint_timer_query_webgl2");
1646
+ const slots = ext ? Array.from({ length: GPU_TIMER_RING_SIZE }, () => ({
1647
+ query: gl.createQuery(),
1648
+ pending: false
1649
+ })) : [];
1650
+ let cursor = 0;
1651
+ let active = null;
1652
+ let lastMs = null;
1653
+ function poll() {
1654
+ if (!ext) return;
1655
+ for (const slot of slots) {
1656
+ if (!slot.pending) continue;
1657
+ if (!gl.getQueryParameter(slot.query, gl.QUERY_RESULT_AVAILABLE)) continue;
1658
+ slot.pending = false;
1659
+ if (gl.getParameter(ext.GPU_DISJOINT_EXT)) continue;
1660
+ const ns = gl.getQueryParameter(slot.query, gl.QUERY_RESULT);
1661
+ lastMs = ns / 1e6;
1662
+ }
1663
+ }
1664
+ function begin() {
1665
+ active = null;
1666
+ if (!ext || slots.length === 0) return;
1667
+ poll();
1668
+ const slot = slots[cursor];
1669
+ if (slot.pending) return;
1670
+ gl.beginQuery(ext.TIME_ELAPSED_EXT, slot.query);
1671
+ active = slot;
1672
+ }
1673
+ function end() {
1674
+ if (!ext || !active) return;
1675
+ gl.endQuery(ext.TIME_ELAPSED_EXT);
1676
+ active.pending = true;
1677
+ cursor = (cursor + 1) % slots.length;
1678
+ active = null;
1679
+ }
1680
+ function time(fn) {
1681
+ begin();
1682
+ try {
1683
+ return fn();
1684
+ } finally {
1685
+ end();
1686
+ }
1687
+ }
1688
+ function getLastMs() {
1689
+ poll();
1690
+ return lastMs;
1691
+ }
1692
+ function destroy() {
1693
+ for (const slot of slots) gl.deleteQuery(slot.query);
1694
+ }
1695
+ return { time, getLastMs, destroy };
1696
+ }
1697
+
1587
1698
  // src/web/probe.ts
1588
1699
  var PROBE_GRID_WIDTH = 48;
1589
1700
  var PROBE_GRID_HEIGHT = 96;
@@ -1804,40 +1915,6 @@ void main() {
1804
1915
  fragColor = texture(u_content, uv);
1805
1916
  }
1806
1917
  `;
1807
- function locationCache(gl, program) {
1808
- const cache = /* @__PURE__ */ new Map();
1809
- return (name) => {
1810
- let loc = cache.get(name);
1811
- if (loc === void 0) {
1812
- loc = gl.getUniformLocation(program, name);
1813
- cache.set(name, loc);
1814
- }
1815
- return loc;
1816
- };
1817
- }
1818
- function setUniform(gl, loc, value) {
1819
- if (!loc) return;
1820
- if (typeof value === "number") {
1821
- gl.uniform1f(loc, value);
1822
- return;
1823
- }
1824
- switch (value.length) {
1825
- case 1:
1826
- gl.uniform1f(loc, value[0]);
1827
- break;
1828
- case 2:
1829
- gl.uniform2f(loc, value[0], value[1]);
1830
- break;
1831
- case 3:
1832
- gl.uniform3f(loc, value[0], value[1], value[2]);
1833
- break;
1834
- case 4:
1835
- gl.uniform4f(loc, value[0], value[1], value[2], value[3]);
1836
- break;
1837
- default:
1838
- throw new Error(`vireglass/web: unsupported uniform size (${value.length})`);
1839
- }
1840
- }
1841
1918
  function applyChannel(gl, get, names, sizes, values) {
1842
1919
  let cursor = 0;
1843
1920
  for (let i = 0; i < names.length; i += 1) {
@@ -1900,6 +1977,12 @@ function createVireGlassRenderer(canvas) {
1900
1977
  width: Math.max(width, 1),
1901
1978
  height: Math.max(height, 1)
1902
1979
  });
1980
+ let backdropFbo = null;
1981
+ function ensureBackdropFbo() {
1982
+ if (!backdropFbo) backdropFbo = createFramebuffer(gl, contentTexture);
1983
+ return backdropFbo;
1984
+ }
1985
+ const gpuTimer = createGpuTimer(gl);
1903
1986
  const iconTexture = createTexture(gl, { width: 1, height: 1 });
1904
1987
  const colorTexture = createTexture(gl, { width: 1, height: 1 });
1905
1988
  gl.bindTexture(gl.TEXTURE_2D, iconTexture);
@@ -1928,128 +2011,164 @@ function createVireGlassRenderer(canvas) {
1928
2011
  if (!sceneCtx) throw new Error("vireglass/web: scene 2D context unavailable");
1929
2012
  gl.deleteTexture(contentTexture);
1930
2013
  contentTexture = createTexture(gl, { width, height });
2014
+ if (backdropFbo) {
2015
+ gl.deleteFramebuffer(backdropFbo);
2016
+ backdropFbo = null;
2017
+ }
1931
2018
  }
1932
2019
  function render(options) {
1933
2020
  if (!sceneCtx) throw new Error("vireglass/web: renderer not initialized (resize was never called)");
1934
- sceneCtx.clearRect(0, 0, width, height);
1935
- options.scene(sceneCtx, width, height, options.offsetX ?? 0, options.offsetY ?? 0, options.density);
1936
- gl.bindTexture(gl.TEXTURE_2D, contentTexture);
1937
- gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, sceneCanvas);
1938
- gl.bindTexture(gl.TEXTURE_2D, null);
1939
- if (options.colorLayer) {
1940
- gl.bindTexture(gl.TEXTURE_2D, colorTexture);
1941
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.colorLayer);
2021
+ const ctx = sceneCtx;
2022
+ if (options.backdrop && options.scene) {
2023
+ throw new Error(
2024
+ "vireglass/web: render() needs exactly one of options.scene or options.backdrop, not both"
2025
+ );
1942
2026
  }
1943
- if (options.iconMask) {
1944
- gl.bindTexture(gl.TEXTURE_2D, iconTexture);
1945
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.iconMask);
1946
- gl.bindTexture(gl.TEXTURE_2D, null);
2027
+ if (!options.backdrop && !options.scene) {
2028
+ throw new Error("vireglass/web: render() needs either options.scene or options.backdrop");
1947
2029
  }
1948
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
1949
- gl.viewport(0, 0, width, height);
1950
- gl.disable(gl.BLEND);
1951
- gl.disable(gl.SCISSOR_TEST);
1952
- gl.useProgram(blitProgram);
1953
- bindTextureAt(gl, 0, contentTexture, blitProgram, "u_content");
1954
- setUniform(gl, blitLoc("u_resolution"), [width, height]);
1955
- drawFullscreenTriangle(gl);
1956
- const probes = [];
1957
- for (let index = 0; index < options.pieces.length; index += 1) {
1958
- const piece = options.pieces[index];
1959
- const halfWidth = piece.geometry.width * options.density / 2;
1960
- const halfHeight = piece.geometry.height * options.density / 2;
1961
- const rect = {
1962
- centerX: piece.centerX,
1963
- centerY: piece.centerY,
1964
- halfWidth,
1965
- halfHeight
1966
- };
1967
- const fresh = index === 0 ? probe.sample(contentTexture, width, height, rect) : probe.statsFor(width, height, rect);
1968
- const stats = fresh ? settleStats(index, fresh) : null;
1969
- probes.push(stats);
1970
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
1971
- gl.viewport(0, 0, width, height);
1972
- gl.enable(gl.BLEND);
1973
- gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
1974
- const reachOf = (m) => m ? Math.hypot(m.offsetX, m.offsetY) + Math.max(m.width, m.height) / 2 + m.smoothing : 0;
1975
- const morphReach = Math.max(reachOf(piece.morph), reachOf(piece.morph2));
1976
- const touchReach = piece.touch ? Math.hypot(piece.touch.pullX, piece.touch.pullY) + piece.touch.waveAmp * 2 + Math.max(piece.geometry.width, piece.geometry.height) * 0.05 * piece.touch.press : 0;
1977
- const padPx = (lensPadDp(piece.geometry, piece.optics) + surfacePadDp(piece.geometry, 0) + morphReach + touchReach) * options.density;
1978
- const left = Math.max(0, Math.floor(piece.centerX - halfWidth - padPx));
1979
- const right = Math.min(width, Math.ceil(piece.centerX + halfWidth + padPx));
1980
- const top = Math.max(0, Math.floor(height - (piece.centerY + halfHeight + padPx)));
1981
- const bottom = Math.min(height, Math.ceil(height - (piece.centerY - halfHeight - padPx)));
1982
- gl.enable(gl.SCISSOR_TEST);
1983
- gl.scissor(left, top, Math.max(0, right - left), Math.max(0, bottom - top));
1984
- gl.useProgram(lensProgram);
1985
- bindTextureAt(gl, 0, contentTexture, lensProgram, "content");
1986
- setUniform(gl, lensLoc("u_contentSize"), [width, height]);
1987
- setUniform(gl, lensLoc("u_resolution"), [width, height]);
1988
- setUniform(gl, lensLoc("u_center"), [piece.centerX, piece.centerY]);
1989
- setUniform(gl, lensLoc("u_reach"), Math.max(width, height));
1990
- setUniform(gl, lensLoc("u_contentMin"), [1, 1]);
1991
- setUniform(gl, lensLoc("u_contentMax"), [width - 1, height - 1]);
1992
- if (stats) {
1993
- setUniform(gl, lensLoc("u_probeLuma"), stats.luma);
1994
- setUniform(gl, lensLoc("u_probeBusy"), stats.busy);
1995
- setUniform(gl, lensLoc("u_probeRange"), [stats.lo, stats.hi]);
1996
- setUniform(gl, lensLoc("u_probeSlope"), [stats.slopeX, stats.slopeY]);
1997
- setUniform(gl, lensLoc("u_probe"), [stats.r, stats.g, stats.b]);
2030
+ return gpuTimer.time(() => {
2031
+ if (options.backdrop) {
2032
+ const fbo = ensureBackdropFbo();
2033
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
2034
+ gl.viewport(0, 0, width, height);
2035
+ options.backdrop(gl, { framebuffer: fbo, width, height });
2036
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
2037
+ gl.viewport(0, 0, width, height);
2038
+ gl.disable(gl.BLEND);
2039
+ gl.disable(gl.SCISSOR_TEST);
2040
+ gl.disable(gl.DEPTH_TEST);
2041
+ gl.disable(gl.CULL_FACE);
2042
+ gl.disable(gl.STENCIL_TEST);
2043
+ gl.colorMask(true, true, true, true);
2044
+ gl.depthMask(true);
2045
+ gl.bindVertexArray(null);
2046
+ gl.activeTexture(gl.TEXTURE0);
2047
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
2048
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
2049
+ gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL);
1998
2050
  } else {
1999
- setUniform(gl, lensLoc("u_probeLuma"), -1);
2051
+ ctx.clearRect(0, 0, width, height);
2052
+ options.scene?.(ctx, width, height, options.offsetX ?? 0, options.offsetY ?? 0, options.density);
2053
+ gl.bindTexture(gl.TEXTURE_2D, contentTexture);
2054
+ gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, sceneCanvas);
2055
+ gl.bindTexture(gl.TEXTURE_2D, null);
2000
2056
  }
2001
- const lens = toLensProps(piece.optics, piece.geometry, options.density, {
2002
- debug: options.debug,
2003
- morph: piece.morph,
2004
- morph2: piece.morph2,
2005
- touch: piece.touch,
2006
- progress: piece.progress,
2007
- light: piece.light,
2008
- appear: piece.appear,
2009
- accent: piece.accent
2010
- });
2011
- applyChannel(gl, lensLoc, lens.uniformNames, lens.uniformSizes, lens.uniformValues);
2012
- drawFullscreenTriangle(gl);
2013
- gl.useProgram(surfaceProgram);
2014
- const rawSurface = toSurfaceUniforms(piece.optics, piece.geometry, {
2015
- debug: options.debug,
2016
- morph: piece.morph,
2017
- morph2: piece.morph2,
2018
- bodyInLens: true,
2019
- touch: piece.touch,
2020
- progress: piece.progress,
2021
- // The shadow is denser over text, weaker over a flat backdrop (M 11:47). The rule lives
2022
- // in the core: while it lived here, Android didn't have it at all.
2023
- shadow: stats ? shadowOpacity(stats.busy) : 1,
2024
- // The probe has already computed the ambient color — the shadow gets it for free. Taken
2025
- // as-is: `settleStats` has already smoothed it, and rounding would bring back exactly the
2026
- // steps the smoothing was written to remove.
2027
- ambient: stats ? [stats.r, stats.g, stats.b] : void 0,
2028
- appear: piece.appear,
2029
- lift: piece.lift
2030
- });
2031
- applyObject(gl, surfaceLoc, {
2032
- ...toDeviceSurfaceUniforms(rawSurface, options.density),
2033
- u_center: [piece.centerX, piece.centerY]
2034
- });
2035
- setUniform(gl, surfaceLoc(DYNAMIC_UNIFORMS[0]), piece.press ?? 0);
2036
- setUniform(gl, surfaceLoc(DYNAMIC_UNIFORMS[1]), piece.active ?? 0);
2037
- const hasIcon = Boolean(piece.icon && options.iconMask);
2038
- setUniform(gl, surfaceLoc(ICON_UNIFORMS[0]), hasIcon ? 1 : 0);
2039
- setUniform(gl, surfaceLoc(ICON_UNIFORMS[1]), 1);
2040
- setUniform(gl, surfaceLoc(ICON_UNIFORMS[2]), piece.inkIdle ?? [1, 1, 1, 1]);
2041
- setUniform(gl, surfaceLoc(ICON_UNIFORMS[3]), piece.inkActive ?? [1, 1, 1, 1]);
2042
- bindTextureAt(gl, 1, iconTexture, surfaceProgram, "u_icon");
2043
- setUniform(gl, surfaceLoc("u_iconSize"), hasIcon ? [width, height] : [1, 1]);
2044
- const hasColor = Boolean(piece.overlay && options.colorLayer);
2045
- setUniform(gl, surfaceLoc(OVERLAY_UNIFORMS[0]), hasColor ? 1 : 0);
2046
- bindTextureAt(gl, 2, colorTexture, surfaceProgram, "u_overlay");
2047
- setUniform(gl, surfaceLoc("u_overlaySize"), hasColor ? [width, height] : [1, 1]);
2048
- setUniform(gl, surfaceLoc("u_resolution"), [width, height]);
2057
+ if (options.colorLayer) {
2058
+ gl.bindTexture(gl.TEXTURE_2D, colorTexture);
2059
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.colorLayer);
2060
+ }
2061
+ if (options.iconMask) {
2062
+ gl.bindTexture(gl.TEXTURE_2D, iconTexture);
2063
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, options.iconMask);
2064
+ gl.bindTexture(gl.TEXTURE_2D, null);
2065
+ }
2066
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
2067
+ gl.viewport(0, 0, width, height);
2068
+ gl.disable(gl.BLEND);
2069
+ gl.disable(gl.SCISSOR_TEST);
2070
+ gl.useProgram(blitProgram);
2071
+ bindTextureAt(gl, 0, contentTexture, blitProgram, "u_content");
2072
+ setUniform(gl, blitLoc("u_resolution"), [width, height]);
2049
2073
  drawFullscreenTriangle(gl);
2050
- }
2051
- gl.disable(gl.SCISSOR_TEST);
2052
- return { probes };
2074
+ const probes = [];
2075
+ for (let index = 0; index < options.pieces.length; index += 1) {
2076
+ const piece = options.pieces[index];
2077
+ const halfWidth = piece.geometry.width * options.density / 2;
2078
+ const halfHeight = piece.geometry.height * options.density / 2;
2079
+ const rect = {
2080
+ centerX: piece.centerX,
2081
+ centerY: piece.centerY,
2082
+ halfWidth,
2083
+ halfHeight
2084
+ };
2085
+ const fresh = index === 0 ? probe.sample(contentTexture, width, height, rect) : probe.statsFor(width, height, rect);
2086
+ const stats = fresh ? settleStats(index, fresh) : null;
2087
+ probes.push(stats);
2088
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
2089
+ gl.viewport(0, 0, width, height);
2090
+ gl.enable(gl.BLEND);
2091
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
2092
+ const reachOf = (m) => m ? Math.hypot(m.offsetX, m.offsetY) + Math.max(m.width, m.height) / 2 + m.smoothing : 0;
2093
+ const morphReach = Math.max(reachOf(piece.morph), reachOf(piece.morph2));
2094
+ const touchReach = piece.touch ? Math.hypot(piece.touch.pullX, piece.touch.pullY) + piece.touch.waveAmp * 2 + Math.max(piece.geometry.width, piece.geometry.height) * 0.05 * piece.touch.press : 0;
2095
+ const padPx = (lensPadDp(piece.geometry, piece.optics) + surfacePadDp(piece.geometry, 0) + morphReach + touchReach) * options.density;
2096
+ const left = Math.max(0, Math.floor(piece.centerX - halfWidth - padPx));
2097
+ const right = Math.min(width, Math.ceil(piece.centerX + halfWidth + padPx));
2098
+ const top = Math.max(0, Math.floor(height - (piece.centerY + halfHeight + padPx)));
2099
+ const bottom = Math.min(height, Math.ceil(height - (piece.centerY - halfHeight - padPx)));
2100
+ gl.enable(gl.SCISSOR_TEST);
2101
+ gl.scissor(left, top, Math.max(0, right - left), Math.max(0, bottom - top));
2102
+ gl.useProgram(lensProgram);
2103
+ bindTextureAt(gl, 0, contentTexture, lensProgram, "content");
2104
+ setUniform(gl, lensLoc("u_contentSize"), [width, height]);
2105
+ setUniform(gl, lensLoc("u_resolution"), [width, height]);
2106
+ setUniform(gl, lensLoc("u_center"), [piece.centerX, piece.centerY]);
2107
+ setUniform(gl, lensLoc("u_reach"), Math.max(width, height));
2108
+ setUniform(gl, lensLoc("u_contentMin"), [1, 1]);
2109
+ setUniform(gl, lensLoc("u_contentMax"), [width - 1, height - 1]);
2110
+ if (stats) {
2111
+ setUniform(gl, lensLoc("u_probeLuma"), stats.luma);
2112
+ setUniform(gl, lensLoc("u_probeBusy"), stats.busy);
2113
+ setUniform(gl, lensLoc("u_probeRange"), [stats.lo, stats.hi]);
2114
+ setUniform(gl, lensLoc("u_probeSlope"), [stats.slopeX, stats.slopeY]);
2115
+ setUniform(gl, lensLoc("u_probe"), [stats.r, stats.g, stats.b]);
2116
+ } else {
2117
+ setUniform(gl, lensLoc("u_probeLuma"), -1);
2118
+ }
2119
+ const lens = toLensProps(piece.optics, piece.geometry, options.density, {
2120
+ debug: options.debug,
2121
+ morph: piece.morph,
2122
+ morph2: piece.morph2,
2123
+ touch: piece.touch,
2124
+ progress: piece.progress,
2125
+ light: piece.light,
2126
+ appear: piece.appear,
2127
+ accent: piece.accent
2128
+ });
2129
+ applyChannel(gl, lensLoc, lens.uniformNames, lens.uniformSizes, lens.uniformValues);
2130
+ drawFullscreenTriangle(gl);
2131
+ gl.useProgram(surfaceProgram);
2132
+ const rawSurface = toSurfaceUniforms(piece.optics, piece.geometry, {
2133
+ debug: options.debug,
2134
+ morph: piece.morph,
2135
+ morph2: piece.morph2,
2136
+ bodyInLens: true,
2137
+ touch: piece.touch,
2138
+ progress: piece.progress,
2139
+ // The shadow is denser over text, weaker over a flat backdrop (M 11:47). The rule lives
2140
+ // in the core: while it lived here, Android didn't have it at all.
2141
+ shadow: stats ? shadowOpacity(stats.busy) : 1,
2142
+ // The probe has already computed the ambient color — the shadow gets it for free. Taken
2143
+ // as-is: `settleStats` has already smoothed it, and rounding would bring back exactly the
2144
+ // steps the smoothing was written to remove.
2145
+ ambient: stats ? [stats.r, stats.g, stats.b] : void 0,
2146
+ appear: piece.appear,
2147
+ lift: piece.lift
2148
+ });
2149
+ applyObject(gl, surfaceLoc, {
2150
+ ...toDeviceSurfaceUniforms(rawSurface, options.density),
2151
+ u_center: [piece.centerX, piece.centerY]
2152
+ });
2153
+ setUniform(gl, surfaceLoc(DYNAMIC_UNIFORMS[0]), piece.press ?? 0);
2154
+ setUniform(gl, surfaceLoc(DYNAMIC_UNIFORMS[1]), piece.active ?? 0);
2155
+ const hasIcon = Boolean(piece.icon && options.iconMask);
2156
+ setUniform(gl, surfaceLoc(ICON_UNIFORMS[0]), hasIcon ? 1 : 0);
2157
+ setUniform(gl, surfaceLoc(ICON_UNIFORMS[1]), 1);
2158
+ setUniform(gl, surfaceLoc(ICON_UNIFORMS[2]), piece.inkIdle ?? [1, 1, 1, 1]);
2159
+ setUniform(gl, surfaceLoc(ICON_UNIFORMS[3]), piece.inkActive ?? [1, 1, 1, 1]);
2160
+ bindTextureAt(gl, 1, iconTexture, surfaceProgram, "u_icon");
2161
+ setUniform(gl, surfaceLoc("u_iconSize"), hasIcon ? [width, height] : [1, 1]);
2162
+ const hasColor = Boolean(piece.overlay && options.colorLayer);
2163
+ setUniform(gl, surfaceLoc(OVERLAY_UNIFORMS[0]), hasColor ? 1 : 0);
2164
+ bindTextureAt(gl, 2, colorTexture, surfaceProgram, "u_overlay");
2165
+ setUniform(gl, surfaceLoc("u_overlaySize"), hasColor ? [width, height] : [1, 1]);
2166
+ setUniform(gl, surfaceLoc("u_resolution"), [width, height]);
2167
+ drawFullscreenTriangle(gl);
2168
+ }
2169
+ gl.disable(gl.SCISSOR_TEST);
2170
+ return { probes };
2171
+ });
2053
2172
  }
2054
2173
  function destroy() {
2055
2174
  probe.destroy();
@@ -2059,13 +2178,24 @@ function createVireGlassRenderer(canvas) {
2059
2178
  gl.deleteTexture(contentTexture);
2060
2179
  gl.deleteTexture(iconTexture);
2061
2180
  gl.deleteTexture(colorTexture);
2181
+ if (backdropFbo) gl.deleteFramebuffer(backdropFbo);
2182
+ gpuTimer.destroy();
2062
2183
  }
2063
- return { resize, render, destroy };
2184
+ return { resize, render, getLastGpuMs: gpuTimer.getLastMs, destroy };
2064
2185
  }
2065
2186
 
2187
+ exports.FULLSCREEN_TRIANGLE_VERTEX_SOURCE = FULLSCREEN_TRIANGLE_VERTEX_SOURCE;
2066
2188
  exports.PROBE_GRID_HEIGHT = PROBE_GRID_HEIGHT;
2067
2189
  exports.PROBE_GRID_WIDTH = PROBE_GRID_WIDTH;
2190
+ exports.bindTextureAt = bindTextureAt;
2191
+ exports.compileShader = compileShader;
2192
+ exports.createFramebuffer = createFramebuffer;
2068
2193
  exports.createProbe = createProbe;
2194
+ exports.createProgram = createProgram;
2195
+ exports.createTexture = createTexture;
2069
2196
  exports.createVireGlassRenderer = createVireGlassRenderer;
2197
+ exports.drawFullscreenTriangle = drawFullscreenTriangle;
2198
+ exports.locationCache = locationCache;
2199
+ exports.setUniform = setUniform;
2070
2200
  //# sourceMappingURL=index.cjs.map
2071
2201
  //# sourceMappingURL=index.cjs.map