@lightningjs/renderer 0.7.6 → 0.8.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 (102) 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 +63 -6
  4. package/dist/src/core/CoreNode.js +97 -20
  5. package/dist/src/core/CoreNode.js.map +1 -1
  6. package/dist/src/core/CoreTextNode.d.ts +5 -0
  7. package/dist/src/core/CoreTextNode.js +15 -10
  8. package/dist/src/core/CoreTextNode.js.map +1 -1
  9. package/dist/src/core/CoreTextureManager.js +2 -0
  10. package/dist/src/core/CoreTextureManager.js.map +1 -1
  11. package/dist/src/core/Stage.d.ts +4 -0
  12. package/dist/src/core/Stage.js +8 -1
  13. package/dist/src/core/Stage.js.map +1 -1
  14. package/dist/src/core/TextureMemoryManager.d.ts +12 -0
  15. package/dist/src/core/TextureMemoryManager.js +42 -0
  16. package/dist/src/core/TextureMemoryManager.js.map +1 -0
  17. package/dist/src/core/platform.js +8 -0
  18. package/dist/src/core/platform.js.map +1 -1
  19. package/dist/src/core/renderers/CoreContextTexture.d.ts +5 -1
  20. package/dist/src/core/renderers/CoreContextTexture.js +3 -1
  21. package/dist/src/core/renderers/CoreContextTexture.js.map +1 -1
  22. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +2 -1
  23. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +2 -2
  24. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +1 -1
  25. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +3 -1
  26. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +22 -5
  27. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  28. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +3 -0
  29. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +4 -2
  30. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  31. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +17 -30
  32. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js.map +1 -1
  33. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.d.ts +1 -0
  34. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +24 -30
  35. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js.map +1 -1
  36. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -0
  37. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +18 -0
  38. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  39. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +8 -0
  40. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +26 -4
  41. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  42. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +1 -3
  43. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
  44. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +19 -0
  45. package/dist/src/core/text-rendering/renderers/TextRenderer.js +26 -0
  46. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  47. package/dist/src/core/textures/Texture.d.ts +26 -1
  48. package/dist/src/core/textures/Texture.js +30 -1
  49. package/dist/src/core/textures/Texture.js.map +1 -1
  50. package/dist/src/main-api/ICoreDriver.d.ts +1 -0
  51. package/dist/src/main-api/Inspector.js +2 -1
  52. package/dist/src/main-api/Inspector.js.map +1 -1
  53. package/dist/src/main-api/RendererMain.d.ts +10 -1
  54. package/dist/src/main-api/RendererMain.js +6 -1
  55. package/dist/src/main-api/RendererMain.js.map +1 -1
  56. package/dist/src/render-drivers/main/MainCoreDriver.d.ts +1 -0
  57. package/dist/src/render-drivers/main/MainCoreDriver.js +7 -0
  58. package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
  59. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +1 -0
  60. package/dist/src/render-drivers/main/MainOnlyNode.js +10 -6
  61. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  62. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +1 -0
  63. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  64. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.d.ts +1 -0
  65. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js.map +1 -1
  66. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +3 -0
  67. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  68. package/dist/src/render-drivers/threadx/worker/renderer.js +1 -0
  69. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  70. package/dist/src/utils.d.ts +6 -0
  71. package/dist/src/utils.js +9 -1
  72. package/dist/src/utils.js.map +1 -1
  73. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  74. package/package.json +1 -1
  75. package/src/common/CommonTypes.ts +7 -0
  76. package/src/core/CoreNode.ts +105 -20
  77. package/src/core/CoreTextNode.ts +44 -43
  78. package/src/core/CoreTextureManager.ts +2 -0
  79. package/src/core/Stage.ts +10 -0
  80. package/src/core/TextureMemoryManager.ts +66 -0
  81. package/src/core/platform.ts +8 -0
  82. package/src/core/renderers/CoreContextTexture.ts +6 -1
  83. package/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +7 -2
  84. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +34 -6
  85. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +10 -2
  86. package/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.ts +16 -32
  87. package/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.ts +26 -32
  88. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +23 -0
  89. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +32 -4
  90. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +1 -3
  91. package/src/core/text-rendering/renderers/TextRenderer.ts +32 -0
  92. package/src/core/textures/Texture.ts +39 -2
  93. package/src/main-api/ICoreDriver.ts +2 -0
  94. package/src/main-api/Inspector.ts +2 -1
  95. package/src/main-api/RendererMain.ts +19 -2
  96. package/src/render-drivers/main/MainCoreDriver.ts +9 -0
  97. package/src/render-drivers/main/MainOnlyNode.ts +12 -6
  98. package/src/render-drivers/threadx/ThreadXCoreDriver.ts +1 -0
  99. package/src/render-drivers/threadx/ThreadXRendererMessage.ts +1 -0
  100. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +7 -0
  101. package/src/render-drivers/threadx/worker/renderer.ts +1 -0
  102. package/src/utils.ts +10 -1
