@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
@@ -317,7 +317,12 @@ export class InputSource {
317
317
  constructor(target, preventDefaultCodes = [], options = {}) {
318
318
  this.target = target;
319
319
  this.preventDefaultCodes = new Set(preventDefaultCodes);
320
- this.isCoarse = window.matchMedia('(pointer: coarse)').matches;
320
+ /* Guarded, and `ui/fullscreen.ts` guards the identical query with the identical test. This
321
+ line is in a constructor, so unguarded it did not degrade a hint — it threw, and an embedder
322
+ without `matchMedia` could not construct input at all. False is what a runtime with no
323
+ pointer media query honestly is. */
324
+ this.isCoarse =
325
+ typeof window.matchMedia === 'function' && window.matchMedia('(pointer: coarse)').matches;
321
326
  this.deadzone = options.deadzone ?? DEFAULT_DEADZONE;
322
327
  this.autoPoll = options.autoPoll ?? true;
323
328
  for (const name of ['keyboard', 'mouse', 'touch', 'gamepad']) {
@@ -18,9 +18,10 @@
18
18
  *
19
19
  * ## What is here, and what is deliberately not
20
20
  *
21
- * `exactSin`, `exactCos`, `exactExp`, `exactAcos`. Four, because four is what the engine and its
22
- * first consumer actually reach for: the terrain binding's slope angle needs `acos`, a lockstep
23
- * fixture rotating anything needs `sin` and `cos`, and exponential decay needs `exp`.
21
+ * `exactSin`, `exactCos`, `exactExp`, `exactAcos` and `exactLog`. Five, because five is what the
22
+ * engine and its first consumer actually reach for: the terrain binding's slope angle needs `acos`,
23
+ * a lockstep fixture rotating anything needs `sin` and `cos`, exponential decay needs `exp`, and a
24
+ * detector's box prior is a logit of the patch grid, which needs `log`.
24
25
  *
25
26
  * **`atan2` is absent.** It is the obvious fifth and nothing here needs one: `shape.ts` sorts by a
26
27
  * comparison key computed from two in-plane axes rather than by an angle, and `joints.ts` compares
@@ -55,6 +56,15 @@ export declare function exactCos(x: number): number;
55
56
  * representation and there is nothing a fitted polynomial would buy.
56
57
  */
57
58
  export declare function exactExp(x: number): number;
59
+ /**
60
+ * `Math.log`, reproducibly: fdlibm's algorithm, which is also V8's.
61
+ *
62
+ * Take the power of two out as `k` and leave a significand in [√2/2, √2), so that `f = m - 1` is
63
+ * small either side of zero; then log(1 + f) = f - f²/2 + s·(f²/2 + R(s²)) with s = f/(2 + f), and
64
+ * `k·ln 2` added back in two parts so the large part loses nothing. The exponent is read and the
65
+ * significand rescaled through the double's own words — integer operations, not transcendentals.
66
+ */
67
+ export declare function exactLog(x: number): number;
58
68
  /**
59
69
  * `Math.acos`, reproducibly. Outside [-1, 1] this answers NaN, as `Math.acos` does.
60
70
  *
@@ -18,9 +18,10 @@
18
18
  *
19
19
  * ## What is here, and what is deliberately not
20
20
  *
21
- * `exactSin`, `exactCos`, `exactExp`, `exactAcos`. Four, because four is what the engine and its
22
- * first consumer actually reach for: the terrain binding's slope angle needs `acos`, a lockstep
23
- * fixture rotating anything needs `sin` and `cos`, and exponential decay needs `exp`.
21
+ * `exactSin`, `exactCos`, `exactExp`, `exactAcos` and `exactLog`. Five, because five is what the
22
+ * engine and its first consumer actually reach for: the terrain binding's slope angle needs `acos`,
23
+ * a lockstep fixture rotating anything needs `sin` and `cos`, exponential decay needs `exp`, and a
24
+ * detector's box prior is a logit of the patch grid, which needs `log`.
24
25
  *
25
26
  * **`atan2` is absent.** It is the obvious fifth and nothing here needs one: `shape.ts` sorts by a
26
27
  * comparison key computed from two in-plane axes rather than by an angle, and `joints.ts` compares
@@ -226,6 +227,70 @@ export function exactExp(x) {
226
227
  sum = 1 + r * sum;
227
228
  return sum * pow2(k);
228
229
  }
230
+ /* fdlibm's `log` polynomial in s², for log(1 + f) around f = 0 by way of s = f / (2 + f). */
231
+ const LG1 = 6.66666666666673513e-1;
232
+ const LG2 = 3.999999999940941908e-1;
233
+ const LG3 = 2.857142874366239149e-1;
234
+ const LG4 = 2.222219843214978396e-1;
235
+ const LG5 = 1.818357216161805012e-1;
236
+ const LG6 = 1.531383769920937332e-1;
237
+ const LG7 = 1.479819860511658591e-1;
238
+ const TWO54 = 1.8014398509481984e16;
239
+ const SMALLEST_NORMAL = 2.2250738585072014e-308;
240
+ /* A double's two words, for reading and setting an exponent; one view, reused. */
241
+ const words = new DataView(new ArrayBuffer(8));
242
+ /**
243
+ * `Math.log`, reproducibly: fdlibm's algorithm, which is also V8's.
244
+ *
245
+ * Take the power of two out as `k` and leave a significand in [√2/2, √2), so that `f = m - 1` is
246
+ * small either side of zero; then log(1 + f) = f - f²/2 + s·(f²/2 + R(s²)) with s = f/(2 + f), and
247
+ * `k·ln 2` added back in two parts so the large part loses nothing. The exponent is read and the
248
+ * significand rescaled through the double's own words — integer operations, not transcendentals.
249
+ */
250
+ export function exactLog(x) {
251
+ if (Number.isNaN(x) || x < 0)
252
+ return Number.NaN;
253
+ if (x === 0)
254
+ return Number.NEGATIVE_INFINITY;
255
+ if (x === Number.POSITIVE_INFINITY)
256
+ return x;
257
+ let k = 0;
258
+ let value = x;
259
+ if (value < SMALLEST_NORMAL) {
260
+ k = -54;
261
+ value *= TWO54;
262
+ }
263
+ words.setFloat64(0, value);
264
+ let hx = words.getUint32(0);
265
+ k += (hx >>> 20) - 1023;
266
+ hx &= 0x000fffff;
267
+ /* Keep the significand at or above √2/2: past √2, halve it and count one more power of two. */
268
+ const i = (hx + 0x95f64) & 0x100000;
269
+ words.setUint32(0, hx | (i ^ 0x3ff00000));
270
+ k += i >>> 20;
271
+ const f = words.getFloat64(0) - 1;
272
+ if ((0x000fffff & (2 + hx)) < 3) {
273
+ /* |f| < 2^-20: two terms of the series are the whole answer. */
274
+ if (f === 0)
275
+ return k === 0 ? 0 : k * LN2_HI + k * LN2_LO;
276
+ const r = f * f * (0.5 - 0.33333333333333333 * f);
277
+ return k === 0 ? f - r : k * LN2_HI - (r - k * LN2_LO - f);
278
+ }
279
+ const s = f / (2 + f);
280
+ const z = s * s;
281
+ const w = z * z;
282
+ const t1 = w * (LG2 + w * (LG4 + w * LG6));
283
+ const t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7)));
284
+ const r = t2 + t1;
285
+ /* Far enough from one that f²/2 is worth carrying apart from f. */
286
+ if (((hx - 0x6147a) | (0x6b851 - hx)) > 0) {
287
+ const half = 0.5 * f * f;
288
+ return k === 0
289
+ ? f - (half - s * (half + r))
290
+ : k * LN2_HI - (half - (s * (half + r) + k * LN2_LO) - f);
291
+ }
292
+ return k === 0 ? f - s * (f - r) : k * LN2_HI - (s * (f - r) - k * LN2_LO - f);
293
+ }
229
294
  /** fdlibm's `asin` rational on `z = x²`, valid where `z` is small. */
