@lightningjs/renderer 0.3.3 → 0.3.4

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 (38) hide show
  1. package/dist/src/core/CoreTextureManager.d.ts +4 -3
  2. package/dist/src/core/CoreTextureManager.js +7 -4
  3. package/dist/src/core/CoreTextureManager.js.map +1 -1
  4. package/dist/src/core/renderers/webgl/shaders/DynamicShader.d.ts +36 -1
  5. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js.map +1 -1
  6. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -2
  7. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +6 -1
  8. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  9. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.d.ts +1 -1
  10. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js.map +1 -1
  11. package/dist/src/core/textures/SubTexture.d.ts +2 -2
  12. package/dist/src/main-api/INode.d.ts +5 -5
  13. package/dist/src/main-api/RendererMain.d.ts +88 -8
  14. package/dist/src/main-api/RendererMain.js +22 -6
  15. package/dist/src/main-api/RendererMain.js.map +1 -1
  16. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +7 -7
  17. package/dist/src/render-drivers/main/MainOnlyNode.js +8 -1
  18. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  19. package/dist/src/render-drivers/threadx/ThreadXMainNode.d.ts +8 -7
  20. package/dist/src/render-drivers/threadx/ThreadXMainNode.js +11 -1
  21. package/dist/src/render-drivers/threadx/ThreadXMainNode.js.map +1 -1
  22. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +0 -1
  23. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  24. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  25. package/package.json +1 -1
  26. package/src/core/CoreTextureManager.ts +7 -6
  27. package/src/core/renderers/webgl/shaders/DynamicShader.ts +42 -2
  28. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +9 -3
  29. package/src/core/text-rendering/renderers/LightningTextTextureRenderer.ts +6 -4
  30. package/src/core/textures/SubTexture.ts +2 -2
  31. package/src/main-api/INode.ts +5 -5
  32. package/src/main-api/RendererMain.ts +125 -23
  33. package/src/main-api/texture-usage-trackers/FinalizationRegistryTextureUsageTracker.ts +45 -0
  34. package/src/main-api/texture-usage-trackers/ManualCountTextureUsageTracker.ts +154 -0
  35. package/src/main-api/texture-usage-trackers/TextureUsageTracker.ts +54 -0
  36. package/src/render-drivers/main/MainOnlyNode.ts +16 -9
  37. package/src/render-drivers/threadx/ThreadXMainNode.ts +20 -9
  38. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +5 -9
@@ -38,6 +38,44 @@ import { GlitchEffect } from './effects/GlitchEffect.js';
38
38
  import { FadeOutEffect } from './effects/FadeOutEffect.js';
39
39
  import { RadialGradientEffect } from './effects/RadialGradientEffect.js';
40
40
 
41
+ /**
42
+ * Allows the `keyof EffectMap` to be mapped over and form an discriminated
43
+ * union of all the EffectDescs structures individually.
44
+ *
45
+ * @remarks
46
+ * When used like the following:
47
+ * ```
48
+ * MapEffectDescs<keyof EffectMap>[]
49
+ * ```
50
+ * The resultant type will be a discriminated union like so:
51
+ * ```
52
+ * (
53
+ * {
54
+ * type: 'radius',
55
+ * props?: {
56
+ * radius?: number | number[];
57
+ * }
58
+ * } |
59
+ * {
60
+ * type: 'border',
61
+ * props?: {
62
+ * width?: number;
63
+ * color?: number;
64
+ * }
65
+ * } |
66
+ * // ...
67
+ * )[]
68
+ * ```
69
+ * Which means TypeScript will now base its type checking on the `type` field
70
+ * and will know exactly what the `props` field should be based on the `type`
71
+ * field.
72
+ */
73
+ type MapEffectDescs<T extends keyof EffectMap> = T extends keyof EffectMap
74
+ ? SpecificEffectDesc<T>
75
+ : never;
76
+
77
+ type EffectDesc = MapEffectDescs<keyof EffectMap>;
78
+
41
79
  export interface DynamicShaderProps
42
80
  extends DimensionsShaderProp,
