@driftengine/core 3.63.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/CHANGELOG.json +111 -0
  2. package/NOTICE +20 -0
  3. package/README.md +1 -1
  4. package/dist/core/loop.js +18 -2
  5. package/dist/core/storage.js +5 -0
  6. package/dist/geometry/meshBuilder.d.ts +1 -1
  7. package/dist/geometry/meshBuilder.js +6 -2
  8. package/dist/index.d.ts +69 -4
  9. package/dist/index.js +112 -2
  10. package/dist/input/input.js +6 -1
  11. package/dist/math/exact.d.ts +13 -3
  12. package/dist/math/exact.js +68 -3
  13. package/dist/math/frustum.d.ts +17 -2
  14. package/dist/math/frustum.js +24 -0
  15. package/dist/render/backend/api.d.ts +34 -1
  16. package/dist/render/backend/api.js +22 -1
  17. package/dist/render/backend/budget.d.ts +7 -6
  18. package/dist/render/backend/budget.js +7 -6
  19. package/dist/render/backend/createRenderer.d.ts +57 -0
  20. package/dist/render/backend/createRenderer.js +51 -2
  21. package/dist/render/backend/materialChanges.d.ts +41 -0
  22. package/dist/render/backend/materialChanges.js +38 -0
  23. package/dist/render/backend/select.js +8 -0
  24. package/dist/render/backend/webgl2/renderer.d.ts +80 -1
  25. package/dist/render/backend/webgl2/renderer.js +208 -10
  26. package/dist/render/backend/webgpu/buffers.d.ts +20 -1
  27. package/dist/render/backend/webgpu/buffers.js +116 -58
  28. package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
  29. package/dist/render/backend/webgpu/fieldCompose.js +396 -0
  30. package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
  31. package/dist/render/backend/webgpu/giFieldPass.js +137 -0
  32. package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
  33. package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
  34. package/dist/render/backend/webgpu/linePass.d.ts +4 -14
  35. package/dist/render/backend/webgpu/linePass.js +4 -33
  36. package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
  37. package/dist/render/backend/webgpu/probeBake.js +493 -0
  38. package/dist/render/backend/webgpu/renderer.d.ts +379 -24
  39. package/dist/render/backend/webgpu/renderer.js +1369 -76
  40. package/dist/render/backend/webgpu/scatterPass.js +15 -1
  41. package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
  42. package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
  43. package/dist/render/causticsRenderer.d.ts +3 -1
  44. package/dist/render/causticsRenderer.js +4 -1
  45. package/dist/render/depthConvention.d.ts +12 -0
  46. package/dist/render/depthConvention.js +14 -0
  47. package/dist/render/deviceFeatures.d.ts +32 -0
  48. package/dist/render/deviceFeatures.js +41 -0
  49. package/dist/render/fog.d.ts +79 -0
  50. package/dist/render/fog.js +138 -0
  51. package/dist/render/frame/alias.d.ts +30 -0
  52. package/dist/render/frame/alias.js +61 -0
  53. package/dist/render/frame/deps.d.ts +37 -0
  54. package/dist/render/frame/deps.js +122 -0
  55. package/dist/render/frame/flushGraph.d.ts +43 -0
  56. package/dist/render/frame/flushGraph.js +84 -0
  57. package/dist/render/frame/graphSchedule.d.ts +49 -0
  58. package/dist/render/frame/graphSchedule.js +120 -0
  59. package/dist/render/frame/index.d.ts +15 -0
  60. package/dist/render/frame/index.js +8 -0
  61. package/dist/render/frame/lifetime.d.ts +24 -0
  62. package/dist/render/frame/lifetime.js +36 -0
  63. package/dist/render/frame/passTimings.d.ts +42 -0
  64. package/dist/render/frame/passTimings.js +38 -0
  65. package/dist/render/frame/validate.d.ts +15 -0
  66. package/dist/render/frame/validate.js +22 -0
  67. package/dist/render/frame/virtual.d.ts +44 -0
  68. package/dist/render/frame/virtual.js +61 -0
  69. package/dist/render/gi/chain.d.ts +82 -0
  70. package/dist/render/gi/chain.js +135 -0
  71. package/dist/render/gi/denoise.d.ts +114 -0
  72. package/dist/render/gi/denoise.js +169 -0
  73. package/dist/render/gi/fieldScene.d.ts +125 -0
  74. package/dist/render/gi/fieldScene.js +219 -0
  75. package/dist/render/gi/globalField.d.ts +157 -0
  76. package/dist/render/gi/globalField.js +373 -0
  77. package/dist/render/gi/probeGridFit.d.ts +24 -0
  78. package/dist/render/gi/probeGridFit.js +71 -0
  79. package/dist/render/gi/probeTrace.d.ts +82 -0
  80. package/dist/render/gi/probeTrace.js +128 -0
  81. package/dist/render/gi/probeVolume.d.ts +136 -0
  82. package/dist/render/gi/probeVolume.js +191 -0
  83. package/dist/render/gi/reflection.d.ts +73 -0
  84. package/dist/render/gi/reflection.js +109 -0
  85. package/dist/render/gi/traceField.d.ts +91 -0
  86. package/dist/render/gi/traceField.js +129 -0
  87. package/dist/render/gi/traceScreen.d.ts +68 -0
  88. package/dist/render/gi/traceScreen.js +82 -0
  89. package/dist/render/gpuScene.d.ts +38 -0
  90. package/dist/render/gpuScene.js +41 -0
  91. package/dist/render/gpudriven/alphaTest.d.ts +28 -0
  92. package/dist/render/gpudriven/alphaTest.js +32 -0
  93. package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
  94. package/dist/render/gpudriven/clusterUpload.js +75 -0
  95. package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
  96. package/dist/render/gpudriven/clusterWorld.js +155 -0
  97. package/dist/render/gpudriven/compact.d.ts +51 -0
  98. package/dist/render/gpudriven/compact.js +68 -0
  99. package/dist/render/gpudriven/cullClusters.d.ts +129 -0
  100. package/dist/render/gpudriven/cullClusters.js +237 -0
  101. package/dist/render/gpudriven/decodeTables.d.ts +64 -0
  102. package/dist/render/gpudriven/decodeTables.js +219 -0
  103. package/dist/render/gpudriven/frustum.d.ts +25 -0
  104. package/dist/render/gpudriven/frustum.js +59 -0
  105. package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
  106. package/dist/render/gpudriven/geometryHarness.js +31 -0
  107. package/dist/render/gpudriven/hzb.d.ts +60 -0
  108. package/dist/render/gpudriven/hzb.js +97 -0
  109. package/dist/render/gpudriven/ibl.d.ts +85 -0
  110. package/dist/render/gpudriven/ibl.js +129 -0
  111. package/dist/render/gpudriven/indirect.d.ts +67 -0
  112. package/dist/render/gpudriven/indirect.js +77 -0
  113. package/dist/render/gpudriven/instances.d.ts +43 -0
  114. package/dist/render/gpudriven/instances.js +172 -0
  115. package/dist/render/gpudriven/lit.d.ts +165 -0
  116. package/dist/render/gpudriven/lit.js +165 -0
  117. package/dist/render/gpudriven/lodCut.d.ts +28 -0
  118. package/dist/render/gpudriven/lodCut.js +38 -0
  119. package/dist/render/gpudriven/materialBin.d.ts +60 -0
  120. package/dist/render/gpudriven/materialBin.js +108 -0
  121. package/dist/render/gpudriven/materialTable.d.ts +88 -0
  122. package/dist/render/gpudriven/materialTable.js +115 -0
  123. package/dist/render/gpudriven/pipeline.d.ts +135 -0
  124. package/dist/render/gpudriven/pipeline.js +237 -0
  125. package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
  126. package/dist/render/gpudriven/rangeAlloc.js +112 -0
  127. package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
  128. package/dist/render/gpudriven/sceneUpload.js +115 -0
  129. package/dist/render/gpudriven/shadeBins.d.ts +92 -0
  130. package/dist/render/gpudriven/shadeBins.js +182 -0
  131. package/dist/render/gpudriven/shadow.d.ts +136 -0
  132. package/dist/render/gpudriven/shadow.js +224 -0
  133. package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
  134. package/dist/render/gpudriven/shadowCamera.js +345 -0
  135. package/dist/render/gpudriven/streamScene.d.ts +194 -0
  136. package/dist/render/gpudriven/streamScene.js +459 -0
  137. package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
  138. package/dist/render/gpudriven/surfaceFrame.js +51 -0
  139. package/dist/render/gpudriven/twoPhase.d.ts +122 -0
  140. package/dist/render/gpudriven/twoPhase.js +160 -0
  141. package/dist/render/gpudriven/visbuffer.d.ts +25 -0
  142. package/dist/render/gpudriven/visbuffer.js +37 -0
  143. package/dist/render/halfFloat.d.ts +10 -0
  144. package/dist/render/halfFloat.js +23 -4
  145. package/dist/render/inference/attentionTiled.d.ts +27 -0
  146. package/dist/render/inference/attentionTiled.js +117 -0
  147. package/dist/render/inference/denseKernels.d.ts +29 -0
  148. package/dist/render/inference/denseKernels.js +312 -0
  149. package/dist/render/inference/device.d.ts +21 -0
  150. package/dist/render/inference/device.js +32 -0
  151. package/dist/render/inference/deviceGraph.d.ts +34 -0
  152. package/dist/render/inference/deviceGraph.js +17 -0
  153. package/dist/render/inference/fuse.d.ts +15 -0
  154. package/dist/render/inference/fuse.js +37 -0
  155. package/dist/render/inference/kernelKit.d.ts +52 -0
  156. package/dist/render/inference/kernelKit.js +70 -0
  157. package/dist/render/inference/kernels.d.ts +3 -0
  158. package/dist/render/inference/kernels.js +11 -0
  159. package/dist/render/inference/plan.d.ts +13 -0
  160. package/dist/render/inference/plan.js +18 -0
  161. package/dist/render/inference/reuse.d.ts +39 -0
  162. package/dist/render/inference/reuse.js +65 -0
  163. package/dist/render/inference/runner.d.ts +31 -0
  164. package/dist/render/inference/runner.js +175 -0
  165. package/dist/render/inference/schedule.d.ts +39 -0
  166. package/dist/render/inference/schedule.js +59 -0
  167. package/dist/render/inference/shapeKernels.d.ts +10 -0
  168. package/dist/render/inference/shapeKernels.js +80 -0
  169. package/dist/render/inference/spatialKernels.d.ts +21 -0
  170. package/dist/render/inference/spatialKernels.js +179 -0
  171. package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
  172. package/dist/render/inference/splatRaster.wgsl.js +337 -0
  173. package/dist/render/inference/splatTiles.d.ts +79 -0
  174. package/dist/render/inference/splatTiles.js +161 -0
  175. package/dist/render/lineBatch.d.ts +2 -5
  176. package/dist/render/lineBatch.js +4 -24
  177. package/dist/render/mesh.d.ts +4 -0
  178. package/dist/render/mesh.js +45 -1
  179. package/dist/render/pass.d.ts +145 -8
  180. package/dist/render/prefilterEnvMap.d.ts +22 -0
  181. package/dist/render/prefilterEnvMap.js +31 -0
  182. package/dist/render/readback.d.ts +30 -0
  183. package/dist/render/readback.js +47 -0
  184. package/dist/render/recon/clamp.d.ts +36 -0
  185. package/dist/render/recon/clamp.js +92 -0
  186. package/dist/render/recon/disocclusion.d.ts +60 -0
  187. package/dist/render/recon/disocclusion.js +109 -0
  188. package/dist/render/recon/frameGen.d.ts +81 -0
  189. package/dist/render/recon/frameGen.js +150 -0
  190. package/dist/render/recon/frameSizes.d.ts +34 -0
  191. package/dist/render/recon/frameSizes.js +58 -0
  192. package/dist/render/recon/jitter.d.ts +60 -0
  193. package/dist/render/recon/jitter.js +118 -0
  194. package/dist/render/recon/motionVectors.d.ts +55 -0
  195. package/dist/render/recon/motionVectors.js +107 -0
  196. package/dist/render/recon/reproject.d.ts +41 -0
  197. package/dist/render/recon/reproject.js +131 -0
  198. package/dist/render/recon/resolve.d.ts +90 -0
  199. package/dist/render/recon/resolve.js +313 -0
  200. package/dist/render/recon/tier.d.ts +24 -0
  201. package/dist/render/recon/tier.js +8 -0
  202. package/dist/render/reflectionProbe.d.ts +26 -0
  203. package/dist/render/reflectionProbe.js +33 -0
  204. package/dist/render/renderQuality.d.ts +69 -0
  205. package/dist/render/renderQuality.js +22 -0
  206. package/dist/render/screenSpaceReflection.d.ts +23 -0
  207. package/dist/render/screenSpaceReflection.js +22 -3
  208. package/dist/render/sdfFont.js +2 -0
  209. package/dist/render/segmentQuads.d.ts +13 -0
  210. package/dist/render/segmentQuads.js +32 -0
  211. package/dist/render/shaders/flat/lobes.d.ts +27 -0
  212. package/dist/render/shaders/flat/lobes.js +33 -3
  213. package/dist/render/shaders/flat/preamble.d.ts +7 -0
  214. package/dist/render/shaders/flat/preamble.js +24 -1
  215. package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
  216. package/dist/render/shaders/flat/probeGrid.js +71 -2
  217. package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
  218. package/dist/render/shaders/generated/flat.wgsl.js +64 -24
  219. package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
  220. package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
  221. package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
  222. package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
  223. package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
  224. package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
  225. package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
  226. package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
  227. package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
  228. package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
  229. package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
  230. package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
  231. package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
  232. package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
  233. package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
  234. package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
  235. package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
  236. package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
  237. package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
  238. package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
  239. package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
  240. package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
  241. package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
  242. package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
  243. package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
  244. package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
  245. package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
  246. package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
  247. package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
  248. package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
  249. package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
  250. package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
  251. package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
  252. package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
  253. package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
  254. package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
  255. package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
  256. package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
  257. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
  258. package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
  259. package/dist/render/shaders/network.wgsl.d.ts +101 -0
  260. package/dist/render/shaders/network.wgsl.js +206 -0
  261. package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
  262. package/dist/render/shaders/recon/motion.wgsl.js +96 -0
  263. package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
  264. package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
  265. package/dist/render/temporalAa.js +8 -33
  266. package/dist/render/textLayout.js +12 -6
  267. package/dist/render/textRenderer.d.ts +3 -1
  268. package/dist/render/textRenderer.js +4 -1
  269. package/dist/render/translucentQueue.d.ts +6 -0
  270. package/dist/render/translucentQueue.js +11 -0
  271. package/dist/render/windStreakRenderer.d.ts +2 -1
  272. package/dist/render/windStreakRenderer.js +3 -1
  273. package/dist/world/cell.d.ts +63 -0
  274. package/dist/world/cell.js +181 -0
  275. package/dist/world/cellStream.d.ts +103 -0
  276. package/dist/world/cellStream.js +198 -0
  277. package/dist/world/freeze.d.ts +81 -0
  278. package/dist/world/freeze.js +114 -0
  279. package/dist/world/rebase.d.ts +29 -0
  280. package/dist/world/rebase.js +41 -0
  281. package/package.json +8 -3
  282. package/scripts/frames.mjs +92 -0
  283. package/scripts/gpuCompute.mjs +323 -0
  284. package/scripts/png.d.mts +16 -0
  285. package/scripts/png.mjs +25 -0
  286. package/src/core/loop.ts +19 -2
  287. package/src/core/storage.ts +5 -0
  288. package/src/geometry/meshBuilder.ts +5 -1
  289. package/src/index.ts +246 -4
  290. package/src/input/input.ts +6 -1
  291. package/src/math/exact.ts +69 -3
  292. package/src/math/frustum.ts +52 -3
  293. package/src/render/backend/api.ts +42 -1
  294. package/src/render/backend/budget.ts +7 -6
  295. package/src/render/backend/createRenderer.ts +92 -3
  296. package/src/render/backend/materialChanges.ts +54 -0
  297. package/src/render/backend/select.ts +8 -0
  298. package/src/render/backend/webgl2/renderer.ts +211 -9
  299. package/src/render/backend/webgpu/buffers.ts +182 -69
  300. package/src/render/backend/webgpu/fieldCompose.ts +482 -0
  301. package/src/render/backend/webgpu/giFieldPass.ts +200 -0
  302. package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
  303. package/src/render/backend/webgpu/linePass.ts +4 -39
  304. package/src/render/backend/webgpu/probeBake.ts +588 -0
  305. package/src/render/backend/webgpu/renderer.ts +1481 -76
  306. package/src/render/backend/webgpu/scatterPass.ts +15 -1
  307. package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
  308. package/src/render/causticsRenderer.ts +5 -2
  309. package/src/render/depthConvention.ts +15 -0
  310. package/src/render/deviceFeatures.ts +77 -0
  311. package/src/render/fog.ts +185 -0
  312. package/src/render/frame/alias.ts +97 -0
  313. package/src/render/frame/deps.ts +155 -0
  314. package/src/render/frame/flushGraph.ts +135 -0
  315. package/src/render/frame/graphSchedule.ts +170 -0
  316. package/src/render/frame/index.ts +52 -0
  317. package/src/render/frame/lifetime.ts +61 -0
  318. package/src/render/frame/passTimings.ts +69 -0
  319. package/src/render/frame/validate.ts +40 -0
  320. package/src/render/frame/virtual.ts +91 -0
  321. package/src/render/gi/chain.ts +225 -0
  322. package/src/render/gi/denoise.ts +276 -0
  323. package/src/render/gi/fieldScene.ts +264 -0
  324. package/src/render/gi/globalField.ts +543 -0
  325. package/src/render/gi/probeGridFit.ts +89 -0
  326. package/src/render/gi/probeTrace.ts +143 -0
  327. package/src/render/gi/probeVolume.ts +288 -0
  328. package/src/render/gi/reflection.ts +163 -0
  329. package/src/render/gi/traceField.ts +223 -0
  330. package/src/render/gi/traceScreen.ts +106 -0
  331. package/src/render/gpuScene.ts +80 -0
  332. package/src/render/gpudriven/alphaTest.ts +33 -0
  333. package/src/render/gpudriven/clusterUpload.ts +123 -0
  334. package/src/render/gpudriven/clusterWorld.ts +239 -0
  335. package/src/render/gpudriven/compact.ts +80 -0
  336. package/src/render/gpudriven/cullClusters.ts +333 -0
  337. package/src/render/gpudriven/decodeTables.ts +332 -0
  338. package/src/render/gpudriven/frustum.ts +70 -0
  339. package/src/render/gpudriven/geometryHarness.ts +51 -0
  340. package/src/render/gpudriven/hzb.ts +113 -0
  341. package/src/render/gpudriven/ibl.ts +147 -0
  342. package/src/render/gpudriven/indirect.ts +89 -0
  343. package/src/render/gpudriven/instances.ts +234 -0
  344. package/src/render/gpudriven/lit.ts +329 -0
  345. package/src/render/gpudriven/lodCut.ts +62 -0
  346. package/src/render/gpudriven/materialBin.ts +131 -0
  347. package/src/render/gpudriven/materialTable.ts +164 -0
  348. package/src/render/gpudriven/pipeline.ts +336 -0
  349. package/src/render/gpudriven/rangeAlloc.ts +122 -0
  350. package/src/render/gpudriven/sceneUpload.ts +173 -0
  351. package/src/render/gpudriven/shadeBins.ts +222 -0
  352. package/src/render/gpudriven/shadow.ts +285 -0
  353. package/src/render/gpudriven/shadowCamera.ts +406 -0
  354. package/src/render/gpudriven/streamScene.ts +575 -0
  355. package/src/render/gpudriven/surfaceFrame.ts +68 -0
  356. package/src/render/gpudriven/twoPhase.ts +214 -0
  357. package/src/render/gpudriven/visbuffer.ts +41 -0
  358. package/src/render/halfFloat.ts +22 -4
  359. package/src/render/inference/attentionTiled.ts +132 -0
  360. package/src/render/inference/denseKernels.ts +369 -0
  361. package/src/render/inference/device.ts +44 -0
  362. package/src/render/inference/deviceGraph.ts +39 -0
  363. package/src/render/inference/fuse.ts +47 -0
  364. package/src/render/inference/kernelKit.ts +120 -0
  365. package/src/render/inference/kernels.ts +18 -0
  366. package/src/render/inference/plan.ts +26 -0
  367. package/src/render/inference/reuse.ts +79 -0
  368. package/src/render/inference/runner.ts +204 -0
  369. package/src/render/inference/schedule.ts +80 -0
  370. package/src/render/inference/shapeKernels.ts +112 -0
  371. package/src/render/inference/spatialKernels.ts +220 -0
  372. package/src/render/inference/splatRaster.wgsl.ts +348 -0
  373. package/src/render/inference/splatTiles.ts +186 -0
  374. package/src/render/lineBatch.ts +4 -24
  375. package/src/render/mesh.ts +46 -1
  376. package/src/render/pass.ts +155 -10
  377. package/src/render/prefilterEnvMap.ts +33 -0
  378. package/src/render/readback.ts +73 -0
  379. package/src/render/recon/clamp.ts +113 -0
  380. package/src/render/recon/disocclusion.ts +143 -0
  381. package/src/render/recon/frameGen.ts +191 -0
  382. package/src/render/recon/frameSizes.ts +64 -0
  383. package/src/render/recon/jitter.ts +126 -0
  384. package/src/render/recon/motionVectors.ts +138 -0
  385. package/src/render/recon/reproject.ts +179 -0
  386. package/src/render/recon/resolve.ts +444 -0
  387. package/src/render/recon/tier.ts +31 -0
  388. package/src/render/reflectionProbe.ts +34 -0
  389. package/src/render/renderQuality.ts +87 -0
  390. package/src/render/screenSpaceReflection.ts +37 -3
  391. package/src/render/sdfFont.ts +2 -0
  392. package/src/render/segmentQuads.ts +39 -0
  393. package/src/render/shaders/flat/lobes.ts +35 -3
  394. package/src/render/shaders/flat/preamble.ts +25 -1
  395. package/src/render/shaders/flat/probeGrid.ts +77 -2
  396. package/src/render/shaders/generated/flat.wgsl.ts +64 -24
  397. package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
  398. package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
  399. package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
  400. package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
  401. package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
  402. package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
  403. package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
  404. package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
  405. package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
  406. package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
  407. package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
  408. package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
  409. package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
  410. package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
  411. package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
  412. package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
  413. package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
  414. package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
  415. package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
  416. package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
  417. package/src/render/shaders/network.wgsl.ts +238 -0
  418. package/src/render/shaders/recon/motion.wgsl.ts +101 -0
  419. package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
  420. package/src/render/temporalAa.ts +9 -33
  421. package/src/render/textLayout.ts +12 -6
  422. package/src/render/textRenderer.ts +5 -2
  423. package/src/render/translucentQueue.ts +11 -0
  424. package/src/render/windStreakRenderer.ts +4 -2
  425. package/src/world/cell.ts +231 -0
  426. package/src/world/cellStream.ts +278 -0
  427. package/src/world/freeze.ts +157 -0
  428. package/src/world/rebase.ts +56 -0
