@lightningjs/renderer 0.7.6 → 0.8.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 (110) hide show
  1. package/README.md +4 -0
  2. package/dist/src/common/CommonTypes.d.ts +6 -0
  3. package/dist/src/core/CoreNode.d.ts +65 -8
  4. package/dist/src/core/CoreNode.js +104 -28
  5. package/dist/src/core/CoreNode.js.map +1 -1
  6. package/dist/src/core/CoreShaderManager.d.ts +2 -0
  7. package/dist/src/core/CoreShaderManager.js +2 -0
  8. package/dist/src/core/CoreShaderManager.js.map +1 -1
  9. package/dist/src/core/CoreTextNode.d.ts +5 -0
  10. package/dist/src/core/CoreTextNode.js +15 -10
  11. package/dist/src/core/CoreTextNode.js.map +1 -1
  12. package/dist/src/core/CoreTextureManager.js +2 -0
  13. package/dist/src/core/CoreTextureManager.js.map +1 -1
  14. package/dist/src/core/Stage.d.ts +4 -0
  15. package/dist/src/core/Stage.js +8 -1
  16. package/dist/src/core/Stage.js.map +1 -1
  17. package/dist/src/core/TextureMemoryManager.d.ts +12 -0
  18. package/dist/src/core/TextureMemoryManager.js +42 -0
  19. package/dist/src/core/TextureMemoryManager.js.map +1 -0
  20. package/dist/src/core/platform.js +8 -0
  21. package/dist/src/core/platform.js.map +1 -1
  22. package/dist/src/core/renderers/CoreContextTexture.d.ts +5 -1
  23. package/dist/src/core/renderers/CoreContextTexture.js +3 -1
  24. package/dist/src/core/renderers/CoreContextTexture.js.map +1 -1
  25. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +2 -1
  26. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +2 -2
  27. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +1 -1
  28. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +3 -1
  29. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +22 -5
  30. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  31. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +3 -0
  32. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +4 -2
  33. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  34. package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.d.ts +58 -0
  35. package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js +112 -0
  36. package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js.map +1 -0
  37. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +17 -30
  38. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js.map +1 -1
  39. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.d.ts +1 -0
  40. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +24 -30
  41. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js.map +1 -1
  42. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -0
  43. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +18 -0
  44. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  45. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +8 -0
  46. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +26 -4
  47. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  48. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +4 -3
  49. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
  50. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +19 -0
  51. package/dist/src/core/text-rendering/renderers/TextRenderer.js +26 -0
  52. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  53. package/dist/src/core/textures/Texture.d.ts +26 -1
  54. package/dist/src/core/textures/Texture.js +30 -1
  55. package/dist/src/core/textures/Texture.js.map +1 -1
  56. package/dist/src/main-api/ICoreDriver.d.ts +1 -0
  57. package/dist/src/main-api/Inspector.js +2 -1
  58. package/dist/src/main-api/Inspector.js.map +1 -1
  59. package/dist/src/main-api/RendererMain.d.ts +10 -1
  60. package/dist/src/main-api/RendererMain.js +6 -1
  61. package/dist/src/main-api/RendererMain.js.map +1 -1
  62. package/dist/src/render-drivers/main/MainCoreDriver.d.ts +1 -0
  63. package/dist/src/render-drivers/main/MainCoreDriver.js +7 -0
  64. package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
  65. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +1 -0
  66. package/dist/src/render-drivers/main/MainOnlyNode.js +10 -6
  67. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  68. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +1 -0
  69. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  70. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.d.ts +1 -0
  71. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js.map +1 -1
  72. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +3 -0
  73. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  74. package/dist/src/render-drivers/threadx/worker/renderer.js +1 -0
  75. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  76. package/dist/src/utils.d.ts +6 -0
  77. package/dist/src/utils.js +9 -1
  78. package/dist/src/utils.js.map +1 -1
  79. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  80. package/package.json +1 -1
  81. package/src/common/CommonTypes.ts +7 -0
  82. package/src/core/CoreNode.ts +118 -34
  83. package/src/core/CoreShaderManager.ts +3 -0
  84. package/src/core/CoreTextNode.ts +44 -43
  85. package/src/core/CoreTextureManager.ts +2 -0
  86. package/src/core/Stage.ts +10 -0
  87. package/src/core/TextureMemoryManager.ts +66 -0
  88. package/src/core/platform.ts +8 -0
  89. package/src/core/renderers/CoreContextTexture.ts +6 -1
  90. package/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +7 -2
  91. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +34 -6
  92. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +10 -2
  93. package/src/core/renderers/webgl/shaders/effects/HolePunchEffect.ts +166 -0
  94. package/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.ts +16 -32
  95. package/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.ts +26 -32
  96. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +23 -0
  97. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +32 -4
  98. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +4 -3
  99. package/src/core/text-rendering/renderers/TextRenderer.ts +32 -0
  100. package/src/core/textures/Texture.ts +39 -2
  101. package/src/main-api/ICoreDriver.ts +2 -0
  102. package/src/main-api/Inspector.ts +2 -1
  103. package/src/main-api/RendererMain.ts +19 -2
  104. package/src/render-drivers/main/MainCoreDriver.ts +9 -0
  105. package/src/render-drivers/main/MainOnlyNode.ts +12 -6
  106. package/src/render-drivers/threadx/ThreadXCoreDriver.ts +1 -0
  107. package/src/render-drivers/threadx/ThreadXRendererMessage.ts +1 -0
  108. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +7 -0
  109. package/src/render-drivers/threadx/worker/renderer.ts +1 -0
  110. package/src/utils.ts +10 -1