@@ -290,6 +290,7 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
290
290
  textH: 0,
291
291
  fontInfo: undefined,
292
292
  fontFaceLoadedHandler: undefined,
293
+ isRenderable: false,
293
294
  debugData: {
294
295
  updateCount: 0,
295
296
  layoutCount: 0,
@@ -500,9 +501,11 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
500
501
  for (const pageInfo of canvasPages) {
501
502
  if (pageInfo.valid) continue;
502
503
  if (pageInfo.lineNumStart < 0) {
504
+ pageInfo.texture?.setRenderableOwner(state, false);
503
505
  pageInfo.texture = this.stage.txManager.loadTexture('ImageTexture', {
504
506
  src: '',
505
507
  });
508
+ pageInfo.texture.setRenderableOwner(state, state.isRenderable);
506
509
  pageInfo.valid = true;
507
510
  continue;
508
511
  }
@@ -517,6 +520,7 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
517
520
  ),
518
521
  });
519
522
  if (!(this.canvas.width === 0 || this.canvas.height === 0)) {
523
+ pageInfo.texture?.setRenderableOwner(state, false);
520
524
  pageInfo.texture = this.stage.txManager.loadTexture(
521
525
  'ImageTexture',
522
526
  {
@@ -531,6 +535,7 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
531
535
  preload: true,
532
536
  },
533
537
  );
538
+ pageInfo.texture.setRenderableOwner(state, state.isRenderable);
534
539
  }
535
540
  pageInfo.valid = true;
536
541
  }
@@ -694,6 +699,24 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
694
699
  // );
695
700
  // }
696
701
  }
702
+
703
+ override setIsRenderable(
704
+ state: CanvasTextRendererState,
705
+ renderable: boolean,
706
+ ): void {
707
+ super.setIsRenderable(state, renderable);
708
+ // Set state object owner from any canvas page textures
709
+ state.canvasPages?.forEach((pageInfo) => {
710
+ pageInfo.texture?.setRenderableOwner(state, renderable);
711
+ });
712
+ }
713
+
714
+ override destroyState(state: CanvasTextRendererState): void {
715
+ // Remove state object owner from any canvas page textures
716
+ state.canvasPages?.forEach((pageInfo) => {
717
+ pageInfo.texture?.setRenderableOwner(state, false);
718
+ });
719
+ }
697
720
  //#endregion Overrides
698
721
 
