@driftengine/core 3.62.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +122 -0
- package/NOTICE +20 -0
- package/README.md +1 -1
- package/dist/core/loop.js +18 -2
- package/dist/core/storage.js +5 -0
- package/dist/geometry/meshBuilder.d.ts +1 -1
- package/dist/geometry/meshBuilder.js +6 -2
- package/dist/index.d.ts +70 -4
- package/dist/index.js +123 -2
- package/dist/input/input.js +6 -1
- package/dist/math/exact.d.ts +13 -3
- package/dist/math/exact.js +68 -3
- package/dist/math/frustum.d.ts +17 -2
- package/dist/math/frustum.js +24 -0
- package/dist/render/backend/api.d.ts +34 -1
- package/dist/render/backend/api.js +22 -1
- package/dist/render/backend/budget.d.ts +7 -6
- package/dist/render/backend/budget.js +7 -6
- package/dist/render/backend/createRenderer.d.ts +57 -0
- package/dist/render/backend/createRenderer.js +51 -2
- package/dist/render/backend/materialChanges.d.ts +41 -0
- package/dist/render/backend/materialChanges.js +38 -0
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/renderer.d.ts +80 -1
- package/dist/render/backend/webgl2/renderer.js +208 -10
- package/dist/render/backend/webgpu/buffers.d.ts +20 -1
- package/dist/render/backend/webgpu/buffers.js +116 -58
- package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
- package/dist/render/backend/webgpu/fieldCompose.js +396 -0
- package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
- package/dist/render/backend/webgpu/giFieldPass.js +137 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
- package/dist/render/backend/webgpu/linePass.d.ts +4 -14
- package/dist/render/backend/webgpu/linePass.js +4 -33
- package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
- package/dist/render/backend/webgpu/probeBake.js +493 -0
- package/dist/render/backend/webgpu/renderer.d.ts +379 -24
- package/dist/render/backend/webgpu/renderer.js +1369 -76
- package/dist/render/backend/webgpu/scatterPass.js +15 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +47 -14
- package/dist/render/backend/webgpu/surfaceTexturePass.js +128 -80
- package/dist/render/causticsRenderer.d.ts +3 -1
- package/dist/render/causticsRenderer.js +4 -1
- package/dist/render/depthConvention.d.ts +12 -0
- package/dist/render/depthConvention.js +14 -0
- package/dist/render/deviceFeatures.d.ts +32 -0
- package/dist/render/deviceFeatures.js +41 -0
- package/dist/render/fog.d.ts +79 -0
- package/dist/render/fog.js +138 -0
- package/dist/render/frame/alias.d.ts +30 -0
- package/dist/render/frame/alias.js +61 -0
- package/dist/render/frame/deps.d.ts +37 -0
- package/dist/render/frame/deps.js +122 -0
- package/dist/render/frame/flushGraph.d.ts +43 -0
- package/dist/render/frame/flushGraph.js +84 -0
- package/dist/render/frame/graphSchedule.d.ts +49 -0
- package/dist/render/frame/graphSchedule.js +120 -0
- package/dist/render/frame/index.d.ts +15 -0
- package/dist/render/frame/index.js +8 -0
- package/dist/render/frame/lifetime.d.ts +24 -0
- package/dist/render/frame/lifetime.js +36 -0
- package/dist/render/frame/passTimings.d.ts +42 -0
- package/dist/render/frame/passTimings.js +38 -0
- package/dist/render/frame/validate.d.ts +15 -0
- package/dist/render/frame/validate.js +22 -0
- package/dist/render/frame/virtual.d.ts +44 -0
- package/dist/render/frame/virtual.js +61 -0
- package/dist/render/gi/chain.d.ts +82 -0
- package/dist/render/gi/chain.js +135 -0
- package/dist/render/gi/denoise.d.ts +114 -0
- package/dist/render/gi/denoise.js +169 -0
- package/dist/render/gi/fieldScene.d.ts +125 -0
- package/dist/render/gi/fieldScene.js +219 -0
- package/dist/render/gi/globalField.d.ts +157 -0
- package/dist/render/gi/globalField.js +373 -0
- package/dist/render/gi/probeGridFit.d.ts +24 -0
- package/dist/render/gi/probeGridFit.js +71 -0
- package/dist/render/gi/probeTrace.d.ts +82 -0
- package/dist/render/gi/probeTrace.js +128 -0
- package/dist/render/gi/probeVolume.d.ts +136 -0
- package/dist/render/gi/probeVolume.js +191 -0
- package/dist/render/gi/reflection.d.ts +73 -0
- package/dist/render/gi/reflection.js +109 -0
- package/dist/render/gi/traceField.d.ts +91 -0
- package/dist/render/gi/traceField.js +129 -0
- package/dist/render/gi/traceScreen.d.ts +68 -0
- package/dist/render/gi/traceScreen.js +82 -0
- package/dist/render/gpuScene.d.ts +38 -0
- package/dist/render/gpuScene.js +41 -0
- package/dist/render/gpudriven/alphaTest.d.ts +28 -0
- package/dist/render/gpudriven/alphaTest.js +32 -0
- package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
- package/dist/render/gpudriven/clusterUpload.js +75 -0
- package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
- package/dist/render/gpudriven/clusterWorld.js +155 -0
- package/dist/render/gpudriven/compact.d.ts +51 -0
- package/dist/render/gpudriven/compact.js +68 -0
- package/dist/render/gpudriven/cullClusters.d.ts +129 -0
- package/dist/render/gpudriven/cullClusters.js +237 -0
- package/dist/render/gpudriven/decodeTables.d.ts +64 -0
- package/dist/render/gpudriven/decodeTables.js +219 -0
- package/dist/render/gpudriven/frustum.d.ts +25 -0
- package/dist/render/gpudriven/frustum.js +59 -0
- package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
- package/dist/render/gpudriven/geometryHarness.js +31 -0
- package/dist/render/gpudriven/hzb.d.ts +60 -0
- package/dist/render/gpudriven/hzb.js +97 -0
- package/dist/render/gpudriven/ibl.d.ts +85 -0
- package/dist/render/gpudriven/ibl.js +129 -0
- package/dist/render/gpudriven/indirect.d.ts +67 -0
- package/dist/render/gpudriven/indirect.js +77 -0
- package/dist/render/gpudriven/instances.d.ts +43 -0
- package/dist/render/gpudriven/instances.js +172 -0
- package/dist/render/gpudriven/lit.d.ts +165 -0
- package/dist/render/gpudriven/lit.js +165 -0
- package/dist/render/gpudriven/lodCut.d.ts +28 -0
- package/dist/render/gpudriven/lodCut.js +38 -0
- package/dist/render/gpudriven/materialBin.d.ts +60 -0
- package/dist/render/gpudriven/materialBin.js +108 -0
- package/dist/render/gpudriven/materialTable.d.ts +88 -0
- package/dist/render/gpudriven/materialTable.js +115 -0
- package/dist/render/gpudriven/pipeline.d.ts +135 -0
- package/dist/render/gpudriven/pipeline.js +237 -0
- package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
- package/dist/render/gpudriven/rangeAlloc.js +112 -0
- package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
- package/dist/render/gpudriven/sceneUpload.js +115 -0
- package/dist/render/gpudriven/shadeBins.d.ts +92 -0
- package/dist/render/gpudriven/shadeBins.js +182 -0
- package/dist/render/gpudriven/shadow.d.ts +136 -0
- package/dist/render/gpudriven/shadow.js +224 -0
- package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
- package/dist/render/gpudriven/shadowCamera.js +345 -0
- package/dist/render/gpudriven/streamScene.d.ts +194 -0
- package/dist/render/gpudriven/streamScene.js +459 -0
- package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
- package/dist/render/gpudriven/surfaceFrame.js +51 -0
- package/dist/render/gpudriven/twoPhase.d.ts +122 -0
- package/dist/render/gpudriven/twoPhase.js +160 -0
- package/dist/render/gpudriven/visbuffer.d.ts +25 -0
- package/dist/render/gpudriven/visbuffer.js +37 -0
- package/dist/render/halfFloat.d.ts +10 -0
- package/dist/render/halfFloat.js +23 -4
- package/dist/render/inference/attentionTiled.d.ts +27 -0
- package/dist/render/inference/attentionTiled.js +117 -0
- package/dist/render/inference/denseKernels.d.ts +29 -0
- package/dist/render/inference/denseKernels.js +312 -0
- package/dist/render/inference/device.d.ts +21 -0
- package/dist/render/inference/device.js +32 -0
- package/dist/render/inference/deviceGraph.d.ts +34 -0
- package/dist/render/inference/deviceGraph.js +17 -0
- package/dist/render/inference/fuse.d.ts +15 -0
- package/dist/render/inference/fuse.js +37 -0
- package/dist/render/inference/kernelKit.d.ts +52 -0
- package/dist/render/inference/kernelKit.js +70 -0
- package/dist/render/inference/kernels.d.ts +3 -0
- package/dist/render/inference/kernels.js +11 -0
- package/dist/render/inference/plan.d.ts +13 -0
- package/dist/render/inference/plan.js +18 -0
- package/dist/render/inference/reuse.d.ts +39 -0
- package/dist/render/inference/reuse.js +65 -0
- package/dist/render/inference/runner.d.ts +31 -0
- package/dist/render/inference/runner.js +175 -0
- package/dist/render/inference/schedule.d.ts +39 -0
- package/dist/render/inference/schedule.js +59 -0
- package/dist/render/inference/shapeKernels.d.ts +10 -0
- package/dist/render/inference/shapeKernels.js +80 -0
- package/dist/render/inference/spatialKernels.d.ts +21 -0
- package/dist/render/inference/spatialKernels.js +179 -0
- package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
- package/dist/render/inference/splatRaster.wgsl.js +337 -0
- package/dist/render/inference/splatTiles.d.ts +79 -0
- package/dist/render/inference/splatTiles.js +161 -0
- package/dist/render/lineBatch.d.ts +2 -5
- package/dist/render/lineBatch.js +4 -24
- package/dist/render/mesh.d.ts +4 -0
- package/dist/render/mesh.js +45 -1
- package/dist/render/pass.d.ts +145 -8
- package/dist/render/prefilterEnvMap.d.ts +22 -0
- package/dist/render/prefilterEnvMap.js +31 -0
- package/dist/render/readback.d.ts +30 -0
- package/dist/render/readback.js +47 -0
- package/dist/render/recon/clamp.d.ts +36 -0
- package/dist/render/recon/clamp.js +92 -0
- package/dist/render/recon/disocclusion.d.ts +60 -0
- package/dist/render/recon/disocclusion.js +109 -0
- package/dist/render/recon/frameGen.d.ts +81 -0
- package/dist/render/recon/frameGen.js +150 -0
- package/dist/render/recon/frameSizes.d.ts +34 -0
- package/dist/render/recon/frameSizes.js +58 -0
- package/dist/render/recon/jitter.d.ts +60 -0
- package/dist/render/recon/jitter.js +118 -0
- package/dist/render/recon/motionVectors.d.ts +55 -0
- package/dist/render/recon/motionVectors.js +107 -0
- package/dist/render/recon/reproject.d.ts +41 -0
- package/dist/render/recon/reproject.js +131 -0
- package/dist/render/recon/resolve.d.ts +90 -0
- package/dist/render/recon/resolve.js +313 -0
- package/dist/render/recon/tier.d.ts +24 -0
- package/dist/render/recon/tier.js +8 -0
- package/dist/render/reflectionProbe.d.ts +26 -0
- package/dist/render/reflectionProbe.js +33 -0
- package/dist/render/renderQuality.d.ts +69 -0
- package/dist/render/renderQuality.js +22 -0
- package/dist/render/screenSpaceReflection.d.ts +23 -0
- package/dist/render/screenSpaceReflection.js +22 -3
- package/dist/render/sdfFont.js +2 -0
- package/dist/render/segmentQuads.d.ts +13 -0
- package/dist/render/segmentQuads.js +32 -0
- package/dist/render/shaders/flat/lobes.d.ts +27 -0
- package/dist/render/shaders/flat/lobes.js +33 -3
- package/dist/render/shaders/flat/preamble.d.ts +7 -0
- package/dist/render/shaders/flat/preamble.js +24 -1
- package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
- package/dist/render/shaders/flat/probeGrid.js +71 -2
- package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
- package/dist/render/shaders/generated/flat.wgsl.js +64 -24
- package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
- package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
- package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
- package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
- package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
- package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
- package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
- package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
- package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
- package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
- package/dist/render/shaders/network.wgsl.d.ts +101 -0
- package/dist/render/shaders/network.wgsl.js +206 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
- package/dist/render/shaders/recon/motion.wgsl.js +96 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
- package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
- package/dist/render/temporalAa.js +8 -33
- package/dist/render/textLayout.js +12 -6
- package/dist/render/textRenderer.d.ts +3 -1
- package/dist/render/textRenderer.js +4 -1
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +11 -0
- package/dist/render/windStreakRenderer.d.ts +2 -1
- package/dist/render/windStreakRenderer.js +3 -1
- package/dist/world/cell.d.ts +63 -0
- package/dist/world/cell.js +181 -0
- package/dist/world/cellStream.d.ts +103 -0
- package/dist/world/cellStream.js +198 -0
- package/dist/world/freeze.d.ts +81 -0
- package/dist/world/freeze.js +114 -0
- package/dist/world/rebase.d.ts +29 -0
- package/dist/world/rebase.js +41 -0
- package/package.json +8 -3
- package/scripts/frames.mjs +92 -0
- package/scripts/gpuCompute.mjs +323 -0
- package/scripts/png.d.mts +16 -0
- package/scripts/png.mjs +25 -0
- package/src/core/loop.ts +19 -2
- package/src/core/storage.ts +5 -0
- package/src/geometry/meshBuilder.ts +5 -1
- package/src/index.ts +261 -4
- package/src/input/input.ts +6 -1
- package/src/math/exact.ts +69 -3
- package/src/math/frustum.ts +52 -3
- package/src/render/backend/api.ts +42 -1
- package/src/render/backend/budget.ts +7 -6
- package/src/render/backend/createRenderer.ts +92 -3
- package/src/render/backend/materialChanges.ts +54 -0
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/renderer.ts +211 -9
- package/src/render/backend/webgpu/buffers.ts +182 -69
- package/src/render/backend/webgpu/fieldCompose.ts +482 -0
- package/src/render/backend/webgpu/giFieldPass.ts +200 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
- package/src/render/backend/webgpu/linePass.ts +4 -39
- package/src/render/backend/webgpu/probeBake.ts +588 -0
- package/src/render/backend/webgpu/renderer.ts +1481 -76
- package/src/render/backend/webgpu/scatterPass.ts +15 -1
- package/src/render/backend/webgpu/surfaceTexturePass.ts +158 -88
- package/src/render/causticsRenderer.ts +5 -2
- package/src/render/depthConvention.ts +15 -0
- package/src/render/deviceFeatures.ts +77 -0
- package/src/render/fog.ts +185 -0
- package/src/render/frame/alias.ts +97 -0
- package/src/render/frame/deps.ts +155 -0
- package/src/render/frame/flushGraph.ts +135 -0
- package/src/render/frame/graphSchedule.ts +170 -0
- package/src/render/frame/index.ts +52 -0
- package/src/render/frame/lifetime.ts +61 -0
- package/src/render/frame/passTimings.ts +69 -0
- package/src/render/frame/validate.ts +40 -0
- package/src/render/frame/virtual.ts +91 -0
- package/src/render/gi/chain.ts +225 -0
- package/src/render/gi/denoise.ts +276 -0
- package/src/render/gi/fieldScene.ts +264 -0
- package/src/render/gi/globalField.ts +543 -0
- package/src/render/gi/probeGridFit.ts +89 -0
- package/src/render/gi/probeTrace.ts +143 -0
- package/src/render/gi/probeVolume.ts +288 -0
- package/src/render/gi/reflection.ts +163 -0
- package/src/render/gi/traceField.ts +223 -0
- package/src/render/gi/traceScreen.ts +106 -0
- package/src/render/gpuScene.ts +80 -0
- package/src/render/gpudriven/alphaTest.ts +33 -0
- package/src/render/gpudriven/clusterUpload.ts +123 -0
- package/src/render/gpudriven/clusterWorld.ts +239 -0
- package/src/render/gpudriven/compact.ts +80 -0
- package/src/render/gpudriven/cullClusters.ts +333 -0
- package/src/render/gpudriven/decodeTables.ts +332 -0
- package/src/render/gpudriven/frustum.ts +70 -0
- package/src/render/gpudriven/geometryHarness.ts +51 -0
- package/src/render/gpudriven/hzb.ts +113 -0
- package/src/render/gpudriven/ibl.ts +147 -0
- package/src/render/gpudriven/indirect.ts +89 -0
- package/src/render/gpudriven/instances.ts +234 -0
- package/src/render/gpudriven/lit.ts +329 -0
- package/src/render/gpudriven/lodCut.ts +62 -0
- package/src/render/gpudriven/materialBin.ts +131 -0
- package/src/render/gpudriven/materialTable.ts +164 -0
- package/src/render/gpudriven/pipeline.ts +336 -0
- package/src/render/gpudriven/rangeAlloc.ts +122 -0
- package/src/render/gpudriven/sceneUpload.ts +173 -0
- package/src/render/gpudriven/shadeBins.ts +222 -0
- package/src/render/gpudriven/shadow.ts +285 -0
- package/src/render/gpudriven/shadowCamera.ts +406 -0
- package/src/render/gpudriven/streamScene.ts +575 -0
- package/src/render/gpudriven/surfaceFrame.ts +68 -0
- package/src/render/gpudriven/twoPhase.ts +214 -0
- package/src/render/gpudriven/visbuffer.ts +41 -0
- package/src/render/halfFloat.ts +22 -4
- package/src/render/inference/attentionTiled.ts +132 -0
- package/src/render/inference/denseKernels.ts +369 -0
- package/src/render/inference/device.ts +44 -0
- package/src/render/inference/deviceGraph.ts +39 -0
- package/src/render/inference/fuse.ts +47 -0
- package/src/render/inference/kernelKit.ts +120 -0
- package/src/render/inference/kernels.ts +18 -0
- package/src/render/inference/plan.ts +26 -0
- package/src/render/inference/reuse.ts +79 -0
- package/src/render/inference/runner.ts +204 -0
- package/src/render/inference/schedule.ts +80 -0
- package/src/render/inference/shapeKernels.ts +112 -0
- package/src/render/inference/spatialKernels.ts +220 -0
- package/src/render/inference/splatRaster.wgsl.ts +348 -0
- package/src/render/inference/splatTiles.ts +186 -0
- package/src/render/lineBatch.ts +4 -24
- package/src/render/mesh.ts +46 -1
- package/src/render/pass.ts +155 -10
- package/src/render/prefilterEnvMap.ts +33 -0
- package/src/render/readback.ts +73 -0
- package/src/render/recon/clamp.ts +113 -0
- package/src/render/recon/disocclusion.ts +143 -0
- package/src/render/recon/frameGen.ts +191 -0
- package/src/render/recon/frameSizes.ts +64 -0
- package/src/render/recon/jitter.ts +126 -0
- package/src/render/recon/motionVectors.ts +138 -0
- package/src/render/recon/reproject.ts +179 -0
- package/src/render/recon/resolve.ts +444 -0
- package/src/render/recon/tier.ts +31 -0
- package/src/render/reflectionProbe.ts +34 -0
- package/src/render/renderQuality.ts +87 -0
- package/src/render/screenSpaceReflection.ts +37 -3
- package/src/render/sdfFont.ts +2 -0
- package/src/render/segmentQuads.ts +39 -0
- package/src/render/shaders/flat/lobes.ts +35 -3
- package/src/render/shaders/flat/preamble.ts +25 -1
- package/src/render/shaders/flat/probeGrid.ts +77 -2
- package/src/render/shaders/generated/flat.wgsl.ts +64 -24
- package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
- package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
- package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
- package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
- package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
- package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
- package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
- package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
- package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
- package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
- package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
- package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
- package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
- package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
- package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
- package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
- package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
- package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
- package/src/render/shaders/network.wgsl.ts +238 -0
- package/src/render/shaders/recon/motion.wgsl.ts +101 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
- package/src/render/temporalAa.ts +9 -33
- package/src/render/textLayout.ts +12 -6
- package/src/render/textRenderer.ts +5 -2
- package/src/render/translucentQueue.ts +11 -0
- package/src/render/windStreakRenderer.ts +4 -2
- package/src/world/cell.ts +231 -0
- package/src/world/cellStream.ts +278 -0
- package/src/world/freeze.ts +157 -0
- package/src/world/rebase.ts +56 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Occlusion tested against depth from **this** frame rather than the last one.
|
|
3
|
+
*
|
|
4
|
+
* A hierarchical depth buffer is only an occluder if something has already been drawn into it, and
|
|
5
|
+
* the cheap way to have one at the start of a frame is to keep last frame's. That is wrong exactly
|
|
6
|
+
* when the camera moved: the pyramid describes a view nobody is looking from, and geometry the old
|
|
7
|
+
* view happened to hide is culled out of the new one. What it looks like is a wall or a floor
|
|
8
|
+
* disappearing for a frame or two after a cut, a teleport, or a fast turn — and it is intermittent,
|
|
9
|
+
* so it is reported as a streaming fault.
|
|
10
|
+
*
|
|
11
|
+
* **So the frame is drawn in two halves.** Phase one draws only what was visible last frame, which
|
|
12
|
+
* on a continuous camera is nearly everything and is a good occluder for the rest. The pyramid is
|
|
13
|
+
* reduced from *that* depth. Phase two then tests everything else against it and draws whatever
|
|
14
|
+
* phase one missed. Both halves write the same colour and depth, because they are one frame.
|
|
15
|
+
*
|
|
16
|
+
* **The history decides which phase a cluster is judged in and never whether it is judged.** Every
|
|
17
|
+
* cluster is in exactly one of the two lists, whatever the history holds, so a history that is
|
|
18
|
+
* stale — which after a teleport is all of it — costs a phase-two test and never a hole. That
|
|
19
|
+
* invariant is what makes the history safe to keep across a cut, and it is the one this file's
|
|
20
|
+
* tests assert most directly.
|
|
21
|
+
*
|
|
22
|
+
* **One history per view.** A shadow pass and the main camera see different things, and sharing a
|
|
23
|
+
* bit set between them puts one view's visibility into the other's phase one — which is the same
|
|
24
|
+
* defect as a stale history, arriving every frame instead of after a cut.
|
|
25
|
+
*
|
|
26
|
+
* The pass recording here is the frame graph's data rather than device calls: `phaseOnePasses` and
|
|
27
|
+
* `phaseTwoPasses` record what each half reads and writes, and the edge from the reduction's write
|
|
28
|
+
* to phase two's read *is* the guarantee in the first paragraph. Wiring them to a real encoder is
|
|
29
|
+
* Task 14's, against a consumer, for the reason `ARCHITECTURE.md` §3 gives about rendering changes
|
|
30
|
+
* nobody has looked at.
|
|
31
|
+
*/
|
|
32
|
+
import { recordDeps } from '../frame/deps.js';
|
|
33
|
+
export function createVisibilityHistory(capacity) {
|
|
34
|
+
return { bits: new Uint32Array(Math.max(1, Math.ceil(capacity / 32))), capacity };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Whether a cluster was drawn last frame.
|
|
38
|
+
*
|
|
39
|
+
* **Out of range answers false rather than throwing or wrapping.** A cluster index past the end is
|
|
40
|
+
* a scene that grew since the history was made, and the honest answer for one nobody has seen is
|
|
41
|
+
* "not last frame" — which puts it in phase two, where it is tested rather than assumed. Wrapping
|
|
42
|
+
* would put an unrelated cluster's bit in its place, which is a visibility answer for the wrong
|
|
43
|
+
* geometry and is invisible until it is wrong.
|
|
44
|
+
*
|
|
45
|
+
* **A perturbation survives this check and it is kept anyway.** `setHistory` refuses the same
|
|
46
|
+
* range, so no bit above `capacity` can ever be set, and a read past the end of the word array is
|
|
47
|
+
* `undefined` — which `&` coerces to zero and therefore answers false already. What it decides is
|
|
48
|
+
* the case where the two guards disagree, and the reason both are here rather than one is that
|
|
49
|
+
* they are read independently: the write side is the one a test can catch, and it is caught. The
|
|
50
|
+
* bits between `capacity` and the end of the last word are the ones that make the write side
|
|
51
|
+
* matter — forty clusters is two words, so twenty-four of them belong to nobody.
|
|
52
|
+
*/
|
|
53
|
+
export function historyHas(history, cluster) {
|
|
54
|
+
if (cluster < 0 || cluster >= history.capacity)
|
|
55
|
+
return false;
|
|
56
|
+
return (history.bits[cluster >> 5] & (1 << (cluster & 31))) !== 0;
|
|
57
|
+
}
|
|
58
|
+
export function setHistory(history, cluster, drawn) {
|
|
59
|
+
if (cluster < 0 || cluster >= history.capacity)
|
|
60
|
+
return;
|
|
61
|
+
const word = cluster >> 5;
|
|
62
|
+
const mask = 1 << (cluster & 31);
|
|
63
|
+
const held = history.bits[word];
|
|
64
|
+
history.bits[word] = drawn ? held | mask : held & ~mask;
|
|
65
|
+
}
|
|
66
|
+
export function clearHistory(history) {
|
|
67
|
+
history.bits.fill(0);
|
|
68
|
+
}
|
|
69
|
+
/** How many of the first `count` clusters the history holds. */
|
|
70
|
+
export function historyCount(history, count) {
|
|
71
|
+
let total = 0;
|
|
72
|
+
for (let cluster = 0; cluster < count; cluster += 1)
|
|
73
|
+
if (historyHas(history, cluster))
|
|
74
|
+
total += 1;
|
|
75
|
+
return total;
|
|
76
|
+
}
|
|
77
|
+
/** The clusters phase one draws: exactly what was visible last frame, in index order. */
|
|
78
|
+
export function phaseOneClusters(history, count, out) {
|
|
79
|
+
let at = 0;
|
|
80
|
+
for (let cluster = 0; cluster < count; cluster += 1) {
|
|
81
|
+
if (!historyHas(history, cluster))
|
|
82
|
+
continue;
|
|
83
|
+
out[at] = cluster;
|
|
84
|
+
at += 1;
|
|
85
|
+
}
|
|
86
|
+
return at;
|
|
87
|
+
}
|
|
88
|
+
/** The clusters phase two tests: everything the history does not hold. */
|
|
89
|
+
export function phaseTwoClusters(history, count, out) {
|
|
90
|
+
let at = 0;
|
|
91
|
+
for (let cluster = 0; cluster < count; cluster += 1) {
|
|
92
|
+
if (historyHas(history, cluster))
|
|
93
|
+
continue;
|
|
94
|
+
out[at] = cluster;
|
|
95
|
+
at += 1;
|
|
96
|
+
}
|
|
97
|
+
return at;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Replace the history with what this frame actually drew.
|
|
101
|
+
*
|
|
102
|
+
* **Replace rather than accumulate**, and that is the half that is easy to leave out: a cluster
|
|
103
|
+
* kept in phase one and then culled in phase two has to leave the set, or it is drawn every frame
|
|
104
|
+
* for ever on the strength of having once been visible. `keep` is the flag buffer
|
|
105
|
+
* `cullClusters` writes, so this is one pass over the same array the draw used.
|
|
106
|
+
*/
|
|
107
|
+
export function updateHistory(history, keep, count) {
|
|
108
|
+
for (let cluster = 0; cluster < count; cluster += 1) {
|
|
109
|
+
setHistory(history, cluster, (keep[cluster] ?? 0) !== 0);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function createVisibilityHistories() {
|
|
113
|
+
return { byView: new Map() };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* This view's history, widened in place if the scene grew.
|
|
117
|
+
*
|
|
118
|
+
* **Widened rather than replaced**, because replacing it throws away a frame of visibility every
|
|
119
|
+
* time a cluster is added and puts the whole scene through phase two — which is correct and is
|
|
120
|
+
* also the slow path this feature exists to avoid.
|
|
121
|
+
*/
|
|
122
|
+
export function historyFor(store, view, capacity) {
|
|
123
|
+
const held = store.byView.get(view);
|
|
124
|
+
if (held === undefined) {
|
|
125
|
+
const made = createVisibilityHistory(capacity);
|
|
126
|
+
store.byView.set(view, made);
|
|
127
|
+
return made;
|
|
128
|
+
}
|
|
129
|
+
if (capacity > held.capacity) {
|
|
130
|
+
const widened = new Uint32Array(Math.max(1, Math.ceil(capacity / 32)));
|
|
131
|
+
widened.set(held.bits);
|
|
132
|
+
held.bits = widened;
|
|
133
|
+
held.capacity = capacity;
|
|
134
|
+
}
|
|
135
|
+
return held;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Phase one: draw what was visible, then reduce the depth it produced into the pyramid.
|
|
139
|
+
*
|
|
140
|
+
* The reduction reads the depth the draw wrote, so nothing can reorder them and nothing can alias
|
|
141
|
+
* the depth away between them — which is exactly what `lifetime.ts` and `alias.ts` are reading
|
|
142
|
+
* these edges for.
|
|
143
|
+
*/
|
|
144
|
+
export function phaseOnePasses(deps, res) {
|
|
145
|
+
const draw = recordDeps(deps, [res.clusters], [res.colour, res.depth]);
|
|
146
|
+
const reduce = recordDeps(deps, [res.depth], [res.hzb]);
|
|
147
|
+
return { draw, reduce };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Phase two: cull everything else against that pyramid, then draw what survived.
|
|
151
|
+
*
|
|
152
|
+
* `cull` reads `hzb`, which `phaseOnePasses` wrote. That one edge is the whole claim of this
|
|
153
|
+
* module — the depth being tested against belongs to this frame — expressed where the scheduler
|
|
154
|
+
* can enforce it rather than in a comment somebody has to obey.
|
|
155
|
+
*/
|
|
156
|
+
export function phaseTwoPasses(deps, res) {
|
|
157
|
+
const cull = recordDeps(deps, [res.hzb, res.clusters], [res.keep]);
|
|
158
|
+
const draw = recordDeps(deps, [res.keep, res.clusters], [res.colour, res.depth]);
|
|
159
|
+
return { cull, draw };
|
|
160
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a pixel records: which triangle of which cluster of which instance covered it.
|
|
3
|
+
*
|
|
4
|
+
* **One 32-bit target, and its ceiling is stated here rather than discovered.** Seven bits carry
|
|
5
|
+
* the triangle, which is the 128 a cluster may hold, and the remaining twenty-five carry the
|
|
6
|
+
* cluster-in-instance identifier — 33.5 million of them, far past what a streaming budget permits.
|
|
7
|
+
* If a scene ever reaches it the answer is a second target, not a repack.
|
|
8
|
+
*
|
|
9
|
+
* **Zero is a legal value, so the buffer is cleared to a sentinel instead.** Cleared to zero, an
|
|
10
|
+
* uncovered pixel is indistinguishable from triangle 0 of cluster 0 of instance 0, which is a real
|
|
11
|
+
* triangle in every scene that has one.
|
|
12
|
+
*/
|
|
13
|
+
/** Triangles a cluster may hold, and therefore what the low bits carry. */
|
|
14
|
+
export declare const VIS_TRIANGLE_BITS = 7;
|
|
15
|
+
export declare const VIS_MAX_TRIANGLES: number;
|
|
16
|
+
/** Cluster-in-instance identifiers the remaining bits carry. */
|
|
17
|
+
export declare const VIS_MAX_CLUSTERS: number;
|
|
18
|
+
/** What an uncovered pixel holds. Not zero — see the header. */
|
|
19
|
+
export declare const VIS_EMPTY = 4294967295;
|
|
20
|
+
export declare function packVisibility(cluster: number, triangle: number): number;
|
|
21
|
+
export declare function unpackVisibility(packed: number): {
|
|
22
|
+
cluster: number;
|
|
23
|
+
triangle: number;
|
|
24
|
+
};
|
|
25
|
+
export declare function visibilityCovered(packed: number): boolean;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a pixel records: which triangle of which cluster of which instance covered it.
|
|
3
|
+
*
|
|
4
|
+
* **One 32-bit target, and its ceiling is stated here rather than discovered.** Seven bits carry
|
|
5
|
+
* the triangle, which is the 128 a cluster may hold, and the remaining twenty-five carry the
|
|
6
|
+
* cluster-in-instance identifier — 33.5 million of them, far past what a streaming budget permits.
|
|
7
|
+
* If a scene ever reaches it the answer is a second target, not a repack.
|
|
8
|
+
*
|
|
9
|
+
* **Zero is a legal value, so the buffer is cleared to a sentinel instead.** Cleared to zero, an
|
|
10
|
+
* uncovered pixel is indistinguishable from triangle 0 of cluster 0 of instance 0, which is a real
|
|
11
|
+
* triangle in every scene that has one.
|
|
12
|
+
*/
|
|
13
|
+
/** Triangles a cluster may hold, and therefore what the low bits carry. */
|
|
14
|
+
export const VIS_TRIANGLE_BITS = 7;
|
|
15
|
+
export const VIS_MAX_TRIANGLES = 1 << VIS_TRIANGLE_BITS;
|
|
16
|
+
/** Cluster-in-instance identifiers the remaining bits carry. */
|
|
17
|
+
export const VIS_MAX_CLUSTERS = 1 << (32 - VIS_TRIANGLE_BITS);
|
|
18
|
+
/** What an uncovered pixel holds. Not zero — see the header. */
|
|
19
|
+
export const VIS_EMPTY = 0xffffffff;
|
|
20
|
+
export function packVisibility(cluster, triangle) {
|
|
21
|
+
if (triangle < 0 || triangle >= VIS_MAX_TRIANGLES) {
|
|
22
|
+
throw new RangeError(`triangle ${triangle} does not fit in ${VIS_TRIANGLE_BITS} bits`);
|
|
23
|
+
}
|
|
24
|
+
if (cluster < 0 || cluster >= VIS_MAX_CLUSTERS) {
|
|
25
|
+
throw new RangeError(`cluster ${cluster} does not fit in ${32 - VIS_TRIANGLE_BITS} bits`);
|
|
26
|
+
}
|
|
27
|
+
return ((cluster << VIS_TRIANGLE_BITS) | triangle) >>> 0;
|
|
28
|
+
}
|
|
29
|
+
export function unpackVisibility(packed) {
|
|
30
|
+
return {
|
|
31
|
+
cluster: packed >>> VIS_TRIANGLE_BITS,
|
|
32
|
+
triangle: packed & (VIS_MAX_TRIANGLES - 1),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function visibilityCovered(packed) {
|
|
36
|
+
return packed !== VIS_EMPTY;
|
|
37
|
+
}
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
/** IEEE 754 binary16 conversion, shared by everything that has to hand a device half floats. */
|
|
2
|
+
/**
|
|
3
|
+
* The nearest half to `value`, ties to even — IEEE 754's default, and what the network runner's
|
|
4
|
+
* half-precision weights need, since the reference they are checked against rounds the same way.
|
|
5
|
+
*
|
|
6
|
+
* **This truncated until 2026-09-19**, so every value moved toward zero by up to a whole unit in the
|
|
7
|
+
* last place: an environment cube lost radiance on every texel rather than rounding on each, and a
|
|
8
|
+
* weight disagreed with the reference's by as much. Rounding costs a few operations per value and
|
|
9
|
+
* runs once per upload. The carry a round produces is added into the packed bits, so a mantissa that
|
|
10
|
+
* overflows lifts the exponent — and past 65,504 lands on infinity — with no case of its own.
|
|
11
|
+
*/
|
|
2
12
|
export declare function halfBits(value: number): number;
|
|
3
13
|
/** A whole array of floats as binary16, which is what a `r16float` or `rgba16float` upload wants. */
|
|
4
14
|
export declare function toHalfFloats(values: Float32Array): Uint16Array;
|
package/dist/render/halfFloat.js
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
const conversion = new ArrayBuffer(4);
|
|
4
4
|
const asFloat = new Float32Array(conversion);
|
|
5
5
|
const asUint = new Uint32Array(conversion);
|
|
6
|
+
/**
|
|
7
|
+
* The nearest half to `value`, ties to even — IEEE 754's default, and what the network runner's
|
|
8
|
+
* half-precision weights need, since the reference they are checked against rounds the same way.
|
|
9
|
+
*
|
|
10
|
+
* **This truncated until 2026-09-19**, so every value moved toward zero by up to a whole unit in the
|
|
11
|
+
* last place: an environment cube lost radiance on every texel rather than rounding on each, and a
|
|
12
|
+
* weight disagreed with the reference's by as much. Rounding costs a few operations per value and
|
|
13
|
+
* runs once per upload. The carry a round produces is added into the packed bits, so a mantissa that
|
|
14
|
+
* overflows lifts the exponent — and past 65,504 lands on infinity — with no case of its own.
|
|
15
|
+
*/
|
|
6
16
|
export function halfBits(value) {
|
|
7
17
|
asFloat[0] = value;
|
|
8
18
|
const bits = asUint[0] ?? 0;
|
|
@@ -16,18 +26,27 @@ export function halfBits(value) {
|
|
|
16
26
|
exponent = exponent - 127 + 15;
|
|
17
27
|
if (exponent >= 0x1f)
|
|
18
28
|
return sign | 0x7c00;
|
|
29
|
+
let shift = 13;
|
|
30
|
+
let packed;
|
|
19
31
|
if (exponent <= 0) {
|
|
20
32
|
/*
|
|
21
33
|
* Subnormal. An environment's darkest texels live here, and flushing them to zero is what
|
|
22
|
-
* makes a night sky's gradient band — so they are shifted rather than discarded.
|
|
34
|
+
* makes a night sky's gradient band — so they are shifted rather than discarded. Below half the
|
|
35
|
+
* smallest subnormal, nothing is left to round up.
|
|
23
36
|
*/
|
|
24
37
|
if (exponent < -10)
|
|
25
38
|
return sign;
|
|
26
39
|
mantissa |= 0x800000;
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
shift = 14 - exponent;
|
|
41
|
+
packed = mantissa >>> shift;
|
|
29
42
|
}
|
|
30
|
-
|
|
43
|
+
else {
|
|
44
|
+
packed = (exponent << 10) | (mantissa >>> 13);
|
|
45
|
+
}
|
|
46
|
+
const halfway = 1 << (shift - 1);
|
|
47
|
+
const rest = mantissa & ((1 << shift) - 1);
|
|
48
|
+
const up = rest > halfway || (rest === halfway && (packed & 1) === 1);
|
|
49
|
+
return sign | (packed + (up ? 1 : 0));
|
|
31
50
|
}
|
|
32
51
|
/** A whole array of floats as binary16, which is what a `r16float` or `rgba16float` upload wants. */
|
|
33
52
|
export function toHalfFloats(values) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attention with keys and values staged in workgroup memory: many queries against many keys, at
|
|
3
|
+
* head widths that would spill an invocation's accumulator from its registers.
|
|
4
|
+
*
|
|
5
|
+
* **A workgroup takes one window's head and as many of its queries as 256 threads hold**, each
|
|
6
|
+
* thread one query and 16 of the head's channels — so what a thread keeps is 16 values of its output
|
|
7
|
+
* and as many of its query, where the other two forms keep the whole head; a head of 256 is 16
|
|
8
|
+
* queries a workgroup, and a head of 64 is 64, and every key staged serves each of them. Keys come
|
|
9
|
+
* in tiles of 8: the tile is staged, each thread's share of every query-key dot is written out, a
|
|
10
|
+
* dot's shares are summed, and the same memory then holds the tile's values for the weighted sum. A
|
|
11
|
+
* query's softmax runs key by key with the running maximum, exactly as the other forms run it, so
|
|
12
|
+
* the bound that holds for them holds here.
|
|
13
|
+
*
|
|
14
|
+
* **What it gives up is barriers**: four a tile, so 2,000 for 4,096 keys, which is why it is chosen
|
|
15
|
+
* where the work is large enough to pay for them. The memory is under 16 KB at any width — at a
|
|
16
|
+
* head of 256, all of what a workgroup is promised — and a head wider than that, or one sixteen does
|
|
17
|
+
* not divide, takes one of the other forms.
|
|
18
|
+
*/
|
|
19
|
+
import { type KernelRequest, type KernelSource } from './kernelKit.ts';
|
|
20
|
+
export declare function tiledGroups(d: number): number;
|
|
21
|
+
/** Whether this form can take a head of `d` channels in `groups` groups. */
|
|
22
|
+
export declare function tiledAttentionFits(d: number, groups?: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The tiled form, each query's channels in `groups` groups and so `256 / groups` queries a
|
|
25
|
+
* workgroup. `attentionKernel` chooses it; the argument is how `opbudget.ts` measured the choice.
|
|
26
|
+
*/
|
|
27
|
+
export declare function tiledAttention(request: KernelRequest, groups?: number): KernelSource;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attention with keys and values staged in workgroup memory: many queries against many keys, at
|
|
3
|
+
* head widths that would spill an invocation's accumulator from its registers.
|
|
4
|
+
*
|
|
5
|
+
* **A workgroup takes one window's head and as many of its queries as 256 threads hold**, each
|
|
6
|
+
* thread one query and 16 of the head's channels — so what a thread keeps is 16 values of its output
|
|
7
|
+
* and as many of its query, where the other two forms keep the whole head; a head of 256 is 16
|
|
8
|
+
* queries a workgroup, and a head of 64 is 64, and every key staged serves each of them. Keys come
|
|
9
|
+
* in tiles of 8: the tile is staged, each thread's share of every query-key dot is written out, a
|
|
10
|
+
* dot's shares are summed, and the same memory then holds the tile's values for the weighted sum. A
|
|
11
|
+
* query's softmax runs key by key with the running maximum, exactly as the other forms run it, so
|
|
12
|
+
* the bound that holds for them holds here.
|
|
13
|
+
*
|
|
14
|
+
* **What it gives up is barriers**: four a tile, so 2,000 for 4,096 keys, which is why it is chosen
|
|
15
|
+
* where the work is large enough to pay for them. The memory is under 16 KB at any width — at a
|
|
16
|
+
* head of 256, all of what a workgroup is promised — and a head wider than that, or one sixteen does
|
|
17
|
+
* not divide, takes one of the other forms.
|
|
18
|
+
*/
|
|
19
|
+
import { float, header, num, perRow, read, } from './kernelKit.js';
|
|
20
|
+
const THREADS = 256;
|
|
21
|
+
const KEYS = 8;
|
|
22
|
+
/*
|
|
23
|
+
* The groups a query's channels split into: 8, or 16 above a head of 128, which measured best on an
|
|
24
|
+
* RX 9070 XT — 8 took a head of 64 from 1.37 ms at 16 to 1.0 and a head of 96 from 7.3 to 4.8, and
|
|
25
|
+
* 16 took a head of 256 from 22.4 at 8 to 12.2 (`tools/capture-weights/opbudget.ts`).
|
|
26
|
+
*/
|
|
27
|
+
export function tiledGroups(d) {
|
|
28
|
+
return d > 128 ? 16 : 8;
|
|
29
|
+
}
|
|
30
|
+
/** Whether this form can take a head of `d` channels in `groups` groups. */
|
|
31
|
+
export function tiledAttentionFits(d, groups = tiledGroups(d)) {
|
|
32
|
+
return d % groups === 0 && d <= 256;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The tiled form, each query's channels in `groups` groups and so `256 / groups` queries a
|
|
36
|
+
* workgroup. `attentionKernel` chooses it; the argument is how `opbudget.ts` measured the choice.
|
|
37
|
+
*/
|
|
38
|
+
export function tiledAttention(request, groups) {
|
|
39
|
+
const queryShape = request.inputShapes[0];
|
|
40
|
+
const [queries, channels] = queryShape.slice(-2);
|
|
41
|
+
const keys = request.inputShapes[1].at(-2);
|
|
42
|
+
const batch = queryShape.length === 3 ? queryShape[0] : 1;
|
|
43
|
+
const heads = num(request.attributes, 'heads', 1);
|
|
44
|
+
const d = channels / heads;
|
|
45
|
+
const GROUPS = groups ?? tiledGroups(d);
|
|
46
|
+
const share = d / GROUPS;
|
|
47
|
+
const QUERIES = THREADS / GROUPS;
|
|
48
|
+
const threads = THREADS;
|
|
49
|
+
const blocks = Math.ceil(queries / QUERIES);
|
|
50
|
+
const grid = perRow(batch * heads * blocks);
|
|
51
|
+
const bias = request.inputShapes.length > 3
|
|
52
|
+
? ` + ${read(request, 3, `(head * ${queries}u + min(query, ${queries - 1}u)) * ${keys}u + key`)}`
|
|
53
|
+
: '';
|
|
54
|
+
const stage = (input) => ` for (var i = local.x; i < ${KEYS * d}u; i = i + ${threads}u) {
|
|
55
|
+
let key = start + i / ${d}u;
|
|
56
|
+
tile[i] = select(0.0, ${read(request, input, `(keyRow + min(key, ${keys - 1}u)) * ${channels}u + first + i % ${d}u`)}, key < ${keys}u);
|
|
57
|
+
}
|
|
58
|
+
workgroupBarrier();`;
|
|
59
|
+
return {
|
|
60
|
+
code: `${header(request)}
|
|
61
|
+
|
|
62
|
+
var<workgroup> tile: array<f32, ${KEYS * d}>;
|
|
63
|
+
var<workgroup> partial: array<f32, ${QUERIES * KEYS * GROUPS}>;
|
|
64
|
+
|
|
65
|
+
@compute @workgroup_size(${threads})
|
|
66
|
+
fn main(@builtin(workgroup_id) group: vec3<u32>, @builtin(local_invocation_id) local: vec3<u32>) {
|
|
67
|
+
let block = ${grid.row};
|
|
68
|
+
if (block >= ${batch * heads * blocks}u) { return; }
|
|
69
|
+
let window = block / ${heads * blocks}u;
|
|
70
|
+
let head = (block / ${blocks}u) % ${heads}u;
|
|
71
|
+
let r = local.x / ${GROUPS}u;
|
|
72
|
+
let g = local.x % ${GROUPS}u;
|
|
73
|
+
let query = (block % ${blocks}u) * ${QUERIES}u + r;
|
|
74
|
+
let row = window * ${queries}u + min(query, ${queries - 1}u);
|
|
75
|
+
let keyRow = window * ${keys}u;
|
|
76
|
+
let first = head * ${d}u;
|
|
77
|
+
let own = g * ${share}u;
|
|
78
|
+
var mine: array<f32, ${share}>;
|
|
79
|
+
var gathered: array<f32, ${share}>;
|
|
80
|
+
for (var c = 0u; c < ${share}u; c = c + 1u) { mine[c] = ${read(request, 0, `row * ${channels}u + first + own + c`)}; }
|
|
81
|
+
var largest = -3.4028234e38;
|
|
82
|
+
var total = 0.0;
|
|
83
|
+
for (var start = 0u; start < ${keys}u; start = start + ${KEYS}u) {
|
|
84
|
+
${stage(1)}
|
|
85
|
+
for (var k = 0u; k < ${KEYS}u; k = k + 1u) {
|
|
86
|
+
var dot = 0.0;
|
|
87
|
+
for (var c = 0u; c < ${share}u; c = c + 1u) { dot = dot + mine[c] * tile[k * ${d}u + own + c]; }
|
|
88
|
+
partial[(r * ${KEYS}u + k) * ${GROUPS}u + g] = dot;
|
|
89
|
+
}
|
|
90
|
+
workgroupBarrier();
|
|
91
|
+
for (var k = g; k < ${KEYS}u; k = k + ${GROUPS}u) {
|
|
92
|
+
var sum = 0.0;
|
|
93
|
+
for (var j = 0u; j < ${GROUPS}u; j = j + 1u) { sum = sum + partial[(r * ${KEYS}u + k) * ${GROUPS}u + j]; }
|
|
94
|
+
partial[(r * ${KEYS}u + k) * ${GROUPS}u] = sum;
|
|
95
|
+
}
|
|
96
|
+
${stage(2)}
|
|
97
|
+
for (var k = 0u; k < ${KEYS}u; k = k + 1u) {
|
|
98
|
+
let key = start + k;
|
|
99
|
+
if (key >= ${keys}u) { break; }
|
|
100
|
+
let score = partial[(r * ${KEYS}u + k) * ${GROUPS}u] * ${float(1 / Math.sqrt(d))}${bias};
|
|
101
|
+
let top = max(largest, score);
|
|
102
|
+
let rescale = exp(largest - top);
|
|
103
|
+
let weight = exp(score - top);
|
|
104
|
+
total = total * rescale + weight;
|
|
105
|
+
for (var c = 0u; c < ${share}u; c = c + 1u) { gathered[c] = gathered[c] * rescale + weight * tile[k * ${d}u + own + c]; }
|
|
106
|
+
largest = top;
|
|
107
|
+
}
|
|
108
|
+
workgroupBarrier();
|
|
109
|
+
}
|
|
110
|
+
if (query < ${queries}u) {
|
|
111
|
+
for (var c = 0u; c < ${share}u; c = c + 1u) { output[row * ${channels}u + first + own + c] = gathered[c] / total; }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
`,
|
|
115
|
+
workgroups: grid.workgroups,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type KernelGenerator } from './kernelKit.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The tile edge `linear` stages in workgroup memory, chosen by `tools/capture-weights/opbudget.ts`:
|
|
4
|
+
* at a depth model's shapes — 1,370 rows, 384 and 1,536 wide — 16 took 0.36 ms a multiply where 8
|
|
5
|
+
* took 0.40 on the two wide ones, and the two tied on the narrow one (an RX 9070 XT on Dawn,
|
|
6
|
+
* 2026-09-19). What would change it is a device that runs 256 invocations a workgroup badly: 8 is
|
|
7
|
+
* the other edge a device's default limit admits.
|
|
8
|
+
*/
|
|
9
|
+
export declare const LINEAR_TILE = 16;
|
|
10
|
+
/**
|
|
11
|
+
* The tiled multiply at a given tile edge, which is a parameter only so the cost tool can measure
|
|
12
|
+
* the alternatives: a tile of `t` is `t × t` invocations, so 8 and 16 are the two a device's default
|
|
13
|
+
* limit of 256 admits.
|
|
14
|
+
*
|
|
15
|
+
* **A `gelu` activation is applied as the sum is written**, which is what `fuse.ts` asks for when a
|
|
16
|
+
* GELU is the only reader of a multiply: one dispatch and one buffer fewer, and the pre-activation
|
|
17
|
+
* value never reaches memory.
|
|
18
|
+
*/
|
|
19
|
+
export declare function linearKernel(tile: number): KernelGenerator;
|
|
20
|
+
export declare function attentionKernel(splitKeys: number, tiled?: {
|
|
21
|
+
readonly queries: number;
|
|
22
|
+
readonly keys: number;
|
|
23
|
+
} | null): KernelGenerator;
|
|
24
|
+
export declare const ATTENTION_SPLIT_KEYS = 1024;
|
|
25
|
+
export declare const ATTENTION_TILED: {
|
|
26
|
+
readonly queries: 64;
|
|
27
|
+
readonly keys: 128;
|
|
28
|
+
};
|
|
29
|
+
export declare const DENSE_KERNELS: readonly (readonly [string, KernelGenerator])[];
|