@lightningjs/renderer 2.11.1 → 2.12.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 (74) hide show
  1. package/dist/src/common/CommonTypes.d.ts +6 -0
  2. package/dist/src/core/CoreNode.d.ts +2 -1
  3. package/dist/src/core/CoreNode.js +27 -24
  4. package/dist/src/core/CoreNode.js.map +1 -1
  5. package/dist/src/core/CoreTextureManager.d.ts +5 -1
  6. package/dist/src/core/CoreTextureManager.js +40 -12
  7. package/dist/src/core/CoreTextureManager.js.map +1 -1
  8. package/dist/src/core/CoreTexturizer.d.ts +14 -0
  9. package/dist/src/core/CoreTexturizer.js +47 -0
  10. package/dist/src/core/CoreTexturizer.js.map +1 -0
  11. package/dist/src/core/Stage.d.ts +9 -0
  12. package/dist/src/core/Stage.js +20 -1
  13. package/dist/src/core/Stage.js.map +1 -1
  14. package/dist/src/core/TextureMemoryManager.d.ts +1 -0
  15. package/dist/src/core/TextureMemoryManager.js +3 -1
  16. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  17. package/dist/src/core/lib/ImageWorker.js +2 -1
  18. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  19. package/dist/src/core/lib/WebGlContextWrapper.d.ts +7 -0
  20. package/dist/src/core/lib/WebGlContextWrapper.js +9 -0
  21. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  22. package/dist/src/core/renderers/CoreRenderer.d.ts +1 -0
  23. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  24. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.d.ts +1 -0
  25. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js +3 -0
  26. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js.map +1 -1
  27. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +3 -0
  28. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +1 -1
  29. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.d.ts +2 -1
  30. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js +9 -5
  31. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js.map +1 -1
  32. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +5 -0
  33. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  34. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +3 -1
  35. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +25 -18
  36. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  37. package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.js +2 -2
  38. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.d.ts +1 -1
  39. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js +5 -5
  40. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js.map +1 -1
  41. package/dist/src/core/text-rendering/font-face-types/utils.d.ts +1 -0
  42. package/dist/src/core/text-rendering/font-face-types/utils.js +38 -0
  43. package/dist/src/core/text-rendering/font-face-types/utils.js.map +1 -0
  44. package/dist/src/core/textures/ImageTexture.js +2 -1
  45. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  46. package/dist/src/core/textures/SubTexture.js +3 -19
  47. package/dist/src/core/textures/SubTexture.js.map +1 -1
  48. package/dist/src/core/textures/Texture.js +0 -7
  49. package/dist/src/core/textures/Texture.js.map +1 -1
  50. package/dist/src/main-api/Renderer.d.ts +19 -0
  51. package/dist/src/main-api/Renderer.js +21 -0
  52. package/dist/src/main-api/Renderer.js.map +1 -1
  53. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  54. package/package.json +1 -1
  55. package/src/common/CommonTypes.ts +7 -0
  56. package/src/core/CoreNode.ts +34 -28
  57. package/src/core/CoreTextureManager.ts +50 -15
  58. package/src/core/Stage.ts +23 -1
  59. package/src/core/TextureMemoryManager.ts +5 -1
  60. package/src/core/lib/ImageWorker.ts +2 -1
  61. package/src/core/lib/WebGlContextWrapper.ts +10 -0
  62. package/src/core/renderers/CoreRenderer.ts +1 -0
  63. package/src/core/renderers/canvas/CanvasCoreRenderer.ts +4 -0
  64. package/src/core/renderers/canvas/CanvasCoreTexture.ts +3 -0
  65. package/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts +12 -5
  66. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +8 -0
  67. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +31 -21
  68. package/src/core/renderers/webgl/shaders/effects/EffectUtils.ts +2 -2
  69. package/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.ts +5 -5
  70. package/src/core/text-rendering/font-face-types/utils.ts +39 -0
  71. package/src/core/textures/ImageTexture.ts +4 -1
  72. package/src/core/textures/SubTexture.ts +3 -23
  73. package/src/core/textures/Texture.ts +0 -10
  74. package/src/main-api/Renderer.ts +22 -0