@@ -74,6 +74,13 @@ export type NodeTextureFailedPayload = {
74
74
  error: Error;
75
75
  };
76
76
 
77
+ /**
78
+ * Payload for when texture failed to load
79
+ */
80
+ export type NodeTextureFreedPayload = {
81
+ type: 'texture';
82
+ };
83
+
77
84
  /**
78
85
  * Combined type for all failed payloads
79
86
  */
@@ -30,10 +30,12 @@ import type { Stage } from './Stage.js';
30
30
  import type {
31
31
  Texture,
32
32
  TextureFailedEventHandler,
33
+ TextureFreedEventHandler,
33
34
  TextureLoadedEventHandler,
34
35
  } from './textures/Texture.js';
35
36
  import type {
36
37
  NodeTextureFailedPayload,
38
+ NodeTextureFreedPayload,
37
39
  NodeTextureLoadedPayload,
38
40
  } from '../common/CommonTypes.js';
39
41
  import { EventEmitter } from '../common/EventEmitter.js';
@@ -111,47 +113,99 @@ export enum UpdateType {
111
113
 
112
114
  /**
113
115
  * Scale/Rotate transform update
116
+ *
117
+ * @remarks
118
+ * CoreNode Properties Updated:
119
+ * - `scaleRotateTransform`
114
120
  */
115
121
  ScaleRotate = 2,
116
122
 
117
123
  /**
118
124
  * Translate transform update (x/y/width/height/pivot/mount)
125
+ *
126
+ * @remarks
127
+ * CoreNode Properties Updated:
128
+ * - `localTransform`
119
129
  */
120
130
  Local = 4,
121
131
 
122
132
  /**
123
- * Global transform update
133
+ * Global Transform update
134
+ *
135
+ * @remarks
136
+ * CoreNode Properties Updated:
137
+ * - `globalTransform`
138
+ * - `renderCoords`
139
+ * - `renderBound`
124
140
  */
125
141
  Global = 8,
126
142
 
127
143
  /**
128
144
  * Clipping rect update
145
+ *
146
+ * @remarks
147
+ * CoreNode Properties Updated:
148
+ * - `clippingRect`
129
149
  */
130
150
  Clipping = 16,
131
151
 
132
152
  /**
133
153
  * Calculated ZIndex update
154
+ *
155
+ * @remarks
156
+ * CoreNode Properties Updated:
157
+ * - `calcZIndex`
134
158
  */
135
159
  CalculatedZIndex = 32,
136
160
 
137
161
  /**
138
162
  * Z-Index Sorted Children update
163
+ *
164
+ * @remarks
165
+ * CoreNode Properties Updated:
166
+ * - `children` (sorts children by their `calcZIndex`)
139
167
  */
140
168
  ZIndexSortedChildren = 64,
141
169
 
142
170
  /**
143
- * Premultiplied Colors
171
+ * Premultiplied Colors update
172
+ *
173
+ * @remarks
174
+ * CoreNode Properties Updated:
175
+ * - `premultipliedColorTl`
176
+ * - `premultipliedColorTr`
177
+ * - `premultipliedColorBl`
178
+ * - `premultipliedColorBr`
144
179
  */
145
180
  PremultipliedColors = 128,
146
181
 
147
182
  /**
148
- * World Alpha
183
+ * World Alpha update
149
184
  *
150
185
  * @remarks
151
- * World Alpha = Parent World Alpha * Alpha
186
+ * CoreNode Properties Updated:
187
+ * - `worldAlpha` = `parent.worldAlpha` * `alpha`
152
188
  */
153
189
  WorldAlpha = 256,
154
190
 
191
+ /**
192
+ * Render State update
193
+ *
194
+ * @remarks
195
+ * CoreNode Properties Updated:
196
+ * - `renderState`
197
+ */
198
+ RenderState = 512,
199
+
200
+ /**
201
+ * Is Renderable update
202
+ *
203
+ * @remarks
204
+ * CoreNode Properties Updated:
205
+ * - `isRenderable`
206
+ */
207
+ IsRenderable = 1024,
208
+
155
209
  /**
156
210
  * None
157
211
  */
@@ -160,7 +214,7 @@ export enum UpdateType {
160
214
  /**
161
215
  * All
162
216
  */
163
- All = 511,
217
+ All = 2047,
164
218
  }
165
219
 
166
220
  export class CoreNode extends EventEmitter implements ICoreNode {
@@ -219,7 +273,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
219
273
 
220
274
  this.props.texture = texture;
221
275
  this.props.textureOptions = options;
222
- this.updateIsRenderable();
276
+ this.setUpdateType(UpdateType.IsRenderable);
223
277
 
224
278
  // If texture is already loaded / failed, trigger loaded event manually
225
279
  // so that users get a consistent event experience.
@@ -230,20 +284,26 @@ export class CoreNode extends EventEmitter implements ICoreNode {
230
284
  this.onTextureLoaded(texture, texture.dimensions!);
231
285
  } else if (texture.state === 'failed') {
232
286
  this.onTextureFailed(texture, texture.error!);
287
+ } else if (texture.state === 'freed') {
288
+ this.onTextureFreed(texture);
233
289
  }
234
290
  texture.on('loaded', this.onTextureLoaded);
235
291
  texture.on('failed', this.onTextureFailed);
292
+ texture.on('freed', this.onTextureFreed);
236
293
  });
