@solidtv/renderer 1.3.2 → 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 (58) hide show
  1. package/dist/src/core/CoreNode.d.ts +0 -2
  2. package/dist/src/core/CoreNode.js.map +1 -1
  3. package/dist/src/core/Stage.js +1 -2
  4. package/dist/src/core/Stage.js.map +1 -1
  5. package/dist/src/core/lib/ImageWorker.d.ts +16 -1
  6. package/dist/src/core/lib/ImageWorker.js +52 -17
  7. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  8. package/dist/src/core/lib/WebGlContextWrapper.d.ts +2 -32
  9. package/dist/src/core/lib/WebGlContextWrapper.js +26 -81
  10. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  11. package/dist/src/core/renderers/CoreRenderer.d.ts +0 -1
  12. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  13. package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +1 -7
  14. package/dist/src/core/renderers/webgl/WebGlRenderer.js +9 -13
  15. package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
  16. package/dist/src/core/renderers/webgl/WebGlShaderNode.d.ts +2 -3
  17. package/dist/src/core/renderers/webgl/WebGlShaderNode.js.map +1 -1
  18. package/dist/src/core/renderers/webgl/WebGlShaderProgram.d.ts +0 -7
  19. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +3 -19
  20. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
  21. package/dist/src/core/renderers/webgl/internal/RendererUtils.d.ts +0 -33
  22. package/dist/src/core/renderers/webgl/internal/RendererUtils.js +0 -50
  23. package/dist/src/core/renderers/webgl/internal/RendererUtils.js.map +1 -1
  24. package/dist/src/core/renderers/webgl/internal/ShaderUtils.d.ts +0 -1
  25. package/dist/src/core/renderers/webgl/internal/ShaderUtils.js.map +1 -1
  26. package/dist/src/core/text-rendering/CanvasFontHandler.js +0 -5
  27. package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
  28. package/dist/src/core/text-rendering/SdfTextRenderer.js +23 -4
  29. package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
  30. package/dist/src/main-api/Inspector.d.ts +2 -1
  31. package/dist/src/main-api/Inspector.js +43 -15
  32. package/dist/src/main-api/Inspector.js.map +1 -1
  33. package/dist/src/main-api/Renderer.d.ts +0 -10
  34. package/dist/src/main-api/Renderer.js +1 -3
  35. package/dist/src/main-api/Renderer.js.map +1 -1
  36. package/dist/src/utils.d.ts +1 -1
  37. package/dist/src/utils.js +2 -2
  38. package/dist/src/utils.js.map +1 -1
  39. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  40. package/package.json +1 -1
  41. package/src/core/CoreNode.ts +0 -3
  42. package/src/core/Stage.ts +0 -2
  43. package/src/core/lib/ImageWorker.test.ts +106 -0
  44. package/src/core/lib/ImageWorker.ts +60 -22
  45. package/src/core/lib/WebGlContextWrapper.ts +32 -101
  46. package/src/core/renderers/CoreRenderer.ts +0 -1
  47. package/src/core/renderers/webgl/WebGlRenderer.ts +9 -26
  48. package/src/core/renderers/webgl/WebGlShaderNode.ts +2 -3
  49. package/src/core/renderers/webgl/WebGlShaderProgram.ts +3 -20
  50. package/src/core/renderers/webgl/internal/RendererUtils.ts +0 -87
  51. package/src/core/renderers/webgl/internal/ShaderUtils.ts +0 -1
  52. package/src/core/text-rendering/CanvasFontHandler.ts +0 -6
  53. package/src/core/text-rendering/SdfTextRenderer.test.ts +17 -0
  54. package/src/core/text-rendering/SdfTextRenderer.ts +24 -4
  55. package/src/main-api/Inspector.ts +46 -16
  56. package/src/main-api/Renderer.ts +1 -14
  57. package/src/utils.ts +1 -2
  58. package/src/core/renderers/webgl/internal/WebGlUtils.ts +0 -16
