@solidtv/renderer 1.4.2 → 1.5.0-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.
Files changed (36) hide show
  1. package/dist/src/common/CommonTypes.d.ts +20 -0
  2. package/dist/src/core/Stage.d.ts +1 -0
  3. package/dist/src/core/Stage.js +11 -1
  4. package/dist/src/core/Stage.js.map +1 -1
  5. package/dist/src/core/lib/WebGlContextWrapper.d.ts +53 -3
  6. package/dist/src/core/lib/WebGlContextWrapper.js +96 -2
  7. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  8. package/dist/src/core/renderers/CoreRenderer.d.ts +36 -0
  9. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  10. package/dist/src/core/renderers/canvas/CanvasRenderer.d.ts +2 -1
  11. package/dist/src/core/renderers/canvas/CanvasRenderer.js +10 -1
  12. package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
  13. package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +10 -1
  14. package/dist/src/core/renderers/webgl/WebGlRenderer.js +19 -1
  15. package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
  16. package/dist/src/core/renderers/webgl/WebGlShaderProgram.d.ts +24 -0
  17. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +74 -0
  18. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
  19. package/dist/src/core/renderers/webgl/internal/RendererUtils.d.ts +1 -1
  20. package/dist/src/core/renderers/webgl/internal/RendererUtils.js +1 -0
  21. package/dist/src/core/renderers/webgl/internal/RendererUtils.js.map +1 -1
  22. package/dist/src/main-api/Renderer.d.ts +33 -1
  23. package/dist/src/main-api/Renderer.js +17 -0
  24. package/dist/src/main-api/Renderer.js.map +1 -1
  25. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  26. package/package.json +1 -1
  27. package/src/common/CommonTypes.ts +20 -0
  28. package/src/core/Stage.ts +14 -1
  29. package/src/core/lib/WebGlContextWrapper.ts +103 -3
  30. package/src/core/renderers/CoreRenderer.ts +37 -0
  31. package/src/core/renderers/canvas/CanvasRenderer.test.ts +14 -0
  32. package/src/core/renderers/canvas/CanvasRenderer.ts +15 -1
  33. package/src/core/renderers/webgl/WebGlRenderer.ts +22 -1
  34. package/src/core/renderers/webgl/WebGlShaderProgram.ts +83 -0
  35. package/src/core/renderers/webgl/internal/RendererUtils.ts +5 -1
  36. package/src/main-api/Renderer.ts +36 -1
@@ -9,6 +9,7 @@ import {
9
9
  CoreRenderer,
10
10
  type BufferInfo,
11
11
  type CoreRendererOptions,
12
+ type RendererCapabilities,
12
13
  } from '../CoreRenderer.js';
13
14
  import { SdfRenderOp } from './SdfRenderOp.js';
14
15
  import type { CoreContextTexture } from '../CoreContextTexture.js';
