@driftengine/core 3.62.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.json +122 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +70 -4
  9. package/dist/index.js +123 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +47 -14
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +128 -80
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +261 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +158 -88
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
@@ -0,0 +1,349 @@
1
+ /** A DriftTexture decode program, interpreted on the device. */
2
+ /**
3
+ * **`@driftengine/texture`'s `decodeCpu`, as WGSL — the half that package's README has listed as
4
+ * missing since Wave 2B.** The reference is the specification and this is checked against it by
5
+ * `scripts/gpu-parity.mjs`, over generated programs and a real texture array.
6
+ *
7
+ * **Core does not import that package**, so its vocabulary is written again here and
8
+ * `decode.wgsl.test.ts` asserts the two are equal.
9
+ *
10
+ * **The tables are uniforms because the shading pass is at eight storage buffers**, which is
11
+ * WebGPU's default per stage. A uniform binding is at most 65,536 bytes, so the tables have fixed
12
+ * capacities, and `decodeTables.ts` refuses a set that does not fit rather than truncating it.
13
+ *
14
+ * **Its networks are evaluated by `../network.wgsl.ts`**, the one evaluator the device has, which
15
+ * reads their weights through `decodeNetWeight` here. Until 2026-09-17 this module carried its own
16
+ * copy of the loop.
17
+ */
18
+ import { networkWgsl } from '../network.wgsl.js';
19
+ export const DECODE_REGISTERS = 16;
20
+ export const DECODE_OPS = {
21
+ SAMPLE_LATENT: 0,
22
+ EVAL_NETWORK: 1,
23
+ SAMPLE_BLOCK: 2,
24
+ PROCEDURAL_FBM: 3,
25
+ FLIPBOOK_INDEX: 4,
26
+ LATENT_LERP: 5,
27
+ REMAP_CHANNEL: 6,
28
+ COMPOSITE: 7,
29
+ CONSTANT: 8,
30
+ };
31
+ /** Lattice clamp, lattice wrap, centre clamp, centre wrap. `ADDRESS_MODE` in the reference. */
32
+ export const DECODE_ADDRESS_MODES = 4;
33
+ /** The order `REMAP_CHANNEL` packs a semantic in, which is `REMAP_SEMANTICS`'. */
34
+ export const DECODE_SEMANTICS = [
35
+ 'albedo-srgb',
36
+ 'albedo-linear',
37
+ 'normal-tangent-yup',
38
+ 'normal-tangent-ydown',
39
+ 'roughness-linear',
40
+ 'gloss-linear',
41
+ 'metallic-linear',
42
+ 'occlusion-linear',
43
+ 'height-linear',
44
+ 'emissive-srgb',
45
+ 'mask-linear',
46
+ ];
47
+ /**
48
+ * **Four in and four out are not a choice**: `EVAL_NETWORK` reads one four-lane register and
49
+ * writes one. The hidden limit holds a small refinement network, which the bake does not make yet.
50
+ */
51
+ export const DECODE_NETWORK_INPUTS = 4;
52
+ export const DECODE_NETWORK_OUTPUTS = 4;
53
+ export const DECODE_NETWORK_HIDDEN_LAYERS = 2;
54
+ export const DECODE_NETWORK_WIDTH = 16;
55
+ /** `PROCEDURAL_FBM`'s octave count is a loop bound on the device, so it has one. */
56
+ export const DECODE_FBM_OCTAVES = 16;
57
+ /** Sixty-four material slots, three programs each. */
58
+ export const DECODE_PROGRAM_CAPACITY = 192;
59
+ export const DECODE_NETWORK_CAPACITY = 256;
60
+ export const DECODE_NODE_CAPACITY = 3200;
61
+ /** Blocks of four floats, so fifteen thousand three hundred and sixty weights. */
62
+ export const DECODE_WEIGHT_BLOCKS = 3840;
63
+ export const DECODE_CONSTANT_CAPACITY = 256;
64
+ export const DECODE_NODES_BYTES = (DECODE_PROGRAM_CAPACITY + DECODE_NETWORK_CAPACITY * 2 + DECODE_NODE_CAPACITY) * 16;
65
+ export const DECODE_WEIGHTS_BYTES = (DECODE_WEIGHT_BLOCKS + DECODE_CONSTANT_CAPACITY) * 16;
66
+ /** What a material's program lane holds when it has no program there. */
67
+ export const DECODE_NO_PROGRAM = 0xffffffff;
68
+ /**
69
+ * The declarations, at the bindings the including module chooses.
70
+ *
71
+ * The including module also defines `fn decodeLatentSize() -> f32`, the edge of level 0 in texels,
72
+ * because where that number lives is the including module's business.
73
+ */
74
+ export function decodeResourcesWgsl(b) {
75
+ return /* wgsl */ `
76
+ struct DecodeNodes {
77
+ programs: array<vec4<u32>, ${DECODE_PROGRAM_CAPACITY}>,
78
+ networks: array<vec4<u32>, ${DECODE_NETWORK_CAPACITY * 2}>,
79
+ nodes: array<vec4<u32>, ${DECODE_NODE_CAPACITY}>,
80
+ }
81
+
82
+ struct DecodeWeights {
83
+ values: array<vec4<f32>, ${DECODE_WEIGHT_BLOCKS}>,
84
+ constants: array<vec4<f32>, ${DECODE_CONSTANT_CAPACITY}>,
85
+ }
86
+
87
+ @group(${b.group}) @binding(${b.nodes}) var<uniform> decodeNodes: DecodeNodes;
88
+ @group(${b.group}) @binding(${b.weights}) var<uniform> decodeWeights: DecodeWeights;
89
+ @group(${b.group}) @binding(${b.latents}) var decodeLatents: texture_2d_array<f32>;
90
+ @group(${b.group}) @binding(${b.clampSampler}) var decodeClampSampler: sampler;
91
+ @group(${b.group}) @binding(${b.repeatSampler}) var decodeRepeatSampler: sampler;
92
+ `;
93
+ }
94
+ /**
95
+ * The interpreter. Every function is the reference's, operation for operation; where a line looks
96
+ * odd, `decodeCpu.ts` is why.
97
+ *
98
+ * - **Registers are a fresh function-scope array**, zeros, one per call. The reference reuses the
99
+ * caller's registers, so its parity check hands it fresh ones per case.
100
+ * - **`PROCEDURAL_FBM`'s hash is `u32` arithmetic with wrapping multiplies**, which is exactly what
101
+ * `Math.imul` computes; only its final division is float, so that operation is compared at a
102
+ * stated tolerance.
103
+ * - **Component-wise aliasing is safe**: the reference reads component `c` of a source before it
104
+ * writes component `c` of a destination, so reading whole vectors first gives the same answer
105
+ * when a node writes the register it reads.
106
+ * - **Every index into a table is clamped**, because a uniform array read past its end is clamped
107
+ * by the language anyway and saying so is cheaper than finding out.
108
+ */
109
+ export const DECODE_WGSL = /* wgsl */ `
110
+ fn decodeWeight(index: u32) -> f32 {
111
+ let block = min(index / 4u, ${DECODE_WEIGHT_BLOCKS - 1}u);
112
+ return decodeWeights.values[block][index % 4u];
113
+ }
114
+
115
+ /* The exact sRGB curve, as semantics.ts has it, not the 2.2 power. */
116
+ fn decodeSrgbToLinear(value: f32) -> f32 {
117
+ if (value <= 0.04045) {
118
+ return value / 12.92;
119
+ }
120
+ return pow((value + 0.055) / 1.055, 2.4);
121
+ }
122
+
123
+ fn decodeHash(x: i32, y: i32, seed: u32) -> f32 {
124
+ var h = (bitcast<u32>(x) * 0x27d4eb2du) ^ (bitcast<u32>(y) * 0x165667b1u) ^ (seed * 0x9e3779b1u);
125
+ h = (h ^ (h >> 15u)) * 0x2c1b3c6du;
126
+ h = (h ^ (h >> 12u)) * 0x297a2d39u;
127
+ return f32(h ^ (h >> 15u)) / 4294967295.0;
128
+ }
129
+
130
+ fn decodeValueNoise(u: f32, v: f32, seed: u32) -> f32 {
131
+ let x0 = floor(u);
132
+ let y0 = floor(v);
133
+ let fx = u - x0;
134
+ let fy = v - y0;
135
+ let sx = fx * fx * (3.0 - 2.0 * fx);
136
+ let sy = fy * fy * (3.0 - 2.0 * fy);
137
+ let ix = i32(x0);
138
+ let iy = i32(y0);
139
+ let a = decodeHash(ix, iy, seed);
140
+ let b = decodeHash(ix + 1, iy, seed);
141
+ let c = decodeHash(ix, iy + 1, seed);
142
+ let d = decodeHash(ix + 1, iy + 1, seed);
143
+ return (a * (1.0 - sx) + b * sx) * (1.0 - sy) + (c * (1.0 - sx) + d * sx) * sy;
144
+ }
145
+
146
+ fn decodeFbm(uv: vec2<f32>, seed: u32, octaves: u32) -> f32 {
147
+ var total = 0.0;
148
+ var amplitude = 0.5;
149
+ var frequency = 1.0;
150
+ let count = clamp(octaves, 1u, ${DECODE_FBM_OCTAVES}u);
151
+ for (var o = 0u; o < count; o = o + 1u) {
152
+ total = total + decodeValueNoise(uv.x * frequency * 8.0, uv.y * frequency * 8.0, seed) * amplitude;
153
+ amplitude = amplitude * 0.5;
154
+ frequency = frequency * 2.0;
155
+ }
156
+ return total;
157
+ }
158
+
159
+ /*
160
+ * timeNodes.ts flipbookFrame with loop true: a modulo that stays non-negative, in integers.
161
+ *
162
+ * Not raw - count * floor(raw / count). On the device that wrote this check, a float 144 / 12 came
163
+ * back 11.999999 and floored to 11, so every frame index that was an exact multiple of the frame
164
+ * count read as the count itself. Integer remainder is what the reference computes, and it has
165
+ * no rounding to disagree about.
166
+ */
167
+ fn decodeFlipbook(t: f32, frames: u32, fps: u32) -> f32 {
168
+ if (frames == 0u) {
169
+ return 0.0;
170
+ }
171
+ let raw = i32(floor(t * f32(fps)));
172
+ let count = i32(min(frames, 2147483647u));
173
+ return f32(((raw % count) + count) % count);
174
+ }
175
+
176
+ fn decodeRemap(value: vec4<f32>, packed: u32) -> vec4<f32> {
177
+ var dst = value;
178
+ let semantic = packed >> 4u;
179
+ let component = packed & 15u;
180
+ if (component >= 4u) {
181
+ return dst;
182
+ }
183
+ let raw = value[component];
184
+ var mapped = raw;
185
+ /* albedo-srgb and emissive-srgb decode; normal-tangent-ydown and gloss-linear flip. */
186
+ if (semantic == 0u || semantic == 9u) {
187
+ mapped = decodeSrgbToLinear(raw);
188
+ } else if (semantic == 3u || semantic == 5u) {
189
+ mapped = 1.0 - raw;
190
+ }
191
+ dst[component] = mapped;
192
+ return dst;
193
+ }
194
+
195
+ /* The networks are the engine's one evaluator, reading its weights out of the uniform table. */
196
+ fn decodeNetWeight(index: u32) -> f32 {
197
+ return decodeWeight(index);
198
+ }
199
+ ${networkWgsl({
200
+ name: 'decodeNet',
201
+ scalar: 'f32',
202
+ width: DECODE_NETWORK_WIDTH,
203
+ hidden: DECODE_NETWORK_HIDDEN_LAYERS,
204
+ })}
205
+ fn decodeNetwork(network: u32, input: vec4<f32>) -> vec4<f32> {
206
+ let slot = min(network, ${DECODE_NETWORK_CAPACITY - 1}u) * 2u;
207
+ let header = decodeNodes.networks[slot];
208
+ let widths = decodeNodes.networks[slot + 1u];
209
+ var values: array<f32, ${DECODE_NETWORK_WIDTH}>;
210
+ for (var i = 0u; i < 4u; i = i + 1u) {
211
+ values[i] = input[i];
212
+ }
213
+ let result = decodeNetEval(
214
+ header.x,
215
+ min(header.y, ${DECODE_NETWORK_INPUTS}u),
216
+ header.w,
217
+ array<u32, ${DECODE_NETWORK_HIDDEN_LAYERS}>(widths.x, widths.y),
218
+ min(header.z, ${DECODE_NETWORK_OUTPUTS}u),
219
+ values,
220
+ );
221
+ return vec4<f32>(result[0], result[1], result[2], result[3]);
222
+ }
223
+
224
+ /* One whole level of a lattice-addressed latent: bilinear by hand, exactly as decodeCpu does it. */
225
+ fn decodeLatticeLevel(layer: u32, uv: vec2<f32>, level: i32, mode: u32) -> vec4<f32> {
226
+ let size = max(i32(decodeLatentSize()) >> u32(level), 1);
227
+ let edge = f32(size);
228
+ var a = clamp(uv, vec2<f32>(0.0), vec2<f32>(1.0));
229
+ if (mode == 1u) {
230
+ a = uv - floor(uv);
231
+ }
232
+ let s = a * (edge - 1.0);
233
+ let base = floor(s);
234
+ let f = s - base;
235
+ let x0 = clamp(i32(base.x), 0, size - 1);
236
+ let x1 = clamp(i32(base.x) + 1, 0, size - 1);
237
+ let y0 = clamp(i32(base.y), 0, size - 1);
238
+ let y1 = clamp(i32(base.y) + 1, 0, size - 1);
239
+ let index = i32(layer);
240
+ let t00 = textureLoad(decodeLatents, vec2<i32>(x0, y0), index, level);
241
+ let t10 = textureLoad(decodeLatents, vec2<i32>(x1, y0), index, level);
242
+ let t01 = textureLoad(decodeLatents, vec2<i32>(x0, y1), index, level);
243
+ let t11 = textureLoad(decodeLatents, vec2<i32>(x1, y1), index, level);
244
+ let top = t00 * (1.0 - f.x) + t10 * f.x;
245
+ let bottom = t01 * (1.0 - f.x) + t11 * f.x;
246
+ return top * (1.0 - f.y) + bottom * f.y;
247
+ }
248
+
249
+ fn decodeLatentSample(layer: u32, uv: vec2<f32>, lod: f32, mode: u32) -> vec4<f32> {
250
+ let top = f32(textureNumLevels(decodeLatents)) - 1.0;
251
+ /* Negative and not-a-number read level 0, as the reference's lod > 0 test does. */
252
+ let level = min(select(0.0, lod, lod > 0.0), top);
253
+ if (mode == 3u) {
254
+ return textureSampleLevel(decodeLatents, decodeRepeatSampler, uv, layer, level);
255
+ }
256
+ if (mode == 2u) {
257
+ return textureSampleLevel(decodeLatents, decodeClampSampler, uv, layer, level);
258
+ }
259
+ let lower = floor(level);
260
+ let upper = min(lower + 1.0, top);
261
+ let blend = level - lower;
262
+ let near = decodeLatticeLevel(layer, uv, i32(lower), mode);
263
+ let far = decodeLatticeLevel(layer, uv, i32(upper), mode);
264
+ return near * (1.0 - blend) + far * blend;
265
+ }
266
+
267
+ fn decodeProgram(program: u32, uv: vec2<f32>, lod: f32, t: f32) -> vec4<f32> {
268
+ var r: array<vec4<f32>, ${DECODE_REGISTERS}>;
269
+ if (program >= ${DECODE_PROGRAM_CAPACITY}u) {
270
+ return r[0];
271
+ }
272
+ let header = decodeNodes.programs[program];
273
+ let first = header.x;
274
+ let count = min(header.y, ${DECODE_NODE_CAPACITY}u);
275
+ let mode = header.w;
276
+ for (var i = 0u; i < count; i = i + 1u) {
277
+ let node = decodeNodes.nodes[min(first + i, ${DECODE_NODE_CAPACITY - 1}u)];
278
+ let a = node.y;
279
+ let b = node.z;
280
+ let dst = min(node.w, ${DECODE_REGISTERS - 1}u);
281
+ let ra = r[min(a, ${DECODE_REGISTERS - 1}u)];
282
+ let rb = r[min(b, ${DECODE_REGISTERS - 1}u)];
283
+ switch (node.x) {
284
+ case 0u, 2u: {
285
+ r[dst] = decodeLatentSample(a, uv, lod, mode);
286
+ }
287
+ case 1u: {
288
+ r[dst] = decodeNetwork(b, ra);
289
+ }
290
+ case 3u: {
291
+ let n = decodeFbm(uv, a, b);
292
+ r[dst] = vec4<f32>(n, n, n, 1.0);
293
+ }
294
+ case 4u: {
295
+ r[dst] = vec4<f32>(decodeFlipbook(t, a, b), 0.0, 0.0, 1.0);
296
+ }
297
+ case 5u: {
298
+ let m = t - floor(t);
299
+ r[dst] = ra * (1.0 - m) + rb * m;
300
+ }
301
+ case 6u: {
302
+ r[dst] = decodeRemap(ra, b);
303
+ }
304
+ case 7u: {
305
+ let alpha = ra.w;
306
+ r[dst] = vec4<f32>(ra.xyz * alpha + rb.xyz * (1.0 - alpha), alpha + rb.w * (1.0 - alpha));
307
+ }
308
+ case 8u: {
309
+ r[dst] = decodeWeights.constants[min(a, ${DECODE_CONSTANT_CAPACITY - 1}u)];
310
+ }
311
+ default: {
312
+ }
313
+ }
314
+ }
315
+ return r[min(header.z, ${DECODE_REGISTERS - 1}u)];
316
+ }
317
+ `;
318
+ /** Floats a case of the device check: program, u, v, lod, t. */
319
+ export const DECODE_CASE_FLOATS = 5;
320
+ export const DECODE_PARITY_WGSL = /* wgsl */ `
321
+ @group(0) @binding(0) var<storage, read> params: array<f32>;
322
+ @group(0) @binding(1) var<storage, read> cases: array<f32>;
323
+ @group(0) @binding(7) var<storage, read_write> results: array<f32>;
324
+ ${decodeResourcesWgsl({ group: 0, nodes: 2, weights: 3, latents: 4, clampSampler: 5, repeatSampler: 6 })}
325
+
326
+ fn decodeLatentSize() -> f32 {
327
+ return params[1];
328
+ }
329
+
330
+ ${DECODE_WGSL}
331
+
332
+ @compute @workgroup_size(64)
333
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
334
+ if (id.x >= u32(params[0])) {
335
+ return;
336
+ }
337
+ let at = id.x * ${DECODE_CASE_FLOATS}u;
338
+ let value = decodeProgram(
339
+ u32(cases[at]),
340
+ vec2<f32>(cases[at + 1u], cases[at + 2u]),
341
+ cases[at + 3u],
342
+ cases[at + 4u],
343
+ );
344
+ results[id.x * 4u] = value.x;
345
+ results[id.x * 4u + 1u] = value.y;
346
+ results[id.x * 4u + 2u] = value.z;
347
+ results[id.x * 4u + 3u] = value.w;
348
+ }
349
+ `;
@@ -0,0 +1,44 @@
1
+ /** Reading a baked probe from a compute stage: the mapping, the levels, and the two lookups. */
2
+ /**
3
+ * **`flat/probeGrid.ts` for one probe, and the "one" is the whole of the difference.**
4
+ *
5
+ * That file blends eight probes for the diffuse term and two for the reflection, because a grid is
6
+ * what a large world wants and a single probe pops as a surface crosses a cell boundary. The
7
+ * second pipeline reads **layer 0** and says so: it owns a fixed cluster set and its rigs are a few
8
+ * metres across, which is one probe's worth of world. A pass handed a grid of more than one reads
9
+ * the first of them, which is wrong in the way a single probe is always wrong — a smooth surface
10
+ * crossing a cell shows the wrong room rather than a blend of two — and `PassEnvironment.layers`
11
+ * is what a caller checks before deciding this pipeline suits their scene.
12
+ *
13
+ * **The mapping is not written again.** `shaders/octahedral.ts` carries `octEncode` and `octInsetUv`
14
+ * in GLSL and in TypeScript with a test, and this is the third spelling rather than a second
15
+ * design — `gpu-parity.mjs` runs it against that TypeScript over generated directions, which is
16
+ * the only thing that keeps three copies of one mapping honest.
17
+ *
18
+ * **The texture read belongs to the including module**, as `shadow.wgsl.ts` does the same thing for
19
+ * the same reason: the parity entry point reads a storage buffer, because that is what a compute
20
+ * harness can be handed, and the shading pass reads a `texture_2d_array`. What must not differ is
21
+ * the arithmetic around the read, so the arithmetic is here and the read is not.
22
+ */
23
+ export declare const ENVIRONMENT_WGSL = "\n/* shaders/octahedral.ts, transcribed. The direction need not be normalised: the L1 divide is. */\nfn octEncode(d: vec3<f32>) -> vec2<f32> {\n let n = d / (abs(d.x) + abs(d.y) + abs(d.z));\n var p = n.xy;\n if (n.z < 0.0) {\n p = vec2<f32>(\n (1.0 - abs(n.y)) * select(-1.0, 1.0, n.x >= 0.0),\n (1.0 - abs(n.x)) * select(-1.0, 1.0, n.y >= 0.0),\n );\n }\n return p * 0.5 + 0.5;\n}\n\n/*\n * Where a direction lands in a map that is edge texels across and whose outer ring is a gutter.\n *\n * The inner region is edge - 2 texels offset by one, so a direction at the border of the\n * octahedral square lands on the last real texel and a bilinear tap past it reads the gutter \u2014\n * which the bake filled with the folded direction rather than the other side of the map.\n */\nfn octInsetUv(d: vec3<f32>, edge: f32) -> vec2<f32> {\n return (octEncode(d) * (edge - 2.0) + 1.0) / edge;\n}\n\n/*\n * A level's own edge, which is not the map's.\n *\n * The gutter is one texel at every level, so its share of the map doubles as the chain coarsens\n * and the inset has to be computed per level. Floored at four, which is where the chain stops.\n */\nfn probeLevelEdge(edge: f32, level: f32) -> f32 {\n return max(edge / exp2(level), 4.0);\n}\n\n/* The diffuse half: the cosine convolution, which lives at one fixed level of the chain. */\nfn probeIrradianceAt(dir: vec3<f32>, edge: f32, irradianceLevel: f32) -> vec3<f32> {\n let uv = octInsetUv(dir, probeLevelEdge(edge, irradianceLevel));\n return environmentSample(uv, irradianceLevel);\n}\n\n/*\n * The reflection at a roughness, as two levels mixed by hand.\n *\n * **By hand, and flat/probeGrid.ts records why**: a level's inset differs between levels, and\n * hardware trilinear applies one coordinate to both \u2014 so one of the two would be read at the wrong\n * inset, by a thirty-second of the map at the top of the chain, which is a visible slide in a\n * reflection as roughness rises. Two fetches at their own insets is the price of the fold.\n */\nfn probeRadianceAt(dir: vec3<f32>, edge: f32, lod: f32, maxLod: f32) -> vec3<f32> {\n let lo = clamp(floor(lod), 0.0, maxLod);\n let hi = min(lo + 1.0, maxLod);\n let coarse = environmentSample(octInsetUv(dir, probeLevelEdge(edge, hi)), hi);\n let fine = environmentSample(octInsetUv(dir, probeLevelEdge(edge, lo)), lo);\n return mix(fine, coarse, clamp(lod - lo, 0.0, 1.0));\n}\n";
24
+ /** Floats a case of the environment check: a direction, then the edge, the lod and the max. */
25
+ export declare const ENVIRONMENT_CASE_FLOATS = 6;
26
+ /**
27
+ * Texels a side of the synthetic chain the check reads, and how many levels it holds.
28
+ *
29
+ * **A chain whose levels are constants, one value each**, so what comes back names the level it
30
+ * was read from. That is what makes the two-level mix checkable without a texture: a result of
31
+ * 3.25 is level 3 and level 4 mixed a quarter of the way, and no bilinear filter can produce it by
32
+ * accident.
33
+ */
34
+ export declare const ENVIRONMENT_CHECK_EDGE = 16;
35
+ export declare const ENVIRONMENT_CHECK_LEVELS = 4;
36
+ /**
37
+ * The mapping and the level arithmetic alone, as a dispatch.
38
+ *
39
+ * `environmentSample` reads a flat array of levels rather than a texture, and returns the level's
40
+ * own constant — so the uv it is handed is reported back as well, which is what lets one check
41
+ * cover both halves: that a direction lands where `octahedral.ts` says, and that a roughness
42
+ * reads the two levels either side of it in the right proportion.
43
+ */
44
+ export declare const ENVIRONMENT_PARITY_WGSL = "\n@group(0) @binding(0) var<storage, read> params: array<f32>;\n@group(0) @binding(1) var<storage, read> cases: array<f32>;\n@group(0) @binding(2) var<storage, read> levels: array<f32>;\n@group(0) @binding(3) var<storage, read_write> results: array<f32>;\n\n/* The level's own constant, and the coordinate it was asked for, so both can be compared. */\nvar<private> lastUv: vec2<f32>;\n\nfn environmentSample(uv: vec2<f32>, level: f32) -> vec3<f32> {\n lastUv = uv;\n let index = u32(clamp(level, 0.0, 4.0 - 1.0));\n return vec3<f32>(levels[index], levels[index], levels[index]);\n}\n\n\n/* shaders/octahedral.ts, transcribed. The direction need not be normalised: the L1 divide is. */\nfn octEncode(d: vec3<f32>) -> vec2<f32> {\n let n = d / (abs(d.x) + abs(d.y) + abs(d.z));\n var p = n.xy;\n if (n.z < 0.0) {\n p = vec2<f32>(\n (1.0 - abs(n.y)) * select(-1.0, 1.0, n.x >= 0.0),\n (1.0 - abs(n.x)) * select(-1.0, 1.0, n.y >= 0.0),\n );\n }\n return p * 0.5 + 0.5;\n}\n\n/*\n * Where a direction lands in a map that is edge texels across and whose outer ring is a gutter.\n *\n * The inner region is edge - 2 texels offset by one, so a direction at the border of the\n * octahedral square lands on the last real texel and a bilinear tap past it reads the gutter \u2014\n * which the bake filled with the folded direction rather than the other side of the map.\n */\nfn octInsetUv(d: vec3<f32>, edge: f32) -> vec2<f32> {\n return (octEncode(d) * (edge - 2.0) + 1.0) / edge;\n}\n\n/*\n * A level's own edge, which is not the map's.\n *\n * The gutter is one texel at every level, so its share of the map doubles as the chain coarsens\n * and the inset has to be computed per level. Floored at four, which is where the chain stops.\n */\nfn probeLevelEdge(edge: f32, level: f32) -> f32 {\n return max(edge / exp2(level), 4.0);\n}\n\n/* The diffuse half: the cosine convolution, which lives at one fixed level of the chain. */\nfn probeIrradianceAt(dir: vec3<f32>, edge: f32, irradianceLevel: f32) -> vec3<f32> {\n let uv = octInsetUv(dir, probeLevelEdge(edge, irradianceLevel));\n return environmentSample(uv, irradianceLevel);\n}\n\n/*\n * The reflection at a roughness, as two levels mixed by hand.\n *\n * **By hand, and flat/probeGrid.ts records why**: a level's inset differs between levels, and\n * hardware trilinear applies one coordinate to both \u2014 so one of the two would be read at the wrong\n * inset, by a thirty-second of the map at the top of the chain, which is a visible slide in a\n * reflection as roughness rises. Two fetches at their own insets is the price of the fold.\n */\nfn probeRadianceAt(dir: vec3<f32>, edge: f32, lod: f32, maxLod: f32) -> vec3<f32> {\n let lo = clamp(floor(lod), 0.0, maxLod);\n let hi = min(lo + 1.0, maxLod);\n let coarse = environmentSample(octInsetUv(dir, probeLevelEdge(edge, hi)), hi);\n let fine = environmentSample(octInsetUv(dir, probeLevelEdge(edge, lo)), lo);\n return mix(fine, coarse, clamp(lod - lo, 0.0, 1.0));\n}\n\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= u32(params[0])) {\n return;\n }\n let at = id.x * 6u;\n let dir = vec3<f32>(cases[at], cases[at + 1u], cases[at + 2u]);\n let edge = cases[at + 3u];\n let lod = cases[at + 4u];\n let maxLod = cases[at + 5u];\n\n let uv = octInsetUv(dir, edge);\n let radiance = probeRadianceAt(dir, edge, lod, maxLod);\n let out = id.x * 4u;\n results[out] = uv.x;\n results[out + 1u] = uv.y;\n results[out + 2u] = radiance.x;\n /* The last coordinate the mix asked for, which is the finer level's and carries its own inset. */\n results[out + 3u] = lastUv.x;\n}\n";
@@ -0,0 +1,137 @@
1
+ /** Reading a baked probe from a compute stage: the mapping, the levels, and the two lookups. */
2
+ /**
3
+ * **`flat/probeGrid.ts` for one probe, and the "one" is the whole of the difference.**
4
+ *
5
+ * That file blends eight probes for the diffuse term and two for the reflection, because a grid is
6
+ * what a large world wants and a single probe pops as a surface crosses a cell boundary. The
7
+ * second pipeline reads **layer 0** and says so: it owns a fixed cluster set and its rigs are a few
8
+ * metres across, which is one probe's worth of world. A pass handed a grid of more than one reads
9
+ * the first of them, which is wrong in the way a single probe is always wrong — a smooth surface
10
+ * crossing a cell shows the wrong room rather than a blend of two — and `PassEnvironment.layers`
11
+ * is what a caller checks before deciding this pipeline suits their scene.
12
+ *
13
+ * **The mapping is not written again.** `shaders/octahedral.ts` carries `octEncode` and `octInsetUv`
14
+ * in GLSL and in TypeScript with a test, and this is the third spelling rather than a second
15
+ * design — `gpu-parity.mjs` runs it against that TypeScript over generated directions, which is
16
+ * the only thing that keeps three copies of one mapping honest.
17
+ *
18
+ * **The texture read belongs to the including module**, as `shadow.wgsl.ts` does the same thing for
19
+ * the same reason: the parity entry point reads a storage buffer, because that is what a compute
20
+ * harness can be handed, and the shading pass reads a `texture_2d_array`. What must not differ is
21
+ * the arithmetic around the read, so the arithmetic is here and the read is not.
22
+ */
23
+ export const ENVIRONMENT_WGSL = /* wgsl */ `
24
+ /* shaders/octahedral.ts, transcribed. The direction need not be normalised: the L1 divide is. */
25
+ fn octEncode(d: vec3<f32>) -> vec2<f32> {
26
+ let n = d / (abs(d.x) + abs(d.y) + abs(d.z));
27
+ var p = n.xy;
28
+ if (n.z < 0.0) {
29
+ p = vec2<f32>(
30
+ (1.0 - abs(n.y)) * select(-1.0, 1.0, n.x >= 0.0),
31
+ (1.0 - abs(n.x)) * select(-1.0, 1.0, n.y >= 0.0),
32
+ );
33
+ }
34
+ return p * 0.5 + 0.5;
35
+ }
36
+
37
+ /*
38
+ * Where a direction lands in a map that is edge texels across and whose outer ring is a gutter.
39
+ *
40
+ * The inner region is edge - 2 texels offset by one, so a direction at the border of the
41
+ * octahedral square lands on the last real texel and a bilinear tap past it reads the gutter —
42
+ * which the bake filled with the folded direction rather than the other side of the map.
43
+ */
44
+ fn octInsetUv(d: vec3<f32>, edge: f32) -> vec2<f32> {
45
+ return (octEncode(d) * (edge - 2.0) + 1.0) / edge;
46
+ }
47
+
48
+ /*
49
+ * A level's own edge, which is not the map's.
50
+ *
51
+ * The gutter is one texel at every level, so its share of the map doubles as the chain coarsens
52
+ * and the inset has to be computed per level. Floored at four, which is where the chain stops.
53
+ */
54
+ fn probeLevelEdge(edge: f32, level: f32) -> f32 {
55
+ return max(edge / exp2(level), 4.0);
56
+ }
57
+
58
+ /* The diffuse half: the cosine convolution, which lives at one fixed level of the chain. */
59
+ fn probeIrradianceAt(dir: vec3<f32>, edge: f32, irradianceLevel: f32) -> vec3<f32> {
60
+ let uv = octInsetUv(dir, probeLevelEdge(edge, irradianceLevel));
61
+ return environmentSample(uv, irradianceLevel);
62
+ }
63
+
64
+ /*
65
+ * The reflection at a roughness, as two levels mixed by hand.
66
+ *
67
+ * **By hand, and flat/probeGrid.ts records why**: a level's inset differs between levels, and
68
+ * hardware trilinear applies one coordinate to both — so one of the two would be read at the wrong
69
+ * inset, by a thirty-second of the map at the top of the chain, which is a visible slide in a
70
+ * reflection as roughness rises. Two fetches at their own insets is the price of the fold.
71
+ */
72
+ fn probeRadianceAt(dir: vec3<f32>, edge: f32, lod: f32, maxLod: f32) -> vec3<f32> {
73
+ let lo = clamp(floor(lod), 0.0, maxLod);
74
+ let hi = min(lo + 1.0, maxLod);
75
+ let coarse = environmentSample(octInsetUv(dir, probeLevelEdge(edge, hi)), hi);
76
+ let fine = environmentSample(octInsetUv(dir, probeLevelEdge(edge, lo)), lo);
77
+ return mix(fine, coarse, clamp(lod - lo, 0.0, 1.0));
78
+ }
79
+ `;
80
+ /** Floats a case of the environment check: a direction, then the edge, the lod and the max. */
81
+ export const ENVIRONMENT_CASE_FLOATS = 6;
82
+ /**
83
+ * Texels a side of the synthetic chain the check reads, and how many levels it holds.
84
+ *
85
+ * **A chain whose levels are constants, one value each**, so what comes back names the level it
86
+ * was read from. That is what makes the two-level mix checkable without a texture: a result of
87
+ * 3.25 is level 3 and level 4 mixed a quarter of the way, and no bilinear filter can produce it by
88
+ * accident.
89
+ */
90
+ export const ENVIRONMENT_CHECK_EDGE = 16;
91
+ export const ENVIRONMENT_CHECK_LEVELS = 4;
92
+ /**
93
+ * The mapping and the level arithmetic alone, as a dispatch.
94
+ *
95
+ * `environmentSample` reads a flat array of levels rather than a texture, and returns the level's
96
+ * own constant — so the uv it is handed is reported back as well, which is what lets one check
97
+ * cover both halves: that a direction lands where `octahedral.ts` says, and that a roughness
98
+ * reads the two levels either side of it in the right proportion.
99
+ */
100
+ export const ENVIRONMENT_PARITY_WGSL = /* wgsl */ `
101
+ @group(0) @binding(0) var<storage, read> params: array<f32>;
102
+ @group(0) @binding(1) var<storage, read> cases: array<f32>;
103
+ @group(0) @binding(2) var<storage, read> levels: array<f32>;
104
+ @group(0) @binding(3) var<storage, read_write> results: array<f32>;
105
+
106
+ /* The level's own constant, and the coordinate it was asked for, so both can be compared. */
107
+ var<private> lastUv: vec2<f32>;
108
+
109
+ fn environmentSample(uv: vec2<f32>, level: f32) -> vec3<f32> {
110
+ lastUv = uv;
111
+ let index = u32(clamp(level, 0.0, ${ENVIRONMENT_CHECK_LEVELS}.0 - 1.0));
112
+ return vec3<f32>(levels[index], levels[index], levels[index]);
113
+ }
114
+
115
+ ${ENVIRONMENT_WGSL}
116
+
117
+ @compute @workgroup_size(64)
118
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
119
+ if (id.x >= u32(params[0])) {
120
+ return;
121
+ }
122
+ let at = id.x * ${ENVIRONMENT_CASE_FLOATS}u;
123
+ let dir = vec3<f32>(cases[at], cases[at + 1u], cases[at + 2u]);
124
+ let edge = cases[at + 3u];
125
+ let lod = cases[at + 4u];
126
+ let maxLod = cases[at + 5u];
127
+
128
+ let uv = octInsetUv(dir, edge);
129
+ let radiance = probeRadianceAt(dir, edge, lod, maxLod);
130
+ let out = id.x * 4u;
131
+ results[out] = uv.x;
132
+ results[out + 1u] = uv.y;
133
+ results[out + 2u] = radiance.x;
134
+ /* The last coordinate the mix asked for, which is the finer level's and carries its own inset. */
135
+ results[out + 3u] = lastUv.x;
136
+ }
137
+ `;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The medium between the camera and a GPU-driven surface, as WGSL.
3
+ *
4
+ * **`render/fog.ts` is the reference and this is checked against it**, over generated cases on a
5
+ * device by `scripts/gpu-parity.mjs` — the pattern `gpudriven/lit.ts` and `gpudriven/alphaTest.ts`
6
+ * are both written under, and for the reason this file exists at all: the forward path's fog is
7
+ * GLSL, so the two pipelines cannot share text and can only be held to one another by measurement.
8
+ *
9
+ * **Two shaders include this, and neither restates it.** The shading pass fogs an opaque surface
10
+ * and the blended raster fogs a pane, and a pane at the render edge that did not take the haze
11
+ * would stand out of it. The previous wave found the weighted-transparency weight written twice
12
+ * after its gate had been watching the copy, so this one starts shared.
13
+ *
14
+ * The including module declares the `fog` block — `frameBlock.wgsl.ts` does, so both readers get
15
+ * one layout — and every guard and constant below is `render/fog.ts`'s, which is `shaders/fog.ts`'s.
16
+ */
17
+ export declare const FOG_WGSL = "\n/** What distance converges toward: the day's haze, or the water column. */\nfn mediumColour() -> vec3<f32> {\n return mix(frame.fogColour.xyz, frame.fogUnderwater.xyz, frame.fogUnderwater.w);\n}\n\n/**\n * How much of a surface at \"pointY\", \"dist\" metres away, is lost to the medium between.\n * 0 is perfectly clear, 1 is the medium's own colour.\n */\nfn mediumFog(dist: f32, pointY: f32) -> f32 {\n let underwaterFactor = frame.fogUnderwater.w;\n if (frame.fogRange.z == 1.0) {\n /*\n * A consumer asking for a hard cut writes far == near, and the guard is why that is a cut and\n * not a NaN at the one distance where the quotient is zero over zero.\n *\n * **This copy of it is not observable on the machine this was written on**, and the perturbation\n * says so: removing it changes nothing the parity check can see, because WGSL does not require\n * NaN to be supported and this device answers 0 for 0.0/0.0 rather than NaN. The guard stays\n * because render/fog.ts needs it \u2014 its own test fails without it \u2014 and because a device that\n * does propagate NaN would paint the fog colour along the cut. The alpha test found the same\n * shape from the other direction on 2026-09-18: a device may assume NaN never arrives.\n */\n let span = max(frame.fogRange.y - frame.fogRange.x, 1e-4);\n let ramp = clamp((dist - frame.fogRange.x) / span, 0.0, 1.0);\n if (underwaterFactor <= 0.0) { return ramp; }\n let wetLinear = frame.fogRange.w * dist;\n return mix(ramp, 1.0 - exp2(-wetLinear * wetLinear * 1.442695), underwaterFactor);\n }\n\n let t = (pointY - frame.fogColour.w) * frame.fogHeight.x;\n /* (1 - e^-t)/t, which tends to 1 as the ray levels out. Guarded rather than branched: at t = 0\n the quotient is 0/0, and 1e-4 of a scale height is 8 mm. */\n var denom = t;\n if (abs(t) < 1e-4) { denom = 1e-4; }\n let air = 1.0 - exp(-frame.fogHeight.y * dist * (1.0 - exp(-denom)) / denom);\n if (underwaterFactor <= 0.0) { return air; }\n /* Water is its own medium and keeps its own curve, so crossing the surface changes nothing\n about how the water column reads. */\n let wet = frame.fogRange.w * dist;\n return mix(air, 1.0 - exp2(-wet * wet * 1.442695), underwaterFactor);\n}\n";
18
+ /** Floats a case of the fog check: distance, height, and the ten options. */
19
+ export declare const FOG_CASE_FLOATS = 16;
20
+ /**
21
+ * An entry point that does nothing but fog, so `gpu-parity.mjs` can compare the expression above
22
+ * with `render/fog.ts` directly rather than through a shading pass that writes to a texture.
23
+ *
24
+ * **It declares its own `frame` and fills it from the case**, rather than restating the arithmetic:
25
+ * the whole point is that the function under test is the one the frame draws with, and a second
26
+ * copy here is a gate watching a copy of what it guards.
27
+ */
28
+ export declare const FOG_PARITY_WGSL = "\nstruct Frame {\n /** rgb is the haze's colour, w is the eye's height. */\n fogColour: vec4<f32>,\n /** rgb is the water's colour, w is how far under it the camera is. */\n fogUnderwater: vec4<f32>,\n /** Reciprocal scale height, extinction per metre at the eye, and two spare. */\n fogHeight: vec4<f32>,\n /** Linear near and far, the mode, and the water's own density. */\n fogRange: vec4<f32>,\n}\n\n@group(0) @binding(0) var<storage, read> cases: array<f32>;\n@group(0) @binding(1) var<storage, read_write> results: array<f32>;\n\nvar<private> frame: Frame;\n\n/** What distance converges toward: the day's haze, or the water column. */\nfn mediumColour() -> vec3<f32> {\n return mix(frame.fogColour.xyz, frame.fogUnderwater.xyz, frame.fogUnderwater.w);\n}\n\n/**\n * How much of a surface at \"pointY\", \"dist\" metres away, is lost to the medium between.\n * 0 is perfectly clear, 1 is the medium's own colour.\n */\nfn mediumFog(dist: f32, pointY: f32) -> f32 {\n let underwaterFactor = frame.fogUnderwater.w;\n if (frame.fogRange.z == 1.0) {\n /*\n * A consumer asking for a hard cut writes far == near, and the guard is why that is a cut and\n * not a NaN at the one distance where the quotient is zero over zero.\n *\n * **This copy of it is not observable on the machine this was written on**, and the perturbation\n * says so: removing it changes nothing the parity check can see, because WGSL does not require\n * NaN to be supported and this device answers 0 for 0.0/0.0 rather than NaN. The guard stays\n * because render/fog.ts needs it \u2014 its own test fails without it \u2014 and because a device that\n * does propagate NaN would paint the fog colour along the cut. The alpha test found the same\n * shape from the other direction on 2026-09-18: a device may assume NaN never arrives.\n */\n let span = max(frame.fogRange.y - frame.fogRange.x, 1e-4);\n let ramp = clamp((dist - frame.fogRange.x) / span, 0.0, 1.0);\n if (underwaterFactor <= 0.0) { return ramp; }\n let wetLinear = frame.fogRange.w * dist;\n return mix(ramp, 1.0 - exp2(-wetLinear * wetLinear * 1.442695), underwaterFactor);\n }\n\n let t = (pointY - frame.fogColour.w) * frame.fogHeight.x;\n /* (1 - e^-t)/t, which tends to 1 as the ray levels out. Guarded rather than branched: at t = 0\n the quotient is 0/0, and 1e-4 of a scale height is 8 mm. */\n var denom = t;\n if (abs(t) < 1e-4) { denom = 1e-4; }\n let air = 1.0 - exp(-frame.fogHeight.y * dist * (1.0 - exp(-denom)) / denom);\n if (underwaterFactor <= 0.0) { return air; }\n /* Water is its own medium and keeps its own curve, so crossing the surface changes nothing\n about how the water column reads. */\n let wet = frame.fogRange.w * dist;\n return mix(air, 1.0 - exp2(-wet * wet * 1.442695), underwaterFactor);\n}\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n let at = id.x * 16u;\n if (at + 15u >= arrayLength(&cases)) { return; }\n let dist = cases[at];\n let pointY = cases[at + 1u];\n frame.fogColour = vec4<f32>(cases[at + 2u], cases[at + 3u], cases[at + 4u], cases[at + 5u]);\n frame.fogUnderwater = vec4<f32>(cases[at + 6u], cases[at + 7u], cases[at + 8u], cases[at + 9u]);\n frame.fogHeight = vec4<f32>(cases[at + 10u], cases[at + 11u], 0.0, 0.0);\n frame.fogRange = vec4<f32>(cases[at + 12u], cases[at + 13u], cases[at + 14u], cases[at + 15u]);\n\n let out = id.x * 4u;\n results[out] = mediumFog(dist, pointY);\n let colour = mediumColour();\n results[out + 1u] = colour.x;\n results[out + 2u] = colour.y;\n results[out + 3u] = colour.z;\n}\n";