@lightningjs/renderer 2.12.1 → 2.13.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 (185) hide show
  1. package/dist/src/core/CoreNode.d.ts +10 -0
  2. package/dist/src/core/CoreNode.js +90 -14
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/CoreTextureManager.d.ts +13 -7
  5. package/dist/src/core/CoreTextureManager.js +92 -105
  6. package/dist/src/core/CoreTextureManager.js.map +1 -1
  7. package/dist/src/core/Stage.d.ts +4 -2
  8. package/dist/src/core/Stage.js +23 -7
  9. package/dist/src/core/Stage.js.map +1 -1
  10. package/dist/src/core/TextureMemoryManager.d.ts +16 -6
  11. package/dist/src/core/TextureMemoryManager.js +75 -17
  12. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  13. package/dist/src/core/lib/ImageWorker.d.ts +1 -1
  14. package/dist/src/core/lib/ImageWorker.js +15 -12
  15. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  16. package/dist/src/core/lib/utils.d.ts +4 -0
  17. package/dist/src/core/lib/utils.js +46 -0
  18. package/dist/src/core/lib/utils.js.map +1 -1
  19. package/dist/src/core/lib/validateImageBitmap.d.ts +6 -0
  20. package/dist/src/core/lib/validateImageBitmap.js +68 -0
  21. package/dist/src/core/lib/validateImageBitmap.js.map +1 -0
  22. package/dist/src/core/platform.js +3 -3
  23. package/dist/src/core/platform.js.map +1 -1
  24. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +7 -12
  25. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  26. package/dist/src/core/textures/ImageTexture.d.ts +2 -2
  27. package/dist/src/core/textures/ImageTexture.js +34 -20
  28. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  29. package/dist/src/main-api/ICoreDriver.d.ts +1 -4
  30. package/dist/src/main-api/Inspector.js +16 -2
  31. package/dist/src/main-api/Inspector.js.map +1 -1
  32. package/dist/src/main-api/Renderer.d.ts +27 -1
  33. package/dist/src/main-api/Renderer.js +8 -3
  34. package/dist/src/main-api/Renderer.js.map +1 -1
  35. package/dist/src/main-api/RendererMain.d.ts +5 -2
  36. package/dist/src/main-api/RendererMain.js +6 -4
  37. package/dist/src/main-api/RendererMain.js.map +1 -1
  38. package/dist/src/main-api/texture-usage-trackers/ManualCountTextureUsageTracker.js.map +1 -1
  39. package/dist/src/render-drivers/main/MainCoreDriver.d.ts +1 -4
  40. package/dist/src/render-drivers/main/MainCoreDriver.js +1 -4
  41. package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
  42. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +8 -6
  43. package/dist/src/render-drivers/main/MainOnlyNode.js +30 -1
  44. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  45. package/dist/src/render-drivers/main/MainOnlyTextNode.d.ts +1 -1
  46. package/dist/src/render-drivers/main/MainOnlyTextNode.js +2 -3
  47. package/dist/src/render-drivers/main/MainOnlyTextNode.js.map +1 -1
  48. package/dist/src/render-drivers/threadx/NodeStruct.d.ts +3 -0
  49. package/dist/src/render-drivers/threadx/NodeStruct.js +9 -0
  50. package/dist/src/render-drivers/threadx/NodeStruct.js.map +1 -1
  51. package/dist/src/render-drivers/threadx/SharedNode.d.ts +1 -0
  52. package/dist/src/render-drivers/threadx/SharedNode.js +1 -0
  53. package/dist/src/render-drivers/threadx/SharedNode.js.map +1 -1
  54. package/dist/src/render-drivers/threadx/TextNodeStruct.js +3 -1
  55. package/dist/src/render-drivers/threadx/TextNodeStruct.js.map +1 -1
  56. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.d.ts +1 -4
  57. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +2 -4
  58. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  59. package/dist/src/render-drivers/threadx/ThreadXMainAnimationController.d.ts +8 -4
  60. package/dist/src/render-drivers/threadx/ThreadXMainAnimationController.js +53 -24
  61. package/dist/src/render-drivers/threadx/ThreadXMainAnimationController.js.map +1 -1
  62. package/dist/src/render-drivers/threadx/ThreadXMainNode.d.ts +7 -5
  63. package/dist/src/render-drivers/threadx/ThreadXMainNode.js +8 -2
  64. package/dist/src/render-drivers/threadx/ThreadXMainNode.js.map +1 -1
  65. package/dist/src/render-drivers/threadx/ThreadXMainTextNode.js.map +1 -1
  66. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +7 -0
  67. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  68. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js +1 -0
  69. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js.map +1 -1
  70. package/dist/src/render-drivers/threadx/worker/renderer.js +2 -0
  71. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  72. package/dist/src/render-drivers/utils.js +6 -1
  73. package/dist/src/render-drivers/utils.js.map +1 -1
  74. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  75. package/dist/tsconfig.tsbuildinfo +1 -0
  76. package/package.json +1 -1
  77. package/src/core/CoreNode.test.ts +155 -152
  78. package/src/core/CoreNode.ts +136 -22
  79. package/src/core/CoreTextureManager.ts +127 -126
  80. package/src/core/Stage.ts +28 -7
  81. package/src/core/TextureMemoryManager.ts +108 -27
  82. package/src/core/lib/ImageWorker.ts +22 -13
  83. package/src/core/lib/utils.ts +74 -0
  84. package/src/core/lib/validateImageBitmap.ts +76 -0
  85. package/src/core/platform.ts +5 -3
  86. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +11 -13
  87. package/src/core/textures/ImageTexture.ts +42 -25
  88. package/src/main-api/Inspector.ts +19 -2
  89. package/src/main-api/Renderer.ts +34 -3
  90. package/dist/src/core/CoreStuff.d.ts +0 -1
  91. package/dist/src/core/CoreStuff.js +0 -138
  92. package/dist/src/core/CoreStuff.js.map +0 -1
  93. package/dist/src/core/CoreTexturizer.d.ts +0 -14
  94. package/dist/src/core/CoreTexturizer.js +0 -47
  95. package/dist/src/core/CoreTexturizer.js.map +0 -1
  96. package/dist/src/core/LngNode.d.ts +0 -736
  97. package/dist/src/core/LngNode.js +0 -1174
  98. package/dist/src/core/LngNode.js.map +0 -1
  99. package/dist/src/core/Matrix2DContext.d.ts +0 -15
  100. package/dist/src/core/Matrix2DContext.js +0 -45
  101. package/dist/src/core/Matrix2DContext.js.map +0 -1
  102. package/dist/src/core/ShaderNode.d.ts +0 -10
  103. package/dist/src/core/ShaderNode.js +0 -30
  104. package/dist/src/core/ShaderNode.js.map +0 -1
  105. package/dist/src/core/TextNode.d.ts +0 -103
  106. package/dist/src/core/TextNode.js +0 -331
  107. package/dist/src/core/TextNode.js.map +0 -1
  108. package/dist/src/core/lib/Coords.d.ts +0 -14
  109. package/dist/src/core/lib/Coords.js +0 -55
  110. package/dist/src/core/lib/Coords.js.map +0 -1
  111. package/dist/src/core/lib/glm/common.d.ts +0 -162
  112. package/dist/src/core/lib/glm/common.js +0 -81
  113. package/dist/src/core/lib/glm/common.js.map +0 -1
  114. package/dist/src/core/lib/glm/index.d.ts +0 -11
  115. package/dist/src/core/lib/glm/index.js +0 -30
  116. package/dist/src/core/lib/glm/index.js.map +0 -1
  117. package/dist/src/core/lib/glm/mat2.d.ts +0 -219
  118. package/dist/src/core/lib/glm/mat2.js +0 -396
  119. package/dist/src/core/lib/glm/mat2.js.map +0 -1
  120. package/dist/src/core/lib/glm/mat2d.d.ts +0 -237
  121. package/dist/src/core/lib/glm/mat2d.js +0 -442
  122. package/dist/src/core/lib/glm/mat2d.js.map +0 -1
  123. package/dist/src/core/lib/glm/mat3.d.ts +0 -283
  124. package/dist/src/core/lib/glm/mat3.js +0 -680
  125. package/dist/src/core/lib/glm/mat3.js.map +0 -1
  126. package/dist/src/core/lib/glm/mat4.d.ts +0 -550
  127. package/dist/src/core/lib/glm/mat4.js +0 -1802
  128. package/dist/src/core/lib/glm/mat4.js.map +0 -1
  129. package/dist/src/core/lib/glm/quat.d.ts +0 -363
  130. package/dist/src/core/lib/glm/quat.js +0 -693
  131. package/dist/src/core/lib/glm/quat.js.map +0 -1
  132. package/dist/src/core/lib/glm/quat2.d.ts +0 -356
  133. package/dist/src/core/lib/glm/quat2.js +0 -754
  134. package/dist/src/core/lib/glm/quat2.js.map +0 -1
  135. package/dist/src/core/lib/glm/vec2.d.ts +0 -365
  136. package/dist/src/core/lib/glm/vec2.js +0 -569
  137. package/dist/src/core/lib/glm/vec2.js.map +0 -1
  138. package/dist/src/core/lib/glm/vec3.d.ts +0 -406
  139. package/dist/src/core/lib/glm/vec3.js +0 -720
  140. package/dist/src/core/lib/glm/vec3.js.map +0 -1
  141. package/dist/src/core/lib/glm/vec4.d.ts +0 -330
  142. package/dist/src/core/lib/glm/vec4.js +0 -608
  143. package/dist/src/core/lib/glm/vec4.js.map +0 -1
  144. package/dist/src/core/renderers/CoreShaderManager.d.ts +0 -19
  145. package/dist/src/core/renderers/CoreShaderManager.js +0 -33
  146. package/dist/src/core/renderers/CoreShaderManager.js.map +0 -1
  147. package/dist/src/core/renderers/webgl/WebGlCoreShaderManager.d.ts +0 -27
  148. package/dist/src/core/renderers/webgl/WebGlCoreShaderManager.js +0 -82
  149. package/dist/src/core/renderers/webgl/WebGlCoreShaderManager.js.map +0 -1
  150. package/dist/src/core/renderers/webgl/WebGlCoreShaderProgram.d.ts +0 -11
  151. package/dist/src/core/renderers/webgl/WebGlCoreShaderProgram.js +0 -34
  152. package/dist/src/core/renderers/webgl/WebGlCoreShaderProgram.js.map +0 -1
  153. package/dist/src/core/scene/Scene.d.ts +0 -59
  154. package/dist/src/core/scene/Scene.js +0 -106
  155. package/dist/src/core/scene/Scene.js.map +0 -1
  156. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.d.ts +0 -20
  157. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.js +0 -55
  158. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.js.map +0 -1
  159. package/dist/src/main-api/IRenderDriver.d.ts +0 -20
  160. package/dist/src/main-api/IRenderDriver.js +0 -20
  161. package/dist/src/main-api/IRenderDriver.js.map +0 -1
  162. package/dist/src/main-api/IShaderController.d.ts +0 -14
  163. package/dist/src/main-api/IShaderController.js +0 -30
  164. package/dist/src/main-api/IShaderController.js.map +0 -1
  165. package/dist/src/main-api/IShaderNode.d.ts +0 -17
  166. package/dist/src/main-api/IShaderNode.js +0 -19
  167. package/dist/src/main-api/IShaderNode.js.map +0 -1
  168. package/dist/src/render-drivers/main/MainOnlyShaderController.d.ts +0 -6
  169. package/dist/src/render-drivers/main/MainOnlyShaderController.js +0 -15
  170. package/dist/src/render-drivers/main/MainOnlyShaderController.js.map +0 -1
  171. package/dist/src/render-drivers/main/MainOnlyShaderNode.d.ts +0 -7
  172. package/dist/src/render-drivers/main/MainOnlyShaderNode.js +0 -34
  173. package/dist/src/render-drivers/main/MainOnlyShaderNode.js.map +0 -1
  174. package/dist/src/render-drivers/main/MainRenderDriver.d.ts +0 -17
  175. package/dist/src/render-drivers/main/MainRenderDriver.js +0 -88
  176. package/dist/src/render-drivers/main/MainRenderDriver.js.map +0 -1
  177. package/dist/src/render-drivers/threadx/ThreadXMainShaderController.d.ts +0 -6
  178. package/dist/src/render-drivers/threadx/ThreadXMainShaderController.js +0 -16
  179. package/dist/src/render-drivers/threadx/ThreadXMainShaderController.js.map +0 -1
  180. package/dist/src/render-drivers/threadx/ThreadXMainShaderNode.d.ts +0 -7
  181. package/dist/src/render-drivers/threadx/ThreadXMainShaderNode.js +0 -15
  182. package/dist/src/render-drivers/threadx/ThreadXMainShaderNode.js.map +0 -1
  183. package/dist/src/render-drivers/threadx/ThreadXRenderDriver.d.ts +0 -21
  184. package/dist/src/render-drivers/threadx/ThreadXRenderDriver.js +0 -198
  185. package/dist/src/render-drivers/threadx/ThreadXRenderDriver.js.map +0 -1
