@solidtv/renderer 1.3.1 → 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.
Files changed (108) hide show
  1. package/dist/src/core/CoreNode.d.ts +0 -10
  2. package/dist/src/core/CoreNode.js +7 -14
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/Stage.d.ts +7 -15
  5. package/dist/src/core/Stage.js +23 -41
  6. package/dist/src/core/Stage.js.map +1 -1
  7. package/dist/src/core/TextureMemoryManager.js +4 -3
  8. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  9. package/dist/src/core/lib/ImageWorker.d.ts +16 -1
  10. package/dist/src/core/lib/ImageWorker.js +52 -17
  11. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  12. package/dist/src/core/lib/WebGlContextWrapper.d.ts +2 -32
  13. package/dist/src/core/lib/WebGlContextWrapper.js +26 -81
  14. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  15. package/dist/src/core/lib/utils.d.ts +0 -1
  16. package/dist/src/core/lib/utils.js +0 -3
  17. package/dist/src/core/lib/utils.js.map +1 -1
  18. package/dist/src/core/platforms/web/WebPlatform.js +1 -0
  19. package/dist/src/core/platforms/web/WebPlatform.js.map +1 -1
  20. package/dist/src/core/renderers/CoreRenderer.d.ts +0 -1
  21. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  22. package/dist/src/core/renderers/CoreShaderNode.d.ts +16 -0
  23. package/dist/src/core/renderers/CoreShaderNode.js +22 -0
  24. package/dist/src/core/renderers/CoreShaderNode.js.map +1 -1
  25. package/dist/src/core/renderers/canvas/CanvasRenderer.js +23 -3
  26. package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
  27. package/dist/src/core/renderers/canvas/CanvasShaderNode.d.ts +0 -1
  28. package/dist/src/core/renderers/canvas/CanvasShaderNode.js +0 -1
  29. package/dist/src/core/renderers/canvas/CanvasShaderNode.js.map +1 -1
  30. package/dist/src/core/renderers/canvas/CanvasTexture.d.ts +1 -1
  31. package/dist/src/core/renderers/canvas/CanvasTexture.js +25 -6
  32. package/dist/src/core/renderers/canvas/CanvasTexture.js.map +1 -1
  33. package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +1 -7
  34. package/dist/src/core/renderers/webgl/WebGlRenderer.js +9 -13
  35. package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
  36. package/dist/src/core/renderers/webgl/WebGlShaderNode.d.ts +2 -4
  37. package/dist/src/core/renderers/webgl/WebGlShaderNode.js +0 -1
  38. package/dist/src/core/renderers/webgl/WebGlShaderNode.js.map +1 -1
  39. package/dist/src/core/renderers/webgl/WebGlShaderProgram.d.ts +0 -7
  40. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +3 -19
  41. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
  42. package/dist/src/core/renderers/webgl/internal/RendererUtils.d.ts +0 -33
  43. package/dist/src/core/renderers/webgl/internal/RendererUtils.js +0 -50
  44. package/dist/src/core/renderers/webgl/internal/RendererUtils.js.map +1 -1
  45. package/dist/src/core/renderers/webgl/internal/ShaderUtils.d.ts +0 -1
  46. package/dist/src/core/renderers/webgl/internal/ShaderUtils.js.map +1 -1
  47. package/dist/src/core/text-rendering/CanvasFontHandler.js +0 -5
  48. package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
  49. package/dist/src/core/text-rendering/CanvasTextRenderer.d.ts +1 -0
  50. package/dist/src/core/text-rendering/CanvasTextRenderer.js +23 -0
  51. package/dist/src/core/text-rendering/CanvasTextRenderer.js.map +1 -1
  52. package/dist/src/core/text-rendering/SdfTextRenderer.d.ts +1 -0
  53. package/dist/src/core/text-rendering/SdfTextRenderer.js +50 -4
  54. package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
  55. package/dist/src/core/text-rendering/TextRenderer.d.ts +7 -0
  56. package/dist/src/core/textures/ImageTexture.js +11 -0
  57. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  58. package/dist/src/core/textures/SubTexture.d.ts +1 -0
  59. package/dist/src/core/textures/SubTexture.js +12 -0
  60. package/dist/src/core/textures/SubTexture.js.map +1 -1
  61. package/dist/src/core/textures/Texture.js +10 -0
  62. package/dist/src/core/textures/Texture.js.map +1 -1
  63. package/dist/src/main-api/Inspector.d.ts +2 -1
  64. package/dist/src/main-api/Inspector.js +43 -15
  65. package/dist/src/main-api/Inspector.js.map +1 -1
  66. package/dist/src/main-api/Renderer.d.ts +16 -10
  67. package/dist/src/main-api/Renderer.js +3 -3
  68. package/dist/src/main-api/Renderer.js.map +1 -1
  69. package/dist/src/utils.d.ts +1 -1
  70. package/dist/src/utils.js +2 -2
  71. package/dist/src/utils.js.map +1 -1
  72. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  73. package/package.json +1 -1
  74. package/src/core/CoreNode.ts +8 -26
  75. package/src/core/Stage.ts +24 -49
  76. package/src/core/TextureMemoryManager.ts +4 -3
  77. package/src/core/lib/ImageWorker.test.ts +106 -0
  78. package/src/core/lib/ImageWorker.ts +60 -22
  79. package/src/core/lib/WebGlContextWrapper.ts +32 -101
  80. package/src/core/lib/utils.ts +0 -4
  81. package/src/core/platforms/web/WebPlatform.outOfMemory.test.ts +1 -0
  82. package/src/core/platforms/web/WebPlatform.ts +1 -0
  83. package/src/core/renderers/CoreRenderer.ts +0 -1
  84. package/src/core/renderers/CoreShaderNode.test.ts +53 -0
  85. package/src/core/renderers/CoreShaderNode.ts +23 -0
  86. package/src/core/renderers/canvas/CanvasRenderer.test.ts +34 -0
  87. package/src/core/renderers/canvas/CanvasRenderer.ts +33 -4
  88. package/src/core/renderers/canvas/CanvasShaderNode.ts +0 -1
  89. package/src/core/renderers/canvas/CanvasTexture.test.ts +31 -0
  90. package/src/core/renderers/canvas/CanvasTexture.ts +35 -7
  91. package/src/core/renderers/webgl/WebGlRenderer.ts +9 -26
  92. package/src/core/renderers/webgl/WebGlShaderNode.ts +2 -4
  93. package/src/core/renderers/webgl/WebGlShaderProgram.ts +3 -20
  94. package/src/core/renderers/webgl/internal/RendererUtils.ts +0 -87
  95. package/src/core/renderers/webgl/internal/ShaderUtils.ts +0 -1
  96. package/src/core/text-rendering/CanvasFontHandler.ts +0 -6
  97. package/src/core/text-rendering/CanvasTextRenderer.ts +26 -0
  98. package/src/core/text-rendering/SdfTextRenderer.test.ts +150 -0
  99. package/src/core/text-rendering/SdfTextRenderer.ts +54 -4
  100. package/src/core/text-rendering/TextRenderer.ts +7 -0
  101. package/src/core/textures/ImageTexture.ts +18 -0
  102. package/src/core/textures/SubTexture.test.ts +45 -0
  103. package/src/core/textures/SubTexture.ts +13 -0
  104. package/src/core/textures/Texture.ts +12 -0
  105. package/src/main-api/Inspector.ts +46 -16
  106. package/src/main-api/Renderer.ts +20 -14
  107. package/src/utils.ts +1 -2
  108. package/src/core/renderers/webgl/internal/WebGlUtils.ts +0 -16
