@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
+ * The GPU-driven frame, as an order and a set of dependencies.
3
+ *
4
+ * **Every stage of Wave 2A exists on its own and this is what says how they fit together.** The
5
+ * frame's colour is cleared, the directional map is drawn, the meshes wholly outside the view are
6
+ * flagged, the cut through the cluster graph is chosen without them and phase one culls what was
7
+ * visible last frame, phase one draws, the depth pyramid is reduced from that, phase two culls the
8
+ * rest against it and draws, the visibility is copied where a dispatch can read it, binned by
9
+ * material, and each bin is shaded. Twelve stages, and the edges between them are what stop a
10
+ * scheduler reordering the ones that must not move — see `twoPhase.ts` for the one that matters
11
+ * most.
12
+ *
13
+ * **The pipeline is opt-in and WebGPU only.** `gpuDrivenSupported` is `indirectSupport`, which is
14
+ * false on WebGL2 and not a thing a fallback can paper over: without an indirect draw the counts
15
+ * this frame produces would have to come back to the processor, and a readback is slower than the
16
+ * draw it would replace. Selecting it on a backend that cannot run it **fails at creation** rather
17
+ * than falling back, because a silent fallback means a consumer ships believing they have a
18
+ * pipeline they do not.
19
+ *
20
+ * **This is what `GpuDrivenPass` is scheduled by**, since 2026-09-17: the pass records these stages
21
+ * into a `Deps` table each frame, `scheduleGraph` decides which of them run, and the pass encodes
22
+ * the ones it kept in the order it kept them. Until then this was a description beside an encoder
23
+ * with its own fixed order — and the description had drifted, naming a separate instance cull the
24
+ * encoder never had and a colour both draws wrote when neither does. The instance cull is a stage
25
+ * of the encoder now, 2026-09-17, which is `wave2a`'s fifth task finally reaching the frame. Binding real buffers and
26
+ * issuing real dispatches stays the backend's; what moved here is the decision about what runs.
27
+ */
28
+ import { indirectSupport } from '../backend/api.js';
29
+ import { recordDeps } from '../frame/deps.js';
30
+ /** Whether a backend can run this pipeline at all. */
31
+ export function gpuDrivenSupported(backend) {
32
+ return indirectSupport(backend);
33
+ }
34
+ /**
35
+ * Why a backend cannot, in the words a consumer sees.
36
+ *
37
+ * A sentence rather than a code, because the one thing the reader needs is that this is a property
38
+ * of the backend and not of their scene — and `''` where it can.
39
+ */
40
+ export function gpuDrivenRefusal(backend) {
41
+ if (gpuDrivenSupported(backend))
42
+ return '';
43
+ return (`the gpu-driven pipeline needs indirect draws and dispatches, which ${backend} does not have. ` +
44
+ 'Emulating them through a readback is slower than the draw it would replace, so this fails ' +
45
+ 'rather than falling back: a silent fallback ships a consumer a pipeline they do not have.');
46
+ }
47
+ /** The identifiers one frame uses. A frame is its own graph, so they start at zero. */
48
+ export const GPU_DRIVEN_RESOURCES = {
49
+ clusters: 0,
50
+ history: 1,
51
+ shadowMap: 2,
52
+ cut: 3,
53
+ keep: 4,
54
+ listOne: 5,
55
+ listTwo: 6,
56
+ drawn: 7,
57
+ depth: 8,
58
+ visibility: 9,
59
+ visibilityCopy: 10,
60
+ hzb: 11,
61
+ binCounts: 12,
62
+ binOffsets: 13,
63
+ binCursors: 14,
64
+ binPixels: 15,
65
+ colour: 16,
66
+ instances: 17,
67
+ blendList: 18,
68
+ oitAccum: 19,
69
+ oitReveal: 20,
70
+ };
71
+ /**
72
+ * What each stage is called, in the order they are recorded — one render or compute pass each, or
73
+ * one copy between them.
74
+ */
75
+ export const GPU_DRIVEN_PASSES = [
76
+ 'clearColour',
77
+ 'shadow',
78
+ 'instanceCull',
79
+ 'cut',
80
+ 'phaseOneDraw',
81
+ 'pyramid',
82
+ 'phaseTwoCull',
83
+ 'phaseTwoDraw',
84
+ 'blendCull',
85
+ 'blendDraw',
86
+ 'visibilityCopy',
87
+ 'bin',
88
+ 'cursorCopy',
89
+ 'shade',
90
+ 'blendResolve',
91
+ ];
92
+ /**
93
+ * **Every stage is ordered, so none merges with a neighbour.** Two adjacent compute stages could
94
+ * share a pass as far as their results go; they do not, because each is bracketed by its own pair
95
+ * of timestamps and a pair brackets a pass. What the schedule decides here is which stages run,
96
+ * and it checks the order the names give against the edges the stages declare.
97
+ */
98
+ export const GPU_DRIVEN_ORDERED = new Uint8Array(GPU_DRIVEN_PASSES.length).fill(1);
99
+ /**
100
+ * The frame's edges, stage by stage. The claim is here, and five parts of it are load-bearing
101
+ * enough to name:
102
+ *
103
+ * - `cut` reads the flags `instanceCull` wrote, so a mesh wholly outside the view has its clusters
104
+ * left out of the cut before the cluster cull or the compaction reads one of them.
105
+ * - `pyramid` reads the depth `phaseOneDraw` wrote, and `phaseTwoCull` reads the pyramid. Together
106
+ * those are "the occlusion is tested against depth from this frame", which is the whole reason
107
+ * the frame has two halves — and `cut` reads no pyramid at all, which is the other half of it.
108
+ * - `cut` and `phaseTwoCull` both read the history and both write the next one: the history
109
+ * decides which half judges a cluster, never whether one does.
110
+ * - `cursorCopy` reads the offsets `bin` wrote and `shade` reads the cursors. The scatter cannot
111
+ * start before the sum has, and neither ordering is a comment anybody has to obey.
112
+ * - `shade` reads the map only where the frame is shadowed — `unshadowedShade` — so an unshadowed
113
+ * frame's map is something nothing reads, and `scheduleGraph` culls the stage that draws it.
114
+ */
115
+ function edgesFor(res) {
116
+ const table = {
117
+ clearColour: [[], [res.colour]],
118
+ shadow: [[res.clusters], [res.shadowMap]],
119
+ instanceCull: [[res.clusters], [res.instances]],
120
+ cut: [
121
+ [res.clusters, res.history, res.instances],
122
+ [res.cut, res.keep, res.listOne, res.drawn],
123
+ ],
124
+ phaseOneDraw: [
125
+ [res.listOne, res.clusters],
126
+ [res.depth, res.visibility],
127
+ ],
128
+ pyramid: [[res.depth], [res.hzb]],
129
+ phaseTwoCull: [
130
+ [res.hzb, res.cut, res.clusters, res.history],
131
+ [res.keep, res.listTwo, res.drawn],
132
+ ],
133
+ phaseTwoDraw: [
134
+ [res.listTwo, res.clusters, res.depth, res.visibility],
135
+ [res.depth, res.visibility],
136
+ ],
137
+ /*
138
+ * **The cut's second run, over the other half of the scene.** It reads what the first run read
139
+ * and writes the same working buffers, which is safe because it runs after both draws have
140
+ * taken what they needed from them — and it writes a list of its own, which is what the blended
141
+ * draw reads. It reads the pyramid too: a pane wholly behind a wall is occluded exactly as an
142
+ * opaque cluster there would be.
143
+ */
144
+ blendCull: [
145
+ [res.clusters, res.instances, res.hzb],
146
+ [res.cut, res.keep, res.blendList],
147
+ ],
148
+ /*
149
+ * **It reads the depth the opaque half wrote and does not write it.** A pane behind a wall is
150
+ * rejected at the wall; nothing here writes depth, so two panes do not hide each other and
151
+ * there is something left to blend.
152
+ */
153
+ blendDraw: [
154
+ [res.blendList, res.clusters, res.depth],
155
+ [res.oitAccum, res.oitReveal],
156
+ ],
157
+ visibilityCopy: [[res.visibility], [res.visibilityCopy]],
158
+ bin: [[res.visibilityCopy], [res.binCounts, res.binOffsets]],
159
+ cursorCopy: [[res.binOffsets], [res.binCursors]],
160
+ shade: [
161
+ [res.visibilityCopy, res.binOffsets, res.binCursors, res.clusters, res.colour, res.shadowMap],
162
+ [res.binPixels, res.colour],
163
+ ],
164
+ /*
165
+ * **After the shading, because it composites over what the shading wrote.** The opaque picture
166
+ * is the background a transparent layer is blended against, so this is the last thing the
167
+ * frame does to its own colour target.
168
+ */
169
+ blendResolve: [[res.oitAccum, res.oitReveal, res.colour], [res.colour]],
170
+ };
171
+ return {
172
+ stages: GPU_DRIVEN_PASSES.map((name) => table[name]),
173
+ unshadowedShade: table.shade[0].filter((id) => id !== res.shadowMap),
174
+ };
175
+ }
176
+ /* Built once a resource table, so a frame records without constructing an edge list. */
177
+ const EDGES = new WeakMap();
178
+ const SHADE = GPU_DRIVEN_PASSES.indexOf('shade');
179
+ /**
180
+ * The three stages a frame with nothing transparent does not record at all.
181
+ *
182
+ * **Culled by giving them no edges rather than by filtering a read**, which is how the shadow map
183
+ * is culled one line above and does not work here: `blendResolve` writes the colour, the colour is
184
+ * what the frame is kept alive for, so a stage that writes it survives any amount of filtering. A
185
+ * stage with no writes is read by nothing and the scheduler drops it, which is the same answer
186
+ * reached the only way that is true for this one.
187
+ */
188
+ const BLEND_STAGES = [
189
+ GPU_DRIVEN_PASSES.indexOf('blendCull'),
190
+ GPU_DRIVEN_PASSES.indexOf('blendDraw'),
191
+ GPU_DRIVEN_PASSES.indexOf('blendResolve'),
192
+ ];
193
+ const NOTHING = [];
194
+ /**
195
+ * Record the whole frame, in order, and answer the index its first stage landed at.
196
+ *
197
+ * Allocates nothing once a resource table has been seen, which is what lets `GpuDrivenPass` call it
198
+ * every frame. Stage `i` of `GPU_DRIVEN_PASSES` is node `first + i`.
199
+ */
200
+ export function recordGpuDrivenStages(deps, res, shape) {
201
+ let edges = EDGES.get(res);
202
+ if (edges === undefined) {
203
+ edges = edgesFor(res);
204
+ EDGES.set(res, edges);
205
+ }
206
+ const first = deps.count;
207
+ for (let stage = 0; stage < edges.stages.length; stage += 1) {
208
+ if (!shape.blended && BLEND_STAGES.includes(stage)) {
209
+ recordDeps(deps, NOTHING, NOTHING);
210
+ continue;
211
+ }
212
+ const [reads, writes] = edges.stages[stage];
213
+ recordDeps(deps, stage === SHADE && !shape.shadowed ? edges.unshadowedShade : reads, writes);
214
+ }
215
+ return first;
216
+ }
217
+ /** Record the whole frame and answer where each pass went, by name. For a reader, not a frame. */
218
+ export function recordGpuDrivenFrame(deps, res, shape) {
219
+ const first = recordGpuDrivenStages(deps, res, shape);
220
+ return Object.fromEntries(GPU_DRIVEN_PASSES.map((name, index) => [name, first + index]));
221
+ }
222
+ /**
223
+ * What a frame leaves behind, which is what `scheduleGraph` is asked to keep alive.
224
+ *
225
+ * The colour, which the blit presents, and the history, which next frame's phase one reads. The
226
+ * depth, the pyramid, the visibility buffer and every bin are this frame's working set and nothing
227
+ * outside reads them. Naming more than is needed keeps resources alive that could have been
228
+ * aliased — which is `alias.ts`'s whole subject — and naming fewer culls a pass that was doing
229
+ * something.
230
+ */
231
+ export function gpuDrivenLive(res) {
232
+ return [res.colour, res.drawn];
233
+ }
234
+ /** What a frame reads without having written: the scene, and last frame's history. */
235
+ export function gpuDrivenImports(res) {
236
+ return [res.clusters, res.history];
237
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * A free list over one axis of units, and nothing else.
3
+ *
4
+ * **Four of these make a streaming scene** — vertices, indices, clusters and meshes — and none of
5
+ * them needs to know what a cluster is. Keeping it that way is what lets "a returned range is
6
+ * reusable" be a test that does not build a mesh.
7
+ *
8
+ * **Best fit rather than first fit**, because the workload that motivated this remeshes a chunk
9
+ * into a slightly different size on every block a player breaks. First fit hands the front of the
10
+ * largest hole to whatever asks next, so the large holes disappear first and the request that
11
+ * needed one is refused while the total says there is room.
12
+ *
13
+ * **Refusal is `-1` rather than an exception**, because a streaming consumer calls `claim` from a
14
+ * frame loop: an exception there is a crashed demo where a refusal is a chunk that arrives a frame
15
+ * later.
16
+ */
17
+ export declare class RangeAlloc {
18
+ /**
19
+ * The free runs, kept sorted by `at` and never touching.
20
+ *
21
+ * **Sorted and coalesced is an invariant rather than a convenience**: it is what makes the
22
+ * layout independent of the order ranges were released in, which a capture would otherwise
23
+ * expose as an image that differs between runs with nothing failing.
24
+ */
25
+ private holes;
26
+ private readonly total;
27
+ private usedUnits;
28
+ private high;
29
+ constructor(capacity: number);
30
+ get capacity(): number;
31
+ get used(): number;
32
+ /** The most units ever live at once. `scripts/stream-fragmentation.mjs` reads this. */
33
+ get highWater(): number;
34
+ /**
35
+ * The first unit of a run of `units`, or -1 where no single run is long enough.
36
+ *
37
+ * A request for nothing succeeds at 0 and occupies nothing: a mesh with no clusters is legal,
38
+ * and `buildGpuDrivenScene`'s header says what dropping one would do to every mesh after it.
39
+ */
40
+ claim(units: number): number;
41
+ /**
42
+ * Give a run back, merging it with whatever it now touches.
43
+ *
44
+ * **Merged on the way in rather than swept later**, which is what keeps the invariant true at
45
+ * every moment a caller could look and makes the order releases arrived in stop mattering.
46
+ */
47
+ release(at: number, units: number): void;
48
+ /** Back to one free run, keeping the high-water mark, which is a record of the run so far. */
49
+ reset(): void;
50
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * A free list over one axis of units, and nothing else.
3
+ *
4
+ * **Four of these make a streaming scene** — vertices, indices, clusters and meshes — and none of
5
+ * them needs to know what a cluster is. Keeping it that way is what lets "a returned range is
6
+ * reusable" be a test that does not build a mesh.
7
+ *
8
+ * **Best fit rather than first fit**, because the workload that motivated this remeshes a chunk
9
+ * into a slightly different size on every block a player breaks. First fit hands the front of the
10
+ * largest hole to whatever asks next, so the large holes disappear first and the request that
11
+ * needed one is refused while the total says there is room.
12
+ *
13
+ * **Refusal is `-1` rather than an exception**, because a streaming consumer calls `claim` from a
14
+ * frame loop: an exception there is a crashed demo where a refusal is a chunk that arrives a frame
15
+ * later.
16
+ */
17
+ export class RangeAlloc {
18
+ /**
19
+ * The free runs, kept sorted by `at` and never touching.
20
+ *
21
+ * **Sorted and coalesced is an invariant rather than a convenience**: it is what makes the
22
+ * layout independent of the order ranges were released in, which a capture would otherwise
23
+ * expose as an image that differs between runs with nothing failing.
24
+ */
25
+ holes;
26
+ total;
27
+ usedUnits = 0;
28
+ high = 0;
29
+ constructor(capacity) {
30
+ const units = Math.max(0, Math.floor(capacity));
31
+ this.total = units;
32
+ this.holes = units > 0 ? [{ at: 0, units }] : [];
33
+ }
34
+ get capacity() {
35
+ return this.total;
36
+ }
37
+ get used() {
38
+ return this.usedUnits;
39
+ }
40
+ /** The most units ever live at once. `scripts/stream-fragmentation.mjs` reads this. */
41
+ get highWater() {
42
+ return this.high;
43
+ }
44
+ /**
45
+ * The first unit of a run of `units`, or -1 where no single run is long enough.
46
+ *
47
+ * A request for nothing succeeds at 0 and occupies nothing: a mesh with no clusters is legal,
48
+ * and `buildGpuDrivenScene`'s header says what dropping one would do to every mesh after it.
49
+ */
50
+ claim(units) {
51
+ const want = Math.max(0, Math.floor(units));
52
+ if (want === 0)
53
+ return 0;
54
+ let best = -1;
55
+ for (let i = 0; i < this.holes.length; i += 1) {
56
+ const hole = this.holes[i];
57
+ if (hole.units < want)
58
+ continue;
59
+ if (best < 0 || hole.units < this.holes[best].units)
60
+ best = i;
61
+ }
62
+ if (best < 0)
63
+ return -1;
64
+ const hole = this.holes[best];
65
+ const at = hole.at;
66
+ if (hole.units === want)
67
+ this.holes.splice(best, 1);
68
+ else {
69
+ hole.at += want;
70
+ hole.units -= want;
71
+ }
72
+ this.usedUnits += want;
73
+ if (this.usedUnits > this.high)
74
+ this.high = this.usedUnits;
75
+ return at;
76
+ }
77
+ /**
78
+ * Give a run back, merging it with whatever it now touches.
79
+ *
80
+ * **Merged on the way in rather than swept later**, which is what keeps the invariant true at
81
+ * every moment a caller could look and makes the order releases arrived in stop mattering.
82
+ */
83
+ release(at, units) {
84
+ const count = Math.max(0, Math.floor(units));
85
+ if (count === 0)
86
+ return;
87
+ const start = Math.max(0, Math.floor(at));
88
+ let index = 0;
89
+ while (index < this.holes.length && this.holes[index].at < start)
90
+ index += 1;
91
+ this.holes.splice(index, 0, { at: start, units: count });
92
+ this.usedUnits = Math.max(0, this.usedUnits - count);
93
+ /* Merge with the one before and the one after, in that order, so three runs become one. */
94
+ const previous = index > 0 ? this.holes[index - 1] : null;
95
+ if (previous !== null && previous.at + previous.units === start) {
96
+ previous.units += count;
97
+ this.holes.splice(index, 1);
98
+ index -= 1;
99
+ }
100
+ const merged = this.holes[index];
101
+ const next = index + 1 < this.holes.length ? this.holes[index + 1] : null;
102
+ if (next !== null && merged.at + merged.units === next.at) {
103
+ merged.units += next.units;
104
+ this.holes.splice(index + 1, 1);
105
+ }
106
+ }
107
+ /** Back to one free run, keeping the high-water mark, which is a record of the run so far. */
108
+ reset() {
109
+ this.holes = this.total > 0 ? [{ at: 0, units: this.total }] : [];
110
+ this.usedUnits = 0;
111
+ }
112
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * A scene's meshes packed into the one set of buffers a GPU-driven frame reads.
3
+ *
4
+ * **`clusterUpload.ts` packs the clusters and this packs everything they point at.** That split is
5
+ * not arbitrary: the cluster packing is about identifiers and spans and has no idea a vertex
6
+ * exists, and it rebases each mesh's index *offsets* by the indices already written while leaving
7
+ * the index *values* alone. Which is right — a mesh's indices address that mesh's vertices — and
8
+ * is exactly wrong the moment two meshes share one buffer, which is the whole point of the
9
+ * pipeline. Every mesh after the first would address vertex zero of the *scene*, a real vertex
10
+ * belonging to the first mesh, drawn in the wrong place with nothing reporting anything.
11
+ *
12
+ * So the rebasing happens here, where the vertex base is known, and the packer is handed sources
13
+ * whose indices are already scene-global.
14
+ *
15
+ * **Three parallel arrays rather than one interleaved vertex.** The raster shader reads
16
+ * `positions[index * 3]` and the shading pass reads normals and colours the same way; an
17
+ * interleaved stride would put the layout in three shaders instead of one place, and the memory
18
+ * this saves is not the memory the pipeline is short of.
19
+ *
20
+ * **Colour is a vertex attribute, because that is what this engine's geometry carries.**
21
+ * `MeshData` has `colors` and most of it is untextured, so the shading pass interpolates them the
22
+ * way the forward path does rather than inventing a material system to sit beside the one that
23
+ * exists. What a *material* then selects is the shading each bin gets — see `materialBin.ts` — and
24
+ * a mesh names one.
25
+ */
26
+ import { type ClusterBuffers, type ClusterSource } from './clusterUpload.ts';
27
+ /**
28
+ * Floats a vertex in the buffer the GPU-driven pass uploads: position, normal, colour, uv, glow.
29
+ *
30
+ * **One number read by four consumers** — the upload, the raster, the blended raster and the
31
+ * shading pass — and `vertexLayout.test.ts` holds all four to it. It was nine until UVs arrived on
32
+ * 2026-09-17, and eleven until the voxel sandbox's block light needed a glow a vertex on 2026-09-18.
33
+ */
34
+ export declare const GPU_DRIVEN_VERTEX_FLOATS = 12;
35
+ /** One mesh of a GPU-driven scene: its vertices, its clusters, and which material shades it. */
36
+ export interface GpuDrivenMesh {
37
+ /** Three floats a vertex. */
38
+ readonly positions: Float32Array;
39
+ /** Three floats a vertex. */
40
+ readonly normals: Float32Array;
41
+ /** Three floats a vertex. */
42
+ readonly colours: Float32Array;
43
+ /**
44
+ * Two floats a vertex. Optional: a mesh without them uploads zeros, and a textured material on
45
+ * it reads one texel everywhere and turns no normal — a statement about the mesh, and what the
46
+ * forward path does with an absent attribute.
47
+ */
48
+ readonly uvs?: Float32Array;
49
+ /**
50
+ * One float a vertex, added to the material's own emissive. Optional: a mesh without it glows as
51
+ * its material says and nothing more, which is every mesh before 2026-09-18.
52
+ *
53
+ * **For light a colour cannot carry.** The voxel sandbox folds occlusion and sky light into the
54
+ * vertex colour and keeps block light here, because it has to survive the sun going down and a
55
+ * colour is multiplied by the sun. The forward path reads the same attribute from `MeshData`.
56
+ */
57
+ readonly emissive?: Float32Array;
58
+ /** This mesh's clusters, with indices addressing *this mesh's* vertices. */
59
+ readonly clusters: ClusterSource;
60
+ /** Which material bin every cluster of this mesh shades in. */
61
+ readonly material: number;
62
+ }
63
+ export interface GpuDrivenScene {
64
+ /** Every mesh's positions, concatenated in the order they were given. */
65
+ readonly positions: Float32Array;
66
+ readonly normals: Float32Array;
67
+ readonly colours: Float32Array;
68
+ /** Two floats a vertex, zeros where a mesh carried none. */
69
+ readonly uvs: Float32Array;
70
+ /** One float a vertex, zeros where a mesh carried none. */
71
+ readonly emissive: Float32Array;
72
+ /** The clusters, with indices addressing `positions` rather than any one mesh. */
73
+ readonly clusters: ClusterBuffers;
74
+ /** One material a cluster, from the mesh it came from. */
75
+ readonly materialOf: Uint32Array;
76
+ /** Where each mesh's vertices begin. One entry a mesh, including meshes with no clusters. */
77
+ readonly vertexBase: Uint32Array;
78
+ readonly vertexCount: number;
79
+ }
80
+ /**
81
+ * A mesh's per-vertex glow, refused by name when it is not one float a vertex.
82
+ *
83
+ * Shared by both packers, because a glow of the wrong length read one float a vertex is somebody
84
+ * else's torchlight on the next mesh along — or `NaN` past the end, which lights nothing and says
85
+ * nothing about why.
86
+ */
87
+ export declare function glowOf(mesh: GpuDrivenMesh, at: number | string): Float32Array | undefined;
88
+ /**
89
+ * Pack a scene.
90
+ *
91
+ * **A mesh with no clusters still takes its slot**, in `vertexBase` and in the mesh numbering the
92
+ * clusters carry. `meshOf` indexes the caller's transform array, so dropping an empty mesh shifts
93
+ * every mesh after it and each of their clusters draws with somebody else's matrix — the same
94
+ * reasoning `packClusters` already gives, one level up.
95
+ */
96
+ export declare function buildGpuDrivenScene(meshes: readonly GpuDrivenMesh[]): GpuDrivenScene;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * A scene's meshes packed into the one set of buffers a GPU-driven frame reads.
3
+ *
4
+ * **`clusterUpload.ts` packs the clusters and this packs everything they point at.** That split is
5
+ * not arbitrary: the cluster packing is about identifiers and spans and has no idea a vertex
6
+ * exists, and it rebases each mesh's index *offsets* by the indices already written while leaving
7
+ * the index *values* alone. Which is right — a mesh's indices address that mesh's vertices — and
8
+ * is exactly wrong the moment two meshes share one buffer, which is the whole point of the
9
+ * pipeline. Every mesh after the first would address vertex zero of the *scene*, a real vertex
10
+ * belonging to the first mesh, drawn in the wrong place with nothing reporting anything.
11
+ *
12
+ * So the rebasing happens here, where the vertex base is known, and the packer is handed sources
13
+ * whose indices are already scene-global.
14
+ *
15
+ * **Three parallel arrays rather than one interleaved vertex.** The raster shader reads
16
+ * `positions[index * 3]` and the shading pass reads normals and colours the same way; an
17
+ * interleaved stride would put the layout in three shaders instead of one place, and the memory
18
+ * this saves is not the memory the pipeline is short of.
19
+ *
20
+ * **Colour is a vertex attribute, because that is what this engine's geometry carries.**
21
+ * `MeshData` has `colors` and most of it is untextured, so the shading pass interpolates them the
22
+ * way the forward path does rather than inventing a material system to sit beside the one that
23
+ * exists. What a *material* then selects is the shading each bin gets — see `materialBin.ts` — and
24
+ * a mesh names one.
25
+ */
26
+ import { packClusters } from './clusterUpload.js';
27
+ /**
28
+ * Floats a vertex in the buffer the GPU-driven pass uploads: position, normal, colour, uv, glow.
29
+ *
30
+ * **One number read by four consumers** — the upload, the raster, the blended raster and the
31
+ * shading pass — and `vertexLayout.test.ts` holds all four to it. It was nine until UVs arrived on
32
+ * 2026-09-17, and eleven until the voxel sandbox's block light needed a glow a vertex on 2026-09-18.
33
+ */
34
+ export const GPU_DRIVEN_VERTEX_FLOATS = 12;
35
+ /**
36
+ * A mesh's per-vertex glow, refused by name when it is not one float a vertex.
37
+ *
38
+ * Shared by both packers, because a glow of the wrong length read one float a vertex is somebody
39
+ * else's torchlight on the next mesh along — or `NaN` past the end, which lights nothing and says
40
+ * nothing about why.
41
+ */
42
+ export function glowOf(mesh, at) {
43
+ const glow = mesh.emissive;
44
+ if (glow === undefined)
45
+ return undefined;
46
+ const count = mesh.positions.length / 3;
47
+ if (glow.length !== count) {
48
+ throw new Error(`[driftengine] mesh ${at} carries ${glow.length} emissive values for ${count} vertices`);
49
+ }
50
+ return glow;
51
+ }
52
+ /**
53
+ * Pack a scene.
54
+ *
55
+ * **A mesh with no clusters still takes its slot**, in `vertexBase` and in the mesh numbering the
56
+ * clusters carry. `meshOf` indexes the caller's transform array, so dropping an empty mesh shifts
57
+ * every mesh after it and each of their clusters draws with somebody else's matrix — the same
58
+ * reasoning `packClusters` already gives, one level up.
59
+ */
60
+ export function buildGpuDrivenScene(meshes) {
61
+ let vertices = 0;
62
+ let clusters = 0;
63
+ const vertexBase = new Uint32Array(meshes.length);
64
+ for (let at = 0; at < meshes.length; at += 1) {
65
+ const mesh = meshes[at];
66
+ vertexBase[at] = vertices;
67
+ vertices += mesh.positions.length / 3;
68
+ clusters += mesh.clusters.count;
69
+ }
70
+ const positions = new Float32Array(vertices * 3);
71
+ const normals = new Float32Array(vertices * 3);
72
+ const colours = new Float32Array(vertices * 3);
73
+ const uvs = new Float32Array(vertices * 2);
74
+ const emissive = new Float32Array(vertices);
75
+ const rebased = [];
76
+ const materialOf = new Uint32Array(clusters);
77
+ let cluster = 0;
78
+ for (let at = 0; at < meshes.length; at += 1) {
79
+ const mesh = meshes[at];
80
+ const base = vertexBase[at];
81
+ positions.set(mesh.positions, base * 3);
82
+ normals.set(mesh.normals, base * 3);
83
+ colours.set(mesh.colours, base * 3);
84
+ if (mesh.uvs !== undefined) {
85
+ const count = mesh.positions.length / 3;
86
+ if (mesh.uvs.length !== count * 2) {
87
+ throw new Error(`[driftengine] mesh ${at} carries ${mesh.uvs.length} uv values for ${count} vertices`);
88
+ }
89
+ uvs.set(mesh.uvs, base * 2);
90
+ }
91
+ const glow = glowOf(mesh, at);
92
+ if (glow !== undefined)
93
+ emissive.set(glow, base);
94
+ const indices = new Uint32Array(mesh.clusters.indices.length);
95
+ for (let i = 0; i < indices.length; i += 1) {
96
+ indices[i] = mesh.clusters.indices[i] + base;
97
+ }
98
+ rebased.push({ ...mesh.clusters, indices });
99
+ for (let c = 0; c < mesh.clusters.count; c += 1) {
100
+ materialOf[cluster] = mesh.material;
101
+ cluster += 1;
102
+ }
103
+ }
104
+ return {
105
+ positions,
106
+ normals,
107
+ colours,
108
+ uvs,
109
+ emissive,
110
+ clusters: packClusters(rebased),
111
+ materialOf,
112
+ vertexBase,
113
+ vertexCount: vertices,
114
+ };
115
+ }