237
294
  }
238
295
 
239
296
  unloadTexture(): void {
240
297
  if (this.props.texture) {
241
- this.props.texture.off('loaded', this.onTextureLoaded);
242
- this.props.texture.off('failed', this.onTextureFailed);
298
+ const { texture } = this.props;
299
+ texture.off('loaded', this.onTextureLoaded);
300
+ texture.off('failed', this.onTextureFailed);
301
+ texture.off('freed', this.onTextureFreed);
302
+ texture.setRenderableOwner(this, false);
243
303
  }
244
304
  this.props.texture = null;
245
305
  this.props.textureOptions = null;
246
- this.updateIsRenderable();
306
+ this.setUpdateType(UpdateType.IsRenderable);
247
307
  }
248
308
 
249
309
  private onTextureLoaded: TextureLoadedEventHandler = (target, dimensions) => {
@@ -262,6 +322,12 @@ export class CoreNode extends EventEmitter implements ICoreNode {
262
322
  error,
263
323
  } satisfies NodeTextureFailedPayload);
264
324
  };
325
+
326
+ private onTextureFreed: TextureFreedEventHandler = (target: Texture) => {
327
+ this.emit('freed', {
328
+ type: 'texture',
329
+ } satisfies NodeTextureFreedPayload);
330
+ };
265
331
  //#endregion Textures