699
722
  /**
@@ -150,22 +150,22 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
150
150
  return {
151
151
  fontFamily: (state, value) => {
152
152
  state.props.fontFamily = value;
153
- state.trFontFace = undefined;
153
+ this.releaseFontFace(state);
154
154
  this.invalidateLayoutCache(state);
155
155
  },
156
156
  fontWeight: (state, value) => {
157
157
  state.props.fontWeight = value;
158
- state.trFontFace = undefined;
158
+ this.releaseFontFace(state);
159
159
  this.invalidateLayoutCache(state);
160
160
  },
161
161
  fontStyle: (state, value) => {
162
162
  state.props.fontStyle = value;
163
- state.trFontFace = undefined;
163
+ this.releaseFontFace(state);
164
164
  this.invalidateLayoutCache(state);
165
165
  },
166
166
  fontStretch: (state, value) => {
167
167
  state.props.fontStretch = value;
168
- state.trFontFace = undefined;
168
+ this.releaseFontFace(state);
169
169
  this.invalidateLayoutCache(state);
170
170
  },
171
171
  fontSize: (state, value) => {
@@ -360,6 +360,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
360
360
  textW: undefined,
361
361
  distanceRange: 0,
362
362
  trFontFace: undefined,
363
+ isRenderable: false,
363
364
  debugData: {
364
365
  updateCount: 0,
365
366
  layoutCount: 0,
@@ -392,6 +393,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
392
393
  this.setStatus(state, 'failed', new Error(msg));
393
394
  return;
394
395
  }
396
+ trFontFace.texture.setRenderableOwner(state, state.isRenderable);
395
397
  }
396
398
 
397
399
  // If the font hasn't been loaded yet, stop here.
@@ -741,6 +743,20 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
741
743
  // debugData.drawCount++;
742
744
  // }
743
745
  }
746
+
747
+ override setIsRenderable(
748
+ state: SdfTextRendererState,
749
+ renderable: boolean,
750
+ ): void {
751
+ super.setIsRenderable(state, renderable);
752
+ state.trFontFace?.texture.setRenderableOwner(state, renderable);
753
+ }
754
+
755
+ override destroyState(state: SdfTextRendererState): void {
756
+ super.destroyState(state);
757
+ // If there's a Font Face assigned we must free the owner relation to its texture
758
+ state.trFontFace?.texture.setRenderableOwner(state, false);
759
+ }
744
760
  //#endregion Overrides
745
761
 
746
762
  public resolveFontFace(props: TrFontProps): SdfTrFontFace | undefined {
@@ -749,6 +765,18 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
749
765
  | undefined;
750
766
  }
751
767
 
768
+ /**
769
+ * Release the loaded SDF font face
770
+ *
771
+ * @param state
772
+ */
773
+ protected releaseFontFace(state: SdfTextRendererState) {
774
+ if (state.trFontFace) {
775
+ state.trFontFace.texture.setRenderableOwner(state, false);
776
+ state.trFontFace = undefined;
777
+ }
778
+ }
779
+
752
780
  /**
753
781
  * Invalidate the layout cache stored in the state. This will cause the text
754
782
  * to be re-layed out on the next update.
@@ -212,8 +212,6 @@ export function layoutText(
212
212
  charEndX >= lineVertexW &&
213
213
  // There is a last word that we can break to the next line
214
214
  lastWord.codepointIndex !== -1 &&
215
- // We have advanced at least one character since the last word started
216
- lastWord.codepointIndex < glyph.cluster &&
217
215
  // Prevents infinite loop when a single word is longer than the width
218
216
  lastWord.xStart > 0
219
217
  ) {
@@ -286,8 +284,8 @@ export function layoutText(
286
284
  }
287
285
 
288
286
  maxY = Math.max(maxY, quadY + glyph.height);
287
+ maxX = Math.max(maxX, quadX + glyph.width);
289
288
  curX += glyph.xAdvance;
290
- maxX = Math.max(maxX, curX);
291
289
  }
292
290
  } else {
293
291
  // Unmapped character
@@ -66,6 +66,8 @@ export interface TextRendererState {
66
66
  textW: number | undefined;
67
67
  textH: number | undefined;
68
68
 
69
+ isRenderable: boolean;
70
+
69
71
  debugData: {
70
72
  updateCount: number;
71
73
  layoutCount: number;
@@ -311,6 +313,7 @@ export interface TrProps extends TrFontProps {
311
313
  overflowSuffix: string;
312
314
 
313
315
  zIndex: number;
316
+
314
317
  debug: Partial<TextRendererDebugProps>;
315
318
  }
316
319
 
@@ -450,6 +453,17 @@ export abstract class TextRenderer<
450
453
  state.emitter.emit(status, error);
451
454
  }
452
455
 
456
+ /**
457
+ * Allows the CoreTextNode to communicate changes to the isRenderable state of
458
+ * the itself.
459
+ *
460
+ * @param state
461
+ * @param renderable
462
+ */
463
+ setIsRenderable(state: StateT, renderable: boolean) {
464
+ state.isRenderable = renderable;
465
+ }
466
+
453
467
  /**
454
468
  * Called by constructor to get a map of property setter functions for this renderer.
455
469
  */
@@ -484,6 +498,24 @@ export abstract class TextRenderer<
484
498
 
485
499
  abstract createState(props: TrProps): StateT;
486
500
 