@@ -96,6 +96,12 @@ export class CoreShaderNode<Props extends object = Record<string, unknown>> {
96
96
  protected node: CoreNode | null = null;
97
97
  readonly time: CoreShaderType['time'] = undefined;
98
98
  update: (() => void) | undefined = undefined;
99
+ /**
100
+ * The shader-value cache key currently held by this node (set by the
101
+ * subclass `update()` after the most recent successful value resolution).
102
+ * Tracked on the base so {@link detachNode} can release it on teardown.
103
+ */
104
+ valueKey = '';
99
105
  private _valueKeyCache = '';
100
106
  private _valueKeyDirty = true;
101
107
  private _lastW = 0;
@@ -162,6 +168,23 @@ export class CoreShaderNode<Props extends object = Record<string, unknown>> {
162
168
  this.node = node;
163
169
  }
164
170
 
171
+ /**
172
+ * Release this node's currently-held shader-value cache entry so the shader
173
+ * manager's idle `cleanup()` can reclaim it, and detach from the owning node.
174
+ *
175
+ * Called from {@link CoreNode.destroy}. Mirrors the `prevKey` release done on
176
+ * every value-key change in the subclass `update()`; without it a destroyed
177
+ * node's last value-cache entry stays pinned at usage >= 1 forever and can
178
+ * never be evicted.
179
+ */
180
+ detachNode() {
181
+ if (this.valueKey.length > 0) {
182
+ this.stage.shManager.mutateShaderValueUsage(this.valueKey, -1);
183
+ this.valueKey = '';
184
+ }
185
+ this.node = null;
186
+ }
187
+
165
188
  createValueKey() {
166
189
  if (
167
190
  this._valueKeyDirty === false &&
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { CanvasRenderer } from './CanvasRenderer.js';
3
+ import { TextureType } from '../../textures/Texture.js';
4
+
5
+ describe('CanvasRenderer.renderContext', () => {
6
+ it('skips drawing when canvas texture image is undefined', () => {
7
+ const drawImage = vi.fn();
8
+
9
+ const renderer = Object.create(CanvasRenderer.prototype) as CanvasRenderer;
10
+ (renderer as any).context = {
11
+ drawImage,
12
+ globalAlpha: 1,
13
+ };
14
+
15
+ const node = {
16
+ premultipliedColorTl: 0xffffffff,
17
+ globalTransform: { tx: 10, ty: 20 },
18
+ props: { w: 100, h: 50 },
19
+ worldAlpha: 1,
20
+ textureCoords: { x1: 0, y1: 0, x2: 1, y2: 1 },
21
+ } as any;
22
+
23
+ const texture = {
24
+ type: TextureType.image,
25
+ ctxTexture: {
26
+ getImage: () => undefined,
27
+ },
28
+ } as any;
29
+
30
+ expect(() => renderer.renderContext(node, texture)).not.toThrow();
31
+ expect(drawImage).not.toHaveBeenCalled();
32
+ expect((renderer as any).context.globalAlpha).toBe(1);
33
+ });
34
+ });
@@ -63,7 +63,19 @@ export class CanvasRenderer extends CoreRenderer {
63
63
  }
64
64
 
65
65
  const hasTransform = ta !== 1;
66
- const hasClipping = clippingRect.w !== 0 && clippingRect.h !== 0;
66
+ const clippingValid = clippingRect.valid === true;
67
+
68
+ // If the clipping rect is valid but zero-area, the node is fully clipped — skip rendering
69
+ if (
70
+ clippingValid === true &&
71
+ clippingRect.w === 0 &&
72
+ clippingRect.h === 0
73
+ ) {
74
+ return;
75
+ }
76
+
77
+ const hasClipping =
78
+ clippingValid === true && clippingRect.w !== 0 && clippingRect.h !== 0;
67
79
  const shader = node.props.shader;
68
80
  const hasShader = shader !== null;
69
81
 
@@ -124,7 +136,7 @@ export class CanvasRenderer extends CoreRenderer {
124
136
 
125
137
  if (textureType !== TextureType.color) {
126
138
  const tintColor = parseColor(color);
127
- let image: ImageBitmap | HTMLCanvasElement | HTMLImageElement;
139
+ let image: ImageBitmap | HTMLCanvasElement | HTMLImageElement | null;
128
140
 
129
141
  if (textureType === TextureType.subTexture) {
130
142
  image = (
@@ -134,12 +146,29 @@ export class CanvasRenderer extends CoreRenderer {
134
146
  image = (texture.ctxTexture as CanvasTexture).getImage(tintColor);
135
147
  }
136
148
 
149
+ // The texture can disappear while an async load/fetch is racing (e.g. 404);
150
+ // skip drawing this frame instead of dereferencing an invalid image object.
151
+ if (image === null || image === undefined) {
152
+ return;
153
+ }
154
+
155
+ const imageWidth = image.width;
156
+ const imageHeight = image.height;
157
+ if (
158
+ typeof imageWidth !== 'number' ||
159
+ typeof imageHeight !== 'number' ||
160
+ imageWidth <= 0 ||
161
+ imageHeight <= 0
162
+ ) {
163
+ return;
164
+ }
165
+
137
166
  this.context.globalAlpha = tintColor.a ?? node.worldAlpha;
138
167
 
139
168
  const txCoords = node.textureCoords;
140
169
  if (txCoords) {
141
- const ix = image.width;
142
- const iy = image.height;
170
+ const ix = imageWidth;
171
+ const iy = imageHeight;
143
172
 
144
173
  let sx = txCoords.x1 * ix;
145
174
  let sy = txCoords.y1 * iy;
@@ -26,7 +26,6 @@ export class CanvasShaderNode<
26
26
  > extends CoreShaderNode<Props> {
27
27
  private updater: ((node: CoreNode, props?: Props) => void) | undefined =
28
28
  undefined;
29
- private valueKey: string = '';
30
29
  computed: Partial<Computed> = {};
31
30
  applySNR: boolean;
32
31
  render: CanvasShaderType<Props>['render'];
@@ -0,0 +1,31 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { CanvasTexture } from './CanvasTexture.js';
3
+
4
+ describe('CanvasTexture.load', () => {
5
+ it('fails gracefully when textureData.data is null', async () => {
6
+ const textureSource = {
7
+ textureData: { data: null },
8
+ state: 'initial',
9
+ dimensions: null,
10
+ setState(nextState: string) {
11
+ this.state = nextState;
12
+ },
13
+ freeTextureData() {
14
+ // no-op
15
+ },
16
+ } as any;
17
+
18
+ const memManager = {
19
+ setTextureMemUse() {
20
+ // no-op
21
+ },
22
+ } as any;
23
+
24
+ const ctxTexture = new CanvasTexture(memManager, textureSource);
25
+
26
+ await expect(ctxTexture.load()).rejects.toThrow(
27
+ 'CanvasTexture: Texture data is null',
28
+ );
29
+ expect(textureSource.state).toBe('failed');
30
+ });
31
+ });
@@ -2,6 +2,7 @@ import type { Dimensions } from '../../../common/CommonTypes.js';
2
2
  import { assertTruthy } from '../../../utils.js';
3
3
  import { formatRgba, type IParsedColor } from '../../lib/colorParser.js';
4
4
  import { CoreContextTexture } from '../CoreContextTexture.js';
5
+ import type { Texture } from '../../textures/Texture.js';
5
6
 
6
7
  export class CanvasTexture extends CoreContextTexture {
7
8
  protected image:
@@ -17,10 +18,23 @@ export class CanvasTexture extends CoreContextTexture {
17
18
  | undefined;
18
19
 
19
20
  async load(): Promise<void> {
21
+ // Capture textureData synchronously before any await - a pending
22
+ // freeTextureDataTask microtask could null textureSource.textureData
23
+ // during the first async suspension, causing onLoadRequest to fail.
24
+ const textureData = this.textureSource.textureData;
25
+ assertTruthy(textureData?.data, 'Texture data is null before load');
26
+
20
27
  this.textureSource.setState('loading');
21
28
 
22
29
  try {
23
- const size = await this.onLoadRequest();
30
+ const size = await this.onLoadRequest(textureData.data);
31
+
32
+ // Guard against the texture being freed while the load was in flight
33
+ if (this.textureSource.state === 'freed') {
34
+ this.image = undefined;
35
+ return;
36
+ }
37
+
24
38
  this.textureSource.setState('loaded', size);
25
39
  this.textureSource.freeTextureData();
26
40
  this.updateMemSize();
@@ -63,9 +77,11 @@ export class CanvasTexture extends CoreContextTexture {
63
77
 
64
78
  getImage(
65
79
  color: IParsedColor,
66
- ): ImageBitmap | HTMLCanvasElement | HTMLImageElement {
80
+ ): ImageBitmap | HTMLCanvasElement | HTMLImageElement | null {
67
81
  const image = this.image;
68
- assertTruthy(image, 'Attempt to get unloaded image texture');
82
+ if (image === undefined) {
83
+ return null;
84
+ }
69
85
 
70
86
  if (color.isWhite) {
71
87
  if (this.tintCache) {
@@ -114,9 +130,21 @@ export class CanvasTexture extends CoreContextTexture {
114
130
  return canvas;
115
131
  }
116
132
 
117
- private async onLoadRequest(): Promise<Dimensions> {
118
- assertTruthy(this.textureSource?.textureData?.data, 'Texture data is null');
119
- const { data } = this.textureSource.textureData;
133
+ private async onLoadRequest(
134
+ data: NonNullable<Texture['textureData']>['data'],
135
+ ): Promise<Dimensions> {
136
+ if (data === null) {
137
+ throw new Error('CanvasTexture: Texture data is null');
138
+ }
139
+
140
+ // CompressedData objects (KTX, PVR, ASTC) carry GPU-format mipmap buffers
141
+ // that cannot be decoded by Canvas2D. Reject explicitly rather than falling
142
+ // through silently and leaving this.image unassigned.
143
+ if (typeof data === 'object' && 'mipmaps' in data) {
144
+ throw new Error(
145
+ 'CanvasTexture: Compressed texture data is not supported in Canvas2D render mode',
146
+ );
147
+ }
120
148
 
121
149
  // TODO: canvas from text renderer should be able to provide the canvas directly
122
150
  // instead of having to re-draw it into a new canvas...
@@ -125,7 +153,7 @@ export class CanvasTexture extends CoreContextTexture {
125
153
  canvas.width = data.width;
126
154
  canvas.height = data.height;
127
155
  const ctx = canvas.getContext('2d');
128
- if (ctx) ctx.putImageData(data, 0, 0);
156
+ if (ctx !== null) ctx.putImageData(data, 0, 0);
129
157
  this.image = canvas;
130
158
  return { w: data.width, h: data.height };
131
159
  } else if (
@@ -14,10 +14,6 @@ import { SdfRenderOp } from './SdfRenderOp.js';
14
14
  import type { CoreContextTexture } from '../CoreContextTexture.js';
15
15
  import {
16
16
  createIndexBuffer,
17
- type CoreWebGlParameters,
18
- type CoreWebGlExtensions,
19
- getWebGlParameters,
20
- getWebGlExtensions,
21
17
  type WebGlColor,
22
18
  } from './internal/RendererUtils.js';
23
19
  import { WebGlCtxTexture } from './WebGlCtxTexture.js';
@@ -51,17 +47,11 @@ const GL_OUT_OF_MEMORY = 0x0505;
51
47
  */
52
48
  const MAX_DRAINED_GL_ERRORS = 8;
53
49
 
54
- interface CoreWebGlSystem {
55
- parameters: CoreWebGlParameters;
56
- extensions: CoreWebGlExtensions;
57
- }
58
-
59
50
  export type WebGlRenderOp = CoreNode | SdfRenderOp;
60
51
 
61
52
  export class WebGlRenderer extends CoreRenderer {
62
53
  //// WebGL Native Context and Data
63
54
  glw: WebGlContextWrapper;
64
- system: CoreWebGlSystem;
65
55
 
66
56
  //// Persistent data
67
57
  quadBuffer: ArrayBuffer;
@@ -169,17 +159,20 @@ export class WebGlRenderer extends CoreRenderer {
169
159
  constructor(options: WebGlRendererOptions) {
170
160
  super(options);
171
161
 
162
+ // CPU-side vertex buffers, reused every frame. Their GL buffers and
163
+ // BufferCollections are wired up below, once the GL context exists.
172
164
  this.quadBuffer = new ArrayBuffer(this.stage.options.quadBufferSize);
173
165
  this.fQuadBuffer = new Float32Array(this.quadBuffer);
174
166
  this.uiQuadBuffer = new Uint32Array(this.quadBuffer);
175
167
 
168
+ // Shared SDF vertex buffer: 512 KB for ~3600 glyphs.
169
+ this.sdfBuffer = new ArrayBuffer(512 * 1024);
170
+ this.fSdfBuffer = new Float32Array(this.sdfBuffer);
171
+ this.uiSdfBuffer = new Uint32Array(this.sdfBuffer);
172
+
176
173
  this.mode = 'webgl';
177
174
 
178
- const gl = createWebGLContext(
179
- options.canvas,
180
- options.forceWebGL2,
181
- options.contextSpy,
182
- );
175
+ const gl = createWebGLContext(options.canvas, options.contextSpy);
183
176
  const glw = (this.glw = new WebGlContextWrapper(gl));
184
177
  glw.viewport(0, 0, options.canvas.width, options.canvas.height);
185
178
 
@@ -192,11 +185,6 @@ export class WebGlRenderer extends CoreRenderer {
192
185
 
193
186
  createIndexBuffer(glw, this.stage.bufferMemory);
194
187
 
195
- this.system = {
196
- parameters: getWebGlParameters(this.glw),
197
- extensions: getWebGlExtensions(this.glw),
198
- };
199
-
200
188
  // Create the static node coords buffer
201
189
  // 80 is the magic number used in createIndexBuffer
202
190
  // @see RendererUtils.ts
@@ -261,13 +249,8 @@ export class WebGlRenderer extends CoreRenderer {
261
249
  },
262
250
  },
263
251
  ]);
264
- // --- Shared SDF buffer ---------------------------------------------------
265
- // Allocate 512 KB for SDF vertex data (~3600 glyphs).
266
- const sdfBufSize = 512 * 1024;
267
- this.sdfBuffer = new ArrayBuffer(sdfBufSize);
268
- this.fSdfBuffer = new Float32Array(this.sdfBuffer);
269
- this.uiSdfBuffer = new Uint32Array(this.sdfBuffer);
270
252
 
253
+ // --- Shared SDF buffer collection (CPU buffer allocated above) ----------
271
254
  const sdfWebGlBuffer = glw.createBuffer();
272
255
  const sdfStride = 6 * Float32Array.BYTES_PER_ELEMENT; // 24 bytes
273
256
  this.sdfQuadBufferCollection = new BufferCollection([
@@ -21,11 +21,11 @@ export type ShaderSource<T> =
21
21
  export type WebGlShaderType<T extends object = Record<string, unknown>> =
22
22
  CoreShaderType<T> & {
23
23
  /**
24
- * fragment shader source for WebGl or WebGl2
24
+ * fragment shader source for WebGl
25
25
  */
26
26
  fragment: ShaderSource<T>;
27
27
  /**
28
- * vertex shader source for WebGl or WebGl2
28
+ * vertex shader source for WebGl
29
29
  */
30
30
  vertex?: ShaderSource<T>;
31
31
  /**
@@ -43,7 +43,6 @@ export type WebGlShaderType<T extends object = Record<string, unknown>> =
43
43
  * extensions required for specific shader?
44
44
  */
45
45
  webgl1Extensions?: string[];
46
- webgl2Extensions?: string[];
47
46
  supportsIndexedTextures?: boolean;
48
47
  };
49
48
 
@@ -53,7 +52,6 @@ export class WebGlShaderNode<
53
52
  readonly program: WebGlShaderProgram;
54
53
  private updater: ((node: CoreNode, props?: Props) => void) | undefined =
55
54
  undefined;
56
- private valueKey: string = '';
57
55
  uniforms: UniformCollection = {
58
56
  single: {},
59
57
  vec2: {},
@@ -19,13 +19,6 @@ import { CoreNode } from '../../CoreNode.js';
19
19
 
20
20
  export class WebGlShaderProgram implements CoreShaderProgram {
21
21
  protected program: WebGLProgram | null;
22
- /**
23
- * Vertex Array Object
24
- *
25
- * @remarks
26
- * Used by WebGL2 Only
27
- */
28
- protected vao: WebGLVertexArrayObject | undefined;
29
22
  protected renderer: WebGlRenderer;
30
23
  protected glw: WebGlContextWrapper;
31
24
  protected attributeLocations: string[];
@@ -45,22 +38,15 @@ export class WebGlShaderProgram implements CoreShaderProgram {
45
38
  this.renderer = renderer;
46
39
  const glw = (this.glw = renderer.glw);
47
40
 
48
- // Check that extensions are supported
49
- const webGl2 = glw.isWebGl2();
50
- let requiredExtensions: string[] = [];
51
-
52
41
  this.supportsIndexedTextures =
53
42
  config.supportsIndexedTextures || this.supportsIndexedTextures;
54
- requiredExtensions =
55
- (webGl2 && config.webgl2Extensions) ||
56
- (!webGl2 && config.webgl1Extensions) ||
57
- [];
58
43
 
59
- const glVersion = webGl2 ? '2.0' : '1.0';
44
+ // Check that required extensions are supported
45
+ const requiredExtensions = config.webgl1Extensions || [];
60
46
  requiredExtensions.forEach((extensionName) => {
61
47
  if (!glw.getExtension(extensionName)) {
62
48
  throw new Error(
63
- `Shader "${this.constructor.name}" requires extension "${extensionName}" for WebGL ${glVersion} but wasn't found`,
49
+ `Shader "${this.constructor.name}" requires extension "${extensionName}" for WebGL 1.0 but wasn't found`,
64
50
  );
65
51
  }
66
52
  });
@@ -310,9 +296,6 @@ export class WebGlShaderProgram implements CoreShaderProgram {
310
296
  return;
311
297
  }
312
298
  this.glw.useProgram(this.program, this.uniformLocations!);
313
- if (this.glw.isWebGl2() && this.vao) {
314
- this.glw.bindVertexArray(this.vao);
315
- }
316
299
  }
317
300
 
318
301
  detach(): void {
@@ -1,92 +1,5 @@
1
1
  import type { WebGlContextWrapper } from '../../../lib/WebGlContextWrapper.js';
2
2
 
3
- export interface CoreWebGlParameters {
4
- MAX_RENDERBUFFER_SIZE: number;
5
- MAX_TEXTURE_SIZE: number;
6
- MAX_VIEWPORT_DIMS: Int32Array;
7
- MAX_VERTEX_TEXTURE_IMAGE_UNITS: number;
8
- MAX_TEXTURE_IMAGE_UNITS: number;
9
- MAX_COMBINED_TEXTURE_IMAGE_UNITS: number;
10
- MAX_VERTEX_ATTRIBS: number;
11
- MAX_VARYING_VECTORS: number;
12
- MAX_VERTEX_UNIFORM_VECTORS: number;
13
- MAX_FRAGMENT_UNIFORM_VECTORS: number;
14
- }
15
-
16
- /**
17
- * Get device specific webgl parameters
18
- * @param glw
19
- */
20
- export function getWebGlParameters(
21
- glw: WebGlContextWrapper,
22
- ): CoreWebGlParameters {
23
- const params: CoreWebGlParameters = {
24
- MAX_RENDERBUFFER_SIZE: 0,
25
- MAX_TEXTURE_SIZE: 0,
26
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
27
- MAX_VIEWPORT_DIMS: 0 as any, // Code below will replace this with an Int32Array
28
- MAX_VERTEX_TEXTURE_IMAGE_UNITS: 0,
29
- MAX_TEXTURE_IMAGE_UNITS: 0,
30
- MAX_COMBINED_TEXTURE_IMAGE_UNITS: 0,
31
- MAX_VERTEX_ATTRIBS: 0,
32
- MAX_VARYING_VECTORS: 0,
33
- MAX_VERTEX_UNIFORM_VECTORS: 0,
34
- MAX_FRAGMENT_UNIFORM_VECTORS: 0,
35
- };
36
-
37
- // Map over all parameters and get them
38
- const keys = Object.keys(params) as Array<keyof CoreWebGlParameters>;
39
- keys.forEach((key) => {
40
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
41
- params[key] = glw.getParameter(glw[key]);
42
- });
43
-
44
- return params;
45
- }
46
-
47
- export interface CoreWebGlExtensions {
48
- ANGLE_instanced_arrays: ANGLE_instanced_arrays | null;
49
- WEBGL_compressed_texture_s3tc: WEBGL_compressed_texture_s3tc | null;
50
- WEBGL_compressed_texture_astc: WEBGL_compressed_texture_astc | null;
51
- WEBGL_compressed_texture_etc: WEBGL_compressed_texture_etc | null;
52
- WEBGL_compressed_texture_etc1: WEBGL_compressed_texture_etc1 | null;
53
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
- WEBGL_compressed_texture_pvrtc: any | null;
55
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
- WEBKIT_WEBGL_compressed_texture_pvrtc: any | null;
57
- WEBGL_compressed_texture_s3tc_srgb: WEBGL_compressed_texture_s3tc_srgb | null;
58
- OES_vertex_array_object: OES_vertex_array_object | null;
59
- }
60
-
61
- /**
62
- * Get device webgl extensions
63
- * @param glw
64
- */
65
- export function getWebGlExtensions(
66
- glw: WebGlContextWrapper,
67
- ): CoreWebGlExtensions {
68
- const extensions: CoreWebGlExtensions = {
69
- ANGLE_instanced_arrays: null,
70
- WEBGL_compressed_texture_s3tc: null,
71
- WEBGL_compressed_texture_astc: null,
72
- WEBGL_compressed_texture_etc: null,
73
- WEBGL_compressed_texture_etc1: null,
74
- WEBGL_compressed_texture_pvrtc: null,
75
- WEBKIT_WEBGL_compressed_texture_pvrtc: null,
76
- WEBGL_compressed_texture_s3tc_srgb: null,
77
- OES_vertex_array_object: null,
78
- };
79
-
80
- // Map over all extensions and get them
81
- const keys = Object.keys(extensions) as Array<keyof CoreWebGlExtensions>;
82
- keys.forEach((key) => {
83
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
84
- extensions[key] = glw.getExtension(key);
85
- });
86
-
87
- return extensions;
88
- }
89
-
90
3
  /**
91
4
  * Allocate big memory chunk that we
92
5
  * can re-use to draw quads
@@ -85,7 +85,6 @@ export interface ShaderOptions {
85
85
  shaderSources?: ShaderProgramSources;
86
86
  supportsIndexedTextures?: boolean;
87
87
  webgl1Extensions?: string[];
88
- webgl2Extensions?: string[];
89
88
  }
90
89
 
91
90
  // prettier-ignore
@@ -19,12 +19,6 @@ interface CanvasFont {
19
19
  metrics?: FontMetrics;
20
20
  }
21
21
 
22
- /**
23
- * Global font set regardless of if run in the main thread or a web worker
24
- */
25
- // const globalFontSet: FontFaceSet = (resolvedGlobal.document?.fonts ||
26
- // (resolvedGlobal as unknown as { fonts: FontFaceSet }).fonts) as FontFaceSet;
27
-
28
22
  // Global state variables for fontHandler
29
23
  const fontFamilies: Record<string, FontFace> = {};
30
24
  const fontLoadPromises = new Map<string, Promise<void>>();
@@ -40,10 +40,22 @@ const layoutCache = new Map<
40
40
  }
41
41
  >();
42
42
 
43
+ // Upper bound on layoutCache entries, enforced on idle via `cleanup`.
44
+ // Overridden from stage options in `init`. Note: the Canvas path does not
45
+ // currently populate `layoutCache`, so this is effectively inert today and
46
+ // exists to keep the eviction policy uniform with the SDF backend should
47
+ // Canvas layout caching be wired up later.
48
+ let maxLayoutCacheSize = 250;
49
+
43
50
  // Initialize the Text Renderer
44
51
  const init = (stage: Stage): void => {
45
52
  const dpr = stage.options.devicePhysicalPixelRatio;
46
53
 
54
+ const configuredCacheSize = stage.options.textLayoutCacheSize;
55
+ if (configuredCacheSize !== undefined) {
56
+ maxLayoutCacheSize = configuredCacheSize;
57
+ }
58
+
47
59
  // Drawing canvas and context
48
60
  canvas = stage.platform.createCanvas() as HTMLCanvasElement | OffscreenCanvas;
49
61
  context = canvas.getContext('2d', { willReadFrequently: true }) as
@@ -224,6 +236,19 @@ const clearLayoutCache = (): void => {
224
236
  layoutCache.clear();
225
237
  };
226
238
 
239
+ /**
240
+ * Trim the layout cache back down to `maxLayoutCacheSize`, evicting the
241
+ * least-recently-used entries first. Called when the stage goes idle. The
242
+ * Canvas path does not currently populate `layoutCache`, so this is a no-op in
243
+ * practice today; it mirrors the SDF backend's eviction policy.
244
+ */
245
+ const cleanup = (): void => {
246
+ while (layoutCache.size > maxLayoutCacheSize) {
247
+ const oldest = layoutCache.keys().next().value as string;
248
+ layoutCache.delete(oldest);
249
+ }
250
+ };
251
+
227
252
  /**
228
253
  * Add quads for rendering (Canvas doesn't use quads)
229
254
  */
@@ -252,6 +277,7 @@ const CanvasTextRenderer = {
252
277
  renderQuads,
253
278
  init,
254
279
  clearLayoutCache,
280
+ cleanup,
255
281
  };
256
282
 
257
283
  export default CanvasTextRenderer;