@@ -0,0 +1,369 @@
1
+ /**
2
+ * The dense operators on the device: projections, activations, normalisation, softmax, attention.
3
+ * The logistic is `1/(1 + exp(−x))` as written, which overflows to 0 far to the left, as it should.
4
+ *
5
+ * **`linear` is the engine's first kernel to use workgroup memory**: a 16 by 16 tile of the input
6
+ * and one of the weight are staged per step, so each value read from storage serves sixteen
7
+ * multiply-adds rather than one. Its sum runs in tile order, not the reference's index order, so it
8
+ * agrees with the reference to within a bound on its rounding rather than bit for bit — which
9
+ * `scripts/inference-parity.mjs` derives per output from the inputs, never samples.
10
+ *
11
+ * **Normalisation and softmax take one workgroup per row**, reducing through workgroup memory.
12
+ * **Attention takes one invocation per window, query and head**, over its own window's keys with any
13
+ * bias added to the scaled scores, and makes one pass over them with the running-maximum softmax: each new maximum rescales what has been gathered so far, so the scores
14
+ * are computed once and nothing of size tokens × tokens is ever stored. What it gives up is reuse:
15
+ * every invocation reads every key and value from storage. At a depth model's shapes that is 1.65
16
+ * ms of each 2.8 ms block (an RX 9070 XT, measured in the encoder), so attention is the first
17
+ * kernel to tile when a budget asks — keys and values staged in workgroup memory, read once a
18
+ * workgroup rather than once an invocation.
19
+ *
20
+ * **GELU is the exact one, through an `erf` accurate to 1.5e-7** (Abramowitz and Stegun 7.1.26) —
21
+ * the tanh approximation differs by 4e-4 and is a different network. The bound it costs is stated
22
+ * with the parity check.
23
+ */
24
+ import { tiledAttention, tiledAttentionFits } from './attentionTiled.ts';
25
+ import {
26
+ type KernelGenerator,
27
+ type KernelRequest,
28
+ float,
29
+ header,
30
+ num,
31
+ perElement,
32
+ perRow,
33
+ read,
34
+ size,
35
+ } from './kernelKit.ts';
36
+
37
+ /**
38
+ * The tile edge `linear` stages in workgroup memory, chosen by `tools/capture-weights/opbudget.ts`:
39
+ * at a depth model's shapes — 1,370 rows, 384 and 1,536 wide — 16 took 0.36 ms a multiply where 8
40
+ * took 0.40 on the two wide ones, and the two tied on the narrow one (an RX 9070 XT on Dawn,
41
+ * 2026-09-19). What would change it is a device that runs 256 invocations a workgroup badly: 8 is
42
+ * the other edge a device's default limit admits.
43
+ */
44
+ export const LINEAR_TILE = 16;
45
+
46
+ /**
47
+ * The tiled multiply at a given tile edge, which is a parameter only so the cost tool can measure
48
+ * the alternatives: a tile of `t` is `t × t` invocations, so 8 and 16 are the two a device's default
49
+ * limit of 256 admits.
50
+ *
51
+ * **A `gelu` activation is applied as the sum is written**, which is what `fuse.ts` asks for when a
52
+ * GELU is the only reader of a multiply: one dispatch and one buffer fewer, and the pre-activation
53
+ * value never reaches memory.
54
+ */
55
+ export function linearKernel(tile: number): KernelGenerator {
56
+ return (request) => {
57
+ const [rows, ins] = request.inputShapes[0] as [number, number];
58
+ const outs = (request.inputShapes[1] as readonly number[])[0] as number;
59
+ const bias = request.inputShapes.length > 2 ? ` + ${read(request, 2, 'col')}` : '';
60
+ const gelu = request.attributes['activation'] === 'gelu';
61
+ const value = gelu ? `geluExact(sum${bias})` : `sum${bias}`;
62
+ return {
63
+ code: `${header(request)}
64
+ ${gelu ? `\n${GELU}\n` : ''}
65
+ var<workgroup> tileA: array<f32, ${tile * tile}>;
66
+ var<workgroup> tileW: array<f32, ${tile * tile}>;
67
+
68
+ @compute @workgroup_size(${tile}, ${tile})
69
+ fn main(@builtin(workgroup_id) group: vec3<u32>, @builtin(local_invocation_id) local: vec3<u32>) {
70
+ let row = group.y * ${tile}u + local.y;
71
+ let col = group.x * ${tile}u + local.x;
72
+ let weightRow = group.x * ${tile}u + local.y;
73
+ var sum = 0.0;
74
+ for (var t = 0u; t < ${ins}u; t = t + ${tile}u) {
75
+ let a = t + local.x;
76
+ tileA[local.y * ${tile}u + local.x] = select(0.0, ${read(request, 0, `min(row * ${ins}u + a, ${rows * ins - 1}u)`)}, row < ${rows}u && a < ${ins}u);
77
+ tileW[local.x * ${tile}u + local.y] = select(0.0, ${read(request, 1, `min(weightRow * ${ins}u + a, ${outs * ins - 1}u)`)}, weightRow < ${outs}u && a < ${ins}u);
78
+ workgroupBarrier();
79
+ for (var k = 0u; k < ${tile}u; k = k + 1u) {
80
+ sum = sum + tileA[local.y * ${tile}u + k] * tileW[k * ${tile}u + local.x];
81
+ }
82
+ workgroupBarrier();
83
+ }
84
+ if (row < ${rows}u && col < ${outs}u) {
85
+ output[row * ${outs}u + col] = ${value};
86
+ }
87
+ }
88
+ `,
89
+ workgroups: [Math.ceil(outs / tile), Math.ceil(rows / tile), 1],
90
+ };
91
+ };
92
+ }
93
+
94
+ const unary =
95
+ (expression: (x: string) => string, prelude = ''): KernelGenerator =>
96
+ (request) => {
97
+ const body = ` let x = ${read(request, 0, 'i')};\n output[i] = ${expression('x')};`;
98
+ const source = perElement(request, size(request.outputShape), body);
99
+ return prelude === ''
100
+ ? source
101
+ : { ...source, code: source.code.replace('\n@compute', `\n${prelude}\n@compute`) };
102
+ };
103
+
104
+ const ERF = `fn erfApprox(x: f32) -> f32 {
105
+ let a = abs(x);
106
+ let t = 1.0 / (1.0 + 0.3275911 * a);
107
+ let y = 1.0 - (((((1.061405429 * t - 1.453152027) * t) + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * exp(-a * a);
108
+ return select(y, -y, x < 0.0);
109
+ }`;
110
+
111
+ /* The exact GELU over that erf, shared by the activation and a multiply it is fused into. */
112
+ const GELU = `${ERF}
113
+
114
+ fn geluExact(x: f32) -> f32 {
115
+ return 0.5 * x * (1.0 + erfApprox(x * 0.70710678118654752));
116
+ }`;
117
+
118
+ const binary =
119
+ (operator: string): KernelGenerator =>
120
+ (request) => {
121
+ const right = size(request.inputShapes[1] as readonly number[]);
122
+ const index = right === size(request.outputShape) ? 'i' : `i % ${right}u`;
123
+ return perElement(
124
+ request,
125
+ size(request.outputShape),
126
+ ` output[i] = ${read(request, 0, 'i')} ${operator} ${read(request, 1, index)};`,
127
+ );
128
+ };
129
+
130
+ /* A tree reduction of `partial` over the workgroup, leaving the total in `partial[0]`. */
131
+ function reduce(combine: string): string {
132
+ return ` workgroupBarrier();
133
+ for (var stride = 32u; stride > 0u; stride = stride / 2u) {
134
+ if (local.x < stride) {
135
+ partial[local.x] = ${combine.replace('A', 'partial[local.x]').replace('B', 'partial[local.x + stride]')};
136
+ }
137
+ workgroupBarrier();
138
+ }`;
139
+ }
140
+
141
+ function perRowKernel(
142
+ request: KernelRequest,
143
+ rows: number,
144
+ body: string,
145
+ ): { code: string; workgroups: [number, number, number] } {
146
+ const grid = perRow(rows);
147
+ return {
148
+ code: `${header(request)}
149
+
150
+ var<workgroup> partial: array<f32, 64>;
151
+
152
+ @compute @workgroup_size(64)
153
+ fn main(@builtin(workgroup_id) group: vec3<u32>, @builtin(local_invocation_id) local: vec3<u32>) {
154
+ let row = ${grid.row};
155
+ if (row >= ${rows}u) { return; }
156
+ ${body}
157
+ }
158
+ `,
159
+ workgroups: grid.workgroups,
160
+ };
161
+ }
162
+
163
+ const layerNorm: KernelGenerator = (request) => {
164
+ const [rows, cols] = request.inputShapes[0] as [number, number];
165
+ const epsilon = num(request.attributes, 'epsilon', 1e-6);
166
+ const x = (c: string): string => read(request, 0, `row * ${cols}u + ${c}`);
167
+ return perRowKernel(
168
+ request,
169
+ rows,
170
+ ` var sum = 0.0;
171
+ for (var c = local.x; c < ${cols}u; c = c + 64u) { sum = sum + ${x('c')}; }
172
+ partial[local.x] = sum;
173
+ ${reduce('A + B')}
174
+ let mean = partial[0] / ${float(cols)};
175
+ workgroupBarrier();
176
+ var squares = 0.0;
177
+ for (var c = local.x; c < ${cols}u; c = c + 64u) { let d = ${x('c')} - mean; squares = squares + d * d; }
178
+ partial[local.x] = squares;
179
+ ${reduce('A + B')}
180
+ let denominator = partial[0] / ${float(cols)} + ${float(epsilon)};
181
+ let scale = select(0.0, inverseSqrt(denominator), denominator > 0.0);
182
+ for (var c = local.x; c < ${cols}u; c = c + 64u) {
183
+ output[row * ${cols}u + c] = (${x('c')} - mean) * scale * ${read(request, 1, 'c')} + ${read(request, 2, 'c')};
184
+ }`,
185
+ );
186
+ };
187
+
188
+ const softmax: KernelGenerator = (request) => {
189
+ const shape = request.inputShapes[0] as readonly number[];
190
+ const cols = shape[shape.length - 1] as number;
191
+ const rows = size(shape) / cols;
192
+ const x = (c: string): string => read(request, 0, `row * ${cols}u + ${c}`);
193
+ return perRowKernel(
194
+ request,
195
+ rows,
196
+ ` var largest = -3.4028234e38;
197
+ for (var c = local.x; c < ${cols}u; c = c + 64u) { largest = max(largest, ${x('c')}); }
198
+ partial[local.x] = largest;
199
+ ${reduce('max(A, B)')}
200
+ let top = partial[0];
201
+ workgroupBarrier();
202
+ var total = 0.0;
203
+ for (var c = local.x; c < ${cols}u; c = c + 64u) { total = total + exp(${x('c')} - top); }
204
+ partial[local.x] = total;
205
+ ${reduce('A + B')}
206
+ let sum = partial[0];
207
+ for (var c = local.x; c < ${cols}u; c = c + 64u) {
208
+ output[row * ${cols}u + c] = exp(${x('c')} - top) / sum;
209
+ }`,
210
+ );
211
+ };
212
+
213
+ /*
214
+ * Attention, in one of three forms by how many queries and keys it has: the tiled form of
215
+ * `attentionTiled.ts` for many of both, and otherwise one of these two by how many keys each query
216
+ * reads.
217
+ *
218
+ * **Few keys: one invocation per window, query and head**, walking its window's keys with the
219
+ * running-maximum softmax. **Many keys: one workgroup per window, query and head**, its 64 threads
220
+ * each walking every 64th key the same way, then merging: the query's largest score found in a
221
+ * tree, each thread's total and values rescaled to it by `exp` of its own maximum less that, and
222
+ * summed in a tree. A query reading thousands of keys — a prompt's few tokens against an image —
223
+ * otherwise runs on as many invocations as it has tokens and heads, which is a few dozen on a
224
+ * device that wants tens of thousands.
225
+ *
226
+ * A bias is read at the head's, query's and key's score and added once scaled, in both.
227
+ */
228
+ export function attentionKernel(
229
+ splitKeys: number,
230
+ tiled: { readonly queries: number; readonly keys: number } | null = null,
231
+ ): KernelGenerator {
232
+ return (request) => {
233
+ const queryShape = request.inputShapes[0] as readonly number[];
234
+ const [queries, channels] = queryShape.slice(-2) as [number, number];
235
+ const keys = (request.inputShapes[1] as readonly number[]).at(-2) as number;
236
+ const batch = queryShape.length === 3 ? (queryShape[0] as number) : 1;
237
+ const heads = num(request.attributes, 'heads', 1);
238
+ const d = channels / heads;
239
+ if (tiled !== null && queries >= tiled.queries && keys >= tiled.keys && tiledAttentionFits(d)) {
240
+ return tiledAttention(request);
241
+ }
242
+ const at = (i: number, token: string, c: string): string =>
243
+ read(request, i, `${token} * ${channels}u + first + ${c}`);
244
+ const bias =
245
+ request.inputShapes.length > 3
246
+ ? ` + ${read(request, 3, `(head * ${queries}u + row % ${queries}u) * ${keys}u + key`)}`
247
+ : '';
248
+ const setup = ` let row = i / ${heads}u;
249
+ let head = i % ${heads}u;
250
+ let first = head * ${d}u;
251
+ let keyRow = (row / ${queries}u) * ${keys}u;
252
+ var largest = -3.4028234e38;
253
+ var total = 0.0;
254
+ var gathered: array<f32, ${d}>;`;
255
+ const walk = (
256
+ from: string,
257
+ step: string,
258
+ ): string => ` for (var key = ${from}; key < ${keys}u; key = key + ${step}) {
259
+ var dot = 0.0;
260
+ for (var c = 0u; c < ${d}u; c = c + 1u) { dot = dot + ${at(0, 'row', 'c')} * ${at(1, '(keyRow + key)', 'c')}; }
261
+ let score = dot * ${float(1 / Math.sqrt(d))}${bias};
262
+ let top = max(largest, score);
263
+ let rescale = exp(largest - top);
264
+ let weight = exp(score - top);
265
+ total = total * rescale + weight;
266
+ for (var c = 0u; c < ${d}u; c = c + 1u) { gathered[c] = gathered[c] * rescale + weight * ${at(2, '(keyRow + key)', 'c')}; }
267
+ largest = top;
268
+ }`;
269
+ if (keys < splitKeys) {
270
+ return perElement(
271
+ request,
272
+ batch * queries * heads,
273
+ `${setup}
274
+ ${walk('0u', '1u')}
275
+ for (var c = 0u; c < ${d}u; c = c + 1u) { output[row * ${channels}u + first + c] = gathered[c] / total; }`,
276
+ );
277
+ }
278
+ /*
279
+ * The merge: the workgroup's largest score first, then each thread's total and values rescaled
280
+ * to it once and summed in a tree — the values `CHUNK` channels at a time, so the workgroup's
281
+ * memory holds 64 threads' worth of one chunk however wide a head is.
282
+ */
283
+ const chunk = Math.ceil(d / Math.ceil(d / CHUNK));
284
+ const tree = (step: string): string => ` workgroupBarrier();
285
+ for (var stride = 32u; stride > 0u; stride = stride / 2u) {
286
+ if (local.x < stride) { ${step} }
287
+ workgroupBarrier();
288
+ }`;
289
+ const grid = perRow(batch * queries * heads);
290
+ return {
291
+ code: `${header(request)}
292
+
293
+ var<workgroup> tops: array<f32, 64>;
294
+ var<workgroup> totals: array<f32, 64>;
295
+ var<workgroup> sums: array<f32, ${64 * chunk}>;
296
+
297
+ @compute @workgroup_size(64)
298
+ fn main(@builtin(workgroup_id) group: vec3<u32>, @builtin(local_invocation_id) local: vec3<u32>) {
299
+ let i = ${grid.row};
300
+ if (i >= ${batch * queries * heads}u) { return; }
301
+ ${setup}
302
+ ${walk('local.x', '64u')}
303
+ tops[local.x] = largest;
304
+ ${tree('tops[local.x] = max(tops[local.x], tops[local.x + stride]);')}
305
+ let scale = exp(largest - tops[0]);
306
+ totals[local.x] = total * scale;
307
+ ${tree('totals[local.x] = totals[local.x] + totals[local.x + stride];')}
308
+ let sum = totals[0];
309
+ for (var part = 0u; part < ${d}u; part = part + ${chunk}u) {
310
+ for (var c = 0u; c < ${chunk}u; c = c + 1u) {
311
+ if (part + c < ${d}u) { sums[local.x * ${chunk}u + c] = gathered[part + c] * scale; }
312
+ }
313
+ ${tree(`for (var c = 0u; c < ${chunk}u; c = c + 1u) { sums[local.x * ${chunk}u + c] = sums[local.x * ${chunk}u + c] + sums[(local.x + stride) * ${chunk}u + c]; }`)}
314
+ if (local.x == 0u) {
315
+ for (var c = 0u; c < ${chunk}u; c = c + 1u) {
316
+ if (part + c < ${d}u) { output[row * ${channels}u + first + part + c] = sums[c] / sum; }
317
+ }
318
+ }
319
+ workgroupBarrier();
320
+ }
321
+ }
322
+ `,
323
+ workgroups: grid.workgroups,
324
+ };
325
+ };
326
+ }
327
+
328
+ /*
329
+ * The most channels merged at a time: 64 threads' worth of 60 is 15 KB of the workgroup's 16, with
330
+ * the maxima and totals beside them. A head is split into as few equal chunks as that allows.
331
+ */
332
+ const CHUNK = 60;
333
+
334
+ /*
335
+ * Where the split form starts, measured by `tools/capture-weights/opbudget.ts` on an RX 9070 XT:
336
+ * split, a prompt's 7 tokens over 4,096 keys went from 1.42 ms to 0.031, 1,024 tokens from 1.47
337
+ * to 0.98 and Depth Anything 3's 1,370 from 1.93 to 1.57 — and 512 tokens from 0.21 to 0.28, 256
338
+ * from 0.104 to 0.121, and TinyViT's windows of 49 from 0.070 to 5.5, where a workgroup's merge
339
+ * costs more than the keys it shares out. **What would move it** is a device whose invocations
340
+ * cost differently against its workgroup memory; the number is this one's.
341
+ *
342
+ * **The merge's own cost**: rescaling at every level of the tree instead, carrying whole heads, was
343
+ * 0.82 ms at 1,024 tokens where this is 0.98, and 1.48 at Depth Anything 3's shape where this is
344
+ * 1.57 — and could not hold a head wider than 62 channels, which a memory attention's 256 is.
345
+ */
346
+ export const ATTENTION_SPLIT_KEYS = 1024;
347
+
348
+ /*
349
+ * Where the tiled form (`attentionTiled.ts`) takes over from both, measured the same way: from 64
350
+ * queries and 128 keys. Tiled, 128 tokens in four heads of 64 went from 0.18 ms to 0.035, Depth
351
+ * Anything 3's 721 from 1.02 to 0.33 and its 1,370 from 1.5 to 1.0, OWLv2's 3,601 from 22 to 10.5,
352
+ * and a memory attention's 4,096 queries over 4,096 keys at a head of 256 from 57 to 12 — while a
353
+ * prompt's 7 tokens over 4,096 keys went from 0.030 to 0.59, 4,096 queries over 7 keys from 0.007
354
+ * to 0.015, and windows of 49 from 0.067 to 0.18, which the other two forms keep. Windows of 196
355
+ * are nearly even, 0.23 against 0.20.
356
+ */
357
+ export const ATTENTION_TILED = { queries: 64, keys: 128 } as const;
358
+
359
+ export const DENSE_KERNELS: readonly (readonly [string, KernelGenerator])[] = [
360
+ ['linear', linearKernel(LINEAR_TILE)],
361
+ ['relu', unary((x) => `max(${x}, 0.0)`)],
362
+ ['gelu', unary((x) => `geluExact(${x})`, GELU)],
363
+ ['sigmoid', unary((x) => `1.0 / (1.0 + exp(-${x}))`)],
364
+ ['add', binary('+')],
365
+ ['mul', binary('*')],
366
+ ['layerNorm', layerNorm],
367
+ ['softmax', softmax],
368
+ ['attention', attentionKernel(ATTENTION_SPLIT_KEYS, ATTENTION_TILED)],
369
+ ];
@@ -0,0 +1,44 @@
1
+ /**
2
+ * A device for running networks, opened from whichever `GPU` the caller has.
3
+ *
4
+ * **The `GPU` is a parameter**, so a browser's `navigator.gpu` and the native host's Dawn are the
5
+ * caller's choice, and nothing here reaches for a global. **Half precision is decided here, once**:
6
+ * asked for on an adapter without `shader-f16`, it is refused with the way out named, rather than
7
+ * failing to compile at the first graph far from the choice that caused it.
8
+ *
9
+ * The storage limits are raised to the adapter's own, because a network's values are large where a
10
+ * frame's are not — a transformer's feed-forward layer at a depth model's resolution is eight
11
+ * megabytes a value. What it gives up is nothing a network wants: those limits are ceilings on
12
+ * single buffers, and the default is a floor every adapter meets rather than what any one offers.
13
+ */
14
+
15
+ export interface InferenceDevice {
16
+ readonly device: GPUDevice;
17
+ /** Whether weights are stored at half precision; see `schedule.ts`. */
18
+ readonly half: boolean;
19
+ }
20
+
21
+ export async function openInferenceDevice(
22
+ gpu: GPU,
23
+ options: { readonly half: boolean },
24
+ ): Promise<InferenceDevice> {
25
+ const adapter = await gpu.requestAdapter({ powerPreference: 'high-performance' });
26
+ if (adapter === null) {
27
+ throw new Error('no WebGPU adapter was offered, so there is no device to run a network on');
28
+ }
29
+ if (options.half && !adapter.features.has('shader-f16')) {
30
+ throw new Error(
31
+ 'half precision was asked for and this adapter has no shader-f16, so a half-precision ' +
32
+ 'weight cannot be read; open the device with { half: false } to store weights at single ' +
33
+ 'precision instead',
34
+ );
35
+ }
36
+ const device = await adapter.requestDevice({
37
+ requiredFeatures: options.half ? ['shader-f16'] : [],
38
+ requiredLimits: {
39
+ maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
40
+ maxBufferSize: adapter.limits.maxBufferSize,
41
+ },
42
+ });
43
+ return { device, half: options.half };
44
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * A network graph as the device runs it: the structure `@driftengine/texture` validated, and the
3
+ * shape of every value it inferred.
4
+ *
5
+ * **Structural rather than imported**, because `@driftengine/core` does not depend on
6
+ * `@driftengine/texture` — texture peers on core, and the arrow cannot run both ways. A caller that
7
+ * holds both validates with texture and hands the result here; `DTEX` and the world's distance
8
+ * field cross the same boundary the same way. **The shapes are given, not inferred again**: a
9
+ * second copy of every operator's shape rule is the copy that drifts.
10
+ */
11
+
12
+ export type DeviceAttribute = number | string | boolean | readonly number[];
13
+
14
+ export interface DeviceGraphNode {
15
+ readonly op: string;
16
+ readonly inputs: readonly string[];
17
+ readonly output: string;
18
+ readonly attributes: Readonly<Record<string, DeviceAttribute>>;
19
+ }
20
+
21
+ export interface DeviceGraph {
22
+ readonly inputs: readonly { readonly name: string; readonly shape: readonly number[] }[];
23
+ readonly outputs: readonly string[];
24
+ readonly nodes: readonly DeviceGraphNode[];
25
+ readonly tensors: readonly {
26
+ readonly name: string;
27
+ readonly shape: readonly number[];
28
+ readonly data: Float32Array;
29
+ }[];
30
+ /** Every value's shape — inputs, tensors and intermediates — as texture's evaluator inferred it. */
31
+ readonly shapes: ReadonlyMap<string, readonly number[]>;
32
+ }
33
+
34
+ /** How many values a shape holds. */
35
+ export function shapeSize(shape: readonly number[]): number {
36
+ let total = 1;
37
+ for (const d of shape) total *= d;
38
+ return total;
39
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * A graph with each GELU folded into the multiply before it, where that is safe.
3
+ *
4
+ * **Safe means nothing else reads what the multiply wrote**: the fused kernel applies the GELU as
5
+ * it writes, so the pre-activation value never reaches memory, and a second reader of it — or a
6
+ * caller asking for it as an output — would read a buffer nobody filled. Where both hold, the
7
+ * multiply takes the GELU's output name and an `activation` attribute, and the GELU goes: one
8
+ * dispatch and one buffer fewer. A transformer's feed-forward block is exactly this shape, once per
9
+ * layer.
10
+ *
11
+ * The attribute is the device's own and never crosses back: `@driftengine/texture`'s references
12
+ * validate the graph before this runs, and nothing here is handed to them.
13
+ */
14
+ import type { DeviceGraph, DeviceGraphNode } from './deviceGraph.ts';
15
+
16
+ export function fuseActivations(graph: DeviceGraph): DeviceGraph {
17
+ const readers = new Map<string, number>();
18
+ for (const node of graph.nodes) {
19
+ for (const input of node.inputs) readers.set(input, (readers.get(input) ?? 0) + 1);
20
+ }
21
+ const outputs = new Set(graph.outputs);
22
+ const producer = new Map<string, number>();
23
+ graph.nodes.forEach((node, at) => producer.set(node.output, at));
24
+
25
+ const folded = new Set<number>();
26
+ const replaced = new Map<number, DeviceGraphNode>();
27
+ graph.nodes.forEach((node, at) => {
28
+ if (node.op !== 'gelu') return;
29
+ const source = node.inputs[0] as string;
30
+ const from = producer.get(source);
31
+ if (from === undefined) return;
32
+ const multiply = graph.nodes[from] as DeviceGraphNode;
33
+ if (multiply.op !== 'linear' || readers.get(source) !== 1 || outputs.has(source)) return;
34
+ replaced.set(from, {
35
+ ...multiply,
36
+ output: node.output,
37
+ attributes: { ...multiply.attributes, activation: 'gelu' },
38
+ });
39
+ folded.add(at);
40
+ });
41
+ if (folded.size === 0) return graph;
42
+ const nodes: DeviceGraphNode[] = [];
43
+ graph.nodes.forEach((node, at) => {
44
+ if (!folded.has(at)) nodes.push(replaced.get(at) ?? node);
45
+ });
46
+ return { ...graph, nodes };
47
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * What every device kernel shares: its request, its source, its bindings, and a per-element grid.
3
+ *
4
+ * **Every size is baked into the source as a constant**, because a graph's shapes are fixed when
5
+ * the runner is built: loops with constant bounds are what a compiler unrolls and schedules, and
6
+ * `networkFixedWgsl` measured the difference at nine to fourteen times against bounds read from a
7
+ * uniform. The cost is one pipeline per node, built once.
8
+ *
9
+ * **Inputs bind at 0 to n − 1, read-only, and the output at n, the only buffer written** — the order
10
+ * the runner builds its bind groups in and `scripts/inference-parity.mjs` binds its buffers in.
11
+ *
12
+ * **Half precision is a storage choice here, not an arithmetic one.** A weight stored as `f16` is
13
+ * read and widened, and every sum accumulates in `f32`: half the memory and bandwidth for the
14
+ * weights, and the reference's answer on half-rounded weights to single-precision tolerance. What
15
+ * it gives up is half-precision arithmetic's speed on devices that have it; measuring that against
16
+ * a model's error is the step after this one, not an assumption in it.
17
+ */
18
+ import type { DeviceAttribute } from './deviceGraph.ts';
19
+
20
+ export type ElementType = 'f32' | 'f16';
21
+
22
+ export interface KernelRequest {
23
+ readonly inputShapes: readonly (readonly number[])[];
24
+ readonly outputShape: readonly number[];
25
+ readonly attributes: Readonly<Record<string, DeviceAttribute>>;
26
+ /** What each input is stored as: `f16` only for a weight kept at half precision. */
27
+ readonly inputTypes: readonly ElementType[];
28
+ }
29
+
30
+ export interface KernelSource {
31
+ readonly code: string;
32
+ readonly workgroups: readonly [number, number, number];
33
+ }
34
+
35
+ export type KernelGenerator = (request: KernelRequest) => KernelSource;
36
+
37
+ /** Invocations a workgroup of a per-element kernel holds. */
38
+ export const GROUP = 64;
39
+ /** The most workgroups one dimension of a dispatch may ask for. */
40
+ const MAX_GROUPS = 65535;
41
+
42
+ export function num(
43
+ attributes: Readonly<Record<string, DeviceAttribute>>,
44
+ name: string,
45
+ fallback?: number,
46
+ ): number {
47
+ const value = attributes[name];
48
+ if (typeof value === 'number') return value;
49
+ if (fallback !== undefined) return fallback;
50
+ throw new RangeError(`attribute "${name}" must be a number`);
51
+ }
52
+
53
+ export function list(
54
+ attributes: Readonly<Record<string, DeviceAttribute>>,
55
+ name: string,
56
+ ): readonly number[] {
57
+ const value = attributes[name];
58
+ return Array.isArray(value) ? (value as readonly number[]) : [];
59
+ }
60
+
61
+ export function size(shape: readonly number[]): number {
62
+ let total = 1;
63
+ for (const d of shape) total *= d;
64
+ return total;
65
+ }
66
+
67
+ /** A WGSL float literal that is a float whatever the number is. */
68
+ export function float(value: number): string {
69
+ const text = value.toPrecision(12);
70
+ return text.includes('.') || text.includes('e') ? text : `${text}.0`;
71
+ }
72
+
73
+ /** The bindings, and `enable f16;` first when any input is stored at half precision. */
74
+ export function header(request: KernelRequest): string {
75
+ const half = request.inputTypes.includes('f16');
76
+ const lines: string[] = half ? ['enable f16;'] : [];
77
+ request.inputTypes.forEach((type, i) => {
78
+ lines.push(`@group(0) @binding(${i}) var<storage, read> input${i}: array<${type}>;`);
79
+ });
80
+ lines.push(
81
+ `@group(0) @binding(${request.inputTypes.length}) var<storage, read_write> output: array<f32>;`,
82
+ );
83
+ return lines.join('\n');
84
+ }
85
+
86
+ /** An expression reading input `i` at `index`, widened to `f32` where it is stored as `f16`. */
87
+ export function read(request: KernelRequest, i: number, index: string): string {
88
+ return request.inputTypes[i] === 'f16' ? `f32(input${i}[${index}])` : `input${i}[${index}]`;
89
+ }
90
+
91
+ /**
92
+ * A kernel with one invocation per element of its output, `count` of them, laid over as many
93
+ * dimensions of workgroups as the dispatch limit needs. `body` sees the element's index as `i`.
94
+ */
95
+ export function perElement(request: KernelRequest, count: number, body: string): KernelSource {
96
+ const groups = Math.max(1, Math.ceil(count / GROUP));
97
+ const across = Math.min(groups, MAX_GROUPS);
98
+ const down = Math.ceil(groups / across);
99
+ return {
100
+ code: `${header(request)}
101
+
102
+ @compute @workgroup_size(${GROUP})
103
+ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
104
+ let i = id.x + id.y * ${across * GROUP}u;
105
+ if (i >= ${count}u) { return; }
106
+ ${body}
107
+ }
108
+ `,
109
+ workgroups: [across, down, 1],
110
+ };
111
+ }
112
+
113
+ /** A grid of one workgroup per row, `rows` of them, across as many dimensions as it needs. */
114
+ export function perRow(rows: number): { workgroups: [number, number, number]; row: string } {
115
+ const across = Math.min(Math.max(1, rows), MAX_GROUPS);
116
+ return {
117
+ workgroups: [across, Math.ceil(Math.max(1, rows) / across), 1],
118
+ row: `group.x + group.y * ${across}u`,
119
+ };
120
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Every operator's device kernel, by the name `@driftengine/texture`'s operator table uses.
3
+ *
4
+ * **Two tables, one set of names**, because the references and the kernels live in packages that
5
+ * cannot import each other at run time. `kernels.test.ts` holds the two sets equal, since an
6
+ * operator in one and not the other is a graph that validates and then cannot run.
7
+ */
8
+ import { DENSE_KERNELS } from './denseKernels.ts';
9
+ import type { KernelGenerator } from './kernelKit.ts';
10
+ import { SHAPE_KERNELS } from './shapeKernels.ts';
11
+ import { SPATIAL_KERNELS } from './spatialKernels.ts';
12
+
13
+ export type { ElementType, KernelGenerator, KernelRequest, KernelSource } from './kernelKit.ts';
14
+
15
+ export const DEVICE_KERNELS: ReadonlyMap<string, KernelGenerator> = new Map<
16
+ string,
17
+ KernelGenerator
18
+ >([...DENSE_KERNELS, ...SPATIAL_KERNELS, ...SHAPE_KERNELS]);
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Which device buffer each value of a graph lives in, reusing a buffer once nothing will read it.
3
+ *
4
+ * **The rule is `@driftengine/texture`'s `planReuse`**, which the reference evaluator plans by too:
5
+ * one decision, so a buffer freed too early cannot mean one thing here and another there. This says
6
+ * what a device holds for the life of a runner — every input, every output and every weight — and
7
+ * measures a buffer in values.
8
+ */
9
+ import { planReuse, type BufferReuse } from './reuse.ts';
10
+
11
+ import { shapeSize, type DeviceGraph } from './deviceGraph.ts';
12
+
13
+ /** The buffer each value lives in, and each buffer's size in values. */
14
+ export type BufferPlan = BufferReuse;
15
+
16
+ export function planBuffers(graph: DeviceGraph): BufferPlan {
17
+ return planReuse(
18
+ {
19
+ nodes: graph.nodes,
20
+ /* An input is written before every run and a weight uploaded once: both keep a buffer. */
21
+ held: [...graph.inputs.map((input) => input.name), ...graph.tensors.map((t) => t.name)],
22
+ outputs: graph.outputs,
23
+ },
24
+ (name) => shapeSize(graph.shapes.get(name) ?? []),
25
+ );
26
+ }