266
332
 
267
333
  loadShader<Type extends keyof ShaderMap>(
@@ -273,7 +339,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
273
339
  const { shader, props: p } = shManager.loadShader(shaderType, props);
274
340
  this.props.shader = shader;
275
341
  this.props.shaderProps = p;
276
- this.updateIsRenderable();
342
+ this.setUpdateType(UpdateType.IsRenderable);
277
343
  }
278
344
 
279
345
  /**
@@ -353,8 +419,9 @@ export class CoreNode extends EventEmitter implements ICoreNode {
353
419
  }
354
420
  this.calculateRenderCoords();
355
421
  this.updateBoundingRect();
356
- this.updateRenderState();
357
- this.setUpdateType(UpdateType.Clipping | UpdateType.Children);
422
+ this.setUpdateType(
423
+ UpdateType.Clipping | UpdateType.RenderState | UpdateType.Children,
424
+ );
358
425
  childUpdateType |= UpdateType.Global;
359
426
  }
360
427
 
@@ -408,9 +475,15 @@ export class CoreNode extends EventEmitter implements ICoreNode {
408
475
  true,
409
476
  );
410
477
  }
478
+ }
479
+
480
+ if (this.updateType & UpdateType.RenderState) {
481
+ this.updateRenderState(parentClippingRect);
482
+ this.setUpdateType(UpdateType.IsRenderable);
483
+ }
484
+
485
+ if (this.updateType & UpdateType.IsRenderable) {
411
486
  this.updateIsRenderable();
412
- this.setUpdateType(UpdateType.Children);
413
- childUpdateType |= UpdateType.PremultipliedColors;
414
487
  }
415
488
 
416
489
  // No need to update zIndex if there is no parent
@@ -501,25 +574,24 @@ export class CoreNode extends EventEmitter implements ICoreNode {
501
574
  return false;
502
575
  }
503
576
 
504
- checkRenderBounds(): CoreNodeRenderState {
505
- assertTruthy(this.clippingRect);
577
+ checkRenderBounds(parentClippingRect: RectWithValid): CoreNodeRenderState {
506
578
  assertTruthy(this.renderBound);
507
- const rectW = this.clippingRect.width || this.stage.root.width;
508
- const rectH = this.clippingRect.height || this.stage.root.height;
579
+ const rectW = parentClippingRect.width || this.stage.root.width;
580
+ const rectH = parentClippingRect.height || this.stage.root.height;
509
581
  this.strictBound = createBound(
510
- this.clippingRect.x,
511
- this.clippingRect.y,
512
- rectW,
513
- rectH,
582
+ parentClippingRect.x,
583
+ parentClippingRect.y,
584
+ parentClippingRect.x + rectW,
585
+ parentClippingRect.y + rectH,
514
586
  this.strictBound,
515
587
  );
516
588
 
517
589
  const renderM = this.stage.boundsMargin;
518
590
  this.preloadBound = createBound(
519
- this.clippingRect.x - renderM[3],
520
- this.clippingRect.y - renderM[0],
521
- this.clippingRect.x + rectW + renderM[1],
522
- this.clippingRect.y + rectH + renderM[2],
591
+ parentClippingRect.x - renderM[3],
592
+ parentClippingRect.y - renderM[0],
593
+ parentClippingRect.x + rectW + renderM[1],
594
+ parentClippingRect.y + rectH + renderM[2],
523
595
  this.preloadBound,
524
596
  );
525
597
 
@@ -533,13 +605,13 @@ export class CoreNode extends EventEmitter implements ICoreNode {
533
605
  return CoreNodeRenderState.OutOfBounds;
534
606
  }
535
607
 
536
- updateRenderState() {
537
- const renderState = this.checkRenderBounds();
608
+ updateRenderState(parentClippingRect: RectWithValid) {
609
+ const renderState = this.checkRenderBounds(parentClippingRect);
538
610
  if (renderState !== this.renderState) {
539
611
  const previous = this.renderState;
540
612
  this.renderState = renderState;
541
613
  if (previous === CoreNodeRenderState.InViewport) {
542
- this.emit('outOfViewPort', {
614
+ this.emit('outOfViewport', {
543
615
  previous,
544
616
  current: renderState,
545
617
  });
@@ -551,7 +623,6 @@ export class CoreNode extends EventEmitter implements ICoreNode {
551
623
  current: renderState,
552
624
  });
553
625
  }
554
- this.updateIsRenderable();
555
626
  }
556
627
 
557
628
  setRenderState(state: CoreNodeRenderState) {
@@ -561,13 +632,26 @@ export class CoreNode extends EventEmitter implements ICoreNode {
561
632
  }
562
633
  }
563
634
 
564
- // This function checks if the current node is renderable based on certain properties.
565
- // It returns true if any of the specified properties are truthy or if any color property is not 0, otherwise it returns false.
635
+ /**
636
+ * This function updates the `isRenderable` property based on certain conditions.
637
+ *
638
+ * @returns
639
+ */
566
640
  updateIsRenderable() {
641
+ let newIsRenderable;
567
642
  if (!this.checkRenderProps()) {
568
- return (this.isRenderable = false);
643
+ newIsRenderable = false;
644
+ } else {
645
+ newIsRenderable = this.renderState > CoreNodeRenderState.OutOfBounds;
646
+ }
647
+ if (this.isRenderable !== newIsRenderable) {
648
+ this.isRenderable = newIsRenderable;
649
+ this.onChangeIsRenderable(newIsRenderable);
569
650
  }
570
- this.isRenderable = this.renderState > CoreNodeRenderState.OutOfBounds;
651
+ }
652
+
653
+ onChangeIsRenderable(isRenderable: boolean) {
654
+ this.props.texture?.setRenderableOwner(this, isRenderable);
571
655
  }
