@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,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which clusters are worth rasterising: the three tests, in the order that makes them cheap.
|
|
3
|
+
*
|
|
4
|
+
* Frustum first, because it is six dot products and throws away most of a world. Then the normal
|
|
5
|
+
* cone, which is one more dot product and throws away roughly half of what is left on a closed
|
|
6
|
+
* surface. Then the hierarchical depth buffer, which costs a projection and four texel reads and
|
|
7
|
+
* is the only one that needs anything from this frame.
|
|
8
|
+
*
|
|
9
|
+
* **Every one of them is conservative in the same direction.** A cluster that might be visible
|
|
10
|
+
* survives; only a cluster that provably cannot be seen is dropped. Culling something visible is a
|
|
11
|
+
* hole in the picture that comes and goes with the camera and is diagnosed as almost anything
|
|
12
|
+
* else; keeping something invisible is a wasted draw. `frustum.ts` says the same in its own words
|
|
13
|
+
* and the two files have to agree about it, because a pipeline whose stages disagree about which
|
|
14
|
+
* way to round is one that drops geometry at exactly the boundaries.
|
|
15
|
+
*
|
|
16
|
+
* **The compaction is here and the flags are the device's.** A dispatch can decide every cluster
|
|
17
|
+
* in parallel and cannot append them in order without a prefix sum, so the shader writes one flag
|
|
18
|
+
* per cluster and this writes the ordered list from the flags. That split is also what
|
|
19
|
+
* `scripts/gpu-parity.mjs` compares: the flags are order-free, so they can be checked
|
|
20
|
+
* element-by-element against a reference that has not had to reproduce an atomic's arrival order.
|
|
21
|
+
*/
|
|
22
|
+
import { sphereOutsideFrustum } from './frustum.js';
|
|
23
|
+
import { hzbOccluded } from './hzb.js';
|
|
24
|
+
/** Eight floats a cluster: centre, radius, cone axis, cone cutoff. `clusterUpload.ts` packs them. */
|
|
25
|
+
export const CLUSTER_CULL_FLOATS = 8;
|
|
26
|
+
/** What a kept cluster's flag holds. One rather than a bit mask: there is one reason to keep. */
|
|
27
|
+
export const CLUSTER_KEEP = 1;
|
|
28
|
+
/**
|
|
29
|
+
* A cluster's cone as it is carried, turned into the cutoff `coneBackfacing` reads.
|
|
30
|
+
*
|
|
31
|
+
* **A cluster carries a cosine and this test is written in a sine**, and until 2026-09-18 nothing
|
|
32
|
+
* said so. `buildClusters` writes "the minimum dot product of any face normal against the axis",
|
|
33
|
+
* the `MSHL` container stores that and says so, and `ClusterSource` passed it through to the test
|
|
34
|
+
* below untouched. Every cluster before then was a single quad or a near-flat patch, whose cosine
|
|
35
|
+
* of about one reads as a sine of one — a hemisphere, never culled — so the mismatch was silent and
|
|
36
|
+
* cone culling simply did not happen. The voxel sandbox grows its clusters by position, across
|
|
37
|
+
* every direction a block faces: a cosine of zero read as a sine of zero is a flat cluster, and a
|
|
38
|
+
* canopy seen from below lost every block whose cluster pointed up.
|
|
39
|
+
*
|
|
40
|
+
* `cos(a)` at or above zero is `sin(a) = sqrt(1 - cos(a)^2)`. Below zero the cone is wider than a
|
|
41
|
+
* hemisphere and no aim of the eye sees every face from behind, so the answer is one, which the
|
|
42
|
+
* test can never satisfy. Converted where a cluster is packed, once, so neither shader nor
|
|
43
|
+
* reference changes.
|
|
44
|
+
*/
|
|
45
|
+
export function cullCutoff(minimumDot) {
|
|
46
|
+
if (minimumDot < 0)
|
|
47
|
+
return 1;
|
|
48
|
+
return Math.sqrt(Math.max(0, 1 - minimumDot * minimumDot));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Whether every triangle in a cluster faces away from the eye.
|
|
52
|
+
*
|
|
53
|
+
* **`cutoff` is `sin(a)`**, where `a` is the half-angle of the cone containing every normal. Zero
|
|
54
|
+
* is a flat cluster; one is a cluster whose normals span a hemisphere, which can never be culled
|
|
55
|
+
* because the test below cannot be satisfied. Stating it as a sine rather than a cosine is what
|
|
56
|
+
* makes the expression a single comparison with no trigonometry in the shader.
|
|
57
|
+
*
|
|
58
|
+
* The derivation, because the constant is otherwise unfalsifiable. Let `d` be the vector from the
|
|
59
|
+
* eye to the cluster's centre. Directions from the eye to points on the cluster lie within
|
|
60
|
+
* `b = asin(radius / |d|)` of `d`. A triangle is backfacing when its normal has a positive dot
|
|
61
|
+
* with the direction the eye looks along it, so *every* triangle is backfacing when
|
|
62
|
+
* `angle(axis, d) + a + b < 90 degrees`, which is `dot(axis, normalise(d)) > sin(a + b)`.
|
|
63
|
+
* Expanding `sin(a + b)` to `sin a + radius / |d|` is exact at `a = 0` and errs on the side of
|
|
64
|
+
* keeping a cluster everywhere else, which is the direction this file rounds.
|
|
65
|
+
*
|
|
66
|
+
* Multiplied through by `|d|` so there is no divide and no normalise.
|
|
67
|
+
*/
|
|
68
|
+
export function coneBackfacing(axisX, axisY, axisZ, cutoff, centreX, centreY, centreZ, radius, eyeX, eyeY, eyeZ) {
|
|
69
|
+
const dx = centreX - eyeX;
|
|
70
|
+
const dy = centreY - eyeY;
|
|
71
|
+
const dz = centreZ - eyeZ;
|
|
72
|
+
const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
73
|
+
return dx * axisX + dy * axisY + dz * axisZ >= cutoff * distance + radius;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The screen rectangle a sphere covers and the nearest clip depth it reaches.
|
|
77
|
+
*
|
|
78
|
+
* Writes `minX, minY, maxX, maxY, nearestDepth` into `out`, in the [0, 1] range a texture
|
|
79
|
+
* coordinate uses, and answers **false when the sphere has no rectangle at all** — when any part
|
|
80
|
+
* of it is at or behind the eye. That case is the reason this returns a boolean rather than
|
|
81
|
+
* clamping: a sphere straddling the near plane does not project to a rectangle, and every
|
|
82
|
+
* implementation that pretends it does culls it on the frames where the wrap happens to land
|
|
83
|
+
* outside the screen and keeps it on the frames where it does not. What that looks like is
|
|
84
|
+
* geometry flickering as the camera moves through it, which is the defect this function's shape
|
|
85
|
+
* exists to prevent.
|
|
86
|
+
*
|
|
87
|
+
* The eight corners of the sphere's bounding box rather than an analytic silhouette: the box is
|
|
88
|
+
* larger than the sphere, so the rectangle is conservative, and it is eight transforms instead of
|
|
89
|
+
* a quadratic.
|
|
90
|
+
*/
|
|
91
|
+
export function sphereScreenBounds(viewProj, centreX, centreY, centreZ, radius, out) {
|
|
92
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
93
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
94
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
95
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
96
|
+
let nearest = Number.NEGATIVE_INFINITY;
|
|
97
|
+
for (let corner = 0; corner < 8; corner += 1) {
|
|
98
|
+
const x = centreX + (corner & 1 ? radius : -radius);
|
|
99
|
+
const y = centreY + (corner & 2 ? radius : -radius);
|
|
100
|
+
const z = centreZ + (corner & 4 ? radius : -radius);
|
|
101
|
+
const cw = viewProj[3] * x +
|
|
102
|
+
viewProj[7] * y +
|
|
103
|
+
viewProj[11] * z +
|
|
104
|
+
viewProj[15];
|
|
105
|
+
/* At or behind the eye, so there is no rectangle. See the header. */
|
|
106
|
+
if (cw <= 1e-6)
|
|
107
|
+
return false;
|
|
108
|
+
const cx = viewProj[0] * x +
|
|
109
|
+
viewProj[4] * y +
|
|
110
|
+
viewProj[8] * z +
|
|
111
|
+
viewProj[12];
|
|
112
|
+
const cy = viewProj[1] * x +
|
|
113
|
+
viewProj[5] * y +
|
|
114
|
+
viewProj[9] * z +
|
|
115
|
+
viewProj[13];
|
|
116
|
+
const cz = viewProj[2] * x +
|
|
117
|
+
viewProj[6] * y +
|
|
118
|
+
viewProj[10] * z +
|
|
119
|
+
viewProj[14];
|
|
120
|
+
const inverse = 1 / cw;
|
|
121
|
+
const u = cx * inverse * 0.5 + 0.5;
|
|
122
|
+
const v = cy * inverse * 0.5 + 0.5;
|
|
123
|
+
minX = Math.min(minX, u);
|
|
124
|
+
maxX = Math.max(maxX, u);
|
|
125
|
+
minY = Math.min(minY, v);
|
|
126
|
+
maxY = Math.max(maxY, v);
|
|
127
|
+
/* Nearest is the *largest* clip depth here; `depthConvention.ts` puts the near plane at 1. */
|
|
128
|
+
nearest = Math.max(nearest, cz * inverse);
|
|
129
|
+
}
|
|
130
|
+
out[0] = minX;
|
|
131
|
+
out[1] = minY;
|
|
132
|
+
out[2] = maxX;
|
|
133
|
+
out[3] = maxY;
|
|
134
|
+
out[4] = nearest;
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The pyramid level whose texels are wide enough that four of them cover the rectangle.
|
|
139
|
+
*
|
|
140
|
+
* `ceil(log2(longest side in texels))` minus one, floored at zero: at that level the rectangle
|
|
141
|
+
* spans at most two texels each way, which is what a four-texel gather reads. One level coarser
|
|
142
|
+
* would read a texel covering geometry outside the rectangle and cull against an occluder that is
|
|
143
|
+
* not in front of this cluster at all.
|
|
144
|
+
*
|
|
145
|
+
* Clamped to the top of the pyramid, because a rectangle larger than the screen is a cluster the
|
|
146
|
+
* camera is inside and there is no level coarse enough for it.
|
|
147
|
+
*/
|
|
148
|
+
export function hzbLevelFor(rectWidthTexels, rectHeightTexels, width, height) {
|
|
149
|
+
const longest = Math.max(rectWidthTexels, rectHeightTexels, 1);
|
|
150
|
+
const level = Math.max(0, Math.ceil(Math.log2(longest)) - 1);
|
|
151
|
+
const top = Math.floor(Math.log2(Math.max(1, Math.max(width, height))));
|
|
152
|
+
return Math.min(level, top);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Whether a rectangle's nearest depth is behind everything already drawn under it.
|
|
156
|
+
*
|
|
157
|
+
* **The weakest of the four texels decides.** Each holds the furthest depth in its own region, so
|
|
158
|
+
* a cluster is hidden only where it is behind *all* of them — and under reversed-Z the furthest of
|
|
159
|
+
* four is the smallest. Taking the nearest instead would cull a cluster visible through a gap,
|
|
160
|
+
* which is the failure mode that reads as holes appearing in geometry as the camera turns.
|
|
161
|
+
*/
|
|
162
|
+
export function clusterOccluded(depths, offsets, width, height, minX, minY, maxX, maxY, nearestDepth) {
|
|
163
|
+
const level = hzbLevelFor((maxX - minX) * width, (maxY - minY) * height, width, height);
|
|
164
|
+
if (level >= offsets.length)
|
|
165
|
+
return false;
|
|
166
|
+
const levelWidth = Math.max(1, width >> level);
|
|
167
|
+
const levelHeight = Math.max(1, height >> level);
|
|
168
|
+
const base = offsets[level];
|
|
169
|
+
const x0 = Math.max(0, Math.min(levelWidth - 1, Math.floor(minX * levelWidth)));
|
|
170
|
+
const x1 = Math.max(0, Math.min(levelWidth - 1, Math.floor(maxX * levelWidth)));
|
|
171
|
+
/*
|
|
172
|
+
* **The rows run the other way, and the swap is not cosmetic.** `sphereScreenBounds` reports the
|
|
173
|
+
* rectangle in clip-up coordinates — `v` of 1 is clip `y` of +1 — and the pyramid is seeded
|
|
174
|
+
* straight from the depth texture, whose row 0 is exactly there. So a `v` used as a row index
|
|
175
|
+
* reads the mirror of what the cluster covers, and the top edge of the rectangle becomes the
|
|
176
|
+
* bottom row.
|
|
177
|
+
*
|
|
178
|
+
* It shipped that way and drew holes: geometry near the top of the frame tested against whatever
|
|
179
|
+
* stood near the bottom, coming and going with the camera. `gpu-parity.mjs` agreed about it for
|
|
180
|
+
* as long, because the WGSL made the same mistake — a parity check compares two implementations
|
|
181
|
+
* and neither of them is the truth.
|
|
182
|
+
*/
|
|
183
|
+
const y0 = Math.max(0, Math.min(levelHeight - 1, Math.floor((1 - maxY) * levelHeight)));
|
|
184
|
+
const y1 = Math.max(0, Math.min(levelHeight - 1, Math.floor((1 - minY) * levelHeight)));
|
|
185
|
+
let furthest = Number.POSITIVE_INFINITY;
|
|
186
|
+
for (let y = y0; y <= y1; y += 1) {
|
|
187
|
+
for (let x = x0; x <= x1; x += 1) {
|
|
188
|
+
furthest = Math.min(furthest, depths[base + y * levelWidth + x]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return hzbOccluded(nearestDepth, furthest);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Every test, over every cluster: one flag each, and the kept indices in input order.
|
|
195
|
+
*
|
|
196
|
+
* `selected`, where given, is the cut's flag a cluster: a cluster it did not select is not tested
|
|
197
|
+
* and not kept, which is how an instance cull reaches the clusters of a mesh it hid. Absent, every
|
|
198
|
+
* cluster is judged.
|
|
199
|
+
*
|
|
200
|
+
* `hzb` is null in the first phase of a two-phase pass, where there is no depth from this frame to
|
|
201
|
+
* test against yet — and null rather than an empty pyramid, because an empty one is a pyramid full
|
|
202
|
+
* of zeroes and under reversed-Z zero is the far plane, which occludes nothing and would look like
|
|
203
|
+
* the test running when it is not.
|
|
204
|
+
*
|
|
205
|
+
* Returns how many survived, which is also what an indirect draw's instance count wants.
|
|
206
|
+
*/
|
|
207
|
+
export function cullClusters(clusters, planes, eyeX, eyeY, eyeZ, hzb, keep, list, selected = null,
|
|
208
|
+
/** False for the blended run, whose raster draws both sides. See `CULL_CONES`. */
|
|
209
|
+
cones = true) {
|
|
210
|
+
const bounds = new Float32Array(5);
|
|
211
|
+
const count = Math.floor(clusters.length / CLUSTER_CULL_FLOATS);
|
|
212
|
+
let kept = 0;
|
|
213
|
+
for (let i = 0; i < count; i += 1) {
|
|
214
|
+
if (selected !== null && selected[i] === 0) {
|
|
215
|
+
keep[i] = 0;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const at = i * CLUSTER_CULL_FLOATS;
|
|
219
|
+
const cx = clusters[at];
|
|
220
|
+
const cy = clusters[at + 1];
|
|
221
|
+
const cz = clusters[at + 2];
|
|
222
|
+
const radius = clusters[at + 3];
|
|
223
|
+
let survives = !sphereOutsideFrustum(planes, cx, cy, cz, radius);
|
|
224
|
+
if (survives && cones) {
|
|
225
|
+
survives = !coneBackfacing(clusters[at + 4], clusters[at + 5], clusters[at + 6], clusters[at + 7], cx, cy, cz, radius, eyeX, eyeY, eyeZ);
|
|
226
|
+
}
|
|
227
|
+
if (survives && hzb !== null && sphereScreenBounds(hzb.viewProj, cx, cy, cz, radius, bounds)) {
|
|
228
|
+
survives = !clusterOccluded(hzb.depths, hzb.offsets, hzb.width, hzb.height, bounds[0], bounds[1], bounds[2], bounds[3], bounds[4]);
|
|
229
|
+
}
|
|
230
|
+
keep[i] = survives ? CLUSTER_KEEP : 0;
|
|
231
|
+
if (survives) {
|
|
232
|
+
list[kept] = i;
|
|
233
|
+
kept += 1;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return kept;
|
|
237
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Every material's decode programs, flattened into what the shading pass binds. */
|
|
2
|
+
/**
|
|
3
|
+
* **A program is `DecodeResources` with bytes rather than floats**, because bytes are what the
|
|
4
|
+
* device array holds. The types are structural: core does not import `@driftengine/texture`, and
|
|
5
|
+
* what `encodeMaterial` returns satisfies `programFromEncoded` as it stands.
|
|
6
|
+
*
|
|
7
|
+
* **Every slot a graph names is program-local and is rewritten here to a global one** — a layer, a
|
|
8
|
+
* network, a constant — so the interpreter needs no per-program indirection table. The graph a
|
|
9
|
+
* caller handed in is not modified; the rewritten copy lives only in the table.
|
|
10
|
+
*/
|
|
11
|
+
export interface GpuDrivenLatent {
|
|
12
|
+
readonly width: number;
|
|
13
|
+
readonly height: number;
|
|
14
|
+
/** One to four. The lanes past them read zero, and alpha reads one, as `decodeCpu` does. */
|
|
15
|
+
readonly components: number;
|
|
16
|
+
/** Level 0 first, each half the last, down to one texel. `components` bytes a texel. */
|
|
17
|
+
readonly levels: readonly Uint8Array[];
|
|
18
|
+
}
|
|
19
|
+
export interface GpuDrivenNetwork {
|
|
20
|
+
readonly shape: {
|
|
21
|
+
readonly inputs: number;
|
|
22
|
+
readonly hidden: readonly number[];
|
|
23
|
+
readonly outputs: number;
|
|
24
|
+
};
|
|
25
|
+
readonly weights: Float32Array;
|
|
26
|
+
}
|
|
27
|
+
export interface GpuDrivenProgram {
|
|
28
|
+
readonly graph: {
|
|
29
|
+
readonly nodes: Uint32Array;
|
|
30
|
+
readonly count: number;
|
|
31
|
+
readonly result: number;
|
|
32
|
+
readonly addressMode: number;
|
|
33
|
+
};
|
|
34
|
+
readonly latents: readonly GpuDrivenLatent[];
|
|
35
|
+
readonly blocks?: readonly GpuDrivenLatent[];
|
|
36
|
+
readonly networks: readonly GpuDrivenNetwork[];
|
|
37
|
+
/** Four floats a slot, for `CONSTANT`. */
|
|
38
|
+
readonly constants?: Float32Array;
|
|
39
|
+
}
|
|
40
|
+
export interface DecodeTables {
|
|
41
|
+
readonly nodes: Uint32Array;
|
|
42
|
+
readonly weights: Float32Array;
|
|
43
|
+
/** One array a mip level: every layer's RGBA bytes, layer after layer. */
|
|
44
|
+
readonly levels: readonly Uint8Array[];
|
|
45
|
+
/** At least one, because a texture array of no layers cannot be created. */
|
|
46
|
+
readonly layerCount: number;
|
|
47
|
+
/** Level 0's edge in texels. One when nothing is sampled. */
|
|
48
|
+
readonly layerSize: number;
|
|
49
|
+
}
|
|
50
|
+
/** What `encodeMaterial` returns, as far as this needs it. */
|
|
51
|
+
export interface EncodedProgramShape {
|
|
52
|
+
readonly latentWidth: number;
|
|
53
|
+
readonly latentHeight: number;
|
|
54
|
+
readonly components: number;
|
|
55
|
+
readonly weights: Float32Array;
|
|
56
|
+
readonly shape: GpuDrivenNetwork['shape'];
|
|
57
|
+
readonly graph: GpuDrivenProgram['graph'];
|
|
58
|
+
/** Level 0 first — `encodeMaterial` puts the latent itself there. */
|
|
59
|
+
readonly mips: readonly {
|
|
60
|
+
readonly data: Uint8Array;
|
|
61
|
+
}[];
|
|
62
|
+
}
|
|
63
|
+
export declare function programFromEncoded(encoded: EncodedProgramShape): GpuDrivenProgram;
|
|
64
|
+
export declare function packDecodeTables(programs: readonly GpuDrivenProgram[]): DecodeTables;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/** Every material's decode programs, flattened into what the shading pass binds. */
|
|
2
|
+
import { DECODE_ADDRESS_MODES, DECODE_CONSTANT_CAPACITY, DECODE_FBM_OCTAVES, DECODE_NETWORK_CAPACITY, DECODE_NETWORK_HIDDEN_LAYERS, DECODE_NETWORK_INPUTS, DECODE_NETWORK_OUTPUTS, DECODE_NETWORK_WIDTH, DECODE_NODE_CAPACITY, DECODE_NODES_BYTES, DECODE_OPS, DECODE_PROGRAM_CAPACITY, DECODE_REGISTERS, DECODE_WEIGHT_BLOCKS, DECODE_WEIGHTS_BYTES, } from '../shaders/gpudriven/decode.wgsl.js';
|
|
3
|
+
export function programFromEncoded(encoded) {
|
|
4
|
+
return {
|
|
5
|
+
graph: encoded.graph,
|
|
6
|
+
latents: [
|
|
7
|
+
{
|
|
8
|
+
width: encoded.latentWidth,
|
|
9
|
+
height: encoded.latentHeight,
|
|
10
|
+
components: encoded.components,
|
|
11
|
+
levels: encoded.mips.map((level) => level.data),
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
networks: [{ shape: encoded.shape, weights: encoded.weights }],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const NETWORK_AT = DECODE_PROGRAM_CAPACITY * 4;
|
|
18
|
+
const NODE_AT = NETWORK_AT + DECODE_NETWORK_CAPACITY * 8;
|
|
19
|
+
const CONSTANT_AT = DECODE_WEIGHT_BLOCKS * 4;
|
|
20
|
+
/** Whether an operation's `a` and `b` name registers. `decodeGraph.ts`'s `OP_ARGS`, again. */
|
|
21
|
+
const REGISTER_ARGS = {
|
|
22
|
+
[DECODE_OPS.SAMPLE_LATENT]: [false, false],
|
|
23
|
+
[DECODE_OPS.EVAL_NETWORK]: [true, false],
|
|
24
|
+
[DECODE_OPS.SAMPLE_BLOCK]: [false, false],
|
|
25
|
+
[DECODE_OPS.PROCEDURAL_FBM]: [false, false],
|
|
26
|
+
[DECODE_OPS.FLIPBOOK_INDEX]: [false, false],
|
|
27
|
+
[DECODE_OPS.LATENT_LERP]: [true, true],
|
|
28
|
+
[DECODE_OPS.REMAP_CHANNEL]: [true, false],
|
|
29
|
+
[DECODE_OPS.COMPOSITE]: [true, true],
|
|
30
|
+
[DECODE_OPS.CONSTANT]: [false, false],
|
|
31
|
+
};
|
|
32
|
+
function refuse(message) {
|
|
33
|
+
throw new Error(`[driftengine] ${message}`);
|
|
34
|
+
}
|
|
35
|
+
/** `networkWeightCount`, again, for the same reason `REGISTER_ARGS` is. */
|
|
36
|
+
function weightCount(shape) {
|
|
37
|
+
let total = 0;
|
|
38
|
+
let previous = shape.inputs;
|
|
39
|
+
for (const width of shape.hidden) {
|
|
40
|
+
total += previous * width + width;
|
|
41
|
+
previous = width;
|
|
42
|
+
}
|
|
43
|
+
return total + previous * shape.outputs + shape.outputs;
|
|
44
|
+
}
|
|
45
|
+
export function packDecodeTables(programs) {
|
|
46
|
+
if (programs.length > DECODE_PROGRAM_CAPACITY) {
|
|
47
|
+
refuse(`${programs.length} programs, past the ${DECODE_PROGRAM_CAPACITY} the table holds`);
|
|
48
|
+
}
|
|
49
|
+
const nodes = new Uint32Array(DECODE_NODES_BYTES / 4);
|
|
50
|
+
const weights = new Float32Array(DECODE_WEIGHTS_BYTES / 4);
|
|
51
|
+
const first = programs.flatMap((p) => [...p.latents, ...(p.blocks ?? [])])[0];
|
|
52
|
+
const layerSize = first?.width ?? 1;
|
|
53
|
+
if (!(layerSize > 0 && (layerSize & (layerSize - 1)) === 0)) {
|
|
54
|
+
refuse(`a latent is ${layerSize} texels across, and a layer's edge has to be a power of two`);
|
|
55
|
+
}
|
|
56
|
+
const levelCount = Math.log2(layerSize) + 1;
|
|
57
|
+
const chunks = Array.from({ length: levelCount }, () => []);
|
|
58
|
+
let layers = 0;
|
|
59
|
+
let networkCount = 0;
|
|
60
|
+
let nodeCount = 0;
|
|
61
|
+
let weightAt = 0;
|
|
62
|
+
let constantCount = 0;
|
|
63
|
+
const addLatent = (latent) => {
|
|
64
|
+
if (latent.width !== layerSize || latent.height !== layerSize) {
|
|
65
|
+
refuse(`a latent is ${latent.width} by ${latent.height}, and every latent in one pass is ` +
|
|
66
|
+
`${layerSize} by ${layerSize}`);
|
|
67
|
+
}
|
|
68
|
+
if (latent.components < 1 || latent.components > 4) {
|
|
69
|
+
refuse(`a latent has ${latent.components} components, not one to four`);
|
|
70
|
+
}
|
|
71
|
+
if (latent.levels.length !== levelCount) {
|
|
72
|
+
refuse(`a latent carries ${latent.levels.length} level${latent.levels.length === 1 ? '' : 's'}, ` +
|
|
73
|
+
`and a ${layerSize}-texel layer has ${levelCount}`);
|
|
74
|
+
}
|
|
75
|
+
for (let level = 0; level < levelCount; level += 1) {
|
|
76
|
+
const edge = Math.max(1, layerSize >> level);
|
|
77
|
+
const source = latent.levels[level];
|
|
78
|
+
if (source.length !== edge * edge * latent.components) {
|
|
79
|
+
refuse(`level ${level} of a latent holds ${source.length} bytes, not ${edge * edge * latent.components}`);
|
|
80
|
+
}
|
|
81
|
+
const rgba = new Uint8Array(edge * edge * 4);
|
|
82
|
+
for (let texel = 0; texel < edge * edge; texel += 1) {
|
|
83
|
+
for (let c = 0; c < 4; c += 1) {
|
|
84
|
+
rgba[texel * 4 + c] =
|
|
85
|
+
c < latent.components
|
|
86
|
+
? source[texel * latent.components + c]
|
|
87
|
+
: c === 3
|
|
88
|
+
? 255
|
|
89
|
+
: 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
chunks[level].push(rgba);
|
|
93
|
+
}
|
|
94
|
+
layers += 1;
|
|
95
|
+
return layers - 1;
|
|
96
|
+
};
|
|
97
|
+
programs.forEach((program, p) => {
|
|
98
|
+
const graph = program.graph;
|
|
99
|
+
if (graph.addressMode >= DECODE_ADDRESS_MODES) {
|
|
100
|
+
refuse(`program ${p} asks for address mode ${graph.addressMode}; the interpreter knows 0 to ${DECODE_ADDRESS_MODES - 1}`);
|
|
101
|
+
}
|
|
102
|
+
if (graph.result >= DECODE_REGISTERS) {
|
|
103
|
+
refuse(`program ${p} returns register ${graph.result}, past the ${DECODE_REGISTERS} it has`);
|
|
104
|
+
}
|
|
105
|
+
const latentLayer = program.latents.map(addLatent);
|
|
106
|
+
const blockLayer = (program.blocks ?? []).map(addLatent);
|
|
107
|
+
const networkIndex = program.networks.map((network) => {
|
|
108
|
+
const { shape } = network;
|
|
109
|
+
if (shape.inputs < 1 || shape.inputs > DECODE_NETWORK_INPUTS) {
|
|
110
|
+
refuse(`a network takes ${shape.inputs} inputs; a register holds ${DECODE_NETWORK_INPUTS}`);
|
|
111
|
+
}
|
|
112
|
+
if (shape.outputs < 1 || shape.outputs > DECODE_NETWORK_OUTPUTS) {
|
|
113
|
+
refuse(`a network gives ${shape.outputs} outputs; a register holds ${DECODE_NETWORK_OUTPUTS}`);
|
|
114
|
+
}
|
|
115
|
+
if (shape.hidden.length > DECODE_NETWORK_HIDDEN_LAYERS) {
|
|
116
|
+
refuse(`a network has ${shape.hidden.length} hidden layers, past ${DECODE_NETWORK_HIDDEN_LAYERS}`);
|
|
117
|
+
}
|
|
118
|
+
for (const width of shape.hidden) {
|
|
119
|
+
if (width < 1 || width > DECODE_NETWORK_WIDTH) {
|
|
120
|
+
refuse(`a hidden layer is ${width} wide, past ${DECODE_NETWORK_WIDTH}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (network.weights.length !== weightCount(shape)) {
|
|
124
|
+
refuse(`a network carries ${network.weights.length} weights and its shape needs ${weightCount(shape)}`);
|
|
125
|
+
}
|
|
126
|
+
if (networkCount >= DECODE_NETWORK_CAPACITY) {
|
|
127
|
+
refuse(`more than ${DECODE_NETWORK_CAPACITY} networks`);
|
|
128
|
+
}
|
|
129
|
+
if (weightAt + network.weights.length > DECODE_WEIGHT_BLOCKS * 4) {
|
|
130
|
+
refuse(`the networks need more than the ${DECODE_WEIGHT_BLOCKS * 4} weights a ` +
|
|
131
|
+
`${DECODE_WEIGHTS_BYTES}-byte uniform holds`);
|
|
132
|
+
}
|
|
133
|
+
const at = NETWORK_AT + networkCount * 8;
|
|
134
|
+
nodes[at] = weightAt;
|
|
135
|
+
nodes[at + 1] = shape.inputs;
|
|
136
|
+
nodes[at + 2] = shape.outputs;
|
|
137
|
+
nodes[at + 3] = shape.hidden.length;
|
|
138
|
+
shape.hidden.forEach((width, k) => {
|
|
139
|
+
nodes[at + 4 + k] = width;
|
|
140
|
+
});
|
|
141
|
+
weights.set(network.weights, weightAt);
|
|
142
|
+
weightAt += network.weights.length;
|
|
143
|
+
networkCount += 1;
|
|
144
|
+
return networkCount - 1;
|
|
145
|
+
});
|
|
146
|
+
const constants = program.constants ?? new Float32Array(0);
|
|
147
|
+
const constantIndex = [];
|
|
148
|
+
for (let k = 0; k * 4 < constants.length; k += 1) {
|
|
149
|
+
if (constantCount >= DECODE_CONSTANT_CAPACITY) {
|
|
150
|
+
refuse(`more than ${DECODE_CONSTANT_CAPACITY} constants`);
|
|
151
|
+
}
|
|
152
|
+
for (let c = 0; c < 4; c += 1) {
|
|
153
|
+
weights[CONSTANT_AT + constantCount * 4 + c] = constants[k * 4 + c] ?? 0;
|
|
154
|
+
}
|
|
155
|
+
constantIndex.push(constantCount);
|
|
156
|
+
constantCount += 1;
|
|
157
|
+
}
|
|
158
|
+
if (nodeCount + graph.count > DECODE_NODE_CAPACITY) {
|
|
159
|
+
refuse(`more than ${DECODE_NODE_CAPACITY} decode nodes`);
|
|
160
|
+
}
|
|
161
|
+
const header = p * 4;
|
|
162
|
+
nodes[header] = nodeCount;
|
|
163
|
+
nodes[header + 1] = graph.count;
|
|
164
|
+
nodes[header + 2] = graph.result;
|
|
165
|
+
nodes[header + 3] = graph.addressMode;
|
|
166
|
+
for (let i = 0; i < graph.count; i += 1) {
|
|
167
|
+
const op = graph.nodes[i * 4];
|
|
168
|
+
let a = graph.nodes[i * 4 + 1];
|
|
169
|
+
let b = graph.nodes[i * 4 + 2];
|
|
170
|
+
const dst = graph.nodes[i * 4 + 3];
|
|
171
|
+
const args = REGISTER_ARGS[op];
|
|
172
|
+
if (args === undefined) {
|
|
173
|
+
refuse(`program ${p} node ${i} has opcode ${op}, which the interpreter does not have`);
|
|
174
|
+
}
|
|
175
|
+
if (dst >= DECODE_REGISTERS ||
|
|
176
|
+
(args[0] && a >= DECODE_REGISTERS) ||
|
|
177
|
+
(args[1] && b >= DECODE_REGISTERS)) {
|
|
178
|
+
refuse(`program ${p} node ${i} names a register past ${DECODE_REGISTERS - 1}: ${a}, ${b}, ${dst}`);
|
|
179
|
+
}
|
|
180
|
+
if (op === DECODE_OPS.SAMPLE_LATENT) {
|
|
181
|
+
a =
|
|
182
|
+
latentLayer[a] ??
|
|
183
|
+
refuse(`program ${p} node ${i} samples latent ${a}, which it does not carry`);
|
|
184
|
+
}
|
|
185
|
+
else if (op === DECODE_OPS.SAMPLE_BLOCK) {
|
|
186
|
+
a =
|
|
187
|
+
blockLayer[a] ??
|
|
188
|
+
refuse(`program ${p} node ${i} samples block ${a}, which it does not carry`);
|
|
189
|
+
}
|
|
190
|
+
else if (op === DECODE_OPS.EVAL_NETWORK) {
|
|
191
|
+
b =
|
|
192
|
+
networkIndex[b] ??
|
|
193
|
+
refuse(`program ${p} node ${i} runs network ${b}, which it does not carry`);
|
|
194
|
+
}
|
|
195
|
+
else if (op === DECODE_OPS.CONSTANT) {
|
|
196
|
+
a =
|
|
197
|
+
constantIndex[a] ??
|
|
198
|
+
refuse(`program ${p} node ${i} reads constant ${a}, which it does not carry`);
|
|
199
|
+
}
|
|
200
|
+
else if (op === DECODE_OPS.PROCEDURAL_FBM && b > DECODE_FBM_OCTAVES) {
|
|
201
|
+
refuse(`program ${p} node ${i} asks for ${b} octaves, past ${DECODE_FBM_OCTAVES}`);
|
|
202
|
+
}
|
|
203
|
+
const at = NODE_AT + nodeCount * 4;
|
|
204
|
+
nodes[at] = op;
|
|
205
|
+
nodes[at + 1] = a;
|
|
206
|
+
nodes[at + 2] = b;
|
|
207
|
+
nodes[at + 3] = dst;
|
|
208
|
+
nodeCount += 1;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
const layerCount = Math.max(1, layers);
|
|
212
|
+
const levels = chunks.map((parts, level) => {
|
|
213
|
+
const edge = Math.max(1, layerSize >> level);
|
|
214
|
+
const bytes = new Uint8Array(layerCount * edge * edge * 4);
|
|
215
|
+
parts.forEach((part, layer) => bytes.set(part, layer * edge * edge * 4));
|
|
216
|
+
return bytes;
|
|
217
|
+
});
|
|
218
|
+
return { nodes, weights, levels, layerCount, layerSize };
|
|
219
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frustum planes from a view-projection matrix, and the sphere test against them.
|
|
3
|
+
*
|
|
4
|
+
* **The reference implementation, and that is its job.** The culling arithmetic is written twice —
|
|
5
|
+
* here for the processor and in WGSL for the device — and a test asserts the two agree over
|
|
6
|
+
* generated input. `splats` already establishes that discipline for shaders authored once and
|
|
7
|
+
* generated to two backends; this is the same rule where the second copy cannot be generated.
|
|
8
|
+
*
|
|
9
|
+
* **Reversed-Z does not change the planes, only their names.** `depthConvention.ts` maps near to 1
|
|
10
|
+
* and far to 0, but the two depth planes are still "clip z at or above 0" and "clip z at or below
|
|
11
|
+
* w" — the mapping swaps which physical plane each one is, and a sphere satisfying both is inside
|
|
12
|
+
* either way. Nothing here has to know which way depth runs, which is why nothing here asks.
|
|
13
|
+
*
|
|
14
|
+
* Column-major throughout, matching the matrices the renderer already passes around.
|
|
15
|
+
*/
|
|
16
|
+
/** Six planes, four floats each: a, b, c, d with the normal pointing inward. */
|
|
17
|
+
export declare const FRUSTUM_FLOATS = 24;
|
|
18
|
+
export declare function frustumPlanes(viewProj: Float32Array, out: Float32Array): void;
|
|
19
|
+
/**
|
|
20
|
+
* Whether a bounding sphere is entirely outside the frustum.
|
|
21
|
+
*
|
|
22
|
+
* **Conservative in one direction only.** A sphere straddling a plane survives, because culling
|
|
23
|
+
* something visible is a hole in the picture and keeping something invisible is a wasted draw.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sphereOutsideFrustum(planes: Float32Array, cx: number, cy: number, cz: number, radius: number): boolean;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frustum planes from a view-projection matrix, and the sphere test against them.
|
|
3
|
+
*
|
|
4
|
+
* **The reference implementation, and that is its job.** The culling arithmetic is written twice —
|
|
5
|
+
* here for the processor and in WGSL for the device — and a test asserts the two agree over
|
|
6
|
+
* generated input. `splats` already establishes that discipline for shaders authored once and
|
|
7
|
+
* generated to two backends; this is the same rule where the second copy cannot be generated.
|
|
8
|
+
*
|
|
9
|
+
* **Reversed-Z does not change the planes, only their names.** `depthConvention.ts` maps near to 1
|
|
10
|
+
* and far to 0, but the two depth planes are still "clip z at or above 0" and "clip z at or below
|
|
11
|
+
* w" — the mapping swaps which physical plane each one is, and a sphere satisfying both is inside
|
|
12
|
+
* either way. Nothing here has to know which way depth runs, which is why nothing here asks.
|
|
13
|
+
*
|
|
14
|
+
* Column-major throughout, matching the matrices the renderer already passes around.
|
|
15
|
+
*/
|
|
16
|
+
/** Six planes, four floats each: a, b, c, d with the normal pointing inward. */
|
|
17
|
+
export const FRUSTUM_FLOATS = 24;
|
|
18
|
+
function setPlane(out, at, a, b, c, d) {
|
|
19
|
+
/* Normalised so the plane test is a true signed distance and can be compared against a radius. */
|
|
20
|
+
const length = Math.hypot(a, b, c);
|
|
21
|
+
const k = length > 0 ? 1 / length : 0;
|
|
22
|
+
out[at] = a * k;
|
|
23
|
+
out[at + 1] = b * k;
|
|
24
|
+
out[at + 2] = c * k;
|
|
25
|
+
out[at + 3] = d * k;
|
|
26
|
+
}
|
|
27
|
+
export function frustumPlanes(viewProj, out) {
|
|
28
|
+
const m = viewProj;
|
|
29
|
+
/* Rows of a column-major matrix: row i is m[i], m[4+i], m[8+i], m[12+i]. */
|
|
30
|
+
const r0 = [m[0], m[4], m[8], m[12]];
|
|
31
|
+
const r1 = [m[1], m[5], m[9], m[13]];
|
|
32
|
+
const r2 = [m[2], m[6], m[10], m[14]];
|
|
33
|
+
const r3 = [m[3], m[7], m[11], m[15]];
|
|
34
|
+
setPlane(out, 0, r3[0] + r0[0], r3[1] + r0[1], r3[2] + r0[2], r3[3] + r0[3]);
|
|
35
|
+
setPlane(out, 4, r3[0] - r0[0], r3[1] - r0[1], r3[2] - r0[2], r3[3] - r0[3]);
|
|
36
|
+
setPlane(out, 8, r3[0] + r1[0], r3[1] + r1[1], r3[2] + r1[2], r3[3] + r1[3]);
|
|
37
|
+
setPlane(out, 12, r3[0] - r1[0], r3[1] - r1[1], r3[2] - r1[2], r3[3] - r1[3]);
|
|
38
|
+
/* The two depth planes, in clip terms rather than near/far terms. See the header. */
|
|
39
|
+
setPlane(out, 16, r2[0], r2[1], r2[2], r2[3]);
|
|
40
|
+
setPlane(out, 20, r3[0] - r2[0], r3[1] - r2[1], r3[2] - r2[2], r3[3] - r2[3]);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether a bounding sphere is entirely outside the frustum.
|
|
44
|
+
*
|
|
45
|
+
* **Conservative in one direction only.** A sphere straddling a plane survives, because culling
|
|
46
|
+
* something visible is a hole in the picture and keeping something invisible is a wasted draw.
|
|
47
|
+
*/
|
|
48
|
+
export function sphereOutsideFrustum(planes, cx, cy, cz, radius) {
|
|
49
|
+
for (let p = 0; p < 6; p += 1) {
|
|
50
|
+
const at = p * 4;
|
|
51
|
+
const distance = planes[at] * cx +
|
|
52
|
+
planes[at + 1] * cy +
|
|
53
|
+
planes[at + 2] * cz +
|
|
54
|
+
planes[at + 3];
|
|
55
|
+
if (distance < -radius)
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GeometrySink, StreamCapacity } from './streamScene.ts';
|
|
2
|
+
export interface MirrorWrite {
|
|
3
|
+
readonly kind: 'vertices' | 'indices';
|
|
4
|
+
readonly first: number;
|
|
5
|
+
readonly count: number;
|
|
6
|
+
/** The array the scene handed over, kept so a test can ask whether it was the same one. */
|
|
7
|
+
readonly source: Float32Array | Uint32Array;
|
|
8
|
+
}
|
|
9
|
+
export interface MirrorSink extends GeometrySink {
|
|
10
|
+
readonly vertices: Float32Array;
|
|
11
|
+
readonly indices: Uint32Array;
|
|
12
|
+
readonly writes: MirrorWrite[];
|
|
13
|
+
}
|
|
14
|
+
/** A mirror as large as the capacity: what the pass's two buffers would hold. */
|
|
15
|
+
export declare function mirrorSink(capacity: StreamCapacity): MirrorSink;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A sink that keeps what a streaming scene writes, for tests that read its geometry back.
|
|
3
|
+
*
|
|
4
|
+
* **Not a `.test.ts` file**, for the reason `rendererHarness.ts` gives: Vitest registers a test
|
|
5
|
+
* when the file declaring it is imported, so a helper living beside tests would re-run them in
|
|
6
|
+
* every file that imported it.
|
|
7
|
+
*
|
|
8
|
+
* **The scene keeps no copy of its vertices and indices** — they go to the device of the pass
|
|
9
|
+
* that draws them, through a `GeometrySink` — so a test that wants to read them attaches one of
|
|
10
|
+
* these where the pass would have attached itself, and reads the mirror.
|
|
11
|
+
*/
|
|
12
|
+
import { GPU_DRIVEN_VERTEX_FLOATS } from './sceneUpload.js';
|
|
13
|
+
/** A mirror as large as the capacity: what the pass's two buffers would hold. */
|
|
14
|
+
export function mirrorSink(capacity) {
|
|
15
|
+
const vertices = new Float32Array(capacity.vertices * GPU_DRIVEN_VERTEX_FLOATS);
|
|
16
|
+
const indices = new Uint32Array(capacity.indices);
|
|
17
|
+
const writes = [];
|
|
18
|
+
return {
|
|
19
|
+
vertices,
|
|
20
|
+
indices,
|
|
21
|
+
writes,
|
|
22
|
+
writeVertices(first, rows, count) {
|
|
23
|
+
vertices.set(rows.subarray(0, count * GPU_DRIVEN_VERTEX_FLOATS), first * GPU_DRIVEN_VERTEX_FLOATS);
|
|
24
|
+
writes.push({ kind: 'vertices', first, count, source: rows });
|
|
25
|
+
},
|
|
26
|
+
writeIndices(first, values, count) {
|
|
27
|
+
indices.set(values.subarray(0, count), first);
|
|
28
|
+
writes.push({ kind: 'indices', first, count, source: values });
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|