501
+ /**
502
+ * Destroy/Clean up the state object
503
+ *
504
+ * @remarks
505
+ * Opposite of createState(). Frees any event listeners / resources held by
506
+ * the state that may not reliably get garbage collected.
507
+ *
508
+ * @param state
509
+ */
510
+ destroyState(state: StateT) {
511
+ const stateEvents = ['loading', 'loaded', 'failed'];
512
+
513
+ // Remove the old event listeners from previous state obj there was one
514
+ stateEvents.forEach((eventName) => {
515
+ state.emitter.off(eventName);
516
+ });
517
+ }
518
+
487
519
  /**
488
520
  * Schedule a state update via queueMicrotask
489
521
  *
@@ -22,6 +22,11 @@ import type { SubTextureProps } from './SubTexture.js';
22
22
  import type { Dimensions } from '../../common/CommonTypes.js';
23
23
  import { EventEmitter } from '../../common/EventEmitter.js';
24
24
 
25
+ /**
26
+ * Event handler for when a Texture is freed
27
+ */
28
+ export type TextureFreedEventHandler = (target: any) => void;
29
+
25
30
  /**
26
31
  * Event handler for when a Texture is loading
27
32
  */
@@ -94,9 +99,10 @@ export interface TextureData {
94
99
  premultiplyAlpha?: boolean | null;
95
100
  }
96
101
 
97
- export type TextureState = 'loading' | 'loaded' | 'failed';
102
+ export type TextureState = 'freed' | 'loading' | 'loaded' | 'failed';
98
103
 