@@ -41,10 +41,12 @@ export class CanvasCoreTexture extends CoreContextTexture {
41
41
  this.onLoadRequest()
42
42
  .then((size) => {
43
43
  this.textureSource.setState('loaded', size);
44
+ this.textureSource.freeTextureData();
44
45
  this.updateMemSize();
45
46
  })
46
47
  .catch((err) => {
47
48
  this.textureSource.setState('failed', err as Error);
49
+ this.textureSource.freeTextureData();
48
50
  });
49
51
  }
50
52
 
@@ -53,6 +55,7 @@ export class CanvasCoreTexture extends CoreContextTexture {
53
55
  this.tintCache = undefined;
54
56
  this.textureSource.setState('freed');
55
57
  this.setTextureMemUse(0);
58
+ this.textureSource.freeTextureData();
56
59
  }
57
60
 
58
61
  updateMemSize(): void {
@@ -27,7 +27,7 @@ import { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js';
27
27
  export class WebGlCoreCtxRenderTexture extends WebGlCoreCtxTexture {
28
28
  declare textureSource: RenderTexture;
29
29
 
30
- readonly framebuffer: WebGLFramebuffer;
30
+ public framebuffer: WebGLFramebuffer | null = null;
31
31
 
32
32
  constructor(
33
33
  glw: WebGlContextWrapper,
@@ -35,10 +35,6 @@ export class WebGlCoreCtxRenderTexture extends WebGlCoreCtxTexture {
35
35
  textureSource: RenderTexture,
36
36
  ) {
37
37
  super(glw, memManager, textureSource);
38
- // Create Framebuffer object
39
- const framebuffer = glw.createFramebuffer();
40
- assertTruthy(framebuffer, 'Unable to create framebuffer');
41
- this.framebuffer = framebuffer;
42
38
  }
43
39
 
44
40
  override async onLoadRequest(): Promise<Dimensions> {
@@ -47,6 +43,9 @@ export class WebGlCoreCtxRenderTexture extends WebGlCoreCtxTexture {
47
43
  this.createNativeCtxTexture());
48
44
  const { width, height } = this.textureSource;
49
45
 
46
+ // Create Framebuffer object
47
+ this.framebuffer = glw.createFramebuffer();
48
+
50
49
  // Set the dimensions of the render texture
51
50
  glw.texImage2D(
52
51
  0,
@@ -76,4 +75,12 @@ export class WebGlCoreCtxRenderTexture extends WebGlCoreCtxTexture {
76
75
  height,
77
76
  };
78
77
  }
78
+
79
+ override free(): void {
80
+ super.free();
81
+
82
+ // Delete the framebuffer
83
+ this.glw.deleteFramebuffer(this.framebuffer);
84
+ this.framebuffer = null;
85
+ }
79
86
  }
@@ -111,6 +111,9 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
111
111
  // Update the texture source's width and height so that it can be used
112
112
  // for rendering.
113
113
  this.textureSource.setState('loaded', { width, height });
114
+
115
+ // cleanup source texture data
116
+ this.textureSource.freeTextureData();
114
117
  })
115
118
  .catch((err) => {
116
119
  // If the texture has been freed while loading, return early.
@@ -119,6 +122,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
119
122
  }
120
123
  this.state = 'failed';
121
124
  this.textureSource.setState('failed', err);
125
+ this.textureSource.freeTextureData();
122
126
  console.error(err);
123
127
  });
124
128
  }
@@ -247,6 +251,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
247
251
  if (this.state === 'freed') {
248
252
  return;
249
253
  }
254
+
250
255
  this.state = 'freed';
251
256
  this.textureSource.setState('freed');
252
257
  this._w = 0;
@@ -259,6 +264,9 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
259
264
  glw.deleteTexture(this._nativeCtxTexture);
260
265
  this.setTextureMemUse(0);
261
266
  this._nativeCtxTexture = null;
267
+
268
+ // if the texture still has source data, free it
269
+ this.textureSource.freeTextureData();
262
270
  }
263
271
 
264
272
  /**
@@ -49,7 +49,7 @@ import type { Dimensions } from '../../../common/CommonTypes.js';
49
49
  import { WebGlCoreShader } from './WebGlCoreShader.js';
50
50
  import { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
51
51
  import { RenderTexture } from '../../textures/RenderTexture.js';
52
- import type { CoreNode } from '../../CoreNode.js';
52
+ import { CoreNodeRenderState, type CoreNode } from '../../CoreNode.js';
53
53
  import { WebGlCoreCtxRenderTexture } from './WebGlCoreCtxRenderTexture.js';
54
54
  import type { BaseShaderController } from '../../../main-api/ShaderController.js';
55
55
 
@@ -95,6 +95,7 @@ export class WebGlCoreRenderer extends CoreRenderer {
95
95
  */
