@lightningjs/renderer 0.7.2 → 0.7.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 (195) hide show
  1. package/COPYING +1 -0
  2. package/LICENSE +202 -202
  3. package/NOTICE +3 -3
  4. package/README.md +221 -221
  5. package/dist/src/common/CommonTypes.d.ts +7 -0
  6. package/dist/src/core/Stage.d.ts +2 -1
  7. package/dist/src/core/Stage.js +4 -0
  8. package/dist/src/core/Stage.js.map +1 -1
  9. package/dist/src/core/animations/CoreAnimation.js +3 -0
  10. package/dist/src/core/animations/CoreAnimation.js.map +1 -1
  11. package/dist/src/core/animations/CoreAnimationController.d.ts +8 -0
  12. package/dist/src/core/animations/CoreAnimationController.js +26 -0
  13. package/dist/src/core/animations/CoreAnimationController.js.map +1 -1
  14. package/dist/src/core/lib/ImageWorker.d.ts +1 -1
  15. package/dist/src/core/lib/ImageWorker.js +30 -22
  16. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  17. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  18. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +47 -47
  19. package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js +61 -61
  20. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +93 -93
  21. package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js +63 -63
  22. package/dist/src/core/renderers/webgl/shaders/SdfShader.js +49 -49
  23. package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js +15 -15
  24. package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js +5 -5
  25. package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js +15 -15
  26. package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js +15 -15
  27. package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js +15 -15
  28. package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js +42 -42
  29. package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js +44 -44
  30. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js +3 -3
  31. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +31 -31
  32. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +13 -13
  33. package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js +37 -37
  34. package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js +19 -19
  35. package/dist/src/core/textures/ImageTexture.d.ts +2 -1
  36. package/dist/src/core/textures/ImageTexture.js +7 -2
  37. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  38. package/dist/src/core/textures/Texture.d.ts +1 -1
  39. package/dist/src/main-api/ICoreDriver.d.ts +2 -1
  40. package/dist/src/main-api/Inspector.d.ts +6 -0
  41. package/dist/src/main-api/Inspector.js +105 -19
  42. package/dist/src/main-api/Inspector.js.map +1 -1
  43. package/dist/src/main-api/RendererMain.d.ts +12 -0
  44. package/dist/src/main-api/RendererMain.js +19 -1
  45. package/dist/src/main-api/RendererMain.js.map +1 -1
  46. package/dist/src/render-drivers/main/MainCoreDriver.d.ts +2 -1
  47. package/dist/src/render-drivers/main/MainCoreDriver.js +7 -1
  48. package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
  49. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.d.ts +2 -1
  50. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +7 -0
  51. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  52. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.d.ts +9 -1
  53. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js.map +1 -1
  54. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  55. package/exports/core-api.ts +102 -102
  56. package/exports/main-api.ts +60 -60
  57. package/exports/utils.ts +41 -41
  58. package/package.json +5 -3
  59. package/scripts/please-use-pnpm.js +13 -0
  60. package/src/common/CommonTypes.ts +113 -105
  61. package/src/common/EventEmitter.ts +77 -77
  62. package/src/common/IAnimationController.ts +29 -29
  63. package/src/core/CoreExtension.ts +32 -32
  64. package/src/core/CoreNode.ts +926 -926
  65. package/src/core/CoreShaderManager.ts +243 -243
  66. package/src/core/CoreTextNode.ts +391 -391
  67. package/src/core/CoreTextureManager.ts +326 -326
  68. package/src/core/Stage.ts +354 -342
  69. package/src/core/animations/AnimationManager.ts +38 -38
  70. package/src/core/animations/CoreAnimation.ts +181 -177
  71. package/src/core/animations/CoreAnimationController.ts +148 -117
  72. package/src/core/lib/ContextSpy.ts +41 -41
  73. package/src/core/lib/ImageWorker.ts +135 -124
  74. package/src/core/lib/Matrix3d.ts +290 -290
  75. package/src/core/lib/WebGlContextWrapper.ts +992 -992
  76. package/src/core/lib/textureCompression.ts +152 -152
  77. package/src/core/lib/utils.ts +241 -241
  78. package/src/core/platform.ts +46 -46
  79. package/src/core/renderers/CoreContextTexture.ts +30 -30
  80. package/src/core/renderers/CoreRenderOp.ts +22 -22
  81. package/src/core/renderers/CoreRenderer.ts +63 -63
  82. package/src/core/renderers/CoreShader.ts +41 -41
  83. package/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +37 -37
  84. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +230 -231
  85. package/src/core/renderers/webgl/WebGlCoreRenderOp.ts +107 -107
  86. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +520 -520
  87. package/src/core/renderers/webgl/WebGlCoreShader.ts +337 -337
  88. package/src/core/renderers/webgl/internal/BufferCollection.ts +54 -54
  89. package/src/core/renderers/webgl/internal/RendererUtils.ts +131 -131
  90. package/src/core/renderers/webgl/internal/ShaderUtils.ts +136 -136
  91. package/src/core/renderers/webgl/internal/WebGlUtils.ts +35 -35
  92. package/src/core/renderers/webgl/shaders/DefaultShader.ts +95 -95
  93. package/src/core/renderers/webgl/shaders/DefaultShaderBatched.ts +132 -132
  94. package/src/core/renderers/webgl/shaders/DynamicShader.ts +474 -474
  95. package/src/core/renderers/webgl/shaders/RoundedRectangle.ts +161 -161
  96. package/src/core/renderers/webgl/shaders/SdfShader.ts +174 -174
  97. package/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.ts +101 -101
  98. package/src/core/renderers/webgl/shaders/effects/BorderEffect.ts +86 -86
  99. package/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.ts +101 -101
  100. package/src/core/renderers/webgl/shaders/effects/BorderRightEffect.ts +101 -101
  101. package/src/core/renderers/webgl/shaders/effects/BorderTopEffect.ts +101 -101
  102. package/src/core/renderers/webgl/shaders/effects/EffectUtils.ts +33 -33
  103. package/src/core/renderers/webgl/shaders/effects/FadeOutEffect.ts +135 -135
  104. package/src/core/renderers/webgl/shaders/effects/GlitchEffect.ts +145 -145
  105. package/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.ts +67 -67
  106. package/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.ts +176 -176
  107. package/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.ts +159 -159
  108. package/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.ts +186 -186
  109. package/src/core/renderers/webgl/shaders/effects/RadiusEffect.ts +121 -121
  110. package/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts +114 -114
  111. package/src/core/text-rendering/TextTextureRendererUtils.ts +189 -189
  112. package/src/core/text-rendering/TrFontManager.ts +96 -96
  113. package/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.ts +141 -141
  114. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/FontShaper.ts +139 -139
  115. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.test.ts +173 -173
  116. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.ts +169 -169
  117. package/src/core/text-rendering/font-face-types/TrFontFace.ts +105 -105
  118. package/src/core/text-rendering/font-face-types/WebTrFontFace.ts +77 -77
  119. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +751 -751
  120. package/src/core/text-rendering/renderers/LightningTextTextureRenderer.ts +741 -741
  121. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +775 -775
  122. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.test.ts +48 -48
  123. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.ts +66 -66
  124. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/SpecialCodepoints.ts +52 -52
  125. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/constants.ts +32 -32
  126. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.ts +84 -84
  127. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.test.ts +133 -133
  128. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.ts +38 -38
  129. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +393 -393
  130. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.test.ts +49 -49
  131. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.ts +51 -51
  132. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.test.ts +205 -205
  133. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.ts +93 -93
  134. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.ts +40 -40
  135. package/src/core/text-rendering/renderers/TextRenderer.ts +504 -504
  136. package/src/core/textures/ColorTexture.ts +86 -86
  137. package/src/core/textures/ImageTexture.ts +140 -133
  138. package/src/core/textures/NoiseTexture.ts +96 -96
  139. package/src/core/textures/SubTexture.ts +143 -143
  140. package/src/core/textures/Texture.ts +218 -218
  141. package/src/core/utils.ts +224 -224
  142. package/src/env.d.ts +7 -7
  143. package/src/main-api/ICoreDriver.ts +66 -61
  144. package/src/main-api/INode.ts +470 -470
  145. package/src/main-api/Inspector.ts +432 -0
  146. package/src/main-api/RendererMain.ts +649 -610
  147. package/src/main-api/texture-usage-trackers/FinalizationRegistryTextureUsageTracker.ts +45 -45
  148. package/src/main-api/texture-usage-trackers/ManualCountTextureUsageTracker.ts +154 -154
  149. package/src/main-api/texture-usage-trackers/TextureUsageTracker.ts +54 -54
  150. package/src/render-drivers/main/MainCoreDriver.ts +148 -133
  151. package/src/render-drivers/main/MainOnlyNode.ts +453 -453
  152. package/src/render-drivers/main/MainOnlyTextNode.ts +261 -261
  153. package/src/render-drivers/threadx/NodeStruct.ts +300 -300
  154. package/src/render-drivers/threadx/SharedNode.ts +97 -97
  155. package/src/render-drivers/threadx/TextNodeStruct.ts +211 -211
  156. package/src/render-drivers/threadx/ThreadXCoreDriver.ts +285 -273
  157. package/src/render-drivers/threadx/ThreadXMainAnimationController.ts +99 -99
  158. package/src/render-drivers/threadx/ThreadXMainNode.ts +178 -178
  159. package/src/render-drivers/threadx/ThreadXMainTextNode.ts +85 -85
  160. package/src/render-drivers/threadx/ThreadXRendererMessage.ts +110 -97
  161. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +238 -238
  162. package/src/render-drivers/threadx/worker/ThreadXRendererTextNode.ts +149 -149
  163. package/src/render-drivers/threadx/worker/renderer.ts +151 -151
  164. package/src/render-drivers/utils.ts +57 -57
  165. package/src/utils.ts +207 -207
  166. package/dist/src/core/lib/WebGlContext.d.ts +0 -414
  167. package/dist/src/core/lib/WebGlContext.js +0 -640
  168. package/dist/src/core/lib/WebGlContext.js.map +0 -1
  169. package/dist/src/core/scene/Scene.d.ts +0 -59
  170. package/dist/src/core/scene/Scene.js +0 -106
  171. package/dist/src/core/scene/Scene.js.map +0 -1
  172. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.d.ts +0 -8
  173. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.js +0 -29
  174. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.js.map +0 -1
  175. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.d.ts +0 -19
  176. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.js +0 -84
  177. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.js.map +0 -1
  178. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.d.ts +0 -8
  179. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.js +0 -40
  180. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.js.map +0 -1
  181. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.d.ts +0 -2
  182. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.js +0 -41
  183. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.js.map +0 -1
  184. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.d.ts +0 -1
  185. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.js +0 -4
  186. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.js.map +0 -1
  187. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.d.ts +0 -1
  188. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.js +0 -2
  189. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.js.map +0 -1
  190. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.d.ts +0 -20
  191. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.js +0 -55
  192. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.js.map +0 -1
  193. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.d.ts +0 -9
  194. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.js +0 -32
  195. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.js.map +0 -1