@@ -28,6 +28,10 @@ import { TextureType, type Texture } from './textures/Texture.js';
28
28
  import { EventEmitter } from '../common/EventEmitter.js';
29
29
  import { getTimeStamp } from './platform.js';
30
30
  import type { Stage } from './Stage.js';
31
+ import {
32
+ validateCreateImageBitmap,
33
+ type CreateImageBitmapSupport,
34
+ } from './lib/validateImageBitmap.js';
31
35
 
32
36
  /**
33
37
  * Augmentable map of texture class types
@@ -45,12 +49,6 @@ export interface TextureMap {
45
49
  RenderTexture: typeof RenderTexture;
46
50
  }
47
51
 
48
- export interface CreateImageBitmapSupport {
49
- basic: boolean; // Supports createImageBitmap(image)
50
- options: boolean; // Supports createImageBitmap(image, options)
51
- full: boolean; // Supports createImageBitmap(image, sx, sy, sw, sh, options)
52
- }
53
-
54
52
  export type ExtractProps<Type> = Type extends { z$__type__Props: infer Props }
55
53
  ? Props
56
54
  : never;
@@ -63,6 +61,11 @@ export interface TextureManagerDebugInfo {
63
61
  keyCacheSize: number;
64
62
  }
65
63
 
64
+ export interface TextureManagerSettings {
65
+ numImageWorkers: number;
66
+ createImageBitmapSupport: 'auto' | 'basic' | 'options' | 'full';
67
+ }
68
+
66
69
  export type ResizeModeOptions =
67
70
  | {
68
71
  /**
@@ -180,6 +183,7 @@ export class CoreTextureManager extends EventEmitter {
180
183
  private uploadTextureQueue: Array<Texture> = [];
181
184
  private initialized = false;
182
185
  private stage: Stage;
186
+ private numImageWorkers: number;
183
187
 
184
188
  imageWorkerManager: ImageWorkerManager | null = null;
185
189
  hasCreateImageBitmap = !!self.createImageBitmap;
@@ -210,48 +214,34 @@ export class CoreTextureManager extends EventEmitter {
210
214
  */