572
656
 
573
657
  calculateRenderCoords() {
@@ -60,6 +60,7 @@ import {
60
60
  RadialProgressEffect,
61
61
  type RadialProgressEffectProps,
62
62
  } from './renderers/webgl/shaders/effects/RadialProgressEffect.js';
63
+ import { HolePunchEffect } from './renderers/webgl/shaders/effects/HolePunchEffect.js';
63
64
 
64
65
  export type { FadeOutEffectProps };
65
66
  export type { LinearGradientEffectProps };
@@ -94,6 +95,7 @@ export interface EffectMap {
94
95
  grayscale: typeof GrayscaleEffect;
95
96
  glitch: typeof GlitchEffect;
96
97
  radialProgress: typeof RadialProgressEffect;
98
+ holePunch: typeof HolePunchEffect;
97
99
  }
98
100
 
99
101
  export type EffectProps =
@@ -131,6 +133,7 @@ export class CoreShaderManager {
131
133
  this.registerEffectType('glitch', GlitchEffect);
132
134
  this.registerEffectType('radius', RadiusEffect);
133
135
  this.registerEffectType('radialProgress', RadialProgressEffect);
136
+ this.registerEffectType('holePunch', HolePunchEffect);
134
137
  }
135
138
 
136
139
  registerShaderType<Type extends keyof ShaderMap>(
@@ -55,35 +55,32 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
55
55
  super(stage, props);
56
56
  this._textRendererOverride = props.textRendererOverride;
57
57
  const { resolvedTextRenderer, textRendererState } =
58
- this.resolveTextRendererAndState(
59
- {
60
- x: this.absX,
61
- y: this.absY,
62
- width: props.width,
63
- height: props.height,
64
- textAlign: props.textAlign,
65
- color: props.color,
66
- zIndex: props.zIndex,
67
- contain: props.contain,
68
- scrollable: props.scrollable,
69
- scrollY: props.scrollY,
70
- offsetY: props.offsetY,
71
- letterSpacing: props.letterSpacing,
72
- debug: props.debug,
73
- fontFamily: props.fontFamily,
74
- fontSize: props.fontSize,
75
- fontStretch: props.fontStretch,
76
- fontStyle: props.fontStyle,
77
- fontWeight: props.fontWeight,
78
- text: props.text,
79
- lineHeight: props.lineHeight,
80
- maxLines: props.maxLines,
81
- textBaseline: props.textBaseline,
82
- verticalAlign: props.verticalAlign,
83
- overflowSuffix: props.overflowSuffix,
84
- },
85
- undefined,
86
- );
58
+ this.resolveTextRendererAndState({
59
+ x: this.absX,
60
+ y: this.absY,
61
+ width: props.width,
62
+ height: props.height,
63
+ textAlign: props.textAlign,
64
+ color: props.color,
65
+ zIndex: props.zIndex,
66
+ contain: props.contain,
67
+ scrollable: props.scrollable,
68
+ scrollY: props.scrollY,
69
+ offsetY: props.offsetY,
70
+ letterSpacing: props.letterSpacing,
71
+ debug: props.debug,
72
+ fontFamily: props.fontFamily,
73
+ fontSize: props.fontSize,
74
+ fontStretch: props.fontStretch,
75
+ fontStyle: props.fontStyle,
76
+ fontWeight: props.fontWeight,
77
+ text: props.text,
78
+ lineHeight: props.lineHeight,
79
+ maxLines: props.maxLines,
80
+ textBaseline: props.textBaseline,
81
+ verticalAlign: props.verticalAlign,
82
+ overflowSuffix: props.overflowSuffix,
83
+ });
87
84
  this.textRenderer = resolvedTextRenderer;
88
85
  this.trState = textRendererState;
89
86
  }
@@ -179,8 +176,10 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
179
176
  set textRendererOverride(value: CoreTextNodeProps['textRendererOverride']) {
180
177
  this._textRendererOverride = value;
181
178
 
179
+ this.textRenderer.destroyState(this.trState);
180
+
182
181
  const { resolvedTextRenderer, textRendererState } =
183
- this.resolveTextRendererAndState(this.trState.props, this.trState);
182
+ this.resolveTextRendererAndState(this.trState.props);
184
183
  this.textRenderer = resolvedTextRenderer;
185
184
  this.trState = textRendererState;
186
185
  }
@@ -348,6 +347,11 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
348
347
  return super.checkRenderProps();
349
348
  }
