@driftengine/core 3.62.0 → 4.0.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 (428) hide show
  1. package/CHANGELOG.json +122 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +70 -4
  9. package/dist/index.js +123 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +47 -14
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +128 -80
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +261 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +158 -88
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Where a frame's milliseconds went, one row per graph pass.
3
+ *
4
+ * **Not a widening of `gpuTimer.ts`, and that is deliberate.** That class is a WebGL2 query pool
5
+ * over `TIME_ELAPSED_EXT`, partitioning a frame into three fixed brackets — shadows, reflection,
6
+ * rest — and it is right for what it measures. A graph pass is not one of three brackets: there
7
+ * are as many as the frame composes, they are named at record time, and on WebGPU the sample
8
+ * comes from a timestamp query rather than an elapsed-time extension. Two different measurements
9
+ * with two different shapes; widening one into the other would leave a class that does neither
10
+ * well.
11
+ *
12
+ * **What it does inherit from that class is its best rule: unmeasured is not zero.** A device
13
+ * without timestamp queries, or a pass that ran no work, must not read as "took no time" — a
14
+ * profiler that cannot distinguish those two is a profiler that sends somebody optimising a pass
15
+ * that never ran. `passMs` returns null for unmeasured and a number for measured, including zero.
16
+ *
17
+ * **Labels persist and timings reset.** A pass that did nothing this frame keeps its name and
18
+ * reports unmeasured, so the profiler's rows stop jumping about as quality settings toggle
19
+ * passes on and off — which is exactly when somebody is reading it.
20
+ */
21
+ export interface PassTimings {
22
+ /** Accumulated milliseconds per pass for the current frame. */
23
+ ms: Float64Array;
24
+ /** Whether each pass received a sample this frame. */
25
+ measured: Uint8Array;
26
+ /** Name per pass, kept across frames. */
27
+ labels: string[];
28
+ }
29
+
30
+ export function createPassTimings(capacity: number): PassTimings {
31
+ return {
32
+ ms: new Float64Array(capacity),
33
+ measured: new Uint8Array(capacity),
34
+ labels: Array.from({ length: capacity }, () => ''),
35
+ };
36
+ }
37
+
38
+ /** Start a frame: timings go, labels stay. See the header for why they are treated differently. */
39
+ export function resetPassTimings(timings: PassTimings): void {
40
+ timings.ms.fill(0);
41
+ timings.measured.fill(0);
42
+ }
43
+
44
+ /**
45
+ * Add one sample to a pass.
46
+ *
47
+ * Accumulates rather than replaces, because a pass may be sampled more than once in a frame — a
48
+ * shadow pass runs per cascade — and the row a reader wants is what that pass cost altogether.
49
+ */
50
+ export function recordPassSample(timings: PassTimings, pass: number, ms: number): void {
51
+ if (pass < 0 || pass >= timings.ms.length) return;
52
+ timings.ms[pass] = (timings.ms[pass] ?? 0) + ms;
53
+ timings.measured[pass] = 1;
54
+ }
55
+
56
+ export function recordPassLabel(timings: PassTimings, pass: number, label: string): void {
57
+ if (pass < 0 || pass >= timings.labels.length) return;
58
+ timings.labels[pass] = label;
59
+ }
60
+
61
+ /** Milliseconds this pass cost, or null where nothing measured it. Null is not zero. */
62
+ export function passMs(timings: PassTimings, pass: number): number | null {
63
+ if (pass < 0 || pass >= timings.ms.length) return null;
64
+ return timings.measured[pass] === 1 ? (timings.ms[pass] ?? 0) : null;
65
+ }
66
+
67
+ export function passLabel(timings: PassTimings, pass: number): string {
68
+ return timings.labels[pass] ?? '';
69
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Whether a recorded frame is one that can be executed.
3
+ *
4
+ * One rule: a node may read an identifier only if something wrote it earlier in this frame, or
5
+ * it was imported into the frame from outside. That catches the whole family of defects a fixed
6
+ * resource table caught by construction — a pass sampling an attachment the quality settings
7
+ * disabled, a pass reading a mip chain built only when a different feature is on — and which
8
+ * identifiers do not catch by construction, because any integer is a plausible identifier.
9
+ *
10
+ * Returns a message rather than throwing. The renderer decides whether a malformed frame is a
11
+ * developer-time assertion or a dropped frame, and a validator that throws has made that
12
+ * decision for it.
13
+ */
14
+ import type { Deps } from './deps.ts';
15
+ import { readsOf, writesOf } from './deps.ts';
16
+
17
+ export function validateGraph(
18
+ deps: Deps,
19
+ count: number,
20
+ imported: readonly number[],
21
+ ): string | null {
22
+ const written = new Set<number>(imported);
23
+ for (let node = 0; node < count; node += 1) {
24
+ /*
25
+ * Reads are checked before this node's writes join the set, which is what makes a read of
26
+ * something written only later a refusal and a read-modify-write legal — the latter is
27
+ * reading contents something earlier put there.
28
+ */
29
+ const reads = readsOf(deps, node);
30
+ for (let i = 0; i < reads.length; i += 1) {
31
+ const id = reads[i] ?? 0;
32
+ if (!written.has(id)) {
33
+ return `node ${node} reads resource ${id}, which nothing wrote and nothing imported`;
34
+ }
35
+ }
36
+ const writes = writesOf(deps, node);
37
+ for (let i = 0; i < writes.length; i += 1) written.add(writes[i] ?? 0);
38
+ }
39
+ return null;
40
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Transient resources a frame declares, addressed by identifier rather than by bit.
3
+ *
4
+ * **`resources.ts` is not replaced and is not wrong.** It is a fixed table of fourteen
5
+ * attachments held in a bit mask, and it says in its own header that the transient allocator a
6
+ * frame graph is famous for would be machinery with no case to serve — which was true while
7
+ * this frame's composition was fixed. A GPU-driven frame's composition is not: hierarchical
8
+ * depth chains, visibility buffers, per-material bin lists and indirect argument buffers are
9
+ * allocated against what the frame contains. Thirty-one bits cannot hold hundreds of them and
10
+ * a thirty-second bit is not the fix.
11
+ *
12
+ * So there are two representations and each is used where it is right: the mask for a fixed
13
+ * composition, identifiers for a dynamic one. Nothing converts between them, because nothing
14
+ * needs to — a frame is scheduled by one or the other.
15
+ *
16
+ * Structure of arrays, growable by doubling, and nothing here holds an object per resource.
17
+ */
18
+
19
+ /** A transient texture, sized in texels. */
20
+ export interface VirtualTextureDesc {
21
+ kind: 'texture';
22
+ width: number;
23
+ height: number;
24
+ bytesPerTexel: number;
25
+ }
26
+
27
+ /** A transient buffer, sized in bytes. */
28
+ export interface VirtualBufferDesc {
29
+ kind: 'buffer';
30
+ bytes: number;
31
+ }
32
+
33
+ export type VirtualDesc = VirtualTextureDesc | VirtualBufferDesc;
34
+
35
+ export interface VirtualTable {
36
+ /** 0 for a texture, 1 for a buffer, one entry per identifier. */
37
+ kinds: Uint8Array;
38
+ /** Byte footprint per identifier, which is what the aliasing allocator packs. */
39
+ bytes: Uint32Array;
40
+ /** How many identifiers have been declared this frame. */
41
+ count: number;
42
+ }
43
+
44
+ export function createVirtualTable(capacity: number): VirtualTable {
45
+ return { kinds: new Uint8Array(capacity), bytes: new Uint32Array(capacity), count: 0 };
46
+ }
47
+
48
+ export function resetVirtualTable(table: VirtualTable): void {
49
+ table.count = 0;
50
+ }
51
+
52
+ export function virtualCount(table: VirtualTable): number {
53
+ return table.count;
54
+ }
55
+
56
+ /*
57
+ * Doubling rather than growing by one, because a frame that declares one more resource than
58
+ * last frame will declare one more again, and a table that reallocates per declaration turns a
59
+ * linear frame into a quadratic one.
60
+ */
61
+ function grow(table: VirtualTable): void {
62
+ const size = table.kinds.length === 0 ? 8 : table.kinds.length * 2;
63
+ const kinds = new Uint8Array(size);
64
+ kinds.set(table.kinds);
65
+ const bytes = new Uint32Array(size);
66
+ bytes.set(table.bytes);
67
+ table.kinds = kinds;
68
+ table.bytes = bytes;
69
+ }
70
+
71
+ export function declareVirtual(table: VirtualTable, desc: VirtualDesc): number {
72
+ if (table.count === table.kinds.length) grow(table);
73
+ const id = table.count;
74
+ if (desc.kind === 'texture') {
75
+ table.kinds[id] = 0;
76
+ table.bytes[id] = desc.width * desc.height * desc.bytesPerTexel;
77
+ } else {
78
+ table.kinds[id] = 1;
79
+ table.bytes[id] = desc.bytes;
80
+ }
81
+ table.count = id + 1;
82
+ return id;
83
+ }
84
+
85
+ export function virtualKind(table: VirtualTable, id: number): number {
86
+ return table.kinds[id] ?? 0;
87
+ }
88
+
89
+ export function virtualBytes(table: VirtualTable, id: number): number {
90
+ return table.bytes[id] ?? 0;
91
+ }
@@ -0,0 +1,225 @@
1
+ /** The three levels in one place, and the rule that there is never a fourth. */
2
+
3
+ import { createProbeBlend } from '../probeGrid.ts';
4
+ import { REFLECTION_EDGE_FADE, edgeFade, newScreenSpaceHit } from '../screenSpaceReflection.ts';
5
+ import { GLOBAL_FIELD_BLEND } from './globalField.ts';
6
+ import { sampleProbeVolume, visibleProbes } from './probeVolume.ts';
7
+ import { FIELD_MARCH, newFieldHit, traceField } from './traceField.ts';
8
+ import { GI_SCREEN_MARCH, traceScreen } from './traceScreen.ts';
9
+
10
+ import type { ReadonlyVec3 } from 'gl-matrix';
11
+ import type { ProbeGrid } from '../probeGrid.ts';
12
+ import type { ScreenSpaceMarch } from '../screenSpaceReflection.ts';
13
+ import type { GlobalField, GlobalFieldCascade } from './globalField.ts';
14
+ import type { ProbeVisibility } from './probeVolume.ts';
15
+ import type { FieldMarch } from './traceField.ts';
16
+
17
+ /**
18
+ * **Three levels, and the architecture is the fallback rather than any one of them.**
19
+ *
20
+ * `ROADMAP.md` refused screen-space global illumination, and this does not reverse that refusal —
21
+ * it satisfies the requirement the refusal was about. Screen space is exact wherever the screen has
22
+ * the answer and has *no* answer where it does not, which as a terminal technique means inventing
23
+ * one from whatever happened to be in frame. Here it is an accelerator with something behind it:
24
+ * a world-space distance field whose error is bounded by its own resolution, and behind that a
25
+ * probe volume which is never wrong and only ever coarse.
26
+ *
27
+ * **The last level cannot fail, and that is the load-bearing sentence.** A ray with no answer
28
+ * leaves a shader nothing to write but what the buffer held, which is the previous frame and reads
29
+ * as a flicker. A probe volume is an interpolation between measurements — no ray to escape, no
30
+ * frame to leave — and `visibleProbes` falls back to the nearest probe even when every visibility
31
+ * weight has crushed to zero. So there are exactly three cases.
32
+ *
33
+ * **And the levels blend rather than switch.** Every boundary between them moves with the camera:
34
+ * the frame's edge does by definition, and the cascades are centred on the camera so their faces do
35
+ * too. A hard switch at a boundary that moves is a seam that sweeps across the picture whenever the
36
+ * camera turns, which is worse than either level's own error — an error that is wrong everywhere
37
+ * reads as a coarse solution, and one that changes abruptly along a surface reads as a crack.
38
+ */
39
+
40
+ export const GI_SOURCE_SCREEN = 0;
41
+ export const GI_SOURCE_FIELD = 1;
42
+ export const GI_SOURCE_PROBES = 2;
43
+
44
+ export type GiSource = 0 | 1 | 2;
45
+
46
+ /** One indirect ray, as the shading path hands it over. */
47
+ export interface IndirectRay {
48
+ /** Where it starts, on the surface being shaded. */
49
+ readonly point: ReadonlyVec3;
50
+ /** That surface's normal, which both traces lift off along. */
51
+ readonly normal: ReadonlyVec3;
52
+ readonly direction: ReadonlyVec3;
53
+ /** Cone half-angle for the world trace. Zero is a mirror; wider is a rougher surface. */
54
+ readonly coneAngle: number;
55
+ }
56
+
57
+ /** Everything the three levels need, gathered so a per-ray call takes one object. */
58
+ export interface GiResources {
59
+ readonly eye: ReadonlyVec3;
60
+ readonly project: (x: number, y: number, z: number, uv: Float32Array) => boolean;
61
+ /** How far the eye is from whatever the frame drew, and `Infinity` where it drew nothing. */
62
+ readonly sceneDistance: (u: number, v: number) => number;
63
+ /** The radiance the frame holds at a screen position. */
64
+ readonly screenRadiance: (u: number, v: number, out: Float32Array) => void;
65
+ readonly field: GlobalField;
66
+ /** The radiance at a world point the field trace arrived at. */
67
+ readonly fieldRadiance: (x: number, y: number, z: number, out: Float32Array) => void;
68
+ readonly grid: ProbeGrid;
69
+ /** Null is the off switch, and it lands on the grid's own blend. See `probeVolume.ts`. */
70
+ readonly visibility: ProbeVisibility | null;
71
+ /** `probeChannels` floats a probe, layer-major. */
72
+ readonly probeValues: Float32Array;
73
+ readonly probeChannels: number;
74
+ readonly screenMarch?: ScreenSpaceMarch;
75
+ readonly fieldMarch?: FieldMarch;
76
+ }
77
+
78
+ export interface IndirectResult {
79
+ /** Always true. See the module header: there is no fourth case. */
80
+ hit: boolean;
81
+ /** Which level contributed most. The radiance is the blend, not this level alone. */
82
+ source: GiSource;
83
+ /** Three channels. */
84
+ readonly radiance: Float32Array;
85
+ /** How much of the answer came from each level, summing to one. */
86
+ readonly blend: Float32Array;
87
+ }
88
+
89
+ export function newIndirectResult(): IndirectResult {
90
+ return {
91
+ hit: false,
92
+ source: GI_SOURCE_PROBES,
93
+ radiance: new Float32Array(3),
94
+ blend: new Float32Array(3),
95
+ };
96
+ }
97
+
98
+ /** Scratch, because this runs once per ray per pixel and the rule about that is absolute. */
99
+ const SCREEN_HIT = newScreenSpaceHit();
100
+ const FIELD_HIT = newFieldHit();
101
+ const PROBE_BLEND = createProbeBlend();
102
+ const LEVEL = new Float32Array(3);
103
+ const PROBE_OUT = new Float32Array(3);
104
+
105
+ /**
106
+ * Trace one indirect ray down the chain, and always answer.
107
+ *
108
+ * The screen answers as much as its distance from the edge of the frame allows; what is left goes
109
+ * to the world field, as much as *its* distance from the edge of the outermost cascade allows; and
110
+ * whatever is left after that goes to the probes, which have nothing behind them and take it all.
111
+ */
112
+ export function traceIndirect(
113
+ ray: IndirectRay,
114
+ resources: GiResources,
115
+ out: IndirectResult,
116
+ ): IndirectResult {
117
+ out.radiance.fill(0);
118
+ out.blend.fill(0);
119
+ out.hit = true;
120
+
121
+ /* Level 0. Weighted by how far the hit landed from the edge of the frame, which is the one
122
+ boundary a viewer is guaranteed to see move. */
123
+ let screen = 0;
124
+ if (
125
+ traceScreen(
126
+ ray.point,
127
+ ray.normal,
128
+ ray.direction,
129
+ resources.eye,
130
+ resources.screenMarch ?? GI_SCREEN_MARCH,
131
+ resources.project,
132
+ resources.sceneDistance,
133
+ SCREEN_HIT,
134
+ )
135
+ ) {
136
+ /*
137
+ * **The *path*'s margin, not the hit's.** Fading only the hit leaves the seam exactly where it
138
+ * was: a ray that grazes the border and lands is faded, and the ray one pixel over leaves the
139
+ * frame and answers nothing at all, so the weight still steps from one to zero across a line
140
+ * that sweeps as the camera turns. See `ScreenSpaceHit.margin`.
141
+ */
142
+ screen = edgeFade(SCREEN_HIT.margin, REFLECTION_EDGE_FADE);
143
+ if (screen > 0) {
144
+ resources.screenRadiance(SCREEN_HIT.u, SCREEN_HIT.v, LEVEL);
145
+ add(out.radiance, LEVEL, screen);
146
+ }
147
+ }
148
+
149
+ /* Level 1, for whatever the screen did not answer. Weighted the same way against the outermost
150
+ cascade's face, because that face is centred on the camera and therefore moves with it too. */
151
+ let fieldShare = 0;
152
+ const remaining = 1 - screen;
153
+ if (remaining > 0) {
154
+ const march = resources.fieldMarch ?? FIELD_MARCH;
155
+ const coned =
156
+ march.coneAngle === ray.coneAngle ? march : { ...march, coneAngle: ray.coneAngle };
157
+ if (traceField(resources.field, ray.point, ray.normal, ray.direction, coned, FIELD_HIT)) {
158
+ fieldShare =
159
+ remaining * fieldConfidence(resources.field, FIELD_HIT.x, FIELD_HIT.y, FIELD_HIT.z);
160
+ if (fieldShare > 0) {
161
+ resources.fieldRadiance(FIELD_HIT.x, FIELD_HIT.y, FIELD_HIT.z, LEVEL);
162
+ add(out.radiance, LEVEL, fieldShare);
163
+ }
164
+ }
165
+ }
166
+
167
+ /*
168
+ * Level 2, for everything still unanswered. **It takes whatever is left without asking**, which
169
+ * is what "never wrong and only ever coarse" buys: there is no case where this declines.
170
+ */
171
+ const probes = 1 - screen - fieldShare;
172
+ if (probes > 0) {
173
+ visibleProbes(
174
+ resources.grid,
175
+ resources.visibility,
176
+ ray.point[0] ?? 0,
177
+ ray.point[1] ?? 0,
178
+ ray.point[2] ?? 0,
179
+ PROBE_BLEND,
180
+ );
181
+ sampleProbeVolume(PROBE_BLEND, resources.probeValues, resources.probeChannels, PROBE_OUT);
182
+ add(out.radiance, PROBE_OUT, probes);
183
+ }
184
+
185
+ out.blend[0] = screen;
186
+ out.blend[1] = fieldShare;
187
+ out.blend[2] = probes;
188
+ out.source =
189
+ screen >= fieldShare && screen >= probes
190
+ ? GI_SOURCE_SCREEN
191
+ : fieldShare >= probes
192
+ ? GI_SOURCE_FIELD
193
+ : GI_SOURCE_PROBES;
194
+ return out;
195
+ }
196
+
197
+ /**
198
+ * How much a world-field hit is trusted, by how far inside the outermost cascade it landed.
199
+ *
200
+ * **The same band `sampleGlobalField` fades its own cascades over**, so the two do not disagree
201
+ * about where the field stops being the answer. A hit right on the outer face is a hit the field
202
+ * barely had the information for, and the probes are what carry it the rest of the way.
203
+ */
204
+ function fieldConfidence(field: GlobalField, x: number, y: number, z: number): number {
205
+ const outer = field.cascades[field.cascades.length - 1] as GlobalFieldCascade;
206
+ const band = GLOBAL_FIELD_BLEND * ((outer.bounds[3] as number) - (outer.bounds[0] as number));
207
+ if (band <= 0) return 1;
208
+ const p = [x, y, z];
209
+ let inside = Infinity;
210
+ for (let axis = 0; axis < 3; axis++) {
211
+ inside = Math.min(
212
+ inside,
213
+ (p[axis] as number) - (outer.bounds[axis] as number),
214
+ (outer.bounds[axis + 3] as number) - (p[axis] as number),
215
+ );
216
+ }
217
+ return Math.min(1, Math.max(0, inside / band));
218
+ }
219
+
220
+ /** `target += source * weight`, three channels. */
221
+ function add(target: Float32Array, source: Float32Array, weight: number): void {
222
+ for (let channel = 0; channel < 3; channel++) {
223
+ target[channel] = (target[channel] as number) + (source[channel] as number) * weight;
224
+ }
225
+ }