@lightningjs/renderer 0.8.3 → 0.8.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 (133) hide show
  1. package/dist/src/core/CoreNode.d.ts +27 -1
  2. package/dist/src/core/CoreNode.js +130 -5
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/CoreShaderManager.js +3 -2
  5. package/dist/src/core/CoreShaderManager.js.map +1 -1
  6. package/dist/src/core/CoreTextNode.js +20 -1
  7. package/dist/src/core/CoreTextNode.js.map +1 -1
  8. package/dist/src/core/CoreTextureManager.d.ts +2 -0
  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.js +25 -9
  12. package/dist/src/core/Stage.js.map +1 -1
  13. package/dist/src/core/TextureMemoryManager.d.ts +1 -0
  14. package/dist/src/core/TextureMemoryManager.js +3 -1
  15. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  16. package/dist/src/core/lib/ImageWorker.d.ts +2 -1
  17. package/dist/src/core/lib/ImageWorker.js +10 -6
  18. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  19. package/dist/src/core/lib/WebGlContextWrapper.d.ts +26 -1
  20. package/dist/src/core/lib/WebGlContextWrapper.js +37 -1
  21. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  22. package/dist/src/core/renderers/CoreRenderer.d.ts +11 -0
  23. package/dist/src/core/renderers/CoreRenderer.js +1 -0
  24. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  25. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.d.ts +10 -4
  26. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js +18 -7
  27. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js.map +1 -1
  28. package/dist/src/core/renderers/canvas/CanvasCoreTexture.d.ts +2 -2
  29. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +8 -6
  30. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +1 -1
  31. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.d.ts +1 -1
  32. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js +1 -1
  33. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js.map +1 -1
  34. package/dist/src/core/renderers/canvas/internal/ColorUtils.js +4 -4
  35. package/dist/src/core/renderers/canvas/internal/ColorUtils.js.map +1 -1
  36. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.d.ts +1 -1
  37. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js +2 -2
  38. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js.map +1 -1
  39. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.d.ts +11 -0
  40. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js +51 -0
  41. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js.map +1 -0
  42. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +11 -1
  43. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +22 -11
  44. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  45. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.d.ts +4 -1
  46. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js +7 -2
  47. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js.map +1 -1
  48. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +16 -1
  49. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +119 -27
  50. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  51. package/dist/src/core/renderers/webgl/WebGlCoreShader.js +19 -4
  52. package/dist/src/core/renderers/webgl/WebGlCoreShader.js.map +1 -1
  53. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +4 -6
  54. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js.map +1 -1
  55. package/dist/src/core/renderers/webgl/shaders/SdfShader.js +6 -1
  56. package/dist/src/core/renderers/webgl/shaders/SdfShader.js.map +1 -1
  57. package/dist/src/core/text-rendering/TextRenderingUtils.d.ts +12 -0
  58. package/dist/src/core/text-rendering/TextRenderingUtils.js +14 -0
  59. package/dist/src/core/text-rendering/TextRenderingUtils.js.map +1 -0
  60. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +2 -1
  61. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +4 -3
  62. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  63. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +2 -1
  64. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  65. package/dist/src/core/textures/RenderTexture.d.ts +28 -0
  66. package/dist/src/core/textures/RenderTexture.js +52 -0
  67. package/dist/src/core/textures/RenderTexture.js.map +1 -0
  68. package/dist/src/core/utils.d.ts +6 -1
  69. package/dist/src/core/utils.js +74 -82
  70. package/dist/src/core/utils.js.map +1 -1
  71. package/dist/src/main-api/INode.d.ts +9 -0
  72. package/dist/src/main-api/RendererMain.js +2 -1
  73. package/dist/src/main-api/RendererMain.js.map +1 -1
  74. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +3 -0
  75. package/dist/src/render-drivers/main/MainOnlyNode.js +29 -0
  76. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  77. package/dist/src/render-drivers/main/MainOnlyTextNode.js +1 -0
  78. package/dist/src/render-drivers/main/MainOnlyTextNode.js.map +1 -1
  79. package/dist/src/render-drivers/threadx/NodeStruct.d.ts +3 -0
  80. package/dist/src/render-drivers/threadx/NodeStruct.js +9 -0
  81. package/dist/src/render-drivers/threadx/NodeStruct.js.map +1 -1
  82. package/dist/src/render-drivers/threadx/SharedNode.d.ts +1 -0
  83. package/dist/src/render-drivers/threadx/SharedNode.js +1 -0
  84. package/dist/src/render-drivers/threadx/SharedNode.js.map +1 -1
  85. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +2 -0
  86. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  87. package/dist/src/render-drivers/threadx/ThreadXMainNode.d.ts +3 -0
  88. package/dist/src/render-drivers/threadx/ThreadXMainNode.js +7 -0
  89. package/dist/src/render-drivers/threadx/ThreadXMainNode.js.map +1 -1
  90. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +1 -0
  91. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  92. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js +1 -0
  93. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js.map +1 -1
  94. package/dist/src/render-drivers/threadx/worker/renderer.js +1 -0
  95. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  96. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  97. package/package.json +1 -1
  98. package/src/core/CoreNode.ts +164 -8
  99. package/src/core/CoreShaderManager.ts +6 -3
  100. package/src/core/CoreTextNode.ts +25 -0
  101. package/src/core/CoreTextureManager.ts +3 -0
  102. package/src/core/Stage.ts +32 -11
  103. package/src/core/TextureMemoryManager.ts +3 -1
  104. package/src/core/lib/ImageWorker.ts +12 -7
  105. package/src/core/lib/WebGlContextWrapper.ts +51 -1
  106. package/src/core/renderers/CoreRenderer.ts +15 -1
  107. package/src/core/renderers/canvas/CanvasCoreRenderer.ts +59 -14
  108. package/src/core/renderers/canvas/CanvasCoreTexture.ts +24 -18
  109. package/src/core/renderers/canvas/internal/C2DShaderUtils.ts +6 -3
  110. package/src/core/renderers/canvas/internal/ColorUtils.ts +6 -6
  111. package/src/core/renderers/canvas/shaders/UnsupportedShader.ts +2 -3
  112. package/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts +79 -0
  113. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +26 -24
  114. package/src/core/renderers/webgl/WebGlCoreRenderOp.ts +4 -2
  115. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +166 -33
  116. package/src/core/renderers/webgl/WebGlCoreShader.ts +29 -4
  117. package/src/core/renderers/webgl/shaders/DefaultShader.ts +4 -6
  118. package/src/core/renderers/webgl/shaders/SdfShader.ts +6 -1
  119. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +8 -1
  120. package/src/core/text-rendering/renderers/TextRenderer.ts +3 -0
  121. package/src/core/textures/RenderTexture.ts +81 -0
  122. package/src/core/utils.ts +101 -93
  123. package/src/main-api/INode.ts +11 -0
  124. package/src/main-api/RendererMain.ts +2 -1
  125. package/src/render-drivers/main/MainOnlyNode.ts +44 -0
  126. package/src/render-drivers/main/MainOnlyTextNode.ts +1 -0
  127. package/src/render-drivers/threadx/NodeStruct.ts +10 -0
  128. package/src/render-drivers/threadx/SharedNode.ts +2 -0
  129. package/src/render-drivers/threadx/ThreadXCoreDriver.ts +2 -0
  130. package/src/render-drivers/threadx/ThreadXMainNode.ts +9 -0
  131. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +1 -0
  132. package/src/render-drivers/threadx/worker/ThreadXRendererTextNode.ts +1 -0
  133. package/src/render-drivers/threadx/worker/renderer.ts +1 -0