350
349
 
350
+ override onChangeIsRenderable(isRenderable: boolean) {
351
+ super.onChangeIsRenderable(isRenderable);
352
+ this.textRenderer.setIsRenderable(this.trState, isRenderable);
353
+ }
354
+
351
355
  override renderQuads(renderer: CoreRenderer) {
352
356
  assertTruthy(this.globalTransform);
353
357
  this.textRenderer.renderQuads(
@@ -358,15 +362,21 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
358
362
  );
359
363
  }
360
364
 
365
+ /**
366
+ * Destroy the node and cleanup all resources
367
+ */
368
+ override destroy(): void {
369
+ super.destroy();
370
+
371
+ this.textRenderer.destroyState(this.trState);
372
+ }
373
+
361
374
  /**
362
375
  * Resolve a text renderer and a new state based on the current text renderer props provided
363
376
  * @param props
364
377
  * @returns
365
378
  */
366
- private resolveTextRendererAndState(
367
- props: TrProps,
368
- prevState?: TextRendererState,
369
- ): {
379
+ private resolveTextRendererAndState(props: TrProps): {
370
380
  resolvedTextRenderer: TextRenderer;
371
381
  textRendererState: TextRendererState;
372
382
  } {
@@ -377,15 +387,6 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
377
387
 
378
388
  const textRendererState = resolvedTextRenderer.createState(props);
379
389
 
380
- const stateEvents = ['loading', 'loaded', 'failed'];
381
-
382
- if (prevState) {
383
- // Remove the old event listeners from previous state obj there was one
384
- stateEvents.forEach((eventName) => {
385
- prevState.emitter.off(eventName);
386
- });
387
- }
388
-
389
390
  textRendererState.emitter.on('loaded', this.onTextLoaded);
390
391
  textRendererState.emitter.on('failed', this.onTextFailed);
391
392
 
@@ -288,6 +288,8 @@ export class CoreTextureManager {
288
288
  }
289
289
  }
290
290
  }
