@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,396 @@
1
+ /**
2
+ * The world's distance field, composed on the device from what the frame declared.
3
+ *
4
+ * **Extracted from `GiFieldPass` rather than written beside it**, because two copies of this
5
+ * arithmetic is exactly the hazard `composeField.wgsl.ts` opens by naming: a shader and a
6
+ * TypeScript function cannot import a struct from each other, so every restatement of the layout
7
+ * is a place the two can drift. There was already one restatement — the reference and the shader,
8
+ * which `scripts/gi-parity.mjs` holds together. A third, in a renderer that composed its own field
9
+ * while a contributed pass composed another, would be one nothing checks.
10
+ *
11
+ * **What changed from the pass is where the instances come from.** `GiFieldPass` took them in its
12
+ * constructor and uploaded them once at `init`, which is right for a rig whose scene is fixed and
13
+ * wrong for a renderer whose consumer declares its fields every frame. Here the instances arrive
14
+ * in a `DistanceFieldScene`, the instance count reaches the shader through the params, and the
15
+ * samples live in a buffer that grows to fit the scene and is never reallocated by a steady one.
16
+ *
17
+ * **The samples are uploaded only when the packing changed.** A field is megabytes and a placement
18
+ * is bytes; a composer that rewrote the samples every frame would move the whole world across the
19
+ * bus to say what it said last frame. `DistanceFieldScene.packingChanged` is what knows.
20
+ */
21
+ import { placeCascade } from '../../gi/globalField.js';
22
+ import { MAX_DISTANCE_FIELDS } from '../../gi/fieldScene.js';
23
+ import { COMPOSE_FIELD_WGSL, COMPOSE_INSTANCE_FLOATS, COMPOSE_OUTPUT_BASE, COMPOSE_PARAM_FLOATS, COMPOSE_PARAM_STRIDE, COMPOSE_WORKGROUP, INSTANCE_ALBEDO, INSTANCE_BOUNDS, INSTANCE_DIMS, INSTANCE_OFFSET, INSTANCE_SCALE, } from '../../shaders/gi/composeField.wgsl.js';
24
+ import { CASCADE_FLOATS } from '../../shaders/gi/sampleField.wgsl.js';
25
+ const COPY_SRC = 0x0004;
26
+ const COPY_DST = 0x0008;
27
+ const STORAGE = 0x0080;
28
+ const QUERY_RESOLVE = 0x0200;
29
+ const MAP_READ = 0x0001;
30
+ /**
31
+ * What the renderer composes with when a consumer only said `quality.indirectLight`.
32
+ *
33
+ * **Three cascades of 49, four metres at the innermost**, which is `demo/giFieldRig.ts`'s
34
+ * measurement and its reasoning: 49 rather than 33 puts the innermost cascade's step at 16.7 cm
35
+ * rather than 25, the difference between a 1.1 m object having six samples across it and four.
36
+ * The three together are 1.4 MB on the device, and the outermost reaches sixteen metres — beyond
37
+ * which the chain's probe level answers and never declines.
38
+ */
39
+ export const DEFAULT_FIELD_COMPOSE = {
40
+ resolution: 49,
41
+ cascades: 3,
42
+ radius: 4,
43
+ };
44
+ export class FieldComposer {
45
+ options;
46
+ samples;
47
+ /** The cascade geometry, kept on the CPU because `placeCascade` is what decides it. */
48
+ cascades = [];
49
+ device;
50
+ pipeline;
51
+ /** One a cascade, each binding that cascade's own params at its own aligned offset. */
52
+ groups = [];
53
+ paramBuffer;
54
+ instanceBuffer;
55
+ fieldBuffer;
56
+ /** The colour of whatever won the union at each sample, three floats. */
57
+ albedoBuffer;
58
+ /**
59
+ * Where each cascade stands, as the field's readers see it: moved by a copy encoded after the
60
+ * composition, never written by the queue. See `cascadeStaged`.
61
+ */
62
+ cascadeBuffer;
63
+ /**
64
+ * **This frame's bounds, on their way to `cascadeBuffer`.** A queue write lands when it is made
65
+ * and an encoded command when its command buffer runs, and a reader of the field — the march in
66
+ * a contributed pass's `prepare` — is encoded before the composition. Written straight into the
67
+ * buffer readers bind, the bounds arrived a frame ahead of the samples they describe, and every
68
+ * frame a cascade stepped the field was read one voxel off. Staged here and copied after the
69
+ * composition's dispatches, a reader before them sees last frame's bounds with last frame's
70
+ * samples and a reader after them sees this frame's with this frame's.
71
+ */
72
+ cascadeStaged;
73
+ /**
74
+ * The declared sources' samples, grown to fit rather than allocated at the budget.
75
+ *
76
+ * **The budget is a ceiling and not a size.** `MAX_DISTANCE_FIELD_SAMPLES` is sixteen megabytes;
77
+ * a scene with two 33-cubed fields wants two hundred and eighty kilobytes, and a renderer that
78
+ * reserved the ceiling would charge every consumer for the largest one imaginable. It grows when
79
+ * a frame declares more than it holds and never shrinks, so a scene that cycles between two sets
80
+ * of fields settles rather than reallocating.
81
+ */
82
+ sourceBuffer;
83
+ sourceFloats = 0;
84
+ paramScratch = new Float32Array(COMPOSE_PARAM_FLOATS);
85
+ cascadeScratch;
86
+ instanceScratch = new Float32Array(MAX_DISTANCE_FIELDS * COMPOSE_INSTANCE_FLOATS);
87
+ outerBounds = new Float32Array(6);
88
+ /** False until a frame has actually composed something, so a reader knows not to trace. */
89
+ composed = false;
90
+ /**
91
+ * The device's own clock around the dispatch, where it has one.
92
+ *
93
+ * **Moved here from `GiFieldPass` with the dispatch it measures.** A timestamp belongs beside
94
+ * the work, and this is now the only place the world's field is composed — so the figure a rig
95
+ * reports is the figure the renderer actually pays rather than a second composition's.
96
+ */
97
+ queries = null;
98
+ queryResolve = null;
99
+ queryRead = null;
100
+ queryCopied = false;
101
+ queryMapping = false;
102
+ timed = false;
103
+ composeMsValue = 0;
104
+ constructor(device, options) {
105
+ if (!(Math.trunc(options.resolution) >= 2)) {
106
+ throw new Error(`FieldComposer: resolution is ${String(options.resolution)}; the minimum is 2.`);
107
+ }
108
+ if (!(Math.trunc(options.cascades) >= 1)) {
109
+ throw new Error(`FieldComposer: cascade count is ${String(options.cascades)}; the minimum is 1.`);
110
+ }
111
+ if (!(options.radius > 0)) {
112
+ throw new Error(`FieldComposer: radius is ${String(options.radius)} and must be positive.`);
113
+ }
114
+ this.device = device;
115
+ this.options = options;
116
+ this.samples = Math.trunc(options.resolution) ** 3;
117
+ for (let level = 0; level < Math.trunc(options.cascades); level += 1) {
118
+ this.cascades.push({
119
+ /* Empty on the CPU: the samples live on the device and are never read back. */
120
+ field: new Float32Array(0),
121
+ albedo: new Float32Array(0),
122
+ dims: [options.resolution, options.resolution, options.resolution],
123
+ bounds: new Float32Array(6),
124
+ step: 0,
125
+ });
126
+ }
127
+ this.cascadeScratch = new Float32Array(this.cascades.length * CASCADE_FLOATS);
128
+ /* One float, grown at the first frame that declares a source. See `sourceBuffer`. */
129
+ this.sourceBuffer = this.storage('sources', 4);
130
+ this.instanceBuffer = this.storage('instances', this.instanceScratch.byteLength);
131
+ this.cascadeBuffer = this.storage('cascades', this.cascadeScratch.byteLength);
132
+ this.cascadeStaged = device.createBuffer({
133
+ label: 'gi-field cascades, staged',
134
+ size: Math.max(4, this.cascadeScratch.byteLength),
135
+ usage: COPY_SRC | COPY_DST,
136
+ });
137
+ this.paramBuffer = this.storage('params', this.cascades.length * COMPOSE_PARAM_STRIDE);
138
+ this.fieldBuffer = this.storage('samples', this.samples * this.cascades.length * 4);
139
+ this.albedoBuffer = this.storage('albedo', this.samples * this.cascades.length * 3 * 4);
140
+ const module = device.createShaderModule({ label: 'gi compose', code: COMPOSE_FIELD_WGSL });
141
+ this.pipeline = device.createComputePipeline({
142
+ label: 'gi compose',
143
+ layout: 'auto',
144
+ compute: { module, entryPoint: 'main' },
145
+ });
146
+ this.rebuildGroups();
147
+ if (device.features.has('timestamp-query')) {
148
+ this.queries = device.createQuerySet({
149
+ label: 'gi-field timings',
150
+ type: 'timestamp',
151
+ count: 2,
152
+ });
153
+ this.queryResolve = device.createBuffer({
154
+ label: 'gi-field timings resolve',
155
+ size: 16,
156
+ usage: QUERY_RESOLVE | COPY_SRC,
157
+ });
158
+ this.queryRead = device.createBuffer({
159
+ label: 'gi-field timings read',
160
+ size: 16,
161
+ usage: COPY_DST | MAP_READ,
162
+ });
163
+ }
164
+ }
165
+ /** One bind group a cascade. Rebuilt whenever the source buffer is replaced by a larger one. */
166
+ rebuildGroups() {
167
+ const layout = this.pipeline.getBindGroupLayout(0);
168
+ this.groups = this.cascades.map((_, level) => this.device.createBindGroup({
169
+ label: `gi compose ${String(level)}`,
170
+ layout,
171
+ entries: [
172
+ {
173
+ binding: 0,
174
+ resource: {
175
+ buffer: this.paramBuffer,
176
+ offset: level * COMPOSE_PARAM_STRIDE,
177
+ size: COMPOSE_PARAM_FLOATS * 4,
178
+ },
179
+ },
180
+ { binding: 1, resource: { buffer: this.instanceBuffer } },
181
+ { binding: 2, resource: { buffer: this.sourceBuffer } },
182
+ { binding: 3, resource: { buffer: this.fieldBuffer } },
183
+ { binding: 4, resource: { buffer: this.albedoBuffer } },
184
+ ],
185
+ }));
186
+ }
187
+ /**
188
+ * Make room for `floats` of samples, replacing the buffer and its bind groups if it is short.
189
+ *
190
+ * **Grown to exactly what is asked rather than doubled.** A doubling policy is for something
191
+ * appended to; this is rewritten whole whenever the packing changes, and the sizes involved are
192
+ * a scene's whole set of baked fields — a quantity that changes when the world does and not
193
+ * between frames of a steady one.
194
+ */
195
+ ensureSources(floats) {
196
+ if (floats <= this.sourceFloats)
197
+ return;
198
+ this.sourceBuffer.destroy();
199
+ this.sourceBuffer = this.storage('sources', floats * 4);
200
+ this.sourceFloats = floats;
201
+ this.rebuildGroups();
202
+ }
203
+ storage(label, bytes) {
204
+ return this.device.createBuffer({
205
+ label: `gi-field ${label}`,
206
+ size: Math.max(4, bytes),
207
+ usage: STORAGE | COPY_DST,
208
+ });
209
+ }
210
+ /** The composed field, or null until a frame has composed one. */
211
+ get field() {
212
+ if (!this.composed)
213
+ return null;
214
+ return {
215
+ samples: this.fieldBuffer,
216
+ albedo: this.albedoBuffer,
217
+ cascades: this.cascadeBuffer,
218
+ levels: this.cascades.length,
219
+ side: Math.trunc(this.options.resolution),
220
+ finestStep: this.cascades[0].step,
221
+ outerBounds: this.outerBounds,
222
+ };
223
+ }
224
+ /**
225
+ * Compose every cascade from what the frame declared, centred on the eye.
226
+ *
227
+ * One dispatch a cascade rather than one for all of them: each has its own origin and step, and
228
+ * a single dispatch would carry them in a buffer indexed by the invocation's own cascade — a
229
+ * divide and a dependent read per thread to save a handful of dispatches.
230
+ *
231
+ * **A frame that declared nothing composes nothing and says so.** An empty instance list would
232
+ * otherwise fill every cascade with the "no surface anywhere" value, which a march reads as an
233
+ * infinite empty room — a ray that travels its whole reach and reports a miss. That is the right
234
+ * answer, but it costs a full dispatch to write it and the field it produces was already there.
235
+ */
236
+ compose(encoder, scene, eye) {
237
+ if (scene.length === 0) {
238
+ this.composed = false;
239
+ return;
240
+ }
241
+ const queue = this.device.queue;
242
+ /*
243
+ * **The samples only when the packing changed.** Sixteen megabytes a frame to say what it said
244
+ * last frame is the upload this flag exists to avoid; `DistanceFieldScene` is what knows,
245
+ * because it is what assigns the offsets and they are positional.
246
+ */
247
+ this.ensureSources(scene.sampleCount);
248
+ if (scene.packingChanged) {
249
+ /* Each source straight to its own offset: no staging copy, and no gap between them. */
250
+ for (const source of scene.sources) {
251
+ queue.writeBuffer(this.sourceBuffer, scene.offsetOf(source) * 4, source.field);
252
+ }
253
+ }
254
+ let at = 0;
255
+ scene.replay((instance) => {
256
+ const base = at * COMPOSE_INSTANCE_FLOATS;
257
+ writeInverse(instance.transform, this.instanceScratch, base);
258
+ this.instanceScratch[base + INSTANCE_SCALE] = Math.hypot(instance.transform[0] ?? 0, instance.transform[1] ?? 0, instance.transform[2] ?? 0);
259
+ this.instanceScratch.set(instance.source.bounds, base + INSTANCE_BOUNDS);
260
+ this.instanceScratch[base + INSTANCE_DIMS] = instance.source.dims[0];
261
+ this.instanceScratch[base + INSTANCE_DIMS + 1] = instance.source.dims[1];
262
+ this.instanceScratch[base + INSTANCE_DIMS + 2] = instance.source.dims[2];
263
+ this.instanceScratch[base + INSTANCE_OFFSET] = scene.offsetOf(instance.source);
264
+ this.instanceScratch[base + INSTANCE_ALBEDO] = instance.albedo[0];
265
+ this.instanceScratch[base + INSTANCE_ALBEDO + 1] = instance.albedo[1];
266
+ this.instanceScratch[base + INSTANCE_ALBEDO + 2] = instance.albedo[2];
267
+ at += 1;
268
+ });
269
+ queue.writeBuffer(this.instanceBuffer, 0, this.instanceScratch, 0, at * COMPOSE_INSTANCE_FLOATS);
270
+ /* Where the cascades stand this frame. `placeCascade` is shared with the reference. */
271
+ for (let level = 0; level < this.cascades.length; level += 1) {
272
+ const cascade = this.cascades[level];
273
+ placeCascade(cascade, level, this.options.radius, eye);
274
+ const slot = level * CASCADE_FLOATS;
275
+ this.cascadeScratch.set(cascade.bounds, slot);
276
+ this.cascadeScratch[slot + 6] = cascade.step;
277
+ }
278
+ queue.writeBuffer(this.cascadeStaged, 0, this.cascadeScratch);
279
+ this.outerBounds.set(this.cascades[this.cascades.length - 1].bounds);
280
+ /*
281
+ * **Every cascade's params written before the pass opens, each at its own offset.**
282
+ * `queue.writeBuffer` is a queue operation and is not ordered against an encoder's commands,
283
+ * so rewriting one region between dispatches would give all of them the last write — every
284
+ * cascade composed with the outermost one's origin and step.
285
+ */
286
+ for (let level = 0; level < this.cascades.length; level += 1) {
287
+ const cascade = this.cascades[level];
288
+ this.paramScratch[0] = cascade.bounds[0];
289
+ this.paramScratch[1] = cascade.bounds[1];
290
+ this.paramScratch[2] = cascade.bounds[2];
291
+ this.paramScratch[3] = cascade.step;
292
+ this.paramScratch[4] = this.options.resolution;
293
+ this.paramScratch[5] = this.options.resolution;
294
+ this.paramScratch[6] = this.options.resolution;
295
+ /* The reach, which is the cascade's half-extent. `composeGlobalField` fills with the same. */
296
+ this.paramScratch[7] = (cascade.bounds[3] - cascade.bounds[0]) / 2;
297
+ this.paramScratch[8] = at;
298
+ this.paramScratch[COMPOSE_OUTPUT_BASE] = level * this.samples;
299
+ queue.writeBuffer(this.paramBuffer, level * COMPOSE_PARAM_STRIDE, this.paramScratch);
300
+ }
301
+ const pass = encoder.beginComputePass({
302
+ label: 'gi compose',
303
+ timestampWrites: this.queries === null
304
+ ? undefined
305
+ : { querySet: this.queries, beginningOfPassWriteIndex: 0, endOfPassWriteIndex: 1 },
306
+ });
307
+ pass.setPipeline(this.pipeline);
308
+ for (let level = 0; level < this.cascades.length; level += 1) {
309
+ pass.setBindGroup(0, this.groups[level]);
310
+ pass.dispatchWorkgroups(Math.ceil(this.samples / COMPOSE_WORKGROUP), 1, 1);
311
+ }
312
+ pass.end();
313
+ /* The bounds reach readers here, in step with the samples just written. See `cascadeStaged`. */
314
+ encoder.copyBufferToBuffer(this.cascadeStaged, 0, this.cascadeBuffer, 0, this.cascadeScratch.byteLength);
315
+ this.composed = true;
316
+ if (this.queries !== null && this.queryResolve !== null && this.queryRead !== null) {
317
+ if (!this.queryCopied && !this.queryMapping) {
318
+ encoder.resolveQuerySet(this.queries, 0, 2, this.queryResolve, 0);
319
+ encoder.copyBufferToBuffer(this.queryResolve, 0, this.queryRead, 0, 16);
320
+ this.queryCopied = true;
321
+ }
322
+ }
323
+ }
324
+ /**
325
+ * The device milliseconds the last measured composition took, or null where nothing measured it.
326
+ *
327
+ * One frame behind, because a timestamp is read back and a readback that blocked would be the
328
+ * measurement changing what it measures.
329
+ */
330
+ get composeMs() {
331
+ return this.timed ? this.composeMsValue : null;
332
+ }
333
+ /** Ask for the last frame's figure, once the frame that recorded it has been submitted. */
334
+ readTimings() {
335
+ if (this.queryRead === null || !this.queryCopied || this.queryMapping)
336
+ return;
337
+ this.queryMapping = true;
338
+ const buffer = this.queryRead;
339
+ void buffer
340
+ .mapAsync(MAP_READ)
341
+ .then(() => {
342
+ const stamps = new BigUint64Array(buffer.getMappedRange().slice(0));
343
+ buffer.unmap();
344
+ const from = stamps[0] ?? 0n;
345
+ const to = stamps[1] ?? 0n;
346
+ /* Nanoseconds, and a pair that never ran reads zero rather than a negative span. */
347
+ this.composeMsValue = to > from ? Number(to - from) / 1e6 : 0;
348
+ this.timed = true;
349
+ })
350
+ .catch(() => {
351
+ /* A device that will not map its own query buffer reports no timing and composes anyway. */
352
+ })
353
+ .finally(() => {
354
+ this.queryMapping = false;
355
+ this.queryCopied = false;
356
+ });
357
+ }
358
+ dispose() {
359
+ this.paramBuffer.destroy();
360
+ this.instanceBuffer.destroy();
361
+ this.sourceBuffer.destroy();
362
+ this.fieldBuffer.destroy();
363
+ this.albedoBuffer.destroy();
364
+ this.cascadeBuffer.destroy();
365
+ this.cascadeStaged.destroy();
366
+ this.queryResolve?.destroy();
367
+ this.queryRead?.destroy();
368
+ this.queries?.destroy();
369
+ }
370
+ }
371
+ /**
372
+ * The inverse of a rigid transform with a uniform scale, written into `out` at `at`.
373
+ *
374
+ * **Not a general inverse**, because `composeGlobalField` refuses anything else: a distance is not
375
+ * preserved by a non-uniform scale and there is no factor to correct one by. Transposing the
376
+ * rotation and dividing by the scale squared is exact for what is allowed, and a general inverse
377
+ * here would be a fourth spelling of a constraint stated three times already.
378
+ */
379
+ export function writeInverse(m, out, at) {
380
+ const scale = Math.hypot(m[0] ?? 0, m[1] ?? 0, m[2] ?? 0);
381
+ const inverse = scale > 0 ? 1 / (scale * scale) : 0;
382
+ for (let row = 0; row < 3; row += 1) {
383
+ for (let col = 0; col < 3; col += 1) {
384
+ out[at + col * 4 + row] = (m[row * 4 + col] ?? 0) * inverse;
385
+ }
386
+ }
387
+ for (let row = 0; row < 3; row += 1) {
388
+ out[at + 12 + row] = -(out[at + row] * (m[12] ?? 0) +
389
+ out[at + 4 + row] * (m[13] ?? 0) +
390
+ out[at + 8 + row] * (m[14] ?? 0));
391
+ }
392
+ out[at + 3] = 0;
393
+ out[at + 7] = 0;
394
+ out[at + 11] = 0;
395
+ out[at + 15] = 1;
396
+ }
@@ -0,0 +1,66 @@
1
+ /** The renderer's distance field, marched into the frame so it can be looked at. */
2
+ import type { ReadonlyMat4, ReadonlyVec3 } from 'gl-matrix';
3
+ import type { PassContext, PassDefinition, PassDevice, PrepareContext } from '../../pass.ts';
4
+ /**
5
+ * **It no longer composes the field, and that is the whole of what changed on 2026-09-18.**
6
+ *
7
+ * This pass used to take a list of instances, compose the world's distance field from them and
8
+ * march the result. It was the only thing in the engine that composed a field at all — which meant
9
+ * the *renderer* had none, and the indirect-light plan had four tasks written on the assumption
10
+ * that it did. The lesson is in `CONTRIBUTING.md`, under auditing a closed plan: grep for who
11
+ * calls a thing, not only for whether it exists.
12
+ *
13
+ * The composition now belongs to the renderer, from what a consumer declares with
14
+ * `addDistanceField`, and this marches what the renderer composed. So there is one field in a
15
+ * frame rather than two, and what this draws is the field the indirect light actually traces
16
+ * — which is the only thing that makes the picture worth looking at.
17
+ *
18
+ * **What `draw` does is a visualisation, and that is deliberate.** A distance field is a thing you
19
+ * can look at, and looking is what a number cannot replace: if the geometry is in the right places
20
+ * at the right sizes the composition is right, and if a cascade boundary shows as a seam the fade
21
+ * is wrong. Wave 4A's own recorded failure — a phantom surface that passed 111,907 parity samples
22
+ * and was found by looking at a picture — is why this exists at all.
23
+ *
24
+ * **The field it marches is one frame old**, because the renderer composes at `endFrame` and
25
+ * `prepare` runs at `beginFrame`. `PrepareContext.distanceField` says why that is right rather
26
+ * than merely what happens.
27
+ */
28
+ export interface GiFieldOptions {
29
+ /** How many steps a marched ray may take before it gives up. */
30
+ readonly marchSteps?: number;
31
+ }
32
+ export declare class GiFieldPass implements PassDefinition {
33
+ readonly label = "driftengine.gi-field";
34
+ private readonly marchSteps;
35
+ private device;
36
+ private pipeline;
37
+ private group;
38
+ private params;
39
+ /** The field the current bind group was built against, so a replaced buffer is noticed. */
40
+ private boundTo;
41
+ private readonly scratch;
42
+ private readonly eye;
43
+ constructor(options?: GiFieldOptions);
44
+ /**
45
+ * Where the camera is and how to unproject from it, for this frame.
46
+ *
47
+ * **Taken rather than derived**, which is the contributed-pass contract: a pass gets its camera
48
+ * from its caller and never sees the renderer's. `invViewProj` is column-major, as `gl-matrix`
49
+ * stores one, and a caller that inverts the same matrix it drew the scene with gets rays that
50
+ * land on the same pixels.
51
+ */
52
+ setView(eye: ReadonlyVec3, invViewProj: ReadonlyMat4): void;
53
+ init(device: PassDevice): void;
54
+ /**
55
+ * Bind whatever field the renderer composed, and tell the march where to look.
56
+ *
57
+ * **The bind group is rebuilt only when the field it was built against is a different object.**
58
+ * The renderer replaces its samples buffer when a scene's fields grow, and a group holding a
59
+ * destroyed buffer is a validation error at the next draw rather than a wrong picture — but
60
+ * rebuilding one every frame is the per-frame allocation the house rules are about.
61
+ */
62
+ prepare(ctx: PrepareContext): void;
63
+ /** March the field into the frame. Nothing is written where the ray found nothing. */
64
+ draw(ctx: PassContext): void;
65
+ dispose(): void;
66
+ }
@@ -0,0 +1,137 @@
1
+ /** The renderer's distance field, marched into the frame so it can be looked at. */
2
+ import { MARCH_EPSILON, MARCH_EYE, MARCH_FIELD_WGSL, MARCH_INV_VIEW_PROJ, MARCH_LEVELS, MARCH_PARAM_FLOATS, MARCH_SIDE, MARCH_STEPS, } from '../../shaders/gi/marchField.wgsl.js';
3
+ const COPY_DST = 0x0008;
4
+ const STORAGE = 0x0080;
5
+ const DEFAULT_MARCH_STEPS = 96;
6
+ export class GiFieldPass {
7
+ label = 'driftengine.gi-field';
8
+ marchSteps;
9
+ device = null;
10
+ pipeline = null;
11
+ group = null;
12
+ params = null;
13
+ /** The field the current bind group was built against, so a replaced buffer is noticed. */
14
+ boundTo = null;
15
+ scratch = new Float32Array(MARCH_PARAM_FLOATS);
16
+ eye = [0, 0, 0];
17
+ constructor(options = {}) {
18
+ this.marchSteps = options.marchSteps ?? DEFAULT_MARCH_STEPS;
19
+ }
20
+ /**
21
+ * Where the camera is and how to unproject from it, for this frame.
22
+ *
23
+ * **Taken rather than derived**, which is the contributed-pass contract: a pass gets its camera
24
+ * from its caller and never sees the renderer's. `invViewProj` is column-major, as `gl-matrix`
25
+ * stores one, and a caller that inverts the same matrix it drew the scene with gets rays that
26
+ * land on the same pixels.
27
+ */
28
+ setView(eye, invViewProj) {
29
+ this.eye[0] = eye[0] ?? 0;
30
+ this.eye[1] = eye[1] ?? 0;
31
+ this.eye[2] = eye[2] ?? 0;
32
+ for (let at = 0; at < 16; at += 1) {
33
+ this.scratch[MARCH_INV_VIEW_PROJ + at] = invViewProj[at] ?? 0;
34
+ }
35
+ }
36
+ init(device) {
37
+ if (device.backend !== 'webgpu') {
38
+ /*
39
+ * Loud at registration rather than silent at the first frame. Marching a distance field
40
+ * means having one, and the field is composed by compute that WebGL2 does not have — the
41
+ * same refusal `INDIRECT_LIGHT_WEBGL2_REFUSAL` makes, arriving at the seam where a consumer
42
+ * can act on it.
43
+ */
44
+ throw new Error('[driftengine] the global illumination field needs compute and this backend is WebGL2. ' +
45
+ 'Ask for WebGPU, or leave the field unregistered; nothing else in the frame depends on it.');
46
+ }
47
+ const gpu = device.device;
48
+ this.device = gpu;
49
+ this.params = gpu.createBuffer({
50
+ label: 'gi-field march params',
51
+ size: this.scratch.byteLength,
52
+ usage: STORAGE | COPY_DST,
53
+ });
54
+ const march = gpu.createShaderModule({ label: 'gi march', code: MARCH_FIELD_WGSL });
55
+ this.pipeline = gpu.createRenderPipeline({
56
+ label: 'gi march',
57
+ layout: 'auto',
58
+ vertex: { module: march, entryPoint: 'vertexMain' },
59
+ fragment: {
60
+ module: march,
61
+ entryPoint: 'fragmentMain',
62
+ targets: [{ format: device.format }],
63
+ },
64
+ primitive: { topology: 'triangle-list' },
65
+ /*
66
+ * **Depth tested against the frame's own attachment and not written to.** The field is drawn
67
+ * where nothing else did, so geometry in front of it hides it; writing depth would make a
68
+ * visualisation occlude the scene it is standing in for. `PassDevice.depthFormat` exists for
69
+ * exactly this and was missing for as long as nobody depth-tested through this seam.
70
+ */
71
+ depthStencil: {
72
+ format: device.depthFormat,
73
+ depthWriteEnabled: false,
74
+ depthCompare: 'always',
75
+ },
76
+ multisample: { count: device.samples },
77
+ });
78
+ }
79
+ /**
80
+ * Bind whatever field the renderer composed, and tell the march where to look.
81
+ *
82
+ * **The bind group is rebuilt only when the field it was built against is a different object.**
83
+ * The renderer replaces its samples buffer when a scene's fields grow, and a group holding a
84
+ * destroyed buffer is a validation error at the next draw rather than a wrong picture — but
85
+ * rebuilding one every frame is the per-frame allocation the house rules are about.
86
+ */
87
+ prepare(ctx) {
88
+ if (ctx.backend !== 'webgpu')
89
+ return;
90
+ const gpu = this.device;
91
+ if (gpu === null || this.pipeline === null || this.params === null)
92
+ return;
93
+ const field = ctx.distanceField;
94
+ if (field === null) {
95
+ /* Nothing composed: no group, and `draw` then draws nothing rather than reading a stale one. */
96
+ this.group = null;
97
+ this.boundTo = null;
98
+ return;
99
+ }
100
+ if (this.boundTo !== field) {
101
+ this.group = gpu.createBindGroup({
102
+ label: 'gi march',
103
+ layout: this.pipeline.getBindGroupLayout(0),
104
+ entries: [
105
+ { binding: 0, resource: { buffer: this.params } },
106
+ { binding: 1, resource: { buffer: field.cascades } },
107
+ { binding: 2, resource: { buffer: field.samples } },
108
+ ],
109
+ });
110
+ this.boundTo = field;
111
+ }
112
+ this.scratch[MARCH_EYE] = this.eye[0];
113
+ this.scratch[MARCH_EYE + 1] = this.eye[1];
114
+ this.scratch[MARCH_EYE + 2] = this.eye[2];
115
+ this.scratch[MARCH_LEVELS] = field.levels;
116
+ this.scratch[MARCH_SIDE] = field.side;
117
+ this.scratch[MARCH_STEPS] = this.marchSteps;
118
+ /* Half a voxel of the finest cascade: below that a march is polishing a value it cannot see. */
119
+ this.scratch[MARCH_EPSILON] = field.finestStep * 0.5;
120
+ gpu.queue.writeBuffer(this.params, 0, this.scratch);
121
+ }
122
+ /** March the field into the frame. Nothing is written where the ray found nothing. */
123
+ draw(ctx) {
124
+ if (ctx.backend !== 'webgpu')
125
+ return;
126
+ if (this.pipeline === null || this.group === null)
127
+ return;
128
+ ctx.pass.setPipeline(this.pipeline);
129
+ ctx.pass.setBindGroup(0, this.group);
130
+ ctx.pass.draw(3, 1, 0, 0);
131
+ }
132
+ dispose() {
133
+ this.params?.destroy();
134
+ this.group = null;
135
+ this.boundTo = null;
136
+ }
137
+ }