@@ -37,6 +37,7 @@ import type {
37
37
  TextureFreedEventHandler,
38
38
  TextureLoadedEventHandler,
39
39
  } from './textures/Texture.js';
40
+ import { RenderTexture } from './textures/RenderTexture.js';
40
41
  import type {
41
42
  Dimensions,
42
43
  NodeTextureFailedPayload,
@@ -104,6 +105,7 @@ export interface CoreNodeProps {
104
105
  pivotX: number;
105
106
  pivotY: number;
106
107
  rotation: number;
108
+ rtt: boolean;
107
109
  }
108
110
 
109
111
  type ICoreNode = Omit<
@@ -212,6 +214,16 @@ export enum UpdateType {
212
214
  */
213
215
  IsRenderable = 1024,
214
216
 
217
+ /**
218
+ * Render Texture update
219
+ */
220
+ RenderTexture = 2048,
221
+
222
+ /**
223
+ * Track if parent has render texture
224
+ */
225
+ ParentRenderTexture = 4096,
226
+
215
227
  /**
216
228
  * None
217
229
  */
@@ -220,7 +232,7 @@ export enum UpdateType {
220
232
  /**
221
233
  * All
222
234
  */
223
- All = 2047,
235
+ All = 8191,
224
236
  }
225
237
 
226
238
  export class CoreNode extends EventEmitter implements ICoreNode {
@@ -252,6 +264,8 @@ export class CoreNode extends EventEmitter implements ICoreNode {
252
264
  public premultipliedColorBl = 0;
253
265
  public premultipliedColorBr = 0;
254
266
  public calcZIndex = 0;
267
+ public hasRTTupdates = false;
268
+ public parentHasRenderTexture = false;
255
269
 
256
270
  constructor(protected stage: Stage, props: CoreNodeProps) {
257
271
  super();
@@ -261,6 +275,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
261
275
  };
262
276
  // Allow for parent to be processed appropriately
263
277
  this.parent = props.parent;
278
+
279
+ // Allow for Render Texture to be processed appropriately
280
+ this.rtt = props.rtt;
281
+
264
282
  this.updateScaleRotateTransform();
265
283
  }
266
284
 
@@ -324,6 +342,13 @@ export class CoreNode extends EventEmitter implements ICoreNode {
324
342
  // Texture was loaded. In case the RAF loop has already stopped, we request
325
343
  // a render to ensure the texture is rendered.
326
344
  this.stage.requestRender();
345
+
346
+ // If parent has a render texture, flag that we need to update
347
+ // @todo: Reserve type for RTT updates
348
+ if (this.parentHasRenderTexture) {
349
+ this.setRTTUpdates(1);
350
+ }
351
+
327
352
  this.emit('loaded', {
328
353
  type: 'texture',
329
354
  dimensions,
@@ -373,6 +398,11 @@ export class CoreNode extends EventEmitter implements ICoreNode {
373
398
  if (parent && !(parent.updateType & UpdateType.Children)) {
374
399
  parent.setUpdateType(UpdateType.Children);
375
400
  }
401
+ // If node is part of RTT texture
402
+ // Flag that we need to update
403
+ if (this.parentHasRenderTexture) {
404
+ this.setRTTUpdates(type);
405
+ }
376
406
  }
377
407
 
378
408
  sortChildren() {
@@ -421,16 +451,43 @@ export class CoreNode extends EventEmitter implements ICoreNode {
421
451
 
422
452
  const parent = this.props.parent;
423
453
  let childUpdateType = UpdateType.None;
454
+
455
+ if (this.updateType & UpdateType.ParentRenderTexture) {
456
+ let p = this.parent;
457
+ while (p) {
458
+ if (p.rtt) {
459
+ this.parentHasRenderTexture = true;
460
+ }
461
+ p = p.parent;
462
+ }
463
+ }
464
+
465
+ // If we have render texture updates and not already running a full update
466
+ if (
467
+ this.updateType ^ UpdateType.All &&
468
+ this.updateType & UpdateType.RenderTexture
469
+ ) {
470
+ this.children.forEach((child) => {
471
+ child.setUpdateType(UpdateType.All);
472
+ });
473
+ }
474
+
424
475
  if (this.updateType & UpdateType.Global) {
425
476
  assertTruthy(this.localTransform);
477
+
426
478
  this.globalTransform = Matrix3d.copy(
427
479
  parent?.globalTransform || this.localTransform,
428
480
  this.globalTransform,
429
481
  );
430
482
 
483
+ if (this.parentHasRenderTexture && this.props.parent?.rtt) {
484
+ this.globalTransform = Matrix3d.identity();
485
+ }
486
+
431
487
  if (parent) {
432
488
  this.globalTransform.multiply(this.localTransform);
433
489
  }
490
+
434
491
  this.calculateRenderCoords();
435
492
  this.updateBoundingRect();
436
493
  this.setUpdateType(
@@ -511,7 +568,11 @@ export class CoreNode extends EventEmitter implements ICoreNode {
511
568
  parent.setUpdateType(UpdateType.ZIndexSortedChildren);
512
569
  }
513
570
 
514
- if (this.updateType & UpdateType.Children && this.children.length) {
571
+ if (
572
+ this.updateType & UpdateType.Children &&
573
+ this.children.length &&
574
+ !this.rtt
575
+ ) {
515
576
  this.children.forEach((child) => {
516
577
  // Trigger the depenedent update types on the child
517
578
  child.setUpdateType(childUpdateType);
@@ -755,7 +816,6 @@ export class CoreNode extends EventEmitter implements ICoreNode {
755
816
  );
756
817
  }
757
818
  }
758
-
759
819
  /**
760
820
  * This function calculates the clipping rectangle for a node.
761
821
  *
@@ -824,13 +884,30 @@ export class CoreNode extends EventEmitter implements ICoreNode {
824
884
  this.props.texture = null;
825
885
  this.props.shader = null;
826
886
 
887
+ if (this.rtt) {
888
+ this.stage.renderer.removeRTTNode(this);
889
+ }
890
+
827
891
  this.removeAllListeners();
828
892
  this.parent = null;
829
893
  }
830
894
 
831
895
  renderQuads(renderer: CoreRenderer): void {
832
- const { width, height, texture, textureOptions, shader, shaderProps } =
896
+ const { width, height, texture, textureOptions, shader, shaderProps, rtt } =
833
897
  this.props;
898
+
899
+ // Prevent quad rendering if parent has a render texture
900
+ // and renderer is not currently rendering to a texture
901
+ if (this.parentHasRenderTexture) {
902
+ if (!renderer.renderToTextureActive) {
903
+ return;
904
+ }
905
+ // Prevent quad rendering if parent render texture is not the active render texture
906
+ if (this.parentRenderTexture !== renderer.activeRttNode) {
907
+ return;
908
+ }
909
+ }
910
+
834
911
  const {
835
912
  premultipliedColorTl,
836
913
  premultipliedColorTr,
@@ -863,10 +940,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
863
940
  tb: gt.tb,
864
941
  tc: gt.tc,
865
942
  td: gt.td,
943
+ rtt,
944
+ parentHasRenderTexture: this.parentHasRenderTexture,
945
+ framebufferDimensions: this.framebufferDimensions,
866
946
  });
867
-
868
- // Calculate absolute X and Y based on all ancestors
869
- // renderer.addQuad(absX, absY, w, h, color, texture, textureOptions, zIndex);
870
947
  }
871
948
 
872
949
  //#region Properties
@@ -915,6 +992,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
915
992
  if (this.props.width !== value) {
916
993
  this.props.width = value;
917
994
  this.setUpdateType(UpdateType.Local);
995
+
996
+ if (this.props.rtt) {
997
+ this.setUpdateType(UpdateType.RenderTexture);
998
+ }
918
999
  }
919
1000
  }
920
1001
 
@@ -926,6 +1007,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
926
1007
  if (this.props.height !== value) {
927
1008
  this.props.height = value;
928
1009
  this.setUpdateType(UpdateType.Local);
1010
+
1011
+ if (this.props.rtt) {
1012
+ this.setUpdateType(UpdateType.RenderTexture);
1013
+ }
929
1014
  }
930
1015
  }
931
1016
 
@@ -1235,9 +1320,80 @@ export class CoreNode extends EventEmitter implements ICoreNode {
1235
1320
  newParent.setUpdateType(
1236
1321
  UpdateType.Children | UpdateType.ZIndexSortedChildren,
1237
1322
  );
1238
- }
1239
1323
 
1324
+ if (newParent.rtt || newParent.parentHasRenderTexture) {
1325
+ this.setRTTUpdates(UpdateType.All);
1326
+ }
1327
+ }
1240
1328
  this.updateScaleRotateTransform();
1241
1329
  }
1330
+
1331
+ get rtt(): boolean {
1332
+ return this.props.rtt;
1333
+ }
1334
+
1335
+ set rtt(value: boolean) {
1336
+ if (!value) {
1337
+ if (this.props.rtt) {
1338
+ this.props.rtt = false;
1339
+ this.unloadTexture();
1340
+ this.setUpdateType(UpdateType.All);
1341
+
1342
+ this.children.forEach((child) => {
1343
+ child.parentHasRenderTexture = false;
1344
+ });
1345
+
1346
+ this.stage.renderer?.removeRTTNode(this);
1347
+ }
1348
+ return;
1349
+ }
1350
+
1351
+ this.props.rtt = true;
1352
+ this.hasRTTupdates = true;
1353
+ this.setUpdateType(UpdateType.All);
1354
+
1355
+ this.children.forEach((child) => {
1356
+ child.setUpdateType(UpdateType.All);
1357
+ });
1358
+
1359
+ // Store RTT nodes in a separate list
1360
+ this.stage.renderer?.renderToTexture(this);
1361
+ }
1362
+
1363
+ /**
1364
+ * Returns the framebuffer dimensions of the node.
1365
+ * If the node has a render texture, the dimensions are the same as the node's dimensions.
1366
+ * If the node does not have a render texture, the dimensions are inherited from the parent.
1367
+ * If the node parent has a render texture and the node is a render texture, the nodes dimensions are used.
1368
+ */
1369
+ get framebufferDimensions(): Dimensions {
1370
+ if (this.parentHasRenderTexture && !this.rtt && this.parent) {
1371
+ return this.parent.framebufferDimensions;
1372
+ }
1373
+ return { width: this.width, height: this.height };
1374
+ }
1375
+
1376
+ /**
1377
+ * Returns the parent render texture node if it exists.
1378
+ */
1379
+ get parentRenderTexture(): CoreNode | null {
1380
+ let parent = this.parent;
1381
+ while (parent) {
1382
+ if (parent.rtt) {
1383
+ return parent;
1384
+ }
1385
+ parent = parent.parent;
1386
+ }
1387
+ return null;
1388
+ }
1389
+
1390
+ get texture(): Texture | null {
1391
+ return this.props.texture;
1392
+ }
1393
+
1394
+ setRTTUpdates(type: number) {
1395
+ this.hasRTTupdates = true;
1396
+ this.parent?.setRTTUpdates(type);
1397
+ }
1242
1398
  //#endregion Properties
1243
1399
  }
@@ -173,11 +173,14 @@ export class CoreShaderManager {
173
173
  throw new Error(`Shader type "${shType as string}" is not registered`);
174
174
  }
175
175
 
176
- if (this.renderer.mode === 'canvas' && ShaderClass.prototype instanceof WebGlCoreShader) {
176
+ if (
177
+ this.renderer.mode === 'canvas' &&
178
+ ShaderClass.prototype instanceof WebGlCoreShader
179
+ ) {
177
180
  return {
178
181
  shader: new UnsupportedShader(shType) as InstanceType<ShaderMap[Type]>,
179
- props: props as Record<string, unknown>
180
- }
182
+ props: props as Record<string, unknown>,
183
+ };
181
184
  }
182
185
 
183
186
  if (shType === 'DynamicShader') {
@@ -34,6 +34,7 @@ import type {
34
34
  } from '../common/CommonTypes.js';
35
35
  import type { Rect, RectWithValid } from './lib/utils.js';
36
36
  import { assertTruthy } from '../utils.js';
37
+ import { Matrix3d } from './lib/Matrix3d.js';
37
38
 
38
39
  export interface CoreTextNodeProps extends CoreNodeProps, TrProps {
39
40
  text: string;
@@ -354,11 +355,35 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
354
355
 
355
356
  override renderQuads(renderer: CoreRenderer) {
356
357
  assertTruthy(this.globalTransform);
358
+
359
+ // Prevent quad rendering if parent has a render texture
360
+ // and this node is not the render texture
361
+ if (this.parentHasRenderTexture) {
362
+ if (!renderer.renderToTextureActive) {
363
+ return;
364
+ }
365
+ // Prevent quad rendering if parent render texture is not the active render texture
366
+ if (this.parentRenderTexture !== renderer.activeRttNode) {
367
+ return;
368
+ }
369
+ }
370
+
371
+ if (this.parentHasRenderTexture && this.props.parent?.rtt) {
372
+ this.globalTransform = Matrix3d.identity();
373
+ if (this.localTransform) {
374
+ this.globalTransform.multiply(this.localTransform);
375
+ }
376
+ }
377
+
378
+ assertTruthy(this.globalTransform);
379
+
357
380
  this.textRenderer.renderQuads(
358
381
  this.trState,
359
382
  this.globalTransform,
360
383
  this.clippingRect,
361
384
  this.worldAlpha,
385
+ this.parentHasRenderTexture,
386
+ this.framebufferDimensions,
362
387
  );
363
388
  }
364
389
 
@@ -25,6 +25,7 @@ import { ColorTexture } from './textures/ColorTexture.js';
25
25
  import { ImageTexture } from './textures/ImageTexture.js';
26
26
  import { NoiseTexture } from './textures/NoiseTexture.js';
27
27
  import { SubTexture } from './textures/SubTexture.js';
28
+ import { RenderTexture } from './textures/RenderTexture.js';
28
29
  import type { Texture } from './textures/Texture.js';
29
30
 
30
31
  /**
@@ -40,6 +41,7 @@ export interface TextureMap {
40
41
  ImageTexture: typeof ImageTexture;
41
42
  NoiseTexture: typeof NoiseTexture;
42
43
  SubTexture: typeof SubTexture;
44
+ RenderTexture: typeof RenderTexture;
43
45
  }
44
46
 
45
47
  export type ExtractProps<Type> = Type extends { z$__type__Props: infer Props }
@@ -173,6 +175,7 @@ export class CoreTextureManager {
173
175
  this.registerTextureType('ColorTexture', ColorTexture);
174
176
  this.registerTextureType('NoiseTexture', NoiseTexture);
175
177
  this.registerTextureType('SubTexture', SubTexture);
178
+ this.registerTextureType('RenderTexture', RenderTexture);
176
179
  }
177
180
 
178
181
  registerTextureType<Type extends keyof TextureMap>(
package/src/core/Stage.ts CHANGED
@@ -38,7 +38,10 @@ import type {
38
38
  FrameTickPayload,
39
39
  } from '../common/CommonTypes.js';
40
40
  import { TextureMemoryManager } from './TextureMemoryManager.js';
41
- import type { CoreRenderer, CoreRendererOptions } from './renderers/CoreRenderer.js';
41
+ import type {
42
+ CoreRenderer,
43
+ CoreRendererOptions,
44
+ } from './renderers/CoreRenderer.js';
42
45
  import { CanvasCoreRenderer } from './renderers/canvas/CanvasCoreRenderer.js';
43
46
 
44
47
  export interface StageOptions {
@@ -113,7 +116,7 @@ export class Stage extends EventEmitter {
113
116
  enableContextSpy,
114
117
  numImageWorkers,
115
118
  txMemByteThreshold,
116
- renderMode
119
+ renderMode,
117
120
  } = options;
118
121
 
119
122
  this.txManager = new CoreTextureManager(numImageWorkers);
@@ -150,7 +153,7 @@ export class Stage extends EventEmitter {
150
153
  txMemManager: this.txMemManager,
151
154
  shManager: this.shManager,
152
155
  contextSpy: this.contextSpy,
153
- }
156
+ };
154
157
 
155
158
  if (renderMode === 'canvas') {
156
159
  this.renderer = new CanvasCoreRenderer(rendererOptions);
@@ -162,12 +165,15 @@ export class Stage extends EventEmitter {
162
165
  // Must do this after renderer is created
163
166
  this.txManager.renderer = this.renderer;
164
167
 
165
- this.textRenderers = renderMode === 'webgl' ? {
166
- canvas: new CanvasTextRenderer(this),
167
- sdf: new SdfTextRenderer(this),
168
- } : {
169
- canvas: new CanvasTextRenderer(this),
170
- };
168
+ this.textRenderers =
169
+ renderMode === 'webgl'
170
+ ? {
171
+ canvas: new CanvasTextRenderer(this),
172
+ sdf: new SdfTextRenderer(this),
173
+ }
174
+ : {
175
+ canvas: new CanvasTextRenderer(this),
176
+ };
171
177
  this.fontManager = new TrFontManager(this.textRenderers);
172
178
 
173
179
  // create root node
@@ -205,6 +211,7 @@ export class Stage extends EventEmitter {
205
211
  textureOptions: null,
206
212
  shader: null,
207
213
  shaderProps: null,
214
+ rtt: false,
208
215
  });
209
216
 
210
217
  this.root = rootNode;
@@ -250,17 +257,31 @@ export class Stage extends EventEmitter {
250
257
  */
251
258
  drawFrame() {
252
259
  const { renderer, renderRequested } = this;
260
+ assertTruthy(renderer);
253
261
 
254
262
  // Update tree if needed
255
263
  if (this.root.updateType !== 0) {
256
264
  this.root.update(this.deltaTime, this.root.clippingRect);
257
265
  }
258
266
 
259
- // test if we need to update the scene
260
- renderer?.reset();
267
+ // Reset render operations and clear the canvas
268
+ renderer.reset();
269
+
270
+ // Check if we need to garbage collect
271
+ if (renderer.txMemManager.gcRequested) {
272
+ renderer.txMemManager.gc();
273
+ }
274
+
275
+ // If we have RTT nodes draw them first
276
+ // So we can use them as textures in the main scene
277
+ if (renderer.rttNodes.length > 0) {
278
+ renderer.renderRTTNodes();
279
+ }
261
280
 
281
+ // Fill quads buffer
262
282
  this.addQuads(this.root);
263
283
 
284
+ // Perform render pass
264
285
  renderer?.render();
265
286
 
266
287
  this.calculateFps();
@@ -22,6 +22,7 @@ export class TextureMemoryManager {
22
22
  private memUsed = 0;
23
23
  private textures: Map<CoreContextTexture, number> = new Map();
24
24
  private threshold: number;
25
+ public gcRequested = false;
25
26
 
26
27
  /**
27
28
  * @param byteThreshold Number of texture bytes to trigger garbage collection
@@ -52,11 +53,12 @@ export class TextureMemoryManager {
52
53
  }
53
54
 
54
55
  if (this.memUsed > this.threshold) {
55
- this.gc();
56
+ this.gcRequested = true;
56
57
  }
57
58
  }
58
59
 
59
60
  gc() {
61
+ this.gcRequested = false;
60
62
  this.textures.forEach((byteSize, ctxTexture) => {
61
63
  if (!ctxTexture.renderable) {
62
64
  ctxTexture.free();
@@ -23,9 +23,10 @@ type MessageCallback = [(value: any) => void, (reason: any) => void];
23
23
 
24
24
  export class ImageWorkerManager {
25
25
  imageWorkersEnabled = true;
26
- messageManager: Record<string, MessageCallback> = {};
26
+ messageManager: Record<number, MessageCallback> = {};
27
27
  workers: Worker[] = [];
28
28
  workerIndex = 0;
29
+ nextId = 0;
29
30
 
30
31
  constructor(numImageWorkers: number) {
31
32
  this.workers = this.createWorkers(numImageWorkers);
@@ -35,15 +36,16 @@ export class ImageWorkerManager {
35
36
  }
36
37
 
37
38
  private handleMessage(event: MessageEvent) {
38
- const { src, data, error } = event.data as {
39
+ const { id, data, error } = event.data as {
40
+ id: number;
39
41
  src: string;
40
42
  data?: any;
41
43
  error?: string;
42
44
  };
43
- const msg = this.messageManager[src];
45
+ const msg = this.messageManager[id];
44
46
  if (msg) {
45
47
  const [resolve, reject] = msg;
46
- delete this.messageManager[src];
48
+ delete this.messageManager[id];
47
49
  if (error) {
48
50
  reject(new Error(error));
49
51
  } else {
@@ -92,15 +94,16 @@ export class ImageWorkerManager {
92
94
  }
93
95
 
94
96
  self.onmessage = (event) => {
97
+ var id = event.data.id;
95
98
  var src = event.data.src;
96
99
  var premultiplyAlpha = event.data.premultiplyAlpha;
97
100
 
98
101
  getImage(src, premultiplyAlpha)
99
102
  .then(function(data) {
100
- self.postMessage({ src: src, data: data }, [data.data]);
103
+ self.postMessage({ id: id, data: data }, [data.data]);
101
104
  })
102
105
  .catch(function(error) {
103
- self.postMessage({ src: src, error: error.message });
106
+ self.postMessage({ id: id, error: error.message });
104
107
  });
105
108
  };
106
109
  `;
@@ -135,8 +138,10 @@ export class ImageWorkerManager {
135
138
  try {
136
139
  if (this.workers) {
137
140
  const absoluteSrcUrl = this.convertUrlToAbsolute(src);
138
- this.messageManager[absoluteSrcUrl] = [resolve, reject];
141
+ const id = this.nextId++;
142
+ this.messageManager[id] = [resolve, reject];
139
143
  this.getNextWorker().postMessage({
144
+ id,
140
145
  src: absoluteSrcUrl,
141
146
  premultiplyAlpha,
142
147
  });
@@ -75,6 +75,7 @@ export class WebGlContextWrapper {
75
75
  public readonly RGBA;
76
76
  public readonly UNSIGNED_BYTE;
77
77
  public readonly UNPACK_PREMULTIPLY_ALPHA_WEBGL;
78
+ public readonly UNPACK_FLIP_Y_WEBGL;
78
79
  public readonly FLOAT;
79
80
  public readonly TRIANGLES;
80
81
  public readonly UNSIGNED_SHORT;
@@ -86,6 +87,7 @@ export class WebGlContextWrapper {
86
87
  public readonly COMPILE_STATUS;
87
88
  public readonly LINK_STATUS;
88
89
  public readonly DYNAMIC_DRAW;
90
+ public readonly COLOR_ATTACHMENT0;
89
91
  //#endregion WebGL Enums
90
92
 
91
93
  constructor(private gl: WebGLRenderingContext | WebGL2RenderingContext) {
@@ -161,6 +163,7 @@ export class WebGlContextWrapper {
161
163
  this.RGBA = gl.RGBA;
162
164
  this.UNSIGNED_BYTE = gl.UNSIGNED_BYTE;
163
165
  this.UNPACK_PREMULTIPLY_ALPHA_WEBGL = gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL;
166
+ this.UNPACK_FLIP_Y_WEBGL = gl.UNPACK_FLIP_Y_WEBGL;
164
167
  this.FLOAT = gl.FLOAT;
165
168
  this.TRIANGLES = gl.TRIANGLES;
166
169
  this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT;
@@ -175,6 +178,7 @@ export class WebGlContextWrapper {
175
178
  this.COMPILE_STATUS = gl.COMPILE_STATUS;
176
179
  this.LINK_STATUS = gl.LINK_STATUS;
177
180
  this.DYNAMIC_DRAW = gl.DYNAMIC_DRAW;
181
+ this.COLOR_ATTACHMENT0 = gl.COLOR_ATTACHMENT0;
178
182
  }
179
183
  /**
180
184
  * Returns true if the WebGL context is WebGL2
@@ -533,7 +537,7 @@ export class WebGlContextWrapper {
533
537
 
534
538
  /**
535
539
  * ```
536
- * createBuffer();
540
+ * gl.createBuffer();
537
541
  * ```
538
542
  *
539
543
  * @returns
@@ -543,6 +547,52 @@ export class WebGlContextWrapper {
543
547
  return gl.createBuffer();
544
548
  }
545
549
 
550
+ /**
551
+ * ```
552
+ * gl.createFramebuffer();
553
+ * ```
554
+ * @returns
555
+ */
556
+ createFramebuffer() {
557
+ const { gl } = this;
558
+ return gl.createFramebuffer();
559
+ }
560
+
561
+ /**
562
+ * ```
563
+ * gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
564
+ * ```
565
+ *
566
+ * @param framebuffer
567
+ */
568
+ bindFramebuffer(framebuffer: WebGLFramebuffer | null) {
569
+ const { gl } = this;
570
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
571
+ }
572
+
573
+ /**
574
+ * ```
575
+ * gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
576
+ * ```
577
+ * @remarks
578
+ * **WebGL Difference**: Bind target is always `gl.FRAMEBUFFER` and textarget is always `gl.TEXTURE_2D`
579
+ */
580
+
581
+ framebufferTexture2D(
582
+ attachment: GLenum,
583
+ texture: WebGLTexture | null,
584
+ level: GLint,
585
+ ) {
586
+ const { gl } = this;
587
+ gl.framebufferTexture2D(
588
+ gl.FRAMEBUFFER,
589
+ attachment,
590
+ gl.TEXTURE_2D,
591
+ texture,
592
+ level,
593
+ );
594
+ }
595
+
546
596
  /**
547
597
  * ```
548
598
  * gl.clear(gl.COLOR_BUFFER_BIT);
@@ -17,8 +17,13 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
+ import type { Dimensions } from '../../common/CommonTypes.js';
21
+ import type { CoreNode } from '../CoreNode.js';
20
22
  import type { CoreShaderManager } from '../CoreShaderManager.js';
21
- import type { CoreTextureManager, TextureOptions } from '../CoreTextureManager.js';
23
+ import type {
24
+ CoreTextureManager,
25
+ TextureOptions,
26
+ } from '../CoreTextureManager.js';
22
27
  import type { Stage } from '../Stage.js';
23
28
  import type { TextureMemoryManager } from '../TextureMemoryManager.js';
24
29
  import type { ContextSpy } from '../lib/ContextSpy.js';
@@ -48,6 +53,9 @@ export interface QuadOptions {
48
53
  tb: number;
49
54
  tc: number;
50
55
  td: number;
56
+ rtt?: boolean;
57
+ parentHasRenderTexture?: boolean;
58
+ framebufferDimensions?: Dimensions;
51
59
  }
52
60
 
53
61
  export interface CoreRendererOptions {
@@ -72,6 +80,7 @@ export abstract class CoreRenderer {
72
80
  txManager: CoreTextureManager;
73
81
  txMemManager: TextureMemoryManager;
74
82
  shManager: CoreShaderManager;
83
+ rttNodes: CoreNode[] = [];
75
84
 
76
85
  constructor(options: CoreRendererOptions) {
77
86
  this.options = options;
@@ -86,4 +95,9 @@ export abstract class CoreRenderer {
86
95
  abstract addQuad(quad: QuadOptions): void;
87
96
  abstract createCtxTexture(textureSource: Texture): CoreContextTexture;
88
97
  abstract getShaderManager(): CoreShaderManager;
98
+ abstract get renderToTextureActive(): boolean;
99
+ abstract get activeRttNode(): CoreNode | null;
100
+ abstract renderRTTNodes(): void;
101
+ abstract removeRTTNode(node: CoreNode): void;
102
+ abstract renderToTexture(node: CoreNode): void;
89
103
  }