@@ -133,6 +134,15 @@ export class WebGlRenderer extends CoreRenderer {
133
134
  defaultShaderNode: WebGlShaderNode | null = null;
134
135
  quadBufferCollection: BufferCollection;
135
136
 
137
+ /**
138
+ * Shared static element (index) buffer for quad rendering.
139
+ *
140
+ * @remarks
141
+ * Bound once globally, but also recorded into each shader program's Vertex
142
+ * Array Object since the element-array binding is part of VAO state.
143
+ */
144
+ indexBuffer: WebGLBuffer | null = null;
145
+
136
146
  clearColor: WebGlColor = {
137
147
  raw: 0x00000000,
138
148
  normalized: [0, 0, 0, 0],
@@ -187,7 +197,7 @@ export class WebGlRenderer extends CoreRenderer {
187
197
  glw.setBlend(true);
188
198
  glw.blendFunc(glw.ONE, glw.ONE_MINUS_SRC_ALPHA);
189
199
 
190
- createIndexBuffer(glw, this.stage.bufferMemory);
200
+ this.indexBuffer = createIndexBuffer(glw, this.stage.bufferMemory);
191
201
 
192
202
  // Create the static node coords buffer
193
203
  // 80 is the magic number used in createIndexBuffer
@@ -1280,6 +1290,17 @@ export class WebGlRenderer extends CoreRenderer {
1280
1290
  return bufferInfo;
1281
1291
  }
1282
1292
 
1293
+ getCapabilities(): RendererCapabilities {
1294
+ const glw = this.glw;
1295
+ return {
1296
+ renderMode: 'webgl',
1297
+ webGlVersion: glw.isWebGl2 ? 2 : 1,
1298
+ vertexArrayObject: glw.canUseVertexArrayObject,
1299
+ maxTextureSize: glw.getParameter(glw.MAX_TEXTURE_SIZE) as number,
1300
+ maxTextureUnits: glw.getParameter(glw.MAX_TEXTURE_IMAGE_UNITS) as number,
1301
+ };
1302
+ }
1303
+
1283
1304
  /**
1284
1305
  * Drain the GL error queue once and report whether a GL_OUT_OF_MEMORY was
1285
1306
  * seen since the last call.
@@ -30,6 +30,19 @@ export class WebGlShaderProgram implements CoreShaderProgram {
30
30
  public isDestroyed = false;
31
31
  supportsIndexedTextures = false;
32
32
 
33
+ /**
34
+ * Cached Vertex Array Objects, keyed by the buffer collection they capture.
35
+ *
36
+ * @remarks
37
+ * A VAO records this program's attribute layout (enabled arrays, pointers and
38
+ * their source buffers) plus the shared element index buffer, so a draw only
39
+ * needs a single `bindVertexArray` instead of re-pointing every attribute. In
40
+ * practice a program only ever binds one collection, but keying by collection
41
+ * keeps it correct if that ever changes. Empty / unused when the context has
42
+ * no VAO support (see {@link WebGlContextWrapper.canUseVertexArrayObject}).
43
+ */
44
+ protected vaos = new Map<BufferCollection, WebGLVertexArrayObject | null>();
45
+
33
46
  constructor(
34
47
  renderer: WebGlRenderer,
35
48
  config: WebGlShaderType,
@@ -259,6 +272,38 @@ export class WebGlShaderProgram implements CoreShaderProgram {
259
272
 
260
273
  bindBufferCollection(buffer: BufferCollection) {
261
274
  const { glw } = this;
275
+
276
+ if (glw.canUseVertexArrayObject === true) {
277
+ let vao = this.vaos.get(buffer);
278
+ if (vao === undefined) {
279
+ // First draw with this collection: try to capture the attribute layout
280
+ // in a VAO. Cache the result — including a null on allocation failure,
281
+ // so we don't retry createVertexArray every frame.
282
+ vao = this.createVao(buffer);
283
+ this.vaos.set(buffer, vao);
284
+ }
285
+ if (vao !== null) {
286
+ // createVao leaves a freshly built VAO bound; re-binding is cheap and
287
+ // keeps the build and reuse paths identical.
288
+ glw.bindVertexArray(vao);
289
+ return;
290
+ }
291
+ // VAO allocation failed (e.g. under GL OOM). Bind the default VAO so the
292
+ // per-draw attribute setup below records into it rather than corrupting
293
+ // another program's cached VAO, then fall through.
294
+ glw.bindVertexArray(null);
295
+ }
296
+
297
+ // No (usable) VAO: re-point every attribute on each draw.
298
+ this.bindAttributes(buffer);
299
+ }
300
+
301
+ /**
302
+ * Point this program's vertex attributes at the given buffer collection.
303
+ * When a VAO is bound this records into it; otherwise it mutates global state.
304
+ */
305
+ private bindAttributes(buffer: BufferCollection) {
306
+ const { glw } = this;
262
307
  const attribs = this.attributeLocations;
263
308
  const attribLen = attribs.length;
264
309
 
@@ -282,6 +327,30 @@ export class WebGlShaderProgram implements CoreShaderProgram {
282
327
  }
283
328
  }
284
329
 
330
+ /**
331
+ * Create and populate a Vertex Array Object capturing this program's
332
+ * attribute layout for the given buffer collection. The new VAO is left bound.
333
+ * Returns null if the context reports VAO support but allocation fails (e.g.
334
+ * under GL OOM), in which case the caller falls back to per-draw binding.
335
+ */
336
+ private createVao(buffer: BufferCollection): WebGLVertexArrayObject | null {
337
+ const { glw } = this;
338
+ const vao = glw.createVertexArray();
339
+ if (vao === null) {
340
+ return null;
341
+ }
342
+ glw.bindVertexArray(vao);
343
+
344
+ this.bindAttributes(buffer);
345
+
346
+ // The element-array binding is part of VAO state and starts out null on a
347
+ // fresh VAO, so record the shared index buffer into it or indexed
348
+ // drawElements would read from no buffer.
349
+ glw.bindElementArrayBuffer(this.renderer.indexBuffer);
350
+
351
+ return vao;
352
+ }
353
+
285
354
  bindTextures(textures: WebGlCtxTexture[]) {
286
355
  const t = textures[0];
287
356
  if (t === undefined) {
@@ -299,6 +368,13 @@ export class WebGlShaderProgram implements CoreShaderProgram {
299
368
  }
300
369
 
301
370
  detach(): void {
371
+ // With VAOs the enabled-attribute state lives in each per-collection VAO,
372
+ // not in global context state. Disabling here would mutate whichever VAO is
373
+ // currently bound and corrupt it for the next draw, so there's nothing to
374
+ // do on a program switch.
375
+ if (this.glw.canUseVertexArrayObject === true) {
376
+ return;
377
+ }
302
378
  this.disableAttributes();
303
379
  }
304
380
 
@@ -310,6 +386,13 @@ export class WebGlShaderProgram implements CoreShaderProgram {
310
386
 
311
387
  this.detach();
312
388
 
389
+ for (const vao of this.vaos.values()) {
390
+ if (vao !== null) {
391
+ glw.deleteVertexArray(vao);
392
+ }
393
+ }
394
+ this.vaos.clear();
395
+
313
396
  glw.deleteProgram(this.program!);
314
397
  this.program = null;
315
398
  this.uniformLocations = null;
@@ -7,7 +7,10 @@ import type { WebGlContextWrapper } from '../../../lib/WebGlContextWrapper.js';
7
7
  * @param glw
8
8
  * @param size
9
9
  */
10
- export function createIndexBuffer(glw: WebGlContextWrapper, size: number) {
10
+ export function createIndexBuffer(
11
+ glw: WebGlContextWrapper,
12
+ size: number,
13
+ ): WebGLBuffer | null {
11
14
  const maxQuads = ~~(size / 80);
12
15
  const indices = new Uint16Array(maxQuads * 6);
13
16
 
@@ -22,6 +25,7 @@ export function createIndexBuffer(glw: WebGlContextWrapper, size: number) {
22
25
 
23
26
  const buffer = glw.createBuffer();
24
27
  glw.elementArrayBufferData(buffer, indices, glw.STATIC_DRAW);
28
+ return buffer;
25
29
  }
26
30
 
27
31
  /**
@@ -14,6 +14,7 @@ import type { CanvasRenderer } from '../core/renderers/canvas/CanvasRenderer.js'
14
14
  import type { WebGlRenderer } from '../core/renderers/webgl/WebGlRenderer.js';
15
15
  import type { Inspector, InspectorOptions } from './Inspector.js';
16
16
  import type { CoreShaderNode } from '../core/renderers/CoreShaderNode.js';
17
+ import type { RendererCapabilities } from '../core/renderers/CoreRenderer.js';
17
18
  import type {
18
19
  ExtractShaderProps,
19
20
  OptionalShaderProps,
@@ -29,7 +30,8 @@ import { Platform } from '../core/platforms/Platform.js';
29
30
  * @example
30
31
  * ```typescript
31
32
  * renderer.on('fpsUpdate', (_target, data) => {
32
- * console.log(`Current FPS: ${data.fps}`);
33
+ * console.log(`${data.fps} fps, ${data.renderOps} draw calls`);
34
+ * console.log('backend:', data.capabilities.renderMode, data.capabilities.webGlVersion);
33
35
  * if (data.contextSpyData) {
34
36
  * console.log('WebGL calls:', data.contextSpyData);
35
37
  * }
@@ -41,6 +43,21 @@ export interface RendererMainFpsUpdateEvent {
41
43
  fps: number;
42
44
  /** Context spy data (if enabled) - contains WebGL call statistics */
43
45
  contextSpyData?: unknown;
46
+ /**
47
+ * Draw calls (render operations) submitted for the most recent frame.
48
+ * `0` on the Canvas backend.
49
+ */
50
+ renderOps: number;
51
+ /** Quads rendered in the most recent frame. `0` on the Canvas backend. */
52
+ quads: number;
53
+ /**
54
+ * Active backend + device capabilities (constant for the renderer's
55
+ * lifetime). Included so each periodic sample is self-describing for
56
+ * telemetry — e.g. slice fps by `webGlVersion` or `vertexArrayObject`.
57
+ *
58
+ * @see {@link RendererCapabilities}
59
+ */
60
+ capabilities: RendererCapabilities;
44
61
  }
45
62
 
46
63
  /**
@@ -1035,6 +1052,24 @@ export class RendererMain extends EventEmitter {
1035
1052
  return this.stage.renderer.getBufferInfo();
1036
1053
  }
1037
1054
 
1055
+ /**
1056
+ * Report the active rendering backend and device capabilities.
1057
+ *
1058
+ * @remarks
1059
+ * Useful for diagnostics and field logging — e.g. confirming which WebGL
1060
+ * version a device negotiated and whether Vertex Array Objects engaged.
1061
+ * Reads live GL parameters (CPU↔GPU round-trips), so call once at startup
1062
+ * rather than per frame.
1063
+ *
1064
+ * @example
1065
+ * ```ts
1066
+ * console.table(renderer.getCapabilities());
1067
+ * ```
1068
+ */
1069
+ getCapabilities(): RendererCapabilities {
1070
+ return this.stage.renderer.getCapabilities();
1071
+ }
1072
+
1038
1073
  /**
1039
1074
  * Re-render the current frame without advancing any running animations.
1040
1075
  *