@driftengine/core 3.63.0 → 4.1.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 +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,459 @@
1
+ /**
2
+ * The frame's geometry, which arrives and leaves while the frame loop runs.
3
+ *
4
+ * **`GpuDrivenPass` uploaded its scene in a method called `buildStatic`**, and its constructor said
5
+ * why: this pipeline's geometry does not move. A voxel world streams chunks as a player walks and
6
+ * remeshes one on every block they break, so it has to.
7
+ *
8
+ * **Four `RangeAlloc`s over what the pass binds**: six arrays the scene keeps — the cluster and
9
+ * mesh records a frame's culling reads — and two it never keeps. The vertices and the indices go
10
+ * through a `GeometrySink` straight into the device's buffers when a mesh is placed, because
11
+ * holding them as well doubled what a world costs, and at the voxel sandbox's radius 32 the copy
12
+ * was an ArrayBuffer Chrome will not allocate. A mesh claims a run of each, writes its rows, and
13
+ * hands the runs back when it leaves.
14
+ *
15
+ * **One path, not two.** The static case is a scene filled once and never emptied — `streamingScene`
16
+ * is that, and it is what every caller of `buildGpuDrivenScene` plus `new GpuDrivenPass` becomes.
17
+ * Two paths would mean the three rigs in `DRAFT_SCENES` exercise the path no published scene uses,
18
+ * which is how a control stops being a control; `streamScene.test.ts` asserts this class packs a
19
+ * static scene byte for byte where `buildGpuDrivenScene` packed it.
20
+ *
21
+ * **`add` allocates only when a mesh larger than any before it arrives**, because it is called
22
+ * from a frame loop when a player walks: the scratch it interleaves into grows to the largest mesh
23
+ * and is reused. A scene filled before its pass mounts is the exception, and it is the static
24
+ * case: it holds each mesh until the pass attaches, and nothing after. `heldBytes` says which.
25
+ */
26
+ import { worldClustersInto } from './clusterWorld.js';
27
+ import { cullCutoff } from './cullClusters.js';
28
+ import { INSTANCE_FLOATS, instanceSphereInto } from './instances.js';
29
+ import { RangeAlloc } from './rangeAlloc.js';
30
+ import { sceneShadowBounds } from './shadowCamera.js';
31
+ import { GPU_DRIVEN_VERTEX_FLOATS, glowOf } from './sceneUpload.js';
32
+ /** Eight floats a cluster in `cull`, matching `clusterWorld.ts` and the cull shader's stride. */
33
+ const CULL_FLOATS = 8;
34
+ /** Six floats a cluster in `lod`, matching the LOD shader's `i * 6u`. */
35
+ const LOD_FLOATS = 6;
36
+ /** Four words a cluster in `meta`: index offset, index count, cluster id, mesh index. */
37
+ const META_WORDS = 4;
38
+ /** Sixteen floats a mesh. */
39
+ const TRANSFORM_FLOATS = 16;
40
+ /** Four words a removed mesh while its ranges wait: vertex base and count, index base and count. */
41
+ const WAITING_WORDS = 4;
42
+ /** What a second pass is told. See `attach`. */
43
+ const SPENT = "[driftengine] a streaming scene writes its geometry to one pass's device and keeps no copy, " +
44
+ "and this one's has already gone to a pass; build the scene again for a second pass";
45
+ /**
46
+ * A list of changed runs, kept sorted and merged.
47
+ *
48
+ * **The same invariant `RangeAlloc` keeps over its holes, for the same reason**: `takeDirty` has to
49
+ * answer the same way however the frame's adds were ordered, or a capture depends on history.
50
+ */
51
+ class DirtyList {
52
+ spans = [];
53
+ mark(from, to) {
54
+ if (to <= from)
55
+ return;
56
+ let index = 0;
57
+ while (index < this.spans.length && this.spans[index].from < from)
58
+ index += 1;
59
+ this.spans.splice(index, 0, { from, to });
60
+ /* Merge backwards then forwards, so three touching runs become one. */
61
+ const previous = index > 0 ? this.spans[index - 1] : undefined;
62
+ if (previous !== undefined && previous.to >= from) {
63
+ previous.to = Math.max(previous.to, to);
64
+ this.spans.splice(index, 1);
65
+ index -= 1;
66
+ }
67
+ let merged = this.spans[index];
68
+ while (index + 1 < this.spans.length) {
69
+ const next = this.spans[index + 1];
70
+ if (next.from > merged.to)
71
+ break;
72
+ merged.to = Math.max(merged.to, next.to);
73
+ this.spans.splice(index + 1, 1);
74
+ merged = this.spans[index];
75
+ }
76
+ }
77
+ take() {
78
+ const out = this.spans;
79
+ this.spans = [];
80
+ return out;
81
+ }
82
+ }
83
+ export class StreamingScene {
84
+ capacity;
85
+ /** Eight floats a cluster: world centre, radius, cone axis, cutoff. */
86
+ cull;
87
+ /** Six floats a cluster: world centre, radius, own error, parent error. */
88
+ lod;
89
+ /** Four words a cluster: index offset, index count, cluster id, mesh index. */
90
+ meta;
91
+ materialOf;
92
+ transforms;
93
+ instanceSpheres;
94
+ vertexAlloc;
95
+ indexAlloc;
96
+ clusterAlloc;
97
+ meshAlloc;
98
+ dirtyClusters = new DirtyList();
99
+ dirtyMeshes = new DirtyList();
100
+ /**
101
+ * Scratch `worldClustersInto` needs, sized once.
102
+ *
103
+ * It wants `bounds` as four floats a cluster — centre and radius together — and `cones` as four,
104
+ * where a `ClusterSource` keeps `boundsCentre` / `boundsRadius` and `coneAxis` / `coneCutoff` as
105
+ * separate arrays. Packing them is a copy a frame loop must not allocate for.
106
+ */
107
+ boundScratch;
108
+ coneScratch;
109
+ errorScratch;
110
+ meshScratch;
111
+ live = 0;
112
+ /**
113
+ * **Where the geometry is.** Held a mesh at a time until a pass attaches (`staging`), written
114
+ * straight to that pass's buffers (`attached`), or nowhere once the pass has gone (`spent`).
115
+ */
116
+ state = 'staging';
117
+ sink = null;
118
+ staged = new Map();
119
+ /* What an attached scene interleaves through: grown to the largest mesh, never shrunk. */
120
+ rowScratch = new Float32Array(0);
121
+ indexScratch = new Uint32Array(0);
122
+ /* Geometry ranges a removal freed while attached, given back at the next `takeDirty`. */
123
+ waiting;
124
+ waitingCount = 0;
125
+ constructor(capacity) {
126
+ const clusters = Math.max(0, Math.floor(capacity.clusters));
127
+ const meshes = Math.max(0, Math.floor(capacity.meshes));
128
+ this.capacity = {
129
+ vertices: Math.max(0, Math.floor(capacity.vertices)),
130
+ indices: Math.max(0, Math.floor(capacity.indices)),
131
+ clusters,
132
+ meshes,
133
+ };
134
+ this.cull = new Float32Array(clusters * CULL_FLOATS);
135
+ this.lod = new Float32Array(clusters * LOD_FLOATS);
136
+ this.meta = new Uint32Array(clusters * META_WORDS);
137
+ this.materialOf = new Uint32Array(clusters);
138
+ this.transforms = new Float32Array(meshes * TRANSFORM_FLOATS);
139
+ this.instanceSpheres = new Float32Array(meshes * INSTANCE_FLOATS);
140
+ this.vertexAlloc = new RangeAlloc(this.capacity.vertices);
141
+ this.indexAlloc = new RangeAlloc(this.capacity.indices);
142
+ this.clusterAlloc = new RangeAlloc(clusters);
143
+ this.meshAlloc = new RangeAlloc(meshes);
144
+ this.boundScratch = new Float32Array(clusters * 4);
145
+ this.coneScratch = new Float32Array(clusters * 4);
146
+ this.errorScratch = new Float32Array(clusters * 2);
147
+ this.meshScratch = new Uint32Array(clusters);
148
+ /* Room for every mesh the scene can hold being removed in one frame; `hold` grows it past. */
149
+ this.waiting = new Uint32Array(meshes * WAITING_WORDS);
150
+ }
151
+ /** How many clusters currently draw. A removed cluster's slot is still allocated until released. */
152
+ get liveClusters() {
153
+ return this.live;
154
+ }
155
+ /** Room left on each axis, which `scripts/stream-fragmentation.mjs` and its tests read. */
156
+ get freeVertices() {
157
+ return this.vertexAlloc.capacity - this.vertexAlloc.used;
158
+ }
159
+ /** The worst each axis ever reached, which is the occupancy a fragmentation run reports. */
160
+ get highWater() {
161
+ return {
162
+ vertices: this.vertexAlloc.highWater,
163
+ indices: this.indexAlloc.highWater,
164
+ clusters: this.clusterAlloc.highWater,
165
+ meshes: this.meshAlloc.highWater,
166
+ };
167
+ }
168
+ /**
169
+ * The geometry this scene holds on the CPU, in bytes: what is staged for a pass that has not
170
+ * attached, and the two scratch arrays an attached scene interleaves through. **Nothing past
171
+ * that** — which is the whole of what `GeometrySink` is for, as a number a test and a readout can
172
+ * read.
173
+ */
174
+ get heldBytes() {
175
+ let bytes = this.rowScratch.byteLength + this.indexScratch.byteLength;
176
+ for (const entry of this.staged.values()) {
177
+ bytes += entry.rows.byteLength + entry.values.byteLength;
178
+ }
179
+ return bytes;
180
+ }
181
+ /**
182
+ * Give the scene the one place its geometry goes, and send it everything staged so far.
183
+ *
184
+ * **Once, and a second time is refused by name.** The scene keeps no copy of what it has sent,
185
+ * so a second pass would bind two empty buffers and draw nothing — silently, which is the one
186
+ * answer this cannot give. The dirty journal already made a scene one pass's: two passes would
187
+ * each take its spans and each miss the other's.
188
+ */
189
+ attach(sink) {
190
+ if (this.state !== 'staging')
191
+ throw new Error(SPENT);
192
+ this.state = 'attached';
193
+ this.sink = sink;
194
+ for (const entry of this.staged.values()) {
195
+ sink.writeVertices(entry.vertexBase, entry.rows, entry.vertexCount);
196
+ sink.writeIndices(entry.indexBase, entry.values, entry.indexCount);
197
+ }
198
+ this.staged.clear();
199
+ }
200
+ /**
201
+ * The pass has gone. Adds and removes still keep the records, and the geometry goes nowhere.
202
+ *
203
+ * **Called by `GpuDrivenPass.dispose` before it destroys its buffers**, because a write to a
204
+ * destroyed buffer is a device error; and nothing waits after it, because nothing reads.
205
+ */
206
+ detach() {
207
+ this.state = 'spent';
208
+ this.sink = null;
209
+ this.staged.clear();
210
+ this.releaseWaiting();
211
+ }
212
+ /**
213
+ * Place a mesh, or refuse.
214
+ *
215
+ * **Every claim before any write, and a rollback if the last one fails.** A partial add is a mesh
216
+ * whose clusters point at vertices another mesh owns, which draws somebody else's geometry rather
217
+ * than failing — the shape of mistake `packClusters` documents one level up.
218
+ *
219
+ * **`null` rather than an exception**, because a streaming consumer calls this from a frame loop
220
+ * when the player walks: an exception there is a crashed demo where a refusal is a chunk that
221
+ * arrives a frame later.
222
+ */
223
+ add(mesh, transform, material) {
224
+ const vertexCount = mesh.positions.length / 3;
225
+ const indexCount = mesh.clusters.indices.length;
226
+ const clusterCount = mesh.clusters.count;
227
+ const vertexBase = this.vertexAlloc.claim(vertexCount);
228
+ const indexBase = vertexBase < 0 ? -1 : this.indexAlloc.claim(indexCount);
229
+ const clusterBase = indexBase < 0 ? -1 : this.clusterAlloc.claim(clusterCount);
230
+ const slot = clusterBase < 0 ? -1 : this.meshAlloc.claim(1);
231
+ if (vertexBase < 0 || indexBase < 0 || clusterBase < 0 || slot < 0) {
232
+ if (clusterBase >= 0)
233
+ this.clusterAlloc.release(clusterBase, clusterCount);
234
+ if (indexBase >= 0)
235
+ this.indexAlloc.release(indexBase, indexCount);
236
+ if (vertexBase >= 0)
237
+ this.vertexAlloc.release(vertexBase, vertexCount);
238
+ return null;
239
+ }
240
+ for (let i = 0; i < TRANSFORM_FLOATS; i += 1) {
241
+ this.transforms[slot * TRANSFORM_FLOATS + i] = transform[i] ?? 0;
242
+ }
243
+ /* The interleave the pass used to do at construction, in the order its shaders index. */
244
+ const uvs = mesh.uvs;
245
+ const glow = glowOf(mesh, 'added');
246
+ const rows = this.rowsFor(vertexCount);
247
+ for (let v = 0; v < vertexCount; v += 1) {
248
+ const to = v * GPU_DRIVEN_VERTEX_FLOATS;
249
+ for (let axis = 0; axis < 3; axis += 1) {
250
+ rows[to + axis] = mesh.positions[v * 3 + axis];
251
+ rows[to + 3 + axis] = mesh.normals[v * 3 + axis];
252
+ rows[to + 6 + axis] = mesh.colours[v * 3 + axis];
253
+ }
254
+ rows[to + 9] = uvs === undefined ? 0 : uvs[v * 2];
255
+ rows[to + 10] = uvs === undefined ? 0 : uvs[v * 2 + 1];
256
+ rows[to + 11] = glow === undefined ? 0 : glow[v];
257
+ }
258
+ /* **Rebased onto the scene**, because the indices arrive addressing the mesh they came from. */
259
+ const values = this.valuesFor(indexCount);
260
+ for (let i = 0; i < indexCount; i += 1) {
261
+ values[i] = mesh.clusters.indices[i] + vertexBase;
262
+ }
263
+ this.place(slot, vertexBase, rows, vertexCount, indexBase, values, indexCount);
264
+ for (let c = 0; c < clusterCount; c += 1) {
265
+ const record = clusterBase + c;
266
+ this.meta[record * META_WORDS] = indexBase + mesh.clusters.triangleOffsets[c] * 3;
267
+ this.meta[record * META_WORDS + 1] = mesh.clusters.triangleCounts[c] * 3;
268
+ /* Its own position, which is what a pixel in the visibility buffer resolves through. */
269
+ this.meta[record * META_WORDS + 2] = record;
270
+ this.meta[record * META_WORDS + 3] = slot;
271
+ this.materialOf[record] = material;
272
+ for (let axis = 0; axis < 3; axis += 1) {
273
+ this.boundScratch[c * 4 + axis] = mesh.clusters.boundsCentre[c * 3 + axis];
274
+ this.coneScratch[c * 4 + axis] = mesh.clusters.coneAxis[c * 3 + axis];
275
+ }
276
+ this.boundScratch[c * 4 + 3] = mesh.clusters.boundsRadius[c];
277
+ /* Carried as a cosine, read by the cull as a sine: see `cullCutoff`. */
278
+ this.coneScratch[c * 4 + 3] = cullCutoff(mesh.clusters.coneCutoff[c]);
279
+ this.errorScratch[c * 2] = mesh.clusters.ownError[c];
280
+ this.errorScratch[c * 2 + 1] = mesh.clusters.parentError[c];
281
+ this.meshScratch[c] = slot;
282
+ }
283
+ /*
284
+ * **The bounds are computed here, at the moment the mesh is placed.** `cull` holds them already
285
+ * multiplied by the transform, and the pass's own constructor records what happened when it did
286
+ * not: every rig stood at the origin, so nothing ever said so.
287
+ */
288
+ worldClustersInto({
289
+ bounds: this.boundScratch,
290
+ cones: this.coneScratch,
291
+ errors: this.errorScratch,
292
+ meshOf: this.meshScratch,
293
+ count: clusterCount,
294
+ }, this.transforms, this.cull, this.lod, clusterBase);
295
+ instanceSphereInto(this.cull, clusterBase, clusterCount, this.instanceSpheres, slot);
296
+ this.dirtyClusters.mark(clusterBase, clusterBase + clusterCount);
297
+ this.dirtyMeshes.mark(slot, slot + 1);
298
+ this.live += clusterCount;
299
+ return {
300
+ mesh: slot,
301
+ vertexBase,
302
+ vertexCount,
303
+ indexBase,
304
+ indexCount,
305
+ clusterBase,
306
+ clusterCount,
307
+ };
308
+ }
309
+ /** Take a mesh out. Its ranges go back and its clusters stop drawing. */
310
+ remove(handle) {
311
+ for (let c = 0; c < handle.clusterCount; c += 1) {
312
+ /*
313
+ * **The index count, not the offset.** Zero indices is a cluster that draws nothing and that
314
+ * `instanceCull` rejects before it can write an instance; a zeroed offset is a cluster that
315
+ * draws somebody else's first triangle.
316
+ */
317
+ this.meta[(handle.clusterBase + c) * META_WORDS + 1] = 0;
318
+ /*
319
+ * **And its world bounds, because `sceneBounds` reads them.** A released cluster whose cull
320
+ * record still holds where it used to stand keeps the light's box stretched over a mesh that
321
+ * is not there — which is a shadow map spread across empty space rather than anything that
322
+ * fails. A radius of zero is what `sceneShadowBounds` skips.
323
+ */
324
+ for (let f = 0; f < CULL_FLOATS; f += 1)
325
+ this.cull[(handle.clusterBase + c) * CULL_FLOATS + f] = 0;
326
+ }
327
+ for (let i = 0; i < INSTANCE_FLOATS; i += 1) {
328
+ this.instanceSpheres[handle.mesh * INSTANCE_FLOATS + i] = 0;
329
+ }
330
+ this.clusterAlloc.release(handle.clusterBase, handle.clusterCount);
331
+ this.meshAlloc.release(handle.mesh, 1);
332
+ /* Removed before a pass attached: nothing of it is ever sent. */
333
+ this.staged.delete(handle.mesh);
334
+ if (this.state === 'attached') {
335
+ this.hold(handle);
336
+ }
337
+ else {
338
+ this.indexAlloc.release(handle.indexBase, handle.indexCount);
339
+ this.vertexAlloc.release(handle.vertexBase, handle.vertexCount);
340
+ }
341
+ this.dirtyClusters.mark(handle.clusterBase, handle.clusterBase + handle.clusterCount);
342
+ this.dirtyMeshes.mark(handle.mesh, handle.mesh + 1);
343
+ this.live -= handle.clusterCount;
344
+ /*
345
+ * **The geometry is deliberately not cleared.** Nothing reads it once no cluster points at it,
346
+ * and not clearing it is what makes a removal a handful of stores rather than a device write —
347
+ * which matters, because a player walking away causes one every few frames.
348
+ */
349
+ }
350
+ /** What changed since the last call, then cleared. An unchanged frame uploads nothing. */
351
+ takeDirty() {
352
+ /* The uploads the caller is about to make are what stop anything reading a waiting range. */
353
+ this.releaseWaiting();
354
+ return {
355
+ clusters: this.dirtyClusters.take(),
356
+ meshes: this.dirtyMeshes.take(),
357
+ };
358
+ }
359
+ /**
360
+ * Where a mesh is interleaved: a fresh array while it has to be held until a pass attaches, and
361
+ * the one scratch otherwise, grown only when a mesh larger than any before it arrives.
362
+ */
363
+ rowsFor(vertexCount) {
364
+ const floats = vertexCount * GPU_DRIVEN_VERTEX_FLOATS;
365
+ if (this.state === 'staging')
366
+ return new Float32Array(floats);
367
+ if (this.rowScratch.length < floats)
368
+ this.rowScratch = new Float32Array(floats);
369
+ return this.rowScratch;
370
+ }
371
+ valuesFor(indexCount) {
372
+ if (this.state === 'staging')
373
+ return new Uint32Array(indexCount);
374
+ if (this.indexScratch.length < indexCount)
375
+ this.indexScratch = new Uint32Array(indexCount);
376
+ return this.indexScratch;
377
+ }
378
+ /** Hand a placed mesh's geometry on, or hold it for the pass that has not attached yet. */
379
+ place(slot, vertexBase, rows, vertexCount, indexBase, values, indexCount) {
380
+ if (this.state === 'staging') {
381
+ this.staged.set(slot, { vertexBase, rows, vertexCount, indexBase, values, indexCount });
382
+ return;
383
+ }
384
+ const sink = this.sink;
385
+ if (sink === null)
386
+ return;
387
+ sink.writeVertices(vertexBase, rows, vertexCount);
388
+ sink.writeIndices(indexBase, values, indexCount);
389
+ }
390
+ /**
391
+ * Keep a removed mesh's vertex and index ranges from the allocator until the next `takeDirty`.
392
+ *
393
+ * **Because the geometry is written when a mesh is added, not when the frame is recorded.** The
394
+ * pass records its frame at `beginFrame` and submits it at `endFrame`, and a queue write lands
395
+ * before the frame it was made during runs. A mesh added in that window into a range freed that
396
+ * frame would be drawn through the removed mesh's clusters, which the device holds until the
397
+ * next upload. `takeDirty` is that upload's first step, so from then on nothing reads the range.
398
+ * The cluster and mesh slots do not wait: their records travel together through the journal.
399
+ */
400
+ hold(handle) {
401
+ if ((this.waitingCount + 1) * WAITING_WORDS > this.waiting.length) {
402
+ const grown = new Uint32Array(Math.max(WAITING_WORDS, this.waiting.length * 2));
403
+ grown.set(this.waiting);
404
+ this.waiting = grown;
405
+ }
406
+ const at = this.waitingCount * WAITING_WORDS;
407
+ this.waiting[at] = handle.vertexBase;
408
+ this.waiting[at + 1] = handle.vertexCount;
409
+ this.waiting[at + 2] = handle.indexBase;
410
+ this.waiting[at + 3] = handle.indexCount;
411
+ this.waitingCount += 1;
412
+ }
413
+ releaseWaiting() {
414
+ const waiting = this.waiting;
415
+ for (let i = 0; i < this.waitingCount; i += 1) {
416
+ const at = i * WAITING_WORDS;
417
+ this.vertexAlloc.release(waiting[at], waiting[at + 1]);
418
+ this.indexAlloc.release(waiting[at + 2], waiting[at + 3]);
419
+ }
420
+ this.waitingCount = 0;
421
+ }
422
+ /**
423
+ * A bounding sphere over every live cluster: centre in floats 0 to 2, radius in float 3.
424
+ *
425
+ * **`sceneShadowBounds` itself rather than a second spelling of it**, and that is not tidiness —
426
+ * the first version of this method wrote a *box* into the same four floats the pass reads a
427
+ * sphere out of, so the shadow camera was fitted to a sphere centred on the box's minimum corner
428
+ * with `maxX` as its radius. Two GPU-driven rigs lost their shadows and the maintainer saw it on
429
+ * the page before the capture diff was read.
430
+ *
431
+ * The function's own header already says why there is one copy of this arithmetic: a light's box
432
+ * that disagrees with the cull's bounds clips casters out of a map the cull believes are in it.
433
+ * A second copy here was the same mistake one level up.
434
+ */
435
+ sceneBounds(out) {
436
+ sceneShadowBounds(this.cull, CULL_FLOATS, this.capacity.clusters, out);
437
+ }
438
+ }
439
+ /**
440
+ * A scene filled once from a list of meshes, which is the static case.
441
+ *
442
+ * With no capacity given it sizes itself to exactly what the meshes need, so a static scene
443
+ * allocates nothing spare and packs where `buildGpuDrivenScene` packed.
444
+ */
445
+ export function streamingScene(meshes, transforms, capacity) {
446
+ let vertices = 0;
447
+ let indices = 0;
448
+ let clusters = 0;
449
+ for (const mesh of meshes) {
450
+ vertices += mesh.positions.length / 3;
451
+ indices += mesh.clusters.indices.length;
452
+ clusters += mesh.clusters.count;
453
+ }
454
+ const scene = new StreamingScene(capacity ?? { vertices, indices, clusters, meshes: meshes.length });
455
+ meshes.forEach((mesh, at) => {
456
+ scene.add(mesh, transforms.subarray(at * TRANSFORM_FLOATS, at * TRANSFORM_FLOATS + TRANSFORM_FLOATS), mesh.material);
457
+ });
458
+ return scene;
459
+ }
@@ -0,0 +1,25 @@
1
+ /** What a textured pixel needs from its triangle that a compute invocation cannot take for itself. */
2
+ /**
3
+ * **Two derivatives the forward path gets from the hardware and this pipeline has to build.** A
4
+ * fragment shader takes `dFdx` and `dFdy`; a compute invocation has no neighbours. The shading
5
+ * pass already has the analytic barycentric gradients — `SHADE_SURFACE_WGSL` builds them for the
6
+ * shadow's receiver plane — and any attribute's screen gradient is those weights applied to its
7
+ * three corners. The UV's picks a mip level; the UV's and the world position's together build the
8
+ * tangent frame a normal map is read through.
9
+ */
10
+ /**
11
+ * The mip level a pixel's texture footprint asks for: the log of the longer of the two screen-axis
12
+ * UV steps, in texels. The common isotropic form; a device choosing its own level for an
13
+ * implicit-derivative sample may differ in the last bits, which is stated where the two pipelines
14
+ * are compared rather than hidden here.
15
+ */
16
+ export declare function surfaceLod(dudx: number, dvdx: number, dudy: number, dvdy: number, size: number): number;
17
+ /**
18
+ * `tangentFrame.ts`'s derived branch, from analytic gradients instead of `dFdx` and `dFdy`.
19
+ *
20
+ * **Term for term, including the determinant's sign and the one scale for both axes**, because the
21
+ * forward path's comment records what each is worth: without the sign the frame points the other
22
+ * way on one backend, and two scales shear a stretched UV layout. The screen axes here are x to the
23
+ * right and y up, as the shading pass measures them; the fold makes the answer the same either way.
24
+ */
25
+ export declare function derivedTangentFrame(n: readonly [number, number, number], dp1: readonly [number, number, number], dp2: readonly [number, number, number], duv1: readonly [number, number], duv2: readonly [number, number], out: Float32Array): Float32Array;
@@ -0,0 +1,51 @@
1
+ /** What a textured pixel needs from its triangle that a compute invocation cannot take for itself. */
2
+ /**
3
+ * **Two derivatives the forward path gets from the hardware and this pipeline has to build.** A
4
+ * fragment shader takes `dFdx` and `dFdy`; a compute invocation has no neighbours. The shading
5
+ * pass already has the analytic barycentric gradients — `SHADE_SURFACE_WGSL` builds them for the
6
+ * shadow's receiver plane — and any attribute's screen gradient is those weights applied to its
7
+ * three corners. The UV's picks a mip level; the UV's and the world position's together build the
8
+ * tangent frame a normal map is read through.
9
+ */
10
+ /**
11
+ * The mip level a pixel's texture footprint asks for: the log of the longer of the two screen-axis
12
+ * UV steps, in texels. The common isotropic form; a device choosing its own level for an
13
+ * implicit-derivative sample may differ in the last bits, which is stated where the two pipelines
14
+ * are compared rather than hidden here.
15
+ */
16
+ export function surfaceLod(dudx, dvdx, dudy, dvdy, size) {
17
+ const footprint = Math.max(Math.hypot(dudx, dvdx), Math.hypot(dudy, dvdy)) * size;
18
+ return Math.max(Math.log2(Math.max(footprint, 1e-30)), 0);
19
+ }
20
+ /**
21
+ * `tangentFrame.ts`'s derived branch, from analytic gradients instead of `dFdx` and `dFdy`.
22
+ *
23
+ * **Term for term, including the determinant's sign and the one scale for both axes**, because the
24
+ * forward path's comment records what each is worth: without the sign the frame points the other
25
+ * way on one backend, and two scales shear a stretched UV layout. The screen axes here are x to the
26
+ * right and y up, as the shading pass measures them; the fold makes the answer the same either way.
27
+ */
28
+ export function derivedTangentFrame(n, dp1, dp2, duv1, duv2, out) {
29
+ const p2x = dp2[1] * n[2] - dp2[2] * n[1];
30
+ const p2y = dp2[2] * n[0] - dp2[0] * n[2];
31
+ const p2z = dp2[0] * n[1] - dp2[1] * n[0];
32
+ const p1x = n[1] * dp1[2] - n[2] * dp1[1];
33
+ const p1y = n[2] * dp1[0] - n[0] * dp1[2];
34
+ const p1z = n[0] * dp1[1] - n[1] * dp1[0];
35
+ const tx = p2x * duv1[0] + p1x * duv2[0];
36
+ const ty = p2y * duv1[0] + p1y * duv2[0];
37
+ const tz = p2z * duv1[0] + p1z * duv2[0];
38
+ const bx = p2x * duv1[1] + p1x * duv2[1];
39
+ const by = p2y * duv1[1] + p1y * duv2[1];
40
+ const bz = p2z * duv1[1] + p1z * duv2[1];
41
+ const det = duv1[0] * duv2[1] - duv2[0] * duv1[1];
42
+ const handed = det < 0 ? -1 : 1;
43
+ const scale = handed / Math.sqrt(Math.max(tx * tx + ty * ty + tz * tz, bx * bx + by * by + bz * bz, 1e-12));
44
+ out[0] = tx * scale;
45
+ out[1] = ty * scale;
46
+ out[2] = tz * scale;
47
+ out[3] = bx * scale;
48
+ out[4] = by * scale;
49
+ out[5] = bz * scale;
50
+ return out;
51
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Occlusion tested against depth from **this** frame rather than the last one.
3
+ *
4
+ * A hierarchical depth buffer is only an occluder if something has already been drawn into it, and
5
+ * the cheap way to have one at the start of a frame is to keep last frame's. That is wrong exactly
6
+ * when the camera moved: the pyramid describes a view nobody is looking from, and geometry the old
7
+ * view happened to hide is culled out of the new one. What it looks like is a wall or a floor
8
+ * disappearing for a frame or two after a cut, a teleport, or a fast turn — and it is intermittent,
9
+ * so it is reported as a streaming fault.
10
+ *
11
+ * **So the frame is drawn in two halves.** Phase one draws only what was visible last frame, which
12
+ * on a continuous camera is nearly everything and is a good occluder for the rest. The pyramid is
13
+ * reduced from *that* depth. Phase two then tests everything else against it and draws whatever
14
+ * phase one missed. Both halves write the same colour and depth, because they are one frame.
15
+ *
16
+ * **The history decides which phase a cluster is judged in and never whether it is judged.** Every
17
+ * cluster is in exactly one of the two lists, whatever the history holds, so a history that is
18
+ * stale — which after a teleport is all of it — costs a phase-two test and never a hole. That
19
+ * invariant is what makes the history safe to keep across a cut, and it is the one this file's
20
+ * tests assert most directly.
21
+ *
22
+ * **One history per view.** A shadow pass and the main camera see different things, and sharing a
23
+ * bit set between them puts one view's visibility into the other's phase one — which is the same
24
+ * defect as a stale history, arriving every frame instead of after a cut.
25
+ *
26
+ * The pass recording here is the frame graph's data rather than device calls: `phaseOnePasses` and
27
+ * `phaseTwoPasses` record what each half reads and writes, and the edge from the reduction's write
28
+ * to phase two's read *is* the guarantee in the first paragraph. Wiring them to a real encoder is
29
+ * Task 14's, against a consumer, for the reason `ARCHITECTURE.md` §3 gives about rendering changes
30
+ * nobody has looked at.
31
+ */
32
+ import type { Deps } from '../frame/deps.ts';
33
+ /** Which clusters a view drew last frame, one bit each. */
34
+ export interface VisibilityHistory {
35
+ /** Thirty-two clusters a word, cluster `n` at bit `n & 31` of word `n >> 5`. */
36
+ bits: Uint32Array;
37
+ /** How many clusters it has room for. A wider one is allocated rather than the old one read. */
38
+ capacity: number;
39
+ }
40
+ export declare function createVisibilityHistory(capacity: number): VisibilityHistory;
41
+ /**
42
+ * Whether a cluster was drawn last frame.
43
+ *
44
+ * **Out of range answers false rather than throwing or wrapping.** A cluster index past the end is
45
+ * a scene that grew since the history was made, and the honest answer for one nobody has seen is
46
+ * "not last frame" — which puts it in phase two, where it is tested rather than assumed. Wrapping
47
+ * would put an unrelated cluster's bit in its place, which is a visibility answer for the wrong
48
+ * geometry and is invisible until it is wrong.
49
+ *
50
+ * **A perturbation survives this check and it is kept anyway.** `setHistory` refuses the same
51
+ * range, so no bit above `capacity` can ever be set, and a read past the end of the word array is
52
+ * `undefined` — which `&` coerces to zero and therefore answers false already. What it decides is
53
+ * the case where the two guards disagree, and the reason both are here rather than one is that
54
+ * they are read independently: the write side is the one a test can catch, and it is caught. The
55
+ * bits between `capacity` and the end of the last word are the ones that make the write side
56
+ * matter — forty clusters is two words, so twenty-four of them belong to nobody.
57
+ */
58
+ export declare function historyHas(history: VisibilityHistory, cluster: number): boolean;
59
+ export declare function setHistory(history: VisibilityHistory, cluster: number, drawn: boolean): void;
60
+ export declare function clearHistory(history: VisibilityHistory): void;
61
+ /** How many of the first `count` clusters the history holds. */
62
+ export declare function historyCount(history: VisibilityHistory, count: number): number;
63
+ /** The clusters phase one draws: exactly what was visible last frame, in index order. */
64
+ export declare function phaseOneClusters(history: VisibilityHistory, count: number, out: Uint32Array): number;
65
+ /** The clusters phase two tests: everything the history does not hold. */
66
+ export declare function phaseTwoClusters(history: VisibilityHistory, count: number, out: Uint32Array): number;
67
+ /**
68
+ * Replace the history with what this frame actually drew.
69
+ *
70
+ * **Replace rather than accumulate**, and that is the half that is easy to leave out: a cluster
71
+ * kept in phase one and then culled in phase two has to leave the set, or it is drawn every frame
72
+ * for ever on the strength of having once been visible. `keep` is the flag buffer
73
+ * `cullClusters` writes, so this is one pass over the same array the draw used.
74
+ */
75
+ export declare function updateHistory(history: VisibilityHistory, keep: Uint32Array, count: number): void;
76
+ /** Every view's history, keyed by whatever identifier the caller gives its views. */
77
+ export interface VisibilityHistories {
78
+ readonly byView: Map<number, VisibilityHistory>;
79
+ }
80
+ export declare function createVisibilityHistories(): VisibilityHistories;
81
+ /**
82
+ * This view's history, widened in place if the scene grew.
83
+ *
84
+ * **Widened rather than replaced**, because replacing it throws away a frame of visibility every
85
+ * time a cluster is added and puts the whole scene through phase two — which is correct and is
86
+ * also the slow path this feature exists to avoid.
87
+ */
88
+ export declare function historyFor(store: VisibilityHistories, view: number, capacity: number): VisibilityHistory;
89
+ /** The virtual resources a two-phase frame moves between its halves. */
90
+ export interface TwoPhaseResources {
91
+ readonly colour: number;
92
+ readonly depth: number;
93
+ readonly hzb: number;
94
+ readonly clusters: number;
95
+ readonly keep: number;
96
+ }
97
+ /** Where phase one's two nodes landed in the dependency table. */
98
+ export interface PhaseOneNodes {
99
+ readonly draw: number;
100
+ readonly reduce: number;
101
+ }
102
+ /** Where phase two's two nodes landed. */
103
+ export interface PhaseTwoNodes {
104
+ readonly cull: number;
105
+ readonly draw: number;
106
+ }
107
+ /**
108
+ * Phase one: draw what was visible, then reduce the depth it produced into the pyramid.
109
+ *
110
+ * The reduction reads the depth the draw wrote, so nothing can reorder them and nothing can alias
111
+ * the depth away between them — which is exactly what `lifetime.ts` and `alias.ts` are reading
112
+ * these edges for.
113
+ */
114
+ export declare function phaseOnePasses(deps: Deps, res: TwoPhaseResources): PhaseOneNodes;
115
+ /**
116
+ * Phase two: cull everything else against that pyramid, then draw what survived.
117
+ *
118
+ * `cull` reads `hzb`, which `phaseOnePasses` wrote. That one edge is the whole claim of this
119
+ * module — the depth being tested against belongs to this frame — expressed where the scheduler
120
+ * can enforce it rather than in a comment somebody has to obey.
121
+ */
122
+ export declare function phaseTwoPasses(deps: Deps, res: TwoPhaseResources): PhaseTwoNodes;