230
295
  function asinRational(z) {
231
296
  const numerator = z * (P0 + z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5)))));
@@ -10,6 +10,11 @@ import type { ReadonlyMat4 } from 'gl-matrix';
10
10
  * the frustum when `n · p + d >= 0` for all six.
11
11
  */
12
12
  export type Frustum = Float32Array;
13
+ /**
14
+ * Planes in either precision. **Double precision is for a world past 2^24 units**, where a plane's
15
+ * offset is a number a single float cannot hold to the metre; `cellsInFrustum` asks for it.
16
+ */
17
+ export type FrustumPlanes = Float32Array | Float64Array;
13
18
  export declare function createFrustum(): Frustum;
14
19
  /**
15
20
  * Pull the six planes out of a view-projection matrix.
@@ -28,7 +33,7 @@ export declare function createFrustum(): Frustum;
28
33
  *
29
34
  * gl-matrix stores column-major, so `m[column * 4 + row]`.
30
35
  */
31
- export declare function frustumFromViewProjection(m: ReadonlyMat4, out: Frustum): Frustum;
36
+ export declare function frustumFromViewProjection<T extends FrustumPlanes>(m: ReadonlyMat4 | Float64Array, out: T): T;
32
37
  /**
33
38
  * Whether a world-space sphere is anywhere inside the frustum.
34
39
  *
@@ -41,4 +46,14 @@ export declare function frustumFromViewProjection(m: ReadonlyMat4, out: Frustum)
41
46
  * all six planes, near an edge. That costs a draw that turns out to be invisible and never costs
42
47
  * a draw that was visible, which is the only asymmetry a cull may have.
43
48
  */