291
+ // Free the ctx texture if it exists.
292
+ this.ctxTextureCache.get(texture)?.free();
291
293
  }
292
294
 
293
295
  /**
package/src/core/Stage.ts CHANGED
@@ -37,11 +37,13 @@ import type {
37
37
  FpsUpdatePayload,
38
38
  FrameTickPayload,
39
39
  } from '../common/CommonTypes.js';
40
+ import { TextureMemoryManager } from './TextureMemoryManager.js';
40
41
 
41
42
  export interface StageOptions {
42
43
  rootId: number;
43
44
  appWidth: number;
44
45
  appHeight: number;
46
+ txMemByteThreshold: number;
45
47
  boundsMargin: number | [number, number, number, number];
46
48
  deviceLogicalPixelRatio: number;
47
49
  devicePhysicalPixelRatio: number;
@@ -73,6 +75,7 @@ export class Stage extends EventEmitter {
73
75
  /// Module Instances
74
76
  public readonly animationManager: AnimationManager;
75
77
  public readonly txManager: CoreTextureManager;
78
+ public readonly txMemManager: TextureMemoryManager;
76
79
  public readonly fontManager: TrFontManager;
77
80
  public readonly textRenderers: Partial<TextRendererMap>;
78
81
  public readonly shManager: CoreShaderManager;
@@ -106,9 +109,11 @@ export class Stage extends EventEmitter {
106
109
  boundsMargin,
107
110
  enableContextSpy,
108
111
  numImageWorkers,
112
+ txMemByteThreshold,
109
113
  } = options;
110
114
 
111
115
  this.txManager = new CoreTextureManager(numImageWorkers);
116
+ this.txMemManager = new TextureMemoryManager(txMemByteThreshold);
112
117
  this.shManager = new CoreShaderManager();
113
118
  this.animationManager = new AnimationManager();
114
119
  this.contextSpy = enableContextSpy ? new ContextSpy() : null;
@@ -138,6 +143,7 @@ export class Stage extends EventEmitter {
138
143
  clearColor: clearColor ?? 0xff000000,
139
144
  bufferMemory,
140
145
  txManager: this.txManager,
146
+ txMemManager: this.txMemManager,
141
147
  shManager: this.shManager,
142
148
  contextSpy: this.contextSpy,
143
149
  });
@@ -243,11 +249,15 @@ export class Stage extends EventEmitter {
243
249
 
244
250
  renderer?.render();
245
251
 
252
+ this.calculateFps();
253
+
246
254
  // Reset renderRequested flag if it was set
247
255
  if (renderRequested) {
248
256
  this.renderRequested = false;
249
257
  }
258
+ }
250
259
 
260
+ calculateFps() {
251
261
  // If there's an FPS update interval, emit the FPS update event
252
262
  // when the specified interval has elapsed.
253
263
  const { fpsUpdateInterval } = this.options;
@@ -0,0 +1,66 @@
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 2024 Comcast Cable Communications Management, LLC.
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
+ import type { CoreContextTexture } from './renderers/CoreContextTexture.js';
20
+
21
+ export class TextureMemoryManager {
22
+ private memUsed = 0;
23
+ private textures: Map<CoreContextTexture, number> = new Map();
24
+ private threshold: number;
25
+
26
+ /**
27
+ * @param byteThreshold Number of texture bytes to trigger garbage collection
28
+ */
29
+ constructor(byteThreshold: number) {
30
+ this.threshold = byteThreshold;
31
+
32
+ // If the threshold is 0, we disable the memory manager by replacing the
33
+ // setTextureMemUse method with a no-op function.
34
+ if (byteThreshold === 0) {
35
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
36
+ this.setTextureMemUse = () => {};
37
+ }
38
+ }
39
+
40
+ setTextureMemUse(ctxTexture: CoreContextTexture, byteSize: number) {
41
+ if (this.textures.has(ctxTexture)) {
42
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
43
+ this.memUsed -= this.textures.get(ctxTexture)!;
44
+ }
45
+
46
+ if (byteSize === 0) {
47
+ this.textures.delete(ctxTexture);
48
+ return;
49
+ } else {
50
+ this.memUsed += byteSize;
51
+ this.textures.set(ctxTexture, byteSize);
52
+ }
53
+
54
+ if (this.memUsed > this.threshold) {
55
+ this.gc();
56
+ }
57
+ }
58
+
59
+ gc() {
60
+ this.textures.forEach((byteSize, ctxTexture) => {
61
+ if (!ctxTexture.renderable) {
62
+ ctxTexture.free();
63
+ }
64
+ });
65
+ }
66
+ }
@@ -23,14 +23,22 @@ import type { Stage } from './Stage.js';
23
23
  * Platform render loop initiator
