@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,44 @@
1
+ /** The directional shadow lookup for the device, as the text both consumers include. */
2
+ /**
3
+ * **`gpudriven/shadow.ts` for the device, term for term, with `gpu-parity.mjs` running both.**
4
+ *
5
+ * The lesson is a week old and this is the first term written with it in hand: a shading term that
6
+ * exists only as a shader is a term nothing can disagree with. The lit expression had no reference
7
+ * until the highlight went in; this one had a reference before it had a shader.
8
+ *
9
+ * **The map is read through a function the *including* module defines**, `shadowDepthAt(u, v)`.
10
+ * The parity entry point reads a storage buffer, because that is what `gpuCompute.mjs` can hand a
11
+ * check; the shading pass reads a depth texture. What must not differ between them is the
12
+ * arithmetic around the read, so the arithmetic is here and the read is not.
13
+ *
14
+ * Every constant is interpolated from the reference rather than restated. A texel scale that is
15
+ * 1.35 here and 1.3 there is a shadow with a different softness on each path, and nothing on screen
16
+ * says which one is the engine's.
17
+ */
18
+ export declare const SHADE_SHADOW_WGSL: string;
19
+ /** Floats the parity entry point's settings block holds. Matches `ShadowSettings`. */
20
+ export declare const SHADOW_SETTINGS_FLOATS = 12;
21
+ /** Floats a receiver: the light-space clip position, the depth gradient and `n · l`. */
22
+ export declare const SHADOW_RECEIVER_FLOATS = 8;
23
+ /**
24
+ * An entry point that does nothing but look a shadow up, so `gpu-parity.mjs` can compare the
25
+ * function above with `gpudriven/shadow.ts` directly.
26
+ *
27
+ * It defines `shadowDepthAt` over a storage buffer, which is what a compute harness can be handed;
28
+ * the shading pass will define it over a depth texture. Nearest, which is what the comparison
29
+ * assumes — the arithmetic above measures its compensation to the texel centre.
30
+ */
31
+ export declare const SHADOW_PARITY_WGSL: string;
32
+ /** Floats a case of the receiver-plane check: three corners and the two weight gradients. */
33
+ export declare const RECEIVER_PLANE_FLOATS = 15;
34
+ /**
35
+ * The gradient assembly alone, as a dispatch, because it is the one piece of the shadow the
36
+ * shading pass computes for itself.
37
+ *
38
+ * The eleventh check hands the solve two derivative vectors. This is the step *before* that one:
39
+ * turning three light-space corners and two barycentric gradients into those vectors, which is
40
+ * where a factor of two or a transposed weight lives. Both are invisible in a picture — a gradient
41
+ * off by two compensates a tap by half of what it should, which is acne on grazing surfaces and
42
+ * looks like a bias that wants tuning.
43
+ */
44
+ export declare const RECEIVER_PLANE_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_write> planes: array<f32>;\n\nfn receiverPlaneDepthGradient(dx: vec3<f32>, dy: vec3<f32>) -> vec2<f32> {\n let determinant = dx.x * dy.y - dx.y * dy.x;\n let conditioning = length(dx.xy) * length(dy.xy);\n if (abs(determinant) < 1e-4 * conditioning) {\n return vec2<f32>(0.0, 0.0);\n }\n return vec2<f32>(\n (dx.z * dy.y - dx.y * dy.z) / determinant,\n (dx.x * dy.z - dx.z * dy.x) / determinant,\n );\n}\n\nfn receiverPlaneFromWeights(\n l0: vec3<f32>,\n l1: vec3<f32>,\n l2: vec3<f32>,\n gx: vec3<f32>,\n gy: vec3<f32>,\n) -> vec2<f32> {\n let dx = vec3<f32>(\n (gx.x * l0.x + gx.y * l1.x + gx.z * l2.x) * 0.5,\n (gx.x * l0.y + gx.y * l1.y + gx.z * l2.y) * 0.5,\n gx.x * l0.z + gx.y * l1.z + gx.z * l2.z,\n );\n let dy = vec3<f32>(\n (gy.x * l0.x + gy.y * l1.x + gy.z * l2.x) * 0.5,\n (gy.x * l0.y + gy.y * l1.y + gy.z * l2.y) * 0.5,\n gy.x * l0.z + gy.y * l1.z + gy.z * l2.z,\n );\n return receiverPlaneDepthGradient(dx, dy);\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 * 15u;\n let plane = receiverPlaneFromWeights(\n vec3<f32>(cases[at], cases[at + 1u], cases[at + 2u]),\n vec3<f32>(cases[at + 3u], cases[at + 4u], cases[at + 5u]),\n vec3<f32>(cases[at + 6u], cases[at + 7u], cases[at + 8u]),\n vec3<f32>(cases[at + 9u], cases[at + 10u], cases[at + 11u]),\n vec3<f32>(cases[at + 12u], cases[at + 13u], cases[at + 14u]),\n );\n planes[id.x * 2u] = plane.x;\n planes[id.x * 2u + 1u] = plane.y;\n}\n";
@@ -0,0 +1,308 @@
1
+ /** The directional shadow lookup for the device, as the text both consumers include. */
2
+ import { SHADOW_DEPTH_BIAS, SHADOW_DEPTH_FADE, SHADOW_PCF_OFFSETS, SHADOW_SLOPE_TEXELS, SHADOW_TERMINATOR_BAND, SHADOW_TEXEL_SCALE, } from '../../gpudriven/shadow.js';
3
+ import { DIRECTIONAL_SHADOW_FADE_START, POINT_SHADOW_FADE_START } from '../../renderQuality.js';
4
+ /**
5
+ * **`gpudriven/shadow.ts` for the device, term for term, with `gpu-parity.mjs` running both.**
6
+ *
7
+ * The lesson is a week old and this is the first term written with it in hand: a shading term that
8
+ * exists only as a shader is a term nothing can disagree with. The lit expression had no reference
9
+ * until the highlight went in; this one had a reference before it had a shader.
10
+ *
11
+ * **The map is read through a function the *including* module defines**, `shadowDepthAt(u, v)`.
12
+ * The parity entry point reads a storage buffer, because that is what `gpuCompute.mjs` can hand a
13
+ * check; the shading pass reads a depth texture. What must not differ between them is the
14
+ * arithmetic around the read, so the arithmetic is here and the read is not.
15
+ *
16
+ * Every constant is interpolated from the reference rather than restated. A texel scale that is
17
+ * 1.35 here and 1.3 there is a shadow with a different softness on each path, and nothing on screen
18
+ * says which one is the engine's.
19
+ */
20
+ export const SHADE_SHADOW_WGSL = /* wgsl */ `
21
+ struct ShadowSettings {
22
+ strength: f32,
23
+ mapSize: f32,
24
+ depthSpan: f32,
25
+ maxDistance: f32,
26
+ maxSlope: f32,
27
+ taps: f32,
28
+ padA: f32,
29
+ padB: f32,
30
+ lightDir: vec4<f32>,
31
+ }
32
+
33
+ const SHADOW_TAPS: array<vec2<f32>, ${SHADOW_PCF_OFFSETS.length}> = array<vec2<f32>, ${SHADOW_PCF_OFFSETS.length}>(
34
+ ${SHADOW_PCF_OFFSETS.map(([x, y]) => ` vec2<f32>(${x.toFixed(3)}, ${y.toFixed(3)})`).join(',\n')}
35
+ );
36
+
37
+ /*
38
+ * The receiver plane, from the two derivatives of the light-space position.
39
+ *
40
+ * Takes them rather than computing them: a fragment shader has dFdx and dFdy and a compute
41
+ * invocation has neither, so the forward path would pass what the hardware gave it and the second
42
+ * pipeline passes what it derives from the barycentric gradients it already has. Refused where the
43
+ * two-by-two system is ill-conditioned — see gpudriven/shadow.ts, which carries the argument.
44
+ */
45
+ fn receiverPlaneDepthGradient(dx: vec3<f32>, dy: vec3<f32>) -> vec2<f32> {
46
+ let determinant = dx.x * dy.y - dx.y * dy.x;
47
+ let conditioning = length(dx.xy) * length(dy.xy);
48
+ if (abs(determinant) < 1e-4 * conditioning) {
49
+ return vec2<f32>(0.0, 0.0);
50
+ }
51
+ return vec2<f32>(
52
+ (dx.z * dy.y - dx.y * dy.z) / determinant,
53
+ (dx.x * dy.z - dx.z * dy.x) / determinant,
54
+ );
55
+ }
56
+
57
+ /*
58
+ * The receiver plane from the barycentric gradients the shading pass already has.
59
+ *
60
+ * gpudriven/shadow.ts is the twin and carries the argument: a light-space position is a linear
61
+ * combination of the three corners, so its derivative is the same combination of the weight
62
+ * gradients. Exact because a directional light's projection is orthographic.
63
+ *
64
+ * The halving on x and y is the remap into the map's 0-to-1 coordinates. Depth is not halved,
65
+ * because this pipeline's light matrix already emits z in 0..1.
66
+ */
67
+ fn receiverPlaneFromWeights(
68
+ l0: vec3<f32>,
69
+ l1: vec3<f32>,
70
+ l2: vec3<f32>,
71
+ gx: vec3<f32>,
72
+ gy: vec3<f32>,
73
+ ) -> vec2<f32> {
74
+ let dx = vec3<f32>(
75
+ (gx.x * l0.x + gx.y * l1.x + gx.z * l2.x) * 0.5,
76
+ (gx.x * l0.y + gx.y * l1.y + gx.z * l2.y) * 0.5,
77
+ gx.x * l0.z + gx.y * l1.z + gx.z * l2.z,
78
+ );
79
+ let dy = vec3<f32>(
80
+ (gy.x * l0.x + gy.y * l1.x + gy.z * l2.x) * 0.5,
81
+ (gy.x * l0.y + gy.y * l1.y + gy.z * l2.y) * 0.5,
82
+ gy.x * l0.z + gy.y * l1.z + gy.z * l2.z,
83
+ );
84
+ return receiverPlaneDepthGradient(dx, dy);
85
+ }
86
+
87
+ /* How much of a shadow survives having been cast this far along the ground. */
88
+ fn shadowReach(distance: f32, maxDistance: f32) -> f32 {
89
+ return 1.0 - smoothstep(maxDistance * ${POINT_SHADOW_FADE_START.toFixed(2)}, maxDistance, distance);
90
+ }
91
+
92
+ fn directionalVisibility(
93
+ receiverDepth: f32,
94
+ compareDepth: f32,
95
+ storedDepth: f32,
96
+ settings: ShadowSettings,
97
+ ) -> f32 {
98
+ if (compareDepth <= storedDepth) {
99
+ return 1.0;
100
+ }
101
+ let rayDistance = max(receiverDepth - storedDepth, 0.0) * settings.depthSpan;
102
+ let ground = rayDistance * length(settings.lightDir.xz);
103
+ return 1.0 - shadowReach(ground, settings.maxDistance);
104
+ }
105
+
106
+ /*
107
+ * How much light reaches a receiver: 0 in full shadow, 1 in full light.
108
+ *
109
+ * Four early-outs, and the reference records that every one is a cost rather than a correctness
110
+ * guard — the result is a mix by the product of all four fades, so a fade at zero returns one
111
+ * whether or not the return fires. They stay because each skips twelve reads. The bounds check has
112
+ * a second job: without it the taps read outside the map.
113
+ */
114
+ fn shadowFactor(
115
+ lightPos: vec4<f32>,
116
+ depthGradient: vec2<f32>,
117
+ ndl: f32,
118
+ settings: ShadowSettings,
119
+ ) -> f32 {
120
+ if (settings.strength <= 0.0) {
121
+ return 1.0;
122
+ }
123
+ if (lightPos.w == 0.0) {
124
+ return 1.0;
125
+ }
126
+ /*
127
+ * x and y are remapped and z is not: WebGPU clips z to 0..1 already, where the GLSL this is
128
+ * transcribed from clips it to -1..1. See gpudriven/shadow.ts, which carries the argument.
129
+ */
130
+ let ndc = lightPos.xyz / lightPos.w;
131
+ let p = vec3<f32>(ndc.x * 0.5 + 0.5, ndc.y * 0.5 + 0.5, ndc.z);
132
+
133
+ let receiverFade = smoothstep(${SHADOW_TERMINATOR_BAND[0].toFixed(2)}, ${SHADOW_TERMINATOR_BAND[1].toFixed(2)}, ndl);
134
+ if (receiverFade <= 0.0) {
135
+ return 1.0;
136
+ }
137
+ if (p.z > 1.0 || p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0) {
138
+ return 1.0;
139
+ }
140
+
141
+ let fromCentre = max(abs(p.x - 0.5) * 2.0, abs(p.y - 0.5) * 2.0);
142
+ let edgeFade = (1.0 - smoothstep(0.72, 0.98, fromCentre)) * (1.0 - smoothstep(${SHADOW_DEPTH_FADE[0].toFixed(2)}, ${SHADOW_DEPTH_FADE[1].toFixed(2)}, p.z));
143
+ if (edgeFade <= 0.0) {
144
+ return 1.0;
145
+ }
146
+
147
+ let slope = length(settings.lightDir.xz) / max(settings.lightDir.y, 0.001);
148
+ let lowElevationFade =
149
+ 1.0 - smoothstep(settings.maxSlope * ${DIRECTIONAL_SHADOW_FADE_START.toFixed(2)}, settings.maxSlope, slope);
150
+ if (lowElevationFade <= 0.0) {
151
+ return 1.0;
152
+ }
153
+
154
+ let texel = ${SHADOW_TEXEL_SCALE.toFixed(2)} / settings.mapSize;
155
+ let slopeLimit = ${SHADOW_SLOPE_TEXELS.toFixed(1)} / settings.mapSize;
156
+ let taps = u32(clamp(settings.taps, 1.0, ${SHADOW_PCF_OFFSETS.length}.0));
157
+
158
+ var lit = 0.0;
159
+ for (var tap = 0u; tap < taps; tap = tap + 1u) {
160
+ let offset = SHADOW_TAPS[tap] * texel;
161
+ let u = p.x + offset.x;
162
+ let v = p.y + offset.y;
163
+ /*
164
+ * The fetched depth belongs to the centre of the containing texel, not to the continuous
165
+ * coordinate — so the plane compensation is measured to that centre. Following the continuous
166
+ * one leaves half a texel of uncompensated slope, which is the repeating ribs a grazing face
167
+ * shows.
168
+ */
169
+ let centreU = (floor(u * settings.mapSize) + 0.5) / settings.mapSize;
170
+ let centreV = (floor(v * settings.mapSize) + 0.5) / settings.mapSize;
171
+ let raw = depthGradient.x * (centreU - p.x) + depthGradient.y * (centreV - p.y);
172
+ let slopeOffset = clamp(raw, -slopeLimit, slopeLimit);
173
+ let receiverDepth = p.z + slopeOffset;
174
+ let compare = receiverDepth - ${SHADOW_DEPTH_BIAS.toFixed(2)} / settings.depthSpan;
175
+ lit = lit + directionalVisibility(receiverDepth, compare, shadowDepthAt(u, v), settings);
176
+ }
177
+ lit = lit / f32(taps);
178
+
179
+ let amount = settings.strength * edgeFade * lowElevationFade * receiverFade;
180
+ return 1.0 + (lit - 1.0) * amount;
181
+ }
182
+ `;
183
+ /** Floats the parity entry point's settings block holds. Matches `ShadowSettings`. */
184
+ export const SHADOW_SETTINGS_FLOATS = 12;
185
+ /** Floats a receiver: the light-space clip position, the depth gradient and `n · l`. */
186
+ export const SHADOW_RECEIVER_FLOATS = 8;
187
+ /**
188
+ * An entry point that does nothing but look a shadow up, so `gpu-parity.mjs` can compare the
189
+ * function above with `gpudriven/shadow.ts` directly.
190
+ *
191
+ * It defines `shadowDepthAt` over a storage buffer, which is what a compute harness can be handed;
192
+ * the shading pass will define it over a depth texture. Nearest, which is what the comparison
193
+ * assumes — the arithmetic above measures its compensation to the texel centre.
194
+ */
195
+ export const SHADOW_PARITY_WGSL = /* wgsl */ `
196
+ @group(0) @binding(0) var<storage, read> params: array<f32>;
197
+ @group(0) @binding(1) var<storage, read> settingsBuffer: array<f32>;
198
+ @group(0) @binding(2) var<storage, read> receivers: array<f32>;
199
+ @group(0) @binding(3) var<storage, read> shadowMap: array<f32>;
200
+ @group(0) @binding(4) var<storage, read> gradients: array<f32>;
201
+ @group(0) @binding(5) var<storage, read_write> factors: array<f32>;
202
+
203
+ fn shadowDepthAt(u: f32, v: f32) -> f32 {
204
+ let size = settingsBuffer[1];
205
+ let x = clamp(floor(u * size), 0.0, size - 1.0);
206
+ let y = clamp(floor(v * size), 0.0, size - 1.0);
207
+ return shadowMap[u32(y) * u32(size) + u32(x)];
208
+ }
209
+
210
+ ${SHADE_SHADOW_WGSL}
211
+
212
+ @compute @workgroup_size(64)
213
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
214
+ if (id.x >= u32(params[0])) {
215
+ return;
216
+ }
217
+ var settings: ShadowSettings;
218
+ settings.strength = settingsBuffer[0];
219
+ settings.mapSize = settingsBuffer[1];
220
+ settings.depthSpan = settingsBuffer[2];
221
+ settings.maxDistance = settingsBuffer[3];
222
+ settings.maxSlope = settingsBuffer[4];
223
+ settings.taps = settingsBuffer[5];
224
+ settings.lightDir = vec4<f32>(settingsBuffer[8], settingsBuffer[9], settingsBuffer[10], 0.0);
225
+
226
+ let at = id.x * ${SHADOW_RECEIVER_FLOATS}u;
227
+ /*
228
+ * The gradient is solved here rather than passed, so the solve is checked too — the corpus feeds
229
+ * the two derivative vectors and the reference runs the same two-by-two.
230
+ */
231
+ let gradient = receiverPlaneDepthGradient(
232
+ vec3<f32>(gradients[id.x * 6u], gradients[id.x * 6u + 1u], gradients[id.x * 6u + 2u]),
233
+ vec3<f32>(gradients[id.x * 6u + 3u], gradients[id.x * 6u + 4u], gradients[id.x * 6u + 5u]),
234
+ );
235
+ factors[id.x] = shadowFactor(
236
+ vec4<f32>(receivers[at], receivers[at + 1u], receivers[at + 2u], receivers[at + 3u]),
237
+ gradient,
238
+ receivers[at + 6u],
239
+ settings,
240
+ );
241
+ }
242
+ `;
243
+ /** Floats a case of the receiver-plane check: three corners and the two weight gradients. */
244
+ export const RECEIVER_PLANE_FLOATS = 15;
245
+ /**
246
+ * The gradient assembly alone, as a dispatch, because it is the one piece of the shadow the
247
+ * shading pass computes for itself.
248
+ *
249
+ * The eleventh check hands the solve two derivative vectors. This is the step *before* that one:
250
+ * turning three light-space corners and two barycentric gradients into those vectors, which is
251
+ * where a factor of two or a transposed weight lives. Both are invisible in a picture — a gradient
252
+ * off by two compensates a tap by half of what it should, which is acne on grazing surfaces and
253
+ * looks like a bias that wants tuning.
254
+ */
255
+ export const RECEIVER_PLANE_PARITY_WGSL = /* wgsl */ `
256
+ @group(0) @binding(0) var<storage, read> params: array<f32>;
257
+ @group(0) @binding(1) var<storage, read> cases: array<f32>;
258
+ @group(0) @binding(2) var<storage, read_write> planes: array<f32>;
259
+
260
+ fn receiverPlaneDepthGradient(dx: vec3<f32>, dy: vec3<f32>) -> vec2<f32> {
261
+ let determinant = dx.x * dy.y - dx.y * dy.x;
262
+ let conditioning = length(dx.xy) * length(dy.xy);
263
+ if (abs(determinant) < 1e-4 * conditioning) {
264
+ return vec2<f32>(0.0, 0.0);
265
+ }
266
+ return vec2<f32>(
267
+ (dx.z * dy.y - dx.y * dy.z) / determinant,
268
+ (dx.x * dy.z - dx.z * dy.x) / determinant,
269
+ );
270
+ }
271
+
272
+ fn receiverPlaneFromWeights(
273
+ l0: vec3<f32>,
274
+ l1: vec3<f32>,
275
+ l2: vec3<f32>,
276
+ gx: vec3<f32>,
277
+ gy: vec3<f32>,
278
+ ) -> vec2<f32> {
279
+ let dx = vec3<f32>(
280
+ (gx.x * l0.x + gx.y * l1.x + gx.z * l2.x) * 0.5,
281
+ (gx.x * l0.y + gx.y * l1.y + gx.z * l2.y) * 0.5,
282
+ gx.x * l0.z + gx.y * l1.z + gx.z * l2.z,
283
+ );
284
+ let dy = vec3<f32>(
285
+ (gy.x * l0.x + gy.y * l1.x + gy.z * l2.x) * 0.5,
286
+ (gy.x * l0.y + gy.y * l1.y + gy.z * l2.y) * 0.5,
287
+ gy.x * l0.z + gy.y * l1.z + gy.z * l2.z,
288
+ );
289
+ return receiverPlaneDepthGradient(dx, dy);
290
+ }
291
+
292
+ @compute @workgroup_size(64)
293
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
294
+ if (id.x >= u32(params[0])) {
295
+ return;
296
+ }
297
+ let at = id.x * ${RECEIVER_PLANE_FLOATS}u;
298
+ let plane = receiverPlaneFromWeights(
299
+ vec3<f32>(cases[at], cases[at + 1u], cases[at + 2u]),
300
+ vec3<f32>(cases[at + 3u], cases[at + 4u], cases[at + 5u]),
301
+ vec3<f32>(cases[at + 6u], cases[at + 7u], cases[at + 8u]),
302
+ vec3<f32>(cases[at + 9u], cases[at + 10u], cases[at + 11u]),
303
+ vec3<f32>(cases[at + 12u], cases[at + 13u], cases[at + 14u]),
304
+ );
305
+ planes[id.x * 2u] = plane.x;
306
+ planes[id.x * 2u + 1u] = plane.y;
307
+ }
308
+ `;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The raster pass.
3
+ *
4
+ * `clusterList` is the compacted output of the cull, `clusterMeta` is four words a cluster — index
5
+ * offset, index count, cluster identifier, mesh — and `transforms` is sixteen floats a mesh. A
6
+ * cluster reaches its transform through its mesh rather than carrying one, because a mesh drawn
7
+ * twice is two instances of one cluster set and a transform per cluster would store it twice.
8
+ */
9
+ export declare const VISBUFFER_RASTER_WGSL: string;
10
+ /**
11
+ * The packing alone, as a dispatch, so the layout above can be checked against the reference.
12
+ *
13
+ * **A raster pass cannot be compared to a processor-side model and a bit layout can.** What goes
14
+ * wrong in a visibility buffer is almost never the rasterisation — the hardware does that — it is
15
+ * one field overflowing into the next, silently, on the one asset whose clusters are full. So the
16
+ * expression is lifted out and run over generated pairs until it agrees with `packVisibility` and
17
+ * `unpackVisibility` for every one of them.
18
+ */
19
+ export declare const VISBUFFER_PACK_WGSL = "\n@group(0) @binding(0) var<storage, read> clusters: array<u32>;\n@group(0) @binding(1) var<storage, read> triangles: array<u32>;\n@group(0) @binding(2) var<storage, read_write> packed: array<u32>;\n@group(0) @binding(3) var<storage, read_write> roundTrip: array<u32>;\n\nconst VIS_TRIANGLE_BITS: u32 = 7u;\nconst VIS_MAX_TRIANGLES: u32 = 128u;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= arrayLength(&clusters)) { return; }\n let value = (clusters[id.x] << VIS_TRIANGLE_BITS) | (triangles[id.x] & (VIS_MAX_TRIANGLES - 1u));\n packed[id.x] = value;\n /* Unpacked and re-packed, so a layout that packs consistently and unpacks wrongly is caught\n as well as one that packs wrongly at all. */\n let cluster = value >> VIS_TRIANGLE_BITS;\n let triangle = value & (VIS_MAX_TRIANGLES - 1u);\n roundTrip[id.x] = select(0u, 1u, cluster == clusters[id.x] && triangle == triangles[id.x]);\n}\n";
20
+ /**
21
+ * The cutout rule alone, as a dispatch, so it can be checked against `gpudriven/alphaTest.ts`.
22
+ *
23
+ * **The same function the raster calls, included rather than copied.** A second spelling of a
24
+ * four-line comparison is exactly the kind of thing that stays right for a year and then does not,
25
+ * and the failure it produces — a seam one texel wide around every cutout surface — reads as a
26
+ * mipmap problem. This shares the declaration, so breaking the rule breaks the check.
27
+ */
28
+ export declare const VISBUFFER_ALPHA_WGSL = "\n@group(0) @binding(0) var<storage, read> alphas: array<f32>;\n@group(0) @binding(1) var<storage, read> cutoffs: array<f32>;\n@group(0) @binding(2) var<storage, read_write> kept: array<f32>;\n\nfn alphaKept(alpha: f32, cutoff: f32) -> bool {\n if (!(cutoff > 0.0)) { return true; }\n return alpha >= cutoff;\n}\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) id: vec3<u32>) {\n if (id.x >= arrayLength(&alphas)) { return; }\n kept[id.x] = select(0.0, 1.0, alphaKept(alphas[id.x], cutoffs[id.x]));\n}\n";
@@ -0,0 +1,245 @@
1
+ import { GPU_DRIVEN_VERTEX_FLOATS } from '../../gpudriven/sceneUpload.js';
2
+ import { DECODE_WGSL, decodeResourcesWgsl } from './decode.wgsl.js';
3
+ import { MATERIAL_SLOTS, MATERIAL_TABLE_WGSL } from './materialTable.wgsl.js';
4
+ import { MESH_TRANSFORM_WGSL } from './meshTransform.wgsl.js';
5
+ /**
6
+ * The hardware path: one indirect draw that fills the visibility buffer for the whole frame.
7
+ *
8
+ * **Hand-written WGSL, for the reason `cull.wgsl.ts` gives at length**: WebGL2 has no compute
9
+ * stage and no indirect draw, so there is no GLSL original to generate this from and nothing it
10
+ * can break by being wrong. What keeps it honest is `scripts/gpu-parity.mjs`, which runs the
11
+ * packing below against `gpudriven/visbuffer.ts` on a real device.
12
+ *
13
+ * **Every surviving cluster is an instance of a fixed 128-triangle draw.** `indirect.ts` carries
14
+ * the argument layout and the reason: one block for the frame rather than thousands, and a cluster
15
+ * with fewer triangles emitting degenerate ones for the remainder. The degenerate case is a real
16
+ * branch here rather than a clamp — clamping draws a cluster's last triangle once per unused slot,
17
+ * which is a sliver along the edge of every cluster that is not exactly full.
18
+ *
19
+ * **The fragment writes an identifier, not a colour.** Depth is the hardware's; what lands in the
20
+ * target is which triangle of which cluster covered the pixel, and the shading reads it later —
21
+ * which is the whole point of a visibility buffer. `VIS_EMPTY` is what a caller clears the target
22
+ * to, because zero is a legal identifier.
23
+ *
24
+ * **It binds a material anyway, and only for the alpha test.** That is the one decision a pixel
25
+ * cannot defer: a cutout leaf that is discarded in the shading pass has already written its
26
+ * identifier here, so what stood behind it was never rastered and there is nothing left to shade.
27
+ * So this stage carries the material table and the whole decode machinery to answer one question —
28
+ * is this texel solid enough to own the pixel — and **a material whose cutoff is zero asks none of
29
+ * it**, which is what keeps an opaque scene paying nothing. A cluster carries one material, so the
30
+ * branch is coherent across every fragment of a draw rather than divergent within one.
31
+ *
32
+ * **The shadow pass shares this module's vertex stage and has no fragment stage at all**, so a
33
+ * cutout surface casts the shadow of its whole quad. That is a real limitation rather than an
34
+ * oversight: giving the depth-only pass a fragment stage would give it the material table and the
35
+ * decode tables too, and the map is drawn every frame over the whole scene. `docs/IMPROVEMENTS.md`
36
+ * carries the row.
37
+ */
38
+ /**
39
+ * The cutout rule, which is `gpudriven/alphaTest.ts`'s `alphaKept` and must stay it.
40
+ *
41
+ * **`>=` rather than `>`** is the boundary every alpha test gets wrong, and getting it wrong shows
42
+ * as a one-texel seam around every leaf rather than as anything failing. **The guard is a separate
43
+ * return rather than arithmetic** because a NaN alpha must be discarded when a test was asked for
44
+ * and kept when it was not, and **it is a negated `>` rather than a `<=`** because a device may
45
+ * assume NaN never arrives and this is where the reference had to move to meet it. The reference's
46
+ * header carries both, and `scripts/gpu-parity.mjs` holds the two to each other on a device.
47
+ */
48
+ const ALPHA_TEST_WGSL = /* wgsl */ `
49
+ fn alphaKept(alpha: f32, cutoff: f32) -> bool {
50
+ if (!(cutoff > 0.0)) { return true; }
51
+ return alpha >= cutoff;
52
+ }
53
+ `;
54
+ /**
55
+ * The footprint-to-level expression, which is `shade.wgsl.ts`'s `surfaceLod` and must stay it.
56
+ *
57
+ * **Two stages choose a mip level for one texel now**, and they choose it from different evidence:
58
+ * this one differences its neighbours, and the shading pass differences barycentric gradients
59
+ * because a compute invocation has no neighbours. What they must not differ about is what a
60
+ * footprint means once measured, which is this.
61
+ */
62
+ const SURFACE_LOD_WGSL = /* wgsl */ `
63
+ fn surfaceLod(gradX: vec2<f32>, gradY: vec2<f32>, size: f32) -> f32 {
64
+ let footprint = max(length(gradX), length(gradY)) * size;
65
+ return max(log2(max(footprint, 1e-30)), 0.0);
66
+ }
67
+ `;
68
+ /**
69
+ * The raster pass.
70
+ *
71
+ * `clusterList` is the compacted output of the cull, `clusterMeta` is four words a cluster — index
72
+ * offset, index count, cluster identifier, mesh — and `transforms` is sixteen floats a mesh. A
73
+ * cluster reaches its transform through its mesh rather than carrying one, because a mesh drawn
74
+ * twice is two instances of one cluster set and a transform per cluster would store it twice.
75
+ */
76
+ export const VISBUFFER_RASTER_WGSL = /* wgsl */ `
77
+ struct Varying {
78
+ @builtin(position) position: vec4<f32>,
79
+ @location(0) @interpolate(flat) visibility: u32,
80
+ /** The cluster's material and this vertex's texture coordinate, for the alpha test. */
81
+ @location(1) @interpolate(flat) material: u32,
82
+ @location(2) uv: vec2<f32>,
83
+ }
84
+
85
+ /** The two numbers the decode machinery needs that nothing else here carries. */
86
+ struct RasterParams {
87
+ /** x is the simulation time, y the latent layer's edge in texels, and two spare. */
88
+ values: vec4<f32>,
89
+ }
90
+
91
+ ${MATERIAL_TABLE_WGSL}
92
+ @group(0) @binding(0) var<storage, read> viewProj: array<f32, 16>;
93
+ @group(0) @binding(1) var<storage, read> clusterList: array<u32>;
94
+ @group(0) @binding(2) var<storage, read> clusterMeta: array<u32>;
95
+ @group(0) @binding(3) var<storage, read> indices: array<u32>;
96
+ /** Eleven floats a vertex: position, normal, colour, uv. One buffer, for the shading pass's
97
+ storage budget — see shade.wgsl.ts. This stage reads the position and the uv. */
98
+ @group(0) @binding(4) var<storage, read> vertices: array<f32>;
99
+ @group(0) @binding(5) var<storage, read> transforms: array<f32>;
100
+ /** One material a cluster, read in the vertex stage and carried flat. */
101
+ @group(0) @binding(6) var<storage, read> materialOf: array<u32>;
102
+ @group(0) @binding(7) var<uniform> materials: array<Material, ${MATERIAL_SLOTS}>;
103
+ @group(0) @binding(8) var<uniform> rasterParams: RasterParams;
104
+ ${decodeResourcesWgsl({ group: 0, latents: 9, clampSampler: 10, repeatSampler: 11, nodes: 12, weights: 13 })}
105
+ fn decodeLatentSize() -> f32 {
106
+ return rasterParams.values.y;
107
+ }
108
+
109
+ ${DECODE_WGSL}
110
+ ${SURFACE_LOD_WGSL}
111
+ ${ALPHA_TEST_WGSL}
112
+
113
+ const VIS_TRIANGLE_BITS: u32 = 7u;
114
+ const VIS_MAX_TRIANGLES: u32 = 128u;
115
+
116
+ /* The same expression as packVisibility in gpudriven/visbuffer.ts, checked against it on the
117
+ device by scripts/gpu-parity.mjs. Seven bits of triangle, twenty-five of cluster. */
118
+ fn packVisibility(cluster: u32, triangle: u32) -> u32 {
119
+ return (cluster << VIS_TRIANGLE_BITS) | (triangle & (VIS_MAX_TRIANGLES - 1u));
120
+ }
121
+
122
+ ${MESH_TRANSFORM_WGSL}
123
+
124
+ @vertex
125
+ fn vertexMain(
126
+ @builtin(vertex_index) vertex: u32,
127
+ @builtin(instance_index) slot: u32,
128
+ ) -> Varying {
129
+ var out: Varying;
130
+ let cluster = clusterList[slot];
131
+ /* metaAt, because meta is a reserved word in WGSL. A backtick cannot appear in this comment
132
+ either: it would close the template literal the shader is written in. This shader was
133
+ committed and read by a generator check for a day before anything compiled it. */
134
+ let metaAt = cluster * 4u;
135
+ let indexOffset = clusterMeta[metaAt];
136
+ let indexCount = clusterMeta[metaAt + 1u];
137
+ let clusterId = clusterMeta[metaAt + 2u];
138
+ let mesh = clusterMeta[metaAt + 3u];
139
+
140
+ let triangle = vertex / 3u;
141
+ /* Past this cluster's own triangles: collapse to a point, which covers no pixel. See the
142
+ header for why this is not a clamp onto the last real triangle. */
143
+ if (triangle * 3u >= indexCount) {
144
+ out.position = vec4<f32>(0.0, 0.0, 0.0, 0.0);
145
+ out.visibility = 0u;
146
+ out.material = 0u;
147
+ out.uv = vec2<f32>(0.0);
148
+ return out;
149
+ }
150
+
151
+ let index = indices[indexOffset + vertex];
152
+ let at = index * ${GPU_DRIVEN_VERTEX_FLOATS}u;
153
+ let local = vec3<f32>(vertices[at], vertices[at + 1u], vertices[at + 2u]);
154
+ let world = worldOf(mesh, local);
155
+
156
+ out.position = vec4<f32>(
157
+ viewProj[0] * world.x + viewProj[4] * world.y + viewProj[8] * world.z + viewProj[12],
158
+ viewProj[1] * world.x + viewProj[5] * world.y + viewProj[9] * world.z + viewProj[13],
159
+ viewProj[2] * world.x + viewProj[6] * world.y + viewProj[10] * world.z + viewProj[14],
160
+ viewProj[3] * world.x + viewProj[7] * world.y + viewProj[11] * world.z + viewProj[15],
161
+ );
162
+ out.visibility = packVisibility(clusterId, triangle);
163
+ /* By the cluster's own record rather than the mesh's, which is where materialOf is indexed. */
164
+ out.material = materialOf[cluster];
165
+ out.uv = vec2<f32>(vertices[at + 9u], vertices[at + 10u]);
166
+ return out;
167
+ }
168
+
169
+ @fragment
170
+ fn fragmentMain(in: Varying) -> @location(0) u32 {
171
+ let entry = materials[min(in.material, ${MATERIAL_SLOTS - 1}u)];
172
+ let cutoff = entry.orm.z;
173
+ /*
174
+ * **A cutoff of zero skips the fetch outright**, and with it the whole decode interpreter. That
175
+ * branch is on a value read from a uniform by a flat-interpolated index that is constant across
176
+ * the draw, so it is coherent — every fragment of a cluster takes the same side of it.
177
+ *
178
+ * **The derivatives are taken before the branch**, because a texture level derived inside
179
+ * non-uniform control flow is what the language forbids and a discard below makes the rest of
180
+ * this function non-uniform anyway. shade.wgsl.ts computes the same footprint from barycentric
181
+ * gradients instead, having no neighbouring invocations to difference against.
182
+ */
183
+ let uv = in.uv * entry.maps.xy;
184
+ let lod = surfaceLod(dpdx(uv), dpdy(uv), rasterParams.values.y);
185
+ /*
186
+ * **This branch duplicates alphaKept's own guard and is not redundant**: alphaKept answers what
187
+ * the picture is and this decides what the frame pays. A material with no cutout skips the
188
+ * decode interpreter entirely, which is the whole of why the raster can carry it at all.
189
+ */
190
+ if (cutoff > 0.0 && entry.programs.x != NO_PROGRAM) {
191
+ let alpha = decodeProgram(entry.programs.x, uv, lod, rasterParams.values.x).w;
192
+ if (!alphaKept(alpha, cutoff)) { discard; }
193
+ }
194
+ return in.visibility;
195
+ }
196
+ `;
197
+ /**
198
+ * The packing alone, as a dispatch, so the layout above can be checked against the reference.
199
+ *
200
+ * **A raster pass cannot be compared to a processor-side model and a bit layout can.** What goes
201
+ * wrong in a visibility buffer is almost never the rasterisation — the hardware does that — it is
202
+ * one field overflowing into the next, silently, on the one asset whose clusters are full. So the
203
+ * expression is lifted out and run over generated pairs until it agrees with `packVisibility` and
204
+ * `unpackVisibility` for every one of them.
205
+ */
206
+ export const VISBUFFER_PACK_WGSL = `
207
+ @group(0) @binding(0) var<storage, read> clusters: array<u32>;
208
+ @group(0) @binding(1) var<storage, read> triangles: array<u32>;
209
+ @group(0) @binding(2) var<storage, read_write> packed: array<u32>;
210
+ @group(0) @binding(3) var<storage, read_write> roundTrip: array<u32>;
211
+
212
+ const VIS_TRIANGLE_BITS: u32 = 7u;
213
+ const VIS_MAX_TRIANGLES: u32 = 128u;
214
+
215
+ @compute @workgroup_size(64)
216
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
217
+ if (id.x >= arrayLength(&clusters)) { return; }
218
+ let value = (clusters[id.x] << VIS_TRIANGLE_BITS) | (triangles[id.x] & (VIS_MAX_TRIANGLES - 1u));
219
+ packed[id.x] = value;
220
+ /* Unpacked and re-packed, so a layout that packs consistently and unpacks wrongly is caught
221
+ as well as one that packs wrongly at all. */
222
+ let cluster = value >> VIS_TRIANGLE_BITS;
223
+ let triangle = value & (VIS_MAX_TRIANGLES - 1u);
224
+ roundTrip[id.x] = select(0u, 1u, cluster == clusters[id.x] && triangle == triangles[id.x]);
225
+ }
226
+ `;
227
+ /**
228
+ * The cutout rule alone, as a dispatch, so it can be checked against `gpudriven/alphaTest.ts`.
229
+ *
230
+ * **The same function the raster calls, included rather than copied.** A second spelling of a
231
+ * four-line comparison is exactly the kind of thing that stays right for a year and then does not,
232
+ * and the failure it produces — a seam one texel wide around every cutout surface — reads as a
233
+ * mipmap problem. This shares the declaration, so breaking the rule breaks the check.
234
+ */
235
+ export const VISBUFFER_ALPHA_WGSL = /* wgsl */ `
236
+ @group(0) @binding(0) var<storage, read> alphas: array<f32>;
237
+ @group(0) @binding(1) var<storage, read> cutoffs: array<f32>;
238
+ @group(0) @binding(2) var<storage, read_write> kept: array<f32>;
239
+ ${ALPHA_TEST_WGSL}
240
+ @compute @workgroup_size(64)
241
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
242
+ if (id.x >= arrayLength(&alphas)) { return; }
243
+ kept[id.x] = select(0.0, 1.0, alphaKept(alphas[id.x], cutoffs[id.x]));
244
+ }
245
+ `;