44
- export declare function sphereInFrustum(frustum: Frustum, x: number, y: number, z: number, radius: number): boolean;
49
+ export declare function sphereInFrustum(frustum: FrustumPlanes, x: number, y: number, z: number, radius: number): boolean;
50
+ /**
51
+ * Whether an axis-aligned box is anywhere inside the frustum, by the same question the sphere test
52
+ * asks: outside only when wholly beyond one plane.
53
+ *
54
+ * **The corner that decides it is chosen per plane** — the one furthest along that plane's normal.
55
+ * A fixed corner answers correctly for the planes whose normals happen to point its way and culls a
56
+ * box straddling any of the others. Conservative at the edges, as the sphere test is: a box near a
57
+ * corner of the frustum can be outside it and still kept.
58
+ */
59
+ export declare function boxInFrustum(frustum: FrustumPlanes, minX: number, minY: number, minZ: number, maxX: number, maxY: number, maxZ: number): boolean;
@@ -88,3 +88,27 @@ export function sphereInFrustum(frustum, x, y, z, radius) {
88
88
  }
89
89
  return true;
90
90
  }
91
+ /**
92
+ * Whether an axis-aligned box is anywhere inside the frustum, by the same question the sphere test
93
+ * asks: outside only when wholly beyond one plane.
94
+ *
95
+ * **The corner that decides it is chosen per plane** — the one furthest along that plane's normal.
96
+ * A fixed corner answers correctly for the planes whose normals happen to point its way and culls a
97
+ * box straddling any of the others. Conservative at the edges, as the sphere test is: a box near a
98
+ * corner of the frustum can be outside it and still kept.
99
+ */
100
+ export function boxInFrustum(frustum, minX, minY, minZ, maxX, maxY, maxZ) {
101
+ for (let plane = 0; plane < PLANE_COUNT; plane += 1) {
102
+ const at = plane * 4;
103
+ const nx = frustum[at] ?? 0;
104
+ const ny = frustum[at + 1] ?? 0;
105
+ const nz = frustum[at + 2] ?? 0;
106
+ const reach = nx * (nx >= 0 ? maxX : minX) +
107
+ ny * (ny >= 0 ? maxY : minY) +
108
+ nz * (nz >= 0 ? maxZ : minZ) +
109
+ (frustum[at + 3] ?? 0);
110
+ if (reach < 0)
111
+ return false;
112
+ }
113
+ return true;
114
+ }
@@ -464,7 +464,20 @@ export type RendererApi = Omit<{
464
464
  */
465
465
  updateMesh(mesh: MeshHandle, positions: Float32Array, normals?: Float32Array): void;
466
466
  /** Draw a handle this renderer made. Passing one from the other backend is a caller error. */
467
- drawMesh(mesh: MeshHandle, model: ReadonlyMat4, depthLayer?: number, tint?: Vec3 | null): void;
467
+ drawMesh(mesh: MeshHandle, model: ReadonlyMat4, depthLayer?: number, tint?: Vec3 | null,
468
+ /**
469
+ * Where this mesh's surface was last frame, for a reconstruction's motion target.
470
+ *
471
+ * **Only a reconstruction reads it**, and only on WebGPU: it is what turns a moving object's
472
+ * stale history from a smear into a reprojection. A draw that says nothing gets the camera's
473
+ * motion, derived from the depth, which is exactly right for anything that did not move and
474
+ * wrong in proportion to how far a mover moved. Null and omitted mean the same thing.
475
+ *
476
+ * It is the *model* matrix this mesh was drawn with last frame, not a velocity: the pass
477
+ * transforms each vertex twice and lets the perspective divide do the rest, so a rotation and a
478
+ * scale are as correct as a translation.
479
+ */
480
+ previousModel?: ReadonlyMat4 | null): void;
468
481
  /** Release the geometry behind a handle. */
469
482
  disposeMesh(mesh: MeshHandle): void;
470
483
  /**
@@ -494,3 +507,23 @@ export type RendererApi = Omit<{
494
507
  };
495
508
  /** Which backend is actually drawing. Reported, never inferred. */
496
509
  export type RenderBackend = 'webgl2' | 'webgpu';
510
+ /**
511
+ * Whether this backend can be driven from buffers the GPU itself wrote.
512
+ *
513
+ * **A fact about the backend, not a method on this surface, and that is the finding rather than
514
+ * the shortcut.** Indirect execution needs a render or compute pass encoder, and both are already
515
+ * in the hands of the code that would use them: `PassContext` hands a contributed pass the
516
+ * `GPURenderPassEncoder`, and `ComputeDefinition.dispatch` hands a definition the
517
+ * `GPUComputePassEncoder` while saying in as many words that the workgroup count is the
518
+ * definition's and does not cross this surface. A `drawIndexedIndirect` here would be a second
519
+ * way to reach an encoder a caller already holds.
520
+ *
521
+ * So what is missing is not a verb. It is an answer to *may I*, which a GPU-driven pipeline has
522
+ * to ask before it builds an argument buffer nothing can consume.
523
+ *
524
+ * **WebGL2 is a refusal rather than an omission.** It has no compute stage to write an argument
525
+ * buffer and no indirect draw to read one. Emulating it means reading the buffer back to the
526
+ * processor and issuing the draws from there, which is slower than the ordinary path it would be
527
+ * pretending to replace — so this reports false and the GPU-driven pipeline is WebGPU-only.
528
+ */
529
+ export declare function indirectSupport(backend: 'webgl2' | 'webgpu'): boolean;
@@ -1 +1,22 @@
1
- export {};
1
+ /**
2
+ * Whether this backend can be driven from buffers the GPU itself wrote.
3
+ *
4
+ * **A fact about the backend, not a method on this surface, and that is the finding rather than
5
+ * the shortcut.** Indirect execution needs a render or compute pass encoder, and both are already
6
+ * in the hands of the code that would use them: `PassContext` hands a contributed pass the
7
+ * `GPURenderPassEncoder`, and `ComputeDefinition.dispatch` hands a definition the
8
+ * `GPUComputePassEncoder` while saying in as many words that the workgroup count is the
9
+ * definition's and does not cross this surface. A `drawIndexedIndirect` here would be a second
10
+ * way to reach an encoder a caller already holds.
11
+ *
12
+ * So what is missing is not a verb. It is an answer to *may I*, which a GPU-driven pipeline has
13
+ * to ask before it builds an argument buffer nothing can consume.
14
+ *
15
+ * **WebGL2 is a refusal rather than an omission.** It has no compute stage to write an argument
16
+ * buffer and no indirect draw to read one. Emulating it means reading the buffer back to the
17
+ * processor and issuing the draws from there, which is slower than the ordinary path it would be
18
+ * pretending to replace — so this reports false and the GPU-driven pipeline is WebGPU-only.
19
+ */
20
+ export function indirectSupport(backend) {
21
+ return backend === 'webgpu';
22
+ }
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * What a frame asked of a backend, against what that backend allows.
3
3
  *
4
- * **This exists because a ceiling firing was unobservable.** The WebGPU backend holds ten per-frame
5
- * ceilings — draws, material changes, overlays, water bodies, light volumes, plumes, three kinds of
6
- * effect batch and the scatter shadow round — and past any of them it skips the work and writes one
7
- * `console.warn` for the lifetime of the renderer. WebGL2 holds none of them and draws everything.
8
- * So a scene over a ceiling renders differently on the two backends, silently, and a consumer
9
- * developing against WebGL2 has no way to see it coming.
4
+ * **This exists because a ceiling firing was unobservable.** The WebGPU backend holds fourteen
5
+ * per-frame ceilings — draws, material changes, two kinds of shadow draw, water bodies, light
6
+ * volumes, four kinds of effect batch and four kinds of overlay — and past any of them it skips the
7
+ * work and writes one `console.warn` for the lifetime of the renderer. WebGL2 holds none of them and
8
+ * draws everything. So a scene over a ceiling renders differently on the two backends, silently,
9
+ * and a consumer developing against WebGL2 had no way to see it coming — which is why WebGL2 counts
10
+ * the same fifteen lines by the same rules, and reports the number the other backend rations.
10
11
  *
11
12
  * **The cost of that was measured from outside.** A consumer spent weeks on ground that
12
13
  * vanished and reappeared as the camera moved; its own report still lists, as the decisive
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * What a frame asked of a backend, against what that backend allows.
3
3
  *
4
- * **This exists because a ceiling firing was unobservable.** The WebGPU backend holds ten per-frame
5
- * ceilings — draws, material changes, overlays, water bodies, light volumes, plumes, three kinds of
6
- * effect batch and the scatter shadow round — and past any of them it skips the work and writes one
7
- * `console.warn` for the lifetime of the renderer. WebGL2 holds none of them and draws everything.
8
- * So a scene over a ceiling renders differently on the two backends, silently, and a consumer
9
- * developing against WebGL2 has no way to see it coming.
4
+ * **This exists because a ceiling firing was unobservable.** The WebGPU backend holds fourteen
5
+ * per-frame ceilings — draws, material changes, two kinds of shadow draw, water bodies, light
6
+ * volumes, four kinds of effect batch and four kinds of overlay — and past any of them it skips the
7
+ * work and writes one `console.warn` for the lifetime of the renderer. WebGL2 holds none of them and
8
+ * draws everything. So a scene over a ceiling renders differently on the two backends, silently,
9
+ * and a consumer developing against WebGL2 had no way to see it coming — which is why WebGL2 counts
10
+ * the same fifteen lines by the same rules, and reports the number the other backend rations.
10
11
  *
11
12
  * **The cost of that was measured from outside.** A consumer spent weeks on ground that
12
13
  * vanished and reappeared as the camera moved; its own report still lists, as the decisive
@@ -1,6 +1,38 @@
1
1
  import { type RenderQualityOptions } from '../renderQuality.ts';
2
2
  import type { RenderBackend, RendererApi } from './api.ts';
3
3
  import { type SplashOptions } from '../../ui/splash.ts';
4
+ /**
5
+ * Which of the two pipelines is drawing.
6
+ *
7
+ * A union rather than a boolean because a third is foreseeable and a boolean named for one of them
8
+ * is what makes the third awkward — `ROADMAP.md`'s own note about `hdrScene` starting life as
9
+ * `bool hdr`.
10
+ */
11
+ export type RenderPipeline = 'forward' | 'gpu-driven';
12
+ /**
13
+ * What a caller who says nothing gets.
14
+ *
15
+ * **A named constant rather than a literal in the `??`**, because the literal is unreachable from
16
+ * a test here: nothing in this environment resolves a renderer, so flipping it changed no answer
17
+ * and no test noticed. The constant is where the decision lives and it is asserted directly.
18
+ *
19
+ * Forward, and it stays forward until the second pipeline has drawn the six published scenes.
20
+ */
21
+ export declare const DEFAULT_PIPELINE: RenderPipeline;
22
+ /**
23
+ * Why a requested pipeline cannot run on the backend that is about to draw, or null where it can.
24
+ *
25
+ * **Its own function because it is the only part of the decision a test in this environment can
26
+ * reach.** Nothing here can resolve a renderer — there is no GL context and no adapter — so every
27
+ * test of `createRenderer` ends in a rejection, and a check that lives only inside the factory is
28
+ * a check nothing exercises. `built` is this function's one caller and every return from the
29
+ * factory passes through it.
30
+ *
31
+ * **The backend is the one that will draw, not the one that was asked for.** Three paths into
32
+ * `built` arrive having fallen back from WebGPU, and a check written against the request passes on
33
+ * all three.
34
+ */
35
+ export declare function pipelineRefusal(pipeline: RenderPipeline, backend: RenderBackend, reason: string): string | null;
4
36
  /** Everything about selection a caller may want to decide instead of inherit. */
5
37
  export interface CreateRendererOptions {
6
38
  /**
@@ -19,6 +51,23 @@ export interface CreateRendererOptions {
19
51
  * shows that, and the fallback is silent when it happens.
20
52
  */
21
53
  readonly preferWebGpu?: boolean;
54
+ /**
55
+ * Which pipeline to draw with. **Defaults to `'forward'`.**
56
+ *
57
+ * `'gpu-driven'` selects the cluster pipeline: instances and clusters culled on the device
58
+ * against a depth pyramid built this frame, a visibility buffer, and one shading dispatch per
59
+ * material. It needs indirect draws and dispatches, which WebGL2 does not have.
60
+ *
61
+ * **Asking for it where it cannot run throws.** Not a warning and not a fallback: a silent
62
+ * fallback means a consumer ships believing they have a pipeline they do not, and the difference
63
+ * only shows as a frame time on somebody else's machine. The throw carries the reason in words,
64
+ * from `gpuDrivenRefusal`.
65
+ *
66
+ * **This includes a WebGPU request that fell back**, which is the case a check written against
67
+ * the *asked-for* backend would miss: every fallback path here is a path where the pipeline
68
+ * became unavailable after the choice was made.
69
+ */
70
+ readonly pipeline?: RenderPipeline;
22
71
  /**
23
72
  * The query string to read `?backend=` from. Defaults to the document's own.
24
73
  *
@@ -116,6 +165,14 @@ export interface CreatedRenderer {
116
165
  * renderer knows.
117
166
  */
118
167
  readonly reversedDepth: boolean;
168
+ /**
169
+ * Which pipeline is drawing: the forward path, or the one driven from buffers the GPU wrote.
170
+ *
171
+ * Reported for the same reason `backend` is — a consumer who asked for one and got the other
172
+ * has to be able to find out. They cannot, here: asking for `gpu-driven` on a backend that
173
+ * cannot run it throws rather than answering `forward`. See `CreateRendererOptions.pipeline`.
174
+ */
175
+ readonly pipeline: RenderPipeline;
119
176
  }
120
177
  /**
121
178
  * Build a renderer on the best backend this browser will actually give us.
@@ -3,6 +3,35 @@ import { resolveRenderQuality } from '../renderQuality.js';
3
3
  import { BACKEND_TIMEOUT_MS, forcedBackend, selectBackend } from './select.js';
4
4
  import { TIMED_OUT, withDeadline } from './deadline.js';
5
5
  import { mountSplash } from '../../ui/splash.js';
6
+ import { gpuDrivenRefusal, gpuDrivenSupported } from '../gpudriven/pipeline.js';
7
+ /**
8
+ * What a caller who says nothing gets.
9
+ *
10
+ * **A named constant rather than a literal in the `??`**, because the literal is unreachable from
11
+ * a test here: nothing in this environment resolves a renderer, so flipping it changed no answer
12
+ * and no test noticed. The constant is where the decision lives and it is asserted directly.
13
+ *
14
+ * Forward, and it stays forward until the second pipeline has drawn the six published scenes.
15
+ */
16
+ export const DEFAULT_PIPELINE = 'forward';
17
+ /**
18
+ * Why a requested pipeline cannot run on the backend that is about to draw, or null where it can.
19
+ *
20
+ * **Its own function because it is the only part of the decision a test in this environment can
21
+ * reach.** Nothing here can resolve a renderer — there is no GL context and no adapter — so every
22
+ * test of `createRenderer` ends in a rejection, and a check that lives only inside the factory is
23
+ * a check nothing exercises. `built` is this function's one caller and every return from the
24
+ * factory passes through it.
25
+ *
26
+ * **The backend is the one that will draw, not the one that was asked for.** Three paths into
27
+ * `built` arrive having fallen back from WebGPU, and a check written against the request passes on
28
+ * all three.
29
+ */
30
+ export function pipelineRefusal(pipeline, backend, reason) {
31
+ if (pipeline !== 'gpu-driven' || gpuDrivenSupported(backend))
32
+ return null;
33
+ return `[driftengine] ${gpuDrivenRefusal(backend)} (${reason})`;
34
+ }
6
35
  /**
7
36
  * Hand the badge the one fact it is waiting for: a frame reached the screen.
8
37
  *
@@ -60,7 +89,22 @@ export async function createRenderer(canvas, quality = {}, options = {}) {
60
89
  * the two failure paths — a fallback still paints, and a badge that outlived a fallback would
61
90
  * sit on the screen until the cap for a game that was running perfectly well underneath it.
62
91
  */
92
+ const pipeline = options.pipeline ?? DEFAULT_PIPELINE;
63
93
  const built = (result) => {
94
+ /*
95
+ * **Checked here rather than at the top, because every return passes through here.** The
96
+ * question is not which backend was *asked for* — it is which one is about to draw, and three
97
+ * of the paths into this function arrive having fallen back. A check against the request would
98
+ * pass on all three and hand back a forward renderer with `pipeline: 'gpu-driven'` on it.
99
+ *
100
+ * The splash goes down first. A page that throws out of `createRenderer` with a badge mounted
101
+ * leaves it on screen over nothing, for as long as the tab is open.
102
+ */
103
+ const refused = pipelineRefusal(pipeline, result.backend, result.reason);
104
+ if (refused !== null) {
105
+ splash?.present();
106
+ throw new Error(refused);
107
+ }
64
108
  if (splash !== null)
65
109
  presentOnFirstFrame(result.renderer, splash);
66
110
  /*
@@ -80,6 +124,7 @@ export async function createRenderer(canvas, quality = {}, options = {}) {
80
124
  ...result,
81
125
  rendererName: result.renderer.rendererName,
82
126
  reversedDepth: result.renderer.reversedDepth,
127
+ pipeline,
83
128
  };
84
129
  };
85
130
  /*
@@ -93,7 +138,11 @@ export async function createRenderer(canvas, quality = {}, options = {}) {
93
138
  * the promise the deadline was racing.
94
139
  */
95
140
  const budgetMs = options.backendTimeoutMs ?? BACKEND_TIMEOUT_MS;
96
- const startedAt = Date.now();
141
+ /* Monotonic, and it has to be: `budgetMs - spent` below subtracts two readings. The wall clock
142
+ steps — daylight saving, an NTP correction, a laptop resuming with a stale clock — and a
143
+ forward step makes `spent` exceed the budget, so the engine falls back to WebGL2 reporting
144
+ "WebGPU stalled" about a device that was fine. */
145
+ const startedAt = performance.now();
97
146
  const choice = await selectBackend(search, options.preferWebGpu ?? true, quality.gpuTiming ?? false, options.probeShaders ?? [], budgetMs);
98
147
  if (choice.backend === 'webgpu' && choice.device !== null) {
99
148
  /*
@@ -131,7 +180,7 @@ export async function createRenderer(canvas, quality = {}, options = {}) {
131
180
  stage = 'constructing the renderer';
132
181
  return { surface, WebGPURenderer };
133
182
  })();
134
- const spent = Date.now() - startedAt;
183
+ const spent = performance.now() - startedAt;
135
184
  const settled = await withDeadline(construction, budgetMs - spent);
136
185
  if (settled === TIMED_OUT) {
137
186
  device.destroy();
@@ -0,0 +1,41 @@
1
+ /**
2
+ * What counts as a material change: the rule both backends count their `materials` line by.
3
+ *
4
+ * **A material is the shading state a draw reads that is not its own placement**, and the WebGPU
5
+ * backend keeps it in a ring of per-frame slots: a draw copies the open material into a new slot
6
+ * when something has changed it since the last draw, and otherwise shares the slot before it. So
7
+ * the slots a frame takes are its material changes, and that is what the backend's ceiling
8
+ * rations. WebGL2 sets the same state as loose uniforms and has nothing to run out of — but a
9
+ * consumer developing there is building the frame the other backend will ration, so it counts the
10
+ * same changes by the same rule, and draws them all.
11
+ *
12
+ * **What changes it**: a material setter; the start of a frame or of a mesh pass, which reopens
13
+ * the pass's own material; a probe bake, which sets its own and puts the pass's back; and a draw
14
+ * carrying options of its own (`ownsMaterial`), whose state is written for it and restored after
15
+ * it, so that it and the draw after it each open one.
16
+ *
17
+ * What it gives up: a setter called with the value already set still counts, because it takes a
18
+ * slot on the backend that keeps slots, and comparing every field would cost more than the slot.
19
+ */
20
+ /** What of a draw's own options its material carries. */
21
+ export interface DrawMaterialOptions {
22
+ readonly opacity: number;
23
+ readonly lit: boolean;
24
+ readonly fog: boolean;
25
+ readonly toneMapped: boolean;
26
+ /** Whether the draw refracts this time, which a backend decides by whether it has a snapshot. */
27
+ readonly refracting: boolean;
28
+ }
29
+ /** Whether a draw's options differ from the pass's, so it takes a material and leaves one to restore. */
30
+ export declare function ownsMaterial(options: DrawMaterialOptions): boolean;
31
+ /** Whether the next draw shares the open material or opens a new one. */
32
+ export declare class MaterialChanges {
33
+ /**
34
+ * The slot the open material occupies on a backend that keeps slots, or -1 when the next draw
35
+ * opens one. A backend without slots writes 0 when it opens one.
36
+ */
37
+ slot: number;
38
+ get open(): boolean;
39
+ /** Something the material holds changed, or a pass reopened it: the next draw opens a new one. */
40
+ dirty(): void;
41
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * What counts as a material change: the rule both backends count their `materials` line by.
3
+ *
4
+ * **A material is the shading state a draw reads that is not its own placement**, and the WebGPU
5
+ * backend keeps it in a ring of per-frame slots: a draw copies the open material into a new slot
6
+ * when something has changed it since the last draw, and otherwise shares the slot before it. So
7
+ * the slots a frame takes are its material changes, and that is what the backend's ceiling
8
+ * rations. WebGL2 sets the same state as loose uniforms and has nothing to run out of — but a
9
+ * consumer developing there is building the frame the other backend will ration, so it counts the
10
+ * same changes by the same rule, and draws them all.
11
+ *
12
+ * **What changes it**: a material setter; the start of a frame or of a mesh pass, which reopens
13
+ * the pass's own material; a probe bake, which sets its own and puts the pass's back; and a draw
14
+ * carrying options of its own (`ownsMaterial`), whose state is written for it and restored after
15
+ * it, so that it and the draw after it each open one.
16
+ *
17
+ * What it gives up: a setter called with the value already set still counts, because it takes a
18
+ * slot on the backend that keeps slots, and comparing every field would cost more than the slot.
19
+ */
20
+ /** Whether a draw's options differ from the pass's, so it takes a material and leaves one to restore. */
21
+ export function ownsMaterial(options) {
22
+ return (options.opacity < 1 || !options.lit || !options.fog || !options.toneMapped || options.refracting);
23
+ }
24
+ /** Whether the next draw shares the open material or opens a new one. */
25
+ export class MaterialChanges {
26
+ /**
27
+ * The slot the open material occupies on a backend that keeps slots, or -1 when the next draw
28
+ * opens one. A backend without slots writes 0 when it opens one.
29
+ */
30
+ slot = -1;
31
+ get open() {
32
+ return this.slot >= 0;
33
+ }
34
+ /** Something the material holds changed, or a pass reopened it: the next draw opens a new one. */
35
+ dirty() {
36
+ this.slot = -1;
37
+ }
38
+ }
@@ -160,11 +160,19 @@ async function acquireWebGpu(gpu, wantsGpuTiming, shaders, stage) {
160
160
  * the default still refuses the model, and `WebGPURenderer.createMesh` says so in one line
161
161
  * rather than letting the driver say it a hundred times.
162
162
  */
163
+ /*
164
+ * **And the storage binding ceiling, for the same reason one level down.** A buffer that can be
165
+ * created is not a buffer that can be bound: `maxStorageBufferBindingSize` defaults to 128 MiB
166
+ * against this machine's 4 GiB, and the GPU-driven pipeline binds its whole vertex buffer as
167
+ * one storage binding. The voxel sandbox's port at a radius of ten asked for 262,807,200 bytes
168
+ * and came back as a hundred device warnings and a world with no terrain.
169
+ */
163
170
  const required = {};
164
171
  for (const limit of [
165
172
  'maxSampledTexturesPerShaderStage',
166
173
  'maxSamplersPerShaderStage',
167
174
  'maxBufferSize',
175
+ 'maxStorageBufferBindingSize',
168
176
  ]) {
169
177
  const ceiling = adapter.limits[limit];
170
178
  if (typeof ceiling === 'number' && ceiling > 0)