@driftengine/core 3.63.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.json +122 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +69 -4
  9. package/dist/index.js +112 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +246 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The one network evaluator the device has, in single or half precision.
3
+ *
4
+ * **Every network the engine evaluates on a device goes through this text.** A DriftTexture's
5
+ * decode interpreter reads its networks from uniform tables; reconstruction's refinement tier and
6
+ * capture's priors read theirs from storage buffers, in half precision where the device has it.
7
+ * Those are different bindings and the same arithmetic, so the arithmetic is written once and the
8
+ * weights are reached through a function the including module defines — the arrangement
9
+ * `shadow.wgsl.ts` uses for its map. A second evaluator would be a second set of numerical
10
+ * conventions to keep in step, which `@driftengine/texture`'s `inference.ts` names as the thing this
11
+ * engine refuses.
12
+ *
13
+ * **The reference is `evalNetwork`, and in half precision `evalNetworkHalf`**, operation for
14
+ * operation: the weighted inputs summed in index order, then the bias, then the rectifier on hidden
15
+ * layers and nothing on the output. The weights are laid out as `NNET` stores them — each layer's
16
+ * matrix row by row by output, then its biases.
17
+ *
18
+ * **Half precision needs `enable f16;` at the top of the including module** and a device created
19
+ * with `shader-f16`. It is chosen per network by its consumer, and `activationBound` is what that
20
+ * consumer asks first: past 65,504 the format has no finite value.
21
+ *
22
+ * `scripts/gpu-parity.mjs` runs both instantiations against the references, and
23
+ * `scripts/wgsl-handwritten.test.mjs` has `naga` validate them without a device.
24
+ */
25
+ export interface NetworkWgslOptions {
26
+ /** The prefix every function this defines carries, so a module can include more than one. */
27
+ readonly name: string;
28
+ /** The scalar the network computes in. `f16` needs `enable f16;` in the including module. */
29
+ readonly scalar: 'f32' | 'f16';
30
+ /** The widest layer, which is the length of the function-scope arrays the layers pass through. */
31
+ readonly width: number;
32
+ /** The most hidden layers a network may have here. */
33
+ readonly hidden: number;
34
+ }
35
+ /**
36
+ * The evaluator, as text.
37
+ *
38
+ * Defines `fn <name>Eval(first, inputs, hidden, widths, outputs, input) -> array<S, width>`:
39
+ * `first` is where the network's weights start, `widths` its hidden widths, and `input` its first
40
+ * `inputs` values. The including module defines `fn <name>Weight(index: u32) -> S`.
41
+ *
42
+ * **Every count is clamped to what the arrays hold.** A loader refuses a network wider or deeper
43
+ * than this was instantiated for; the clamps are what keep a malformed one from indexing past a
44
+ * function-scope array, which the language would otherwise clamp silently and differently.
45
+ */
46
+ export declare function networkWgsl(options: NetworkWgslOptions): string;
47
+ /** A network's shape, which the fixed form bakes in. The same shape `evalNetwork` takes. */
48
+ export interface FixedNetworkShape {
49
+ readonly inputs: number;
50
+ readonly hidden: readonly number[];
51
+ readonly outputs: number;
52
+ }
53
+ export interface FixedNetworkWgslOptions {
54
+ /** The prefix every function this defines carries, as for the variable form. */
55
+ readonly name: string;
56
+ readonly scalar: 'f32' | 'f16';
57
+ readonly shape: FixedNetworkShape;
58
+ /** Where the network's weights start in what `<name>Weight` reads. */
59
+ readonly first?: number;
60
+ }
61
+ /**
62
+ * The same evaluator with its shape fixed when the pipeline is built, for a consumer whose network
63
+ * is known then — reconstruction's refinement tier, capture's priors.
64
+ *
65
+ * **Nine to fourteen times faster, measured**, 2026-09-19 on an RX 9070 XT, once per pixel of a
66
+ * 1280×720 frame: a network of 11-8-3 in 0.031 ms against the variable form's 0.286, and 11-16-16-3
67
+ * in 0.076 against 1.09 (`tools/recon-train/budget.ts`). The variable form indexes function-scope
68
+ * arrays with counts it reads at run time, which a compiler cannot keep in registers; with every
69
+ * count a constant the loops unroll and the arrays are registers. The DriftTexture interpreter keeps
70
+ * the variable form, because its networks change per material and its pipeline does not.
71
+ *
72
+ * **The arithmetic is the variable form's, in the same order** — the weighted inputs summed in index
73
+ * order, then the bias, then the rectifier on hidden layers and nothing on the output, over weights
74
+ * laid out as `NNET` stores them — so `evalNetwork` and `evalNetworkHalf` are its references too,
75
+ * and `gpu-parity.mjs` holds it to them the same way.
76
+ *
77
+ * Defines `fn <name>Eval(input: array<S, inputs>) -> array<S, outputs>`. The including module
78
+ * defines `fn <name>Weight(index: u32) -> S`, as for the variable form.
79
+ */
80
+ export declare function networkFixedWgsl(options: FixedNetworkWgslOptions): string;
81
+ /**
82
+ * An entry point that evaluates one fixed-shape network over many inputs, so a harness can hold
83
+ * the fixed form to `evalNetwork` and `evalNetworkHalf` as it holds the variable one. Inputs and
84
+ * outputs cross as `f32`, as the variable form's parity entry point has them.
85
+ */
86
+ export declare function networkFixedParityWgsl(scalar: 'f32' | 'f16', shape: FixedNetworkShape): string;
87
+ /** The widest network the parity entry points evaluate. */
88
+ export declare const NETWORK_PARITY_WIDTH = 16;
89
+ /** The deepest. */
90
+ export declare const NETWORK_PARITY_HIDDEN = 4;
91
+ /** Unsigned words per case: first, inputs, hidden count, outputs, then the hidden widths. */
92
+ export declare const NETWORK_CASE_WORDS: number;
93
+ /**
94
+ * An entry point that evaluates one network per invocation from storage buffers, so a harness can
95
+ * compare the device with `evalNetwork` or `evalNetworkHalf`.
96
+ *
97
+ * Inputs and outputs cross as `f32` whatever the network computes in: a half-precision network's
98
+ * inputs are converted on the way in, as `evalNetworkHalf` rounds them, and its outputs widen on
99
+ * the way out, which loses nothing.
100
+ */
101
+ export declare function networkParityWgsl(scalar: 'f32' | 'f16'): string;
@@ -0,0 +1,206 @@
1
+ /**
2
+ * The one network evaluator the device has, in single or half precision.
3
+ *
4
+ * **Every network the engine evaluates on a device goes through this text.** A DriftTexture's
5
+ * decode interpreter reads its networks from uniform tables; reconstruction's refinement tier and
6
+ * capture's priors read theirs from storage buffers, in half precision where the device has it.
7
+ * Those are different bindings and the same arithmetic, so the arithmetic is written once and the
8
+ * weights are reached through a function the including module defines — the arrangement
9
+ * `shadow.wgsl.ts` uses for its map. A second evaluator would be a second set of numerical
10
+ * conventions to keep in step, which `@driftengine/texture`'s `inference.ts` names as the thing this
11
+ * engine refuses.
12
+ *
13
+ * **The reference is `evalNetwork`, and in half precision `evalNetworkHalf`**, operation for
14
+ * operation: the weighted inputs summed in index order, then the bias, then the rectifier on hidden
15
+ * layers and nothing on the output. The weights are laid out as `NNET` stores them — each layer's
16
+ * matrix row by row by output, then its biases.
17
+ *
18
+ * **Half precision needs `enable f16;` at the top of the including module** and a device created
19
+ * with `shader-f16`. It is chosen per network by its consumer, and `activationBound` is what that
20
+ * consumer asks first: past 65,504 the format has no finite value.
21
+ *
22
+ * `scripts/gpu-parity.mjs` runs both instantiations against the references, and
23
+ * `scripts/wgsl-handwritten.test.mjs` has `naga` validate them without a device.
24
+ */
25
+ /**
26
+ * The evaluator, as text.
27
+ *
28
+ * Defines `fn <name>Eval(first, inputs, hidden, widths, outputs, input) -> array<S, width>`:
29
+ * `first` is where the network's weights start, `widths` its hidden widths, and `input` its first
30
+ * `inputs` values. The including module defines `fn <name>Weight(index: u32) -> S`.
31
+ *
32
+ * **Every count is clamped to what the arrays hold.** A loader refuses a network wider or deeper
33
+ * than this was instantiated for; the clamps are what keep a malformed one from indexing past a
34
+ * function-scope array, which the language would otherwise clamp silently and differently.
35
+ */
36
+ export function networkWgsl(options) {
37
+ const { name, scalar: s, width: w, hidden: h } = options;
38
+ return /* wgsl */ `
39
+ fn ${name}Eval(
40
+ first: u32,
41
+ inputs: u32,
42
+ hidden: u32,
43
+ widths: array<u32, ${h}>,
44
+ outputs: u32,
45
+ input: array<${s}, ${w}>,
46
+ ) -> array<${s}, ${w}> {
47
+ var current = input;
48
+ var next: array<${s}, ${w}>;
49
+ var at = first;
50
+ var previous = min(inputs, ${w}u);
51
+ let layers = min(hidden, ${h}u);
52
+ for (var layer = 0u; layer < layers; layer = layer + 1u) {
53
+ let width = min(widths[layer], ${w}u);
54
+ for (var o = 0u; o < width; o = o + 1u) {
55
+ var total = ${s}(0.0);
56
+ for (var i = 0u; i < previous; i = i + 1u) {
57
+ total = total + current[i] * ${name}Weight(at + o * previous + i);
58
+ }
59
+ /* Rectified linear on hidden layers and nothing on the output, as inference.ts insists. */
60
+ next[o] = max(total + ${name}Weight(at + width * previous + o), ${s}(0.0));
61
+ }
62
+ at = at + previous * width + width;
63
+ previous = width;
64
+ current = next;
65
+ }
66
+ var result: array<${s}, ${w}>;
67
+ let count = min(outputs, ${w}u);
68
+ for (var o = 0u; o < count; o = o + 1u) {
69
+ var total = ${s}(0.0);
70
+ for (var i = 0u; i < previous; i = i + 1u) {
71
+ total = total + current[i] * ${name}Weight(at + o * previous + i);
72
+ }
73
+ result[o] = total + ${name}Weight(at + count * previous + o);
74
+ }
75
+ return result;
76
+ }
77
+ `;
78
+ }
79
+ /**
80
+ * The same evaluator with its shape fixed when the pipeline is built, for a consumer whose network
81
+ * is known then — reconstruction's refinement tier, capture's priors.
82
+ *
83
+ * **Nine to fourteen times faster, measured**, 2026-09-19 on an RX 9070 XT, once per pixel of a
84
+ * 1280×720 frame: a network of 11-8-3 in 0.031 ms against the variable form's 0.286, and 11-16-16-3
85
+ * in 0.076 against 1.09 (`tools/recon-train/budget.ts`). The variable form indexes function-scope
86
+ * arrays with counts it reads at run time, which a compiler cannot keep in registers; with every
87
+ * count a constant the loops unroll and the arrays are registers. The DriftTexture interpreter keeps
88
+ * the variable form, because its networks change per material and its pipeline does not.
89
+ *
90
+ * **The arithmetic is the variable form's, in the same order** — the weighted inputs summed in index
91
+ * order, then the bias, then the rectifier on hidden layers and nothing on the output, over weights
92
+ * laid out as `NNET` stores them — so `evalNetwork` and `evalNetworkHalf` are its references too,
93
+ * and `gpu-parity.mjs` holds it to them the same way.
94
+ *
95
+ * Defines `fn <name>Eval(input: array<S, inputs>) -> array<S, outputs>`. The including module
96
+ * defines `fn <name>Weight(index: u32) -> S`, as for the variable form.
97
+ */
98
+ export function networkFixedWgsl(options) {
99
+ const { name, scalar: s, shape } = options;
100
+ const layers = [shape.inputs, ...shape.hidden, shape.outputs];
101
+ let at = options.first ?? 0;
102
+ let body = '';
103
+ let previous = 'input';
104
+ for (let layer = 1; layer < layers.length; layer += 1) {
105
+ const width = layers[layer];
106
+ const fanIn = layers[layer - 1];
107
+ const output = layer === layers.length - 1;
108
+ const target = output ? 'result' : `layer${layer}`;
109
+ const bias = at + width * fanIn;
110
+ const value = output
111
+ ? `total + ${name}Weight(${bias}u + o)`
112
+ : `max(total + ${name}Weight(${bias}u + o), ${s}(0.0))`;
113
+ body += `
114
+ var ${target}: array<${s}, ${width}>;
115
+ for (var o = 0u; o < ${width}u; o = o + 1u) {
116
+ var total = ${s}(0.0);
117
+ for (var i = 0u; i < ${fanIn}u; i = i + 1u) {
118
+ total = total + ${previous}[i] * ${name}Weight(${at}u + o * ${fanIn}u + i);
119
+ }
120
+ ${target}[o] = ${value};
121
+ }`;
122
+ at = bias + width;
123
+ previous = target;
124
+ }
125
+ return /* wgsl */ `
126
+ fn ${name}Eval(input: array<${s}, ${shape.inputs}>) -> array<${s}, ${shape.outputs}> {${body}
127
+ return result;
128
+ }
129
+ `;
130
+ }
131
+ /**
132
+ * An entry point that evaluates one fixed-shape network over many inputs, so a harness can hold
133
+ * the fixed form to `evalNetwork` and `evalNetworkHalf` as it holds the variable one. Inputs and
134
+ * outputs cross as `f32`, as the variable form's parity entry point has them.
135
+ */
136
+ export function networkFixedParityWgsl(scalar, shape) {
137
+ return /* wgsl */ `${scalar === 'f16' ? 'enable f16;\n' : ''}
138
+ @group(0) @binding(0) var<storage, read> weights: array<${scalar}>;
139
+ @group(0) @binding(1) var<storage, read> inputs: array<f32>;
140
+ @group(0) @binding(2) var<storage, read_write> outputs: array<f32>;
141
+
142
+ fn parityWeight(index: u32) -> ${scalar} {
143
+ return weights[index];
144
+ }
145
+ ${networkFixedWgsl({ name: 'parity', scalar, shape })}
146
+ @compute @workgroup_size(64)
147
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
148
+ let count = arrayLength(&inputs) / ${shape.inputs}u;
149
+ if (id.x >= count) { return; }
150
+ var input: array<${scalar}, ${shape.inputs}>;
151
+ for (var i = 0u; i < ${shape.inputs}u; i = i + 1u) {
152
+ input[i] = ${scalar}(inputs[id.x * ${shape.inputs}u + i]);
153
+ }
154
+ let result = parityEval(input);
155
+ for (var o = 0u; o < ${shape.outputs}u; o = o + 1u) {
156
+ outputs[id.x * ${shape.outputs}u + o] = f32(result[o]);
157
+ }
158
+ }
159
+ `;
160
+ }
161
+ /** The widest network the parity entry points evaluate. */
162
+ export const NETWORK_PARITY_WIDTH = 16;
163
+ /** The deepest. */
164
+ export const NETWORK_PARITY_HIDDEN = 4;
165
+ /** Unsigned words per case: first, inputs, hidden count, outputs, then the hidden widths. */
166
+ export const NETWORK_CASE_WORDS = 4 + NETWORK_PARITY_HIDDEN;
167
+ /**
168
+ * An entry point that evaluates one network per invocation from storage buffers, so a harness can
169
+ * compare the device with `evalNetwork` or `evalNetworkHalf`.
170
+ *
171
+ * Inputs and outputs cross as `f32` whatever the network computes in: a half-precision network's
172
+ * inputs are converted on the way in, as `evalNetworkHalf` rounds them, and its outputs widen on
173
+ * the way out, which loses nothing.
174
+ */
175
+ export function networkParityWgsl(scalar) {
176
+ const w = NETWORK_PARITY_WIDTH;
177
+ return /* wgsl */ `${scalar === 'f16' ? 'enable f16;\n' : ''}
178
+ @group(0) @binding(0) var<storage, read> cases: array<u32>;
179
+ @group(0) @binding(1) var<storage, read> weights: array<${scalar}>;
180
+ @group(0) @binding(2) var<storage, read> inputs: array<f32>;
181
+ @group(0) @binding(3) var<storage, read_write> outputs: array<f32>;
182
+
183
+ fn parityWeight(index: u32) -> ${scalar} {
184
+ return weights[index];
185
+ }
186
+ ${networkWgsl({ name: 'parity', scalar, width: w, hidden: NETWORK_PARITY_HIDDEN })}
187
+ @compute @workgroup_size(64)
188
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
189
+ let count = arrayLength(&cases) / ${NETWORK_CASE_WORDS}u;
190
+ if (id.x >= count) { return; }
191
+ let at = id.x * ${NETWORK_CASE_WORDS}u;
192
+ var widths: array<u32, ${NETWORK_PARITY_HIDDEN}>;
193
+ for (var layer = 0u; layer < ${NETWORK_PARITY_HIDDEN}u; layer = layer + 1u) {
194
+ widths[layer] = cases[at + 4u + layer];
195
+ }
196
+ var input: array<${scalar}, ${w}>;
197
+ for (var i = 0u; i < ${w}u; i = i + 1u) {
198
+ input[i] = ${scalar}(inputs[id.x * ${w}u + i]);
199
+ }
200
+ let result = parityEval(cases[at], cases[at + 1u], cases[at + 2u], widths, cases[at + 3u], input);
201
+ for (var o = 0u; o < ${w}u; o = o + 1u) {
202
+ outputs[id.x * ${w}u + o] = f32(result[o]);
203
+ }
204
+ }
205
+ `;
206
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Where a drawn surface was last frame, written into the reconstruction's motion target.
3
+ *
4
+ * **A pass of its own rather than a second output on every material.** The alternative is a second
5
+ * colour attachment on every generated fragment stage, which `ARCHITECTURE.md` §1 prices at 196,910
6
+ * gzipped bytes for one flag — and it would be paid by every consumer, including the ones who never
7
+ * reconstruct anything. This draws only the meshes a caller said had moved, with one small shader
8
+ * over the position buffer alone.
9
+ *
10
+ * **What a pixel carries**: the offset from where it is now to where its surface was, in the uv the
11
+ * resolve measures in; the view depth it had then, which is what the disocclusion compares against
12
+ * last frame's depth; and a flag. `recon/resolve.ts`'s `historyWeight` is the reader, and a texel
13
+ * this pass did not write is a zero flag, which means *derive the camera's motion from the depth* —
14
+ * so a scene that says nothing about its movers is exactly the scene this pass never ran for.
15
+ *
16
+ * **The rasterisation matrix is handed over whole, and that is deliberate.** The scene is drawn by
17
+ * shaders `naga` generated, every one of which ends its vertex stage with `gl_Position.y = -y`, and
18
+ * `CLIP_CORRECTION` carries a negation to cancel it — §3 rows 56 and 57. This shader is hand
19
+ * written and has no such negation, so rather than reason about halves of that pair per draw, the
20
+ * renderer multiplies the two together once a frame and passes the product. The vertex stage then
21
+ * has one job: put the position exactly where the scene put it, so the depth test compares like
22
+ * with like.
23
+ *
24
+ * **The motion itself is measured from the *unjittered* matrices**, which is the whole of §3 row
25
+ * 121: the history stands for a picture of the scene as it would have been drawn with no jitter, so
26
+ * a motion vector that carried the jitter difference would move every sample by a fraction of a
27
+ * pixel in a pattern that repeats with the jitter period — shimmer, diagnosed as anything but its
28
+ * cause.
29
+ */
30
+ /** Floats in the per-frame block: three matrices. */
31
+ export declare const MOTION_FRAME_FLOATS = 48;
32
+ /**
33
+ * Bytes a draw's own block takes.
34
+ *
35
+ * Two matrices is 128, and a dynamic uniform offset must be a multiple of 256 — the same alignment
36
+ * §3 row 50's neighbours are about. So each draw takes a whole 256 and half of it is padding, which
37
+ * is the cheaper half of the trade against a bind group per draw inside the frame loop.
38
+ */
39
+ export declare const MOTION_DRAW_STRIDE = 256;
40
+ /** Floats a draw actually writes inside that stride. */
41
+ export declare const MOTION_DRAW_FLOATS = 32;
42
+ /** The motion target's format. Half float holds a uv to a hundredth of a pixel and a view depth. */
43
+ export declare const MOTION_TARGET_FORMAT: GPUTextureFormat;
44
+ export declare const RECON_MOTION_WGSL = "\nstruct MotionFrame {\n /* The scene's own clip transform, with the generated shaders' y negation already folded in. */\n raster: mat4x4<f32>,\n /* This frame's and last frame's view-projections, unjittered, in the matrices' own convention. */\n viewProj: mat4x4<f32>,\n previousViewProj: mat4x4<f32>,\n}\n\nstruct MotionDraw {\n model: mat4x4<f32>,\n previousModel: mat4x4<f32>,\n}\n\n@group(0) @binding(0) var<uniform> frame: MotionFrame;\n@group(0) @binding(1) var<uniform> draw: MotionDraw;\n\nstruct Varyings {\n @builtin(position) clip: vec4<f32>,\n /* Clip positions rather than uv: an interpolator divides by w for us, so dividing these by their\n own interpolated w in the fragment stage is the perspective-correct answer at every pixel. */\n @location(0) now: vec4<f32>,\n @location(1) was: vec4<f32>,\n}\n\n@vertex\nfn motionVert(@location(0) position: vec3<f32>) -> Varyings {\n let world = draw.model * vec4<f32>(position, 1.0);\n let wasWorld = draw.previousModel * vec4<f32>(position, 1.0);\n var out: Varyings;\n out.clip = frame.raster * world;\n out.now = frame.viewProj * world;\n out.was = frame.previousViewProj * wasWorld;\n return out;\n}\n\n/* Normalised device coordinates to the uv the resolve measures in: v increases upward, as the\n references' images do, and the accessor that reads this target is what flips the texture's rows. */\nfn motionUv(clip: vec4<f32>) -> vec2<f32> {\n return clip.xy / clip.w * 0.5 + vec2<f32>(0.5);\n}\n\n@fragment\nfn motionFrag(in: Varyings) -> @location(0) vec4<f32> {\n /*\n * A surface behind last frame's eye has a w that is not positive, and there is no uv for it. The\n * flag stays set: the disocclusion refuses a depth that is not positive, so the resolve drops the\n * history rather than reading it from wherever a divide by a negative w would have pointed.\n */\n let offset = select(vec2<f32>(0.0), motionUv(in.was) - motionUv(in.now), in.was.w > 0.0);\n return vec4<f32>(offset, in.was.w, 1.0);\n}\n";
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Where a drawn surface was last frame, written into the reconstruction's motion target.
3
+ *
4
+ * **A pass of its own rather than a second output on every material.** The alternative is a second
5
+ * colour attachment on every generated fragment stage, which `ARCHITECTURE.md` §1 prices at 196,910
6
+ * gzipped bytes for one flag — and it would be paid by every consumer, including the ones who never
7
+ * reconstruct anything. This draws only the meshes a caller said had moved, with one small shader
8
+ * over the position buffer alone.
9
+ *
10
+ * **What a pixel carries**: the offset from where it is now to where its surface was, in the uv the
11
+ * resolve measures in; the view depth it had then, which is what the disocclusion compares against
12
+ * last frame's depth; and a flag. `recon/resolve.ts`'s `historyWeight` is the reader, and a texel
13
+ * this pass did not write is a zero flag, which means *derive the camera's motion from the depth* —
14
+ * so a scene that says nothing about its movers is exactly the scene this pass never ran for.
15
+ *
16
+ * **The rasterisation matrix is handed over whole, and that is deliberate.** The scene is drawn by
17
+ * shaders `naga` generated, every one of which ends its vertex stage with `gl_Position.y = -y`, and
18
+ * `CLIP_CORRECTION` carries a negation to cancel it — §3 rows 56 and 57. This shader is hand
19
+ * written and has no such negation, so rather than reason about halves of that pair per draw, the
20
+ * renderer multiplies the two together once a frame and passes the product. The vertex stage then
21
+ * has one job: put the position exactly where the scene put it, so the depth test compares like
22
+ * with like.
23
+ *
24
+ * **The motion itself is measured from the *unjittered* matrices**, which is the whole of §3 row
25
+ * 121: the history stands for a picture of the scene as it would have been drawn with no jitter, so
26
+ * a motion vector that carried the jitter difference would move every sample by a fraction of a
27
+ * pixel in a pattern that repeats with the jitter period — shimmer, diagnosed as anything but its
28
+ * cause.
29
+ */
30
+ /** Floats in the per-frame block: three matrices. */
31
+ export const MOTION_FRAME_FLOATS = 48;
32
+ /**
33
+ * Bytes a draw's own block takes.
34
+ *
35
+ * Two matrices is 128, and a dynamic uniform offset must be a multiple of 256 — the same alignment
36
+ * §3 row 50's neighbours are about. So each draw takes a whole 256 and half of it is padding, which
37
+ * is the cheaper half of the trade against a bind group per draw inside the frame loop.
38
+ */
39
+ export const MOTION_DRAW_STRIDE = 256;
40
+ /** Floats a draw actually writes inside that stride. */
41
+ export const MOTION_DRAW_FLOATS = 32;
42
+ /** The motion target's format. Half float holds a uv to a hundredth of a pixel and a view depth. */
43
+ export const MOTION_TARGET_FORMAT = 'rgba16float';
44
+ export const RECON_MOTION_WGSL = /* wgsl */ `
45
+ struct MotionFrame {
46
+ /* The scene's own clip transform, with the generated shaders' y negation already folded in. */
47
+ raster: mat4x4<f32>,
48
+ /* This frame's and last frame's view-projections, unjittered, in the matrices' own convention. */
49
+ viewProj: mat4x4<f32>,
50
+ previousViewProj: mat4x4<f32>,
51
+ }
52
+
53
+ struct MotionDraw {
54
+ model: mat4x4<f32>,
55
+ previousModel: mat4x4<f32>,
56
+ }
57
+
58
+ @group(0) @binding(0) var<uniform> frame: MotionFrame;
59
+ @group(0) @binding(1) var<uniform> draw: MotionDraw;
60
+
61
+ struct Varyings {
62
+ @builtin(position) clip: vec4<f32>,
63
+ /* Clip positions rather than uv: an interpolator divides by w for us, so dividing these by their
64
+ own interpolated w in the fragment stage is the perspective-correct answer at every pixel. */
65
+ @location(0) now: vec4<f32>,
66
+ @location(1) was: vec4<f32>,
67
+ }
68
+
69
+ @vertex
70
+ fn motionVert(@location(0) position: vec3<f32>) -> Varyings {
71
+ let world = draw.model * vec4<f32>(position, 1.0);
72
+ let wasWorld = draw.previousModel * vec4<f32>(position, 1.0);
73
+ var out: Varyings;
74
+ out.clip = frame.raster * world;
75
+ out.now = frame.viewProj * world;
76
+ out.was = frame.previousViewProj * wasWorld;
77
+ return out;
78
+ }
79
+
80
+ /* Normalised device coordinates to the uv the resolve measures in: v increases upward, as the
81
+ references' images do, and the accessor that reads this target is what flips the texture's rows. */
82
+ fn motionUv(clip: vec4<f32>) -> vec2<f32> {
83
+ return clip.xy / clip.w * 0.5 + vec2<f32>(0.5);
84
+ }
85
+
86
+ @fragment
87
+ fn motionFrag(in: Varyings) -> @location(0) vec4<f32> {
88
+ /*
89
+ * A surface behind last frame's eye has a w that is not positive, and there is no uv for it. The
90
+ * flag stays set: the disocclusion refuses a depth that is not positive, so the resolve drops the
91
+ * history rather than reading it from wherever a divide by a negative w would have pointed.
92
+ */
93
+ let offset = select(vec2<f32>(0.0), motionUv(in.was) - motionUv(in.now), in.was.w > 0.0);
94
+ return vec4<f32>(offset, in.was.w, 1.0);
95
+ }
96
+ `;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The parameters one resolve reads, as `ResolveFrame` minus its images.
3
+ *
4
+ * `hasHistory` is a `u32` because a uniform buffer has no `bool`. The three matrices are the
5
+ * unjittered ones, in the same convention as the depths the accessors return.
6
+ */
7
+ export declare const RECON_PARAMS_WGSL = "\nstruct ReconParams {\n renderSize: vec2<u32>,\n outputSize: vec2<u32>,\n jitter: vec2<f32>,\n previousJitter: vec2<f32>,\n inverseViewProj: mat4x4<f32>,\n previousViewProj: mat4x4<f32>,\n previousInverseViewProj: mat4x4<f32>,\n eye: vec4<f32>,\n previousEye: vec4<f32>,\n alpha: f32,\n sharpenAmount: f32,\n clampGamma: f32,\n hasHistory: u32,\n depthTolerance: f32,\n motionScale: f32,\n normalFloor: f32,\n normalCeiling: f32,\n}\n";
8
+ /** Floats in `ReconParams`, which is what a caller sizes its uniform buffer by. */
9
+ export declare const RECON_PARAM_FLOATS = 72;
10
+ /** `SAMPLE_SPREAD` and `CONFIDENT_WEIGHT`, which the reference exports and this must not restate. */
11
+ export declare const RECON_SAMPLE_SPREAD = 0.47;
12
+ export declare const RECON_CONFIDENT_WEIGHT = 1;
13
+ /** The workgroup edge both entry points dispatch over. */
14
+ export declare const RECON_WORKGROUP = 8;
15
+ /**
16
+ * What the histories and the shown picture are stored in.
17
+ *
18
+ * **Half float, and the alpha carries a weight rather than an opacity.** The gathered weight is
19
+ * capped at `(1 - alpha) / alpha` plus one frame's gathering — about twelve at the default — and
20
+ * eleven bits of mantissa hold that to a part in two thousand, which is far inside what the
21
+ * disocclusion smoothstep can tell apart. Filterable, which the five-tap Catmull-Rom needs and no
22
+ * 32-bit float format offers, and storage-capable, which the dispatch writing it needs.
23
+ */
24
+ export declare const RECON_HISTORY_FORMAT: GPUTextureFormat;
25
+ /**
26
+ * The arithmetic, as text. Defines `reconResolvePixel(ox, oy) -> vec4<f32>` — the next history's
27
+ * colour and the weight it has gathered — and `reconSharpenAt(ox, oy) -> vec3<f32>`, the picture
28
+ * shown, which reads the history the first has already written.
29
+ */
30
+ export declare const RECON_RESOLVE_CORE_WGSL = "\nconst RECON_SAMPLE_SPREAD: f32 = 0.47;\nconst RECON_CONFIDENT_WEIGHT: f32 = 1.0;\n\nfn reconClampTexel(value: i32, size: i32) -> i32 {\n return clamp(value, 0, size - 1);\n}\n\n/* JavaScript's Math.round, which is not WGSL's round: a half goes up, not to even. */\nfn reconRound(value: f32) -> f32 {\n return floor(value + 0.5);\n}\n\n/* Written out rather than the built-in, whose result is undefined where the edges are equal. */\nfn reconSmoothstep(edge0: f32, edge1: f32, x: f32) -> f32 {\n let t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);\n return t * t * (3.0 - 2.0 * t);\n}\n\nfn reconWeightAt(dx: f32, dy: f32) -> f32 {\n return exp(-(dx * dx + dy * dy) / (2.0 * RECON_SAMPLE_SPREAD * RECON_SAMPLE_SPREAD));\n}\n\nfn reconToYCoCg(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(\n c.r * 0.25 + c.g * 0.5 + c.b * 0.25,\n c.r * 0.5 - c.b * 0.5,\n -c.r * 0.25 + c.g * 0.5 - c.b * 0.25,\n );\n}\n\nfn reconFromYCoCg(c: vec3<f32>) -> vec3<f32> {\n let base = c.x - c.z;\n return vec3<f32>(base + c.y, c.x + c.z, base - c.y);\n}\n\nstruct ReconBox {\n low: vec3<f32>,\n high: vec3<f32>,\n}\n\n/*\n * Each channel's mean plus or minus gamma of its standard deviation \u2014 and a channel whose nine\n * samples all agree is that value exactly, because a mean of nine equal floats is not always one\n * of them and a box a rounding wide lets a history through by a rounding.\n */\nfn reconVarianceBox(samples: array<vec3<f32>, 9>, gamma: f32) -> ReconBox {\n var s = samples;\n var box: ReconBox;\n for (var c = 0u; c < 3u; c = c + 1u) {\n let first = s[0][c];\n var same = true;\n var sum = 0.0;\n for (var i = 0u; i < 9u; i = i + 1u) {\n let value = s[i][c];\n sum = sum + value;\n if (value != first) { same = false; }\n }\n if (same) {\n box.low[c] = first;\n box.high[c] = first;\n continue;\n }\n let mean = sum / 9.0;\n var spread = 0.0;\n for (var i = 0u; i < 9u; i = i + 1u) {\n let d = s[i][c] - mean;\n spread = spread + d * d;\n }\n let deviation = sqrt(spread / 9.0);\n box.low[c] = mean - gamma * deviation;\n box.high[c] = mean + gamma * deviation;\n }\n return box;\n}\n\n/*\n * The history pulled towards the box's centre until it is inside, by one ratio for all three\n * channels \u2014 a clamp per channel would land it on a corner and change its hue, which is coloured\n * fringing on every moving edge. A channel with no extent bounds anything that differs from it\n * infinitely, so such a history collapses onto the centre.\n */\nfn reconClipToBox(history: vec3<f32>, low: vec3<f32>, high: vec3<f32>) -> vec3<f32> {\n let centre = (low + high) * 0.5;\n var offset = history - centre;\n var extent = (high - low) * 0.5;\n var ratio = 0.0;\n var infinite = false;\n for (var c = 0u; c < 3u; c = c + 1u) {\n if (extent[c] > 1e-7) {\n ratio = max(ratio, abs(offset[c]) / extent[c]);\n } else if (abs(offset[c]) > 1e-7) {\n infinite = true;\n }\n }\n if (!infinite && !(ratio > 1.0)) { return history; }\n var scale = 0.0;\n if (!infinite) { scale = 1.0 / ratio; }\n return centre + offset * scale;\n}\n\nfn reconAccumulate(current: vec3<f32>, history: vec3<f32>, weight: f32, alpha: f32) -> vec3<f32> {\n let kept = clamp(weight, 0.0, 1.0) * (1.0 - clamp(alpha, 0.0, 1.0));\n return current * (1.0 - kept) + history * kept;\n}\n\nfn reconCatmullRom(t: f32) -> vec4<f32> {\n let t2 = t * t;\n let t3 = t2 * t;\n return vec4<f32>(\n -0.5 * t3 + t2 - 0.5 * t,\n 1.5 * t3 - 2.5 * t2 + 1.0,\n -1.5 * t3 + 2.0 * t2 + 0.5 * t,\n 0.5 * t3 - 0.5 * t2,\n );\n}\n\n/*\n * Five bilinear taps standing for the sixteen-texel Catmull-Rom: each axis's middle pair folded\n * into one tap placed between them, the corners dropped, the five weights renormalised. It is the\n * five-tap form because the taps are the expensive part \u2014 which is only true while the bilinear is\n * the sampler's, so the production module's accessor is a filtered sample and the check's is the\n * reference's own arithmetic over loaded texels.\n */\nstruct ReconTaps {\n at0: vec2<f32>,\n at1: vec2<f32>,\n at2: vec2<f32>,\n at3: vec2<f32>,\n at4: vec2<f32>,\n weights: array<f32, 5>,\n total: f32,\n}\n\nfn reconBicubicTaps(x: f32, y: f32) -> ReconTaps {\n let x1 = floor(x);\n let y1 = floor(y);\n let wx = reconCatmullRom(x - x1);\n let wy = reconCatmullRom(y - y1);\n let wx12 = wx[1] + wx[2];\n let wy12 = wy[1] + wy[2];\n let x12 = x1 + wx[2] / wx12;\n let y12 = y1 + wy[2] / wy12;\n var taps: ReconTaps;\n taps.at0 = vec2<f32>(x12, y1 - 1.0);\n taps.at1 = vec2<f32>(x1 - 1.0, y12);\n taps.at2 = vec2<f32>(x12, y12);\n taps.at3 = vec2<f32>(x1 + 2.0, y12);\n taps.at4 = vec2<f32>(x12, y1 + 2.0);\n taps.weights[0] = wx12 * wy[0];\n taps.weights[1] = wx[0] * wy12;\n taps.weights[2] = wx12 * wy12;\n taps.weights[3] = wx[3] * wy12;\n taps.weights[4] = wx12 * wy[3];\n taps.total =\n taps.weights[0] + taps.weights[1] + taps.weights[2] + taps.weights[3] + taps.weights[4];\n return taps;\n}\n\n/* A colour cannot be negative, and Catmull-Rom's negative lobes undershoot beside a bright edge. */\nfn reconSceneBicubic5(x: f32, y: f32) -> vec3<f32> {\n let taps = reconBicubicTaps(x, y);\n var sum = reconSceneBilinear(taps.at0) * taps.weights[0];\n sum = sum + reconSceneBilinear(taps.at1) * taps.weights[1];\n sum = sum + reconSceneBilinear(taps.at2) * taps.weights[2];\n sum = sum + reconSceneBilinear(taps.at3) * taps.weights[3];\n sum = sum + reconSceneBilinear(taps.at4) * taps.weights[4];\n return max(sum / taps.total, vec3<f32>(0.0));\n}\n\nfn reconHistoryBicubic5(x: f32, y: f32) -> vec4<f32> {\n let taps = reconBicubicTaps(x, y);\n var sum = reconHistoryBilinear(taps.at0) * taps.weights[0];\n sum = sum + reconHistoryBilinear(taps.at1) * taps.weights[1];\n sum = sum + reconHistoryBilinear(taps.at2) * taps.weights[2];\n sum = sum + reconHistoryBilinear(taps.at3) * taps.weights[3];\n sum = sum + reconHistoryBilinear(taps.at4) * taps.weights[4];\n return max(sum / taps.total, vec4<f32>(0.0));\n}\n\nstruct ReconWorld {\n ok: bool,\n position: vec3<f32>,\n}\n\n/* The world position a clip depth stands for, and nothing where the matrix has no answer there. */\nfn reconWorldFromDepth(m: mat4x4<f32>, u: f32, v: f32, clipDepth: f32) -> ReconWorld {\n var out: ReconWorld;\n out.position = vec3<f32>(0.0);\n out.ok = false;\n let x = u * 2.0 - 1.0;\n let y = v * 2.0 - 1.0;\n let w = m[0][3] * x + m[1][3] * y + m[2][3] * clipDepth + m[3][3];\n if (!(abs(w) > 1e-12)) { return out; }\n let p = m * vec4<f32>(x, y, clipDepth, 1.0);\n out.position = p.xyz / w;\n out.ok = all(abs(out.position) < vec3<f32>(3.4e38));\n return out;\n}\n\nstruct ReconNormal {\n ok: bool,\n normal: vec3<f32>,\n}\n\n/* Turned to face the eye, so the answer is the same whichever way the target's rows run. */\nfn reconNormalFrom(\n centre: vec3<f32>,\n right: vec3<f32>,\n up: vec3<f32>,\n eye: vec3<f32>,\n) -> ReconNormal {\n var out: ReconNormal;\n out.normal = vec3<f32>(0.0);\n out.ok = false;\n let a = right - centre;\n let b = up - centre;\n let n = cross(a, b);\n let length2 = dot(n, n);\n let scale2 = dot(a, a) * dot(b, b);\n if (!(length2 > 1e-12 * scale2)) { return out; }\n let toEye = dot(n, eye - centre);\n var sign = 1.0;\n if (toEye < 0.0) { sign = -1.0; }\n out.normal = n * (sign / sqrt(length2));\n out.ok = true;\n return out;\n}\n\nfn reconDisocclusion(\n currDepth: f32,\n histDepth: f32,\n motionLength: f32,\n normalDot: f32,\n) -> f32 {\n if (!(currDepth > 0.0) || !(histDepth > 0.0) || !(motionLength >= 0.0)) { return 0.0; }\n let disagreement = abs(currDepth - histDepth) / currDepth;\n let tolerance = recon.depthTolerance + recon.motionScale * motionLength;\n let depth = 1.0 - reconSmoothstep(tolerance, 2.0 * tolerance, disagreement);\n let normal = reconSmoothstep(recon.normalFloor, recon.normalCeiling, normalDot);\n return depth * normal;\n}\n\nstruct ReconAt {\n depth: f32,\n position: vec3<f32>,\n}\n\n/* A texel's world position at its unjittered uv, and the view depth it stands at, or zero. */\nfn reconWorldAt(previous: bool, x: i32, y: i32) -> ReconAt {\n let w = i32(recon.renderSize.x);\n let h = i32(recon.renderSize.y);\n let tx = reconClampTexel(x, w);\n let ty = reconClampTexel(y, h);\n var jitter = recon.jitter;\n var m = recon.inverseViewProj;\n var z = reconDepth(tx, ty);\n if (previous) {\n jitter = recon.previousJitter;\n m = recon.previousInverseViewProj;\n z = reconPreviousDepth(tx, ty);\n }\n let u = (f32(tx) + 0.5 - jitter.x) / f32(w);\n let v = (f32(ty) + 0.5 - jitter.y) / f32(h);\n var out: ReconAt;\n out.depth = 0.0;\n out.position = vec3<f32>(0.0);\n let world = reconWorldFromDepth(m, u, v, z);\n if (!world.ok) { return out; }\n out.position = world.position;\n /* The view depth is the clip w, which the inverse's w is the reciprocal of. */\n let cw = m[0][3] * (u * 2.0 - 1.0) + m[1][3] * (v * 2.0 - 1.0) + m[2][3] * z + m[3][3];\n out.depth = 1.0 / cw;\n return out;\n}\n\n/* The neighbour on the inside of the border, so the edge texels have a normal too. */\nfn reconNormalAt(previous: bool, x: i32, y: i32) -> ReconNormal {\n var out: ReconNormal;\n out.normal = vec3<f32>(0.0);\n out.ok = false;\n let w = i32(recon.renderSize.x);\n let h = i32(recon.renderSize.y);\n let centre = reconWorldAt(previous, x, y);\n if (!(centre.depth > 0.0)) { return out; }\n var dx = -1;\n if (x + 1 < w) { dx = 1; }\n var dy = -1;\n if (y + 1 < h) { dy = 1; }\n let right = reconWorldAt(previous, x + dx, y);\n if (!(right.depth > 0.0)) { return out; }\n let up = reconWorldAt(previous, x, y + dy);\n if (!(up.depth > 0.0)) { return out; }\n var eye = recon.eye.xyz;\n if (previous) { eye = recon.previousEye.xyz; }\n return reconNormalFrom(centre.position, right.position, up.position, eye);\n}\n\nstruct ReconHistory {\n weight: f32,\n colour: vec4<f32>,\n}\n\n/*\n * The weight a pixel's history still deserves: none on the first frame, none where the surface was\n * behind last frame's eye or off its picture, and otherwise what it gathered, scaled by whether it\n * is the same surface.\n */\nfn reconHistoryWeight(u: f32, v: f32, dx: i32, dy: i32) -> ReconHistory {\n var out: ReconHistory;\n out.weight = 0.0;\n out.colour = vec4<f32>(0.0);\n if (recon.hasHistory == 0u) { return out; }\n let rw = f32(recon.renderSize.x);\n let rh = f32(recon.renderSize.y);\n let motion = reconMotion(dx, dy);\n var mu = 0.0;\n var mv = 0.0;\n var expected = 0.0;\n if (motion.w > 0.5) {\n mu = motion.x;\n mv = motion.y;\n expected = motion.z;\n } else {\n let here = reconWorldAt(false, dx, dy);\n if (!(here.depth > 0.0)) { return out; }\n let clip = recon.previousViewProj * vec4<f32>(here.position, 1.0);\n /*\n * A surface behind last frame's eye has a w that is not positive: the disocclusion trusts no\n * such depth, so it needs no refusal of its own here.\n */\n expected = clip.w;\n let du = (f32(dx) + 0.5 - recon.jitter.x) / rw;\n let dv = (f32(dy) + 0.5 - recon.jitter.y) / rh;\n mu = (clip.x / expected) * 0.5 + 0.5 - du;\n mv = (clip.y / expected) * 0.5 + 0.5 - dv;\n }\n\n let pu = u + mu;\n let pv = v + mv;\n if (!(pu >= 0.0 && pu <= 1.0 && pv >= 0.0 && pv <= 1.0)) { return out; }\n out.colour = reconHistoryBicubic5(\n pu * f32(recon.outputSize.x) - 0.5,\n pv * f32(recon.outputSize.y) - 0.5,\n );\n let px = reconClampTexel(i32(reconRound(pu * rw + recon.previousJitter.x - 0.5)), i32(rw));\n let py = reconClampTexel(i32(reconRound(pv * rh + recon.previousJitter.y - 0.5)), i32(rh));\n let held = reconWorldAt(true, px, py).depth;\n var normalDot = 1.0;\n let here = reconNormalAt(false, dx, dy);\n let there = reconNormalAt(true, px, py);\n if (here.ok && there.ok) { normalDot = dot(here.normal, there.normal); }\n let trust = reconDisocclusion(expected, held, length(vec2<f32>(mu, mv)), normalDot);\n out.weight = out.colour.w * trust;\n return out;\n}\n\n/* One output pixel: the history's next colour, before sharpening, and the weight it has gathered. */\nfn reconResolvePixel(ox: i32, oy: i32) -> vec4<f32> {\n let rw = i32(recon.renderSize.x);\n let rh = i32(recon.renderSize.y);\n let ow = i32(recon.outputSize.x);\n let oh = i32(recon.outputSize.y);\n let u = (f32(ox) + 0.5) / f32(ow);\n let v = (f32(oy) + 0.5) / f32(oh);\n let jx = recon.jitter.x;\n let jy = recon.jitter.y;\n\n /* Where this frame observed the pixel, and the render read there for the gaps. */\n let rx = u * f32(rw) + jx - 0.5;\n let ry = v * f32(rh) + jy - 0.5;\n let sample = reconSceneBicubic5(rx, ry);\n let nx = reconClampTexel(i32(reconRound(rx)), rw);\n let ny = reconClampTexel(i32(reconRound(ry)), rh);\n\n /* Each texel around: its weight, its colour in the box, and its depth. */\n var nearest = reconDepth(nx, ny);\n var dx = nx;\n var dy = ny;\n var gathered = 0.0;\n var accum = vec3<f32>(0.0);\n var neighbours: array<vec3<f32>, 9>;\n for (var j = -1; j <= 1; j = j + 1) {\n for (var i = -1; i <= 1; i = i + 1) {\n let tx = reconClampTexel(nx + i, rw);\n let ty = reconClampTexel(ny + j, rh);\n let colour = reconScene(tx, ty);\n let weight = reconWeightAt(\n ((f32(tx) + 0.5 - jx) / f32(rw)) * f32(ow) - (f32(ox) + 0.5),\n ((f32(ty) + 0.5 - jy) / f32(rh)) * f32(oh) - (f32(oy) + 0.5),\n );\n gathered = gathered + weight;\n accum = accum + colour * weight;\n neighbours[(j + 1) * 3 + (i + 1)] = reconToYCoCg(colour);\n let z = reconDepth(tx, ty);\n if (z < nearest) {\n nearest = z;\n dx = tx;\n dy = ty;\n }\n }\n }\n let box = reconVarianceBox(neighbours, recon.clampGamma);\n accum = accum / gathered;\n\n /* The history, and the weight it still deserves; a tenth of each frame is always new. */\n let cap = (1.0 - recon.alpha) / recon.alpha;\n let history = reconHistoryWeight(u, v, dx, dy);\n let kept = min(cap, history.weight);\n\n let total = kept + gathered;\n var mixed = accum;\n if (kept > 0.0) {\n let clipped = reconClipToBox(reconToYCoCg(history.colour.xyz), box.low, box.high);\n mixed = reconAccumulate(accum, reconFromYCoCg(clipped), 1.0, gathered / total);\n }\n let known = min(1.0, total / RECON_CONFIDENT_WEIGHT);\n return vec4<f32>(sample * (1.0 - known) + mixed * known, total);\n}\n\n/*\n * The centre pushed away from its four neighbours' mean, and held inside the range of the five \u2014\n * the halo a plain unsharp mask draws beside every edge is a value outside that range.\n */\nfn reconSharpenAt(ox: i32, oy: i32) -> vec3<f32> {\n let ow = i32(recon.outputSize.x);\n let oh = i32(recon.outputSize.y);\n let centre = reconHistory(reconClampTexel(ox, ow), reconClampTexel(oy, oh)).xyz;\n var offsets = array<vec2<i32>, 4>(\n vec2<i32>(-1, 0),\n vec2<i32>(1, 0),\n vec2<i32>(0, -1),\n vec2<i32>(0, 1),\n );\n var low = centre;\n var high = centre;\n var sum = vec3<f32>(0.0);\n for (var n = 0; n < 4; n = n + 1) {\n let at = offsets[n];\n let value = reconHistory(\n reconClampTexel(ox + at.x, ow),\n reconClampTexel(oy + at.y, oh),\n ).xyz;\n sum = sum + value;\n low = min(low, value);\n high = max(high, value);\n }\n let pushed = centre + recon.sharpenAmount * (centre - sum / 4.0);\n return min(high, max(low, pushed));\n}\n";
31
+ /**
32
+ * The parity module: the core over plain float textures written in the reference's own row order,
33
+ * doing its own bilinear so the comparison is arithmetic rather than a tolerance on the device's
34
+ * sampler.
35
+ *
36
+ * Two entry points over one set of bindings. `resolveMain` writes four floats a pixel — the next
37
+ * history — and `sharpenMain` reads that history back at binding 4 and writes three.
38
+ */
39
+ export declare function reconResolveParityWgsl(): string;
40
+ /**
41
+ * The production module: the core over the renderer's own attachments.
42
+ *
43
+ * **Three things differ from the check, and all three are in the accessors.** The depth textures
44
+ * hold the renderer's reversed depth, which `wgslSceneDepthToNdc` turns into the sense the matrices
45
+ * were built for — the resolve picks the *nearest* of nine depths, and under reversed-Z the nearest
46
+ * is the largest, so an unconverted accessor would dilate the motion of the furthest surface at
47
+ * every silhouette. A texture's row 0 is the top one and the reference's images run from `v = 0`
48
+ * upward, so every accessor mirrors the row. And the bilinear reads are the sampler's, which is
49
+ * what makes the five-tap form cheaper than the sixteen.
50
+ */
51
+ export declare function reconResolveWgsl(): string;