@@ -0,0 +1,106 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { ImageWorkerManager } from './ImageWorker.js';
3
+ import type { CreateImageBitmapSupport } from './validateImageBitmap.js';
4
+
5
+ /**
6
+ * Minimal Web Worker stand-in. The real Worker spawns a thread that parses the
7
+ * blob script; here we only care about how many are constructed and when, so we
8
+ * record every instance and never auto-respond (so `getImage` calls stay
9
+ * "in flight" and accumulate load, simulating concurrency).
10
+ */
11
+ class FakeWorker {
12
+ static instances: FakeWorker[] = [];
13
+ static reset() {
14
+ FakeWorker.instances = [];
15
+ }
16
+ onmessage: ((e: unknown) => void) | null = null;
17
+ onerror: ((e: unknown) => void) | null = null;
18
+ onmessageerror: ((e: unknown) => void) | null = null;
19
+ postMessage = vi.fn();
20
+ terminate = vi.fn();
21
+ constructor(public url: string) {
22
+ FakeWorker.instances.push(this);
23
+ }
24
+ }
25
+
26
+ const support: CreateImageBitmapSupport = {
27
+ basic: true,
28
+ options: true,
29
+ full: true,
30
+ premultiplyHonored: true,
31
+ };
32
+
33
+ let createObjectURL: ReturnType<typeof vi.fn>;
34
+ let revokeObjectURL: ReturnType<typeof vi.fn>;
35
+
36
+ beforeEach(() => {
37
+ FakeWorker.reset();
38
+ createObjectURL = vi.fn(() => 'blob:fake-url');
39
+ revokeObjectURL = vi.fn();
40
+ // The manager builds the worker source via Blob + object URL, then spawns
41
+ // Workers. None of these exist in the node test env, so stub them.
42
+ vi.stubGlobal(
43
+ 'Blob',
44
+ class {
45
+ constructor(public parts: unknown[], public opts: unknown) {}
46
+ },
47
+ );
48
+ vi.stubGlobal('URL', { createObjectURL, revokeObjectURL });
49
+ vi.stubGlobal('self', { URL: { createObjectURL, revokeObjectURL } });
50
+ vi.stubGlobal('Worker', FakeWorker);
51
+ });
52
+
53
+ afterEach(() => {
54
+ vi.unstubAllGlobals();
55
+ });
56
+
57
+ const load = (mgr: ImageWorkerManager) =>
58
+ void mgr.getImage('img.png', null, null, null, null, null);
59
+
60
+ describe('ImageWorkerManager pool spawning', () => {
61
+ it('does not spawn any workers at construction', () => {
62
+ new ImageWorkerManager(3, support);
63
+ // Spawning is lazy — nothing happens until the first image request.
64
+ expect(FakeWorker.instances.length).toBe(0);
65
+ });
66
+
67
+ it('spawns the whole pool at once on the first image request', () => {
68
+ const mgr = new ImageWorkerManager(3, support);
69
+ load(mgr);
70
+ expect(FakeWorker.instances.length).toBe(3);
71
+ });
72
+
73
+ it('does not respawn the pool on subsequent requests', () => {
74
+ const mgr = new ImageWorkerManager(3, support);
75
+ load(mgr); // spawns the pool
76
+ expect(FakeWorker.instances.length).toBe(3);
77
+ // Subsequent requests reuse the existing pool — no new workers.
78
+ load(mgr);
79
+ load(mgr);
80
+ load(mgr);
81
+ expect(FakeWorker.instances.length).toBe(3);
82
+ });
83
+
84
+ it('spawns exactly maxWorkers when that is 1', () => {
85
+ const mgr = new ImageWorkerManager(1, support);
86
+ load(mgr);
87
+ expect(FakeWorker.instances.length).toBe(1);
88
+ load(mgr);
89
+ load(mgr);
90
+ expect(FakeWorker.instances.length).toBe(1);
91
+ });
92
+
93
+ it('serializes the worker source once, reusing the blob per worker', () => {
94
+ const mgr = new ImageWorkerManager(3, support);
95
+ load(mgr);
96
+ // One object URL created+revoked per spawned worker (3), serialized once.
97
+ expect(createObjectURL).toHaveBeenCalledTimes(3);
98
+ expect(revokeObjectURL).toHaveBeenCalledTimes(3);
99
+ });
100
+
101
+ it('routes each request to a worker via postMessage', () => {
102
+ const mgr = new ImageWorkerManager(2, support);
103
+ load(mgr);
104
+ expect(FakeWorker.instances[0]!.postMessage).toHaveBeenCalledTimes(1);
105
+ });
106
+ });
@@ -177,20 +177,32 @@ export class ImageWorkerManager {
177
177
  workers: Worker[] = [];
178
178
  workerLoad: number[] = [];
179
179
  nextId = 0;
180
+ /** Upper bound on the pool size, from the `numImageWorkers` setting. */
181
+ private readonly maxWorkers: number;
182
+ /** Retained so the worker source is serialized once, when the pool spawns. */
183
+ private workerBlob: Blob | null = null;
184
+ private readonly createImageBitmapSupport: CreateImageBitmapSupport;
180
185
 
181
186
  constructor(
182
187
  numImageWorkers: number,
183
188
  createImageBitmapSupport: CreateImageBitmapSupport,
184
189
  ) {
185
- this.workers = this.createWorkers(
186
- numImageWorkers,
187
- createImageBitmapSupport,
188
- );
189
- this.workers.forEach((worker, index) => {
190
- worker.onmessage = (event) => this.handleMessage(event, index);
191
- worker.onerror = (event) => this.handleWorkerError(event, index);
192
- worker.onmessageerror = (event) => this.handleWorkerError(event, index);
193
- });
190
+ this.maxWorkers = numImageWorkers;
191
+ this.createImageBitmapSupport = createImageBitmapSupport;
192
+ }
193
+
194
+ /**
195
+ * Build the shared worker source once and spawn the full pool in a single
196
+ * burst. Called lazily on the first image request. No-op once spawned.
197
+ */
198
+ private spawnWorkers(): void {
199
+ if (this.workers.length > 0) {
200
+ return;
201
+ }
202
+ this.workerBlob = this.createWorkerBlob(this.createImageBitmapSupport);
203
+ for (let i = 0; i < this.maxWorkers; i++) {
204
+ this.spawnWorker();
205
+ }
194
206
  }
195
207
 
196
208
  private handleMessage(event: MessageEvent, workerIndex: number) {
@@ -231,10 +243,9 @@ export class ImageWorkerManager {
231
243
  this.workerLoad[workerIndex] = 0;
232
244
  }
233
245
 
234
- private createWorkers(
235
- numWorkers = 1,
246
+ private createWorkerBlob(
236
247
  createImageBitmapSupport: CreateImageBitmapSupport,
237
- ): Worker[] {
248
+ ): Blob {
238
249
  let workerCode = `(${createImageWorker.toString()})()`;
239
250
 
240
251
  // Replace placeholders with actual initialization values
@@ -265,19 +276,39 @@ export class ImageWorkerManager {
265
276
  }
266
277
 
267
278
  workerCode = workerCode.replace('"use strict";', '');
268
- const blob: Blob = new Blob([workerCode], {
279
+ return new Blob([workerCode], {
269
280
  type: 'application/javascript',
270
281
  });
271
- const urlFactory = self.URL ? URL : webkitURL;
272
- const blobURL: string = urlFactory.createObjectURL(blob);
273
- const workers: Worker[] = [];
274
- for (let i = 0; i < numWorkers; i++) {
275
- workers.push(new Worker(blobURL));
276
- this.workerLoad.push(0);
282
+ }
283
+
284
+ /**
285
+ * Spawn a single worker from the shared blob and wire up its handlers.
286
+ * No-op once the pool has reached `maxWorkers`.
287
+ */
288
+ private spawnWorker(): void {
289
+ if (this.workerBlob === null || this.workers.length >= this.maxWorkers) {
290
+ return;
277
291
  }
278
- // Workers retain the script; the URL itself is no longer needed.
292
+
293
+ const index = this.workers.length;
294
+ const urlFactory = self.URL ? URL : webkitURL;
295
+ const blobURL: string = urlFactory.createObjectURL(this.workerBlob);
296
+ const worker = new Worker(blobURL);
297
+ // The worker retains the script after construction; the URL is no longer
298
+ // needed once the Worker has been created from it.
279
299
  urlFactory.revokeObjectURL(blobURL);
280
- return workers;
300
+
301
+ worker.onmessage = (event) => this.handleMessage(event, index);
302
+ worker.onerror = (event) => this.handleWorkerError(event, index);
303
+ worker.onmessageerror = (event) => this.handleWorkerError(event, index);
304
+
305
+ this.workers.push(worker);
306
+ this.workerLoad.push(0);
307
+
308
+ // Pool is full — the blob will never be needed again, so release it.
309
+ if (this.workers.length >= this.maxWorkers) {
310
+ this.workerBlob = null;
311
+ }
281
312
  }
282
313
 
283
314
  private getNextWorkerIndex(): number {
@@ -311,7 +342,14 @@ export class ImageWorkerManager {
311
342
  ): Promise<TextureData> {
312
343
  return new Promise((resolve, reject) => {
313
344
  try {
314
- const nextWorkerIndex = this.getNextWorkerIndex();
345
+ let nextWorkerIndex = this.getNextWorkerIndex();
346
+ if (nextWorkerIndex === -1) {
347
+ // Pool not spawned yet — spin up all workers at once on the first
348
+ // image request, off the boot/first-render critical path.
349
+ this.spawnWorkers();
350
+ nextWorkerIndex = this.getNextWorkerIndex();
351
+ }
352
+
315
353
  if (nextWorkerIndex === -1) {
316
354
  reject(new Error('No image workers available'));
317
355
  return;
@@ -6,13 +6,12 @@ import type {
6
6
  Vec3,
7
7
  Vec4,
8
8
  } from '../renderers/webgl/internal/ShaderUtils.js';
9
- import { isWebGl2 } from '../renderers/webgl/internal/WebGlUtils.js';
10
9
 
11
10
  /**
12
11
  * Optimized WebGL Context Wrapper
13
12
  *
14
13
  * @remarks
15
- * This class contains the subset of the WebGLRenderingContext & WebGL2RenderingContext
14
+ * This class contains the subset of the WebGLRenderingContext
16
15
  * API that is used by the renderer. Select high volume WebGL methods include
17
16
  * caching optimizations to avoid making WebGL calls if the state is already set
18
17
  * to the desired value.
@@ -94,56 +93,40 @@ export class WebGlContextWrapper {
94
93
  public readonly INVALID_OPERATION: number;
95
94
  //#endregion WebGL Enums
96
95
 
97
- constructor(private gl: WebGLRenderingContext | WebGL2RenderingContext) {
98
- // The following code extracts the current state of the WebGL context
99
- // to our local JavaScript cached version of it. This is so we can
100
- // avoid making WebGL calls if we don't need to.
101
- // We could assume that the WebGL context is in a default state, but
102
- // in the future we may want to support restoring a broken WebGL context
103
- // and this will help with that.
104
- this.activeTextureUnit =
105
- (gl.getParameter(gl.ACTIVE_TEXTURE) as number) - gl.TEXTURE0;
96
+ constructor(private gl: WebGLRenderingContext) {
97
+ // A freshly created WebGL context is in a fully specified default state.
98
+ // Rather than reading that state back with getParameter/isEnabled each a
99
+ // synchronous CPU<->GPU round-trip, and previously ~one per texture unit
100
+ // (16-32) just to enumerate the bindings we seed the cache with those
101
+ // known defaults. The engine never restores a lost context in place (it
102
+ // reloads the app on `contextLost`), so there is no live state to recover.
103
+ this.activeTextureUnit = 0;
106
104
  const maxTextureUnits = gl.getParameter(
107
105
  gl.MAX_TEXTURE_IMAGE_UNITS,
108
106
  ) as number;
109
- // save current texture units
110
- this.texture2dUnits = new Array<undefined>(maxTextureUnits)
111
- .fill(undefined)
112
- .map((_, i) => {
113
- this.activeTexture(i);
114
- return gl.getParameter(gl.TEXTURE_BINDING_2D) as WebGLTexture;
115
- });
116
- // restore active texture unit
117
- this.activeTexture(this.activeTextureUnit);
118
- this.scissorEnabled = gl.isEnabled(gl.SCISSOR_TEST);
119
-
120
- const scissorBox = gl.getParameter(gl.SCISSOR_BOX) as [
121
- number,
122
- number,
123
- number,
124
- number,
125
- ];
126
- this.scissorX = scissorBox[0];
127
- this.scissorY = scissorBox[1];
128
- this.scissorWidth = scissorBox[2];
129
- this.scissorHeight = scissorBox[3];
130
-
131
- this.blendEnabled = gl.isEnabled(gl.BLEND);
132
- this.blendSrcRgb = gl.getParameter(gl.BLEND_SRC_RGB) as number;
133
- this.blendDstRgb = gl.getParameter(gl.BLEND_DST_RGB) as number;
134
- this.blendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA) as number;
135
- this.blendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA) as number;
136
-
137
- this.boundArrayBuffer = gl.getParameter(
138
- gl.ARRAY_BUFFER_BINDING,
139
- ) as WebGLBuffer;
140
- this.boundElementArrayBuffer = gl.getParameter(
141
- gl.ELEMENT_ARRAY_BUFFER_BINDING,
142
- ) as WebGLBuffer;
143
-
144
- this.curProgram = gl.getParameter(
145
- gl.CURRENT_PROGRAM,
146
- ) as WebGLProgram | null;
107
+ // All texture units start unbound (TEXTURE_BINDING_2D === null).
108
+ this.texture2dUnits = new Array<WebGLTexture | null>(maxTextureUnits).fill(
109
+ null,
110
+ );
111
+
112
+ // SCISSOR_TEST disabled; SCISSOR_BOX defaults to the full drawing buffer.
113
+ this.scissorEnabled = false;
114
+ this.scissorX = 0;
115
+ this.scissorY = 0;
116
+ this.scissorWidth = gl.drawingBufferWidth;
117
+ this.scissorHeight = gl.drawingBufferHeight;
118
+
119
+ // BLEND disabled; blend func defaults to (ONE, ZERO) for both RGB and alpha.
120
+ this.blendEnabled = false;
121
+ this.blendSrcRgb = gl.ONE;
122
+ this.blendDstRgb = gl.ZERO;
123
+ this.blendSrcAlpha = gl.ONE;
124
+ this.blendDstAlpha = gl.ZERO;
125
+
126
+ // No buffers or program bound on a fresh context.
127
+ this.boundArrayBuffer = null;
128
+ this.boundElementArrayBuffer = null;
129
+ this.curProgram = null;
147
130
 
148
131
  this.canvas = gl.canvas;
149
132
 
@@ -175,9 +158,6 @@ export class WebGlContextWrapper {
175
158
  this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT;
176
159
  this.ONE = gl.ONE;
177
160
  this.ONE_MINUS_SRC_ALPHA = gl.ONE_MINUS_SRC_ALPHA;
178
- this.MAX_VERTEX_TEXTURE_IMAGE_UNITS = gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS;
179
- this.TRIANGLES = gl.TRIANGLES;
180
- this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT;
181
161
  this.VERTEX_SHADER = gl.VERTEX_SHADER;
182
162
  this.FRAGMENT_SHADER = gl.FRAGMENT_SHADER;
183
163
  this.STATIC_DRAW = gl.STATIC_DRAW;
@@ -188,15 +168,6 @@ export class WebGlContextWrapper {
188
168
  this.INVALID_ENUM = gl.INVALID_ENUM;
189
169
  this.INVALID_OPERATION = gl.INVALID_OPERATION;
190
170
  }
191
- /**
192
- * Returns true if the WebGL context is WebGL2
193
- *
194
- * @returns
195
- */
196
- isWebGl2() {
197
- return isWebGl2(this.gl);
198
- }
199
-
200
171
  /**
201
172
  * ```
202
173
  * gl.activeTexture(textureUnit + gl.TEXTURE0);
@@ -1109,33 +1080,6 @@ export class WebGlContextWrapper {
1109
1080
  return this.gl.getError();
1110
1081
  }
1111
1082
 
1112
- /**
1113
- * ```
1114
- * gl.createVertexArray();
1115
- * ```
1116
- *
1117
- * @returns
1118
- */
1119
- createVertexArray() {
1120
- if (this.gl instanceof WebGL2RenderingContext) {
1121
- return this.gl.createVertexArray();
1122
- }
1123
- return undefined;
1124
- }
1125
-
1126
- /**
1127
- * ```
1128
- * gl.bindVertexArray(vertexArray);
1129
- * ```
1130
- *
1131
- * @param vertexArray
1132
- */
1133
- bindVertexArray(vertexArray: WebGLVertexArrayObject | null) {
1134
- if (this.gl instanceof WebGL2RenderingContext) {
1135
- this.gl.bindVertexArray(vertexArray);
1136
- }
1137
- }
1138
-
1139
1083
  /**
1140
1084
  * ```
1141
1085
  * gl.getAttribLocation(program, name);
@@ -1341,19 +1285,6 @@ export class WebGlContextWrapper {
1341
1285
  }
1342
1286
  }
1343
1287
 
1344
- /**
1345
- * ```
1346
- * gl.deleteVertexArray(vertexArray);
1347
- * ```
1348
- *
1349
- * @param vertexArray - The vertex array object to delete
1350
- */
1351
- deleteVertexArray(vertexArray: WebGLVertexArrayObject) {
1352
- if (this.isWebGl2()) {
1353
- (this.gl as WebGL2RenderingContext).deleteVertexArray(vertexArray);
1354
- }
1355
- }
1356
-
1357
1288
  /**
1358
1289
  * Check for WebGL errors and return error information
1359
1290
  * @param operation Description of the operation for error reporting
@@ -10,7 +10,6 @@ export interface CoreRendererOptions {
10
10
  stage: Stage;
11
11
  canvas: HTMLCanvasElement | OffscreenCanvas;
12
12
  contextSpy: ContextSpy | null;
13
- forceWebGL2: boolean;
14
13
  }
15
14
 
16
15
  export interface BufferInfo {
@@ -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
 
@@ -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>>();