@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
@@ -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
+ }