43
81
  AlphaShaderProp {
@@ -72,7 +110,9 @@ const Effects = {
72
110
  glitch: GlitchEffect,
73
111
  };
74
112
 
75
- export interface EffectDesc<FxType extends keyof EffectMap = keyof EffectMap> {
113
+ export interface SpecificEffectDesc<
114
+ FxType extends keyof EffectMap = keyof EffectMap,
115
+ > {
76
116
  type: FxType;
77
117
  props?: ExtractProps<EffectMap[FxType]>;
78
118
  }
@@ -325,7 +365,7 @@ export class DynamicShader extends WebGlCoreShader {
325
365
  effects: (props.effects ?? []).map((effect) => ({
326
366
  type: effect.type,
327
367
  props: Effects[effect.type].resolveDefaults(effect.props || {}),
328
- })),
368
+ })) as MapEffectDescs<keyof EffectMap>[],
329
369
  $dimensions: {
330
370
  width: 0,
331
371
  height: 0,
@@ -85,12 +85,18 @@ export interface CanvasTextRendererState extends TextRendererState {
85
85
  }
86
86
 
87
87
  export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
88
- protected canvas: OffscreenCanvas;
89
- protected context: OffscreenCanvasRenderingContext2D;
88
+ protected canvas: OffscreenCanvas | HTMLCanvasElement;
89
+ protected context:
90
+ | OffscreenCanvasRenderingContext2D
91
+ | CanvasRenderingContext2D;
90
92
 
91
93
  constructor(stage: Stage) {
92
94
  super(stage);
93
- this.canvas = new OffscreenCanvas(0, 0);
95
+ if (typeof OffscreenCanvas !== 'undefined') {
96
+ this.canvas = new OffscreenCanvas(0, 0);
97
+ } else {
98
+ this.canvas = document.createElement('canvas');
99
+ }
94
100
  const context = this.canvas.getContext('2d');
95
101
  assertTruthy(context);
96
102
  this.context = context;
@@ -126,14 +126,16 @@ export interface RenderInfo {
126
126
  }
127
127
 
128
128
  export class LightningTextTextureRenderer {
129
- private _canvas: OffscreenCanvas;
130
- private _context: OffscreenCanvasRenderingContext2D;
129
+ private _canvas: OffscreenCanvas | HTMLCanvasElement;
130
+ private _context:
131
+ | OffscreenCanvasRenderingContext2D
132
+ | CanvasRenderingContext2D;
131
133
  private _settings: Settings;
132
134
  private renderInfo: RenderInfo | undefined;
133
135
 
134
136
  constructor(
135
- canvas: OffscreenCanvas,
136
- context: OffscreenCanvasRenderingContext2D,
137
+ canvas: OffscreenCanvas | HTMLCanvasElement,
138
+ context: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D,
137
139
  ) {
138
140
  this._canvas = canvas;
139
141
  this._context = context;
@@ -21,7 +21,7 @@ import type {
21
21
  TextureFailedEventHandler,
22
22
  TextureLoadedEventHandler,
23
23
  } from '../../common/CommonTypes.js';
24
- import type { TextureDesc } from '../../main-api/RendererMain.js';
24
+ import type { TextureRef } from '../../main-api/RendererMain.js';
25
25
  import type { CoreTextureManager } from '../CoreTextureManager.js';
26
26
  import { Texture } from './Texture.js';
27
27
 
@@ -32,7 +32,7 @@ export interface SubTextureProps {
32
32
  /**
33
33
  * The texture that this sub-texture is a sub-region of.
34
34
  */
35
- texture: TextureDesc;
35
+ texture: TextureRef;
36
36
 
37
37
  /**
38
38
  * The x pixel position of the top-left of the sub-texture within the parent
@@ -19,7 +19,7 @@
19
19
 
20
20
  import type { IEventEmitter } from '@lightningjs/threadx';
21
21
  import type { IAnimationController } from '../common/IAnimationController.js';
22
- import type { ShaderDesc, TextureDesc } from './RendererMain.js';
22
+ import type { ShaderRef, TextureRef } from './RendererMain.js';
23
23
  import type {
24
24
  TextRendererMap,
25
25
  TrProps,
@@ -206,7 +206,7 @@ export interface INodeWritableProps {
206
206
  * and TBD properties.
207
207
  *
208
208
  * To create a Texture in order to set it on this property, call
209
- * {@link RendererMain.makeTexture}.
209
+ * {@link RendererMain.createTexture}.
210
210
  *
211
211
  * If the {@link src} is set on a Node, the Node will use the
212
212
  * {@link ImageTexture} by default and the Node will simply load the image at
@@ -215,7 +215,7 @@ export interface INodeWritableProps {
215
215
  * Note: If this is a Text Node, the Texture will be managed by the Node's
216
216
  * {@link TextRenderer} and should not be set explicitly.
217
217
  */
218
- texture: TextureDesc | null;
218
+ texture: TextureRef | null;
219
219
  /**
220
220
  * The Node's shader
221
221
  *
@@ -225,12 +225,12 @@ export interface INodeWritableProps {
225
225
  * or {@link color}(s) within the Node without any special effects.
226
226
  *
227
227
  * To create a Shader in order to set it on this property, call
228
- * {@link RendererMain.makeShader}.
228
+ * {@link RendererMain.createShader}.
229
229
  *
230
230
  * Note: If this is a Text Node, the Shader will be managed by the Node's
231
231
  * {@link TextRenderer} and should not be set explicitly.
232
232
  */
233
- shader: ShaderDesc | null;
233
+ shader: ShaderRef | null;
234
234
  /**
235
235
  * Image URL
236
236
  *
@@ -31,29 +31,80 @@ import type {
31
31
  ITextNodeWritableProps,
32
32
  } from './INode.js';
33
33
  import type { IRenderDriver } from './IRenderDriver.js';
34
+ import {
35
+ ManualCountTextureUsageTracker,
36
+ type ManualCountTextureUsageTrackerOptions,
37
+ } from './texture-usage-trackers/ManualCountTextureUsageTracker.js';
38
+ import { FinalizationRegistryTextureUsageTracker } from './texture-usage-trackers/FinalizationRegistryTextureUsageTracker.js';
39
+ import type { TextureUsageTracker } from './texture-usage-trackers/TextureUsageTracker.js';
34
40
 
35
41
  /**
36
- * A description of a Texture
42
+ * An immutable reference to a specific Texture type
37
43
  *
38
44
  * @remarks
39
- * This structure should only be created by the RendererMain's `makeTexture`
40
- * method. The structure is immutable and should not be modified once created.
45
+ * See {@link TextureRef} for more details.
41
46
  */
42
- export interface TextureDesc<
43
- TxType extends keyof TextureMap = keyof TextureMap,
44
- > {
47
+ export interface SpecificTextureRef<TxType extends keyof TextureMap> {
45
48
  readonly descType: 'texture';
46
49
  readonly txType: TxType;
47
50
  readonly props: ExtractProps<TextureMap[TxType]>;
48
51
  readonly options?: Readonly<TextureOptions>;
49
52
  }
50
53
 
51
- export interface ShaderDesc<ShType extends keyof ShaderMap = keyof ShaderMap> {
54
+ type MapTextureRefs<TxType extends keyof TextureMap> =
55
+ TxType extends keyof TextureMap ? SpecificTextureRef<TxType> : never;
56
+
57
+ /**
58
+ * An immutable reference to a Texture
59
+ *
60
+ * @remarks
61
+ * This structure should only be created by the RendererMain's `createTexture`
62
+ * method. The structure is immutable and should not be modified once created.
63
+ *
64
+ * A `TextureRef` exists in the Main API Space and is used to point to an actual
65
+ * `Texture` instance in the Core API Space. The `TextureRef` is used to
66
+ * communicate with the Core API Space to create, load, and destroy the
67
+ * `Texture` instance.
68
+ *
69
+ * This type is technically a discriminated union of all possible texture types.
70
+ * If you'd like to represent a specific texture type, you can use the
71
+ * `SpecificTextureRef` generic type.
72
+ */
73
+ export type TextureRef = MapTextureRefs<keyof TextureMap>;
74
+
75
+ /**
76
+ * An immutable reference to a specific Shader type
77
+ *
78
+ * @remarks
79
+ * See {@link ShaderRef} for more details.
80
+ */
81
+ export interface SpecificShaderRef<ShType extends keyof ShaderMap> {
52
82
  readonly descType: 'shader';
53
83
  readonly shType: ShType;
54
84
  readonly props: ExtractProps<ShaderMap[ShType]>;
55
85
  }
56
86
 
87
+ type MapShaderRefs<ShType extends keyof ShaderMap> =
88
+ ShType extends keyof ShaderMap ? SpecificShaderRef<ShType> : never;
89
+
90
+ /**
91
+ * An immutable reference to a Shader
92
+ *
93
+ * @remarks
94
+ * This structure should only be created by the RendererMain's `createShader`
95
+ * method. The structure is immutable and should not be modified once created.
96
+ *
97
+ * A `ShaderRef` exists in the Main API Space and is used to point to an actual
98
+ * `Shader` instance in the Core API Space. The `ShaderRef` is used to
99
+ * communicate with the Core API Space to create, load, and destroy the
100
+ * `Shader` instance.
101
+ *
102
+ * This type is technically a discriminated union of all possible shader types.
103
+ * If you'd like to represent a specific shader type, you can use the
104
+ * `SpecificShaderRef` generic type.
105
+ */
106
+ export type ShaderRef = MapShaderRefs<keyof ShaderMap>;
107
+
57
108
  /**
58
109
  * Configuration settings for {@link RendererMain}
59
110
  */
@@ -115,8 +166,40 @@ export interface RendererMainSettings {
115
166
 
116
167
  /**
117
168
  * Path to a custom core module to use
169
+ *
170
+ * @defaultValue `null`
118
171
  */
119
172
  coreExtensionModule?: string | null;
173
+
174
+ /**
175
+ * Enable experimental FinalizationRegistry-based texture usage tracking
176
+ * for texture garbage collection
177
+ *
178
+ * @remarks
179
+ * By default, the Renderer uses a manual reference counting system to track
180
+ * texture usage. Textures are eventually released from the Core Texture
181
+ * Manager's Usage Cache when they are no longer referenced by any Nodes (or
182
+ * SubTextures that are referenced by nodes). This works well enough, but has
183
+ * the consequence of textures being removed from Usage Cache even if their
184
+ * references are still alive in memory. This can require a texture to be
185
+ * reloaded from the source when it is used again after being removed from
186
+ * cache.
187
+ *
188
+ * This is an experimental feature that uses a FinalizationRegistry to track
189
+ * texture usage. This causes textures to be removed from the Usage Cache only
190
+ * when their references are no longer alive in memory. Meaning a loaded texture
191
+ * will remain in the Usage Cache until it's reference is garbage collected.
192
+ *
193
+ * This feature is not enabled by default because browser support for the
194
+ * FinalizationRegistry is limited. It should NOT be enabled in production apps
195
+ * as this behavior is not guaranteed to be supported in the future. Developer
196
+ * feedback on this feature, however, is welcome.
197
+ *
198
+ * @defaultValue `false`
199
+ */
200
+ experimental_FinalizationRegistryTextureUsageTracker?: boolean;
201
+
202
+ textureCleanupOptions?: ManualCountTextureUsageTrackerOptions;
120
203
  }
121
204
 
122
205
  /**
@@ -151,11 +234,13 @@ export class RendererMain {
151
234
  private nodes: Map<number, INode> = new Map();
152
235
  private nextTextureId = 1;
153
236
 
154
- private textureRegistry = new FinalizationRegistry(
155
- (textureDescId: number) => {
156
- this.driver.releaseTexture(textureDescId);
157
- },
158
- );
237
+ /**
238
+ * Texture Usage Tracker for Usage Based Texture Garbage Collection
239
+ *
240
+ * @remarks
241
+ * For internal use only. DO NOT ACCESS.
242
+ */
243
+ public textureTracker: TextureUsageTracker;
159
244
 
160
245
  /**
161
246
  * Constructs a new Renderer instance
@@ -177,6 +262,9 @@ export class RendererMain {
177
262
  settings.devicePhysicalPixelRatio || window.devicePixelRatio,
178
263
  clearColor: settings.clearColor ?? 0x00000000,
179
264
  coreExtensionModule: settings.coreExtensionModule || null,
265
+ experimental_FinalizationRegistryTextureUsageTracker:
266
+ settings.experimental_FinalizationRegistryTextureUsageTracker ?? false,
267
+ textureCleanupOptions: settings.textureCleanupOptions || {},
180
268
  };
181
269
  this.settings = resolvedSettings;
182
270
 
@@ -187,6 +275,20 @@ export class RendererMain {
187
275
  devicePhysicalPixelRatio,
188
276
  } = resolvedSettings;
189
277
 
278
+ const releaseCallback = (textureId: number) => {
279
+ this.driver.releaseTexture(textureId);
280
+ };
281
+
282
+ const useFinalizationRegistryTracker =
283
+ resolvedSettings.experimental_FinalizationRegistryTextureUsageTracker &&
284
+ typeof FinalizationRegistry === 'function';
285
+ this.textureTracker = useFinalizationRegistryTracker
286
+ ? new FinalizationRegistryTextureUsageTracker(releaseCallback)
287
+ : new ManualCountTextureUsageTracker(
288
+ releaseCallback,
289
+ this.settings.textureCleanupOptions,
290
+ );
291
+
190
292
  const deviceLogicalWidth = appWidth * deviceLogicalPixelRatio;
191
293
  const deviceLogicalHeight = appHeight * deviceLogicalPixelRatio;
192
294
 
@@ -373,13 +475,13 @@ export class RendererMain {
373
475
  * @param options
374
476
  * @returns
375
477
  */
376
- makeTexture<Type extends keyof TextureMap>(
377
- textureType: Type,
378
- props: TextureDesc<Type>['props'],
478
+ createTexture<TxType extends keyof TextureMap>(
479
+ textureType: TxType,
480
+ props: SpecificTextureRef<TxType>['props'],
379
481
  options?: TextureOptions,
380
- ): TextureDesc<Type> {
482
+ ): SpecificTextureRef<TxType> {
381
483
  const id = this.nextTextureId++;
382
- const desc: TextureDesc<Type> = {
484
+ const desc = {
383
485
  descType: 'texture',
384
486
  txType: textureType,
385
487
  props,
@@ -389,8 +491,8 @@ export class RendererMain {
389
491
  // ID Texture Map cache.
390
492
  id,
391
493
  },
392
- };
393
- this.textureRegistry.register(desc, id);
494
+ } satisfies SpecificTextureRef<TxType>;
495
+ this.textureTracker.registerTexture(desc as TextureRef);
394
496
  return desc;
395
497
  }
396
498
 
@@ -407,14 +509,14 @@ export class RendererMain {
407
509
  * @param props
408
510
  * @returns
409
511
  */
410
- makeShader<ShType extends keyof ShaderMap>(
512
+ createShader<ShType extends keyof ShaderMap>(
411
513
  shaderType: ShType,
412
- props?: ShaderDesc<ShType>['props'],
413
- ): ShaderDesc<ShType> {
514
+ props?: SpecificShaderRef<ShType>['props'],
515
+ ): SpecificShaderRef<ShType> {
414
516
  return {
415
517
  descType: 'shader',
416
518
  shType: shaderType,
417
- props: props as ShaderDesc<ShType>['props'],
519
+ props: props as SpecificShaderRef<ShType>['props'],
418
520
  };
419
521
  }
420
522
 
@@ -0,0 +1,45 @@
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 2023 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
+
20
+ import { assertTruthy } from '../../utils.js';
21
+ import type { TextureRef } from '../RendererMain.js';
22
+ import { TextureUsageTracker } from './TextureUsageTracker.js';
23
+
24
+ export class FinalizationRegistryTextureUsageTracker extends TextureUsageTracker {
25
+ private registry: FinalizationRegistry<number>;
26
+
27
+ constructor(releaseCallback: (textureDescId: number) => void) {
28
+ super(releaseCallback);
29
+ this.registry = new FinalizationRegistry(releaseCallback);
30
+ }
31
+
32
+ override registerTexture(texture: TextureRef): void {
33
+ assertTruthy(
34
+ texture.options?.id,
35
+ 'Texture must have an ID to be registered',
36
+ );
37
+ this.registry.register(texture, texture.options?.id);
38
+ }
39
+ override incrementTextureRefCount(): void {
40
+ // No-op for FinalizationRegistry
41
+ }
42
+ override decrementTextureRefCount(): void {
43
+ // No-op for FinalizationRegistry
44
+ }
45
+ }
@@ -0,0 +1,154 @@
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 2023 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
+
20
+ import { assertTruthy } from '../../utils.js';
21
+ import type { TextureRef } from '../RendererMain.js';
22
+ import { TextureUsageTracker } from './TextureUsageTracker.js';
23
+
24
+ interface TextureRefInfo {
25
+ /**
26
+ * Texture Reference ID
27
+ */
28
+ id: number;
29
+ /**
30
+ * The number of references to this texture that are currently assigned to
31
+ * Nodes.
32
+ */
33
+ nodeRefCount: number;
34
+ /**
35
+ * The last time the texture reference count was updated.
36
+ *
37
+ * @remarks
38
+ * This is used to determine when a texture is no longer referenced by any
39
+ * Nodes and can be removed from the GPU.
40
+ */
41
+ lastUpdate: number;
42
+ }
43
+
44
+ export interface ManualCountTextureUsageTrackerOptions {
45
+ /**
46
+ * The interval at which to check if textures that are no longer referenced
47
+ * by any Nodes can be released from the Core Space Texture Usage Cache.
48
+ *
49
+ * @remarks
50
+ * Only valid when the {@link ManualCountTextureUsageTracker} is used.
51
+ *
52
+ * @defaultValue 10000 (10 seconds)
53
+ */
54
+ textureCleanupIntervalMs?: number;
55
+ /**
56
+ * The age at which a texture is considered to be no longer referenced by any
57
+ * Nodes and can be released from the Core Space Texture Usage Cache.
58
+ *
59
+ * @remarks
60
+ * Only valid when the {@link ManualCountTextureUsageTracker} is used.
61
+ *
62
+ * @defaultValue 60000 (1 minute)
63
+ */
64
+ textureCleanupAgeThreadholdMs?: number;
65
+ }
66
+
67
+ /**
68
+ * Usage-based Texture Garbage Collection Registry
69
+ */
70
+ export class ManualCountTextureUsageTracker extends TextureUsageTracker {
71
+ textureMap: Map<number, TextureRefInfo> = new Map();
72
+ zeroReferenceTextureSet: Set<TextureRefInfo> = new Set();
73
+ options: Required<ManualCountTextureUsageTrackerOptions>;
74
+
75
+ constructor(
76
+ releaseCallback: (textureDescId: number) => void,
77
+ options: ManualCountTextureUsageTrackerOptions,
78
+ ) {
79
+ super(releaseCallback);
80
+ this.options = {
81
+ textureCleanupIntervalMs: options.textureCleanupIntervalMs ?? 10000,
82
+ textureCleanupAgeThreadholdMs:
83
+ options.textureCleanupAgeThreadholdMs ?? 60000,
84
+ };
85
+ // Periodically check for textures that are no longer referenced by any
86
+ // Nodes and notify RendererMain to release them.
87
+ setInterval(() => {
88
+ const now = Date.now();
89
+ const thresholdMs = this.options.textureCleanupAgeThreadholdMs;
90
+ for (const textureRefInfo of this.zeroReferenceTextureSet) {
91
+ if (now - textureRefInfo.lastUpdate > thresholdMs) {
92
+ this.releaseCallback(textureRefInfo.id);
93
+ this.textureMap.delete(textureRefInfo.id);
94
+ this.zeroReferenceTextureSet.delete(textureRefInfo);
95
+ }
96
+ }
97
+ }, this.options.textureCleanupIntervalMs);
98
+ }
99
+
100
+ registerTexture(texture: TextureRef) {
101
+ const textureId = texture.options?.id;
102
+ assertTruthy(textureId, 'Texture must have an id to be registered');
103
+ if (!this.textureMap.has(textureId)) {
104
+ const textureRefInfo: TextureRefInfo = {
105
+ id: textureId,
106
+ nodeRefCount: 0,
107
+ lastUpdate: Date.now(),
108
+ };
109
+ this.textureMap.set(textureId, textureRefInfo);
110
+ this.zeroReferenceTextureSet.add(textureRefInfo);
111
+ }
112
+ }
113
+
114
+ incrementTextureRefCount(texture: TextureRef) {
115
+ const textureId = texture.options?.id;
116
+ assertTruthy(textureId, 'Texture must have an id to be registered');
117
+ let textureRefInfo = this.textureMap.get(textureId);
118
+ if (!textureRefInfo) {
119
+ // Texture has not been registered yet, so register it now.
120
+ // This may happen if the TextureRef was cleaned up from the registry
121
+ // but was still alive in memory and eventually re-used.
122
+ this.registerTexture(texture);
123
+ textureRefInfo = this.textureMap.get(textureId);
124
+ }
125
+ assertTruthy(textureRefInfo, 'Texture must have been registered');
126
+ if (texture.txType === 'SubTexture') {
127
+ // If this is a SubTexture, then increment the reference count of the
128
+ // parent texture as well.
129
+ this.incrementTextureRefCount(texture.props.texture);
130
+ }
131
+ textureRefInfo.nodeRefCount++;
132
+ textureRefInfo.lastUpdate = Date.now();
133
+ if (this.zeroReferenceTextureSet.has(textureRefInfo)) {
134
+ this.zeroReferenceTextureSet.delete(textureRefInfo);
135
+ }
136
+ }
137
+
138
+ decrementTextureRefCount(texture: TextureRef) {
139
+ const textureId = texture.options?.id;
140
+ assertTruthy(textureId, 'Texture must have an id to be registered');
141
+ const textureRefInfo = this.textureMap.get(textureId);
142
+ assertTruthy(textureRefInfo, 'Texture must have been registered');
143
+ textureRefInfo.nodeRefCount--;
144
+ textureRefInfo.lastUpdate = Date.now();
145
+ if (textureRefInfo.nodeRefCount === 0) {
146
+ this.zeroReferenceTextureSet.add(textureRefInfo);
147
+ }
148
+ if (texture.txType === 'SubTexture') {
149
+ // If this is a SubTexture, then decrement the reference count of the
150
+ // parent texture as well.
151
+ this.decrementTextureRefCount(texture.props.texture);
152
+ }
153
+ }
154
+ }
@@ -0,0 +1,54 @@
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 2023 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
+
20
+ import type { TextureRef } from '../RendererMain.js';
21
+
22
+ /**
23
+ * Texture Usage Tracker for Usage Based Texture Garbage Collection
24
+ */
25
+ export abstract class TextureUsageTracker {
26
+ constructor(protected releaseCallback: (textureDescId: number) => void) {}
27
+
28
+ /**
29
+ * Register a texture with the tracker.
30
+ *
31
+ * @param texture
32
+ */
33
+ abstract registerTexture(texture: TextureRef): void;
34
+
35
+ /**
36
+ * Increment the reference count for a texture.
37
+ *
38
+ * @remarks
39
+ * This should be called anytime a Node sets a new texture.
40
+ *
41
+ * @param texture
42
+ */
43
+ abstract incrementTextureRefCount(texture: TextureRef): void;
44
+
45
+ /**
46
+ * Decrement the Node reference count for a texture.
47
+ *
48
+ * @remarks
49
+ * This should be called anytime a Node removes a texture.
50
+ *
51
+ * @param texture
52
+ */
53
+ abstract decrementTextureRefCount(texture: TextureRef): void;
54
+ }