@driftengine/core 3.62.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.json +122 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +70 -4
  9. package/dist/index.js +123 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +47 -14
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +128 -80
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +261 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +158 -88
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Which tile of the frame each splat lands in, so a device can draw a cloud one tile at a time.
3
+ *
4
+ * **A splat cannot be drawn by one invocation per splat**, because splats overlap and compositing
5
+ * is ordered: two invocations writing the same pixel have no way to agree who was in front. The
6
+ * answer every implementation reaches is to turn the problem inside out — one workgroup per tile
7
+ * of the frame, each walking a list of the splats that touch it, each pixel accumulating its own
8
+ * colour in a register. This builds those lists.
9
+ *
10
+ * **The lists keep the order the splats arrive in.** The scatter is stable, so a caller that hands
11
+ * over a cloud already sorted by depth gets tiles whose lists are sorted by depth, and the
12
+ * compositing is correct without a per-tile sort anywhere. That is the whole reason the sort is a
13
+ * separate step rather than part of this one, and it is a *contract*: a caller that presents its
14
+ * splats in an arbitrary order gets tiles that composite in that arbitrary order, which is a
15
+ * picture rather than an error.
16
+ *
17
+ * **The tile range is derived from the same pixel box the rasteriser loops over**, not from the
18
+ * ellipse again. Two derivations of one footprint drift, and the failure is a splat missing from a
19
+ * tile it actually covers — a hole in the picture that moves with the camera and looks like a
20
+ * culling bug.
21
+ *
22
+ * **The lists are built twice over, by tile and by splat, and the second is what makes a backward
23
+ * pass deterministic.** A tile's gradients have to be added into the splats it walked, and many
24
+ * tiles touch one splat — which on a device is a floating-point atomic. WGSL has none, and the
25
+ * usual answer, a compare-and-swap loop over the bits, sums in whatever order the tiles happen to
26
+ * finish: floating-point addition is not associative, so the same cloud gives a different gradient
27
+ * every run and a fit stops reproducing. So every (tile, splat) pair gets a slot of its own,
28
+ * `pairOf` says which, and a splat's slots are **contiguous** — a reduction over them is a loop in
29
+ * a fixed order, which is the same answer every time on every device.
30
+ */
31
+
32
+ /** The side of a tile in pixels; one workgroup covers one tile. */
33
+ export const SPLAT_TILE = 16;
34
+
35
+ /** Floats a splat's screen footprint occupies: `x`, `y`, `radius`, `depth`. */
36
+ export const SPLAT_BIN_FLOATS = 4;
37
+
38
+ /** How many tiles a frame of this size is cut into, across and down. */
39
+ export function splatTileGrid(width: number, height: number): { across: number; down: number } {
40
+ return {
41
+ across: Math.max(0, Math.ceil(width / SPLAT_TILE)),
42
+ down: Math.max(0, Math.ceil(height / SPLAT_TILE)),
43
+ };
44
+ }
45
+
46
+ /**
47
+ * The pixel box a splat covers, clamped to the frame, into `out` as `left`, `top`, `right`,
48
+ * `bottom` — or `false` where it covers nothing.
49
+ *
50
+ * **This is the rasteriser's own box.** A device that computed its footprint any other way would
51
+ * be a second answer to the same question.
52
+ */
53
+ export function splatPixelBox(
54
+ bins: ArrayLike<number>,
55
+ at: number,
56
+ width: number,
57
+ height: number,
58
+ out: Int32Array,
59
+ ): boolean {
60
+ const x = bins[at * SPLAT_BIN_FLOATS] as number;
61
+ const y = bins[at * SPLAT_BIN_FLOATS + 1] as number;
62
+ const radius = bins[at * SPLAT_BIN_FLOATS + 2] as number;
63
+ if (!(radius > 0)) return false;
64
+ const left = Math.max(0, Math.floor(x - radius));
65
+ const right = Math.min(width - 1, Math.ceil(x + radius));
66
+ const top = Math.max(0, Math.floor(y - radius));
67
+ const bottom = Math.min(height - 1, Math.ceil(y + radius));
68
+ if (right < left || bottom < top) return false;
69
+ out[0] = left;
70
+ out[1] = top;
71
+ out[2] = right;
72
+ out[3] = bottom;
73
+ return true;
74
+ }
75
+
76
+ /**
77
+ * How many splats each tile holds, and how many entries the lists need in total.
78
+ *
79
+ * `counts` is cleared here rather than by the caller: a count buffer reused across steps without
80
+ * clearing grows without bound, and the lists then run off the end of themselves.
81
+ */
82
+ export function countSplatTiles(
83
+ bins: ArrayLike<number>,
84
+ count: number,
85
+ width: number,
86
+ height: number,
87
+ counts: Uint32Array,
88
+ tilesPerSplat?: Uint32Array,
89
+ ): number {
90
+ counts.fill(0);
91
+ tilesPerSplat?.fill(0);
92
+ const { across } = splatTileGrid(width, height);
93
+ const box = new Int32Array(4);
94
+ let total = 0;
95
+ for (let at = 0; at < count; at += 1) {
96
+ if (!splatPixelBox(bins, at, width, height, box)) continue;
97
+ const first = Math.floor((box[0] as number) / SPLAT_TILE);
98
+ const last = Math.floor((box[2] as number) / SPLAT_TILE);
99
+ const top = Math.floor((box[1] as number) / SPLAT_TILE);
100
+ const bottom = Math.floor((box[3] as number) / SPLAT_TILE);
101
+ for (let row = top; row <= bottom; row += 1) {
102
+ for (let column = first; column <= last; column += 1) {
103
+ const tile = row * across + column;
104
+ counts[tile] = (counts[tile] as number) + 1;
105
+ total += 1;
106
+ }
107
+ }
108
+ if (tilesPerSplat !== undefined) {
109
+ tilesPerSplat[at] = (last - first + 1) * (bottom - top + 1);
110
+ }
111
+ }
112
+ return total;
113
+ }
114
+
115
+ /**
116
+ * Where each tile's list begins: the exclusive prefix sum of the counts. Answers the total.
117
+ *
118
+ * A tile with no splats gets a zero-length list at a real offset rather than no entry at all,
119
+ * because the offsets are indexed by tile and dropping an empty one moves every later tile's list.
120
+ */
121
+ export function splatTileOffsets(counts: Uint32Array, offsets: Uint32Array): number {
122
+ let running = 0;
123
+ for (let tile = 0; tile < counts.length; tile += 1) {
124
+ offsets[tile] = running;
125
+ running += counts[tile] as number;
126
+ }
127
+ return running;
128
+ }
129
+
130
+ /**
131
+ * Every splat written into the list of each tile it touches, in the order the splats are given.
132
+ *
133
+ * `cursors` is where each tile has got to and starts as a copy of `offsets` — it cannot be the
134
+ * offsets themselves, or the second splat of a tile overwrites the first's offset and every later
135
+ * tile's list starts in the wrong place.
136
+ */
137
+ export function fillSplatTiles(
138
+ bins: ArrayLike<number>,
139
+ count: number,
140
+ width: number,
141
+ height: number,
142
+ offsets: Uint32Array,
143
+ cursors: Uint32Array,
144
+ lists: Uint32Array,
145
+ pairs?: { readonly offsets: Uint32Array; readonly pairOf: Uint32Array },
146
+ ): void {
147
+ cursors.set(offsets);
148
+ const { across } = splatTileGrid(width, height);
149
+ const box = new Int32Array(4);
150
+ for (let at = 0; at < count; at += 1) {
151
+ if (!splatPixelBox(bins, at, width, height, box)) continue;
152
+ const first = Math.floor((box[0] as number) / SPLAT_TILE);
153
+ const last = Math.floor((box[2] as number) / SPLAT_TILE);
154
+ const top = Math.floor((box[1] as number) / SPLAT_TILE);
155
+ const bottom = Math.floor((box[3] as number) / SPLAT_TILE);
156
+ let taken = 0;
157
+ for (let row = top; row <= bottom; row += 1) {
158
+ for (let column = first; column <= last; column += 1) {
159
+ const tile = row * across + column;
160
+ const slot = cursors[tile] as number;
161
+ if (slot >= lists.length) continue;
162
+ lists[slot] = at;
163
+ cursors[tile] = slot + 1;
164
+ if (pairs !== undefined) {
165
+ pairs.pairOf[slot] = (pairs.offsets[at] as number) + taken;
166
+ taken += 1;
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Where each splat's own run of partial-gradient slots begins: the prefix sum over `tilesPerSplat`.
175
+ *
176
+ * The same shape as `splatTileOffsets` and for the opposite index. A splat that covers no tile gets
177
+ * an empty run rather than no entry, because a reduction is indexed by splat.
178
+ */
179
+ export function splatPairOffsets(tilesPerSplat: Uint32Array, offsets: Uint32Array): number {
180
+ let running = 0;
181
+ for (let at = 0; at < tilesPerSplat.length; at += 1) {
182
+ offsets[at] = running;
183
+ running += tilesPerSplat[at] as number;
184
+ }
185
+ return running;
186
+ }
@@ -1,6 +1,6 @@
1
1
  import { compileProgram, uniformLocations } from './shader.ts';
2
2
  import { LINE_FRAG, LINE_VERT } from './shaders/line.ts';
3
- import { buildSegmentQuads } from './segmentQuads.ts';
3
+ import { buildSegmentQuads, expandLineSegments } from './segmentQuads.ts';
4
4
  import type { LineSegments } from './linePoints.ts';
5
5
 
6
6
  /**
@@ -71,31 +71,11 @@ export class LineBatch {
71
71
  /**
72
72
  * Send this frame's polyline, expanding each segment to its four vertices.
73
73
  *
74
- * The expansion is written here rather than folded into `expandBoltSegments`: that
75
- * function reads a `BoltSegments` — five arrays beyond `from`/`to` — and teaching it to
76
- * read a `LineSegments` too would grow a union that couples two renderers sharing only a
77
- * quad. This walk is the same shape with none of that: two positions in, copied to four
78
- * vertices each.
74
+ * The expansion is `expandLineSegments`, which the other backend calls too, so the number of
75
+ * segments drawn — and whether anything is — is decided in one place.
79
76
  */
80
77
  upload(gl: WebGL2RenderingContext, data: LineSegments): number {
81
- const count = Math.min(data.count, this.capacity);
82
- for (let s = 0; s < count; s++) {
83
- const fx = data.from[s * 3] as number;
84
- const fy = data.from[s * 3 + 1] as number;
85
- const fz = data.from[s * 3 + 2] as number;
86
- const tx = data.to[s * 3] as number;
87
- const ty = data.to[s * 3 + 1] as number;
88
- const tz = data.to[s * 3 + 2] as number;
89
- for (let c = 0; c < 4; c++) {
90
- const v = s * 4 + c;
91
- this.from[v * 3] = fx;
92
- this.from[v * 3 + 1] = fy;
93
- this.from[v * 3 + 2] = fz;
94
- this.to[v * 3] = tx;
95
- this.to[v * 3 + 1] = ty;
96
- this.to[v * 3 + 2] = tz;
97
- }
98
- }
78
+ const count = expandLineSegments(data, this.capacity, this.from, this.to);
99
79
  if (count === 0) return 0;
100
80
  const verts = count * 4;
101
81
  gl.bindBuffer(gl.ARRAY_BUFFER, this.fromBuffer);
@@ -57,6 +57,15 @@ const ATTR_JOINTS = 11;
57
57
  * setting sway, watching nothing move, and having nothing to read about why.
58
58
  */
59
59
  const ATTR_CHANNEL = 13;
60
+
61
+ /**
62
+ * The locations `attachInstances` claims, which a plain draw through the same array must not read.
63
+ *
64
+ * Eleven to fourteen are the model matrix's columns and fifteen is the tint. **Thirteen is also
65
+ * the channel**, and eleven and twelve are the skinning pair — which cannot collide, because
66
+ * `flatVert` refuses skinning with instancing. See `draw`.
67
+ */
68
+ const INSTANCE_COLUMNS = [11, 12, 13, 14, 15] as const;
60
69
  const ATTR_WEIGHTS = 12;
61
70
 
62
71
  /**
@@ -312,6 +321,29 @@ export class Mesh {
312
321
  /** Caller is responsible for having the flat program + uniforms bound. */
313
322
  draw(gl: WebGL2RenderingContext): void {
314
323
  gl.bindVertexArray(this.vao);
324
+ /*
325
+ * **A mesh with a batch has the instance columns enabled on this very array, and location 13
326
+ * is the channel.** `attachInstances` claims 11 to 14 for the model matrix and 15 for the
327
+ * tint; a `Mesh` owns one vertex array, so those bindings are still live for a *plain* draw
328
+ * through it — and `aChannel` then reads the third column of instance zero's matrix instead
329
+ * of the absent-attribute constant. For a placement with no rotation that column is
330
+ * `(0, 0, 1, 0)`, so `aChannel.y` is zero, `vSkyDirect = aChannel.y` scales the directional
331
+ * term to nothing, and the mesh renders lit by the hemispheric ambient alone — correct
332
+ * normals, correct albedo, no error, no warning.
333
+ *
334
+ * Found on `demo/instancing.ts`, which exists to draw two ranks of one box the two ways and
335
+ * compare them: the `drawMesh` rank's top face read (51, 30, 21) against the `drawInstanced`
336
+ * rank's (218, 110, 59), and 51 is exactly the ambient. WebGPU has no array to share and drew
337
+ * them alike, which is what made it visible at all.
338
+ *
339
+ * Disabled here and put back by `drawInstances`, rather than given the batch an array of its
340
+ * own: that is the larger change this class's own note about two batches already contemplates,
341
+ * and it wants one for a different reason. Three calls on a mesh that has a batch and none on
342
+ * a mesh that does not.
343
+ */
344
+ if (this.instanced) {
345
+ for (const location of INSTANCE_COLUMNS) gl.disableVertexAttribArray(location);
346
+ }
315
347
  // The VAO restores the arrays; these it cannot. See `constants`.
316
348
  for (const constant of this.constants) {
317
349
  const value = constant.value;
@@ -339,7 +371,16 @@ export class Mesh {
339
371
  );
340
372
  }
341
373
  this.instanced = true;
374
+ this.instanceBuffer = buffer;
375
+ this.instanceStride = stride;
342
376
  gl.bindVertexArray(this.vao);
377
+ this.bindInstanceColumns(gl, buffer);
378
+ gl.bindVertexArray(null);
379
+ }
380
+
381
+ /** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
382
+ private bindInstanceColumns(gl: WebGL2RenderingContext, buffer: WebGLBuffer): void {
383
+ const stride = this.instanceStride;
343
384
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
344
385
  /* Four vec4 columns then a vec3 tint: a mat4 is not a vertex attribute in GLSL ES 300. */
345
386
  for (let column = 0; column < 4; column += 1) {
@@ -351,9 +392,11 @@ export class Mesh {
351
392
  gl.enableVertexAttribArray(15);
352
393
  gl.vertexAttribPointer(15, 3, gl.FLOAT, false, stride, 64);
353
394
  gl.vertexAttribDivisor(15, 1);
354
- gl.bindVertexArray(null);
355
395
  }
356
396
 
397
+ private instanceBuffer: WebGLBuffer | null = null;
398
+ private instanceStride = 0;
399
+
357
400
  /** Whether `attachInstances` has already claimed locations 11 to 15 on this mesh's array. */
358
401
  private instanced = false;
359
402
 
@@ -365,6 +408,8 @@ export class Mesh {
365
408
  */
366
409
  drawInstances(gl: WebGL2RenderingContext, count: number): void {
367
410
  gl.bindVertexArray(this.vao);
411
+ /* Put back what `draw` disabled, if a plain draw of this mesh came first. See its note. */
412
+ if (this.instanceBuffer !== null) this.bindInstanceColumns(gl, this.instanceBuffer);
368
413
  for (const constant of this.constants) {
369
414
  const value = constant.value;
370
415
  applyConstant(gl, constant.location, value);
@@ -37,22 +37,37 @@ import type { FrameResource } from './frame/index.ts';
37
37
  */
38
38
  export type PassDevice = {
39
39
  /**
40
- * Pre-multiply a projection by this before uploading it. Identity on WebGL2.
41
- *
42
- * **The two APIs do not agree about which way clip space points, and the engine settles it in
43
- * the matrix rather than in the shaders** — see `CLIP_CORRECTION`, whose own comment gives the
44
- * reason: the shaders are generated from the GLSL the WebGL2 path uses, and a Y flip written
45
- * into them would be a difference no generator could keep honest. WebGPU's framebuffer origin
46
- * is the top-left and OpenGL's is the bottom-left, and depth clips to [0, 1] rather than
47
- * [-1, 1].
40
+ * Pre-multiply a projection by this **if your WGSL was generated from GLSL**. Identity on WebGL2.
48
41
  *
49
42
  * A contributed pass takes its camera from its caller, so it never sees the corrected matrix the
50
43
  * renderer built for its own verbs. Without this it would have to copy four numbers — and a
51
44
  * copied convention is one that silently disagrees the day the original changes. The splat pass
52
45
  * drew the world upside down on WebGPU until this existed, which is the same first frame this
53
46
  * backend ever produced: a colonnade hanging from the ceiling.
47
+ *
48
+ * **It does two things and only one of them is about the APIs.** Depth: OpenGL clips z to
49
+ * [-1, 1] and WebGPU to [0, 1], so an uncorrected matrix throws away half the buffer. Y: it is
50
+ * negated — and *not* because the framebuffer origins differ, which is what this comment used to
51
+ * say. Both viewport transforms put clip `y = +1` at the top of the image. What flips it is
52
+ * `naga`, which ends every generated vertex entry point with `gl_Position.y = -gl_Position.y`;
53
+ * the negation here cancels that one. See `CLIP_CORRECTION` in `webgpu/renderer.ts`.
54
+ *
55
+ * So **a pass whose shader went through the engine's generator wants this one**, which is every
56
+ * pass that has ever used it — `@driftengine/splats` writes GLSL and generates its WGSL — and a
57
+ * pass that wrote WGSL by hand wants `depthCorrection` instead.
54
58
  */
55
59
  readonly clipCorrection: Float32Array;
60
+ /**
61
+ * Pre-multiply a projection by this **if you wrote your WGSL by hand**. Identity on WebGL2.
62
+ *
63
+ * The same matrix without the Y negation, because there is no generated negation to cancel.
64
+ * Taking `clipCorrection` in a hand-written shader mirrors the picture vertically — and mirrors
65
+ * the triangle winding with it, so it presents as a culling problem and is answered with
66
+ * `frontFace: 'cw'`, which draws the correct faces of a mirrored world. That is exactly what
67
+ * happened to the GPU-driven pipeline's raster, and the capture that settled it was the same
68
+ * scene through `drawMesh`: an exact vertical mirror about the canvas centre.
69
+ */
70
+ readonly depthCorrection: Float32Array;
56
71
  } & (
57
72
  | { readonly backend: 'webgl2'; readonly gl: WebGL2RenderingContext }
58
73
  | {
@@ -61,6 +76,19 @@ export type PassDevice = {
61
76
  readonly format: GPUTextureFormat;
62
77
  readonly depthFormat: GPUTextureFormat;
63
78
  readonly samples: number;
79
+ /**
80
+ * Whether this renderer reconstructs its frames, which is fixed at construction as `samples`
81
+ * is.
82
+ *
83
+ * **A pass drawing the world into a reconstructed frame owes it two things.** It draws with
84
+ * the frame's jitter (`PrepareContext.jitter`), and it leaves its depth in the frame's
85
+ * attachment: the resolve reprojects every pixel's history through that depth, and a pixel
86
+ * still holding the clear reads as infinitely far away — so a turning camera's history is
87
+ * moved correctly, since a turn moves every depth alike, and a sliding camera's is not moved
88
+ * at all and trails the picture. A pass that decides at `init` whether to write depth
89
+ * decides it by this.
90
+ */
91
+ readonly reconstruction: boolean;
64
92
  }
65
93
  );
66
94
 
@@ -90,7 +118,12 @@ export type PassContext = {
90
118
  readonly outputExposure: number;
91
119
  } & (
92
120
  | { readonly backend: 'webgl2'; readonly gl: WebGL2RenderingContext }
93
- | { readonly backend: 'webgpu'; readonly pass: GPURenderPassEncoder }
121
+ | {
122
+ readonly backend: 'webgpu';
123
+ readonly pass: GPURenderPassEncoder;
124
+ /** The frame's jitter, the same array `PrepareContext.jitter` held. */
125
+ readonly jitter: Float32Array;
126
+ }
94
127
  );
95
128
 
96
129
  /**
@@ -119,7 +152,119 @@ export type PassContext = {
119
152
  */
120
153
  export type PrepareContext =
121
154
  | { readonly backend: 'webgl2'; readonly gl: WebGL2RenderingContext }
122
- | { readonly backend: 'webgpu'; readonly encoder: GPUCommandEncoder };
155
+ | {
156
+ readonly backend: 'webgpu';
157
+ readonly encoder: GPUCommandEncoder;
158
+ /**
159
+ * The frame's environment probe, or null where the scene has not baked one.
160
+ *
161
+ * **The first thing this seam hands over that a pass could not render for itself**, and it
162
+ * is here rather than on `PassDevice` because a probe is baked by a scene whenever it likes
163
+ * — after registration, and again when the world changes — so a value captured once at
164
+ * `init` would be null for the life of a pass that registered before the bake.
165
+ *
166
+ * It is offered rather than promised: a pass that wants to light what it draws by the room
167
+ * the rest of the frame is lit by has no other way to reach it, and a pass that does not
168
+ * care ignores the field. What it is *not* is an attachment of the frame — the rule that a
169
+ * contributed pass renders its own targets is unchanged, and this is a resource the renderer
170
+ * owns for the whole frame rather than one it is in the middle of writing.
171
+ */
172
+ readonly environment: PassEnvironment | null;
173
+ /**
174
+ * The world's composed distance field, or null where the frame composed none.
175
+ *
176
+ * **One frame behind, and that is what it is for.** The renderer composes at `endFrame`,
177
+ * because a consumer declares its fields wherever in its own frame the objects live and the
178
+ * set is not complete until the last verb. `prepare` runs at `beginFrame`, so what a pass is
179
+ * handed here is the field the *previous* frame declared — which is right for everything
180
+ * that reads a distance field, since a field is the shape of the world and the world does
181
+ * not usually change between two frames.
182
+ *
183
+ * Offered rather than promised, as `environment` is: a pass that wants to march the world
184
+ * the renderer's own indirect light marches has no other way to reach it, and one that does
185
+ * not care ignores the field. It is null whenever `quality.indirectLight` is off, which is
186
+ * the default, and whenever the frame declared nothing.
187
+ */
188
+ readonly distanceField: PassDistanceField | null;
189
+ /**
190
+ * The offset the renderer's own verbs are drawn with this frame, as a fraction of the clip
191
+ * square — x rightward and y upward, the camera's own convention — and zero on a frame that
192
+ * is not reconstructed. Two floats, the same array every frame.
193
+ *
194
+ * **A pass that draws the world applies it**, with `jitterClip`, to the camera matrix it was
195
+ * given and before any correction. Reconstruction un-jitters every sample it takes, so
196
+ * geometry drawn without the offset is placed up to half a render pixel from where it was,
197
+ * differently every frame, and the picture shimmers and softens. Settled at `beginFrame`,
198
+ * before this runs, and once a frame however many mesh passes the frame binds.
199
+ *
200
+ * What it gives up: the temporal resolve's jitter is not handed over. That resolve never
201
+ * un-jitters, so a pass drawn without its offset is left unantialiased by it rather than
202
+ * misplaced — a lesser fault, and one a pass can live with. A pass drawing into a mirror
203
+ * should not apply it either; the renderer's own verbs do not jitter a mirror.
204
+ */
205
+ readonly jitter: Float32Array;
206
+ };
207
+
208
+ /**
209
+ * The world's distance field on the device, as the two buffers and four numbers that address it.
210
+ *
211
+ * **Cascades rather than one grid.** A single grid fine enough to resolve a doorway and large
212
+ * enough to hold a street is not a thing that fits on a device; nested cascades each of the same
213
+ * sample count, each reaching twice as far as the one inside it, spend their resolution where the
214
+ * camera is. `gi/globalField.ts` composes them and `shaders/gi/sampleField.wgsl.ts` reads them,
215
+ * and the layout below is what the two agree on.
216
+ */
217
+ export interface PassDistanceField {
218
+ /** Every cascade's samples, tightly packed, cascade 0 first. */
219
+ readonly samples: GPUBuffer;
220
+ /**
221
+ * The colour of whatever surface won the union at each sample, three floats, same order.
222
+ *
223
+ * **A union loses which instance won**, and a ray that lands on a wall needs to know what colour
224
+ * it is: what leaves a surface is the light arriving times its albedo, and a distance alone
225
+ * cannot say. `GlobalFieldCascade.albedo` is the reference's own copy of this.
226
+ */
227
+ readonly albedo: GPUBuffer;
228
+ /** Six bounds and a step per cascade, as `sampleField.wgsl.ts` reads them. */
229
+ readonly cascades: GPUBuffer;
230
+ /** How many cascades stand. */
231
+ readonly levels: number;
232
+ /** Samples along one side of every cascade. */
233
+ readonly side: number;
234
+ /** Metres between samples in the innermost cascade — the finest the field resolves. */
235
+ readonly finestStep: number;
236
+ /** The outermost cascade's corners, `[minX, minY, minZ, maxX, maxY, maxZ]`. */
237
+ readonly outerBounds: Float32Array;
238
+ }
239
+
240
+ /**
241
+ * A baked environment probe, as the two things that sample it and the four numbers that address it.
242
+ *
243
+ * **Octahedral rather than a cubemap**, which is the engine's own storage: one 2D array texture,
244
+ * one layer a probe, a one-texel gutter at every level so a bilinear tap at the border reads the
245
+ * folded direction rather than the other side of the map. `shaders/octahedral.ts` carries the
246
+ * mapping in GLSL and in TypeScript, and `octInsetUv` is what turns a direction into a coordinate.
247
+ *
248
+ * **The chain is roughness, not size.** Level 0 is the mirror and `maxLod` is the roughest GGX
249
+ * convolution; `irradianceLevel` sits one beyond it and holds the cosine convolution, which is the
250
+ * diffuse half. `prefilterEnvMap.ts` derives all three from the edge and is the one place that
251
+ * arithmetic lives.
252
+ */
253
+ export interface PassEnvironment {
254
+ /** The whole array, `2d-array`, as the lit pass binds it. */
255
+ readonly view: GPUTextureView;
256
+ readonly sampler: GPUSampler;
257
+ /** Texels across level 0, gutter included. A level's own edge is this over `exp2(level)`. */
258
+ readonly edge: number;
259
+ /** The coarsest level of the GGX chain. */
260
+ readonly maxLod: number;
261
+ /** Which level holds the cosine convolution. */
262
+ readonly irradianceLevel: number;
263
+ /** Whether the scene asked this grid to supply its ambient. `ProbeBakeOptions.irradiance`. */
264
+ readonly irradiance: boolean;
265
+ /** How many probes stand in the grid. A pass reading layer 0 alone is right only for one. */
266
+ readonly layers: number;
267
+ }
123
268
 
124
269
  export interface PassDefinition {
125
270
  /** For diagnostics, and for the label a backend gives the work. */
@@ -160,3 +160,36 @@ export function sourceLevelForSample(
160
160
 
161
161
  return Math.min(maxLevel, Math.max(0, level));
162
162
  }
163
+
164
+ /**
165
+ * A level's own edge in texels, which is not the map's.
166
+ *
167
+ * **The gutter is one texel at every level**, so its share of the map doubles as the chain
168
+ * coarsens and an inset computed once from the map's own size is wrong everywhere but level zero.
169
+ * `flat/probeGrid.ts` carries the same expression for the forward path and records the slide it
170
+ * prevents: a thirty-second of the map at the top of the chain, visible as a reflection sliding as
171
+ * roughness rises.
172
+ *
173
+ * Floored at four, which is where the chain stops — a level two texels across is all gutter.
174
+ */
175
+ export function probeLevelEdge(edge: number, level: number): number {
176
+ return Math.max(edge / 2 ** level, 4);
177
+ }
178
+
179
+ /**
180
+ * The two levels a roughness reads and how far between them it sits, as `lo`, `hi`, `t`.
181
+ *
182
+ * **Mixed by hand rather than by hardware trilinear**, for the reason above: the two levels have
183
+ * different insets and hardware applies one coordinate to both. So the two fetches are issued at
184
+ * their own insets and blended here, and this is the arithmetic that says which two and by how
185
+ * much. Clamped at both ends, so a lod past the chain reads the coarsest level twice rather than
186
+ * sampling a level that does not exist.
187
+ */
188
+ export function probeLevelMix(lod: number, maxLevel: number, out: Float32Array): Float32Array {
189
+ const lo = Math.min(Math.max(Math.floor(lod), 0), maxLevel);
190
+ const hi = Math.min(lo + 1, maxLevel);
191
+ out[0] = lo;
192
+ out[1] = hi;
193
+ out[2] = Math.min(Math.max(lod - lo, 0), 1);
194
+ return out;
195
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Getting a number back from the GPU without waiting for it.
3
+ *
4
+ * The answer to a request made this frame arrives two or three frames later, and a caller that
5
+ * waits has stalled the pipeline to learn something it could have learned late for free. So this
6
+ * is a ring of slots: a request takes one, the completion fills it, and the caller collects
7
+ * whatever has arrived whenever it asks.
8
+ *
9
+ * **A full ring refuses rather than overwrites.** Silently dropping the oldest result produces a
10
+ * statistic that is wrong in a way nothing detects — a frame time that is quietly somebody
11
+ * else's, which is worse than a frame time that is missing.
12
+ *
13
+ * **Deliberately not what texture residency uses.** Reactive streaming is exactly the
14
+ * arrangement where this latency becomes visible as pop-in; the answer there is to predict
15
+ * rather than to read back. This ring is for timings and statistics, where being three frames
16
+ * late costs nothing.
17
+ */
18
+ export interface ReadbackRing {
19
+ /** 0 free, 1 outstanding, 2 arrived, one per slot. */
20
+ state: Uint8Array;
21
+ /** The arrived buffer per slot, or null. */
22
+ data: (ArrayBuffer | null)[];
23
+ /** Where to start looking for a free slot, so requests spread rather than reusing one. */
24
+ next: number;
25
+ }
26
+
27
+ export function createReadbackRing(slots: number): ReadbackRing {
28
+ return {
29
+ state: new Uint8Array(slots),
30
+ data: Array.from({ length: slots }, () => null),
31
+ next: 0,
32
+ };
33
+ }
34
+
35
+ export function beginReadback(ring: ReadbackRing): number {
36
+ const slots = ring.state.length;
37
+ for (let i = 0; i < slots; i += 1) {
38
+ const slot = (ring.next + i) % slots;
39
+ if (ring.state[slot] === 0) {
40
+ ring.state[slot] = 1;
41
+ ring.next = (slot + 1) % slots;
42
+ return slot;
43
+ }
44
+ }
45
+ return -1;
46
+ }
47
+
48
+ export function completeReadback(ring: ReadbackRing, slot: number, data: ArrayBuffer): void {
49
+ if (slot < 0 || slot >= ring.state.length) return;
50
+ /* Only an outstanding slot may be completed: one that was evicted or never requested is not
51
+ * ours to fill, and filling it would hand the caller a result for a request it has forgotten. */
52
+ if (ring.state[slot] !== 1) return;
53
+ ring.state[slot] = 2;
54
+ ring.data[slot] = data;
55
+ }
56
+
57
+ export function takeReadback(ring: ReadbackRing): ArrayBuffer | null {
58
+ for (let slot = 0; slot < ring.state.length; slot += 1) {
59
+ if (ring.state[slot] === 2) {
60
+ const data = ring.data[slot] ?? null;
61
+ ring.state[slot] = 0;
62
+ ring.data[slot] = null;
63
+ return data;
64
+ }
65
+ }
66
+ return null;
67
+ }
68
+
69
+ export function readbackPending(ring: ReadbackRing): number {
70
+ let pending = 0;
71
+ for (let slot = 0; slot < ring.state.length; slot += 1) if (ring.state[slot] === 1) pending += 1;
72
+ return pending;
73
+ }