211
215
  frameTime = 0;
212
216
 
213
- constructor(stage: Stage, numImageWorkers: number) {
217
+ constructor(stage: Stage, settings: TextureManagerSettings) {
214
218
  super();
219
+
220
+ const { numImageWorkers, createImageBitmapSupport } = settings;
215
221
  this.stage = stage;
216
- this.validateCreateImageBitmap()
217
- .then((result) => {
218
- this.hasCreateImageBitmap =
219
- result.basic || result.options || result.full;
220
- this.imageBitmapSupported = result;
222
+ this.numImageWorkers = numImageWorkers;
221
223
 
222
- if (!this.hasCreateImageBitmap) {
224
+ if (createImageBitmapSupport === 'auto') {
225
+ validateCreateImageBitmap()
226
+ .then((result) => {
227
+ this.initialize(result);
228
+ })
229
+ .catch((e) => {
223
230
  console.warn(
224
231
  '[Lightning] createImageBitmap is not supported on this browser. ImageTexture will be slower.',
225
232
  );
226
- }
227
233
 
228
- if (
229
- this.hasCreateImageBitmap &&
230
- this.hasWorker &&
231
- numImageWorkers > 0
232
- ) {
233
- this.imageWorkerManager = new ImageWorkerManager(
234
- numImageWorkers,
235
- result,
236
- );
237
- } else {
238
- console.warn(
239
- '[Lightning] Imageworker is 0 or not supported on this browser. Image loading will be slower.',
240
- );
241
- }
242
-
243
- this.initialized = true;
244
- this.emit('initialized');
245
- })
246
- .catch((e) => {
247
- console.warn(
248
- '[Lightning] createImageBitmap is not supported on this browser. ImageTexture will be slower.',
249
- );
250
-
251
- // initialized without image worker manager and createImageBitmap
252
- this.initialized = true;
253
- this.emit('initialized');
234
+ // initialized without image worker manager and createImageBitmap
235
+ this.initialized = true;
236
+ this.emit('initialized');
237
+ });
238
+ } else {
239
+ this.initialize({
240
+ basic: createImageBitmapSupport === 'basic',
241
+ options: createImageBitmapSupport === 'options',
242
+ full: createImageBitmapSupport === 'full',
254
243
  });
244
+ }
255
245
 
256
246
  this.registerTextureType('ImageTexture', ImageTexture);
257
247
  this.registerTextureType('ColorTexture', ColorTexture);
@@ -260,77 +250,6 @@ export class CoreTextureManager extends EventEmitter {
260
250
  this.registerTextureType('RenderTexture', RenderTexture);
261
251
  }
262
252
 
263
- private async validateCreateImageBitmap(): Promise<CreateImageBitmapSupport> {
264
- // Test if createImageBitmap is supported using a simple 1x1 PNG image
265
- // prettier-ignore
266
- const pngBinaryData = new Uint8Array([
267
- 0x89, 0x50, 0x4e, 0x47,
268
- 0x0d, 0x0a, 0x1a, 0x0a, // PNG signature
269
- 0x00, 0x00, 0x00, 0x0d, // IHDR chunk length
270
- 0x49, 0x48, 0x44, 0x52, // "IHDR" chunk type
271
- 0x00, 0x00, 0x00, 0x01, // Width: 1
272
- 0x00, 0x00, 0x00, 0x01, // Height: 1
273
- 0x01, // Bit depth: 1
274
- 0x03, // Color type: Indexed
275
- 0x00, // Compression method: Deflate
276
- 0x00, // Filter method: None
277
- 0x00, // Interlace method: None
278
- 0x25, 0xdb, 0x56, 0xca, // CRC for IHDR
279
- 0x00, 0x00, 0x00, 0x03, // PLTE chunk length
280
- 0x50, 0x4c, 0x54, 0x45, // "PLTE" chunk type
281
- 0x00, 0x00, 0x00, // Palette entry: Black
282
- 0xa7, 0x7a, 0x3d, 0xda, // CRC for PLTE
283
- 0x00, 0x00, 0x00, 0x01, // tRNS chunk length
284
- 0x74, 0x52, 0x4e, 0x53, // "tRNS" chunk type
285
- 0x00, // Transparency for black: Fully transparent
286
- 0x40, 0xe6, 0xd8, 0x66, // CRC for tRNS
287
- 0x00, 0x00, 0x00, 0x0a, // IDAT chunk length
288
- 0x49, 0x44, 0x41, 0x54, // "IDAT" chunk type
289
- 0x08, 0xd7, // Deflate header
290
- 0x63, 0x60, 0x00, 0x00,
291
- 0x00, 0x02, 0x00, 0x01, // Zlib-compressed data
292
- 0xe2, 0x21, 0xbc, 0x33, // CRC for IDAT
293
- 0x00, 0x00, 0x00, 0x00, // IEND chunk length
294
- 0x49, 0x45, 0x4e, 0x44, // "IEND" chunk type
295
- 0xae, 0x42, 0x60, 0x82, // CRC for IEND
296
- ]);
297
-
298
- const support: CreateImageBitmapSupport = {
299
- basic: false,
300
- options: false,
301
- full: false,
302
- };
303
-
304
- // Test basic createImageBitmap support
305
- const blob = new Blob([pngBinaryData], { type: 'image/png' });
306
- const bitmap = await createImageBitmap(blob);
307
- bitmap.close?.();
308
- support.basic = true;
309
-
310
- // Test createImageBitmap with options support
311
- try {
312
- const options = { premultiplyAlpha: 'none' as const };
313
- const bitmapWithOptions = await createImageBitmap(blob, options);
314
- bitmapWithOptions.close?.();
315
- support.options = true;
316
- } catch (e) {
317
- /* ignore */
318
- }
319
-
320
- // Test createImageBitmap with full options support
321
- try {
322
- const bitmapWithFullOptions = await createImageBitmap(blob, 0, 0, 1, 1, {
323
- premultiplyAlpha: 'none',
324
- });
325
- bitmapWithFullOptions.close?.();
326
- support.full = true;
327
- } catch (e) {
328
- /* ignore */
329
- }
330
-
331
- return support;
332
- }
333
-
334
253
  registerTextureType<Type extends keyof TextureMap>(
335
254
  textureType: Type,
336
255
  textureClass: TextureMap[Type],
@@ -338,6 +257,36 @@ export class CoreTextureManager extends EventEmitter {
338
257
  this.txConstructors[textureType] = textureClass;
339
258
  }
340
259
 
260
+ private initialize(support: CreateImageBitmapSupport) {
261
+ this.hasCreateImageBitmap =
262
+ support.basic || support.options || support.full;
263
+ this.imageBitmapSupported = support;
264
+
265
+ if (!this.hasCreateImageBitmap) {
266
+ console.warn(
267
+ '[Lightning] createImageBitmap is not supported on this browser. ImageTexture will be slower.',
268
+ );
269
+ }
270
+
271
+ if (
272
+ this.hasCreateImageBitmap &&
273
+ this.hasWorker &&
274
+ this.numImageWorkers > 0
275
+ ) {
276
+ this.imageWorkerManager = new ImageWorkerManager(
277
+ this.numImageWorkers,
278
+ support,
279
+ );
280
+ } else {
281
+ console.warn(
282
+ '[Lightning] Imageworker is 0 or not supported on this browser. Image loading will be slower.',
283
+ );
284
+ }
285
+
286
+ this.initialized = true;
287
+ this.emit('initialized');
288
+ }
289
+
341
290
  /**
342
291
  * Enqueue a texture for downloading its source image.
343
292
  */
@@ -392,15 +341,7 @@ export class CoreTextureManager extends EventEmitter {
392
341
 
393
342
  orphanTexture(texture: Texture): void {
394
343
  // if it is part of the download or upload queue, remove it
395
- const downloadIndex = this.downloadTextureSourceQueue.indexOf(texture);
396
- if (downloadIndex !== -1) {
397
- this.downloadTextureSourceQueue.splice(downloadIndex, 1);
398
- }
399
-
400
- const uploadIndex = this.uploadTextureQueue.indexOf(texture);
401
- if (uploadIndex !== -1) {
402
- this.uploadTextureQueue.splice(uploadIndex, 1);
403
- }
344
+ this.removeTextureFromQueue(texture);
404
345
 
405
346
  if (texture.type === TextureType.subTexture) {
406
347
  // ignore subtextures
@@ -419,11 +360,42 @@ export class CoreTextureManager extends EventEmitter {
419
360
  loadTexture(texture: Texture, priority?: boolean): void {
420
361
  this.stage.txMemManager.removeFromOrphanedTextures(texture);
421
362
 
422
- if (texture.state !== 'initial' && texture.state !== 'freed') {
363
+ if (texture.type === TextureType.subTexture) {
364
+ // ignore subtextures - they get loaded through their parent
423
365
  return;
424
366
  }
425
367
 
426
- texture.setState('loading');
368
+ // if the texture is already loaded, don't load it again
369
+ if (
370
+ texture.ctxTexture !== undefined &&
371
+ texture.ctxTexture.state === 'loaded'
372
+ ) {
373
+ texture.setState('loaded');
374
+ return;
375
+ }
376
+
377
+ // if the texture is already being processed, don't load it again
378
+ if (
379
+ this.downloadTextureSourceQueue.includes(texture) === true ||
380
+ this.uploadTextureQueue.includes(texture) === true
381
+ ) {
382
+ return;
383
+ }
384
+
385
+ // if the texture is already loading, free it, this can happen if the texture is
386
+ // orphaned and then reloaded
387
+ if (
388
+ texture.ctxTexture !== undefined &&
389
+ texture.ctxTexture.state === 'loading'
390
+ ) {
391
+ // if the texture has texture data, queue it for upload
392
+ if (texture.textureData !== null) {
393
+ this.enqueueUploadTexture(texture);
394
+ }
395
+
396
+ // else we will have to re-download the texture
397
+ texture.free();
398
+ }
427
399
 
428
400
  // if we're not initialized, just queue the texture into the priority queue
429
401
  if (this.initialized === false) {
@@ -435,15 +407,17 @@ export class CoreTextureManager extends EventEmitter {
435
407
  // Technically the noise texture shouldn't either, but it's a special case
436
408
  // and not really used in production so who cares ¯\_(ツ)_/¯
437
409
  if (
438
- texture.type === TextureType.subTexture ||
439
- texture.type === TextureType.color ||
440
- texture.type === TextureType.renderToTexture
410
+ (texture.type === TextureType.color ||
411
+ texture.type === TextureType.renderToTexture) &&
412
+ texture.state !== 'initial'
441
413
  ) {
442
414
  texture.setState('fetched');
443
415
  this.enqueueUploadTexture(texture);
444
416
  return;
445
417
  }
446
418
 
419
+ texture.setState('loading');
420
+
447
421
  // prioritize the texture for immediate loading
448
422
  if (priority === true) {
449
423
  texture
@@ -466,11 +440,21 @@ export class CoreTextureManager extends EventEmitter {
466
440
  * @param texture Texture to upload
467
441
  */
468
442
  uploadTexture(texture: Texture): void {
469
- if (texture.state !== 'fetched') {
443
+ if (
444
+ this.stage.txMemManager.doNotExceedCriticalThreshold === true &&
445
+ this.stage.txMemManager.criticalCleanupRequested === true
446
+ ) {
447
+ // we're at a critical memory threshold, don't upload textures
448
+ this.enqueueUploadTexture(texture);
470
449
  return;
471
450
  }
472
451
 
473
452
  const coreContext = texture.loadCtxTexture();
453
+ if (coreContext !== null && coreContext.state === 'loaded') {
454
+ texture.setState('loaded');
455
+ return;
456
+ }
457
+
474
458
  coreContext.load();
475
459
  }
476
460
 
@@ -576,6 +560,23 @@ export class CoreTextureManager extends EventEmitter {
576
560
  }
577
561
  }
578
562
 
563
+ /**
564
+ * Remove texture from the queue's
565
+ *
566
+ * @param texture - The texture to remove
567
+ */
568
+ removeTextureFromQueue(texture: Texture): void {
569
+ const downloadIndex = this.downloadTextureSourceQueue.indexOf(texture);
570
+ if (downloadIndex !== -1) {
571
+ this.downloadTextureSourceQueue.splice(downloadIndex, 1);
572
+ }
573
+
574
+ const uploadIndex = this.uploadTextureQueue.indexOf(texture);
575
+ if (uploadIndex !== -1) {
576
+ this.uploadTextureQueue.splice(uploadIndex, 1);
577
+ }
578
+ }
579
+
579
580
  /**
580
581
  * Resolve a parent texture from the cache or fallback to the provided texture.
581
582
  *
package/src/core/Stage.ts CHANGED
@@ -20,6 +20,7 @@ import { startLoop, getTimeStamp } from './platform.js';
20
20
  import { assertTruthy, setPremultiplyMode } from '../utils.js';
21
21
  import { AnimationManager } from './animations/AnimationManager.js';
22
22
  import {
23
+ UpdateType,
23
24
  CoreNode,
24
25
  CoreNodeRenderState,
25
26
  type CoreNodeProps,
@@ -77,6 +78,7 @@ export interface StageOptions {
77
78
  inspector: boolean;
78
79
  strictBounds: boolean;
79
80
  textureProcessingTimeLimit: number;
81
+ createImageBitmapSupport: 'auto' | 'basic' | 'options' | 'full';
80
82
  }
81
83
 
82
84
  export type StageFpsUpdateHandler = (
@@ -102,7 +104,7 @@ export class Stage {
102
104
  public readonly shManager: CoreShaderManager;
103
105
  public readonly renderer: CoreRenderer;
104
106
  public readonly root: CoreNode;
105
- public readonly boundsMargin: [number, number, number, number];
107
+ public boundsMargin: [number, number, number, number];
106
108
  public readonly defShaderCtr: BaseShaderController;
107
109
  public readonly strictBound: Bound;
108
110
  public readonly preloadBound: Bound;
@@ -150,10 +152,14 @@ export class Stage {
150
152
  textureMemory,
151
153
  renderEngine,
152
154
  fontEngines,
155
+ createImageBitmapSupport,
153
156
  } = options;
154
157
 
155
158
  this.eventBus = options.eventBus;
156
- this.txManager = new CoreTextureManager(this, numImageWorkers);
159
+ this.txManager = new CoreTextureManager(this, {
160
+ numImageWorkers,
161
+ createImageBitmapSupport,
162
+ });
157
163
 
158
164
  // Wait for the Texture Manager to initialize
159
165
  // once it does, request a render
@@ -240,6 +246,7 @@ export class Stage {
240
246
  height: appHeight,
241
247
  alpha: 1,
242
248
  autosize: false,
249
+ boundsMargin: null,
243
250
  clipping: false,
244
251
  color: 0x00000000,
245
252
  colorTop: 0x00000000,
@@ -307,6 +314,7 @@ export class Stage {
307
314
  * Create default PixelTexture
308
315
  */
309
316
  createDefaultTexture() {
317
+ console.log('Creating default texture');
310
318
  (this.defaultTexture as ColorTexture) = this.txManager.createTexture(
311
319
  'ColorTexture',
312
320
  {
@@ -315,7 +323,6 @@ export class Stage {
315
323
  );
316
324
 
317
325
  assertTruthy(this.defaultTexture instanceof ColorTexture);
318
-
319
326
  this.txManager.loadTexture(this.defaultTexture, true);
320
327
 
321
328
  // Mark the default texture as ALWAYS renderable
@@ -372,8 +379,13 @@ export class Stage {
372
379
  renderer.reset();
373
380
 
374
381
  // Check if we need to cleanup textures
375
- if (this.txMemManager.criticalCleanupRequested) {
376
- this.txMemManager.cleanup();
382
+ if (this.txMemManager.criticalCleanupRequested === true) {
383
+ this.txMemManager.cleanup(false);
384
+
385
+ if (this.txMemManager.criticalCleanupRequested === true) {
386
+ // If we still need to cleanup, request another but aggressive cleanup
387
+ this.txMemManager.cleanup(true);
388
+ }
377
389
  }
378
390
 
379
391
  // If we have RTT nodes draw them first
@@ -636,6 +648,14 @@ export class Stage {
636
648
  return new CoreTextNode(this, resolvedProps, resolvedTextRenderer);
637
649
  }
638
650
 
651
+ setBoundsMargin(value: number | [number, number, number, number]) {
652
+ this.boundsMargin = Array.isArray(value)
653
+ ? value
654
+ : [value, value, value, value];
655
+
656
+ this.root.setUpdateType(UpdateType.RenderBounds);
657
+ }
658
+
639
659
  /**
640
660
  * Resolves the default property values for a Node
641
661
  *
@@ -669,6 +689,7 @@ export class Stage {
669
689
  height: props.height ?? 0,
670
690
  alpha: props.alpha ?? 1,
671
691
  autosize: props.autosize ?? false,
692
+ boundsMargin: props.boundsMargin ?? null,
672
693
  clipping: props.clipping ?? false,
673
694
  color,
674
695
  colorTop: props.colorTop ?? color,
@@ -716,7 +737,7 @@ export class Stage {
716
737
  * @remarks
717
738
  * This method is used to cleanup orphaned textures that are no longer in use.
718
739
  */
719
- cleanup() {
720
- this.txMemManager.cleanup();
740
+ cleanup(aggressive: boolean) {
741
+ this.txMemManager.cleanup(aggressive);
721
742
  }
722
743
  }
@@ -19,7 +19,7 @@
19
19
  import { isProductionEnvironment } from '../utils.js';
20
20
  import { getTimeStamp } from './platform.js';
21
21
  import type { Stage } from './Stage.js';
22
- import type { Texture } from './textures/Texture.js';
22
+ import { TextureType, type Texture } from './textures/Texture.js';
23
23
  import { bytesToMb } from './utils.js';
24
24
 
25
25
  export interface TextureMemoryManagerSettings {
@@ -53,13 +53,13 @@ export interface TextureMemoryManagerSettings {
53
53
  targetThresholdLevel: number;
54
54
 
55
55
  /**
56
- * Interval between Texture Cleanups (in milliseconds)
56
+ * Interval between non-aggressive Texture Cleanups (in milliseconds)
57
57
  *
58
58
  * @remarks
59
- * Texture Memory Manager will perform a Texture Cleanup no more
60
- * frequently than this interval generally when the scene becomes idle.
59
+ * Texture Memory Manager will perform a non aggressive Texture Cleanup no more
60
+ * frequently than this interval when the scene becomes idle.
61
61
  *
62
- * @defaultValue `30,000` (30 seconds)
62
+ * @defaultValue `5,000` (5 seconds)
63
63
  */
64
64
  cleanupInterval: number;
65
65
 
@@ -80,6 +80,13 @@ export interface TextureMemoryManagerSettings {
80
80
  * @defaultValue `25e6` (25 MB)
81
81
  */
82
82
  baselineMemoryAllocation: number;
83
+
84
+ /**
85
+ * Do not exceed critical threshold
86
+ *
87
+ * @defaultValue `false`
88
+ */
89
+ doNotExceedCriticalThreshold: boolean;
83
90
  }
84
91
 
85
92
  export interface MemoryInfo {
@@ -117,7 +124,10 @@ export class TextureMemoryManager {
117
124
  private debugLogging: boolean;
118
125
  private lastCleanupTime = 0;
119
126
  private baselineMemoryAllocation: number;
127
+
120
128
  public criticalCleanupRequested = false;
129
+ public doNotExceedCriticalThreshold: boolean;
130
+
121
131
  /**
122
132
  * The current frame time in milliseconds
123
133
  *
@@ -129,18 +139,22 @@ export class TextureMemoryManager {
129
139
  public frameTime = 0;
130
140
 
131
141
  constructor(private stage: Stage, settings: TextureMemoryManagerSettings) {
132
- const { criticalThreshold } = settings;
142
+ const { criticalThreshold, doNotExceedCriticalThreshold } = settings;
143
+ this.doNotExceedCriticalThreshold = doNotExceedCriticalThreshold || false;
133
144
  this.criticalThreshold = Math.round(criticalThreshold);
134
145
  const targetFraction = Math.max(
135
146
  0,
136
147
  Math.min(1, settings.targetThresholdLevel),
137
148
  );
138
- this.targetThreshold = Math.round(criticalThreshold * targetFraction);
139
149
  this.cleanupInterval = settings.cleanupInterval;
140
150
  this.debugLogging = settings.debugLogging;
141
151
  this.baselineMemoryAllocation = Math.round(
142
152
  settings.baselineMemoryAllocation,
143
153
  );
154
+ this.targetThreshold = Math.max(
155
+ Math.round(criticalThreshold * targetFraction),
156
+ this.baselineMemoryAllocation,
157
+ );
144
158
  this.memUsed = Math.round(settings.baselineMemoryAllocation);
145
159
 
146
160
  if (settings.debugLogging) {
@@ -229,24 +243,11 @@ export class TextureMemoryManager {
229
243
  );
230
244
  }
231
245
 
232
- cleanup() {
233
- const critical = this.criticalCleanupRequested;
234
- this.lastCleanupTime = this.frameTime;
235
- this.criticalCleanupRequested = false;
236
-
237
- if (critical === true) {
238
- this.stage.queueFrameEvent('criticalCleanup', {
239
- memUsed: this.memUsed,
240
- criticalThreshold: this.criticalThreshold,
241
- });
242
- }
243
-
244
- if (this.debugLogging) {
245
- console.log(
246
- `[TextureMemoryManager] Cleaning up textures. Critical: ${critical}`,
247
- );
248
- }
246
+ checkCriticalCleanup() {
247
+ return this.memUsed > this.criticalThreshold;
248
+ }
249
249
 
250
+ cleanupQuick(critical: boolean) {
250
251
  // Free non-renderable textures until we reach the target threshold
251
252
  const memTarget = this.targetThreshold;
252
253
  const txManager = this.stage.txManager;
@@ -255,10 +256,13 @@ export class TextureMemoryManager {
255
256
  while (
256
257
  this.memUsed >= memTarget &&
257
258
  this.orphanedTextures.length > 0 &&
258
- // if it a non-critical cleanup, we will only cleanup for 10ms
259
259
  (critical || getTimeStamp() - timestamp < 10)
260
260
  ) {
261
- const texture = this.orphanedTextures.shift()!;
261
+ const texture = this.orphanedTextures.shift();
262
+
263
+ if (texture === undefined) {
264
+ continue;
265
+ }
262
266
 
263
267
  if (texture.renderable === true) {
264
268
  // If the texture is renderable, we can't free it up
@@ -268,6 +272,80 @@ export class TextureMemoryManager {
268
272
  texture.free();
269
273
  txManager.removeTextureFromCache(texture);
270
274
  }
275
+ }
276
+
277
+ cleanupDeep(critical: boolean) {
278
+ // Free non-renderable textures until we reach the target threshold
279
+ const memTarget = critical ? this.criticalThreshold : this.targetThreshold;
280
+ const txManager = this.stage.txManager;
281
+
282
+ // sort by renderability
283
+ const filteredAndSortedTextures: Texture[] = [];
284
+ const textures = [...this.loadedTextures.keys()];
285
+ for (let i = 0; i < textures.length; i++) {
286
+ const texture = textures[i];
287
+ if (texture === undefined) {
288
+ continue;
289
+ }
290
+
291
+ if (
292
+ texture.type === TextureType.image ||
293
+ texture.type === TextureType.noise ||
294
+ texture.type === TextureType.renderToTexture
295
+ ) {
296
+ if (texture.renderable === true) {
297
+ filteredAndSortedTextures.push(texture);
298
+ } else {
299
+ filteredAndSortedTextures.unshift(texture);
300
+ }
301
+ }
302
+ }
303
+
304
+ while (this.memUsed >= memTarget && filteredAndSortedTextures.length > 0) {
305
+ const texture = filteredAndSortedTextures.shift();
306
+ if (texture === undefined) {
307
+ continue;
308
+ }
309
+
310
+ if (texture.preventCleanup === true) {
311
+ continue;
312
+ }
313
+
314
+ if (texture.renderable === true) {
315
+ break;
316
+ }
317
+
318
+ texture.free();
319
+ this.removeFromOrphanedTextures(texture);
320
+ txManager.removeTextureFromCache(texture);
321
+ txManager.removeTextureFromQueue(texture);
322
+ }
323
+ }
324
+
325
+ cleanup(aggressive: boolean = false) {
326
+ const critical = this.criticalCleanupRequested;
327
+ this.lastCleanupTime = this.frameTime;
328
+
329
+ if (critical === true) {
330
+ this.stage.queueFrameEvent('criticalCleanup', {
331
+ memUsed: this.memUsed,
332
+ criticalThreshold: this.criticalThreshold,
333
+ });
334
+ }
335
+
336
+ if (this.debugLogging === true) {
337
+ console.log(
338
+ `[TextureMemoryManager] Cleaning up textures. Critical: ${critical}. Aggressive: ${aggressive}`,
339
+ );
340
+ }
341
+
342
+ // try a quick cleanup first
343
+ this.cleanupQuick(critical);
344
+
345
+ // if we're still above the target threshold, do a deep cleanup
346
+ if (aggressive === true && this.memUsed >= this.criticalThreshold) {
347
+ this.cleanupDeep(critical);
348
+ }
271
349
 
272
350
  if (this.memUsed >= this.criticalThreshold) {
273
351
  this.stage.queueFrameEvent('criticalCleanupFailed', {
@@ -280,6 +358,8 @@ export class TextureMemoryManager {
280
358
  `[TextureMemoryManager] Memory usage above critical threshold after cleanup: ${this.memUsed}`,
281
359
  );
282
360
  }
361
+ } else {
362
+ this.criticalCleanupRequested = false;
283
363
  }
284
364
  }
285
365
 
@@ -299,8 +379,9 @@ export class TextureMemoryManager {
299
379
  acc + (texture.renderable ? this.loadedTextures.get(texture)! : 0)
300
380
  );
301
381
  },
302
- 0,
382
+ this.baselineMemoryAllocation,
303
383
  );
384
+
304
385
  return {
305
386
  criticalThreshold: this.criticalThreshold,
306
387
  targetThreshold: this.targetThreshold,