@codexo/exojs 0.8.3 → 0.9.0

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 (317) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +26 -5
  3. package/dist/esm/animation/Tween.d.ts +20 -3
  4. package/dist/esm/animation/Tween.js +16 -8
  5. package/dist/esm/animation/Tween.js.map +1 -1
  6. package/dist/esm/animation/TweenManager.d.ts +16 -1
  7. package/dist/esm/animation/TweenManager.js +27 -2
  8. package/dist/esm/animation/TweenManager.js.map +1 -1
  9. package/dist/esm/audio/AudioAnalyser.d.ts +2 -0
  10. package/dist/esm/audio/AudioAnalyser.js +24 -9
  11. package/dist/esm/audio/AudioAnalyser.js.map +1 -1
  12. package/dist/esm/audio/AudioBus.d.ts +1 -0
  13. package/dist/esm/audio/AudioBus.js +8 -4
  14. package/dist/esm/audio/AudioBus.js.map +1 -1
  15. package/dist/esm/audio/AudioListener.d.ts +1 -0
  16. package/dist/esm/audio/AudioListener.js +7 -5
  17. package/dist/esm/audio/AudioListener.js.map +1 -1
  18. package/dist/esm/audio/AudioManager.d.ts +8 -2
  19. package/dist/esm/audio/AudioManager.js +14 -1
  20. package/dist/esm/audio/AudioManager.js.map +1 -1
  21. package/dist/esm/audio/BeatDetector.d.ts +2 -0
  22. package/dist/esm/audio/BeatDetector.js +27 -661
  23. package/dist/esm/audio/BeatDetector.js.map +1 -1
  24. package/dist/esm/audio/Music.d.ts +1 -0
  25. package/dist/esm/audio/Music.js +7 -3
  26. package/dist/esm/audio/Music.js.map +1 -1
  27. package/dist/esm/audio/OscillatorSound.d.ts +1 -0
  28. package/dist/esm/audio/OscillatorSound.js +7 -3
  29. package/dist/esm/audio/OscillatorSound.js.map +1 -1
  30. package/dist/esm/audio/Sound.d.ts +1 -0
  31. package/dist/esm/audio/Sound.js +7 -3
  32. package/dist/esm/audio/Sound.js.map +1 -1
  33. package/dist/esm/audio/audio-context.d.ts +2 -2
  34. package/dist/esm/audio/audio-context.js +4 -4
  35. package/dist/esm/audio/audio-context.js.map +1 -1
  36. package/dist/esm/audio/filters/ChorusFilter.d.ts +1 -0
  37. package/dist/esm/audio/filters/ChorusFilter.js +7 -3
  38. package/dist/esm/audio/filters/ChorusFilter.js.map +1 -1
  39. package/dist/esm/audio/filters/CompressorFilter.d.ts +1 -0
  40. package/dist/esm/audio/filters/CompressorFilter.js +7 -3
  41. package/dist/esm/audio/filters/CompressorFilter.js.map +1 -1
  42. package/dist/esm/audio/filters/DelayFilter.d.ts +1 -0
  43. package/dist/esm/audio/filters/DelayFilter.js +7 -3
  44. package/dist/esm/audio/filters/DelayFilter.js.map +1 -1
  45. package/dist/esm/audio/filters/EqualizerFilter.d.ts +1 -0
  46. package/dist/esm/audio/filters/EqualizerFilter.js +7 -3
  47. package/dist/esm/audio/filters/EqualizerFilter.js.map +1 -1
  48. package/dist/esm/audio/filters/GranularFilter.js +1 -86
  49. package/dist/esm/audio/filters/GranularFilter.js.map +1 -1
  50. package/dist/esm/audio/filters/HighpassFilter.d.ts +1 -0
  51. package/dist/esm/audio/filters/HighpassFilter.js +7 -3
  52. package/dist/esm/audio/filters/HighpassFilter.js.map +1 -1
  53. package/dist/esm/audio/filters/LowpassFilter.d.ts +1 -0
  54. package/dist/esm/audio/filters/LowpassFilter.js +7 -3
  55. package/dist/esm/audio/filters/LowpassFilter.js.map +1 -1
  56. package/dist/esm/audio/filters/PitchShiftFilter.js +1 -71
  57. package/dist/esm/audio/filters/PitchShiftFilter.js.map +1 -1
  58. package/dist/esm/audio/filters/ReverbFilter.d.ts +1 -0
  59. package/dist/esm/audio/filters/ReverbFilter.js +7 -3
  60. package/dist/esm/audio/filters/ReverbFilter.js.map +1 -1
  61. package/dist/esm/audio/filters/VocoderFilter.js +1 -89
  62. package/dist/esm/audio/filters/VocoderFilter.js.map +1 -1
  63. package/dist/esm/audio/filters/WorkletFilter.d.ts +2 -0
  64. package/dist/esm/audio/filters/WorkletFilter.js +10 -5
  65. package/dist/esm/audio/filters/WorkletFilter.js.map +1 -1
  66. package/dist/esm/audio/index.d.ts +15 -10
  67. package/dist/esm/audio/worklet/registerWorklet.d.ts +1 -1
  68. package/dist/esm/audio/worklet/registerWorklet.js +2 -2
  69. package/dist/esm/audio/worklet/registerWorklet.js.map +1 -1
  70. package/dist/esm/audio/worklets/beat-detector.worklet.d.ts +1 -0
  71. package/dist/esm/audio/worklets/beat-detector.worklet.js +647 -0
  72. package/dist/esm/audio/worklets/beat-detector.worklet.js.map +1 -0
  73. package/dist/esm/audio/worklets/granular.worklet.d.ts +1 -0
  74. package/dist/esm/audio/worklets/granular.worklet.js +89 -0
  75. package/dist/esm/audio/worklets/granular.worklet.js.map +1 -0
  76. package/dist/esm/audio/worklets/pitch-shift.worklet.d.ts +1 -0
  77. package/dist/esm/audio/worklets/pitch-shift.worklet.js +74 -0
  78. package/dist/esm/audio/worklets/pitch-shift.worklet.js.map +1 -0
  79. package/dist/esm/audio/worklets/vocoder.worklet.d.ts +1 -0
  80. package/dist/esm/audio/worklets/vocoder.worklet.js +92 -0
  81. package/dist/esm/audio/worklets/vocoder.worklet.js.map +1 -0
  82. package/dist/esm/core/Application.d.ts +62 -24
  83. package/dist/esm/core/Application.js +116 -40
  84. package/dist/esm/core/Application.js.map +1 -1
  85. package/dist/esm/core/Color.d.ts +0 -8
  86. package/dist/esm/core/Color.js +0 -24
  87. package/dist/esm/core/Color.js.map +1 -1
  88. package/dist/esm/core/Scene.d.ts +32 -72
  89. package/dist/esm/core/Scene.js +56 -36
  90. package/dist/esm/core/Scene.js.map +1 -1
  91. package/dist/esm/core/SceneManager.d.ts +11 -25
  92. package/dist/esm/core/SceneManager.js +37 -100
  93. package/dist/esm/core/SceneManager.js.map +1 -1
  94. package/dist/esm/core/SceneNode.d.ts +28 -14
  95. package/dist/esm/core/SceneNode.js +66 -33
  96. package/dist/esm/core/SceneNode.js.map +1 -1
  97. package/dist/esm/core/Signal.d.ts +24 -28
  98. package/dist/esm/core/Signal.js +64 -50
  99. package/dist/esm/core/Signal.js.map +1 -1
  100. package/dist/esm/core/Timer.d.ts +1 -0
  101. package/dist/esm/core/Timer.js +3 -0
  102. package/dist/esm/core/Timer.js.map +1 -1
  103. package/dist/esm/debug/BoundingBoxesLayer.js +1 -1
  104. package/dist/esm/debug/BoundingBoxesLayer.js.map +1 -1
  105. package/dist/esm/debug/HitTestLayer.js +1 -1
  106. package/dist/esm/debug/HitTestLayer.js.map +1 -1
  107. package/dist/esm/debug/PerformanceLayer.js +1 -2
  108. package/dist/esm/debug/PerformanceLayer.js.map +1 -1
  109. package/dist/esm/debug/PointerStackLayer.js +1 -2
  110. package/dist/esm/debug/PointerStackLayer.js.map +1 -1
  111. package/dist/esm/debug/RenderPassInspectorLayer.js +1 -2
  112. package/dist/esm/debug/RenderPassInspectorLayer.js.map +1 -1
  113. package/dist/esm/index.js +23 -7
  114. package/dist/esm/index.js.map +1 -1
  115. package/dist/esm/input/InputManager.d.ts +1 -0
  116. package/dist/esm/input/InputManager.js +30 -3
  117. package/dist/esm/input/InputManager.js.map +1 -1
  118. package/dist/esm/input/InteractionManager.d.ts +5 -2
  119. package/dist/esm/input/InteractionManager.js +29 -18
  120. package/dist/esm/input/InteractionManager.js.map +1 -1
  121. package/dist/esm/input/Pointer.js +3 -2
  122. package/dist/esm/input/Pointer.js.map +1 -1
  123. package/dist/esm/input/internal/interactionManagerRegistry.d.ts +9 -0
  124. package/dist/esm/input/internal/interactionManagerRegistry.js +10 -0
  125. package/dist/esm/input/internal/interactionManagerRegistry.js.map +1 -0
  126. package/dist/esm/math/AbstractVector.d.ts +1 -7
  127. package/dist/esm/math/AbstractVector.js +6 -20
  128. package/dist/esm/math/AbstractVector.js.map +1 -1
  129. package/dist/esm/math/Circle.js +0 -2
  130. package/dist/esm/math/Circle.js.map +1 -1
  131. package/dist/esm/math/Collision.d.ts +9 -3
  132. package/dist/esm/math/Ellipse.d.ts +2 -5
  133. package/dist/esm/math/Ellipse.js +10 -7
  134. package/dist/esm/math/Ellipse.js.map +1 -1
  135. package/dist/esm/math/ObservableVector.d.ts +1 -1
  136. package/dist/esm/math/ObservableVector.js +3 -3
  137. package/dist/esm/math/ObservableVector.js.map +1 -1
  138. package/dist/esm/math/Polygon.d.ts +0 -2
  139. package/dist/esm/math/Polygon.js +1 -9
  140. package/dist/esm/math/Polygon.js.map +1 -1
  141. package/dist/esm/math/Rectangle.js +0 -2
  142. package/dist/esm/math/Rectangle.js.map +1 -1
  143. package/dist/esm/math/collision-detection.d.ts +19 -4
  144. package/dist/esm/math/collision-detection.js +61 -4
  145. package/dist/esm/math/collision-detection.js.map +1 -1
  146. package/dist/esm/math/swept-collision.d.ts +16 -12
  147. package/dist/esm/math/swept-collision.js +109 -19
  148. package/dist/esm/math/swept-collision.js.map +1 -1
  149. package/dist/esm/particles/ParticleSystem.js +1 -1
  150. package/dist/esm/particles/ParticleSystem.js.map +1 -1
  151. package/dist/esm/particles/index.d.ts +1 -0
  152. package/dist/esm/rendering/CallbackRenderPass.d.ts +1 -0
  153. package/dist/esm/rendering/CallbackRenderPass.js +1 -0
  154. package/dist/esm/rendering/CallbackRenderPass.js.map +1 -1
  155. package/dist/esm/rendering/Container.d.ts +2 -1
  156. package/dist/esm/rendering/Container.js +17 -16
  157. package/dist/esm/rendering/Container.js.map +1 -1
  158. package/dist/esm/rendering/RenderBackend.d.ts +1 -0
  159. package/dist/esm/rendering/RenderNode.d.ts +1 -3
  160. package/dist/esm/rendering/RenderNode.js +12 -19
  161. package/dist/esm/rendering/RenderNode.js.map +1 -1
  162. package/dist/esm/rendering/RenderPass.d.ts +1 -0
  163. package/dist/esm/rendering/RenderStats.d.ts +9 -0
  164. package/dist/esm/rendering/RenderStats.js +2 -0
  165. package/dist/esm/rendering/RenderStats.js.map +1 -1
  166. package/dist/esm/rendering/RendererRegistry.d.ts +1 -0
  167. package/dist/esm/rendering/RendererRegistry.js +1 -0
  168. package/dist/esm/rendering/RendererRegistry.js.map +1 -1
  169. package/dist/esm/rendering/View.d.ts +23 -0
  170. package/dist/esm/rendering/View.js +42 -0
  171. package/dist/esm/rendering/View.js.map +1 -1
  172. package/dist/esm/rendering/index.d.ts +89 -59
  173. package/dist/esm/rendering/mesh/Mesh.js +1 -1
  174. package/dist/esm/rendering/mesh/Mesh.js.map +1 -1
  175. package/dist/esm/rendering/mesh/MeshShader.d.ts +1 -0
  176. package/dist/esm/rendering/mesh/MeshShader.js +1 -0
  177. package/dist/esm/rendering/mesh/MeshShader.js.map +1 -1
  178. package/dist/esm/rendering/shader/Shader.d.ts +1 -0
  179. package/dist/esm/rendering/shader/Shader.js +1 -0
  180. package/dist/esm/rendering/shader/Shader.js.map +1 -1
  181. package/dist/esm/rendering/shader/ShaderUniform.d.ts +1 -0
  182. package/dist/esm/rendering/shader/ShaderUniform.js +1 -0
  183. package/dist/esm/rendering/shader/ShaderUniform.js.map +1 -1
  184. package/dist/esm/rendering/sprite/Sprite.d.ts +3 -2
  185. package/dist/esm/rendering/sprite/Sprite.js +15 -13
  186. package/dist/esm/rendering/sprite/Sprite.js.map +1 -1
  187. package/dist/esm/rendering/text/AbstractText.d.ts +36 -0
  188. package/dist/esm/rendering/text/AbstractText.js +49 -0
  189. package/dist/esm/rendering/text/AbstractText.js.map +1 -0
  190. package/dist/esm/rendering/text/BitmapText.d.ts +97 -0
  191. package/dist/esm/rendering/text/BitmapText.js +220 -0
  192. package/dist/esm/rendering/text/BitmapText.js.map +1 -0
  193. package/dist/esm/rendering/text/BmFont.d.ts +50 -0
  194. package/dist/esm/rendering/text/BmFont.js +24 -0
  195. package/dist/esm/rendering/text/BmFont.js.map +1 -0
  196. package/dist/esm/rendering/text/GlyphAtlas.d.ts +104 -0
  197. package/dist/esm/rendering/text/GlyphAtlas.js +347 -0
  198. package/dist/esm/rendering/text/GlyphAtlas.js.map +1 -0
  199. package/dist/esm/rendering/text/GlyphAtlasPool.d.ts +40 -0
  200. package/dist/esm/rendering/text/GlyphAtlasPool.js +67 -0
  201. package/dist/esm/rendering/text/GlyphAtlasPool.js.map +1 -0
  202. package/dist/esm/rendering/text/GlyphSdf.d.ts +92 -0
  203. package/dist/esm/rendering/text/GlyphSdf.js +220 -0
  204. package/dist/esm/rendering/text/GlyphSdf.js.map +1 -0
  205. package/dist/esm/rendering/text/HTMLText.d.ts +107 -0
  206. package/dist/esm/rendering/text/HTMLText.js +284 -0
  207. package/dist/esm/rendering/text/HTMLText.js.map +1 -0
  208. package/dist/esm/rendering/text/LayoutOptions.d.ts +30 -0
  209. package/dist/esm/rendering/text/Text.d.ts +89 -20
  210. package/dist/esm/rendering/text/Text.js +176 -101
  211. package/dist/esm/rendering/text/Text.js.map +1 -1
  212. package/dist/esm/rendering/text/TextLayout.d.ts +20 -8
  213. package/dist/esm/rendering/text/TextLayout.js +234 -25
  214. package/dist/esm/rendering/text/TextLayout.js.map +1 -1
  215. package/dist/esm/rendering/text/TextStyle.d.ts +154 -87
  216. package/dist/esm/rendering/text/TextStyle.js +257 -203
  217. package/dist/esm/rendering/text/TextStyle.js.map +1 -1
  218. package/dist/esm/rendering/text/types.d.ts +73 -13
  219. package/dist/esm/rendering/texture/Texture.d.ts +1 -0
  220. package/dist/esm/rendering/texture/Texture.js +1 -0
  221. package/dist/esm/rendering/texture/Texture.js.map +1 -1
  222. package/dist/esm/rendering/types.d.ts +3 -1
  223. package/dist/esm/rendering/types.js +2 -0
  224. package/dist/esm/rendering/types.js.map +1 -1
  225. package/dist/esm/rendering/video/Video.d.ts +2 -1
  226. package/dist/esm/rendering/video/Video.js +9 -5
  227. package/dist/esm/rendering/video/Video.js.map +1 -1
  228. package/dist/esm/rendering/webgl2/WebGl2Backend.js +41 -5
  229. package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -1
  230. package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js +4 -4
  231. package/dist/esm/rendering/webgl2/WebGl2MeshRenderer.js.map +1 -1
  232. package/dist/esm/rendering/webgl2/WebGl2TextRenderer.d.ts +56 -0
  233. package/dist/esm/rendering/webgl2/WebGl2TextRenderer.js +482 -0
  234. package/dist/esm/rendering/webgl2/WebGl2TextRenderer.js.map +1 -0
  235. package/dist/esm/rendering/webgl2/glsl/text-color.frag.js +4 -0
  236. package/dist/esm/rendering/webgl2/glsl/text-color.frag.js.map +1 -0
  237. package/dist/esm/rendering/webgl2/glsl/text-msdf.frag.js +4 -0
  238. package/dist/esm/rendering/webgl2/glsl/text-msdf.frag.js.map +1 -0
  239. package/dist/esm/rendering/webgl2/glsl/text-sdf.frag.js +4 -0
  240. package/dist/esm/rendering/webgl2/glsl/text-sdf.frag.js.map +1 -0
  241. package/dist/esm/rendering/webgl2/glsl/text.vert.js +4 -0
  242. package/dist/esm/rendering/webgl2/glsl/text.vert.js.map +1 -0
  243. package/dist/esm/rendering/webgpu/WebGpuBackend.js +16 -8
  244. package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -1
  245. package/dist/esm/rendering/webgpu/WebGpuBlendState.js +26 -0
  246. package/dist/esm/rendering/webgpu/WebGpuBlendState.js.map +1 -1
  247. package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.d.ts +2 -2
  248. package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js +23 -15
  249. package/dist/esm/rendering/webgpu/WebGpuMeshRenderer.js.map +1 -1
  250. package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.js +9 -1
  251. package/dist/esm/rendering/webgpu/WebGpuSpriteRenderer.js.map +1 -1
  252. package/dist/esm/rendering/webgpu/WebGpuTextRenderer.d.ts +70 -0
  253. package/dist/esm/rendering/webgpu/WebGpuTextRenderer.js +773 -0
  254. package/dist/esm/rendering/webgpu/WebGpuTextRenderer.js.map +1 -0
  255. package/dist/esm/rendering/webgpu/compute/WebGpuComputePipeline.js +96 -0
  256. package/dist/esm/rendering/webgpu/compute/WebGpuComputePipeline.js.map +1 -0
  257. package/dist/esm/rendering/webgpu/compute/WebGpuStorageBuffer.js +68 -0
  258. package/dist/esm/rendering/webgpu/compute/WebGpuStorageBuffer.js.map +1 -0
  259. package/dist/esm/resources/Asset.d.ts +23 -0
  260. package/dist/esm/resources/Asset.js +23 -0
  261. package/dist/esm/resources/Asset.js.map +1 -0
  262. package/dist/esm/resources/AssetDefinitions.d.ts +137 -0
  263. package/dist/esm/resources/Assets.d.ts +35 -0
  264. package/dist/esm/resources/Assets.js +32 -0
  265. package/dist/esm/resources/Assets.js.map +1 -0
  266. package/dist/esm/resources/IndexedDbDatabase.js +17 -1
  267. package/dist/esm/resources/IndexedDbDatabase.js.map +1 -1
  268. package/dist/esm/resources/IndexedDbStore.js +17 -1
  269. package/dist/esm/resources/IndexedDbStore.js.map +1 -1
  270. package/dist/esm/resources/Loader.d.ts +244 -18
  271. package/dist/esm/resources/Loader.js +456 -50
  272. package/dist/esm/resources/Loader.js.map +1 -1
  273. package/dist/esm/resources/LoadingQueue.d.ts +28 -0
  274. package/dist/esm/resources/LoadingQueue.js +59 -0
  275. package/dist/esm/resources/LoadingQueue.js.map +1 -0
  276. package/dist/esm/resources/factories/BmFontFactory.d.ts +25 -0
  277. package/dist/esm/resources/factories/BmFontFactory.js +96 -0
  278. package/dist/esm/resources/factories/BmFontFactory.js.map +1 -0
  279. package/dist/esm/resources/factories/CsvFactory.d.ts +35 -0
  280. package/dist/esm/resources/factories/CsvFactory.js +87 -0
  281. package/dist/esm/resources/factories/CsvFactory.js.map +1 -0
  282. package/dist/esm/resources/factories/MusicFactory.d.ts +8 -2
  283. package/dist/esm/resources/factories/MusicFactory.js +25 -14
  284. package/dist/esm/resources/factories/MusicFactory.js.map +1 -1
  285. package/dist/esm/resources/factories/SoundFactory.d.ts +2 -2
  286. package/dist/esm/resources/factories/SoundFactory.js.map +1 -1
  287. package/dist/esm/resources/factories/SubtitleFactory.d.ts +28 -0
  288. package/dist/esm/resources/factories/SubtitleFactory.js +203 -0
  289. package/dist/esm/resources/factories/SubtitleFactory.js.map +1 -0
  290. package/dist/esm/resources/factories/SvgFactory.d.ts +18 -1
  291. package/dist/esm/resources/factories/SvgFactory.js +21 -2
  292. package/dist/esm/resources/factories/SvgFactory.js.map +1 -1
  293. package/dist/esm/resources/factories/VideoFactory.d.ts +8 -2
  294. package/dist/esm/resources/factories/VideoFactory.js +27 -20
  295. package/dist/esm/resources/factories/VideoFactory.js.map +1 -1
  296. package/dist/esm/resources/factories/XmlFactory.d.ts +24 -0
  297. package/dist/esm/resources/factories/XmlFactory.js +37 -0
  298. package/dist/esm/resources/factories/XmlFactory.js.map +1 -0
  299. package/dist/esm/resources/index.d.ts +8 -1
  300. package/dist/esm/resources/tokens.d.ts +49 -3
  301. package/dist/esm/resources/tokens.js +50 -4
  302. package/dist/esm/resources/tokens.js.map +1 -1
  303. package/dist/exo.esm.js +6635 -2550
  304. package/dist/exo.esm.js.map +1 -1
  305. package/package.json +19 -6
  306. package/dist/esm/input/interaction-hooks.d.ts +0 -34
  307. package/dist/esm/input/interaction-hooks.js +0 -35
  308. package/dist/esm/input/interaction-hooks.js.map +0 -1
  309. package/dist/esm/rendering/text/DynamicGlyphAtlas.d.ts +0 -33
  310. package/dist/esm/rendering/text/DynamicGlyphAtlas.js +0 -134
  311. package/dist/esm/rendering/text/DynamicGlyphAtlas.js.map +0 -1
  312. package/dist/esm/rendering/text/atlas-singleton.d.ts +0 -7
  313. package/dist/esm/rendering/text/atlas-singleton.js +0 -17
  314. package/dist/esm/rendering/text/atlas-singleton.js.map +0 -1
  315. package/dist/esm/resources/factories/VttFactory.d.ts +0 -24
  316. package/dist/esm/resources/factories/VttFactory.js +0 -158
  317. package/dist/esm/resources/factories/VttFactory.js.map +0 -1