@@ -1,610 +1,649 @@
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
- /* eslint-disable @typescript-eslint/no-unused-vars */
21
- import type { ShaderMap } from '../core/CoreShaderManager.js';
22
- import type {
23
- ExtractProps,
24
- TextureMap,
25
- TextureOptions,
26
- } from '../core/CoreTextureManager.js';
27
- import type {
28
- INode,
29
- INodeWritableProps,
30
- ITextNode,
31
- ITextNodeWritableProps,
32
- } from './INode.js';
33
- import type { ICoreDriver } from './ICoreDriver.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';
40
- import { EventEmitter } from '../common/EventEmitter.js';
41
-
42
- /**
43
- * An immutable reference to a specific Texture type
44
- *
45
- * @remarks
46
- * See {@link TextureRef} for more details.
47
- */
48
- export interface SpecificTextureRef<TxType extends keyof TextureMap> {
49
- readonly descType: 'texture';
50
- readonly txType: TxType;
51
- readonly props: ExtractProps<TextureMap[TxType]>;
52
- readonly options?: Readonly<TextureOptions>;
53
- }
54
-
55
- type MapTextureRefs<TxType extends keyof TextureMap> =
56
- TxType extends keyof TextureMap ? SpecificTextureRef<TxType> : never;
57
-
58
- /**
59
- * An immutable reference to a Texture
60
- *
61
- * @remarks
62
- * This structure should only be created by the RendererMain's `createTexture`
63
- * method. The structure is immutable and should not be modified once created.
64
- *
65
- * A `TextureRef` exists in the Main API Space and is used to point to an actual
66
- * `Texture` instance in the Core API Space. The `TextureRef` is used to
67
- * communicate with the Core API Space to create, load, and destroy the
68
- * `Texture` instance.
69
- *
70
- * This type is technically a discriminated union of all possible texture types.
71
- * If you'd like to represent a specific texture type, you can use the
72
- * `SpecificTextureRef` generic type.
73
- */
74
- export type TextureRef = MapTextureRefs<keyof TextureMap>;
75
-
76
- /**
77
- * An immutable reference to a specific Shader type
78
- *
79
- * @remarks
80
- * See {@link ShaderRef} for more details.
81
- */
82
- export interface SpecificShaderRef<ShType extends keyof ShaderMap> {
83
- readonly descType: 'shader';
84
- readonly shType: ShType;
85
- readonly props: ExtractProps<ShaderMap[ShType]>;
86
- }
87
-
88
- type MapShaderRefs<ShType extends keyof ShaderMap> =
89
- ShType extends keyof ShaderMap ? SpecificShaderRef<ShType> : never;
90
-
91
- /**
92
- * An immutable reference to a Shader
93
- *
94
- * @remarks
95
- * This structure should only be created by the RendererMain's `createShader`
96
- * method. The structure is immutable and should not be modified once created.
97
- *
98
- * A `ShaderRef` exists in the Main API Space and is used to point to an actual
99
- * `Shader` instance in the Core API Space. The `ShaderRef` is used to
100
- * communicate with the Core API Space to create, load, and destroy the
101
- * `Shader` instance.
102
- *
103
- * This type is technically a discriminated union of all possible shader types.
104
- * If you'd like to represent a specific shader type, you can use the
105
- * `SpecificShaderRef` generic type.
106
- */
107
- export type ShaderRef = MapShaderRefs<keyof ShaderMap>;
108
-
109
- /**
110
- * Configuration settings for {@link RendererMain}
111
- */
112
- export interface RendererMainSettings {
113
- /**
114
- * Authored logical pixel width of the application
115
- *
116
- * @defaultValue `1920`
117
- */
118
- appWidth?: number;
119
-
120
- /**
121
- * Authored logical pixel height of the application
122
- *
123
- * @defaultValue `1080`
124
- */
125
- appHeight?: number;
126
-
127
- /**
128
- * Factor to convert app-authored logical coorindates to device logical coordinates
129
- *
130
- * @remarks
131
- * This value allows auto-scaling to support larger/small resolutions than the
132
- * app was authored for.
133
- *
134
- * If the app was authored for 1920x1080 and this value is 2, the app's canvas
135
- * will be rendered at 3840x2160 logical pixels.
136
- *
137
- * Likewise, if the app was authored for 1920x1080 and this value is 0.66667,
138
- * the app's canvas will be rendered at 1280x720 logical pixels.
139
- *
140
- * @defaultValue `1`
141
- */
142
- deviceLogicalPixelRatio?: number;
143
-
144
- /**
145
- * Factor to convert device logical coordinates to device physical coordinates
146
- *
147
- * @remarks
148
- * This value allows auto-scaling to support devices with different pixel densities.
149
- *
150
- * This controls the number of physical pixels that are used to render each logical
151
- * pixel. For example, if the device has a pixel density of 2, each logical pixel
152
- * will be rendered using 2x2 physical pixels.
153
- *
154
- * By default, it will be set to `window.devicePixelRatio` which is the pixel
155
- * density of the device the app is running on reported by the browser.
156
- *
157
- * @defaultValue `window.devicePixelRatio`
158
- */
159
- devicePhysicalPixelRatio?: number;
160
-
161
- /**
162
- * RGBA encoded number of the background to use
163
- *
164
- * @defaultValue `0x00000000`
165
- */
166
- clearColor?: number;
167
-
168
- /**
169
- * Path to a custom core module to use
170
- *
171
- * @defaultValue `null`
172
- */
173
- coreExtensionModule?: string | null;
174
-
175
- /**
176
- * Enable experimental FinalizationRegistry-based texture usage tracking
177
- * for texture garbage collection
178
- *
179
- * @remarks
180
- * By default, the Renderer uses a manual reference counting system to track
181
- * texture usage. Textures are eventually released from the Core Texture
182
- * Manager's Usage Cache when they are no longer referenced by any Nodes (or
183
- * SubTextures that are referenced by nodes). This works well enough, but has
184
- * the consequence of textures being removed from Usage Cache even if their
185
- * references are still alive in memory. This can require a texture to be
186
- * reloaded from the source when it is used again after being removed from
187
- * cache.
188
- *
189
- * This is an experimental feature that uses a FinalizationRegistry to track
190
- * texture usage. This causes textures to be removed from the Usage Cache only
191
- * when their references are no longer alive in memory. Meaning a loaded texture
192
- * will remain in the Usage Cache until it's reference is garbage collected.
193
- *
194
- * This feature is not enabled by default because browser support for the
195
- * FinalizationRegistry is limited. It should NOT be enabled in production apps
196
- * as this behavior is not guaranteed to be supported in the future. Developer
197
- * feedback on this feature, however, is welcome.
198
- *
199
- * @defaultValue `false`
200
- */
201
- experimental_FinalizationRegistryTextureUsageTracker?: boolean;
202
-
203
- textureCleanupOptions?: ManualCountTextureUsageTrackerOptions;
204
-
205
- /**
206
- * Interval in milliseconds to receive FPS updates
207
- *
208
- * @remarks
209
- * If set to `0`, FPS updates will be disabled.
210
- *
211
- * @defaultValue `0` (disabled)
212
- */
213
- fpsUpdateInterval?: number;
214
-
215
- /**
216
- * Include context call (i.e. WebGL) information in FPS updates
217
- *
218
- * @remarks
219
- * When enabled the number of calls to each context method over the
220
- * `fpsUpdateInterval` will be included in the FPS update payload's
221
- * `contextSpyData` property.
222
- *
223
- * Enabling the context spy has a serious impact on performance so only use it
224
- * when you need to extract context call information.
225
- *
226
- * @defaultValue `false` (disabled)
227
- */
228
- enableContextSpy?: boolean;
229
-
230
- /**
231
- * Number or Image Workers to use
232
- *
233
- * @remarks
234
- * On devices with multiple cores, this can be used to improve image loading
235
- * as well as reduce the impact of image loading on the main thread.
236
- * Set to 0 to disable image workers.
237
- *
238
- * @defaultValue `2`
239
- */
240
- numImageWorkers?: number;
241
- }
242
-
243
- /**
244
- * The Renderer Main API
245
- *
246
- * @remarks
247
- * This is the primary class used to configure and operate the Renderer.
248
- *
249
- * It is used to create and destroy Nodes, as well as Texture and Shader
250
- * references.
251
- *
252
- * Example:
253
- * ```ts
254
- * import { RendererMain, MainCoreDriver } from '@lightningjs/renderer';
255
- *
256
- * // Initialize the Renderer
257
- * const renderer = new RendererMain(
258
- * {
259
- * appWidth: 1920,
260
- * appHeight: 1080
261
- * },
262
- * 'app',
263
- * new MainCoreDriver(),
264
- * );
265
- * ```
266
- */
267
- export class RendererMain extends EventEmitter {
268
- readonly root: INode | null = null;
269
- readonly driver: ICoreDriver;
270
- readonly canvas: HTMLCanvasElement;
271
- readonly settings: Readonly<Required<RendererMainSettings>>;
272
- private nodes: Map<number, INode> = new Map();
273
- private nextTextureId = 1;
274
-
275
- /**
276
- * Texture Usage Tracker for Usage Based Texture Garbage Collection
277
- *
278
- * @remarks
279
- * For internal use only. DO NOT ACCESS.
280
- */
281
- public textureTracker: TextureUsageTracker;
282
-
283
- /**
284
- * Constructs a new Renderer instance
285
- *
286
- * @param settings Renderer settings
287
- * @param target Element ID or HTMLElement to insert the canvas into
288
- * @param driver Core Driver to use
289
- */
290
- constructor(
291
- settings: RendererMainSettings,
292
- target: string | HTMLElement,
293
- driver: ICoreDriver,
294
- ) {
295
- super();
296
- const resolvedSettings: Required<RendererMainSettings> = {
297
- appWidth: settings.appWidth || 1920,
298
- appHeight: settings.appHeight || 1080,
299
- deviceLogicalPixelRatio: settings.deviceLogicalPixelRatio || 1,
300
- devicePhysicalPixelRatio:
301
- settings.devicePhysicalPixelRatio || window.devicePixelRatio,
302
- clearColor: settings.clearColor ?? 0x00000000,
303
- coreExtensionModule: settings.coreExtensionModule || null,
304
- experimental_FinalizationRegistryTextureUsageTracker:
305
- settings.experimental_FinalizationRegistryTextureUsageTracker ?? false,
306
- textureCleanupOptions: settings.textureCleanupOptions || {},
307
- fpsUpdateInterval: settings.fpsUpdateInterval || 0,
308
- numImageWorkers:
309
- settings.numImageWorkers !== undefined ? settings.numImageWorkers : 2,
310
- enableContextSpy: settings.enableContextSpy ?? false,
311
- };
312
- this.settings = resolvedSettings;
313
-
314
- const {
315
- appWidth,
316
- appHeight,
317
- deviceLogicalPixelRatio,
318
- devicePhysicalPixelRatio,
319
- } = resolvedSettings;
320
-
321
- const releaseCallback = (textureId: number) => {
322
- this.driver.releaseTexture(textureId);
323
- };
324
-
325
- const useFinalizationRegistryTracker =
326
- resolvedSettings.experimental_FinalizationRegistryTextureUsageTracker &&
327
- typeof FinalizationRegistry === 'function';
328
- this.textureTracker = useFinalizationRegistryTracker
329
- ? new FinalizationRegistryTextureUsageTracker(releaseCallback)
330
- : new ManualCountTextureUsageTracker(
331
- releaseCallback,
332
- this.settings.textureCleanupOptions,
333
- );
334
-
335
- const deviceLogicalWidth = appWidth * deviceLogicalPixelRatio;
336
- const deviceLogicalHeight = appHeight * deviceLogicalPixelRatio;
337
-
338
- this.driver = driver;
339
-
340
- const canvas = document.createElement('canvas');
341
- this.canvas = canvas;
342
- canvas.width = deviceLogicalWidth * devicePhysicalPixelRatio;
343
- canvas.height = deviceLogicalHeight * devicePhysicalPixelRatio;
344
-
345
- canvas.style.width = `${deviceLogicalWidth}px`;
346
- canvas.style.height = `${deviceLogicalHeight}px`;
347
-
348
- let targetEl: HTMLElement | null;
349
- if (typeof target === 'string') {
350
- targetEl = document.getElementById(target);
351
- } else {
352
- targetEl = target;
353
- }
354
-
355
- if (!targetEl) {
356
- throw new Error('Could not find target element');
357
- }
358
-
359
- // Hook up the driver's callbacks
360
- driver.onCreateNode = (node) => {
361
- this.nodes.set(node.id, node);
362
- };
363
-
364
- driver.onBeforeDestroyNode = (node) => {
365
- this.nodes.delete(node.id);
366
- };
367
-
368
- driver.onFpsUpdate = (fpsData) => {
369
- this.emit('fpsUpdate', fpsData);
370
- };
371
-
372
- targetEl.appendChild(canvas);
373
- }
374
-
375
- /**
376
- * Initialize the renderer
377
- *
378
- * @remarks
379
- * This method must be called and resolved asyncronously before any other
380
- * methods are called.
381
- */
382
- async init(): Promise<void> {
383
- await this.driver.init(this, this.settings, this.canvas);
384
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
385
- (this.root as INode) = this.driver.getRootNode();
386
- }
387
-
388
- /**
389
- * Create a new scene graph node
390
- *
391
- * @remarks
392
- * A node is the main graphical building block of the Renderer scene graph. It
393
- * can be a container for other nodes, or it can be a leaf node that renders a
394
- * solid color, gradient, image, or specific texture, using a specific shader.
395
- *
396
- * To create a text node, see {@link createTextNode}.
397
- *
398
- * See {@link INode} for more details.
399
- *
400
- * @param props
401
- * @returns
402
- */
403
- createNode(props: Partial<INodeWritableProps>): INode {
404
- return this.driver.createNode(this.resolveNodeDefaults(props));
405
- }
406
-
407
- /**
408
- * Create a new scene graph text node
409
- *
410
- * @remarks
411
- * A text node is the second graphical building block of the Renderer scene
412
- * graph. It renders text using a specific text renderer that is automatically
413
- * chosen based on the font requested and what type of fonts are installed
414
- * into an app via a CoreExtension.
415
- *
416
- * See {@link ITextNode} for more details.
417
- *
418
- * @param props
419
- * @returns
420
- */
421
- createTextNode(props: Partial<ITextNodeWritableProps>): ITextNode {
422
- const fontSize = props.fontSize ?? 16;
423
- const data = {
424
- ...this.resolveNodeDefaults(props),
425
- text: props.text ?? '',
426
- textRendererOverride: props.textRendererOverride ?? null,
427
- fontSize,
428
- fontFamily: props.fontFamily ?? 'sans-serif',
429
- fontStyle: props.fontStyle ?? 'normal',
430
- fontWeight: props.fontWeight ?? 'normal',
431
- fontStretch: props.fontStretch ?? 'normal',
432
- textAlign: props.textAlign ?? 'left',
433
- contain: props.contain ?? 'none',
434
- scrollable: props.scrollable ?? false,
435
- scrollY: props.scrollY ?? 0,
436
- offsetY: props.offsetY ?? 0,
437
- letterSpacing: props.letterSpacing ?? 0,
438
- lineHeight: props.lineHeight ?? fontSize,
439
- maxLines: props.maxLines ?? 0,
440
- textBaseline: props.textBaseline ?? 'alphabetic',
441
- verticalAlign: props.verticalAlign ?? 'top',
442
- overflowSuffix: props.overflowSuffix ?? '...',
443
- debug: props.debug ?? {},
444
- };
445
-
446
- return this.driver.createTextNode(data);
447
- }
448
-
449
- /**
450
- * Resolves the default property values for a Node
451
- *
452
- * @remarks
453
- * This method is used internally by the RendererMain to resolve the default
454
- * property values for a Node. It is exposed publicly so that it can be used
455
- * by Core Driver implementations.
456
- *
457
- * @param props
458
- * @returns
459
- */
460
- resolveNodeDefaults(props: Partial<INodeWritableProps>): INodeWritableProps {
461
- const color = props.color ?? 0xffffffff;
462
- const colorTl = props.colorTl ?? props.colorTop ?? props.colorLeft ?? color;
463
- const colorTr =
464
- props.colorTr ?? props.colorTop ?? props.colorRight ?? color;
465
- const colorBl =
466
- props.colorBl ?? props.colorBottom ?? props.colorLeft ?? color;
467
- const colorBr =
468
- props.colorBr ?? props.colorBottom ?? props.colorRight ?? color;
469
-
470
- return {
471
- x: props.x ?? 0,
472
- y: props.y ?? 0,
473
- width: props.width ?? 0,
474
- height: props.height ?? 0,
475
- alpha: props.alpha ?? 1,
476
- clipping: props.clipping ?? false,
477
- color,
478
- colorTop: props.colorTop ?? color,
479
- colorBottom: props.colorBottom ?? color,
480
- colorLeft: props.colorLeft ?? color,
481
- colorRight: props.colorRight ?? color,
482
- colorBl,
483
- colorBr,
484
- colorTl,
485
- colorTr,
486
- zIndex: props.zIndex ?? 0,
487
- zIndexLocked: props.zIndexLocked ?? 0,
488
- parent: props.parent ?? null,
489
- texture: props.texture ?? null,
490
- shader: props.shader ?? null,
491
- // Since setting the `src` will trigger a texture load, we need to set it after
492
- // we set the texture. Otherwise, problems happen.
493
- src: props.src ?? '',
494
- scale: props.scale ?? null,
495
- scaleX: props.scaleX ?? props.scale ?? 1,
496
- scaleY: props.scaleY ?? props.scale ?? 1,
497
- mount: props.mount ?? 0,
498
- mountX: props.mountX ?? props.mount ?? 0,
499
- mountY: props.mountY ?? props.mount ?? 0,
500
- pivot: props.pivot ?? 0.5,
501
- pivotX: props.pivotX ?? props.pivot ?? 0.5,
502
- pivotY: props.pivotY ?? props.pivot ?? 0.5,
503
- rotation: props.rotation ?? 0,
504
- };
505
- }
506
-
507
- /**
508
- * Destroy a node
509
- *
510
- * @remarks
511
- * This method destroys a node but does not destroy its children.
512
- *
513
- * @param node
514
- * @returns
515
- */
516
- destroyNode(node: INode) {
517
- return this.driver.destroyNode(node);
518
- }
519
-
520
- /**
521
- * Create a new texture reference
522
- *
523
- * @remarks
524
- * This method creates a new reference to a texture. The texture is not
525
- * loaded until it is used on a node.
526
- *
527
- * It can be assigned to a node's `texture` property, or it can be used
528
- * when creating a SubTexture.
529
- *
530
- * @param textureType
531
- * @param props
532
- * @param options
533
- * @returns
534
- */
535
- createTexture<TxType extends keyof TextureMap>(
536
- textureType: TxType,
537
- props: SpecificTextureRef<TxType>['props'],
538
- options?: TextureOptions,
539
- ): SpecificTextureRef<TxType> {
540
- const id = this.nextTextureId++;
541
- const desc = {
542
- descType: 'texture',
543
- txType: textureType,
544
- props,
545
- options: {
546
- ...options,
547
- // This ID is used to identify the texture in the CoreTextureManager's
548
- // ID Texture Map cache.
549
- id,
550
- },
551
- } satisfies SpecificTextureRef<TxType>;
552
- this.textureTracker.registerTexture(desc as TextureRef);
553
- return desc;
554
- }
555
-
556
- /**
557
- * Create a new shader reference
558
- *
559
- * @remarks
560
- * This method creates a new reference to a shader. The shader is not
561
- * loaded until it is used on a Node.
562
- *
563
- * It can be assigned to a Node's `shader` property.
564
- *
565
- * @param shaderType
566
- * @param props
567
- * @returns
568
- */
569
- createShader<ShType extends keyof ShaderMap>(
570
- shaderType: ShType,
571
- props?: SpecificShaderRef<ShType>['props'],
572
- ): SpecificShaderRef<ShType> {
573
- return {
574
- descType: 'shader',
575
- shType: shaderType,
576
- props: props as SpecificShaderRef<ShType>['props'],
577
- };
578
- }
579
-
580
- /**
581
- * Get a Node by its ID
582
- *
583
- * @param id
584
- * @returns
585
- */
586
- getNodeById(id: number): INode | null {
587
- return this.nodes.get(id) || null;
588
- }
589
-
590
- toggleFreeze() {
591
- throw new Error('Not implemented');
592
- }
593
-
594
- advanceFrame() {
595
- throw new Error('Not implemented');
596
- }
597
-
598
- /**
599
- * Re-render the current frame without advancing any running animations.
600
- *
601
- * @remarks
602
- * Any state changes will be reflected in the re-rendered frame. Useful for
603
- * debugging.
604
- *
605
- * May not do anything if the render loop is running on a separate worker.
606
- */
607
- rerender() {
608
- throw new Error('Not implemented');
609
- }
610
- }
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
+ /* eslint-disable @typescript-eslint/no-unused-vars */
21
+ import type { ShaderMap } from '../core/CoreShaderManager.js';
22
+ import type {
23
+ ExtractProps,
24
+ TextureMap,
25
+ TextureOptions,
26
+ } from '../core/CoreTextureManager.js';
27
+ import type {
28
+ INode,
29
+ INodeWritableProps,
30
+ ITextNode,
31
+ ITextNodeWritableProps,
32
+ } from './INode.js';
33
+ import type { ICoreDriver } from './ICoreDriver.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';
40
+ import { EventEmitter } from '../common/EventEmitter.js';
41
+ import { Inspector } from './Inspector.js';
42
+
43
+ /**
44
+ * An immutable reference to a specific Texture type
45
+ *
46
+ * @remarks
47
+ * See {@link TextureRef} for more details.
48
+ */
49
+ export interface SpecificTextureRef<TxType extends keyof TextureMap> {
50
+ readonly descType: 'texture';
51
+ readonly txType: TxType;
52
+ readonly props: ExtractProps<TextureMap[TxType]>;
53
+ readonly options?: Readonly<TextureOptions>;
54
+ }
55
+
56
+ type MapTextureRefs<TxType extends keyof TextureMap> =
57
+ TxType extends keyof TextureMap ? SpecificTextureRef<TxType> : never;
58
+
59
+ /**
60
+ * An immutable reference to a Texture
61
+ *
62
+ * @remarks
63
+ * This structure should only be created by the RendererMain's `createTexture`
64
+ * method. The structure is immutable and should not be modified once created.
65
+ *
66
+ * A `TextureRef` exists in the Main API Space and is used to point to an actual
67
+ * `Texture` instance in the Core API Space. The `TextureRef` is used to
68
+ * communicate with the Core API Space to create, load, and destroy the
69
+ * `Texture` instance.
70
+ *
71
+ * This type is technically a discriminated union of all possible texture types.
72
+ * If you'd like to represent a specific texture type, you can use the
73
+ * `SpecificTextureRef` generic type.
74
+ */
75
+ export type TextureRef = MapTextureRefs<keyof TextureMap>;
76
+
77
+ /**
78
+ * An immutable reference to a specific Shader type
79
+ *
80
+ * @remarks
81
+ * See {@link ShaderRef} for more details.
82
+ */
83
+ export interface SpecificShaderRef<ShType extends keyof ShaderMap> {
84
+ readonly descType: 'shader';
85
+ readonly shType: ShType;
86
+ readonly props: ExtractProps<ShaderMap[ShType]>;
87
+ }
88
+
89
+ type MapShaderRefs<ShType extends keyof ShaderMap> =
90
+ ShType extends keyof ShaderMap ? SpecificShaderRef<ShType> : never;
91
+
92
+ /**
93
+ * An immutable reference to a Shader
94
+ *
95
+ * @remarks
96
+ * This structure should only be created by the RendererMain's `createShader`
97
+ * method. The structure is immutable and should not be modified once created.
98
+ *
99
+ * A `ShaderRef` exists in the Main API Space and is used to point to an actual
100
+ * `Shader` instance in the Core API Space. The `ShaderRef` is used to
101
+ * communicate with the Core API Space to create, load, and destroy the
102
+ * `Shader` instance.
103
+ *
104
+ * This type is technically a discriminated union of all possible shader types.
105
+ * If you'd like to represent a specific shader type, you can use the
106
+ * `SpecificShaderRef` generic type.
107
+ */
108
+ export type ShaderRef = MapShaderRefs<keyof ShaderMap>;
109
+
110
+ /**
111
+ * Configuration settings for {@link RendererMain}
112
+ */
113
+ export interface RendererMainSettings {
114
+ /**
115
+ * Authored logical pixel width of the application
116
+ *
117
+ * @defaultValue `1920`
118
+ */
119
+ appWidth?: number;
120
+
121
+ /**
122
+ * Authored logical pixel height of the application
123
+ *
124
+ * @defaultValue `1080`
125
+ */
126
+ appHeight?: number;
127
+
128
+ /**
129
+ * Factor to convert app-authored logical coorindates to device logical coordinates
130
+ *
131
+ * @remarks
132
+ * This value allows auto-scaling to support larger/small resolutions than the
133
+ * app was authored for.
134
+ *
135
+ * If the app was authored for 1920x1080 and this value is 2, the app's canvas
136
+ * will be rendered at 3840x2160 logical pixels.
137
+ *
138
+ * Likewise, if the app was authored for 1920x1080 and this value is 0.66667,
139
+ * the app's canvas will be rendered at 1280x720 logical pixels.
140
+ *
141
+ * @defaultValue `1`
142
+ */
143
+ deviceLogicalPixelRatio?: number;
144
+
145
+ /**
146
+ * Factor to convert device logical coordinates to device physical coordinates
147
+ *
148
+ * @remarks
149
+ * This value allows auto-scaling to support devices with different pixel densities.
150
+ *
151
+ * This controls the number of physical pixels that are used to render each logical
152
+ * pixel. For example, if the device has a pixel density of 2, each logical pixel
153
+ * will be rendered using 2x2 physical pixels.
154
+ *
155
+ * By default, it will be set to `window.devicePixelRatio` which is the pixel
156
+ * density of the device the app is running on reported by the browser.
157
+ *
158
+ * @defaultValue `window.devicePixelRatio`
159
+ */
160
+ devicePhysicalPixelRatio?: number;
161
+
162
+ /**
163
+ * RGBA encoded number of the background to use
164
+ *
165
+ * @defaultValue `0x00000000`
166
+ */
167
+ clearColor?: number;
168
+
169
+ /**
170
+ * Path to a custom core module to use
171
+ *
172
+ * @defaultValue `null`
173
+ */
174
+ coreExtensionModule?: string | null;
175
+
176
+ /**
177
+ * Enable experimental FinalizationRegistry-based texture usage tracking
178
+ * for texture garbage collection
179
+ *
180
+ * @remarks
181
+ * By default, the Renderer uses a manual reference counting system to track
182
+ * texture usage. Textures are eventually released from the Core Texture
183
+ * Manager's Usage Cache when they are no longer referenced by any Nodes (or
184
+ * SubTextures that are referenced by nodes). This works well enough, but has
185
+ * the consequence of textures being removed from Usage Cache even if their
186
+ * references are still alive in memory. This can require a texture to be
187
+ * reloaded from the source when it is used again after being removed from
188
+ * cache.
189
+ *
190
+ * This is an experimental feature that uses a FinalizationRegistry to track
191
+ * texture usage. This causes textures to be removed from the Usage Cache only
192
+ * when their references are no longer alive in memory. Meaning a loaded texture
193
+ * will remain in the Usage Cache until it's reference is garbage collected.
194
+ *
195
+ * This feature is not enabled by default because browser support for the
196
+ * FinalizationRegistry is limited. It should NOT be enabled in production apps
197
+ * as this behavior is not guaranteed to be supported in the future. Developer
198
+ * feedback on this feature, however, is welcome.
199
+ *
200
+ * @defaultValue `false`
201
+ */
202
+ experimental_FinalizationRegistryTextureUsageTracker?: boolean;
203
+
204
+ textureCleanupOptions?: ManualCountTextureUsageTrackerOptions;
205
+
206
+ /**
207
+ * Interval in milliseconds to receive FPS updates
208
+ *
209
+ * @remarks
210
+ * If set to `0`, FPS updates will be disabled.
211
+ *
212
+ * @defaultValue `0` (disabled)
213
+ */
214
+ fpsUpdateInterval?: number;
215
+
216
+ /**
217
+ * Include context call (i.e. WebGL) information in FPS updates
218
+ *
219
+ * @remarks
220
+ * When enabled the number of calls to each context method over the
221
+ * `fpsUpdateInterval` will be included in the FPS update payload's
222
+ * `contextSpyData` property.
223
+ *
224
+ * Enabling the context spy has a serious impact on performance so only use it
225
+ * when you need to extract context call information.
226
+ *
227
+ * @defaultValue `false` (disabled)
228
+ */
229
+ enableContextSpy?: boolean;
230
+
231
+ /**
232
+ * Number or Image Workers to use
233
+ *
234
+ * @remarks
235
+ * On devices with multiple cores, this can be used to improve image loading
236
+ * as well as reduce the impact of image loading on the main thread.
237
+ * Set to 0 to disable image workers.
238
+ *
239
+ * @defaultValue `2`
240
+ */
241
+ numImageWorkers?: number;
242
+
243
+ /**
244
+ * Enable inspector
245
+ *
246
+ * @remarks
247
+ * When enabled the renderer will spawn a inspector. The inspector will
248
+ * replicate the state of the Nodes created in the renderer and allow
249
+ * inspection of the state of the nodes.
250
+ *
251
+ * @defaultValue `false` (disabled)
252
+ */
253
+ enableInspector?: boolean;
254
+ }
255
+
256
+ /**
257
+ * The Renderer Main API
258
+ *
259
+ * @remarks
260
+ * This is the primary class used to configure and operate the Renderer.
261
+ *
262
+ * It is used to create and destroy Nodes, as well as Texture and Shader
263
+ * references.
264
+ *
265
+ * Example:
266
+ * ```ts
267
+ * import { RendererMain, MainCoreDriver } from '@lightningjs/renderer';
268
+ *
269
+ * // Initialize the Renderer
270
+ * const renderer = new RendererMain(
271
+ * {
272
+ * appWidth: 1920,
273
+ * appHeight: 1080
274
+ * },
275
+ * 'app',
276
+ * new MainCoreDriver(),
277
+ * );
278
+ * ```
279
+ */
280
+ export class RendererMain extends EventEmitter {
281
+ readonly root: INode | null = null;
282
+ readonly driver: ICoreDriver;
283
+ readonly canvas: HTMLCanvasElement;
284
+ readonly settings: Readonly<Required<RendererMainSettings>>;
285
+ private inspector: Inspector | null = null;
286
+ private nodes: Map<number, INode> = new Map();
287
+ private nextTextureId = 1;
288
+
289
+ /**
290
+ * Texture Usage Tracker for Usage Based Texture Garbage Collection
291
+ *
292
+ * @remarks
293
+ * For internal use only. DO NOT ACCESS.
294
+ */
295
+ public textureTracker: TextureUsageTracker;
296
+
297
+ /**
298
+ * Constructs a new Renderer instance
299
+ *
300
+ * @param settings Renderer settings
301
+ * @param target Element ID or HTMLElement to insert the canvas into
302
+ * @param driver Core Driver to use
303
+ */
304
+ constructor(
305
+ settings: RendererMainSettings,
306
+ target: string | HTMLElement,
307
+ driver: ICoreDriver,
308
+ ) {
309
+ super();
310
+ const resolvedSettings: Required<RendererMainSettings> = {
311
+ appWidth: settings.appWidth || 1920,
312
+ appHeight: settings.appHeight || 1080,
313
+ deviceLogicalPixelRatio: settings.deviceLogicalPixelRatio || 1,
314
+ devicePhysicalPixelRatio:
315
+ settings.devicePhysicalPixelRatio || window.devicePixelRatio,
316
+ clearColor: settings.clearColor ?? 0x00000000,
317
+ coreExtensionModule: settings.coreExtensionModule || null,
318
+ experimental_FinalizationRegistryTextureUsageTracker:
319
+ settings.experimental_FinalizationRegistryTextureUsageTracker ?? false,
320
+ textureCleanupOptions: settings.textureCleanupOptions || {},
321
+ fpsUpdateInterval: settings.fpsUpdateInterval || 0,
322
+ numImageWorkers:
323
+ settings.numImageWorkers !== undefined ? settings.numImageWorkers : 2,
324
+ enableContextSpy: settings.enableContextSpy ?? false,
325
+ enableInspector: settings.enableInspector ?? false,
326
+ };
327
+ this.settings = resolvedSettings;
328
+
329
+ const {
330
+ appWidth,
331
+ appHeight,
332
+ deviceLogicalPixelRatio,
333
+ devicePhysicalPixelRatio,
334
+ enableInspector,
335
+ } = resolvedSettings;
336
+
337
+ const releaseCallback = (textureId: number) => {
338
+ this.driver.releaseTexture(textureId);
339
+ };
340
+
341
+ const useFinalizationRegistryTracker =
342
+ resolvedSettings.experimental_FinalizationRegistryTextureUsageTracker &&
343
+ typeof FinalizationRegistry === 'function';
344
+ this.textureTracker = useFinalizationRegistryTracker
345
+ ? new FinalizationRegistryTextureUsageTracker(releaseCallback)
346
+ : new ManualCountTextureUsageTracker(
347
+ releaseCallback,
348
+ this.settings.textureCleanupOptions,
349
+ );
350
+
351
+ const deviceLogicalWidth = appWidth * deviceLogicalPixelRatio;
352
+ const deviceLogicalHeight = appHeight * deviceLogicalPixelRatio;
353
+
354
+ this.driver = driver;
355
+
356
+ const canvas = document.createElement('canvas');
357
+ this.canvas = canvas;
358
+ canvas.width = deviceLogicalWidth * devicePhysicalPixelRatio;
359
+ canvas.height = deviceLogicalHeight * devicePhysicalPixelRatio;
360
+
361
+ canvas.style.width = `${deviceLogicalWidth}px`;
362
+ canvas.style.height = `${deviceLogicalHeight}px`;
363
+
364
+ let targetEl: HTMLElement | null;
365
+ if (typeof target === 'string') {
366
+ targetEl = document.getElementById(target);
367
+ } else {
368
+ targetEl = target;
369
+ }
370
+
371
+ if (!targetEl) {
372
+ throw new Error('Could not find target element');
373
+ }
374
+
375
+ // Hook up the driver's callbacks
376
+ driver.onCreateNode = (node) => {
377
+ this.nodes.set(node.id, node);
378
+ };
379
+
380
+ driver.onBeforeDestroyNode = (node) => {
381
+ this.nodes.delete(node.id);
382
+ };
383
+
384
+ driver.onFpsUpdate = (fpsData) => {
385
+ this.emit('fpsUpdate', fpsData);
386
+ };
387
+
388
+ driver.onFrameTick = (frameTickData) => {
389
+ this.emit('frameTick', frameTickData);
390
+ };
391
+
392
+ targetEl.appendChild(canvas);
393
+
394
+ if (enableInspector && !import.meta.env.PROD) {
395
+ this.inspector = new Inspector(canvas, resolvedSettings);
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Initialize the renderer
401
+ *
402
+ * @remarks
403
+ * This method must be called and resolved asyncronously before any other
404
+ * methods are called.
405
+ */
406
+ async init(): Promise<void> {
407
+ await this.driver.init(this, this.settings, this.canvas);
408
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
409
+ (this.root as INode) = this.driver.getRootNode();
410
+ }
411
+
412
+ /**
413
+ * Create a new scene graph node
414
+ *
415
+ * @remarks
416
+ * A node is the main graphical building block of the Renderer scene graph. It
417
+ * can be a container for other nodes, or it can be a leaf node that renders a
418
+ * solid color, gradient, image, or specific texture, using a specific shader.
419
+ *
420
+ * To create a text node, see {@link createTextNode}.
421
+ *
422
+ * See {@link INode} for more details.
423
+ *
424
+ * @param props
425
+ * @returns
426
+ */
427
+ createNode(props: Partial<INodeWritableProps>): INode {
428
+ if (this.inspector) {
429
+ return this.inspector.createNode(
430
+ this.driver,
431
+ this.resolveNodeDefaults(props),
432
+ );
433
+ }
434
+
435
+ return this.driver.createNode(this.resolveNodeDefaults(props));
436
+ }
437
+
438
+ /**
439
+ * Create a new scene graph text node
440
+ *
441
+ * @remarks
442
+ * A text node is the second graphical building block of the Renderer scene
443
+ * graph. It renders text using a specific text renderer that is automatically
444
+ * chosen based on the font requested and what type of fonts are installed
445
+ * into an app via a CoreExtension.
446
+ *
447
+ * See {@link ITextNode} for more details.
448
+ *
449
+ * @param props
450
+ * @returns
451
+ */
452
+ createTextNode(props: Partial<ITextNodeWritableProps>): ITextNode {
453
+ const fontSize = props.fontSize ?? 16;
454
+ const data = {
455
+ ...this.resolveNodeDefaults(props),
456
+ text: props.text ?? '',
457
+ textRendererOverride: props.textRendererOverride ?? null,
458
+ fontSize,
459
+ fontFamily: props.fontFamily ?? 'sans-serif',
460
+ fontStyle: props.fontStyle ?? 'normal',
461
+ fontWeight: props.fontWeight ?? 'normal',
462
+ fontStretch: props.fontStretch ?? 'normal',
463
+ textAlign: props.textAlign ?? 'left',
464
+ contain: props.contain ?? 'none',
465
+ scrollable: props.scrollable ?? false,
466
+ scrollY: props.scrollY ?? 0,
467
+ offsetY: props.offsetY ?? 0,
468
+ letterSpacing: props.letterSpacing ?? 0,
469
+ lineHeight: props.lineHeight ?? fontSize,
470
+ maxLines: props.maxLines ?? 0,
471
+ textBaseline: props.textBaseline ?? 'alphabetic',
472
+ verticalAlign: props.verticalAlign ?? 'top',
473
+ overflowSuffix: props.overflowSuffix ?? '...',
474
+ debug: props.debug ?? {},
475
+ };
476
+
477
+ if (this.inspector) {
478
+ return this.inspector.createTextNode(this.driver, data);
479
+ }
480
+
481
+ return this.driver.createTextNode(data);
482
+ }
483
+
484
+ /**
485
+ * Resolves the default property values for a Node
486
+ *
487
+ * @remarks
488
+ * This method is used internally by the RendererMain to resolve the default
489
+ * property values for a Node. It is exposed publicly so that it can be used
490
+ * by Core Driver implementations.
491
+ *
492
+ * @param props
493
+ * @returns
494
+ */
495
+ resolveNodeDefaults(props: Partial<INodeWritableProps>): INodeWritableProps {
496
+ const color = props.color ?? 0xffffffff;
497
+ const colorTl = props.colorTl ?? props.colorTop ?? props.colorLeft ?? color;
498
+ const colorTr =
499
+ props.colorTr ?? props.colorTop ?? props.colorRight ?? color;
500
+ const colorBl =
501
+ props.colorBl ?? props.colorBottom ?? props.colorLeft ?? color;
502
+ const colorBr =
503
+ props.colorBr ?? props.colorBottom ?? props.colorRight ?? color;
504
+
505
+ return {
506
+ x: props.x ?? 0,
507
+ y: props.y ?? 0,
508
+ width: props.width ?? 0,
509
+ height: props.height ?? 0,
510
+ alpha: props.alpha ?? 1,
511
+ clipping: props.clipping ?? false,
512
+ color,
513
+ colorTop: props.colorTop ?? color,
514
+ colorBottom: props.colorBottom ?? color,
515
+ colorLeft: props.colorLeft ?? color,
516
+ colorRight: props.colorRight ?? color,
517
+ colorBl,
518
+ colorBr,
519
+ colorTl,
520
+ colorTr,
521
+ zIndex: props.zIndex ?? 0,
522
+ zIndexLocked: props.zIndexLocked ?? 0,
523
+ parent: props.parent ?? null,
524
+ texture: props.texture ?? null,
525
+ shader: props.shader ?? null,
526
+ // Since setting the `src` will trigger a texture load, we need to set it after
527
+ // we set the texture. Otherwise, problems happen.
528
+ src: props.src ?? '',
529
+ scale: props.scale ?? null,
530
+ scaleX: props.scaleX ?? props.scale ?? 1,
531
+ scaleY: props.scaleY ?? props.scale ?? 1,
532
+ mount: props.mount ?? 0,
533
+ mountX: props.mountX ?? props.mount ?? 0,
534
+ mountY: props.mountY ?? props.mount ?? 0,
535
+ pivot: props.pivot ?? 0.5,
536
+ pivotX: props.pivotX ?? props.pivot ?? 0.5,
537
+ pivotY: props.pivotY ?? props.pivot ?? 0.5,
538
+ rotation: props.rotation ?? 0,
539
+ };
540
+ }
541
+
542
+ /**
543
+ * Destroy a node
544
+ *
545
+ * @remarks
546
+ * This method destroys a node but does not destroy its children.
547
+ *
548
+ * @param node
549
+ * @returns
550
+ */
551
+ destroyNode(node: INode) {
552
+ if (this.inspector) {
553
+ this.inspector.destroyNode(node);
554
+ }
555
+
556
+ return this.driver.destroyNode(node);
557
+ }
558
+
559
+ /**
560
+ * Create a new texture reference
561
+ *
562
+ * @remarks
563
+ * This method creates a new reference to a texture. The texture is not
564
+ * loaded until it is used on a node.
565
+ *
566
+ * It can be assigned to a node's `texture` property, or it can be used
567
+ * when creating a SubTexture.
568
+ *
569
+ * @param textureType
570
+ * @param props
571
+ * @param options
572
+ * @returns
573
+ */
574
+ createTexture<TxType extends keyof TextureMap>(
575
+ textureType: TxType,
576
+ props: SpecificTextureRef<TxType>['props'],
577
+ options?: TextureOptions,
578
+ ): SpecificTextureRef<TxType> {
579
+ const id = this.nextTextureId++;
580
+ const desc = {
581
+ descType: 'texture',
582
+ txType: textureType,
583
+ props,
584
+ options: {
585
+ ...options,
586
+ // This ID is used to identify the texture in the CoreTextureManager's
587
+ // ID Texture Map cache.
588
+ id,
589
+ },
590
+ } satisfies SpecificTextureRef<TxType>;
591
+ this.textureTracker.registerTexture(desc as TextureRef);
592
+ return desc;
593
+ }
594
+
595
+ /**
596
+ * Create a new shader reference
597
+ *
598
+ * @remarks
599
+ * This method creates a new reference to a shader. The shader is not
600
+ * loaded until it is used on a Node.
601
+ *
602
+ * It can be assigned to a Node's `shader` property.
603
+ *
604
+ * @param shaderType
605
+ * @param props
606
+ * @returns
607
+ */
608
+ createShader<ShType extends keyof ShaderMap>(
609
+ shaderType: ShType,
610
+ props?: SpecificShaderRef<ShType>['props'],
611
+ ): SpecificShaderRef<ShType> {
612
+ return {
613
+ descType: 'shader',
614
+ shType: shaderType,
615
+ props: props as SpecificShaderRef<ShType>['props'],
616
+ };
617
+ }
618
+
619
+ /**
620
+ * Get a Node by its ID
621
+ *
622
+ * @param id
623
+ * @returns
624
+ */
625
+ getNodeById(id: number): INode | null {
626
+ return this.nodes.get(id) || null;
627
+ }
628
+
629
+ toggleFreeze() {
630
+ throw new Error('Not implemented');
631
+ }
632
+
633
+ advanceFrame() {
634
+ throw new Error('Not implemented');
635
+ }
636
+
637
+ /**
638
+ * Re-render the current frame without advancing any running animations.
639
+ *
640
+ * @remarks
641
+ * Any state changes will be reflected in the re-rendered frame. Useful for
642
+ * debugging.
643
+ *
644
+ * May not do anything if the render loop is running on a separate worker.
645
+ */
646
+ rerender() {
647
+ throw new Error('Not implemented');
648
+ }
649
+ }