96
96
 
97
97
  quadBufferUsage = 0;
98
+ numQuadsRendered = 0;
98
99
  /**
99
100
  * Whether the renderer is currently rendering to a texture.
100
101
  */
@@ -543,25 +544,25 @@ export class WebGlCoreRenderer extends CoreRenderer {
543
544
  }
544
545
 
545
546
  // Switching clipping rect will require a new render operation
546
- if (!compareRect(this.curRenderOp.clippingRect, clippingRect)) {
547
+ if (compareRect(this.curRenderOp.clippingRect, clippingRect) === false) {
547
548
  return false;
548
549
  }
549
550
 
550
551
  // Force new render operation if rendering to texture
551
552
  // @todo: This needs to be improved, render operations could also be reused
552
553
  // for rendering to texture
553
- if (parentHasRenderTexture || rtt) {
554
+ if (parentHasRenderTexture !== undefined || rtt !== undefined) {
554
555
  return false;
555
556
  }
556
557
 
557
558
  // Check if the shader can batch the shader properties
558
559
  if (
559
560
  this.curRenderOp.shader !== this.defaultShader &&
560
- (!shaderProps ||
561
- !this.curRenderOp.shader.canBatchShaderProps(
561
+ (shaderProps === null ||
562
+ this.curRenderOp.shader.canBatchShaderProps(
562
563
  this.curRenderOp.shaderProps,
563
564
  shaderProps,
564
- ))
565
+ ) === false)
565
566
  ) {
566
567
  return false;
567
568
  }
@@ -590,22 +591,22 @@ export class WebGlCoreRenderer extends CoreRenderer {
590
591
 
591
592
  const arr = new Float32Array(quadBuffer, 0, this.curBufferIdx);
592
593
 
593
- const buffer = this.quadBufferCollection.getBuffer('a_position') ?? null;
594
+ const buffer = this.quadBufferCollection.getBuffer('a_position') || null;
594
595
  glw.arrayBufferData(buffer, arr, glw.STATIC_DRAW);
595
596
 
596
- const doLog = false; // idx++ % 100 === 0;
597
- if (doLog) {
598
- console.log('renderOps', this.renderOps.length);
599
- }
600
-
601
597
  for (let i = 0, length = this.renderOps.length; i < length; i++) {
602
- const renderOp = this.renderOps[i] as WebGlCoreRenderOp;
603
- if (doLog) {
604
- console.log('Quads per operation', renderOp.numQuads);
605
- }
606
- renderOp.draw();
598
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
599
+ this.renderOps[i]!.draw();
607
600
  }
608
601
  this.quadBufferUsage = this.curBufferIdx * arr.BYTES_PER_ELEMENT;
602
+
603
+ // Calculate the size of each quad in bytes (4 vertices per quad) times the size of each vertex in bytes
604
+ const QUAD_SIZE_IN_BYTES = 4 * (6 * arr.BYTES_PER_ELEMENT); // 6 attributes per vertex
605
+ this.numQuadsRendered = this.quadBufferUsage / QUAD_SIZE_IN_BYTES;
606
+ }
607
+
608
+ getQuadCount(): number {
609
+ return this.numQuadsRendered;
609
610
  }
610
611
 
611
612
  renderToTexture(node: CoreNode) {
@@ -702,12 +703,21 @@ export class WebGlCoreRenderer extends CoreRenderer {
702
703
  const node = this.rttNodes[i];
703
704
 
704
705
  // Skip nodes that don't have RTT updates
705
- if (!node || !node.hasRTTupdates) {
706
+ if (node === undefined || node.hasRTTupdates === false) {
707
+ continue;
708
+ }
709
+
710
+ // Skip nodes that are not visible
711
+ if (
712
+ node.worldAlpha === 0 ||
713
+ (node.strictBounds === true &&
714
+ node.renderState === CoreNodeRenderState.OutOfBounds)
715
+ ) {
706
716
  continue;
707
717
  }
708
718
 
709
- if (!node.texture || !node.texture.ctxTexture) {
710
- console.warn('Texture not loaded for RTT node', node);
719
+ // Skip nodes that do not have a loaded texture
720
+ if (node.texture === null || node.texture.state !== 'loaded') {
711
721
  continue;
712
722
  }
713
723
 
@@ -732,7 +742,7 @@ export class WebGlCoreRenderer extends CoreRenderer {
732
742
  for (let i = 0; i < node.children.length; i++) {
733
743
  const child = node.children[i];
734
744
 
735
- if (!child) {
745
+ if (child === undefined) {
736
746
  continue;
737
747
  }
738
748
 
@@ -66,8 +66,8 @@ export const updateFloat32ArrayLength4 = (values: ShaderEffectValueMap) => {
66
66
  const floatArray = values.programValue;
67
67
  floatArray[0] = validatedValue[0]!;
68
68
  floatArray[1] = validatedValue[1]!;
69
- floatArray[2] = validatedValue[1]!;
70
- floatArray[3] = validatedValue[1]!;
69
+ floatArray[2] = validatedValue[2]!;
70
+ floatArray[3] = validatedValue[3]!;
71
71
  } else {
72
72
  values.programValue = new Float32Array(validatedValue);
73
73
  }
@@ -27,6 +27,7 @@ import {
27
27
  type NormalizedFontMetrics,
28
28
  type TrFontFaceOptions,
29
29
  } from '../TrFontFace.js';
30
+ import { fetchJson } from '../utils.js';
30
31
  import type { FontShaper } from './internal/FontShaper.js';
31
32
  import { SdfFontShaper, type SdfFontData } from './internal/SdfFontShaper.js';
32
33
 
@@ -62,9 +63,9 @@ export class SdfTrFontFace<
62
63
  * in SDF/vertex units.
63
64
  */
64
65
  public readonly maxCharHeight: number = 0;
65
- public readonly data: SdfFontData | undefined;
66
66
  public readonly shaper: FontShaper | undefined;
67
67
  public readonly glyphMap: Map<number, SdfFontData['chars'][0]> = new Map();
68
+ public data: SdfFontData | undefined;
68
69
 
69
70
  constructor(type: FontTypeT, options: SdfTrFontFaceOptions) {
70
71
  super(options);
@@ -103,10 +104,9 @@ export class SdfTrFontFace<
103
104
  });
104
105
 
105
106
  // Set this.data to the fetched data from dataUrl
106
- fetch(atlasDataUrl)
107
- .then(async (response) => {
108
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
109
- (this.data as SdfFontData) = await response.json();
107
+ fetchJson(atlasDataUrl)
108
+ .then((response) => {
109
+ this.data = JSON.parse(response as unknown as string) as SdfFontData;
110
110
  assertTruthy(this.data);
111
111
  // Add all the glyphs to the glyph map
112
112
 
@@ -0,0 +1,39 @@
1
+ /*
2
+ * If not stated otherwise in this file or this component's LICENSE file the
3
+ * following copyright and licenses apply:
4
+ *
5
+ * Copyright 2020 Metrological
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the License);
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ export function fetchJson(
20
+ url: string,
21
+ responseType: XMLHttpRequestResponseType = '',
22
+ ): Promise<unknown> {
23
+ return new Promise((resolve, reject) => {
24
+ const xhr = new XMLHttpRequest();
25
+ xhr.responseType = responseType;
26
+ xhr.onreadystatechange = function () {
27
+ if (xhr.readyState == XMLHttpRequest.DONE) {
28
+ // On most devices like WebOS and Tizen, the file protocol returns 0 while http(s) protocol returns 200
29
+ if (xhr.status === 0 || xhr.status === 200) {
30
+ resolve(xhr.response);
31
+ } else {
32
+ reject(xhr.statusText);
33
+ }
34
+ }
35
+ };
36
+ xhr.open('GET', url, true);
37
+ xhr.send(null);
38
+ });
39
+ }
@@ -25,6 +25,7 @@ import {
25
25
  } from '../lib/textureCompression.js';
26
26
  import { convertUrlToAbsolute, isBase64Image } from '../lib/utils.js';
27
27
  import { isSvgImage, loadSvg } from '../lib/textureSvg.js';
28
+ import { fetchJson } from '../text-rendering/font-face-types/utils.js';
28
29
 
29
30
  /**
30
31
  * Properties of the {@link ImageTexture}
@@ -220,7 +221,9 @@ export class ImageTexture extends Texture {
220
221
  );
221
222
  }
222
223
 
223
- const blob = await fetch(src).then((response) => response.blob());
224
+ const blob = await fetchJson(src, 'blob').then(
225
+ (response) => response as Blob,
226
+ );
224
227
  return this.createImageBitmap(blob, premultiplyAlpha, sx, sy, sw, sh);
225
228
  }
226
229
 
@@ -137,14 +137,10 @@ export class SubTexture extends Texture {
137
137
  width: this.props.width,
138
138
  height: this.props.height,
139
139
  });
140
-
141
- // free our source, if any
142
- this.freeTextureData();
143
140
  };
144
141
 
145
142
  private onParentTxFailed: TextureFailedEventHandler = (target, error) => {
146
143
  this.forwardParentTxState('failed', error);
147
- this.free();
148
144
  };
149
145
 
150
146
  private onParentTxFetched = () => {
@@ -164,7 +160,6 @@ export class SubTexture extends Texture {
164
160
 
165
161
  private onParentTxFreed = () => {
166
162
  this.forwardParentTxState('freed');
167
- this.free();
168
163
  };
169
164
 
170
165
  private forwardParentTxState(
@@ -182,24 +177,9 @@ export class SubTexture extends Texture {
182
177
  override async getTextureSource(): Promise<TextureData> {
183
178
  // Check if parent texture is loaded
184
179
  return new Promise((resolve, reject) => {
185
- if (this.parentTexture.state === 'loaded') {
186
- resolve({
187
- data: this.props,
188
- });
189
- }
190
-
191
- this.parentTexture.once('loaded', (target, data) => {
192
- resolve({
193
- data: this.props,
194
- });
195
- });
196
-
197
- this.parentTexture.once('failed', (target, error) => {
198
- reject(error);
199
- });
200
-
201
- this.parentTexture.once('freed', () => {
202
- reject(new Error('Parent texture was freed'));
180
+ this.setState('fetched');
181
+ resolve({
182
+ data: this.props,
203
183
  });
204
184
  });
205
185
  }
@@ -200,15 +200,6 @@ export abstract class Texture extends EventEmitter {
200
200
  }
201
201
 
202
202
  load(): void {
203
- if (
204
- this.state === 'fetching' ||
205
- this.state === 'loading' ||
206
- this.state === 'loaded' ||
207
- this.state === 'failed'
208
- ) {
209
- return;
210
- }
211
-
212
203
  this.txManager.loadTexture(this);
213
204
  }
214
205
 
@@ -245,7 +236,6 @@ export abstract class Texture extends EventEmitter {
245
236
  */
246
237
  free(): void {
247
238
  this.ctxTexture?.free();
248
- this.textureData = null;
249
239
  }
250
240
 
251
241
  /**
@@ -319,6 +319,8 @@ export interface RendererMainSettings {
319
319
  * - Emitted every `fpsUpdateInterval` milliseconds with the current FPS
320
320
  * - `frameTick`
321
321
  * - Emitted every frame tick
322
+ * - `quadsUpdate`
323
+ * - Emitted when number of quads rendered is updated
322
324
  * - `idle`
323
325
  * - Emitted when the renderer is idle (no changes to the scene
324
326
  * graph/animations running)
@@ -687,6 +689,26 @@ export class RendererMain extends EventEmitter {
687
689
  this.stage.requestRender();
688
690
  }
689
691
 
692
+ /**
693
+ * Cleanup textures that are not being used
694
+ *
695
+ * @remarks
696
+ * This can be used to free up GFX memory used by textures that are no longer
697
+ * being displayed.
698
+ *
699
+ * This routine is also called automatically when the memory used by textures
700
+ * exceeds the critical threshold on frame generation **OR** when the renderer
701
+ * is idle and the memory used by textures exceeds the target threshold.
702
+ *
703
+ * **NOTE**: This is a heavy operation and should be used sparingly.
704
+ * **NOTE2**: This will not cleanup textures that are currently being displayed.
705
+ * **NOTE3**: This will not cleanup textures that are marked as `preventCleanup`.
706
+ * **NOTE4**: This has nothing to do with the garbage collection of JavaScript.
707
+ */
708
+ cleanup() {
709
+ this.stage.cleanup();
710
+ }
711
+
690
712
  /**
691
713
  * Sets the clear color for the stage.
692
714
  *