@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,223 @@
1
+ /** The second level of the chain: march the world's own distance field, where the screen ran out. */
2
+
3
+ import { sampleGlobalField } from './globalField.ts';
4
+
5
+ import type { ReadonlyVec3 } from 'gl-matrix';
6
+ import type { GlobalField, GlobalFieldCascade } from './globalField.ts';
7
+
8
+ /**
9
+ * **This is the level that makes the one above it legitimate.**
10
+ *
11
+ * `ROADMAP.md` refused screen-space global illumination because its error is unbounded: a ray that
12
+ * leaves the frame has no answer at all, and every technique that ships one anyway invents it from
13
+ * whatever happened to be on screen. Here the screen is an accelerator with something behind it,
14
+ * and this is the something. Its error is bounded by the field's resolution — a number written
15
+ * down, measurable, and the same in every direction — rather than by what the camera is pointing
16
+ * at.
17
+ *
18
+ * **Sphere tracing, which is the only march a distance field admits.** Step by the field's own
19
+ * value: it is a distance nothing can be nearer than, so the step is as long as it can safely be
20
+ * and the march converges in a handful of steps rather than in a fixed grid walk. Every invariant
21
+ * that makes that true is tested where it is produced — `sdf.ts` measures the gradient's length,
22
+ * `globalField.ts` measures that the composed field never overestimates — because a march cannot
23
+ * tell a field that is a distance from one that merely has the right sign.
24
+ */
25
+
26
+ /** How the march is shaped. */
27
+ export interface FieldMarch {
28
+ /** The most samples one ray may take. A converging march uses a fraction of this. */
29
+ readonly steps: number;
30
+ /** How far the ray travels in world metres before it gives up. */
31
+ readonly reachM: number;
32
+ /**
33
+ * Half-angle of the cone, in radians. Zero is a pencil ray.
34
+ *
35
+ * **A rough surface reflects a lobe rather than a ray**, and a cone is how a distance field
36
+ * traces one: the radius grows with the distance travelled and a hit is where the field falls
37
+ * below *that* rather than below a constant. The blur is then correct by construction — a wide
38
+ * cone stops further from a surface, so what it gathers is a broader patch of it — instead of
39
+ * being a filter applied to a sharp answer afterwards.
40
+ */
41
+ readonly coneAngle: number;
42
+ /** How close to a surface counts as arriving, in metres, before the cone's own radius. */
43
+ readonly hitEpsilonM: number;
44
+ }
45
+
46
+ /**
47
+ * How far along the ray a march starts, in voxels of the finest cascade.
48
+ *
49
+ * **Proportional to the field's resolution and not a constant, because what it is escaping is the
50
+ * field's own error.** A trilinear read of a grid is wrong by something on the order of a voxel
51
+ * near a surface, so a ray leaving a surface is inside that error until it has travelled about
52
+ * that far — and a march that starts at zero reports a hit before it has moved, on every surface
53
+ * at once. A voxel is a different number in each cascade and in each scene, which is exactly why
54
+ * this is a count of them rather than a length.
55
+ */
56
+ export const FIELD_START_VOXELS = 2;
57
+
58
+ /** The march an indirect ray takes through the world field. */
59
+ export const FIELD_MARCH: FieldMarch = {
60
+ steps: 64,
61
+ reachM: 20,
62
+ coneAngle: 0,
63
+ hitEpsilonM: 0.01,
64
+ };
65
+
66
+ /** Where a march ended. Reused rather than returned, because the chain runs this per ray. */
67
+ export interface FieldHit {
68
+ hit: boolean;
69
+ /** Where it arrived, in world metres. */
70
+ x: number;
71
+ y: number;
72
+ z: number;
73
+ /**
74
+ * How far along the ray, world metres, from the **lifted** origin.
75
+ *
76
+ * The lift is `FIELD_START_VOXELS` voxels along the normal, so this differs from the distance to
77
+ * the point a caller passed in by at most that — and by nothing at all where the caller passed
78
+ * no normal, which is what a ray from open space does.
79
+ */
80
+ distanceM: number;
81
+ /** The cone's radius there, which is how broad a patch of surface this hit stands for. */
82
+ radiusM: number;
83
+ /** How many samples it took. Diagnostic, and the thing a convergence test asserts on. */
84
+ steps: number;
85
+ /**
86
+ * Whether it left the field rather than running out of reach.
87
+ *
88
+ * **The two misses are different and the chain acts on the difference.** A ray that escaped has
89
+ * gone somewhere this level has no information about, and the probe volume behind it is what
90
+ * answers. A ray that ran out of reach travelled its whole budget through open space, which is a
91
+ * statement about the world rather than about the field.
92
+ */
93
+ escaped: boolean;
94
+ }
95
+
96
+ export function newFieldHit(): FieldHit {
97
+ return { hit: false, x: 0, y: 0, z: 0, distanceM: 0, radiusM: 0, steps: 0, escaped: false };
98
+ }
99
+
100
+ /** The cone's radius after travelling `distance`, for a cone of this half-angle. */
101
+ export function coneRadiusAt(distanceM: number, coneAngle: number): number {
102
+ return coneAngle <= 0 ? 0 : distanceM * Math.tan(coneAngle);
103
+ }
104
+
105
+ /**
106
+ * March a ray through the composed world field.
107
+ *
108
+ * Returns whether it arrived at a surface. A false is the chain's cue to go one level further
109
+ * down; `FieldHit.escaped` says which of the two misses it was.
110
+ */
111
+ export function traceField(
112
+ field: GlobalField,
113
+ point: ReadonlyVec3,
114
+ normal: ReadonlyVec3,
115
+ direction: ReadonlyVec3,
116
+ march: FieldMarch,
117
+ out: FieldHit,
118
+ ): boolean {
119
+ out.hit = false;
120
+ out.x = 0;
121
+ out.y = 0;
122
+ out.z = 0;
123
+ out.distanceM = 0;
124
+ out.radiusM = 0;
125
+ out.steps = 0;
126
+ out.escaped = false;
127
+
128
+ const dx = direction[0] ?? 0;
129
+ const dy = direction[1] ?? 0;
130
+ const dz = direction[2] ?? 0;
131
+ const length = Math.hypot(dx, dy, dz);
132
+ if (!(length > 0)) {
133
+ throw new Error(
134
+ 'traceField: the direction has no length, so every step would be the same point and the ' +
135
+ 'march would spend its whole budget standing still.',
136
+ );
137
+ }
138
+ const ux = dx / length;
139
+ const uy = dy / length;
140
+ const uz = dz / length;
141
+
142
+ const finest = field.cascades[0] as GlobalFieldCascade;
143
+ const start = FIELD_START_VOXELS * finest.step;
144
+
145
+ /*
146
+ * **Lifted along the normal, and only along the normal.** Pushing the start along the *ray* as
147
+ * well double-counts — a ray leaving along its own normal would then begin two offsets out, and
148
+ * every distance it reported would carry the second one — and it does not help the case that
149
+ * needs help: a tangent ray pushed along itself is still on the surface, because a surface is
150
+ * what tangent means. The normal is the one direction that always leaves.
151
+ *
152
+ * A point with no normal, which is what a ray from open space has, is not lifted at all, so
153
+ * `distanceM` is measured from exactly where the caller asked.
154
+ */
155
+ const nx = normal[0] ?? 0;
156
+ const ny = normal[1] ?? 0;
157
+ const nz = normal[2] ?? 0;
158
+ const normalLength = Math.hypot(nx, ny, nz);
159
+ const lift = normalLength > 0 ? start / normalLength : 0;
160
+ const ox = (point[0] ?? 0) + nx * lift;
161
+ const oy = (point[1] ?? 0) + ny * lift;
162
+ const oz = (point[2] ?? 0) + nz * lift;
163
+
164
+ const steps = Math.max(1, Math.round(march.steps));
165
+ let travelled = 0;
166
+
167
+ for (let step = 0; step < steps; step++) {
168
+ const x = ox + ux * travelled;
169
+ const y = oy + uy * travelled;
170
+ const z = oz + uz * travelled;
171
+ out.steps = step + 1;
172
+
173
+ /*
174
+ * **Outside the outermost cascade the march stops rather than sampling.** There is no
175
+ * information out there at all: `sampleGlobalField` clamps to the edge, which is the right
176
+ * answer for a sample and the wrong one for a march — stepping on clamped values converges on
177
+ * the boundary of the cascade and reports a surface made of nothing.
178
+ */
179
+ if (!withinField(field, x, y, z)) {
180
+ out.escaped = true;
181
+ out.distanceM = travelled;
182
+ return false;
183
+ }
184
+
185
+ const distance = sampleGlobalField(field, x, y, z);
186
+ const radius = coneRadiusAt(travelled, march.coneAngle);
187
+ if (distance <= radius + march.hitEpsilonM) {
188
+ out.hit = true;
189
+ out.x = x;
190
+ out.y = y;
191
+ out.z = z;
192
+ out.distanceM = travelled;
193
+ out.radiusM = radius;
194
+ return true;
195
+ }
196
+
197
+ /*
198
+ * **Step by the distance, and by at least a fraction of a voxel.** A field whose value is
199
+ * genuinely tiny beside a surface the cone is not wide enough to accept would otherwise take
200
+ * ever-smaller steps and spend the whole budget arriving nowhere, which reads as a miss and
201
+ * costs a fallback. The floor makes that case terminate as a miss quickly instead.
202
+ */
203
+ travelled += Math.max(distance, finest.step * 0.25);
204
+ if (travelled > march.reachM) {
205
+ out.distanceM = march.reachM;
206
+ return false;
207
+ }
208
+ }
209
+
210
+ out.distanceM = travelled;
211
+ return false;
212
+ }
213
+
214
+ /** Whether a point is inside the outermost cascade, which is the whole of what the field knows. */
215
+ function withinField(field: GlobalField, x: number, y: number, z: number): boolean {
216
+ const outer = field.cascades[field.cascades.length - 1] as GlobalFieldCascade;
217
+ const p = [x, y, z];
218
+ for (let axis = 0; axis < 3; axis++) {
219
+ if ((p[axis] as number) < (outer.bounds[axis] as number)) return false;
220
+ if ((p[axis] as number) > (outer.bounds[axis + 3] as number)) return false;
221
+ }
222
+ return true;
223
+ }
@@ -0,0 +1,106 @@
1
+ /** The first level of the chain: trace the frame, which is exact wherever the frame has the answer. */
2
+
3
+ import { traceScreenSpaceRay } from '../screenSpaceReflection.ts';
4
+
5
+ import type { ReadonlyVec3 } from 'gl-matrix';
6
+ import type { ScreenSpaceHit, ScreenSpaceMarch } from '../screenSpaceReflection.ts';
7
+
8
+ /**
9
+ * **This module is thin, and that is the finding rather than a shortcut.**
10
+ *
11
+ * The plan for this wave asked for a screen-space first hit whose hardest requirement is that *a
12
+ * ray leaving the screen returns a miss, never a clamped edge hit* — because a clamped edge hit is
13
+ * exactly the unbounded error `ROADMAP.md` refused screen-space global illumination over.
14
+ * `screenSpaceReflection.ts` already makes that refusal, in `projectToUv`, with the reasoning
15
+ * written out and a test beside it: a point behind the eye has a negative `w` and divides into a
16
+ * perfectly ordinary screen position somewhere else in the frame, and a point outside the frame
17
+ * has no pixel at all. `traceScreenSpaceRay` already treats a hit as a *crossing* rather than a
18
+ * comparison, which is the second requirement, and already carries a ray over a gap in the
19
+ * geometry rather than stopping at its edge, which is the third.
20
+ *
21
+ * So writing a second march would be a second thing to be wrong, and the two would disagree about
22
+ * a frame the day one of them was changed. What is here is the part global illumination owns that
23
+ * a reflection does not: **where the ray starts.**
24
+ *
25
+ * A reflection begins at a surface a caller declared reflective and leaves it at the mirror angle,
26
+ * so it moves away from that surface immediately. An indirect ray begins at whatever pixel is being
27
+ * shaded and leaves it in a direction drawn from the hemisphere — which means a good fraction of
28
+ * them leave nearly tangent, stay inside the depth buffer's own rounding of the surface for their
29
+ * whole length, and find a crossing made of quantisation noise. That is a hit on the surface the
30
+ * ray is standing on, taken by every pixel of a floor at once.
31
+ */
32
+
33
+ /**
34
+ * How far along the normal an indirect ray starts, in metres.
35
+ *
36
+ * **It has to be larger than the depth buffer's quantum and smaller than anything it could hide.**
37
+ * Two centimetres is about twenty times the rounding of a 24-bit buffer over a hundred-metre range
38
+ * at close quarters, and is below the contact detail a viewer reads — the gap where an object meets
39
+ * a floor, which is the first thing indirect light is judged on.
40
+ *
41
+ * **What would make it wrong** is a scene whose unit is not the metre, which this engine's is; and
42
+ * a depth range far enough that the quantum grows past it, which is a reason to bias by a fraction
43
+ * of the sampled depth rather than by a constant. Nothing measured yet asks for that.
44
+ */
45
+ export const SCREEN_TRACE_BIAS_M = 0.02;
46
+
47
+ /**
48
+ * The march an indirect ray takes, which is not the one a mirror takes.
49
+ *
50
+ * **Shorter and coarser on purpose.** A reflection is read as a picture and wants the crossing
51
+ * placed precisely; indirect light is integrated over a hemisphere and then filtered, so a step
52
+ * that lands a few centimetres out is invisible and a step that is not taken at all is a miss that
53
+ * costs a whole fallback. The reach is short because what is further away than a few metres is the
54
+ * world-space field's question — that is the whole architecture of the chain.
55
+ */
56
+ export const GI_SCREEN_MARCH: ScreenSpaceMarch = { steps: 16, reachM: 4, thicknessM: 0.5 };
57
+
58
+ /** Scratch, so a per-pixel trace allocates nothing. */
59
+ const ORIGIN = new Float32Array(3);
60
+
61
+ /**
62
+ * Lift a point off the surface it stands on, along that surface's normal.
63
+ *
64
+ * A normal that is not unit length is normalised, because an interpolated one rarely is. A normal
65
+ * of zero length has no direction to lift along, so the point is returned unmoved rather than as
66
+ * three `NaN`s — which would reach the projection and come back as a miss for the wrong reason.
67
+ */
68
+ export function screenRayOrigin(
69
+ point: ReadonlyVec3,
70
+ normal: ReadonlyVec3,
71
+ biasM: number,
72
+ out: Float32Array,
73
+ ): Float32Array {
74
+ const nx = normal[0] ?? 0;
75
+ const ny = normal[1] ?? 0;
76
+ const nz = normal[2] ?? 0;
77
+ const length = Math.hypot(nx, ny, nz);
78
+ const scale = length > 0 ? biasM / length : 0;
79
+ out[0] = (point[0] ?? 0) + nx * scale;
80
+ out[1] = (point[1] ?? 0) + ny * scale;
81
+ out[2] = (point[2] ?? 0) + nz * scale;
82
+ return out;
83
+ }
84
+
85
+ /**
86
+ * Trace an indirect ray against the frame the renderer has already drawn.
87
+ *
88
+ * Returns whether the screen could answer. **A false here is the chain's instruction to keep
89
+ * going**, not a statement that there is nothing there: the ray may have left the frame, gone
90
+ * behind something, or simply run out of reach, and in every one of those cases the world-space
91
+ * field has the next word. That is what makes this an accelerator rather than an answer, and it is
92
+ * the distinction the refusal in `ROADMAP.md` rests on.
93
+ */
94
+ export function traceScreen(
95
+ point: ReadonlyVec3,
96
+ normal: ReadonlyVec3,
97
+ direction: ReadonlyVec3,
98
+ eye: ReadonlyVec3,
99
+ march: ScreenSpaceMarch,
100
+ project: (x: number, y: number, z: number, uv: Float32Array) => boolean,
101
+ sceneDistance: (u: number, v: number) => number,
102
+ out: ScreenSpaceHit,
103
+ ): boolean {
104
+ screenRayOrigin(point, normal, SCREEN_TRACE_BIAS_M, ORIGIN);
105
+ return traceScreenSpaceRay(ORIGIN, direction, eye, march, project, sceneDistance, out);
106
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The scene as the GPU sees it: one transform, one bounding sphere and one material index per
3
+ * instance, in buffers that persist across frames.
4
+ *
5
+ * **Uploaded by dirty range rather than rebuilt.** A GPU-driven frame reads these every frame and
6
+ * reads the processor-side scene not at all, so the cost of keeping them current is the cost of
7
+ * the pipeline. A world of a hundred thousand instances in which four moved must upload four.
8
+ *
9
+ * **One range rather than a list of them**, which is a deliberate approximation: instances that
10
+ * move together are overwhelmingly adjacent in a world partitioned by space, so one
11
+ * first-to-last span costs a few wasted bytes and saves keeping a set. The case it is wrong for
12
+ * — two objects moving at opposite ends of a large world — uploads the whole span between them,
13
+ * and that is the trade being made rather than an oversight.
14
+ *
15
+ * Allocates nothing per write. Every setter fills existing storage.
16
+ */
17
+ export interface GpuScene {
18
+ /** Sixteen floats per instance, in the renderer's existing matrix convention. */
19
+ transforms: Float32Array;
20
+ /** Four floats per instance: centre x, y, z then radius. */
21
+ bounds: Float32Array;
22
+ /** One material index per instance. */
23
+ materials: Uint32Array;
24
+ /** Lowest instance written since the last clear, or -1. */
25
+ dirtyFirst: number;
26
+ /** Highest instance written since the last clear, or -1. */
27
+ dirtyLast: number;
28
+ }
29
+
30
+ export function createGpuScene(capacity: number): GpuScene {
31
+ return {
32
+ transforms: new Float32Array(capacity * 16),
33
+ bounds: new Float32Array(capacity * 4),
34
+ materials: new Uint32Array(capacity),
35
+ dirtyFirst: -1,
36
+ dirtyLast: -1,
37
+ };
38
+ }
39
+
40
+ function touch(scene: GpuScene, index: number): void {
41
+ if (scene.dirtyFirst === -1 || index < scene.dirtyFirst) scene.dirtyFirst = index;
42
+ if (index > scene.dirtyLast) scene.dirtyLast = index;
43
+ }
44
+
45
+ export function setInstanceTransform(scene: GpuScene, index: number, m: Float32Array): void {
46
+ scene.transforms.set(m, index * 16);
47
+ touch(scene, index);
48
+ }
49
+
50
+ export function setInstanceBounds(
51
+ scene: GpuScene,
52
+ index: number,
53
+ cx: number,
54
+ cy: number,
55
+ cz: number,
56
+ radius: number,
57
+ ): void {
58
+ const at = index * 4;
59
+ scene.bounds[at] = cx;
60
+ scene.bounds[at + 1] = cy;
61
+ scene.bounds[at + 2] = cz;
62
+ scene.bounds[at + 3] = radius;
63
+ touch(scene, index);
64
+ }
65
+
66
+ export function setInstanceMaterial(scene: GpuScene, index: number, material: number): void {
67
+ scene.materials[index] = material;
68
+ touch(scene, index);
69
+ }
70
+
71
+ export function dirtyRange(scene: GpuScene): { first: number; count: number } {
72
+ if (scene.dirtyFirst === -1) return { first: 0, count: 0 };
73
+ return { first: scene.dirtyFirst, count: scene.dirtyLast - scene.dirtyFirst + 1 };
74
+ }
75
+
76
+ /** Forget what moved. The data stays; only the record of which part of it is unsent is cleared. */
77
+ export function clearDirty(scene: GpuScene): void {
78
+ scene.dirtyFirst = -1;
79
+ scene.dirtyLast = -1;
80
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Whether a fragment survives its material's alpha test.
3
+ *
4
+ * **The reference the raster's WGSL is checked against**, which is `gpu-parity.mjs`'s pattern and
5
+ * is not about the arithmetic being hard: a term that exists only as a shader is a term nothing
6
+ * can disagree with. The rule here is four lines; the boundary conditions are where every alpha
7
+ * test goes wrong, and they are what the tests beside this are about.
8
+ *
9
+ * **`>=` rather than `>`**, which is GLSL's own convention and the boundary every alpha test gets
10
+ * wrong. At `>` a texel exactly at the cutoff is discarded, which puts a one-texel seam around
11
+ * every leaf and reads as a mipmap problem rather than as a comparison.
12
+ *
13
+ * **The zero case is a branch and not just an arithmetic identity.** It is what lets a material
14
+ * that says nothing about cutout skip the base-colour fetch entirely, which is the promise every
15
+ * term in this pipeline has made since it was four lines: a frame that asked for nothing draws
16
+ * exactly the frame it drew before, and pays for nothing.
17
+ *
18
+ * **The guard is written as a negated `>` rather than as `<=`, and a device is why.** WGSL does not
19
+ * require an implementation to support NaN, so a shader may assume one never arrives; written
20
+ * `cutoff <= 0` this file discarded every fragment of a material whose cutoff was not a number and
21
+ * the device kept every one of them, which `scripts/gpu-parity.mjs` reported as four disagreements
22
+ * the first time it ran. Negating the comparison makes both keep it, and keeping is also the right
23
+ * answer: a cutoff that is not a number is not a test, and a surface a consumer can see is
24
+ * findable where one that silently vanished is not.
25
+ */
26
+
27
+ /** A material that never asked for a test. Zero, so an unset field is off. */
28
+ export const ALPHA_TEST_OFF = 0;
29
+
30
+ export function alphaKept(alpha: number, cutoff: number): boolean {
31
+ if (!(cutoff > ALPHA_TEST_OFF)) return true;
32
+ return alpha >= cutoff;
33
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Every cluster in the scene, packed into one set of buffers.
3
+ *
4
+ * **One dispatch over every cluster is the whole point of the pipeline.** A buffer per mesh means
5
+ * a dispatch per mesh, which is the draw-call bound this exists to remove — so the packing rebases
6
+ * each mesh's index offsets by the indices already written and gives every cluster an identifier
7
+ * that is unique across the scene rather than within its mesh.
8
+ *
9
+ * **A cluster says which mesh it came from**, because a cluster that cannot name its mesh cannot
10
+ * find the transform it is drawn with. The number is the mesh's position in the list handed in,
11
+ * which is what a caller's own transform array is indexed by — so an empty mesh still takes a slot.
12
+ * Skipping empty meshes shifts every index after them and every cluster past the gap draws
13
+ * somewhere else.
14
+ *
15
+ * **Bounds and cones are interleaved as vec4s.** A storage buffer read as `array<vec4<f32>>` wants
16
+ * sixteen-byte stride, and three floats followed by one is exactly the sphere and exactly the cone,
17
+ * so the packing costs nothing and the shader indexes without arithmetic.
18
+ */
19
+
20
+ import { cullCutoff } from './cullClusters.ts';
21
+
22
+ /** Floats per cluster in `bounds` and in `cones`: a vec4 each. */
23
+ export const CLUSTER_FLOATS = 8;
24
+
25
+ /** What packing needs of one mesh's clusters. `MeshletLevel` from `MSHL` satisfies it. */
26
+ export interface ClusterSource {
27
+ readonly count: number;
28
+ readonly triangleOffsets: Uint32Array;
29
+ readonly triangleCounts: Uint32Array;
30
+ readonly boundsCentre: Float32Array;
31
+ readonly boundsRadius: Float32Array;
32
+ readonly coneAxis: Float32Array;
33
+ /**
34
+ * The minimum dot product of any face normal against the axis — the cosine of the cone's
35
+ * half-angle, as `buildClusters` writes it and `MSHL` stores it. Packing turns it into the sine
36
+ * the cull is written in; see `cullCutoff`.
37
+ */
38
+ readonly coneCutoff: Float32Array;
39
+ readonly ownError: Float32Array;
40
+ readonly parentError: Float32Array;
41
+ readonly indices: Uint32Array;
42
+ }
43
+
44
+ export interface ClusterBuffers {
45
+ /** Four floats a cluster: centre and radius. */
46
+ bounds: Float32Array;
47
+ /** Four floats a cluster: cone axis and cutoff. */
48
+ cones: Float32Array;
49
+ /** Two floats a cluster: its own error and its parent's. */
50
+ errors: Float32Array;
51
+ /** Where this cluster's indices begin, in the packed index buffer. */
52
+ indexOffsets: Uint32Array;
53
+ /** How many indices it has. */
54
+ indexCounts: Uint32Array;
55
+ /** Unique across the scene, and equal to the cluster's position here. */
56
+ clusterId: Uint32Array;
57
+ /** Which mesh of the input list this cluster came from. */
58
+ meshOf: Uint32Array;
59
+ indices: Uint32Array;
60
+ count: number;
61
+ }
62
+
63
+ /** Floats a shader steps by to reach the next cluster's bounds or cone. */
64
+ export function clusterStride(): number {
65
+ return CLUSTER_FLOATS;
66
+ }
67
+
68
+ export function packClusters(meshes: readonly ClusterSource[]): ClusterBuffers {
69
+ let clusters = 0;
70
+ let indices = 0;
71
+ for (const mesh of meshes) {
72
+ clusters += mesh.count;
73
+ indices += mesh.indices.length;
74
+ }
75
+
76
+ const packed: ClusterBuffers = {
77
+ bounds: new Float32Array(clusters * 4),
78
+ cones: new Float32Array(clusters * 4),
79
+ errors: new Float32Array(clusters * 2),
80
+ indexOffsets: new Uint32Array(clusters),
81
+ indexCounts: new Uint32Array(clusters),
82
+ clusterId: new Uint32Array(clusters),
83
+ meshOf: new Uint32Array(clusters),
84
+ indices: new Uint32Array(indices),
85
+ count: clusters,
86
+ };
87
+
88
+ let cluster = 0;
89
+ let indexAt = 0;
90
+ for (let meshIndex = 0; meshIndex < meshes.length; meshIndex += 1) {
91
+ const mesh = meshes[meshIndex] as ClusterSource;
92
+ packed.indices.set(mesh.indices, indexAt);
93
+
94
+ for (let c = 0; c < mesh.count; c += 1) {
95
+ packed.bounds[cluster * 4] = mesh.boundsCentre[c * 3] as number;
96
+ packed.bounds[cluster * 4 + 1] = mesh.boundsCentre[c * 3 + 1] as number;
97
+ packed.bounds[cluster * 4 + 2] = mesh.boundsCentre[c * 3 + 2] as number;
98
+ packed.bounds[cluster * 4 + 3] = mesh.boundsRadius[c] as number;
99
+
100
+ packed.cones[cluster * 4] = mesh.coneAxis[c * 3] as number;
101
+ packed.cones[cluster * 4 + 1] = mesh.coneAxis[c * 3 + 1] as number;
102
+ packed.cones[cluster * 4 + 2] = mesh.coneAxis[c * 3 + 2] as number;
103
+ /* Carried as a cosine, read by the cull as a sine: see `cullCutoff`. */
104
+ packed.cones[cluster * 4 + 3] = cullCutoff(mesh.coneCutoff[c] as number);
105
+
106
+ packed.errors[cluster * 2] = mesh.ownError[c] as number;
107
+ packed.errors[cluster * 2 + 1] = mesh.parentError[c] as number;
108
+
109
+ /*
110
+ * `MSHL` stores a cluster's span in *triangles*; the packed buffer stores it in *indices*,
111
+ * because a shader fetching indices counts indices. Three of one is one of the other, and
112
+ * mixing them is a cluster that draws a third of itself.
113
+ */
114
+ packed.indexOffsets[cluster] = indexAt + (mesh.triangleOffsets[c] as number) * 3;
115
+ packed.indexCounts[cluster] = (mesh.triangleCounts[c] as number) * 3;
116
+ packed.clusterId[cluster] = cluster;
117
+ packed.meshOf[cluster] = meshIndex;
118
+ cluster += 1;
119
+ }
120
+ indexAt += mesh.indices.length;
121
+ }
122
+ return packed;
123
+ }