@driftengine/core 3.63.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 +111 -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 +69 -4
  9. package/dist/index.js +112 -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 +19 -9
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
  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 +246 -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 +63 -36
  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,237 @@
1
+ /**
2
+ * Which clusters are worth rasterising: the three tests, in the order that makes them cheap.
3
+ *
4
+ * Frustum first, because it is six dot products and throws away most of a world. Then the normal
5
+ * cone, which is one more dot product and throws away roughly half of what is left on a closed
6
+ * surface. Then the hierarchical depth buffer, which costs a projection and four texel reads and
7
+ * is the only one that needs anything from this frame.
8
+ *
9
+ * **Every one of them is conservative in the same direction.** A cluster that might be visible
10
+ * survives; only a cluster that provably cannot be seen is dropped. Culling something visible is a
11
+ * hole in the picture that comes and goes with the camera and is diagnosed as almost anything
12
+ * else; keeping something invisible is a wasted draw. `frustum.ts` says the same in its own words
13
+ * and the two files have to agree about it, because a pipeline whose stages disagree about which
14
+ * way to round is one that drops geometry at exactly the boundaries.
15
+ *
16
+ * **The compaction is here and the flags are the device's.** A dispatch can decide every cluster
17
+ * in parallel and cannot append them in order without a prefix sum, so the shader writes one flag
18
+ * per cluster and this writes the ordered list from the flags. That split is also what
19
+ * `scripts/gpu-parity.mjs` compares: the flags are order-free, so they can be checked
20
+ * element-by-element against a reference that has not had to reproduce an atomic's arrival order.
21
+ */
22
+ import { sphereOutsideFrustum } from './frustum.js';
23
+ import { hzbOccluded } from './hzb.js';
24
+ /** Eight floats a cluster: centre, radius, cone axis, cone cutoff. `clusterUpload.ts` packs them. */
25
+ export const CLUSTER_CULL_FLOATS = 8;
26
+ /** What a kept cluster's flag holds. One rather than a bit mask: there is one reason to keep. */
27
+ export const CLUSTER_KEEP = 1;
28
+ /**
29
+ * A cluster's cone as it is carried, turned into the cutoff `coneBackfacing` reads.
30
+ *
31
+ * **A cluster carries a cosine and this test is written in a sine**, and until 2026-09-18 nothing
32
+ * said so. `buildClusters` writes "the minimum dot product of any face normal against the axis",
33
+ * the `MSHL` container stores that and says so, and `ClusterSource` passed it through to the test
34
+ * below untouched. Every cluster before then was a single quad or a near-flat patch, whose cosine
35
+ * of about one reads as a sine of one — a hemisphere, never culled — so the mismatch was silent and
36
+ * cone culling simply did not happen. The voxel sandbox grows its clusters by position, across
37
+ * every direction a block faces: a cosine of zero read as a sine of zero is a flat cluster, and a
38
+ * canopy seen from below lost every block whose cluster pointed up.
39
+ *
40
+ * `cos(a)` at or above zero is `sin(a) = sqrt(1 - cos(a)^2)`. Below zero the cone is wider than a
41
+ * hemisphere and no aim of the eye sees every face from behind, so the answer is one, which the
42
+ * test can never satisfy. Converted where a cluster is packed, once, so neither shader nor
43
+ * reference changes.
44
+ */
45
+ export function cullCutoff(minimumDot) {
46
+ if (minimumDot < 0)
47
+ return 1;
48
+ return Math.sqrt(Math.max(0, 1 - minimumDot * minimumDot));
49
+ }
50
+ /**
51
+ * Whether every triangle in a cluster faces away from the eye.
52
+ *
53
+ * **`cutoff` is `sin(a)`**, where `a` is the half-angle of the cone containing every normal. Zero
54
+ * is a flat cluster; one is a cluster whose normals span a hemisphere, which can never be culled
55
+ * because the test below cannot be satisfied. Stating it as a sine rather than a cosine is what
56
+ * makes the expression a single comparison with no trigonometry in the shader.
57
+ *
58
+ * The derivation, because the constant is otherwise unfalsifiable. Let `d` be the vector from the
59
+ * eye to the cluster's centre. Directions from the eye to points on the cluster lie within
60
+ * `b = asin(radius / |d|)` of `d`. A triangle is backfacing when its normal has a positive dot
61
+ * with the direction the eye looks along it, so *every* triangle is backfacing when
62
+ * `angle(axis, d) + a + b < 90 degrees`, which is `dot(axis, normalise(d)) > sin(a + b)`.
63
+ * Expanding `sin(a + b)` to `sin a + radius / |d|` is exact at `a = 0` and errs on the side of
64
+ * keeping a cluster everywhere else, which is the direction this file rounds.
65
+ *
66
+ * Multiplied through by `|d|` so there is no divide and no normalise.
67
+ */
68
+ export function coneBackfacing(axisX, axisY, axisZ, cutoff, centreX, centreY, centreZ, radius, eyeX, eyeY, eyeZ) {
69
+ const dx = centreX - eyeX;
70
+ const dy = centreY - eyeY;
71
+ const dz = centreZ - eyeZ;
72
+ const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
73
+ return dx * axisX + dy * axisY + dz * axisZ >= cutoff * distance + radius;
74
+ }
75
+ /**
76
+ * The screen rectangle a sphere covers and the nearest clip depth it reaches.
77
+ *
78
+ * Writes `minX, minY, maxX, maxY, nearestDepth` into `out`, in the [0, 1] range a texture
79
+ * coordinate uses, and answers **false when the sphere has no rectangle at all** — when any part
80
+ * of it is at or behind the eye. That case is the reason this returns a boolean rather than
81
+ * clamping: a sphere straddling the near plane does not project to a rectangle, and every
82
+ * implementation that pretends it does culls it on the frames where the wrap happens to land
83
+ * outside the screen and keeps it on the frames where it does not. What that looks like is
84
+ * geometry flickering as the camera moves through it, which is the defect this function's shape
85
+ * exists to prevent.
86
+ *
87
+ * The eight corners of the sphere's bounding box rather than an analytic silhouette: the box is
88
+ * larger than the sphere, so the rectangle is conservative, and it is eight transforms instead of
89
+ * a quadratic.
90
+ */
91
+ export function sphereScreenBounds(viewProj, centreX, centreY, centreZ, radius, out) {
92
+ let minX = Number.POSITIVE_INFINITY;
93
+ let minY = Number.POSITIVE_INFINITY;
94
+ let maxX = Number.NEGATIVE_INFINITY;
95
+ let maxY = Number.NEGATIVE_INFINITY;
96
+ let nearest = Number.NEGATIVE_INFINITY;
97
+ for (let corner = 0; corner < 8; corner += 1) {
98
+ const x = centreX + (corner & 1 ? radius : -radius);
99
+ const y = centreY + (corner & 2 ? radius : -radius);
100
+ const z = centreZ + (corner & 4 ? radius : -radius);
101
+ const cw = viewProj[3] * x +
102
+ viewProj[7] * y +
103
+ viewProj[11] * z +
104
+ viewProj[15];
105
+ /* At or behind the eye, so there is no rectangle. See the header. */
106
+ if (cw <= 1e-6)
107
+ return false;
108
+ const cx = viewProj[0] * x +
109
+ viewProj[4] * y +
110
+ viewProj[8] * z +
111
+ viewProj[12];
112
+ const cy = viewProj[1] * x +
113
+ viewProj[5] * y +
114
+ viewProj[9] * z +
115
+ viewProj[13];
116
+ const cz = viewProj[2] * x +
117
+ viewProj[6] * y +
118
+ viewProj[10] * z +
119
+ viewProj[14];
120
+ const inverse = 1 / cw;
121
+ const u = cx * inverse * 0.5 + 0.5;
122
+ const v = cy * inverse * 0.5 + 0.5;
123
+ minX = Math.min(minX, u);
124
+ maxX = Math.max(maxX, u);
125
+ minY = Math.min(minY, v);
126
+ maxY = Math.max(maxY, v);
127
+ /* Nearest is the *largest* clip depth here; `depthConvention.ts` puts the near plane at 1. */
128
+ nearest = Math.max(nearest, cz * inverse);
129
+ }
130
+ out[0] = minX;
131
+ out[1] = minY;
132
+ out[2] = maxX;
133
+ out[3] = maxY;
134
+ out[4] = nearest;
135
+ return true;
136
+ }
137
+ /**
138
+ * The pyramid level whose texels are wide enough that four of them cover the rectangle.
139
+ *
140
+ * `ceil(log2(longest side in texels))` minus one, floored at zero: at that level the rectangle
141
+ * spans at most two texels each way, which is what a four-texel gather reads. One level coarser
142
+ * would read a texel covering geometry outside the rectangle and cull against an occluder that is
143
+ * not in front of this cluster at all.
144
+ *
145
+ * Clamped to the top of the pyramid, because a rectangle larger than the screen is a cluster the
146
+ * camera is inside and there is no level coarse enough for it.
147
+ */
148
+ export function hzbLevelFor(rectWidthTexels, rectHeightTexels, width, height) {
149
+ const longest = Math.max(rectWidthTexels, rectHeightTexels, 1);
150
+ const level = Math.max(0, Math.ceil(Math.log2(longest)) - 1);
151
+ const top = Math.floor(Math.log2(Math.max(1, Math.max(width, height))));
152
+ return Math.min(level, top);
153
+ }
154
+ /**
155
+ * Whether a rectangle's nearest depth is behind everything already drawn under it.
156
+ *
157
+ * **The weakest of the four texels decides.** Each holds the furthest depth in its own region, so
158
+ * a cluster is hidden only where it is behind *all* of them — and under reversed-Z the furthest of
159
+ * four is the smallest. Taking the nearest instead would cull a cluster visible through a gap,
160
+ * which is the failure mode that reads as holes appearing in geometry as the camera turns.
161
+ */
162
+ export function clusterOccluded(depths, offsets, width, height, minX, minY, maxX, maxY, nearestDepth) {
163
+ const level = hzbLevelFor((maxX - minX) * width, (maxY - minY) * height, width, height);
164
+ if (level >= offsets.length)
165
+ return false;
166
+ const levelWidth = Math.max(1, width >> level);
167
+ const levelHeight = Math.max(1, height >> level);
168
+ const base = offsets[level];
169
+ const x0 = Math.max(0, Math.min(levelWidth - 1, Math.floor(minX * levelWidth)));
170
+ const x1 = Math.max(0, Math.min(levelWidth - 1, Math.floor(maxX * levelWidth)));
171
+ /*
172
+ * **The rows run the other way, and the swap is not cosmetic.** `sphereScreenBounds` reports the
173
+ * rectangle in clip-up coordinates — `v` of 1 is clip `y` of +1 — and the pyramid is seeded
174
+ * straight from the depth texture, whose row 0 is exactly there. So a `v` used as a row index
175
+ * reads the mirror of what the cluster covers, and the top edge of the rectangle becomes the
176
+ * bottom row.
177
+ *
178
+ * It shipped that way and drew holes: geometry near the top of the frame tested against whatever
179
+ * stood near the bottom, coming and going with the camera. `gpu-parity.mjs` agreed about it for
180
+ * as long, because the WGSL made the same mistake — a parity check compares two implementations
181
+ * and neither of them is the truth.
182
+ */
183
+ const y0 = Math.max(0, Math.min(levelHeight - 1, Math.floor((1 - maxY) * levelHeight)));
184
+ const y1 = Math.max(0, Math.min(levelHeight - 1, Math.floor((1 - minY) * levelHeight)));
185
+ let furthest = Number.POSITIVE_INFINITY;
186
+ for (let y = y0; y <= y1; y += 1) {
187
+ for (let x = x0; x <= x1; x += 1) {
188
+ furthest = Math.min(furthest, depths[base + y * levelWidth + x]);
189
+ }
190
+ }
191
+ return hzbOccluded(nearestDepth, furthest);
192
+ }
193
+ /**
194
+ * Every test, over every cluster: one flag each, and the kept indices in input order.
195
+ *
196
+ * `selected`, where given, is the cut's flag a cluster: a cluster it did not select is not tested
197
+ * and not kept, which is how an instance cull reaches the clusters of a mesh it hid. Absent, every
198
+ * cluster is judged.
199
+ *
200
+ * `hzb` is null in the first phase of a two-phase pass, where there is no depth from this frame to
201
+ * test against yet — and null rather than an empty pyramid, because an empty one is a pyramid full
202
+ * of zeroes and under reversed-Z zero is the far plane, which occludes nothing and would look like
203
+ * the test running when it is not.
204
+ *
205
+ * Returns how many survived, which is also what an indirect draw's instance count wants.
206
+ */
207
+ export function cullClusters(clusters, planes, eyeX, eyeY, eyeZ, hzb, keep, list, selected = null,
208
+ /** False for the blended run, whose raster draws both sides. See `CULL_CONES`. */
209
+ cones = true) {
210
+ const bounds = new Float32Array(5);
211
+ const count = Math.floor(clusters.length / CLUSTER_CULL_FLOATS);
212
+ let kept = 0;
213
+ for (let i = 0; i < count; i += 1) {
214
+ if (selected !== null && selected[i] === 0) {
215
+ keep[i] = 0;
216
+ continue;
217
+ }
218
+ const at = i * CLUSTER_CULL_FLOATS;
219
+ const cx = clusters[at];
220
+ const cy = clusters[at + 1];
221
+ const cz = clusters[at + 2];
222
+ const radius = clusters[at + 3];
223
+ let survives = !sphereOutsideFrustum(planes, cx, cy, cz, radius);
224
+ if (survives && cones) {
225
+ survives = !coneBackfacing(clusters[at + 4], clusters[at + 5], clusters[at + 6], clusters[at + 7], cx, cy, cz, radius, eyeX, eyeY, eyeZ);
226
+ }
227
+ if (survives && hzb !== null && sphereScreenBounds(hzb.viewProj, cx, cy, cz, radius, bounds)) {
228
+ survives = !clusterOccluded(hzb.depths, hzb.offsets, hzb.width, hzb.height, bounds[0], bounds[1], bounds[2], bounds[3], bounds[4]);
229
+ }
230
+ keep[i] = survives ? CLUSTER_KEEP : 0;
231
+ if (survives) {
232
+ list[kept] = i;
233
+ kept += 1;
234
+ }
235
+ }
236
+ return kept;
237
+ }
@@ -0,0 +1,64 @@
1
+ /** Every material's decode programs, flattened into what the shading pass binds. */
2
+ /**
3
+ * **A program is `DecodeResources` with bytes rather than floats**, because bytes are what the
4
+ * device array holds. The types are structural: core does not import `@driftengine/texture`, and
5
+ * what `encodeMaterial` returns satisfies `programFromEncoded` as it stands.
6
+ *
7
+ * **Every slot a graph names is program-local and is rewritten here to a global one** — a layer, a
8
+ * network, a constant — so the interpreter needs no per-program indirection table. The graph a
9
+ * caller handed in is not modified; the rewritten copy lives only in the table.
10
+ */
11
+ export interface GpuDrivenLatent {
12
+ readonly width: number;
13
+ readonly height: number;
14
+ /** One to four. The lanes past them read zero, and alpha reads one, as `decodeCpu` does. */
15
+ readonly components: number;
16
+ /** Level 0 first, each half the last, down to one texel. `components` bytes a texel. */
17
+ readonly levels: readonly Uint8Array[];
18
+ }
19
+ export interface GpuDrivenNetwork {
20
+ readonly shape: {
21
+ readonly inputs: number;
22
+ readonly hidden: readonly number[];
23
+ readonly outputs: number;
24
+ };
25
+ readonly weights: Float32Array;
26
+ }
27
+ export interface GpuDrivenProgram {
28
+ readonly graph: {
29
+ readonly nodes: Uint32Array;
30
+ readonly count: number;
31
+ readonly result: number;
32
+ readonly addressMode: number;
33
+ };
34
+ readonly latents: readonly GpuDrivenLatent[];
35
+ readonly blocks?: readonly GpuDrivenLatent[];
36
+ readonly networks: readonly GpuDrivenNetwork[];
37
+ /** Four floats a slot, for `CONSTANT`. */
38
+ readonly constants?: Float32Array;
39
+ }
40
+ export interface DecodeTables {
41
+ readonly nodes: Uint32Array;
42
+ readonly weights: Float32Array;
43
+ /** One array a mip level: every layer's RGBA bytes, layer after layer. */
44
+ readonly levels: readonly Uint8Array[];
45
+ /** At least one, because a texture array of no layers cannot be created. */
46
+ readonly layerCount: number;
47
+ /** Level 0's edge in texels. One when nothing is sampled. */
48
+ readonly layerSize: number;
49
+ }
50
+ /** What `encodeMaterial` returns, as far as this needs it. */
51
+ export interface EncodedProgramShape {
52
+ readonly latentWidth: number;
53
+ readonly latentHeight: number;
54
+ readonly components: number;
55
+ readonly weights: Float32Array;
56
+ readonly shape: GpuDrivenNetwork['shape'];
57
+ readonly graph: GpuDrivenProgram['graph'];
58
+ /** Level 0 first — `encodeMaterial` puts the latent itself there. */
59
+ readonly mips: readonly {
60
+ readonly data: Uint8Array;
61
+ }[];
62
+ }
63
+ export declare function programFromEncoded(encoded: EncodedProgramShape): GpuDrivenProgram;
64
+ export declare function packDecodeTables(programs: readonly GpuDrivenProgram[]): DecodeTables;
@@ -0,0 +1,219 @@
1
+ /** Every material's decode programs, flattened into what the shading pass binds. */
2
+ import { DECODE_ADDRESS_MODES, DECODE_CONSTANT_CAPACITY, DECODE_FBM_OCTAVES, DECODE_NETWORK_CAPACITY, DECODE_NETWORK_HIDDEN_LAYERS, DECODE_NETWORK_INPUTS, DECODE_NETWORK_OUTPUTS, DECODE_NETWORK_WIDTH, DECODE_NODE_CAPACITY, DECODE_NODES_BYTES, DECODE_OPS, DECODE_PROGRAM_CAPACITY, DECODE_REGISTERS, DECODE_WEIGHT_BLOCKS, DECODE_WEIGHTS_BYTES, } from '../shaders/gpudriven/decode.wgsl.js';
3
+ export function programFromEncoded(encoded) {
4
+ return {
5
+ graph: encoded.graph,
6
+ latents: [
7
+ {
8
+ width: encoded.latentWidth,
9
+ height: encoded.latentHeight,
10
+ components: encoded.components,
11
+ levels: encoded.mips.map((level) => level.data),
12
+ },
13
+ ],
14
+ networks: [{ shape: encoded.shape, weights: encoded.weights }],
15
+ };
16
+ }
17
+ const NETWORK_AT = DECODE_PROGRAM_CAPACITY * 4;
18
+ const NODE_AT = NETWORK_AT + DECODE_NETWORK_CAPACITY * 8;
19
+ const CONSTANT_AT = DECODE_WEIGHT_BLOCKS * 4;
20
+ /** Whether an operation's `a` and `b` name registers. `decodeGraph.ts`'s `OP_ARGS`, again. */
21
+ const REGISTER_ARGS = {
22
+ [DECODE_OPS.SAMPLE_LATENT]: [false, false],
23
+ [DECODE_OPS.EVAL_NETWORK]: [true, false],
24
+ [DECODE_OPS.SAMPLE_BLOCK]: [false, false],
25
+ [DECODE_OPS.PROCEDURAL_FBM]: [false, false],
26
+ [DECODE_OPS.FLIPBOOK_INDEX]: [false, false],
27
+ [DECODE_OPS.LATENT_LERP]: [true, true],
28
+ [DECODE_OPS.REMAP_CHANNEL]: [true, false],
29
+ [DECODE_OPS.COMPOSITE]: [true, true],
30
+ [DECODE_OPS.CONSTANT]: [false, false],
31
+ };
32
+ function refuse(message) {
33
+ throw new Error(`[driftengine] ${message}`);
34
+ }
35
+ /** `networkWeightCount`, again, for the same reason `REGISTER_ARGS` is. */
36
+ function weightCount(shape) {
37
+ let total = 0;
38
+ let previous = shape.inputs;
39
+ for (const width of shape.hidden) {
40
+ total += previous * width + width;
41
+ previous = width;
42
+ }
43
+ return total + previous * shape.outputs + shape.outputs;
44
+ }
45
+ export function packDecodeTables(programs) {
46
+ if (programs.length > DECODE_PROGRAM_CAPACITY) {
47
+ refuse(`${programs.length} programs, past the ${DECODE_PROGRAM_CAPACITY} the table holds`);
48
+ }
49
+ const nodes = new Uint32Array(DECODE_NODES_BYTES / 4);
50
+ const weights = new Float32Array(DECODE_WEIGHTS_BYTES / 4);
51
+ const first = programs.flatMap((p) => [...p.latents, ...(p.blocks ?? [])])[0];
52
+ const layerSize = first?.width ?? 1;
53
+ if (!(layerSize > 0 && (layerSize & (layerSize - 1)) === 0)) {
54
+ refuse(`a latent is ${layerSize} texels across, and a layer's edge has to be a power of two`);
55
+ }
56
+ const levelCount = Math.log2(layerSize) + 1;
57
+ const chunks = Array.from({ length: levelCount }, () => []);
58
+ let layers = 0;
59
+ let networkCount = 0;
60
+ let nodeCount = 0;
61
+ let weightAt = 0;
62
+ let constantCount = 0;
63
+ const addLatent = (latent) => {
64
+ if (latent.width !== layerSize || latent.height !== layerSize) {
65
+ refuse(`a latent is ${latent.width} by ${latent.height}, and every latent in one pass is ` +
66
+ `${layerSize} by ${layerSize}`);
67
+ }
68
+ if (latent.components < 1 || latent.components > 4) {
69
+ refuse(`a latent has ${latent.components} components, not one to four`);
70
+ }
71
+ if (latent.levels.length !== levelCount) {
72
+ refuse(`a latent carries ${latent.levels.length} level${latent.levels.length === 1 ? '' : 's'}, ` +
73
+ `and a ${layerSize}-texel layer has ${levelCount}`);
74
+ }
75
+ for (let level = 0; level < levelCount; level += 1) {
76
+ const edge = Math.max(1, layerSize >> level);
77
+ const source = latent.levels[level];
78
+ if (source.length !== edge * edge * latent.components) {
79
+ refuse(`level ${level} of a latent holds ${source.length} bytes, not ${edge * edge * latent.components}`);
80
+ }
81
+ const rgba = new Uint8Array(edge * edge * 4);
82
+ for (let texel = 0; texel < edge * edge; texel += 1) {
83
+ for (let c = 0; c < 4; c += 1) {
84
+ rgba[texel * 4 + c] =
85
+ c < latent.components
86
+ ? source[texel * latent.components + c]
87
+ : c === 3
88
+ ? 255
89
+ : 0;
90
+ }
91
+ }
92
+ chunks[level].push(rgba);
93
+ }
94
+ layers += 1;
95
+ return layers - 1;
96
+ };
97
+ programs.forEach((program, p) => {
98
+ const graph = program.graph;
99
+ if (graph.addressMode >= DECODE_ADDRESS_MODES) {
100
+ refuse(`program ${p} asks for address mode ${graph.addressMode}; the interpreter knows 0 to ${DECODE_ADDRESS_MODES - 1}`);
101
+ }
102
+ if (graph.result >= DECODE_REGISTERS) {
103
+ refuse(`program ${p} returns register ${graph.result}, past the ${DECODE_REGISTERS} it has`);
104
+ }
105
+ const latentLayer = program.latents.map(addLatent);
106
+ const blockLayer = (program.blocks ?? []).map(addLatent);
107
+ const networkIndex = program.networks.map((network) => {
108
+ const { shape } = network;
109
+ if (shape.inputs < 1 || shape.inputs > DECODE_NETWORK_INPUTS) {
110
+ refuse(`a network takes ${shape.inputs} inputs; a register holds ${DECODE_NETWORK_INPUTS}`);
111
+ }
112
+ if (shape.outputs < 1 || shape.outputs > DECODE_NETWORK_OUTPUTS) {
113
+ refuse(`a network gives ${shape.outputs} outputs; a register holds ${DECODE_NETWORK_OUTPUTS}`);
114
+ }
115
+ if (shape.hidden.length > DECODE_NETWORK_HIDDEN_LAYERS) {
116
+ refuse(`a network has ${shape.hidden.length} hidden layers, past ${DECODE_NETWORK_HIDDEN_LAYERS}`);
117
+ }
118
+ for (const width of shape.hidden) {
119
+ if (width < 1 || width > DECODE_NETWORK_WIDTH) {
120
+ refuse(`a hidden layer is ${width} wide, past ${DECODE_NETWORK_WIDTH}`);
121
+ }
122
+ }
123
+ if (network.weights.length !== weightCount(shape)) {
124
+ refuse(`a network carries ${network.weights.length} weights and its shape needs ${weightCount(shape)}`);
125
+ }
126
+ if (networkCount >= DECODE_NETWORK_CAPACITY) {
127
+ refuse(`more than ${DECODE_NETWORK_CAPACITY} networks`);
128
+ }
129
+ if (weightAt + network.weights.length > DECODE_WEIGHT_BLOCKS * 4) {
130
+ refuse(`the networks need more than the ${DECODE_WEIGHT_BLOCKS * 4} weights a ` +
131
+ `${DECODE_WEIGHTS_BYTES}-byte uniform holds`);
132
+ }
133
+ const at = NETWORK_AT + networkCount * 8;
134
+ nodes[at] = weightAt;
135
+ nodes[at + 1] = shape.inputs;
136
+ nodes[at + 2] = shape.outputs;
137
+ nodes[at + 3] = shape.hidden.length;
138
+ shape.hidden.forEach((width, k) => {
139
+ nodes[at + 4 + k] = width;
140
+ });
141
+ weights.set(network.weights, weightAt);
142
+ weightAt += network.weights.length;
143
+ networkCount += 1;
144
+ return networkCount - 1;
145
+ });
146
+ const constants = program.constants ?? new Float32Array(0);
147
+ const constantIndex = [];
148
+ for (let k = 0; k * 4 < constants.length; k += 1) {
149
+ if (constantCount >= DECODE_CONSTANT_CAPACITY) {
150
+ refuse(`more than ${DECODE_CONSTANT_CAPACITY} constants`);
151
+ }
152
+ for (let c = 0; c < 4; c += 1) {
153
+ weights[CONSTANT_AT + constantCount * 4 + c] = constants[k * 4 + c] ?? 0;
154
+ }
155
+ constantIndex.push(constantCount);
156
+ constantCount += 1;
157
+ }
158
+ if (nodeCount + graph.count > DECODE_NODE_CAPACITY) {
159
+ refuse(`more than ${DECODE_NODE_CAPACITY} decode nodes`);
160
+ }
161
+ const header = p * 4;
162
+ nodes[header] = nodeCount;
163
+ nodes[header + 1] = graph.count;
164
+ nodes[header + 2] = graph.result;
165
+ nodes[header + 3] = graph.addressMode;
166
+ for (let i = 0; i < graph.count; i += 1) {
167
+ const op = graph.nodes[i * 4];
168
+ let a = graph.nodes[i * 4 + 1];
169
+ let b = graph.nodes[i * 4 + 2];
170
+ const dst = graph.nodes[i * 4 + 3];
171
+ const args = REGISTER_ARGS[op];
172
+ if (args === undefined) {
173
+ refuse(`program ${p} node ${i} has opcode ${op}, which the interpreter does not have`);
174
+ }
175
+ if (dst >= DECODE_REGISTERS ||
176
+ (args[0] && a >= DECODE_REGISTERS) ||
177
+ (args[1] && b >= DECODE_REGISTERS)) {
178
+ refuse(`program ${p} node ${i} names a register past ${DECODE_REGISTERS - 1}: ${a}, ${b}, ${dst}`);
179
+ }
180
+ if (op === DECODE_OPS.SAMPLE_LATENT) {
181
+ a =
182
+ latentLayer[a] ??
183
+ refuse(`program ${p} node ${i} samples latent ${a}, which it does not carry`);
184
+ }
185
+ else if (op === DECODE_OPS.SAMPLE_BLOCK) {
186
+ a =
187
+ blockLayer[a] ??
188
+ refuse(`program ${p} node ${i} samples block ${a}, which it does not carry`);
189
+ }
190
+ else if (op === DECODE_OPS.EVAL_NETWORK) {
191
+ b =
192
+ networkIndex[b] ??
193
+ refuse(`program ${p} node ${i} runs network ${b}, which it does not carry`);
194
+ }
195
+ else if (op === DECODE_OPS.CONSTANT) {
196
+ a =
197
+ constantIndex[a] ??
198
+ refuse(`program ${p} node ${i} reads constant ${a}, which it does not carry`);
199
+ }
200
+ else if (op === DECODE_OPS.PROCEDURAL_FBM && b > DECODE_FBM_OCTAVES) {
201
+ refuse(`program ${p} node ${i} asks for ${b} octaves, past ${DECODE_FBM_OCTAVES}`);
202
+ }
203
+ const at = NODE_AT + nodeCount * 4;
204
+ nodes[at] = op;
205
+ nodes[at + 1] = a;
206
+ nodes[at + 2] = b;
207
+ nodes[at + 3] = dst;
208
+ nodeCount += 1;
209
+ }
210
+ });
211
+ const layerCount = Math.max(1, layers);
212
+ const levels = chunks.map((parts, level) => {
213
+ const edge = Math.max(1, layerSize >> level);
214
+ const bytes = new Uint8Array(layerCount * edge * edge * 4);
215
+ parts.forEach((part, layer) => bytes.set(part, layer * edge * edge * 4));
216
+ return bytes;
217
+ });
218
+ return { nodes, weights, levels, layerCount, layerSize };
219
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Frustum planes from a view-projection matrix, and the sphere test against them.
3
+ *
4
+ * **The reference implementation, and that is its job.** The culling arithmetic is written twice —
5
+ * here for the processor and in WGSL for the device — and a test asserts the two agree over
6
+ * generated input. `splats` already establishes that discipline for shaders authored once and
7
+ * generated to two backends; this is the same rule where the second copy cannot be generated.
8
+ *
9
+ * **Reversed-Z does not change the planes, only their names.** `depthConvention.ts` maps near to 1
10
+ * and far to 0, but the two depth planes are still "clip z at or above 0" and "clip z at or below
11
+ * w" — the mapping swaps which physical plane each one is, and a sphere satisfying both is inside
12
+ * either way. Nothing here has to know which way depth runs, which is why nothing here asks.
13
+ *
14
+ * Column-major throughout, matching the matrices the renderer already passes around.
15
+ */
16
+ /** Six planes, four floats each: a, b, c, d with the normal pointing inward. */
17
+ export declare const FRUSTUM_FLOATS = 24;
18
+ export declare function frustumPlanes(viewProj: Float32Array, out: Float32Array): void;
19
+ /**
20
+ * Whether a bounding sphere is entirely outside the frustum.
21
+ *
22
+ * **Conservative in one direction only.** A sphere straddling a plane survives, because culling
23
+ * something visible is a hole in the picture and keeping something invisible is a wasted draw.
24
+ */
25
+ export declare function sphereOutsideFrustum(planes: Float32Array, cx: number, cy: number, cz: number, radius: number): boolean;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Frustum planes from a view-projection matrix, and the sphere test against them.
3
+ *
4
+ * **The reference implementation, and that is its job.** The culling arithmetic is written twice —
5
+ * here for the processor and in WGSL for the device — and a test asserts the two agree over
6
+ * generated input. `splats` already establishes that discipline for shaders authored once and
7
+ * generated to two backends; this is the same rule where the second copy cannot be generated.
8
+ *
9
+ * **Reversed-Z does not change the planes, only their names.** `depthConvention.ts` maps near to 1
10
+ * and far to 0, but the two depth planes are still "clip z at or above 0" and "clip z at or below
11
+ * w" — the mapping swaps which physical plane each one is, and a sphere satisfying both is inside
12
+ * either way. Nothing here has to know which way depth runs, which is why nothing here asks.
13
+ *
14
+ * Column-major throughout, matching the matrices the renderer already passes around.
15
+ */
16
+ /** Six planes, four floats each: a, b, c, d with the normal pointing inward. */
17
+ export const FRUSTUM_FLOATS = 24;
18
+ function setPlane(out, at, a, b, c, d) {
19
+ /* Normalised so the plane test is a true signed distance and can be compared against a radius. */
20
+ const length = Math.hypot(a, b, c);
21
+ const k = length > 0 ? 1 / length : 0;
22
+ out[at] = a * k;
23
+ out[at + 1] = b * k;
24
+ out[at + 2] = c * k;
25
+ out[at + 3] = d * k;
26
+ }
27
+ export function frustumPlanes(viewProj, out) {
28
+ const m = viewProj;
29
+ /* Rows of a column-major matrix: row i is m[i], m[4+i], m[8+i], m[12+i]. */
30
+ const r0 = [m[0], m[4], m[8], m[12]];
31
+ const r1 = [m[1], m[5], m[9], m[13]];
32
+ const r2 = [m[2], m[6], m[10], m[14]];
33
+ const r3 = [m[3], m[7], m[11], m[15]];
34
+ setPlane(out, 0, r3[0] + r0[0], r3[1] + r0[1], r3[2] + r0[2], r3[3] + r0[3]);
35
+ setPlane(out, 4, r3[0] - r0[0], r3[1] - r0[1], r3[2] - r0[2], r3[3] - r0[3]);
36
+ setPlane(out, 8, r3[0] + r1[0], r3[1] + r1[1], r3[2] + r1[2], r3[3] + r1[3]);
37
+ setPlane(out, 12, r3[0] - r1[0], r3[1] - r1[1], r3[2] - r1[2], r3[3] - r1[3]);
38
+ /* The two depth planes, in clip terms rather than near/far terms. See the header. */
39
+ setPlane(out, 16, r2[0], r2[1], r2[2], r2[3]);
40
+ setPlane(out, 20, r3[0] - r2[0], r3[1] - r2[1], r3[2] - r2[2], r3[3] - r2[3]);
41
+ }
42
+ /**
43
+ * Whether a bounding sphere is entirely outside the frustum.
44
+ *
45
+ * **Conservative in one direction only.** A sphere straddling a plane survives, because culling
46
+ * something visible is a hole in the picture and keeping something invisible is a wasted draw.
47
+ */
48
+ export function sphereOutsideFrustum(planes, cx, cy, cz, radius) {
49
+ for (let p = 0; p < 6; p += 1) {
50
+ const at = p * 4;
51
+ const distance = planes[at] * cx +
52
+ planes[at + 1] * cy +
53
+ planes[at + 2] * cz +
54
+ planes[at + 3];
55
+ if (distance < -radius)
56
+ return true;
57
+ }
58
+ return false;
59
+ }
@@ -0,0 +1,15 @@
1
+ import type { GeometrySink, StreamCapacity } from './streamScene.ts';
2
+ export interface MirrorWrite {
3
+ readonly kind: 'vertices' | 'indices';
4
+ readonly first: number;
5
+ readonly count: number;
6
+ /** The array the scene handed over, kept so a test can ask whether it was the same one. */
7
+ readonly source: Float32Array | Uint32Array;
8
+ }
9
+ export interface MirrorSink extends GeometrySink {
10
+ readonly vertices: Float32Array;
11
+ readonly indices: Uint32Array;
12
+ readonly writes: MirrorWrite[];
13
+ }
14
+ /** A mirror as large as the capacity: what the pass's two buffers would hold. */
15
+ export declare function mirrorSink(capacity: StreamCapacity): MirrorSink;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * A sink that keeps what a streaming scene writes, for tests that read its geometry back.
3
+ *
4
+ * **Not a `.test.ts` file**, for the reason `rendererHarness.ts` gives: Vitest registers a test
5
+ * when the file declaring it is imported, so a helper living beside tests would re-run them in
6
+ * every file that imported it.
7
+ *
8
+ * **The scene keeps no copy of its vertices and indices** — they go to the device of the pass
9
+ * that draws them, through a `GeometrySink` — so a test that wants to read them attaches one of
10
+ * these where the pass would have attached itself, and reads the mirror.
11
+ */
12
+ import { GPU_DRIVEN_VERTEX_FLOATS } from './sceneUpload.js';
13
+ /** A mirror as large as the capacity: what the pass's two buffers would hold. */
14
+ export function mirrorSink(capacity) {
15
+ const vertices = new Float32Array(capacity.vertices * GPU_DRIVEN_VERTEX_FLOATS);
16
+ const indices = new Uint32Array(capacity.indices);
17
+ const writes = [];
18
+ return {
19
+ vertices,
20
+ indices,
21
+ writes,
22
+ writeVertices(first, rows, count) {
23
+ vertices.set(rows.subarray(0, count * GPU_DRIVEN_VERTEX_FLOATS), first * GPU_DRIVEN_VERTEX_FLOATS);
24
+ writes.push({ kind: 'vertices', first, count, source: rows });
25
+ },
26
+ writeIndices(first, values, count) {
27
+ indices.set(values.subarray(0, count), first);
28
+ writes.push({ kind: 'indices', first, count, source: values });
29
+ },
30
+ };
31
+ }