24
24
  */
25
25
  export const startLoop = (stage: Stage) => {
26
+ let isIdle = false;
26
27
  const runLoop = () => {
27
28
  stage.updateAnimations();
28
29
 
29
30
  if (!stage.hasSceneUpdates()) {
31
+ // We still need to calculate the fps else it looks like the app is frozen
32
+ stage.calculateFps();
30
33
  setTimeout(runLoop, 16.666666666666668);
34
+ if (!isIdle) {
35
+ stage.emit('idle');
36
+ isIdle = true;
37
+ }
31
38
  return;
32
39
  }
33
40
 
41
+ isIdle = false;
34
42
  stage.drawFrame();
35
43
  requestAnimationFrame(runLoop);
36
44
  };
@@ -17,14 +17,19 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
+ import type { TextureMemoryManager } from '../TextureMemoryManager.js';
20
21
  import type { Texture } from '../textures/Texture.js';
21
22
 
22
23
  export abstract class CoreContextTexture {
24
+ readonly memManager: TextureMemoryManager;
23
25
  readonly textureSource: Texture;
24
26
 
25
- constructor(textureSource: Texture) {
27
+ constructor(memManager: TextureMemoryManager, textureSource: Texture) {
28
+ this.memManager = memManager;
26
29
  this.textureSource = textureSource;
27
30
  }
28
31
 
29
32
  abstract load(): void;
33
+ abstract free(): void;
34
+ abstract get renderable(): boolean;
30
35
  }
@@ -18,13 +18,18 @@
18
18
  */
19
19
 
20
20
  import type { Dimensions } from '../../../common/CommonTypes.js';
21
+ import type { TextureMemoryManager } from '../../TextureMemoryManager.js';
21
22
  import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
22
23
  import type { SubTexture } from '../../textures/SubTexture.js';
23
24
  import { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js';
24
25
 
25
26
  export class WebGlCoreCtxSubTexture extends WebGlCoreCtxTexture {
26
- constructor(glw: WebGlContextWrapper, textureSource: SubTexture) {
27
- super(glw, textureSource);
27
+ constructor(
28
+ glw: WebGlContextWrapper,
29
+ memManager: TextureMemoryManager,
30
+ textureSource: SubTexture,
31
+ ) {
32
+ super(glw, memManager, textureSource);
28
33
  }
29
34
 
30
35
  override async onLoadRequest(): Promise<Dimensions> {