@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,79 @@
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
+ /** The side of a tile in pixels; one workgroup covers one tile. */
32
+ export declare const SPLAT_TILE = 16;
33
+ /** Floats a splat's screen footprint occupies: `x`, `y`, `radius`, `depth`. */
34
+ export declare const SPLAT_BIN_FLOATS = 4;
35
+ /** How many tiles a frame of this size is cut into, across and down. */
36
+ export declare function splatTileGrid(width: number, height: number): {
37
+ across: number;
38
+ down: number;
39
+ };
40
+ /**
41
+ * The pixel box a splat covers, clamped to the frame, into `out` as `left`, `top`, `right`,
42
+ * `bottom` — or `false` where it covers nothing.
43
+ *
44
+ * **This is the rasteriser's own box.** A device that computed its footprint any other way would
45
+ * be a second answer to the same question.
46
+ */
47
+ export declare function splatPixelBox(bins: ArrayLike<number>, at: number, width: number, height: number, out: Int32Array): boolean;
48
+ /**
49
+ * How many splats each tile holds, and how many entries the lists need in total.
50
+ *
51
+ * `counts` is cleared here rather than by the caller: a count buffer reused across steps without
52
+ * clearing grows without bound, and the lists then run off the end of themselves.
53
+ */
54
+ export declare function countSplatTiles(bins: ArrayLike<number>, count: number, width: number, height: number, counts: Uint32Array, tilesPerSplat?: Uint32Array): number;
55
+ /**
56
+ * Where each tile's list begins: the exclusive prefix sum of the counts. Answers the total.
57
+ *
58
+ * A tile with no splats gets a zero-length list at a real offset rather than no entry at all,
59
+ * because the offsets are indexed by tile and dropping an empty one moves every later tile's list.
60
+ */
61
+ export declare function splatTileOffsets(counts: Uint32Array, offsets: Uint32Array): number;
62
+ /**
63
+ * Every splat written into the list of each tile it touches, in the order the splats are given.
64
+ *
65
+ * `cursors` is where each tile has got to and starts as a copy of `offsets` — it cannot be the
66
+ * offsets themselves, or the second splat of a tile overwrites the first's offset and every later
67
+ * tile's list starts in the wrong place.
68
+ */
69
+ export declare function fillSplatTiles(bins: ArrayLike<number>, count: number, width: number, height: number, offsets: Uint32Array, cursors: Uint32Array, lists: Uint32Array, pairs?: {
70
+ readonly offsets: Uint32Array;
71
+ readonly pairOf: Uint32Array;
72
+ }): void;
73
+ /**
74
+ * Where each splat's own run of partial-gradient slots begins: the prefix sum over `tilesPerSplat`.
75
+ *
76
+ * The same shape as `splatTileOffsets` and for the opposite index. A splat that covers no tile gets
77
+ * an empty run rather than no entry, because a reduction is indexed by splat.
78
+ */
79
+ export declare function splatPairOffsets(tilesPerSplat: Uint32Array, offsets: Uint32Array): number;
@@ -0,0 +1,161 @@
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
+ /** The side of a tile in pixels; one workgroup covers one tile. */
32
+ export const SPLAT_TILE = 16;
33
+ /** Floats a splat's screen footprint occupies: `x`, `y`, `radius`, `depth`. */
34
+ export const SPLAT_BIN_FLOATS = 4;
35
+ /** How many tiles a frame of this size is cut into, across and down. */
36
+ export function splatTileGrid(width, height) {
37
+ return {
38
+ across: Math.max(0, Math.ceil(width / SPLAT_TILE)),
39
+ down: Math.max(0, Math.ceil(height / SPLAT_TILE)),
40
+ };
41
+ }
42
+ /**
43
+ * The pixel box a splat covers, clamped to the frame, into `out` as `left`, `top`, `right`,
44
+ * `bottom` — or `false` where it covers nothing.
45
+ *
46
+ * **This is the rasteriser's own box.** A device that computed its footprint any other way would
47
+ * be a second answer to the same question.
48
+ */
49
+ export function splatPixelBox(bins, at, width, height, out) {
50
+ const x = bins[at * SPLAT_BIN_FLOATS];
51
+ const y = bins[at * SPLAT_BIN_FLOATS + 1];
52
+ const radius = bins[at * SPLAT_BIN_FLOATS + 2];
53
+ if (!(radius > 0))
54
+ return false;
55
+ const left = Math.max(0, Math.floor(x - radius));
56
+ const right = Math.min(width - 1, Math.ceil(x + radius));
57
+ const top = Math.max(0, Math.floor(y - radius));
58
+ const bottom = Math.min(height - 1, Math.ceil(y + radius));
59
+ if (right < left || bottom < top)
60
+ return false;
61
+ out[0] = left;
62
+ out[1] = top;
63
+ out[2] = right;
64
+ out[3] = bottom;
65
+ return true;
66
+ }
67
+ /**
68
+ * How many splats each tile holds, and how many entries the lists need in total.
69
+ *
70
+ * `counts` is cleared here rather than by the caller: a count buffer reused across steps without
71
+ * clearing grows without bound, and the lists then run off the end of themselves.
72
+ */
73
+ export function countSplatTiles(bins, count, width, height, counts, tilesPerSplat) {
74
+ counts.fill(0);
75
+ tilesPerSplat?.fill(0);
76
+ const { across } = splatTileGrid(width, height);
77
+ const box = new Int32Array(4);
78
+ let total = 0;
79
+ for (let at = 0; at < count; at += 1) {
80
+ if (!splatPixelBox(bins, at, width, height, box))
81
+ continue;
82
+ const first = Math.floor(box[0] / SPLAT_TILE);
83
+ const last = Math.floor(box[2] / SPLAT_TILE);
84
+ const top = Math.floor(box[1] / SPLAT_TILE);
85
+ const bottom = Math.floor(box[3] / SPLAT_TILE);
86
+ for (let row = top; row <= bottom; row += 1) {
87
+ for (let column = first; column <= last; column += 1) {
88
+ const tile = row * across + column;
89
+ counts[tile] = counts[tile] + 1;
90
+ total += 1;
91
+ }
92
+ }
93
+ if (tilesPerSplat !== undefined) {
94
+ tilesPerSplat[at] = (last - first + 1) * (bottom - top + 1);
95
+ }
96
+ }
97
+ return total;
98
+ }
99
+ /**
100
+ * Where each tile's list begins: the exclusive prefix sum of the counts. Answers the total.
101
+ *
102
+ * A tile with no splats gets a zero-length list at a real offset rather than no entry at all,
103
+ * because the offsets are indexed by tile and dropping an empty one moves every later tile's list.
104
+ */
105
+ export function splatTileOffsets(counts, offsets) {
106
+ let running = 0;
107
+ for (let tile = 0; tile < counts.length; tile += 1) {
108
+ offsets[tile] = running;
109
+ running += counts[tile];
110
+ }
111
+ return running;
112
+ }
113
+ /**
114
+ * Every splat written into the list of each tile it touches, in the order the splats are given.
115
+ *
116
+ * `cursors` is where each tile has got to and starts as a copy of `offsets` — it cannot be the
117
+ * offsets themselves, or the second splat of a tile overwrites the first's offset and every later
118
+ * tile's list starts in the wrong place.
119
+ */
120
+ export function fillSplatTiles(bins, count, width, height, offsets, cursors, lists, pairs) {
121
+ cursors.set(offsets);
122
+ const { across } = splatTileGrid(width, height);
123
+ const box = new Int32Array(4);
124
+ for (let at = 0; at < count; at += 1) {
125
+ if (!splatPixelBox(bins, at, width, height, box))
126
+ continue;
127
+ const first = Math.floor(box[0] / SPLAT_TILE);
128
+ const last = Math.floor(box[2] / SPLAT_TILE);
129
+ const top = Math.floor(box[1] / SPLAT_TILE);
130
+ const bottom = Math.floor(box[3] / SPLAT_TILE);
131
+ let taken = 0;
132
+ for (let row = top; row <= bottom; row += 1) {
133
+ for (let column = first; column <= last; column += 1) {
134
+ const tile = row * across + column;
135
+ const slot = cursors[tile];
136
+ if (slot >= lists.length)
137
+ continue;
138
+ lists[slot] = at;
139
+ cursors[tile] = slot + 1;
140
+ if (pairs !== undefined) {
141
+ pairs.pairOf[slot] = pairs.offsets[at] + taken;
142
+ taken += 1;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ /**
149
+ * Where each splat's own run of partial-gradient slots begins: the prefix sum over `tilesPerSplat`.
150
+ *
151
+ * The same shape as `splatTileOffsets` and for the opposite index. A splat that covers no tile gets
152
+ * an empty run rather than no entry, because a reduction is indexed by splat.
153
+ */
154
+ export function splatPairOffsets(tilesPerSplat, offsets) {
155
+ let running = 0;
156
+ for (let at = 0; at < tilesPerSplat.length; at += 1) {
157
+ offsets[at] = running;
158
+ running += tilesPerSplat[at];
159
+ }
160
+ return running;
161
+ }
@@ -32,11 +32,8 @@ export declare class LineBatch {
32
32
  /**
33
33
  * Send this frame's polyline, expanding each segment to its four vertices.
34
34
  *
35
- * The expansion is written here rather than folded into `expandBoltSegments`: that
36
- * function reads a `BoltSegments` — five arrays beyond `from`/`to` — and teaching it to
37
- * read a `LineSegments` too would grow a union that couples two renderers sharing only a
38
- * quad. This walk is the same shape with none of that: two positions in, copied to four
39
- * vertices each.
35
+ * The expansion is `expandLineSegments`, which the other backend calls too, so the number of
36
+ * segments drawn — and whether anything is — is decided in one place.
40
37
  */
41
38
  upload(gl: WebGL2RenderingContext, data: LineSegments): number;
42
39
  /**
@@ -1,6 +1,6 @@
1
1
  import { compileProgram, uniformLocations } from './shader.js';
2
2
  import { LINE_FRAG, LINE_VERT } from './shaders/line.js';
3
- import { buildSegmentQuads } from './segmentQuads.js';
3
+ import { buildSegmentQuads, expandLineSegments } from './segmentQuads.js';
4
4
  /**
5
5
  * One draw call for a polyline with a real width — an audio waveform, a trail, a plotted
6
6
  * curve, anything that is a stroke rather than a surface.
@@ -64,31 +64,11 @@ export class LineBatch {
64
64
  /**
65
65
  * Send this frame's polyline, expanding each segment to its four vertices.
66
66
  *
67
- * The expansion is written here rather than folded into `expandBoltSegments`: that
68
- * function reads a `BoltSegments` — five arrays beyond `from`/`to` — and teaching it to
69
- * read a `LineSegments` too would grow a union that couples two renderers sharing only a
70
- * quad. This walk is the same shape with none of that: two positions in, copied to four
71
- * vertices each.
67
+ * The expansion is `expandLineSegments`, which the other backend calls too, so the number of
68
+ * segments drawn — and whether anything is — is decided in one place.
72
69
  */
73
70
  upload(gl, data) {
74
- const count = Math.min(data.count, this.capacity);
75
- for (let s = 0; s < count; s++) {
76
- const fx = data.from[s * 3];
77
- const fy = data.from[s * 3 + 1];
78
- const fz = data.from[s * 3 + 2];
79
- const tx = data.to[s * 3];
80
- const ty = data.to[s * 3 + 1];
81
- const tz = data.to[s * 3 + 2];
82
- for (let c = 0; c < 4; c++) {
83
- const v = s * 4 + c;
84
- this.from[v * 3] = fx;
85
- this.from[v * 3 + 1] = fy;
86
- this.from[v * 3 + 2] = fz;
87
- this.to[v * 3] = tx;
88
- this.to[v * 3 + 1] = ty;
89
- this.to[v * 3 + 2] = tz;
90
- }
91
- }
71
+ const count = expandLineSegments(data, this.capacity, this.from, this.to);
92
72
  if (count === 0)
93
73
  return 0;
94
74
  const verts = count * 4;
@@ -105,6 +105,10 @@ export declare class Mesh {
105
105
  * which wants a vertex array per batch rather than a flag here.
106
106
  */
107
107
  attachInstances(gl: WebGL2RenderingContext, buffer: WebGLBuffer, stride: number): void;
108
+ /** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
109
+ private bindInstanceColumns;
110
+ private instanceBuffer;
111
+ private instanceStride;
108
112
  /** Whether `attachInstances` has already claimed locations 11 to 15 on this mesh's array. */
109
113
  private instanced;
110
114
  /**
@@ -49,6 +49,14 @@ const ATTR_JOINTS = 11;
49
49
  * setting sway, watching nothing move, and having nothing to read about why.
50
50
  */
51
51
  const ATTR_CHANNEL = 13;
52
+ /**
53
+ * The locations `attachInstances` claims, which a plain draw through the same array must not read.
54
+ *
55
+ * Eleven to fourteen are the model matrix's columns and fifteen is the tint. **Thirteen is also
56
+ * the channel**, and eleven and twelve are the skinning pair — which cannot collide, because
57
+ * `flatVert` refuses skinning with instancing. See `draw`.
58
+ */
59
+ const INSTANCE_COLUMNS = [11, 12, 13, 14, 15];
52
60
  const ATTR_WEIGHTS = 12;
53
61
  /**
54
62
  * One absent attribute's constant, at the width the table gave it.
@@ -295,6 +303,30 @@ export class Mesh {
295
303
  /** Caller is responsible for having the flat program + uniforms bound. */
296
304
  draw(gl) {
297
305
  gl.bindVertexArray(this.vao);
306
+ /*
307
+ * **A mesh with a batch has the instance columns enabled on this very array, and location 13
308
+ * is the channel.** `attachInstances` claims 11 to 14 for the model matrix and 15 for the
309
+ * tint; a `Mesh` owns one vertex array, so those bindings are still live for a *plain* draw
310
+ * through it — and `aChannel` then reads the third column of instance zero's matrix instead
311
+ * of the absent-attribute constant. For a placement with no rotation that column is
312
+ * `(0, 0, 1, 0)`, so `aChannel.y` is zero, `vSkyDirect = aChannel.y` scales the directional
313
+ * term to nothing, and the mesh renders lit by the hemispheric ambient alone — correct
314
+ * normals, correct albedo, no error, no warning.
315
+ *
316
+ * Found on `demo/instancing.ts`, which exists to draw two ranks of one box the two ways and
317
+ * compare them: the `drawMesh` rank's top face read (51, 30, 21) against the `drawInstanced`
318
+ * rank's (218, 110, 59), and 51 is exactly the ambient. WebGPU has no array to share and drew
319
+ * them alike, which is what made it visible at all.
320
+ *
321
+ * Disabled here and put back by `drawInstances`, rather than given the batch an array of its
322
+ * own: that is the larger change this class's own note about two batches already contemplates,
323
+ * and it wants one for a different reason. Three calls on a mesh that has a batch and none on
324
+ * a mesh that does not.
325
+ */
326
+ if (this.instanced) {
327
+ for (const location of INSTANCE_COLUMNS)
328
+ gl.disableVertexAttribArray(location);
329
+ }
298
330
  // The VAO restores the arrays; these it cannot. See `constants`.
299
331
  for (const constant of this.constants) {
300
332
  const value = constant.value;
@@ -319,7 +351,15 @@ export class Mesh {
319
351
  'second batch would rebind the attributes the first draws through.');
320
352
  }
321
353
  this.instanced = true;
354
+ this.instanceBuffer = buffer;
355
+ this.instanceStride = stride;
322
356
  gl.bindVertexArray(this.vao);
357
+ this.bindInstanceColumns(gl, buffer);
358
+ gl.bindVertexArray(null);
359
+ }
360
+ /** The instance columns, bound. Called at attach and again after a plain draw disabled them. */
361
+ bindInstanceColumns(gl, buffer) {
362
+ const stride = this.instanceStride;
323
363
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
324
364
  /* Four vec4 columns then a vec3 tint: a mat4 is not a vertex attribute in GLSL ES 300. */
325
365
  for (let column = 0; column < 4; column += 1) {
@@ -331,8 +371,9 @@ export class Mesh {
331
371
  gl.enableVertexAttribArray(15);
332
372
  gl.vertexAttribPointer(15, 3, gl.FLOAT, false, stride, 64);
333
373
  gl.vertexAttribDivisor(15, 1);
334
- gl.bindVertexArray(null);
335
374
  }
375
+ instanceBuffer = null;
376
+ instanceStride = 0;
336
377
  /** Whether `attachInstances` has already claimed locations 11 to 15 on this mesh's array. */
337
378
  instanced = false;
338
379
  /**
@@ -343,6 +384,9 @@ export class Mesh {
343
384
  */
344
385
  drawInstances(gl, count) {
345
386
  gl.bindVertexArray(this.vao);
387
+ /* Put back what `draw` disabled, if a plain draw of this mesh came first. See its note. */
388
+ if (this.instanceBuffer !== null)
389
+ this.bindInstanceColumns(gl, this.instanceBuffer);
346
390
  for (const constant of this.constants) {
347
391
  const value = constant.value;
348
392
  applyConstant(gl, constant.location, value);
@@ -34,22 +34,37 @@ import type { FrameResource } from './frame/index.ts';
34
34
  */
35
35
  export type PassDevice = {
36
36
  /**
37
- * Pre-multiply a projection by this before uploading it. Identity on WebGL2.
38
- *
39
- * **The two APIs do not agree about which way clip space points, and the engine settles it in
40
- * the matrix rather than in the shaders** — see `CLIP_CORRECTION`, whose own comment gives the
41
- * reason: the shaders are generated from the GLSL the WebGL2 path uses, and a Y flip written
42
- * into them would be a difference no generator could keep honest. WebGPU's framebuffer origin
43
- * is the top-left and OpenGL's is the bottom-left, and depth clips to [0, 1] rather than
44
- * [-1, 1].
37
+ * Pre-multiply a projection by this **if your WGSL was generated from GLSL**. Identity on WebGL2.
45
38
  *
46
39
  * A contributed pass takes its camera from its caller, so it never sees the corrected matrix the
47
40
  * renderer built for its own verbs. Without this it would have to copy four numbers — and a
48
41
  * copied convention is one that silently disagrees the day the original changes. The splat pass
49
42
  * drew the world upside down on WebGPU until this existed, which is the same first frame this
50
43
  * backend ever produced: a colonnade hanging from the ceiling.
44
+ *
45
+ * **It does two things and only one of them is about the APIs.** Depth: OpenGL clips z to
46
+ * [-1, 1] and WebGPU to [0, 1], so an uncorrected matrix throws away half the buffer. Y: it is
47
+ * negated — and *not* because the framebuffer origins differ, which is what this comment used to
48
+ * say. Both viewport transforms put clip `y = +1` at the top of the image. What flips it is
49
+ * `naga`, which ends every generated vertex entry point with `gl_Position.y = -gl_Position.y`;
50
+ * the negation here cancels that one. See `CLIP_CORRECTION` in `webgpu/renderer.ts`.
51
+ *
52
+ * So **a pass whose shader went through the engine's generator wants this one**, which is every
53
+ * pass that has ever used it — `@driftengine/splats` writes GLSL and generates its WGSL — and a
54
+ * pass that wrote WGSL by hand wants `depthCorrection` instead.
51
55
  */
52
56
  readonly clipCorrection: Float32Array;
57
+ /**
58
+ * Pre-multiply a projection by this **if you wrote your WGSL by hand**. Identity on WebGL2.
59
+ *
60
+ * The same matrix without the Y negation, because there is no generated negation to cancel.
61
+ * Taking `clipCorrection` in a hand-written shader mirrors the picture vertically — and mirrors
62
+ * the triangle winding with it, so it presents as a culling problem and is answered with
63
+ * `frontFace: 'cw'`, which draws the correct faces of a mirrored world. That is exactly what
64
+ * happened to the GPU-driven pipeline's raster, and the capture that settled it was the same
65
+ * scene through `drawMesh`: an exact vertical mirror about the canvas centre.
66
+ */
67
+ readonly depthCorrection: Float32Array;
53
68
  } & ({
54
69
  readonly backend: 'webgl2';
55
70
  readonly gl: WebGL2RenderingContext;
@@ -59,6 +74,19 @@ export type PassDevice = {
59
74
  readonly format: GPUTextureFormat;
60
75
  readonly depthFormat: GPUTextureFormat;
61
76
  readonly samples: number;
77
+ /**
78
+ * Whether this renderer reconstructs its frames, which is fixed at construction as `samples`
79
+ * is.
80
+ *
81
+ * **A pass drawing the world into a reconstructed frame owes it two things.** It draws with
82
+ * the frame's jitter (`PrepareContext.jitter`), and it leaves its depth in the frame's
83
+ * attachment: the resolve reprojects every pixel's history through that depth, and a pixel
84
+ * still holding the clear reads as infinitely far away — so a turning camera's history is
85
+ * moved correctly, since a turn moves every depth alike, and a sliding camera's is not moved
86
+ * at all and trails the picture. A pass that decides at `init` whether to write depth
87
+ * decides it by this.
88
+ */
89
+ readonly reconstruction: boolean;
62
90
  });
63
91
  /**
64
92
  * What it is handed each frame, at the point the caller asked for it.
@@ -90,6 +118,8 @@ export type PassContext = {
90
118
  } | {
91
119
  readonly backend: 'webgpu';
92
120
  readonly pass: GPURenderPassEncoder;
121
+ /** The frame's jitter, the same array `PrepareContext.jitter` held. */
122
+ readonly jitter: Float32Array;
93
123
  });
94
124
  /**
95
125
  * What a pass is handed once a frame, before the frame's own target exists.
@@ -121,7 +151,114 @@ export type PrepareContext = {
121
151
  } | {
122
152
  readonly backend: 'webgpu';
123
153
  readonly encoder: GPUCommandEncoder;
154
+ /**
155
+ * The frame's environment probe, or null where the scene has not baked one.
156
+ *
157
+ * **The first thing this seam hands over that a pass could not render for itself**, and it
158
+ * is here rather than on `PassDevice` because a probe is baked by a scene whenever it likes
159
+ * — after registration, and again when the world changes — so a value captured once at
160
+ * `init` would be null for the life of a pass that registered before the bake.
161
+ *
162
+ * It is offered rather than promised: a pass that wants to light what it draws by the room
163
+ * the rest of the frame is lit by has no other way to reach it, and a pass that does not
164
+ * care ignores the field. What it is *not* is an attachment of the frame — the rule that a
165
+ * contributed pass renders its own targets is unchanged, and this is a resource the renderer
166
+ * owns for the whole frame rather than one it is in the middle of writing.
167
+ */
168
+ readonly environment: PassEnvironment | null;
169
+ /**
170
+ * The world's composed distance field, or null where the frame composed none.
171
+ *
172
+ * **One frame behind, and that is what it is for.** The renderer composes at `endFrame`,
173
+ * because a consumer declares its fields wherever in its own frame the objects live and the
174
+ * set is not complete until the last verb. `prepare` runs at `beginFrame`, so what a pass is
175
+ * handed here is the field the *previous* frame declared — which is right for everything
176
+ * that reads a distance field, since a field is the shape of the world and the world does
177
+ * not usually change between two frames.
178
+ *
179
+ * Offered rather than promised, as `environment` is: a pass that wants to march the world
180
+ * the renderer's own indirect light marches has no other way to reach it, and one that does
181
+ * not care ignores the field. It is null whenever `quality.indirectLight` is off, which is
182
+ * the default, and whenever the frame declared nothing.
183
+ */
184
+ readonly distanceField: PassDistanceField | null;
185
+ /**
186
+ * The offset the renderer's own verbs are drawn with this frame, as a fraction of the clip
187
+ * square — x rightward and y upward, the camera's own convention — and zero on a frame that
188
+ * is not reconstructed. Two floats, the same array every frame.
189
+ *
190
+ * **A pass that draws the world applies it**, with `jitterClip`, to the camera matrix it was
191
+ * given and before any correction. Reconstruction un-jitters every sample it takes, so
192
+ * geometry drawn without the offset is placed up to half a render pixel from where it was,
193
+ * differently every frame, and the picture shimmers and softens. Settled at `beginFrame`,
194
+ * before this runs, and once a frame however many mesh passes the frame binds.
195
+ *
196
+ * What it gives up: the temporal resolve's jitter is not handed over. That resolve never
197
+ * un-jitters, so a pass drawn without its offset is left unantialiased by it rather than
198
+ * misplaced — a lesser fault, and one a pass can live with. A pass drawing into a mirror
199
+ * should not apply it either; the renderer's own verbs do not jitter a mirror.
200
+ */
201
+ readonly jitter: Float32Array;
124
202
  };
203
+ /**
204
+ * The world's distance field on the device, as the two buffers and four numbers that address it.
205
+ *
206
+ * **Cascades rather than one grid.** A single grid fine enough to resolve a doorway and large
207
+ * enough to hold a street is not a thing that fits on a device; nested cascades each of the same
208
+ * sample count, each reaching twice as far as the one inside it, spend their resolution where the
209
+ * camera is. `gi/globalField.ts` composes them and `shaders/gi/sampleField.wgsl.ts` reads them,
210
+ * and the layout below is what the two agree on.
211
+ */
212
+ export interface PassDistanceField {
213
+ /** Every cascade's samples, tightly packed, cascade 0 first. */
214
+ readonly samples: GPUBuffer;
215
+ /**
216
+ * The colour of whatever surface won the union at each sample, three floats, same order.
217
+ *
218
+ * **A union loses which instance won**, and a ray that lands on a wall needs to know what colour
219
+ * it is: what leaves a surface is the light arriving times its albedo, and a distance alone
220
+ * cannot say. `GlobalFieldCascade.albedo` is the reference's own copy of this.
221
+ */
222
+ readonly albedo: GPUBuffer;
223
+ /** Six bounds and a step per cascade, as `sampleField.wgsl.ts` reads them. */
224
+ readonly cascades: GPUBuffer;
225
+ /** How many cascades stand. */
226
+ readonly levels: number;
227
+ /** Samples along one side of every cascade. */
228
+ readonly side: number;
229
+ /** Metres between samples in the innermost cascade — the finest the field resolves. */
230
+ readonly finestStep: number;
231
+ /** The outermost cascade's corners, `[minX, minY, minZ, maxX, maxY, maxZ]`. */
232
+ readonly outerBounds: Float32Array;
233
+ }
234
+ /**
235
+ * A baked environment probe, as the two things that sample it and the four numbers that address it.
236
+ *
237
+ * **Octahedral rather than a cubemap**, which is the engine's own storage: one 2D array texture,
238
+ * one layer a probe, a one-texel gutter at every level so a bilinear tap at the border reads the
239
+ * folded direction rather than the other side of the map. `shaders/octahedral.ts` carries the
240
+ * mapping in GLSL and in TypeScript, and `octInsetUv` is what turns a direction into a coordinate.
241
+ *
242
+ * **The chain is roughness, not size.** Level 0 is the mirror and `maxLod` is the roughest GGX
243
+ * convolution; `irradianceLevel` sits one beyond it and holds the cosine convolution, which is the
244
+ * diffuse half. `prefilterEnvMap.ts` derives all three from the edge and is the one place that
245
+ * arithmetic lives.
246
+ */
247
+ export interface PassEnvironment {
248
+ /** The whole array, `2d-array`, as the lit pass binds it. */
249
+ readonly view: GPUTextureView;
250
+ readonly sampler: GPUSampler;
251
+ /** Texels across level 0, gutter included. A level's own edge is this over `exp2(level)`. */
252
+ readonly edge: number;
253
+ /** The coarsest level of the GGX chain. */
254
+ readonly maxLod: number;
255
+ /** Which level holds the cosine convolution. */
256
+ readonly irradianceLevel: number;
257
+ /** Whether the scene asked this grid to supply its ambient. `ProbeBakeOptions.irradiance`. */
258
+ readonly irradiance: boolean;
259
+ /** How many probes stand in the grid. A pass reading layer 0 alone is right only for one. */
260
+ readonly layers: number;
261
+ }
125
262
  export interface PassDefinition {
126
263
  /** For diagnostics, and for the label a backend gives the work. */
127
264
  readonly label: string;
@@ -122,3 +122,25 @@ export declare function roughnessForLevel(level: number, maxLevel: number): numb
122
122
  * objects rather than one written in place.
123
123
  */
124
124
  export declare function sourceLevelForSample(pdf: number, faceTexels: number, count: number, maxLevel: number): number;
125
+ /**
126
+ * A level's own edge in texels, which is not the map's.
127
+ *
128
+ * **The gutter is one texel at every level**, so its share of the map doubles as the chain
129
+ * coarsens and an inset computed once from the map's own size is wrong everywhere but level zero.
130
+ * `flat/probeGrid.ts` carries the same expression for the forward path and records the slide it
131
+ * prevents: a thirty-second of the map at the top of the chain, visible as a reflection sliding as
132
+ * roughness rises.
133
+ *
134
+ * Floored at four, which is where the chain stops — a level two texels across is all gutter.
135
+ */
136
+ export declare function probeLevelEdge(edge: number, level: number): number;
137
+ /**
138
+ * The two levels a roughness reads and how far between them it sits, as `lo`, `hi`, `t`.
139
+ *
140
+ * **Mixed by hand rather than by hardware trilinear**, for the reason above: the two levels have
141
+ * different insets and hardware applies one coordinate to both. So the two fetches are issued at
142
+ * their own insets and blended here, and this is the arithmetic that says which two and by how
143
+ * much. Clamped at both ends, so a lod past the chain reads the coarsest level twice rather than
144
+ * sampling a level that does not exist.
145
+ */
146
+ export declare function probeLevelMix(lod: number, maxLevel: number, out: Float32Array): Float32Array;
@@ -144,3 +144,34 @@ export function sourceLevelForSample(pdf, faceTexels, count, maxLevel) {
144
144
  const level = 0.5 * Math.log2(sampleSolidAngle / texelSolidAngle);
145
145
  return Math.min(maxLevel, Math.max(0, level));
146
146
  }
147
+ /**
148
+ * A level's own edge in texels, which is not the map's.
149
+ *
150
+ * **The gutter is one texel at every level**, so its share of the map doubles as the chain
151
+ * coarsens and an inset computed once from the map's own size is wrong everywhere but level zero.
152
+ * `flat/probeGrid.ts` carries the same expression for the forward path and records the slide it
153
+ * prevents: a thirty-second of the map at the top of the chain, visible as a reflection sliding as
154
+ * roughness rises.
155
+ *
156
+ * Floored at four, which is where the chain stops — a level two texels across is all gutter.
157
+ */
158
+ export function probeLevelEdge(edge, level) {
159
+ return Math.max(edge / 2 ** level, 4);
160
+ }
161
+ /**
162
+ * The two levels a roughness reads and how far between them it sits, as `lo`, `hi`, `t`.
163
+ *
164
+ * **Mixed by hand rather than by hardware trilinear**, for the reason above: the two levels have
165
+ * different insets and hardware applies one coordinate to both. So the two fetches are issued at
166
+ * their own insets and blended here, and this is the arithmetic that says which two and by how
167
+ * much. Clamped at both ends, so a lod past the chain reads the coarsest level twice rather than
168
+ * sampling a level that does not exist.
169
+ */
170
+ export function probeLevelMix(lod, maxLevel, out) {
171
+ const lo = Math.min(Math.max(Math.floor(lod), 0), maxLevel);
172
+ const hi = Math.min(lo + 1, maxLevel);
173
+ out[0] = lo;
174
+ out[1] = hi;
175
+ out[2] = Math.min(Math.max(lod - lo, 0), 1);
176
+ return out;
177
+ }