@@ -0,0 +1,773 @@
1
+ import '../../core/Color.js';
2
+ import { BlendModes } from '../types.js';
3
+ import '../../core/SceneNode.js';
4
+ import '../../core/Time.js';
5
+ import '../../math/Size.js';
6
+ import '../../math/Rectangle.js';
7
+ import '../texture/RenderTexture.js';
8
+ import '../View.js';
9
+ import { Text } from '../text/Text.js';
10
+ import { AbstractWebGpuRenderer } from './AbstractWebGpuRenderer.js';
11
+ import { getWebGpuBlendState } from './WebGpuBlendState.js';
12
+
13
+ /// <reference types="@webgpu/types" />
14
+ // ── Node data layout (identical to WebGl2TextRenderer) ───────────────────────
15
+ //
16
+ // Storage buffer: array<vec4<f32>> — 10 entries per node.
17
+ //
18
+ // [ni*10+0]: (a, c, 0, tx) transform col 0+2
19
+ // [ni*10+1]: (b, d, 0, ty) transform col 1+2
20
+ // [ni*10+2]: (r, g, b, a ) fillColor
21
+ // [ni*10+3]: (r, g, b, a ) outlineColor
22
+ // [ni*10+4]: (outlineMin, shadowAlpha, softness, gradientEnabled)
23
+ // [ni*10+5]: (r, g, b, a ) shadowColor
24
+ // [ni*10+6]: (shadowOffX_px, shadowOffY_px, gradientVertical, 0)
25
+ // [ni*10+7]: (r, g, b, a ) gradientTop
26
+ // [ni*10+8]: (r, g, b, a ) gradientBottom
27
+ // [ni*10+9]: (minX, minY, w, h) text block bounds
28
+ const nodeTexels = 10;
29
+ const nodeFloats = nodeTexels * 4;
30
+ // Per-vertex layout (20 bytes): pos f32x2 + uv f32x2 + nodeIndex f32
31
+ const vertexStrideBytes = 20;
32
+ const vertexStrideWords = vertexStrideBytes / 4;
33
+ const initialVertexCapacity = 256;
34
+ const initialIndexCapacity = 384;
35
+ const initialNodeCapacity = 32;
36
+ // FrameUniforms: 3 × vec4<f32> = 48 bytes (projection mat3x3 column-major)
37
+ const projectionBytes = 48;
38
+ // ── WGSL: shared vertex + three fragment entry points ────────────────────────
39
+ const textShaderSource = `
40
+ struct FrameUniforms {
41
+ projCol0 : vec4<f32>,
42
+ projCol1 : vec4<f32>,
43
+ projCol2 : vec4<f32>,
44
+ };
45
+
46
+ @group(0) @binding(0) var<uniform> frame : FrameUniforms;
47
+ @group(0) @binding(1) var<storage, read> nodes : array<vec4<f32>>;
48
+
49
+ @group(1) @binding(0) var atlasTexture : texture_2d<f32>;
50
+ @group(1) @binding(1) var atlasSampler : sampler;
51
+
52
+ struct VertexInput {
53
+ @location(0) position : vec2<f32>,
54
+ @location(1) texcoord : vec2<f32>,
55
+ @location(2) nodeIndex : f32,
56
+ };
57
+
58
+ struct VertexOutput {
59
+ @builtin(position) clipPos : vec4<f32>,
60
+ @location(0) texcoord : vec2<f32>,
61
+ @location(1) gradUV : vec2<f32>,
62
+ @location(2) @interpolate(flat) nodeIdx : u32,
63
+ };
64
+
65
+ @vertex
66
+ fn vertexMain(input: VertexInput) -> VertexOutput {
67
+ let ni = u32(input.nodeIndex);
68
+ let base = ni * 10u;
69
+
70
+ let t0 = nodes[base + 0u];
71
+ let t1 = nodes[base + 1u];
72
+ let t9 = nodes[base + 9u];
73
+
74
+ let proj = mat3x3<f32>(
75
+ frame.projCol0.xyz,
76
+ frame.projCol1.xyz,
77
+ frame.projCol2.xyz,
78
+ );
79
+ let xf = mat3x3<f32>(
80
+ vec3<f32>(t0.x, t0.y, 0.0),
81
+ vec3<f32>(t1.x, t1.y, 0.0),
82
+ vec3<f32>(t0.w, t1.w, 1.0),
83
+ );
84
+
85
+ let worldPos = proj * xf * vec3<f32>(input.position, 1.0);
86
+
87
+ let bSize = t9.zw;
88
+ var gradUV = vec2<f32>(0.0);
89
+ if (bSize.x > 0.0 && bSize.y > 0.0) {
90
+ gradUV = clamp((input.position - t9.xy) / bSize, vec2<f32>(0.0), vec2<f32>(1.0));
91
+ }
92
+
93
+ var out: VertexOutput;
94
+ out.clipPos = vec4<f32>(worldPos.xy, 0.0, 1.0);
95
+ out.texcoord = input.texcoord;
96
+ out.gradUV = gradUV;
97
+ out.nodeIdx = ni;
98
+ return out;
99
+ }
100
+
101
+ // ── SDF (R8 atlas) ────────────────────────────────────────────────────────────
102
+
103
+ @fragment
104
+ fn fragmentSdf(in: VertexOutput) -> @location(0) vec4<f32> {
105
+ let ni = in.nodeIdx;
106
+ let base = ni * 10u;
107
+
108
+ let tFill = nodes[base + 2u];
109
+ let tOutline = nodes[base + 3u];
110
+ let tParams = nodes[base + 4u];
111
+ let tShadow = nodes[base + 5u];
112
+ let tShadow2 = nodes[base + 6u];
113
+ let tGradTop = nodes[base + 7u];
114
+ let tGradBot = nodes[base + 8u];
115
+
116
+ let outlineMin = tParams.x;
117
+ let shadowAlpha = tParams.y;
118
+ let soft = max(tParams.z, 0.001);
119
+ let gradEnabled = tParams.w;
120
+ let pageSize = f32(textureDimensions(atlasTexture, 0).x);
121
+ let shadowOffset = tShadow2.xy / pageSize;
122
+ let gradVertical = tShadow2.z;
123
+
124
+ let sd = textureSample(atlasTexture, atlasSampler, in.texcoord).r;
125
+ let fill = smoothstep(0.5 - soft, 0.5 + soft, sd);
126
+
127
+ let shadowSd = textureSample(atlasTexture, atlasSampler, in.texcoord - shadowOffset).r;
128
+
129
+ let outline = select(0.0,
130
+ smoothstep(outlineMin - soft, outlineMin + soft, sd) * (1.0 - fill),
131
+ outlineMin < 0.5);
132
+
133
+ let shadow = smoothstep(0.5 - soft, 0.5 + soft, shadowSd)
134
+ * shadowAlpha * (1.0 - fill) * (1.0 - outline);
135
+
136
+ var fillColor : vec4<f32>;
137
+ if (gradEnabled > 0.5) {
138
+ let t = select(in.gradUV.x, in.gradUV.y, gradVertical > 0.5);
139
+ fillColor = mix(tGradBot, tGradTop, t);
140
+ } else {
141
+ fillColor = tFill;
142
+ }
143
+
144
+ return fillColor * fill + tOutline * outline + tShadow * shadow;
145
+ }
146
+
147
+ // ── MSDF (RGB atlas) ──────────────────────────────────────────────────────────
148
+
149
+ fn median3(r: f32, g: f32, b: f32) -> f32 {
150
+ return max(min(r, g), min(max(r, g), b));
151
+ }
152
+
153
+ @fragment
154
+ fn fragmentMsdf(in: VertexOutput) -> @location(0) vec4<f32> {
155
+ let ni = in.nodeIdx;
156
+ let base = ni * 10u;
157
+
158
+ let tFill = nodes[base + 2u];
159
+ let tOutline = nodes[base + 3u];
160
+ let tParams = nodes[base + 4u];
161
+ let tShadow = nodes[base + 5u];
162
+ let tShadow2 = nodes[base + 6u];
163
+ let tGradTop = nodes[base + 7u];
164
+ let tGradBot = nodes[base + 8u];
165
+
166
+ let outlineMin = tParams.x;
167
+ let shadowAlpha = tParams.y;
168
+ let soft = max(tParams.z, 0.001);
169
+ let gradEnabled = tParams.w;
170
+ let pageSize = f32(textureDimensions(atlasTexture, 0).x);
171
+ let shadowOffset = tShadow2.xy / pageSize;
172
+ let gradVertical = tShadow2.z;
173
+
174
+ let msd = textureSample(atlasTexture, atlasSampler, in.texcoord).rgb;
175
+ let sd = median3(msd.r, msd.g, msd.b);
176
+ let fill = smoothstep(0.5 - soft, 0.5 + soft, sd);
177
+
178
+ let shadowMsd = textureSample(atlasTexture, atlasSampler, in.texcoord - shadowOffset).rgb;
179
+ let shadowSd = median3(shadowMsd.r, shadowMsd.g, shadowMsd.b);
180
+
181
+ let outline = select(0.0,
182
+ smoothstep(outlineMin - soft, outlineMin + soft, sd) * (1.0 - fill),
183
+ outlineMin < 0.5);
184
+
185
+ let shadow = smoothstep(0.5 - soft, 0.5 + soft, shadowSd)
186
+ * shadowAlpha * (1.0 - fill) * (1.0 - outline);
187
+
188
+ var fillColor : vec4<f32>;
189
+ if (gradEnabled > 0.5) {
190
+ let t = select(in.gradUV.x, in.gradUV.y, gradVertical > 0.5);
191
+ fillColor = mix(tGradBot, tGradTop, t);
192
+ } else {
193
+ fillColor = tFill;
194
+ }
195
+
196
+ return fillColor * fill + tOutline * outline + tShadow * shadow;
197
+ }
198
+
199
+ // ── Color (RGBA atlas) ────────────────────────────────────────────────────────
200
+
201
+ @fragment
202
+ fn fragmentColor(in: VertexOutput) -> @location(0) vec4<f32> {
203
+ let ni = in.nodeIdx;
204
+ let base = ni * 10u;
205
+ let tint = nodes[base + 2u];
206
+ let sample = textureSample(atlasTexture, atlasSampler, in.texcoord);
207
+ return sample * tint;
208
+ }
209
+ `;
210
+ /**
211
+ * WebGPU renderer for {@link Text} and {@link BitmapText} nodes.
212
+ *
213
+ * Mirrors {@link WebGl2TextRenderer}: per-node style data is packed once per
214
+ * flush into a `var<storage, read>` buffer, three specialised WGSL fragment
215
+ * variants handle SDF / MSDF / colour-atlas glyphs, and quads are sorted and
216
+ * batched by (shaderType, atlasPage) to minimise draw calls within a single
217
+ * render pass.
218
+ */
219
+ class WebGpuTextRenderer extends AbstractWebGpuRenderer {
220
+ _device = null;
221
+ _shaderModule = null;
222
+ _frameBindGroupLayout = null;
223
+ _textureBindGroupLayout = null;
224
+ _pipelineLayout = null;
225
+ _pipelines = new Map();
226
+ // Weak cache: avoids retaining transient atlas textures via strong keys.
227
+ _texBindGroups = new WeakMap();
228
+ _projBuffer = null;
229
+ _nodeBuffer = null;
230
+ _vertexBuffer = null;
231
+ _indexBuffer = null;
232
+ _nodeBufferCapacity = 0;
233
+ _vertexBufferCapacity = 0;
234
+ _indexBufferCapacity = 0;
235
+ _frameBindGroup = null;
236
+ _frameBindGroupDirty = true;
237
+ // CPU-side working arrays
238
+ _vertexCapacity = initialVertexCapacity;
239
+ _indexCapacity = initialIndexCapacity;
240
+ _vertexData = new ArrayBuffer(initialVertexCapacity * vertexStrideBytes);
241
+ _float32View = new Float32Array(this._vertexData);
242
+ _indexData = new Uint16Array(initialIndexCapacity);
243
+ _projData = new Float32Array(projectionBytes / 4);
244
+ _nodeDataArray = new Float32Array(initialNodeCapacity * nodeFloats);
245
+ _nodeCapacity = initialNodeCapacity;
246
+ _nodeCount = 0;
247
+ _pendingQuads = [];
248
+ _nodeIndexMap = new Map();
249
+ _textureKeyMap = new Map();
250
+ _textureKeyCounter = 0;
251
+ // ── Public API ──────────────────────────────────────────────────────────────
252
+ render(node) {
253
+ if (!this._device)
254
+ throw new Error('WebGpuTextRenderer is not connected to a backend.');
255
+ if (node instanceof Text) {
256
+ this._collectText(node);
257
+ }
258
+ else {
259
+ this._collectBitmapText(node);
260
+ }
261
+ }
262
+ flush() {
263
+ if (this._pendingQuads.length === 0) {
264
+ this._resetFrameState();
265
+ return;
266
+ }
267
+ const backend = this.getBackend();
268
+ const device = this._device;
269
+ // Assign stable sort keys to atlas textures seen this flush
270
+ for (const pq of this._pendingQuads) {
271
+ if (!this._textureKeyMap.has(pq.atlasTexture)) {
272
+ this._textureKeyMap.set(pq.atlasTexture, this._textureKeyCounter++);
273
+ }
274
+ }
275
+ // Sort by (shaderType, atlasTexture) so equal-key quads are contiguous
276
+ this._pendingQuads.sort((a, b) => {
277
+ const sc = a.shaderType.localeCompare(b.shaderType);
278
+ if (sc !== 0)
279
+ return sc;
280
+ return (this._textureKeyMap.get(a.atlasTexture) ?? 0) - (this._textureKeyMap.get(b.atlasTexture) ?? 0);
281
+ });
282
+ // Upload projection (3 × vec4<f32> = column-major mat3x3)
283
+ const m = backend.view.getTransform().toArray(false);
284
+ this._projData[0] = m[0];
285
+ this._projData[1] = m[1];
286
+ this._projData[2] = m[2];
287
+ this._projData[3] = 0;
288
+ this._projData[4] = m[3];
289
+ this._projData[5] = m[4];
290
+ this._projData[6] = m[5];
291
+ this._projData[7] = 0;
292
+ this._projData[8] = m[6];
293
+ this._projData[9] = m[7];
294
+ this._projData[10] = m[8];
295
+ this._projData[11] = 0;
296
+ device.queue.writeBuffer(this._projBuffer, 0, this._projData.buffer, 0, projectionBytes);
297
+ // Upload per-node style data (may reallocate the storage buffer)
298
+ this._uploadNodeBuffer(device);
299
+ // Build interleaved vertex/index data for all batches in one pass
300
+ const quads = this._pendingQuads;
301
+ const batches = [];
302
+ let totalV = 0, totalI = 0;
303
+ for (const pq of quads) {
304
+ totalV += pq.quads.quadCount * 4;
305
+ totalI += pq.quads.indices.length;
306
+ }
307
+ this._ensureVertexCapacity(totalV);
308
+ this._ensureIndexCapacity(totalI);
309
+ let packedV = 0, packedI = 0, qi = 0;
310
+ while (qi < quads.length) {
311
+ const first = quads[qi];
312
+ const firstTextureKey = this._textureKeyMap.get(first.atlasTexture);
313
+ let qj = qi + 1;
314
+ while (qj < quads.length) {
315
+ const pq = quads[qj];
316
+ if (pq.shaderType !== first.shaderType || this._textureKeyMap.get(pq.atlasTexture) !== firstTextureKey)
317
+ break;
318
+ qj++;
319
+ }
320
+ const batchFirstIndex = packedI;
321
+ let batchIndexCount = 0;
322
+ for (let k = qi; k < qj; k++) {
323
+ const { quads: batch, nodeIndex } = quads[k];
324
+ const qVerts = batch.quadCount * 4;
325
+ const { vertices, uvs, indices } = batch;
326
+ for (let v = 0; v < qVerts; v++) {
327
+ const w = (packedV + v) * vertexStrideWords;
328
+ const vp = v * 2;
329
+ this._float32View[w + 0] = vertices[vp];
330
+ this._float32View[w + 1] = vertices[vp + 1];
331
+ this._float32View[w + 2] = uvs[vp];
332
+ this._float32View[w + 3] = uvs[vp + 1];
333
+ this._float32View[w + 4] = nodeIndex;
334
+ }
335
+ for (let x = 0; x < indices.length; x++) {
336
+ this._indexData[packedI + x] = indices[x] + packedV;
337
+ }
338
+ packedV += qVerts;
339
+ packedI += indices.length;
340
+ batchIndexCount += indices.length;
341
+ }
342
+ batches.push({
343
+ shaderType: first.shaderType,
344
+ atlasTexture: first.atlasTexture,
345
+ firstIndex: batchFirstIndex,
346
+ indexCount: batchIndexCount,
347
+ });
348
+ qi = qj;
349
+ }
350
+ // Upload vertex/index buffers (reallocate GPU side when needed)
351
+ this._ensureGpuVertexBuffer(device, packedV);
352
+ this._ensureGpuIndexBuffer(device, packedI);
353
+ device.queue.writeBuffer(this._vertexBuffer, 0, this._vertexData, 0, packedV * vertexStrideBytes);
354
+ device.queue.writeBuffer(this._indexBuffer, 0, this._indexData.buffer, 0, packedI * 2);
355
+ const scissor = backend.getScissorRect();
356
+ const format = backend.renderTargetFormat;
357
+ const frameBindGroup = this._getFrameBindGroup(device);
358
+ const encoder = device.createCommandEncoder({ label: 'WebGpuTextRenderer' });
359
+ const pass = encoder.beginRenderPass({
360
+ colorAttachments: [backend.createColorAttachment()],
361
+ label: 'WebGpuTextRenderer pass',
362
+ });
363
+ backend.stats.renderPasses++;
364
+ if (scissor !== null) {
365
+ pass.setScissorRect(scissor.x, scissor.y, scissor.width, scissor.height);
366
+ }
367
+ pass.setVertexBuffer(0, this._vertexBuffer);
368
+ pass.setIndexBuffer(this._indexBuffer, 'uint16');
369
+ let lastShaderType = null;
370
+ let lastTexture = null;
371
+ for (const batch of batches) {
372
+ if (batch.shaderType !== lastShaderType) {
373
+ pass.setPipeline(this._getPipeline(batch.shaderType, format));
374
+ pass.setBindGroup(0, frameBindGroup);
375
+ lastShaderType = batch.shaderType;
376
+ }
377
+ if (batch.atlasTexture !== lastTexture) {
378
+ pass.setBindGroup(1, this._getTexBindGroup(device, backend, batch.atlasTexture));
379
+ lastTexture = batch.atlasTexture;
380
+ }
381
+ pass.drawIndexed(batch.indexCount, 1, batch.firstIndex, 0, 0);
382
+ backend.stats.batches++;
383
+ backend.stats.drawCalls++;
384
+ }
385
+ pass.end();
386
+ backend.submit(encoder.finish());
387
+ this._resetFrameState();
388
+ }
389
+ destroy() {
390
+ this.disconnect();
391
+ }
392
+ /**
393
+ * Pre-create render pipelines for every (shaderType × targetFormat) pair
394
+ * asynchronously. Called from the backend init path so first-frame draws
395
+ * do not block on synchronous pipeline compilation.
396
+ */
397
+ async prewarmPipelines(formats) {
398
+ const device = this._device;
399
+ if (!device || !this._shaderModule || !this._pipelineLayout)
400
+ return;
401
+ if (typeof device.createRenderPipelineAsync !== 'function')
402
+ return;
403
+ const shaderTypes = ['sdf', 'msdf', 'color'];
404
+ const promises = [];
405
+ for (const shaderType of shaderTypes) {
406
+ for (const format of formats) {
407
+ const key = `${shaderType}:${format}`;
408
+ if (this._pipelines.has(key))
409
+ continue;
410
+ promises.push(device.createRenderPipelineAsync(this._buildPipelineDescriptor(shaderType, format)).then(pipeline => {
411
+ this._pipelines.set(key, pipeline);
412
+ }));
413
+ }
414
+ }
415
+ await Promise.all(promises);
416
+ }
417
+ // ── Connection lifecycle ─────────────────────────────────────────────────
418
+ onConnect(backend) {
419
+ const device = backend.device;
420
+ this._device = device;
421
+ this._shaderModule = device.createShaderModule({
422
+ label: 'WebGpuTextRenderer',
423
+ code: textShaderSource,
424
+ });
425
+ this._frameBindGroupLayout = device.createBindGroupLayout({
426
+ label: 'WebGpuTextRenderer/frame',
427
+ entries: [
428
+ {
429
+ binding: 0,
430
+ visibility: GPUShaderStage.VERTEX,
431
+ buffer: { type: 'uniform' },
432
+ },
433
+ {
434
+ binding: 1,
435
+ visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
436
+ buffer: { type: 'read-only-storage' },
437
+ },
438
+ ],
439
+ });
440
+ this._textureBindGroupLayout = device.createBindGroupLayout({
441
+ label: 'WebGpuTextRenderer/texture',
442
+ entries: [
443
+ {
444
+ binding: 0,
445
+ visibility: GPUShaderStage.FRAGMENT,
446
+ texture: { sampleType: 'float' },
447
+ },
448
+ {
449
+ binding: 1,
450
+ visibility: GPUShaderStage.FRAGMENT,
451
+ sampler: { type: 'filtering' },
452
+ },
453
+ ],
454
+ });
455
+ this._pipelineLayout = device.createPipelineLayout({
456
+ label: 'WebGpuTextRenderer',
457
+ bindGroupLayouts: [this._frameBindGroupLayout, this._textureBindGroupLayout],
458
+ });
459
+ this._projBuffer = device.createBuffer({
460
+ label: 'WebGpuTextRenderer/proj',
461
+ size: projectionBytes,
462
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
463
+ });
464
+ const nodeBytes = initialNodeCapacity * nodeFloats * 4;
465
+ this._nodeBuffer = device.createBuffer({
466
+ label: 'WebGpuTextRenderer/nodes',
467
+ size: nodeBytes,
468
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
469
+ });
470
+ this._nodeBufferCapacity = nodeBytes;
471
+ const vertexBytes = initialVertexCapacity * vertexStrideBytes;
472
+ this._vertexBuffer = device.createBuffer({
473
+ label: 'WebGpuTextRenderer/vertices',
474
+ size: vertexBytes,
475
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
476
+ });
477
+ this._vertexBufferCapacity = vertexBytes;
478
+ const indexBytes = initialIndexCapacity * 2;
479
+ this._indexBuffer = device.createBuffer({
480
+ label: 'WebGpuTextRenderer/indices',
481
+ size: indexBytes,
482
+ usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
483
+ });
484
+ this._indexBufferCapacity = indexBytes;
485
+ this._frameBindGroupDirty = true;
486
+ }
487
+ onDisconnect() {
488
+ this._projBuffer?.destroy();
489
+ this._nodeBuffer?.destroy();
490
+ this._vertexBuffer?.destroy();
491
+ this._indexBuffer?.destroy();
492
+ this._projBuffer = null;
493
+ this._nodeBuffer = null;
494
+ this._vertexBuffer = null;
495
+ this._indexBuffer = null;
496
+ this._nodeBufferCapacity = 0;
497
+ this._vertexBufferCapacity = 0;
498
+ this._indexBufferCapacity = 0;
499
+ this._frameBindGroup = null;
500
+ this._frameBindGroupDirty = true;
501
+ this._pipelines.clear();
502
+ this._texBindGroups = new WeakMap();
503
+ this._pipelineLayout = null;
504
+ this._textureBindGroupLayout = null;
505
+ this._frameBindGroupLayout = null;
506
+ this._shaderModule = null;
507
+ this._device = null;
508
+ this._resetFrameState();
509
+ }
510
+ // ── Collection ───────────────────────────────────────────────────────────
511
+ _collectText(node) {
512
+ node.syncDirty();
513
+ const { pageQuads, atlas } = node;
514
+ if (pageQuads.length === 0 || atlas === null)
515
+ return;
516
+ const nodeIndex = this._assignNodeIndex(node);
517
+ const shaderType = node.colorGlyphs ? 'color' : 'sdf';
518
+ const pages = atlas.pages;
519
+ for (const batch of pageQuads) {
520
+ const page = pages[batch.pageIndex];
521
+ if (page === undefined)
522
+ continue;
523
+ this._pendingQuads.push({ quads: batch, nodeIndex, shaderType, atlasTexture: page.texture });
524
+ }
525
+ }
526
+ _collectBitmapText(node) {
527
+ const { pageQuads, textures, msdf } = node;
528
+ if (pageQuads.length === 0)
529
+ return;
530
+ const nodeIndex = this._assignNodeIndex(node);
531
+ const shaderType = msdf ? 'msdf' : 'color';
532
+ for (const batch of pageQuads) {
533
+ const tex = textures[batch.pageIndex];
534
+ if (tex === undefined)
535
+ continue;
536
+ this._pendingQuads.push({ quads: batch, nodeIndex, shaderType, atlasTexture: tex });
537
+ }
538
+ }
539
+ _assignNodeIndex(node) {
540
+ const existing = this._nodeIndexMap.get(node);
541
+ if (existing !== undefined)
542
+ return existing;
543
+ const idx = this._nodeCount++;
544
+ this._nodeIndexMap.set(node, idx);
545
+ this._ensureNodeCapacity(idx + 1);
546
+ this._packNodeData(idx, node);
547
+ return idx;
548
+ }
549
+ // ── Node data packing (identical to WebGl2TextRenderer) ──────────────────
550
+ _packNodeData(ni, node) {
551
+ const arr = this._nodeDataArray;
552
+ const base = ni * nodeFloats;
553
+ const style = node.style;
554
+ const m = node.getGlobalTransform().toArray(false);
555
+ arr[base + 0] = m[0];
556
+ arr[base + 1] = m[1];
557
+ arr[base + 2] = m[2];
558
+ arr[base + 3] = m[6];
559
+ arr[base + 4] = m[3];
560
+ arr[base + 5] = m[4];
561
+ arr[base + 6] = m[5];
562
+ arr[base + 7] = m[7];
563
+ const fc = style.fillColor;
564
+ arr[base + 8] = fc.r / 255;
565
+ arr[base + 9] = fc.g / 255;
566
+ arr[base + 10] = fc.b / 255;
567
+ arr[base + 11] = fc.a;
568
+ const oc = style.outlineColor;
569
+ arr[base + 12] = oc.r / 255;
570
+ arr[base + 13] = oc.g / 255;
571
+ arr[base + 14] = oc.b / 255;
572
+ arr[base + 15] = oc.a;
573
+ const outlineMin = style.outlineWidth > 0 ? Math.max(0, 0.5 - style.outlineWidth) : 0.5;
574
+ arr[base + 16] = outlineMin;
575
+ arr[base + 17] = style.shadowAlpha;
576
+ arr[base + 18] = Math.max(0.03, style.shadowBlur * 0.1);
577
+ arr[base + 19] = style.gradientColors !== null ? 1 : 0;
578
+ const sc = style.shadowColor;
579
+ arr[base + 20] = sc.r / 255;
580
+ arr[base + 21] = sc.g / 255;
581
+ arr[base + 22] = sc.b / 255;
582
+ arr[base + 23] = sc.a;
583
+ arr[base + 24] = style.shadowOffsetX;
584
+ arr[base + 25] = style.shadowOffsetY;
585
+ arr[base + 26] = style.gradientAxis === 'vertical' ? 1 : 0;
586
+ arr[base + 27] = 0;
587
+ const gc = style.gradientColors;
588
+ if (gc !== null) {
589
+ arr[base + 28] = gc[0].r / 255;
590
+ arr[base + 29] = gc[0].g / 255;
591
+ arr[base + 30] = gc[0].b / 255;
592
+ arr[base + 31] = gc[0].a;
593
+ arr[base + 32] = gc[1].r / 255;
594
+ arr[base + 33] = gc[1].g / 255;
595
+ arr[base + 34] = gc[1].b / 255;
596
+ arr[base + 35] = gc[1].a;
597
+ }
598
+ else {
599
+ arr[base + 28] = arr[base + 29] = arr[base + 30] = arr[base + 31] = 0;
600
+ arr[base + 32] = arr[base + 33] = arr[base + 34] = arr[base + 35] = 0;
601
+ }
602
+ const bounds = node.textBounds;
603
+ arr[base + 36] = 0;
604
+ arr[base + 37] = 0;
605
+ arr[base + 38] = bounds.width;
606
+ arr[base + 39] = bounds.height;
607
+ }
608
+ // ── GPU resource helpers ─────────────────────────────────────────────────
609
+ _uploadNodeBuffer(device) {
610
+ const requiredBytes = this._nodeCount * nodeFloats * 4;
611
+ if (requiredBytes > this._nodeBufferCapacity) {
612
+ let newCap = this._nodeBufferCapacity;
613
+ while (newCap < requiredBytes)
614
+ newCap *= 2;
615
+ this._nodeBuffer?.destroy();
616
+ this._nodeBuffer = device.createBuffer({
617
+ label: 'WebGpuTextRenderer/nodes',
618
+ size: newCap,
619
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
620
+ });
621
+ this._nodeBufferCapacity = newCap;
622
+ this._frameBindGroupDirty = true;
623
+ }
624
+ device.queue.writeBuffer(this._nodeBuffer, 0, this._nodeDataArray.buffer, 0, requiredBytes);
625
+ }
626
+ _ensureGpuVertexBuffer(device, vertexCount) {
627
+ const required = vertexCount * vertexStrideBytes;
628
+ if (required <= this._vertexBufferCapacity)
629
+ return;
630
+ let newCap = this._vertexBufferCapacity;
631
+ while (newCap < required)
632
+ newCap *= 2;
633
+ this._vertexBuffer?.destroy();
634
+ this._vertexBuffer = device.createBuffer({
635
+ label: 'WebGpuTextRenderer/vertices',
636
+ size: newCap,
637
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
638
+ });
639
+ this._vertexBufferCapacity = newCap;
640
+ }
641
+ _ensureGpuIndexBuffer(device, indexCount) {
642
+ const required = indexCount * 2;
643
+ if (required <= this._indexBufferCapacity)
644
+ return;
645
+ let newCap = this._indexBufferCapacity;
646
+ while (newCap < required)
647
+ newCap *= 2;
648
+ this._indexBuffer?.destroy();
649
+ this._indexBuffer = device.createBuffer({
650
+ label: 'WebGpuTextRenderer/indices',
651
+ size: newCap,
652
+ usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
653
+ });
654
+ this._indexBufferCapacity = newCap;
655
+ }
656
+ _getFrameBindGroup(device) {
657
+ if (!this._frameBindGroupDirty && this._frameBindGroup !== null) {
658
+ return this._frameBindGroup;
659
+ }
660
+ this._frameBindGroup = device.createBindGroup({
661
+ layout: this._frameBindGroupLayout,
662
+ entries: [
663
+ { binding: 0, resource: { buffer: this._projBuffer } },
664
+ { binding: 1, resource: { buffer: this._nodeBuffer } },
665
+ ],
666
+ });
667
+ this._frameBindGroupDirty = false;
668
+ return this._frameBindGroup;
669
+ }
670
+ _getTexBindGroup(device, backend, texture) {
671
+ const existing = this._texBindGroups.get(texture);
672
+ if (existing !== undefined)
673
+ return existing;
674
+ const { view, sampler } = backend.getTextureBinding(texture);
675
+ const group = device.createBindGroup({
676
+ layout: this._textureBindGroupLayout,
677
+ entries: [
678
+ { binding: 0, resource: view },
679
+ { binding: 1, resource: sampler },
680
+ ],
681
+ });
682
+ this._texBindGroups.set(texture, group);
683
+ return group;
684
+ }
685
+ // ── Pipeline helpers ─────────────────────────────────────────────────────
686
+ _getPipeline(shaderType, format) {
687
+ const key = `${shaderType}:${format}`;
688
+ const existing = this._pipelines.get(key);
689
+ if (existing)
690
+ return existing;
691
+ const pipeline = this._device.createRenderPipeline(this._buildPipelineDescriptor(shaderType, format));
692
+ this._pipelines.set(key, pipeline);
693
+ return pipeline;
694
+ }
695
+ _buildPipelineDescriptor(shaderType, format) {
696
+ let fragEntry;
697
+ if (shaderType === 'sdf') {
698
+ fragEntry = 'fragmentSdf';
699
+ }
700
+ else if (shaderType === 'msdf') {
701
+ fragEntry = 'fragmentMsdf';
702
+ }
703
+ else {
704
+ fragEntry = 'fragmentColor';
705
+ }
706
+ return {
707
+ label: `WebGpuTextRenderer/${shaderType}`,
708
+ layout: this._pipelineLayout,
709
+ vertex: {
710
+ module: this._shaderModule,
711
+ entryPoint: 'vertexMain',
712
+ buffers: [
713
+ {
714
+ arrayStride: vertexStrideBytes,
715
+ stepMode: 'vertex',
716
+ attributes: [
717
+ { shaderLocation: 0, offset: 0, format: 'float32x2' },
718
+ { shaderLocation: 1, offset: 8, format: 'float32x2' },
719
+ { shaderLocation: 2, offset: 16, format: 'float32' },
720
+ ],
721
+ },
722
+ ],
723
+ },
724
+ fragment: {
725
+ module: this._shaderModule,
726
+ entryPoint: fragEntry,
727
+ targets: [
728
+ {
729
+ format,
730
+ blend: getWebGpuBlendState(BlendModes.Normal),
731
+ writeMask: GPUColorWrite.ALL,
732
+ },
733
+ ],
734
+ },
735
+ primitive: { topology: 'triangle-list' },
736
+ };
737
+ }
738
+ // ── Capacity helpers ─────────────────────────────────────────────────────
739
+ _ensureVertexCapacity(vertexCount) {
740
+ if (vertexCount <= this._vertexCapacity)
741
+ return;
742
+ while (this._vertexCapacity < vertexCount)
743
+ this._vertexCapacity *= 2;
744
+ this._vertexData = new ArrayBuffer(this._vertexCapacity * vertexStrideBytes);
745
+ this._float32View = new Float32Array(this._vertexData);
746
+ }
747
+ _ensureIndexCapacity(indexCount) {
748
+ if (indexCount <= this._indexCapacity)
749
+ return;
750
+ while (this._indexCapacity < indexCount)
751
+ this._indexCapacity *= 2;
752
+ this._indexData = new Uint16Array(this._indexCapacity);
753
+ }
754
+ _ensureNodeCapacity(nodeCount) {
755
+ if (nodeCount <= this._nodeCapacity)
756
+ return;
757
+ while (this._nodeCapacity < nodeCount)
758
+ this._nodeCapacity *= 2;
759
+ const next = new Float32Array(this._nodeCapacity * nodeFloats);
760
+ next.set(this._nodeDataArray);
761
+ this._nodeDataArray = next;
762
+ }
763
+ _resetFrameState() {
764
+ this._pendingQuads.length = 0;
765
+ this._nodeIndexMap.clear();
766
+ this._textureKeyMap.clear();
767
+ this._textureKeyCounter = 0;
768
+ this._nodeCount = 0;
769
+ }
770
+ }
771
+
772
+ export { WebGpuTextRenderer };
773
+ //# sourceMappingURL=WebGpuTextRenderer.js.map