99
104
  export interface TextureStateEventMap {
105
+ freed: TextureFreedEventHandler;
100
106
  loading: TextureLoadingEventHandler;
101
107
  loaded: TextureLoadedEventHandler;
102
108
  failed: TextureFailedEventHandler;
@@ -135,12 +141,43 @@ export abstract class Texture extends EventEmitter {
135
141
 
136
142
  readonly error: Error | null = null;
137
143
 
138
- readonly state: TextureState = 'loading';
144
+ readonly state: TextureState = 'freed';
145
+
146
+ readonly renderableOwners = new Set<unknown>();
139
147
 
140
148
  constructor(protected txManager: CoreTextureManager) {
141
149
  super();
142
150
  }
143
151
 
152
+ /**
153
+ * Add/remove an owner to/from the Texture based on its renderability.
154
+ *
155
+ * @remarks
156
+ * Any object can own a texture, be it a CoreNode or even the state object
157
+ * from a Text Renderer.
158
+ *
159
+ * When the reference to the texture that an owner object holds is replaced
160
+ * or cleared it must call this with `renderable=false` to release the owner
161
+ * association.
162
+ *
163
+ * @param owner
164
+ * @param renderable
165
+ */
166
+ setRenderableOwner(owner: unknown, renderable: boolean): void {
167
+ if (renderable) {
168
+ this.renderableOwners.add(owner);
169
+ } else {
170
+ this.renderableOwners.delete(owner);
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Returns true if the texture is assigned to any Nodes that are renderable.
176
+ */
177
+ get renderable(): boolean {
178
+ return this.renderableOwners.size > 0;
179
+ }
180
+
144
181
  /**
145
182
  * Set the state of the texture
146
183
  *
@@ -63,4 +63,6 @@ export interface ICoreDriver {
63
63
  onFpsUpdate(fpsData: FpsUpdatePayload): void;
64
64
 
65
65
  onFrameTick(frameTickData: FrameTickPayload): void;
66
+
67
+ onIdle?(): void;
66
68
  }
@@ -9,6 +9,7 @@ import type { ICoreDriver } from './ICoreDriver.js';
9
9
  import { type RendererMainSettings } from './RendererMain.js';
10
10
  import type { AnimationSettings } from '../core/animations/CoreAnimation.js';
11
11
  import type { IAnimationController } from '../common/IAnimationController.js';
12
+ import { isProductionEnvironment } from '../utils.js';
12
13
 
13
14
  /**
14
15
  * Inspector
@@ -152,7 +153,7 @@ export class Inspector {
152
153
  private scaleY = 1;
153
154
 
154
155
  constructor(canvas: HTMLCanvasElement, settings: RendererMainSettings) {
155
- if (import.meta.env.PROD) return;
156
+ if (isProductionEnvironment()) return;
156
157
 
157
158
  if (!settings) {
158
159
  throw new Error('settings is required');
@@ -40,6 +40,7 @@ import type { TextureUsageTracker } from './texture-usage-trackers/TextureUsageT
40
40
  import { EventEmitter } from '../common/EventEmitter.js';
41
41
  import { Inspector } from './Inspector.js';
42
42
  import { santizeCustomDataMap } from '../render-drivers/utils.js';
43
+ import { isProductionEnvironment } from '../utils.js';
43
44
 
44
45
  /**
45
46
  * An immutable reference to a specific Texture type
@@ -127,11 +128,22 @@ export interface RendererMainSettings {
127
128
  appHeight?: number;
128
129
 
129
130
  /**
130
- * Bounds margin to extend the boundary in which a CoreNode is added as Quad.
131
+ * Texture Memory Byte Threshold
131
132
  *
133
+ * @remarks
134
+ * When the amount of GPU VRAM used by textures exceeds this threshold,
135
+ * the Renderer will free up all the textures that are current not visible
136
+ * within the configured `boundsMargin`.
132
137
  *
138
+ * When set to `0`, the threshold-based texture memory manager is disabled.
139
+ */
140
+ txMemByteThreshold?: number;
141
+
142
+ /**
143
+ * Bounds margin to extend the boundary in which a CoreNode is added as Quad.
133
144
  */
134
145
  boundsMargin?: number | [number, number, number, number];
146
+
135
147
  /**
136
148
  * Factor to convert app-authored logical coorindates to device logical coordinates
137
149
  *
@@ -317,6 +329,7 @@ export class RendererMain extends EventEmitter {
317
329
  const resolvedSettings: Required<RendererMainSettings> = {
318
330
  appWidth: settings.appWidth || 1920,
319
331
  appHeight: settings.appHeight || 1080,
332
+ txMemByteThreshold: settings.txMemByteThreshold || 124e6,
320
333
  boundsMargin: settings.boundsMargin || 0,
321
334
  deviceLogicalPixelRatio: settings.deviceLogicalPixelRatio || 1,
322
335
  devicePhysicalPixelRatio:
@@ -397,9 +410,13 @@ export class RendererMain extends EventEmitter {
397
410
  this.emit('frameTick', frameTickData);
398
411
  };
399
412
 
413
+ driver.onIdle = () => {
414
+ this.emit('idle');
415
+ };
416
+
400
417
  targetEl.appendChild(canvas);
401
418
 
402
- if (enableInspector && !import.meta.env.PROD) {
419
+ if (enableInspector && !isProductionEnvironment()) {
403
420
  this.inspector = new Inspector(canvas, resolvedSettings);
404
421
  }
405
422
  }
@@ -55,6 +55,7 @@ export class MainCoreDriver implements ICoreDriver {
55
55
  rootId: getNewId(),
56
56
  appWidth: rendererSettings.appWidth,
57
57
  appHeight: rendererSettings.appHeight,
58
+ txMemByteThreshold: rendererSettings.txMemByteThreshold,
58
59
  boundsMargin: rendererSettings.boundsMargin,
59
60
  deviceLogicalPixelRatio: rendererSettings.deviceLogicalPixelRatio,
60
61
  devicePhysicalPixelRatio: rendererSettings.devicePhysicalPixelRatio,
@@ -92,6 +93,10 @@ export class MainCoreDriver implements ICoreDriver {
92
93
  this.stage.on('frameTick', ((stage, frameTickData) => {
93
94
  this.onFrameTick(frameTickData);
94
95
  }) satisfies StageFrameTickHandler);
96
+
97
+ this.stage.on('idle', () => {
98
+ this.onIdle();
99
+ });
95
100
  }
96
101
 
97
102
  createNode(props: INodeWritableProps): INode {
@@ -145,5 +150,9 @@ export class MainCoreDriver implements ICoreDriver {
145
150
  onFrameTick(frameTickData: FrameTickPayload) {
146
151
  throw new Error('Method not implemented.');
147
152
  }
153
+
154
+ onIdle() {
155
+ throw new Error('Method not implemented.');
156
+ }
148
157
  //#endregion
149
158
  }
@@ -108,6 +108,7 @@ export class MainOnlyNode extends EventEmitter implements INode {
108
108
  // Forward loaded/failed events
109
109
  this.coreNode.on('loaded', this.onTextureLoaded);
110
110
  this.coreNode.on('failed', this.onTextureFailed);
111
+ this.coreNode.on('freed', this.onTextureFreed);
111
112
 
112
113
  this.coreNode.on('outOfBounds', this.onOutOfBounds);
113
114
  this.coreNode.on('inBounds', this.onInBounds);
@@ -420,6 +421,10 @@ export class MainOnlyNode extends EventEmitter implements INode {
420
421
  this.emit('failed', payload);
421
422
  };
422
423
 
424
+ private onTextureFreed: NodeLoadedEventHandler = (target, payload) => {
425
+ this.emit('freed', payload);
426
+ };
427
+
423
428
  private onOutOfBounds: NodeRenderStateEventHandler = (target, payload) => {
424
429
  this.emit('outOfBounds', payload);
425
430
  };
@@ -461,13 +466,14 @@ export class MainOnlyNode extends EventEmitter implements INode {
461
466
 
462
467
  destroy(): void {
463
468
  this.emit('beforeDestroy', {});
464
- this.coreNode.destroy();
465
- // destroy children
466
- const length = this.children.length;
467
- for (let i = 0; i < length; i++) {
468
- this.children[i]?.destroy();
469
+
470
+ //use while loop since setting parent to null removes it from array
471
+ let child = this.children[0];
472
+ while (child) {
473
+ child.destroy();
474
+ child = this.children[0];
469
475
  }
470
- this.children.length = 0;
476
+ this.coreNode.destroy();
471
477
  this.parent = null;
472
478
  this.texture = null;
473
479
  this.emit('afterDestroy', {});
@@ -112,6 +112,7 @@ export class ThreadXCoreDriver implements ICoreDriver {
112
112
  canvas: offscreenCanvas,
113
113
  appWidth: rendererSettings.appWidth,
114
114
  appHeight: rendererSettings.appHeight,
115
+ txMemByteThreshold: rendererSettings.txMemByteThreshold,
115
116
  boundsMargin: rendererSettings.boundsMargin,
116
117
  deviceLogicalPixelRatio: rendererSettings.deviceLogicalPixelRatio,
117
118
  devicePhysicalPixelRatio: rendererSettings.devicePhysicalPixelRatio,
@@ -44,6 +44,7 @@ export interface ThreadXRendererInitMessage extends ThreadXRendererMessage {
44
44
  canvas: OffscreenCanvas;
45
45
  appWidth: number;
46
46
  appHeight: number;
47
+ txMemByteThreshold: number;
47
48
  boundsMargin: number | [number, number, number, number];
48
49
  deviceLogicalPixelRatio: number;
49
50
  devicePhysicalPixelRatio: number;
@@ -33,6 +33,7 @@ import type { AnimationSettings } from '../../../core/animations/CoreAnimation.j
33
33
  import type {
34
34
  NodeLoadedPayload,
35
35
  NodeFailedPayload,
36
+ NodeTextureFreedPayload,
36
37
  } from '../../../common/CommonTypes.js';
37
38
 
38
39
  export class ThreadXRendererNode extends SharedNode {
@@ -143,6 +144,12 @@ export class ThreadXRendererNode extends SharedNode {
143
144
  this.emit('failed', payload);
144
145
  },
145
146
  );
147
+ this.coreNode.on(
148
+ 'freed',
149
+ (target: CoreNode, payload: NodeTextureFreedPayload) => {
150
+ this.emit('freed', payload);
151
+ },
152
+ );
146
153
  }
147
154
 
148
155
  override onPropertyChange<Key extends keyof this['z$__type__Props']>(
@@ -66,6 +66,7 @@ const threadx = ThreadX.init({
66
66
  rootId: nodeStruct.id,
67
67
  appWidth: message.appWidth,
68
68
  appHeight: message.appHeight,
69
+ txMemByteThreshold: message.txMemByteThreshold,
69
70
  boundsMargin: message.boundsMargin,
70
71
  deviceLogicalPixelRatio: message.deviceLogicalPixelRatio,
71
72
  devicePhysicalPixelRatio: message.devicePhysicalPixelRatio,
package/src/utils.ts CHANGED
@@ -79,7 +79,7 @@ export function assertTruthy(
79
79
  condition: unknown,
80
80
  message?: string,
81
81
  ): asserts condition {
82
- if (import.meta.env.PROD) return;
82
+ if (isProductionEnvironment()) return;
83
83
  if (!condition) {
84
84
  throw new Error(message || 'Assertion failed');
85
85
  }
@@ -205,3 +205,12 @@ export function hasOwn(obj: object, prop: string | number | symbol): boolean {
205
205
  export function deg2Rad(degrees: number): number {
206
206
  return (degrees * Math.PI) / 180;
207
207
  }
208
+
209
+ /**
210
+ * Checks import.meta if env is production
211
+ *
212
+ * @returns
213
+ */
214
+ export function isProductionEnvironment(): boolean {
215
+ return import.meta.env && import.meta.env.PROD;
216
+ }