@driftengine/core 3.63.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.json +111 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +69 -4
  9. package/dist/index.js +112 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +246 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
@@ -0,0 +1,32 @@
1
+ /** Reading the composed field on the device, the way `sampleGlobalField` reads it on the CPU. */
2
+ /**
3
+ * **One function, included by everything that reads the field, because two spellings drift.**
4
+ *
5
+ * `globalField.ts`'s `sampleGlobalField` is the reference: the finest cascade that holds a point
6
+ * answers, faded into the one outside it across the outer tenth of its extent, and a point past
7
+ * every cascade takes the outermost clamped at its edge. Every one of those three is a decision a
8
+ * second implementation would get subtly differently, so this is text rather than a copy — the
9
+ * march includes it, the parity entry point below includes it, and anything else that needs the
10
+ * field includes it too.
11
+ *
12
+ * **`GLOBAL_FIELD_BLEND` is interpolated in rather than restated**, which is the 2026-08-13 rule
13
+ * about one number in one place: a fade that is a tenth here and a twelfth there is a seam that
14
+ * moves with the camera and nothing in either file looks wrong.
15
+ */
16
+ /** Floats a cascade in the `cascades` buffer: six bounds, the step, and a pad to keep it aligned. */
17
+ export declare const CASCADE_FLOATS = 8;
18
+ /**
19
+ * The sampler, as WGSL text to include.
20
+ *
21
+ * Expects `cascades: array<f32>` and `fields: array<f32>` in scope, and takes the cascade count and
22
+ * the samples a side as arguments rather than reading them from a third buffer — a function that
23
+ * reads a global it was not given is one that cannot be moved.
24
+ */
25
+ export declare const SAMPLE_FIELD_WGSL: string;
26
+ /** Floats in the sampler parity entry point's params: cascade count, samples a side, point count. */
27
+ export declare const SAMPLE_PARAM_FLOATS = 4;
28
+ /**
29
+ * A compute entry point that does nothing but sample, so `gi-parity.mjs` can compare the function
30
+ * above against `sampleGlobalField` directly rather than through whatever is using it.
31
+ */
32
+ export declare const SAMPLE_FIELD_PARITY_WGSL: string;
@@ -0,0 +1,134 @@
1
+ /** Reading the composed field on the device, the way `sampleGlobalField` reads it on the CPU. */
2
+ import { GLOBAL_FIELD_BLEND } from '../../gi/globalField.js';
3
+ /**
4
+ * **One function, included by everything that reads the field, because two spellings drift.**
5
+ *
6
+ * `globalField.ts`'s `sampleGlobalField` is the reference: the finest cascade that holds a point
7
+ * answers, faded into the one outside it across the outer tenth of its extent, and a point past
8
+ * every cascade takes the outermost clamped at its edge. Every one of those three is a decision a
9
+ * second implementation would get subtly differently, so this is text rather than a copy — the
10
+ * march includes it, the parity entry point below includes it, and anything else that needs the
11
+ * field includes it too.
12
+ *
13
+ * **`GLOBAL_FIELD_BLEND` is interpolated in rather than restated**, which is the 2026-08-13 rule
14
+ * about one number in one place: a fade that is a tenth here and a twelfth there is a seam that
15
+ * moves with the camera and nothing in either file looks wrong.
16
+ */
17
+ /** Floats a cascade in the `cascades` buffer: six bounds, the step, and a pad to keep it aligned. */
18
+ export const CASCADE_FLOATS = 8;
19
+ /**
20
+ * The sampler, as WGSL text to include.
21
+ *
22
+ * Expects `cascades: array<f32>` and `fields: array<f32>` in scope, and takes the cascade count and
23
+ * the samples a side as arguments rather than reading them from a third buffer — a function that
24
+ * reads a global it was not given is one that cannot be moved.
25
+ */
26
+ export const SAMPLE_FIELD_WGSL = /* wgsl */ `
27
+ /* Trilinear read of one cascade, clamped at its own edge. The layout is x fastest, then y, then z. */
28
+ fn cascadeAt(level: u32, side: u32, p: vec3<f32>) -> f32 {
29
+ let base = level * ${CASCADE_FLOATS}u;
30
+ let low = vec3<f32>(cascades[base], cascades[base + 1u], cascades[base + 2u]);
31
+ let step = cascades[base + 6u];
32
+ let count = f32(side);
33
+
34
+ var corner = vec3<f32>(0.0, 0.0, 0.0);
35
+ var frac = vec3<f32>(0.0, 0.0, 0.0);
36
+ for (var axis = 0u; axis < 3u; axis = axis + 1u) {
37
+ let local = (p[axis] - low[axis]) / step;
38
+ let clamped = clamp(local, 0.0, count - 1.0);
39
+ /*
40
+ * **The same line guards different things on the two sides, and only one of them shows.**
41
+ * Stopping one plane short keeps the pair this interpolates between inside the cascade. Drop it
42
+ * and the corner at count carries weight zero, so the *value* is identical here — WGSL clamps
43
+ * an out-of-range storage read and zero times anything is zero — while the TypeScript reads
44
+ * \`undefined\` out of a \`Float32Array\` past its end and \`undefined * 0\` is NaN. Perturbing it
45
+ * fails \`globalField.test.ts\` and passes \`gi-parity.mjs\`. It stays because it is what keeps
46
+ * the read inside this cascade rather than in the next one's samples, which share the buffer.
47
+ */
48
+ let index = min(floor(clamped), count - 2.0);
49
+ corner[axis] = index;
50
+ frac[axis] = clamped - index;
51
+ }
52
+
53
+ let offset = level * side * side * side;
54
+ let lx = u32(corner.x);
55
+ let ly = u32(corner.y);
56
+ let lz = u32(corner.z);
57
+ var total = 0.0;
58
+ for (var cz = 0u; cz < 2u; cz = cz + 1u) {
59
+ let wz = select(1.0 - frac.z, frac.z, cz == 1u);
60
+ for (var cy = 0u; cy < 2u; cy = cy + 1u) {
61
+ let wy = select(1.0 - frac.y, frac.y, cy == 1u);
62
+ for (var cx = 0u; cx < 2u; cx = cx + 1u) {
63
+ let wx = select(1.0 - frac.x, frac.x, cx == 1u);
64
+ let at = lx + cx + side * (ly + cy + side * (lz + cz));
65
+ total = total + fields[offset + at] * wx * wy * wz;
66
+ }
67
+ }
68
+ }
69
+ return total;
70
+ }
71
+
72
+ /* How far inside a cascade a point is, measured to the nearest face. Negative means outside. */
73
+ fn insideBy(level: u32, p: vec3<f32>) -> f32 {
74
+ let base = level * ${CASCADE_FLOATS}u;
75
+ let low = vec3<f32>(cascades[base], cascades[base + 1u], cascades[base + 2u]);
76
+ let high = vec3<f32>(cascades[base + 3u], cascades[base + 4u], cascades[base + 5u]);
77
+ let gap = min(p - low, high - p);
78
+ return min(gap.x, min(gap.y, gap.z));
79
+ }
80
+
81
+ /*
82
+ * The field at a world point, from the finest cascade that holds it.
83
+ *
84
+ * Faded into the cascade outside across the outer tenth of the extent, because every boundary here
85
+ * moves with the camera — the cascades are centred on it — and a hard switch at a boundary that
86
+ * moves is a seam sweeping through the picture whenever the camera turns.
87
+ */
88
+ fn sampleField(levels: u32, side: u32, p: vec3<f32>) -> f32 {
89
+ let last = levels - 1u;
90
+ for (var level = 0u; level <= last; level = level + 1u) {
91
+ let inside = insideBy(level, p);
92
+ if (inside <= 0.0) {
93
+ continue;
94
+ }
95
+ let value = cascadeAt(level, side, p);
96
+ if (level == last) {
97
+ return value;
98
+ }
99
+ let base = level * ${CASCADE_FLOATS}u;
100
+ let band = ${GLOBAL_FIELD_BLEND.toFixed(6)} * (cascades[base + 3u] - cascades[base]);
101
+ if (inside >= band) {
102
+ return value;
103
+ }
104
+ let outer = cascadeAt(level + 1u, side, p);
105
+ return mix(outer, value, inside / band);
106
+ }
107
+ return cascadeAt(last, side, p);
108
+ }
109
+ `;
110
+ /** Floats in the sampler parity entry point's params: cascade count, samples a side, point count. */
111
+ export const SAMPLE_PARAM_FLOATS = 4;
112
+ /**
113
+ * A compute entry point that does nothing but sample, so `gi-parity.mjs` can compare the function
114
+ * above against `sampleGlobalField` directly rather than through whatever is using it.
115
+ */
116
+ export const SAMPLE_FIELD_PARITY_WGSL = /* wgsl */ `
117
+ @group(0) @binding(0) var<storage, read> params: array<f32>;
118
+ @group(0) @binding(1) var<storage, read> cascades: array<f32>;
119
+ @group(0) @binding(2) var<storage, read> fields: array<f32>;
120
+ @group(0) @binding(3) var<storage, read> points: array<f32>;
121
+ @group(0) @binding(4) var<storage, read_write> out: array<f32>;
122
+
123
+ ${SAMPLE_FIELD_WGSL}
124
+
125
+ @compute @workgroup_size(64)
126
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
127
+ let count = u32(params[2]);
128
+ if (id.x >= count) {
129
+ return;
130
+ }
131
+ let p = vec3<f32>(points[id.x * 3u], points[id.x * 3u + 1u], points[id.x * 3u + 2u]);
132
+ out[id.x] = sampleField(u32(params[0]), u32(params[1]), p);
133
+ }
134
+ `;
@@ -0,0 +1,26 @@
1
+ export declare const BLEND_RASTER_WGSL: string;
2
+ /**
3
+ * The two targets composited over what the opaque half drew.
4
+ *
5
+ * **`resolveOit` in `render/orderIndependent.ts`, transcribed**, and the division's guard is that
6
+ * file's: a pixel no fragment touched has a weighted alpha of zero, and its revealage of one
7
+ * discards whatever this produced anyway.
8
+ *
9
+ * **Premultiplied, where the forward path's composite is a lerp**, and the difference is what the
10
+ * alpha channel means on each side. `renderer.ts` resolves over a scene target whose alpha is not a
11
+ * coverage mask; this pipeline's colour target has one, because the blit reads it to decide whether
12
+ * the pass drew a pixel at all. Writing `average * (1 - reveal)` with `1 - reveal` in alpha and
13
+ * blending `(one, one-minus-src-alpha)` gives both at once: the colour is the same lerp, and the
14
+ * coverage becomes `1 - (1 - covered) * reveal` — so a pane over empty sky marks the pixel as
15
+ * drawn, which a lerp would not have.
16
+ */
17
+ export declare const BLEND_RESOLVE_WGSL = "\n@group(0) @binding(0) var accumMap: texture_2d<f32>;\n@group(0) @binding(1) var revealMap: texture_2d<f32>;\n\nfn oitWeight(viewDepth: f32, alpha: f32) -> f32 {\n let z = max(viewDepth, 0.0) / 200.0;\n let falloff = 0.03 / (1e-5 + z * z * z * z);\n return alpha * clamp(falloff, 1e-2, 3e3);\n}\n\n/* Premultiplied and weighted, against a blend of \"one, one\": the target holds the two sums. */\nfn oitAccumulate(colour: vec3<f32>, alpha: f32, weight: f32) -> vec4<f32> {\n return vec4<f32>(colour * alpha * weight, alpha * weight);\n}\n\n/*\n * The two targets, as a premultiplied fragment to lay over what is behind them.\n *\n * The weighted sum divided by the weighted alpha is the average colour the layers make, and the\n * revealage says how much of the background still shows. The division is guarded because a pixel no\n * fragment touched has a weighted alpha of zero \u2014 and its revealage of one discards whatever this\n * produces anyway.\n */\nfn oitResolved(accum: vec4<f32>, reveal: f32) -> vec4<f32> {\n let average = accum.xyz / max(accum.w, 1e-5);\n let covered = 1.0 - reveal;\n return vec4<f32>(average * covered, covered);\n}\n\n\nstruct Corner {\n @builtin(position) position: vec4<f32>,\n}\n\n@vertex\nfn resolveVert(@builtin(vertex_index) vertex: u32) -> Corner {\n /* One triangle covering the target, which needs no vertex buffer and no clip correction. */\n var out: Corner;\n let x = f32((vertex << 1u) & 2u) * 2.0 - 1.0;\n let y = f32(vertex & 2u) * 2.0 - 1.0;\n out.position = vec4<f32>(x, y, 0.0, 1.0);\n return out;\n}\n\n@fragment\nfn resolveFrag(in: Corner) -> @location(0) vec4<f32> {\n let at = vec2<u32>(in.position.xy);\n let accum = textureLoad(accumMap, at, 0);\n let reveal = textureLoad(revealMap, at, 0).x;\n return oitResolved(accum, reveal);\n}\n";
18
+ /**
19
+ * The weight and the resolve alone, as a dispatch, so both can be checked against the reference.
20
+ *
21
+ * **What a picture cannot show is whether the weight is the forward path's.** Two panes blended by
22
+ * a weight twice as steep still look like two panes; what it changes is how much a far layer keeps,
23
+ * and nothing about the frame says so. So the arithmetic is lifted out and compared against
24
+ * `oitWeight` and `resolveOit` over generated cases.
25
+ */
26
+ export declare const BLEND_OIT_PARITY_WGSL = "\n/** Six floats a case: depth, alpha, colour, and the background behind it. */\n@group(0) @binding(0) var<storage, read> cases: array<f32>;\n/** Four out: the weight, and the three resolved channels. */\n@group(0) @binding(1) var<storage, read_write> results: array<f32>;\n\nfn oitWeight(viewDepth: f32, alpha: f32) -> f32 {\n let z = max(viewDepth, 0.0) / 200.0;\n let falloff = 0.03 / (1e-5 + z * z * z * z);\n return alpha * clamp(falloff, 1e-2, 3e3);\n}\n\n/* Premultiplied and weighted, against a blend of \"one, one\": the target holds the two sums. */\nfn oitAccumulate(colour: vec3<f32>, alpha: f32, weight: f32) -> vec4<f32> {\n return vec4<f32>(colour * alpha * weight, alpha * weight);\n}\n\n/*\n * The two targets, as a premultiplied fragment to lay over what is behind them.\n *\n * The weighted sum divided by the weighted alpha is the average colour the layers make, and the\n * revealage says how much of the background still shows. The division is guarded because a pixel no\n * fragment touched has a weighted alpha of zero \u2014 and its revealage of one discards whatever this\n * produces anyway.\n */\nfn oitResolved(accum: vec4<f32>, reveal: f32) -> vec4<f32> {\n let average = accum.xyz / max(accum.w, 1e-5);\n let covered = 1.0 - reveal;\n return vec4<f32>(average * covered, covered);\n}\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x * 8u + 7u >= arrayLength(&cases)) { return; }\n let at = id.x * 8u;\n let depth = cases[at];\n let alpha = cases[at + 1u];\n let colour = vec3<f32>(cases[at + 2u], cases[at + 3u], cases[at + 4u]);\n let background = vec3<f32>(cases[at + 5u], cases[at + 6u], cases[at + 7u]);\n\n let weight = oitWeight(depth, alpha);\n /* One fragment folded in, which is the anchor: for a single layer this reduces to \"over\". */\n let accum = oitAccumulate(colour, alpha, weight);\n let reveal = 1.0 - alpha;\n /*\n * The resolve's own fragment, then the blend state applied by hand: the pipeline lays it over\n * the target with \"one, one-minus-src-alpha\", so the background keeps the revealage.\n */\n let over = oitResolved(accum, reveal);\n let resolved = over.xyz + background * (1.0 - over.w);\n\n let out = id.x * 4u;\n results[out] = weight;\n results[out + 1u] = resolved.x;\n results[out + 2u] = resolved.y;\n results[out + 3u] = resolved.z;\n}\n";
@@ -0,0 +1,339 @@
1
+ import { GPU_DRIVEN_VERTEX_FLOATS } from '../../gpudriven/sceneUpload.js';
2
+ import { DECODE_WGSL, decodeResourcesWgsl } from './decode.wgsl.js';
3
+ import { ENVIRONMENT_WGSL } from './environment.wgsl.js';
4
+ import { FOG_WGSL } from './fog.wgsl.js';
5
+ import { FRAME_BLOCK_WGSL } from './frameBlock.wgsl.js';
6
+ import { MATERIAL_SLOTS, MATERIAL_TABLE_WGSL } from './materialTable.wgsl.js';
7
+ import { MESH_TRANSFORM_WGSL } from './meshTransform.wgsl.js';
8
+ import { SHADE_LIGHTING_WGSL } from './shade.wgsl.js';
9
+ /**
10
+ * The transparent half: one indirect draw, blended, shaded in the fragment stage.
11
+ *
12
+ * **A visibility buffer stores one surface a pixel and therefore cannot hold a blend at all.** That
13
+ * is architecture rather than an omission: the whole saving of the opaque path is that shading
14
+ * happens once per pixel, after the frame knows which triangle won, and a pane of glass has no
15
+ * winner. So the transparent set skips the visibility buffer and is drawn again here, shading where
16
+ * it stands, with the divergence that implies — exactly as every fragment on the forward path does.
17
+ *
18
+ * **What it reuses is the pipeline's front half.** `instanceCull` and the cut answer "which
19
+ * clusters, at which level of detail", and that question is the same for a pane as for a wall, so
20
+ * they run a second time over the other half of the scene rather than being written again. The
21
+ * material's blend flag is what the cut filters on; `cull.wgsl.ts` carries the filter.
22
+ *
23
+ * **It includes `SHADE_LIGHTING_WGSL` rather than restating it**, so the expression a pane of glass
24
+ * is lit by is the expression a wall is lit by, character for character. That is why the lighting
25
+ * was packaged as an includable function in the first place, and `scripts/gpu-parity.mjs` holds
26
+ * both to `gpudriven/lit.ts`.
27
+ *
28
+ * **Two targets and two blend states, both of which commute.** The accumulation sums weighted
29
+ * colour and the revealage multiplies what each layer let through, so the frame stops depending on
30
+ * which surface was submitted first. `orderIndependent.ts` is the arithmetic on the CPU and says
31
+ * what it approximates; `flatPass.ts`'s `oitTarget` is the same pair of blend states on the forward
32
+ * path. The weight is `oitWeight`, which both paths share.
33
+ *
34
+ * **Depth attached read-only.** A pane behind a wall is rejected at the wall, and nothing here
35
+ * writes depth, so two panes do not hide each other and there is something left to blend.
36
+ *
37
+ * **A transparent surface is not shadowed here**, which is the one place it is lit differently from
38
+ * an opaque one. `shadowFactor` wants a receiver plane, the shading pass derives one from
39
+ * barycentric gradients because a compute invocation has no neighbours, and a fragment stage would
40
+ * have to derive it another way — a second expression for a term whose whole point is that there is
41
+ * one. `docs/IMPROVEMENTS.md` carries the row. A pane in shadow therefore reads as a pane in
42
+ * sunlight, which is visible on a bright scene and is stated rather than hidden.
43
+ */
44
+ /**
45
+ * The weighted-transparency arithmetic, declared once for the three things that need it.
46
+ *
47
+ * **The blended raster, the resolve and the parity entry point are three readers of one rule**, and
48
+ * the first version of this file gave each of them its own copy. It was caught by the audit the
49
+ * plan asks for rather than by anything failing: perturbing the weight broke the check while the
50
+ * shader that draws the frame went on using the old one, which is the exact shape of a gate that
51
+ * watches a copy of the thing it is guarding.
52
+ *
53
+ * `render/orderIndependent.ts` is the reference all of this is written against — `oitWeight` and
54
+ * `resolveOit`, where both clamps are argued and the algebra is asserted against numbers — and
55
+ * `flat/main.ts` computes the same weight on the forward path.
56
+ */
57
+ const OIT_WGSL = /* wgsl */ `
58
+ fn oitWeight(viewDepth: f32, alpha: f32) -> f32 {
59
+ let z = max(viewDepth, 0.0) / 200.0;
60
+ let falloff = 0.03 / (1e-5 + z * z * z * z);
61
+ return alpha * clamp(falloff, 1e-2, 3e3);
62
+ }
63
+
64
+ /* Premultiplied and weighted, against a blend of "one, one": the target holds the two sums. */
65
+ fn oitAccumulate(colour: vec3<f32>, alpha: f32, weight: f32) -> vec4<f32> {
66
+ return vec4<f32>(colour * alpha * weight, alpha * weight);
67
+ }
68
+
69
+ /*
70
+ * The two targets, as a premultiplied fragment to lay over what is behind them.
71
+ *
72
+ * The weighted sum divided by the weighted alpha is the average colour the layers make, and the
73
+ * revealage says how much of the background still shows. The division is guarded because a pixel no
74
+ * fragment touched has a weighted alpha of zero — and its revealage of one discards whatever this
75
+ * produces anyway.
76
+ */
77
+ fn oitResolved(accum: vec4<f32>, reveal: f32) -> vec4<f32> {
78
+ let average = accum.xyz / max(accum.w, 1e-5);
79
+ let covered = 1.0 - reveal;
80
+ return vec4<f32>(average * covered, covered);
81
+ }
82
+ `;
83
+ export const BLEND_RASTER_WGSL = /* wgsl */ `
84
+ struct BlendVarying {
85
+ @builtin(position) position: vec4<f32>,
86
+ @location(0) world: vec3<f32>,
87
+ @location(1) normal: vec3<f32>,
88
+ @location(2) colour: vec3<f32>,
89
+ @location(3) uv: vec2<f32>,
90
+ @location(4) @interpolate(flat) material: u32,
91
+ /* The vertex's own glow, added to the material's in the fragment as the shading pass adds it. */
92
+ @location(5) emissive: f32,
93
+ }
94
+ ${MATERIAL_TABLE_WGSL}
95
+ ${FRAME_BLOCK_WGSL}
96
+ @group(0) @binding(0) var<storage, read> viewProj: array<f32, 16>;
97
+ /** The compacted list of blended clusters, from the cut's second run. */
98
+ @group(0) @binding(1) var<storage, read> blendList: array<u32>;
99
+ @group(0) @binding(2) var<storage, read> clusterMeta: array<u32>;
100
+ @group(0) @binding(3) var<storage, read> indices: array<u32>;
101
+ @group(0) @binding(4) var<storage, read> vertices: array<f32>;
102
+ @group(0) @binding(5) var<storage, read> transforms: array<f32>;
103
+ @group(0) @binding(6) var<storage, read> materialOf: array<u32>;
104
+ @group(0) @binding(7) var<uniform> materials: array<Material, ${MATERIAL_SLOTS}>;
105
+ @group(0) @binding(8) var<uniform> frame: Frame;
106
+ @group(0) @binding(9) var environmentMap: texture_2d_array<f32>;
107
+ @group(0) @binding(10) var environmentSampler: sampler;
108
+ ${decodeResourcesWgsl({ group: 0, latents: 11, clampSampler: 12, repeatSampler: 13, nodes: 14, weights: 15 })}
109
+ fn decodeLatentSize() -> f32 {
110
+ return frame.size.w;
111
+ }
112
+
113
+ ${DECODE_WGSL}
114
+ /*
115
+ * **Layer 0, which is this pipeline reading one probe of a grid**, and the same fetch
116
+ * shade.wgsl.ts makes for the same reason: what this pipeline owns is a fixed cluster set a few
117
+ * metres across, where a grid would pop as a surface crossed a cell.
118
+ */
119
+ fn environmentSample(uv: vec2<f32>, level: f32) -> vec3<f32> {
120
+ return textureSampleLevel(environmentMap, environmentSampler, uv, 0, level).rgb;
121
+ }
122
+ ${ENVIRONMENT_WGSL}
123
+ ${MESH_TRANSFORM_WGSL}
124
+ ${SHADE_LIGHTING_WGSL}
125
+ ${FOG_WGSL}
126
+ ${OIT_WGSL}
127
+
128
+ const NO_PROGRAM_HERE: u32 = ${0xffffffff}u;
129
+
130
+ /*
131
+ * The same fetch the visibility raster does, keeping the attributes instead of an identifier.
132
+ *
133
+ * **"metaAt", because "meta" is a reserved word in WGSL** — the visibility raster's own comment
134
+ * records that, and this shader indexes the same buffer the same way.
135
+ */
136
+ @vertex
137
+ fn blendVert(
138
+ @builtin(vertex_index) vertex: u32,
139
+ @builtin(instance_index) slot: u32,
140
+ ) -> BlendVarying {
141
+ var out: BlendVarying;
142
+ out.world = vec3<f32>(0.0);
143
+ out.normal = vec3<f32>(0.0, 1.0, 0.0);
144
+ out.colour = vec3<f32>(0.0);
145
+ out.uv = vec2<f32>(0.0);
146
+ out.material = 0u;
147
+ out.emissive = 0.0;
148
+ let cluster = blendList[slot];
149
+ let metaAt = cluster * 4u;
150
+ let indexOffset = clusterMeta[metaAt];
151
+ let indexCount = clusterMeta[metaAt + 1u];
152
+ let mesh = clusterMeta[metaAt + 3u];
153
+
154
+ let triangle = vertex / 3u;
155
+ /* Past this cluster's triangles: collapse to a point, which covers no pixel. */
156
+ if (triangle * 3u >= indexCount) {
157
+ out.position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
158
+ return out;
159
+ }
160
+
161
+ let index = indices[indexOffset + vertex];
162
+ let at = index * ${GPU_DRIVEN_VERTEX_FLOATS}u;
163
+ let local = vec3<f32>(vertices[at], vertices[at + 1u], vertices[at + 2u]);
164
+ let world = worldOf(mesh, local);
165
+
166
+ out.world = world;
167
+ out.normal = rotatedOf(mesh, vec3<f32>(vertices[at + 3u], vertices[at + 4u], vertices[at + 5u]));
168
+ out.colour = vec3<f32>(vertices[at + 6u], vertices[at + 7u], vertices[at + 8u]);
169
+ out.uv = vec2<f32>(vertices[at + 9u], vertices[at + 10u]);
170
+ out.emissive = vertices[at + 11u];
171
+ out.material = materialOf[cluster];
172
+ out.position = vec4<f32>(
173
+ viewProj[0] * world.x + viewProj[4] * world.y + viewProj[8] * world.z + viewProj[12],
174
+ viewProj[1] * world.x + viewProj[5] * world.y + viewProj[9] * world.z + viewProj[13],
175
+ viewProj[2] * world.x + viewProj[6] * world.y + viewProj[10] * world.z + viewProj[14],
176
+ viewProj[3] * world.x + viewProj[7] * world.y + viewProj[11] * world.z + viewProj[15],
177
+ );
178
+ return out;
179
+ }
180
+
181
+ struct BlendTargets {
182
+ @location(0) accum: vec4<f32>,
183
+ @location(1) reveal: vec4<f32>,
184
+ }
185
+
186
+ @fragment
187
+ fn blendFrag(in: BlendVarying) -> BlendTargets {
188
+ var out: BlendTargets;
189
+ let entry = materials[min(in.material, ${MATERIAL_SLOTS - 1}u)];
190
+ let uv = in.uv * entry.maps.xy;
191
+ let lod = 0.0;
192
+
193
+ var baseColour = vec4<f32>(1.0);
194
+ if (entry.programs.x != NO_PROGRAM_HERE) {
195
+ baseColour = decodeProgram(entry.programs.x, uv, lod, frame.size.z);
196
+ }
197
+ /* The material's own opacity times whatever its base-colour map said about this texel. */
198
+ let alpha = clamp(entry.extra.x * baseColour.w, 0.0, 1.0);
199
+ let toEye = normalize(frame.eye.xyz - in.world);
200
+
201
+ var surface: LitSurface;
202
+ surface.albedo = in.colour * entry.tint.xyz * baseColour.xyz;
203
+ surface.normal = normalize(in.normal);
204
+ surface.toEye = toEye;
205
+ surface.roughness = entry.surface.x;
206
+ surface.specular = entry.surface.y;
207
+ surface.emissive = entry.tint.w + in.emissive;
208
+ /* Where the pane glows, as the shading pass decides it: the albedo times the emissive map. */
209
+ var emissiveMapped = vec3<f32>(1.0);
210
+ if (entry.programs.w != NO_PROGRAM_HERE) {
211
+ emissiveMapped = decodeProgram(entry.programs.w, uv, lod, frame.size.z).xyz;
212
+ }
213
+ surface.emissiveColour = surface.albedo * emissiveMapped;
214
+ /* Unshadowed, and the header says why and what it costs. */
215
+ surface.shade = 1.0;
216
+ surface.reflectivity = entry.surface.z;
217
+ surface.metalness = entry.surface.w;
218
+ surface.occlusion = 1.0;
219
+
220
+ var room: LitEnvironment;
221
+ room.lightDir = frame.lightDir.xyz;
222
+ room.lightColour = frame.lightColour.xyz;
223
+ room.sky = frame.sky.xyz;
224
+ room.ground = frame.ground.xyz;
225
+ let probeBound = frame.environment.w;
226
+ let edge = frame.environment.x;
227
+ let maxLod = frame.environment.y;
228
+ room.radiance = probeRadianceAt(reflect(-toEye, surface.normal), edge, clamp(entry.surface.x * maxLod, 0.0, maxLod), maxLod);
229
+ room.radianceAmount = probeBound;
230
+ room.irradiance = probeIrradianceAt(surface.normal, edge, frame.environment.z);
231
+ room.irradianceAmount = frame.environmentMix.x;
232
+ room.prefiltered = probeBound;
233
+ room.emission = frame.environmentMix.y;
234
+
235
+ /*
236
+ * **The haze multiplies the colour and not the alpha.** A pane at the render edge should take the
237
+ * haze's colour, not become more transparent: fogging its alpha would make distant glass vanish
238
+ * where it ought to grey out, and the two read completely differently across a large view
239
+ * distance — which is the whole reason this pipeline gained a fog.
240
+ */
241
+ let toward = distance(in.world, frame.eye.xyz);
242
+ let colour = mix(litColour(surface, room), mediumColour(), mediumFog(toward, in.world.y));
243
+
244
+ /*
245
+ * **The distance to the eye, not the clip-space depth**, because that is what oitWeight's falloff
246
+ * is written in metres against — render/orderIndependent.ts divides by 200 and flat/main.ts takes
247
+ * "distance(vWorldPos, uCameraPos)". A weight fed a 0-to-1 depth instead would put every layer of
248
+ * every scene inside the first hundredth of the falloff.
249
+ */
250
+ let weight = oitWeight(toward, alpha);
251
+ out.accum = oitAccumulate(colour, alpha, weight);
252
+ /* One channel, and it multiplies: what this layer let through. See the blend state. */
253
+ out.reveal = vec4<f32>(alpha, 0.0, 0.0, alpha);
254
+ return out;
255
+ }
256
+ `;
257
+ /**
258
+ * The two targets composited over what the opaque half drew.
259
+ *
260
+ * **`resolveOit` in `render/orderIndependent.ts`, transcribed**, and the division's guard is that
261
+ * file's: a pixel no fragment touched has a weighted alpha of zero, and its revealage of one
262
+ * discards whatever this produced anyway.
263
+ *
264
+ * **Premultiplied, where the forward path's composite is a lerp**, and the difference is what the
265
+ * alpha channel means on each side. `renderer.ts` resolves over a scene target whose alpha is not a
266
+ * coverage mask; this pipeline's colour target has one, because the blit reads it to decide whether
267
+ * the pass drew a pixel at all. Writing `average * (1 - reveal)` with `1 - reveal` in alpha and
268
+ * blending `(one, one-minus-src-alpha)` gives both at once: the colour is the same lerp, and the
269
+ * coverage becomes `1 - (1 - covered) * reveal` — so a pane over empty sky marks the pixel as
270
+ * drawn, which a lerp would not have.
271
+ */
272
+ export const BLEND_RESOLVE_WGSL = /* wgsl */ `
273
+ @group(0) @binding(0) var accumMap: texture_2d<f32>;
274
+ @group(0) @binding(1) var revealMap: texture_2d<f32>;
275
+ ${OIT_WGSL}
276
+
277
+ struct Corner {
278
+ @builtin(position) position: vec4<f32>,
279
+ }
280
+
281
+ @vertex
282
+ fn resolveVert(@builtin(vertex_index) vertex: u32) -> Corner {
283
+ /* One triangle covering the target, which needs no vertex buffer and no clip correction. */
284
+ var out: Corner;
285
+ let x = f32((vertex << 1u) & 2u) * 2.0 - 1.0;
286
+ let y = f32(vertex & 2u) * 2.0 - 1.0;
287
+ out.position = vec4<f32>(x, y, 0.0, 1.0);
288
+ return out;
289
+ }
290
+
291
+ @fragment
292
+ fn resolveFrag(in: Corner) -> @location(0) vec4<f32> {
293
+ let at = vec2<u32>(in.position.xy);
294
+ let accum = textureLoad(accumMap, at, 0);
295
+ let reveal = textureLoad(revealMap, at, 0).x;
296
+ return oitResolved(accum, reveal);
297
+ }
298
+ `;
299
+ /**
300
+ * The weight and the resolve alone, as a dispatch, so both can be checked against the reference.
301
+ *
302
+ * **What a picture cannot show is whether the weight is the forward path's.** Two panes blended by
303
+ * a weight twice as steep still look like two panes; what it changes is how much a far layer keeps,
304
+ * and nothing about the frame says so. So the arithmetic is lifted out and compared against
305
+ * `oitWeight` and `resolveOit` over generated cases.
306
+ */
307
+ export const BLEND_OIT_PARITY_WGSL = /* wgsl */ `
308
+ /** Six floats a case: depth, alpha, colour, and the background behind it. */
309
+ @group(0) @binding(0) var<storage, read> cases: array<f32>;
310
+ /** Four out: the weight, and the three resolved channels. */
311
+ @group(0) @binding(1) var<storage, read_write> results: array<f32>;
312
+ ${OIT_WGSL}
313
+ @compute @workgroup_size(64)
314
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
315
+ if (id.x * 8u + 7u >= arrayLength(&cases)) { return; }
316
+ let at = id.x * 8u;
317
+ let depth = cases[at];
318
+ let alpha = cases[at + 1u];
319
+ let colour = vec3<f32>(cases[at + 2u], cases[at + 3u], cases[at + 4u]);
320
+ let background = vec3<f32>(cases[at + 5u], cases[at + 6u], cases[at + 7u]);
321
+
322
+ let weight = oitWeight(depth, alpha);
323
+ /* One fragment folded in, which is the anchor: for a single layer this reduces to "over". */
324
+ let accum = oitAccumulate(colour, alpha, weight);
325
+ let reveal = 1.0 - alpha;
326
+ /*
327
+ * The resolve's own fragment, then the blend state applied by hand: the pipeline lays it over
328
+ * the target with "one, one-minus-src-alpha", so the background keeps the revealage.
329
+ */
330
+ let over = oitResolved(accum, reveal);
331
+ let resolved = over.xyz + background * (1.0 - over.w);
332
+
333
+ let out = id.x * 4u;
334
+ results[out] = weight;
335
+ results[out + 1u] = resolved.x;
336
+ results[out + 2u] = resolved.y;
337
+ results[out + 3u] = resolved.z;
338
+ }
339
+ `;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Three flag buffers into one list, and the block that draws it.
3
+ *
4
+ * **Hand-written WGSL for the reason `cull.wgsl.ts` gives**: there is no GLSL original, because
5
+ * WebGL2 has neither a compute stage nor an indirect draw. What keeps it honest is
6
+ * `scripts/gpu-parity.mjs`, which runs it against `gpudriven/compact.ts` over generated flags.
7
+ *
8
+ * **The atomic is the whole of it and it is on the draw block rather than on a counter beside
9
+ * it.** `instanceCount` is the number of clusters this phase draws; incrementing it *is* claiming
10
+ * a slot in the list, so a separate counter would be the same number in two places and the block
11
+ * would be written by whoever remembered. The other three words never change — the vertex count is
12
+ * a cluster's index cap and the two firsts are zero — so a caller writes them once at creation and
13
+ * clears the one word a frame.
14
+ *
15
+ * **Which phase is a setting rather than two shaders.** The two differ only in which side of the
16
+ * history they take, and a second entry point would be a second place for the cut and the cull to
17
+ * be applied — which is the half that must be identical in both. See `compact.ts` for the
18
+ * invariant this is obeying: the history decides which phase a cluster is judged in and never
19
+ * whether it is judged.
20
+ */
21
+ /**
22
+ * One invocation a cluster.
23
+ *
24
+ * `settings` is the phase — 0 for the half the history holds, 1 for the rest — and the cluster
25
+ * count. `drawn` is next frame's history and **only phase two writes it**, for every cluster it
26
+ * keeps whichever half judged it, because only phase two's cull has seen this frame's pyramid; see
27
+ * `compactPhase` in `compact.ts`. A caller clears it once a frame.
28
+ */
29
+ export declare const COMPACT_CLUSTERS_WGSL = "\n/** phase, clusterCount. */\n@group(0) @binding(0) var<storage, read> settings: array<u32, 2>;\n@group(0) @binding(1) var<storage, read> selected: array<u32>;\n@group(0) @binding(2) var<storage, read> keep: array<u32>;\n@group(0) @binding(3) var<storage, read> history: array<u32>;\n@group(0) @binding(4) var<storage, read_write> list: array<u32>;\n@group(0) @binding(5) var<storage, read_write> drawn: array<u32>;\n/** A four-word non-indexed draw block. Only word one is touched here; see the header. */\n@group(0) @binding(6) var<storage, read_write> args: array<atomic<u32>>;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= settings[1]) { return; }\n\n let seen = history[id.x] != 0u;\n let phaseOne = settings[0] == 0u;\n let visible = selected[id.x] != 0u && keep[id.x] != 0u;\n /* Next frame's history is phase two's answer, for the clusters phase one drew as well. */\n if (!phaseOne && visible) { drawn[id.x] = 1u; }\n\n /* The history decides which half judges this cluster, never whether one does. */\n if (seen != phaseOne || !visible) { return; }\n\n /* Claiming the slot and counting the instance are one operation, which is why the atomic is on\n the draw block. The order slots are handed out in is the scheduler's and is not an answer. */\n let slot = atomicAdd(&args[1], 1u);\n list[slot] = id.x;\n}\n";
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Three flag buffers into one list, and the block that draws it.
3
+ *
4
+ * **Hand-written WGSL for the reason `cull.wgsl.ts` gives**: there is no GLSL original, because
5
+ * WebGL2 has neither a compute stage nor an indirect draw. What keeps it honest is
6
+ * `scripts/gpu-parity.mjs`, which runs it against `gpudriven/compact.ts` over generated flags.
7
+ *
8
+ * **The atomic is the whole of it and it is on the draw block rather than on a counter beside
9
+ * it.** `instanceCount` is the number of clusters this phase draws; incrementing it *is* claiming
10
+ * a slot in the list, so a separate counter would be the same number in two places and the block
11
+ * would be written by whoever remembered. The other three words never change — the vertex count is
12
+ * a cluster's index cap and the two firsts are zero — so a caller writes them once at creation and
13
+ * clears the one word a frame.
14
+ *
15
+ * **Which phase is a setting rather than two shaders.** The two differ only in which side of the
16
+ * history they take, and a second entry point would be a second place for the cut and the cull to
17
+ * be applied — which is the half that must be identical in both. See `compact.ts` for the
18
+ * invariant this is obeying: the history decides which phase a cluster is judged in and never
19
+ * whether it is judged.
20
+ */
21
+ /**
22
+ * One invocation a cluster.
23
+ *
24
+ * `settings` is the phase — 0 for the half the history holds, 1 for the rest — and the cluster
25
+ * count. `drawn` is next frame's history and **only phase two writes it**, for every cluster it
26
+ * keeps whichever half judged it, because only phase two's cull has seen this frame's pyramid; see
27
+ * `compactPhase` in `compact.ts`. A caller clears it once a frame.
28
+ */
29
+ export const COMPACT_CLUSTERS_WGSL = `
30
+ /** phase, clusterCount. */
31
+ @group(0) @binding(0) var<storage, read> settings: array<u32, 2>;
32
+ @group(0) @binding(1) var<storage, read> selected: array<u32>;
33
+ @group(0) @binding(2) var<storage, read> keep: array<u32>;
34
+ @group(0) @binding(3) var<storage, read> history: array<u32>;
35
+ @group(0) @binding(4) var<storage, read_write> list: array<u32>;
36
+ @group(0) @binding(5) var<storage, read_write> drawn: array<u32>;
37
+ /** A four-word non-indexed draw block. Only word one is touched here; see the header. */
38
+ @group(0) @binding(6) var<storage, read_write> args: array<atomic<u32>>;
39
+
40
+ @compute @workgroup_size(64)
41
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
42
+ if (id.x >= settings[1]) { return; }
43
+
44
+ let seen = history[id.x] != 0u;
45
+ let phaseOne = settings[0] == 0u;
46
+ let visible = selected[id.x] != 0u && keep[id.x] != 0u;
47
+ /* Next frame's history is phase two's answer, for the clusters phase one drew as well. */
48
+ if (!phaseOne && visible) { drawn[id.x] = 1u; }
49
+
50
+ /* The history decides which half judges this cluster, never whether one does. */
51
+ if (seen != phaseOne || !visible) { return; }
52
+
53
+ /* Claiming the slot and counting the instance are one operation, which is why the atomic is on
54
+ the draw block. The order slots are handed out in is the scheduler's and is not an answer. */
55
+ let slot = atomicAdd(&args[1], 1u);
56
+ list[slot] = id.x;
57
+ }
58
+ `;