@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,120 @@
|
|
|
1
|
+
import { readsOf, writesOf } from './deps.js';
|
|
2
|
+
export function createGraphPasses(capacity) {
|
|
3
|
+
return Array.from({ length: capacity }, () => ({ first: 0, count: 0, ordered: true }));
|
|
4
|
+
}
|
|
5
|
+
export function createGraphScratch(nodes, identifiers) {
|
|
6
|
+
return {
|
|
7
|
+
keep: new Uint8Array(Math.max(1, nodes)),
|
|
8
|
+
wanted: new Uint8Array(Math.max(1, identifiers)),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/*
|
|
12
|
+
* Make the scratch big enough for this frame, which is the one place it may allocate.
|
|
13
|
+
*
|
|
14
|
+
* A frame that outgrows it has to be scheduled somehow, and growing here once is what the arena
|
|
15
|
+
* does too; the scratch the caller keeps is then large enough for every frame like it. Doubled, so
|
|
16
|
+
* a frame that grows by one node at a time does not reallocate every frame.
|
|
17
|
+
*/
|
|
18
|
+
function fitScratch(scratch, deps, count) {
|
|
19
|
+
if (scratch.keep.length < count) {
|
|
20
|
+
scratch.keep = new Uint8Array(Math.max(count, scratch.keep.length * 2));
|
|
21
|
+
}
|
|
22
|
+
let ceiling = 0;
|
|
23
|
+
for (let i = 0; i < deps.used; i += 1)
|
|
24
|
+
ceiling = Math.max(ceiling, (deps.edges[i] ?? 0) + 1);
|
|
25
|
+
if (scratch.wanted.length < ceiling) {
|
|
26
|
+
scratch.wanted = new Uint8Array(Math.max(ceiling, scratch.wanted.length * 2));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/*
|
|
30
|
+
* Mark every node that contributes to something in `live`, to a fixed point.
|
|
31
|
+
*
|
|
32
|
+
* Backwards, because a node is needed exactly when something after it reads what it writes. One
|
|
33
|
+
* backward sweep suffices for a chain; the outer repeat covers a node that writes a resource
|
|
34
|
+
* read by an earlier-indexed node, which cannot happen in a well-formed frame but costs one
|
|
35
|
+
* comparison to be safe about rather than one silently wrong picture to discover.
|
|
36
|
+
*
|
|
37
|
+
* `fitScratch` sized both flags for everything recorded, and this relies on it: a keep flag past
|
|
38
|
+
* the end of `keep` would read 0 after being set, and the sweep would never settle.
|
|
39
|
+
*/
|
|
40
|
+
function markLive(deps, count, live, liveCount, scratch) {
|
|
41
|
+
const { keep, wanted } = scratch;
|
|
42
|
+
keep.fill(0, 0, count);
|
|
43
|
+
wanted.fill(0);
|
|
44
|
+
/* A live identifier past the end is one no node writes, and a typed array ignores the write. */
|
|
45
|
+
for (let i = 0; i < liveCount; i += 1)
|
|
46
|
+
wanted[live[i] ?? -1] = 1;
|
|
47
|
+
let changed = true;
|
|
48
|
+
while (changed) {
|
|
49
|
+
changed = false;
|
|
50
|
+
for (let node = count - 1; node >= 0; node -= 1) {
|
|
51
|
+
if (keep[node] === 1)
|
|
52
|
+
continue;
|
|
53
|
+
const writes = writesOf(deps, node);
|
|
54
|
+
let needed = false;
|
|
55
|
+
for (let i = 0; i < writes.length; i += 1) {
|
|
56
|
+
if (wanted[writes[i] ?? 0] === 1) {
|
|
57
|
+
needed = true;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!needed)
|
|
62
|
+
continue;
|
|
63
|
+
keep[node] = 1;
|
|
64
|
+
changed = true;
|
|
65
|
+
const reads = readsOf(deps, node);
|
|
66
|
+
for (let i = 0; i < reads.length; i += 1)
|
|
67
|
+
wanted[reads[i] ?? 0] = 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function sameWrites(deps, a, b) {
|
|
72
|
+
const left = writesOf(deps, a);
|
|
73
|
+
const right = writesOf(deps, b);
|
|
74
|
+
if (left.length !== right.length)
|
|
75
|
+
return false;
|
|
76
|
+
for (let i = 0; i < left.length; i += 1)
|
|
77
|
+
if (left[i] !== right[i])
|
|
78
|
+
return false;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Group the live nodes of a frame into passes, and answer how many of `out` were filled.
|
|
83
|
+
*
|
|
84
|
+
* `live` is what must survive the frame, and only its first `liveCount` entries are read — so a
|
|
85
|
+
* caller can keep one typed array and a count rather than build a list. `scratch` is the working
|
|
86
|
+
* set; a caller that passes none gets one made for the call, which is what a test wants and what a
|
|
87
|
+
* frame must not do.
|
|
88
|
+
*/
|
|
89
|
+
export function scheduleGraph(deps, count, live, ordered, out, scratch = createGraphScratch(count, 1), liveCount = live.length) {
|
|
90
|
+
fitScratch(scratch, deps, count);
|
|
91
|
+
markLive(deps, count, live, liveCount, scratch);
|
|
92
|
+
const keep = scratch.keep;
|
|
93
|
+
let passes = 0;
|
|
94
|
+
let at = 0;
|
|
95
|
+
while (at < count) {
|
|
96
|
+
if (keep[at] === 0) {
|
|
97
|
+
at += 1;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const isOrdered = ordered[at] === 1;
|
|
101
|
+
let end = at + 1;
|
|
102
|
+
while (end < count && keep[end] === 1) {
|
|
103
|
+
const nextOrdered = ordered[end] === 1;
|
|
104
|
+
if (isOrdered !== nextOrdered)
|
|
105
|
+
break;
|
|
106
|
+
if (isOrdered && !sameWrites(deps, at, end))
|
|
107
|
+
break;
|
|
108
|
+
end += 1;
|
|
109
|
+
}
|
|
110
|
+
const pass = out[passes];
|
|
111
|
+
if (pass === undefined)
|
|
112
|
+
return passes;
|
|
113
|
+
pass.first = at;
|
|
114
|
+
pass.count = end - at;
|
|
115
|
+
pass.ordered = isOrdered;
|
|
116
|
+
passes += 1;
|
|
117
|
+
at = end;
|
|
118
|
+
}
|
|
119
|
+
return passes;
|
|
120
|
+
}
|
|
@@ -16,3 +16,18 @@ export { NODE_STRIDE, createArena, nodeCount, nodeMatrix, nodeReads, nodeState,
|
|
|
16
16
|
export type { ScheduledPass } from './schedule.ts';
|
|
17
17
|
export { schedule } from './schedule.ts';
|
|
18
18
|
export { keptNodes, scratchFor } from './replay.ts';
|
|
19
|
+
export type { VirtualBufferDesc, VirtualDesc, VirtualTable, VirtualTextureDesc, } from './virtual.ts';
|
|
20
|
+
export { createVirtualTable, declareVirtual, resetVirtualTable, virtualBytes, virtualCount, virtualKind, } from './virtual.ts';
|
|
21
|
+
export type { Deps } from './deps.ts';
|
|
22
|
+
export { createDeps, depsNodeCount, readsOf, recordDeps, recordMaskDeps, resetDeps, writesOf, } from './deps.ts';
|
|
23
|
+
export type { Lifetimes } from './lifetime.ts';
|
|
24
|
+
export { computeLifetimes, createLifetimes, firstWrite, lastRead } from './lifetime.ts';
|
|
25
|
+
export type { AliasPlan } from './alias.ts';
|
|
26
|
+
export { createAliasPlan, offsetOf, planAliases } from './alias.ts';
|
|
27
|
+
export type { GraphPass, GraphScratch } from './graphSchedule.ts';
|
|
28
|
+
export { createGraphPasses, createGraphScratch, scheduleGraph } from './graphSchedule.ts';
|
|
29
|
+
export type { FlushSchedule } from './flushGraph.ts';
|
|
30
|
+
export { createFlushSchedule, scheduleFlush } from './flushGraph.ts';
|
|
31
|
+
export { validateGraph } from './validate.ts';
|
|
32
|
+
export type { PassTimings } from './passTimings.ts';
|
|
33
|
+
export { createPassTimings, passLabel, passMs, recordPassLabel, recordPassSample, resetPassTimings, } from './passTimings.ts';
|
|
@@ -2,3 +2,11 @@ export { FRAME_RESOURCES, RESOURCE_COUNT, maskOf, namesIn, resourceBit } from '.
|
|
|
2
2
|
export { NODE_STRIDE, createArena, nodeCount, nodeMatrix, nodeReads, nodeState, nodeVerb, nodeWrites, recordNode, resetArena, } from './arena.js';
|
|
3
3
|
export { schedule } from './schedule.js';
|
|
4
4
|
export { keptNodes, scratchFor } from './replay.js';
|
|
5
|
+
export { createVirtualTable, declareVirtual, resetVirtualTable, virtualBytes, virtualCount, virtualKind, } from './virtual.js';
|
|
6
|
+
export { createDeps, depsNodeCount, readsOf, recordDeps, recordMaskDeps, resetDeps, writesOf, } from './deps.js';
|
|
7
|
+
export { computeLifetimes, createLifetimes, firstWrite, lastRead } from './lifetime.js';
|
|
8
|
+
export { createAliasPlan, offsetOf, planAliases } from './alias.js';
|
|
9
|
+
export { createGraphPasses, createGraphScratch, scheduleGraph } from './graphSchedule.js';
|
|
10
|
+
export { createFlushSchedule, scheduleFlush } from './flushGraph.js';
|
|
11
|
+
export { validateGraph } from './validate.js';
|
|
12
|
+
export { createPassTimings, passLabel, passMs, recordPassLabel, recordPassSample, resetPassTimings, } from './passTimings.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When each transient resource is first written and last read.
|
|
3
|
+
*
|
|
4
|
+
* Two numbers per identifier and one pass over the nodes, which is all the aliasing allocator
|
|
5
|
+
* needs and is deliberately less than a general dependency graph would compute. There is no
|
|
6
|
+
* topological sort here because there is no reordering here: the node order is the caller's,
|
|
7
|
+
* exactly as `schedule.ts` requires for the fixed path and for the same reason — blending and
|
|
8
|
+
* depth make *after* different from *before*.
|
|
9
|
+
*
|
|
10
|
+
* **A resource written and never read still has a lifetime**, ending at the node that writes
|
|
11
|
+
* it. It has to: memory it is being written into cannot be handed to something else at the same
|
|
12
|
+
* moment. Reporting an empty lifetime there is the bug this module's third test exists to catch.
|
|
13
|
+
*/
|
|
14
|
+
import type { Deps } from './deps.ts';
|
|
15
|
+
export interface Lifetimes {
|
|
16
|
+
/** Node index of the first write per identifier, or -1. */
|
|
17
|
+
first: Int32Array;
|
|
18
|
+
/** Node index of the last read per identifier, or the first write when nothing reads it. */
|
|
19
|
+
last: Int32Array;
|
|
20
|
+
}
|
|
21
|
+
export declare function createLifetimes(capacity: number): Lifetimes;
|
|
22
|
+
export declare function computeLifetimes(deps: Deps, count: number, out: Lifetimes): void;
|
|
23
|
+
export declare function firstWrite(out: Lifetimes, id: number): number;
|
|
24
|
+
export declare function lastRead(out: Lifetimes, id: number): number;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readsOf, writesOf } from './deps.js';
|
|
2
|
+
export function createLifetimes(capacity) {
|
|
3
|
+
return { first: new Int32Array(capacity), last: new Int32Array(capacity) };
|
|
4
|
+
}
|
|
5
|
+
export function computeLifetimes(deps, count, out) {
|
|
6
|
+
out.first.fill(-1);
|
|
7
|
+
out.last.fill(-1);
|
|
8
|
+
for (let node = 0; node < count; node += 1) {
|
|
9
|
+
const writes = writesOf(deps, node);
|
|
10
|
+
for (let i = 0; i < writes.length; i += 1) {
|
|
11
|
+
const id = writes[i] ?? 0;
|
|
12
|
+
if (out.first[id] === -1) {
|
|
13
|
+
out.first[id] = node;
|
|
14
|
+
out.last[id] = node;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/*
|
|
18
|
+
* Reads are folded in after this node's writes, so a node that reads and writes the same
|
|
19
|
+
* identifier extends the lifetime rather than starting a second one. A read of something
|
|
20
|
+
* nothing has written is ignored here and refused by `validate.ts`, which is where a
|
|
21
|
+
* malformed frame belongs.
|
|
22
|
+
*/
|
|
23
|
+
const reads = readsOf(deps, node);
|
|
24
|
+
for (let i = 0; i < reads.length; i += 1) {
|
|
25
|
+
const id = reads[i] ?? 0;
|
|
26
|
+
if (out.first[id] !== -1)
|
|
27
|
+
out.last[id] = node;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function firstWrite(out, id) {
|
|
32
|
+
return out.first[id] ?? -1;
|
|
33
|
+
}
|
|
34
|
+
export function lastRead(out, id) {
|
|
35
|
+
return out.last[id] ?? -1;
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a frame's milliseconds went, one row per graph pass.
|
|
3
|
+
*
|
|
4
|
+
* **Not a widening of `gpuTimer.ts`, and that is deliberate.** That class is a WebGL2 query pool
|
|
5
|
+
* over `TIME_ELAPSED_EXT`, partitioning a frame into three fixed brackets — shadows, reflection,
|
|
6
|
+
* rest — and it is right for what it measures. A graph pass is not one of three brackets: there
|
|
7
|
+
* are as many as the frame composes, they are named at record time, and on WebGPU the sample
|
|
8
|
+
* comes from a timestamp query rather than an elapsed-time extension. Two different measurements
|
|
9
|
+
* with two different shapes; widening one into the other would leave a class that does neither
|
|
10
|
+
* well.
|
|
11
|
+
*
|
|
12
|
+
* **What it does inherit from that class is its best rule: unmeasured is not zero.** A device
|
|
13
|
+
* without timestamp queries, or a pass that ran no work, must not read as "took no time" — a
|
|
14
|
+
* profiler that cannot distinguish those two is a profiler that sends somebody optimising a pass
|
|
15
|
+
* that never ran. `passMs` returns null for unmeasured and a number for measured, including zero.
|
|
16
|
+
*
|
|
17
|
+
* **Labels persist and timings reset.** A pass that did nothing this frame keeps its name and
|
|
18
|
+
* reports unmeasured, so the profiler's rows stop jumping about as quality settings toggle
|
|
19
|
+
* passes on and off — which is exactly when somebody is reading it.
|
|
20
|
+
*/
|
|
21
|
+
export interface PassTimings {
|
|
22
|
+
/** Accumulated milliseconds per pass for the current frame. */
|
|
23
|
+
ms: Float64Array;
|
|
24
|
+
/** Whether each pass received a sample this frame. */
|
|
25
|
+
measured: Uint8Array;
|
|
26
|
+
/** Name per pass, kept across frames. */
|
|
27
|
+
labels: string[];
|
|
28
|
+
}
|
|
29
|
+
export declare function createPassTimings(capacity: number): PassTimings;
|
|
30
|
+
/** Start a frame: timings go, labels stay. See the header for why they are treated differently. */
|
|
31
|
+
export declare function resetPassTimings(timings: PassTimings): void;
|
|
32
|
+
/**
|
|
33
|
+
* Add one sample to a pass.
|
|
34
|
+
*
|
|
35
|
+
* Accumulates rather than replaces, because a pass may be sampled more than once in a frame — a
|
|
36
|
+
* shadow pass runs per cascade — and the row a reader wants is what that pass cost altogether.
|
|
37
|
+
*/
|
|
38
|
+
export declare function recordPassSample(timings: PassTimings, pass: number, ms: number): void;
|
|
39
|
+
export declare function recordPassLabel(timings: PassTimings, pass: number, label: string): void;
|
|
40
|
+
/** Milliseconds this pass cost, or null where nothing measured it. Null is not zero. */
|
|
41
|
+
export declare function passMs(timings: PassTimings, pass: number): number | null;
|
|
42
|
+
export declare function passLabel(timings: PassTimings, pass: number): string;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function createPassTimings(capacity) {
|
|
2
|
+
return {
|
|
3
|
+
ms: new Float64Array(capacity),
|
|
4
|
+
measured: new Uint8Array(capacity),
|
|
5
|
+
labels: Array.from({ length: capacity }, () => ''),
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
/** Start a frame: timings go, labels stay. See the header for why they are treated differently. */
|
|
9
|
+
export function resetPassTimings(timings) {
|
|
10
|
+
timings.ms.fill(0);
|
|
11
|
+
timings.measured.fill(0);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Add one sample to a pass.
|
|
15
|
+
*
|
|
16
|
+
* Accumulates rather than replaces, because a pass may be sampled more than once in a frame — a
|
|
17
|
+
* shadow pass runs per cascade — and the row a reader wants is what that pass cost altogether.
|
|
18
|
+
*/
|
|
19
|
+
export function recordPassSample(timings, pass, ms) {
|
|
20
|
+
if (pass < 0 || pass >= timings.ms.length)
|
|
21
|
+
return;
|
|
22
|
+
timings.ms[pass] = (timings.ms[pass] ?? 0) + ms;
|
|
23
|
+
timings.measured[pass] = 1;
|
|
24
|
+
}
|
|
25
|
+
export function recordPassLabel(timings, pass, label) {
|
|
26
|
+
if (pass < 0 || pass >= timings.labels.length)
|
|
27
|
+
return;
|
|
28
|
+
timings.labels[pass] = label;
|
|
29
|
+
}
|
|
30
|
+
/** Milliseconds this pass cost, or null where nothing measured it. Null is not zero. */
|
|
31
|
+
export function passMs(timings, pass) {
|
|
32
|
+
if (pass < 0 || pass >= timings.ms.length)
|
|
33
|
+
return null;
|
|
34
|
+
return timings.measured[pass] === 1 ? (timings.ms[pass] ?? 0) : null;
|
|
35
|
+
}
|
|
36
|
+
export function passLabel(timings, pass) {
|
|
37
|
+
return timings.labels[pass] ?? '';
|
|
38
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a recorded frame is one that can be executed.
|
|
3
|
+
*
|
|
4
|
+
* One rule: a node may read an identifier only if something wrote it earlier in this frame, or
|
|
5
|
+
* it was imported into the frame from outside. That catches the whole family of defects a fixed
|
|
6
|
+
* resource table caught by construction — a pass sampling an attachment the quality settings
|
|
7
|
+
* disabled, a pass reading a mip chain built only when a different feature is on — and which
|
|
8
|
+
* identifiers do not catch by construction, because any integer is a plausible identifier.
|
|
9
|
+
*
|
|
10
|
+
* Returns a message rather than throwing. The renderer decides whether a malformed frame is a
|
|
11
|
+
* developer-time assertion or a dropped frame, and a validator that throws has made that
|
|
12
|
+
* decision for it.
|
|
13
|
+
*/
|
|
14
|
+
import type { Deps } from './deps.ts';
|
|
15
|
+
export declare function validateGraph(deps: Deps, count: number, imported: readonly number[]): string | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readsOf, writesOf } from './deps.js';
|
|
2
|
+
export function validateGraph(deps, count, imported) {
|
|
3
|
+
const written = new Set(imported);
|
|
4
|
+
for (let node = 0; node < count; node += 1) {
|
|
5
|
+
/*
|
|
6
|
+
* Reads are checked before this node's writes join the set, which is what makes a read of
|
|
7
|
+
* something written only later a refusal and a read-modify-write legal — the latter is
|
|
8
|
+
* reading contents something earlier put there.
|
|
9
|
+
*/
|
|
10
|
+
const reads = readsOf(deps, node);
|
|
11
|
+
for (let i = 0; i < reads.length; i += 1) {
|
|
12
|
+
const id = reads[i] ?? 0;
|
|
13
|
+
if (!written.has(id)) {
|
|
14
|
+
return `node ${node} reads resource ${id}, which nothing wrote and nothing imported`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const writes = writesOf(deps, node);
|
|
18
|
+
for (let i = 0; i < writes.length; i += 1)
|
|
19
|
+
written.add(writes[i] ?? 0);
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transient resources a frame declares, addressed by identifier rather than by bit.
|
|
3
|
+
*
|
|
4
|
+
* **`resources.ts` is not replaced and is not wrong.** It is a fixed table of fourteen
|
|
5
|
+
* attachments held in a bit mask, and it says in its own header that the transient allocator a
|
|
6
|
+
* frame graph is famous for would be machinery with no case to serve — which was true while
|
|
7
|
+
* this frame's composition was fixed. A GPU-driven frame's composition is not: hierarchical
|
|
8
|
+
* depth chains, visibility buffers, per-material bin lists and indirect argument buffers are
|
|
9
|
+
* allocated against what the frame contains. Thirty-one bits cannot hold hundreds of them and
|
|
10
|
+
* a thirty-second bit is not the fix.
|
|
11
|
+
*
|
|
12
|
+
* So there are two representations and each is used where it is right: the mask for a fixed
|
|
13
|
+
* composition, identifiers for a dynamic one. Nothing converts between them, because nothing
|
|
14
|
+
* needs to — a frame is scheduled by one or the other.
|
|
15
|
+
*
|
|
16
|
+
* Structure of arrays, growable by doubling, and nothing here holds an object per resource.
|
|
17
|
+
*/
|
|
18
|
+
/** A transient texture, sized in texels. */
|
|
19
|
+
export interface VirtualTextureDesc {
|
|
20
|
+
kind: 'texture';
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
bytesPerTexel: number;
|
|
24
|
+
}
|
|
25
|
+
/** A transient buffer, sized in bytes. */
|
|
26
|
+
export interface VirtualBufferDesc {
|
|
27
|
+
kind: 'buffer';
|
|
28
|
+
bytes: number;
|
|
29
|
+
}
|
|
30
|
+
export type VirtualDesc = VirtualTextureDesc | VirtualBufferDesc;
|
|
31
|
+
export interface VirtualTable {
|
|
32
|
+
/** 0 for a texture, 1 for a buffer, one entry per identifier. */
|
|
33
|
+
kinds: Uint8Array;
|
|
34
|
+
/** Byte footprint per identifier, which is what the aliasing allocator packs. */
|
|
35
|
+
bytes: Uint32Array;
|
|
36
|
+
/** How many identifiers have been declared this frame. */
|
|
37
|
+
count: number;
|
|
38
|
+
}
|
|
39
|
+
export declare function createVirtualTable(capacity: number): VirtualTable;
|
|
40
|
+
export declare function resetVirtualTable(table: VirtualTable): void;
|
|
41
|
+
export declare function virtualCount(table: VirtualTable): number;
|
|
42
|
+
export declare function declareVirtual(table: VirtualTable, desc: VirtualDesc): number;
|
|
43
|
+
export declare function virtualKind(table: VirtualTable, id: number): number;
|
|
44
|
+
export declare function virtualBytes(table: VirtualTable, id: number): number;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transient resources a frame declares, addressed by identifier rather than by bit.
|
|
3
|
+
*
|
|
4
|
+
* **`resources.ts` is not replaced and is not wrong.** It is a fixed table of fourteen
|
|
5
|
+
* attachments held in a bit mask, and it says in its own header that the transient allocator a
|
|
6
|
+
* frame graph is famous for would be machinery with no case to serve — which was true while
|
|
7
|
+
* this frame's composition was fixed. A GPU-driven frame's composition is not: hierarchical
|
|
8
|
+
* depth chains, visibility buffers, per-material bin lists and indirect argument buffers are
|
|
9
|
+
* allocated against what the frame contains. Thirty-one bits cannot hold hundreds of them and
|
|
10
|
+
* a thirty-second bit is not the fix.
|
|
11
|
+
*
|
|
12
|
+
* So there are two representations and each is used where it is right: the mask for a fixed
|
|
13
|
+
* composition, identifiers for a dynamic one. Nothing converts between them, because nothing
|
|
14
|
+
* needs to — a frame is scheduled by one or the other.
|
|
15
|
+
*
|
|
16
|
+
* Structure of arrays, growable by doubling, and nothing here holds an object per resource.
|
|
17
|
+
*/
|
|
18
|
+
export function createVirtualTable(capacity) {
|
|
19
|
+
return { kinds: new Uint8Array(capacity), bytes: new Uint32Array(capacity), count: 0 };
|
|
20
|
+
}
|
|
21
|
+
export function resetVirtualTable(table) {
|
|
22
|
+
table.count = 0;
|
|
23
|
+
}
|
|
24
|
+
export function virtualCount(table) {
|
|
25
|
+
return table.count;
|
|
26
|
+
}
|
|
27
|
+
/*
|
|
28
|
+
* Doubling rather than growing by one, because a frame that declares one more resource than
|
|
29
|
+
* last frame will declare one more again, and a table that reallocates per declaration turns a
|
|
30
|
+
* linear frame into a quadratic one.
|
|
31
|
+
*/
|
|
32
|
+
function grow(table) {
|
|
33
|
+
const size = table.kinds.length === 0 ? 8 : table.kinds.length * 2;
|
|
34
|
+
const kinds = new Uint8Array(size);
|
|
35
|
+
kinds.set(table.kinds);
|
|
36
|
+
const bytes = new Uint32Array(size);
|
|
37
|
+
bytes.set(table.bytes);
|
|
38
|
+
table.kinds = kinds;
|
|
39
|
+
table.bytes = bytes;
|
|
40
|
+
}
|
|
41
|
+
export function declareVirtual(table, desc) {
|
|
42
|
+
if (table.count === table.kinds.length)
|
|
43
|
+
grow(table);
|
|
44
|
+
const id = table.count;
|
|
45
|
+
if (desc.kind === 'texture') {
|
|
46
|
+
table.kinds[id] = 0;
|
|
47
|
+
table.bytes[id] = desc.width * desc.height * desc.bytesPerTexel;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
table.kinds[id] = 1;
|
|
51
|
+
table.bytes[id] = desc.bytes;
|
|
52
|
+
}
|
|
53
|
+
table.count = id + 1;
|
|
54
|
+
return id;
|
|
55
|
+
}
|
|
56
|
+
export function virtualKind(table, id) {
|
|
57
|
+
return table.kinds[id] ?? 0;
|
|
58
|
+
}
|
|
59
|
+
export function virtualBytes(table, id) {
|
|
60
|
+
return table.bytes[id] ?? 0;
|
|
61
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** The three levels in one place, and the rule that there is never a fourth. */
|
|
2
|
+
import type { ReadonlyVec3 } from 'gl-matrix';
|
|
3
|
+
import type { ProbeGrid } from '../probeGrid.ts';
|
|
4
|
+
import type { ScreenSpaceMarch } from '../screenSpaceReflection.ts';
|
|
5
|
+
import type { GlobalField } from './globalField.ts';
|
|
6
|
+
import type { ProbeVisibility } from './probeVolume.ts';
|
|
7
|
+
import type { FieldMarch } from './traceField.ts';
|
|
8
|
+
/**
|
|
9
|
+
* **Three levels, and the architecture is the fallback rather than any one of them.**
|
|
10
|
+
*
|
|
11
|
+
* `ROADMAP.md` refused screen-space global illumination, and this does not reverse that refusal —
|
|
12
|
+
* it satisfies the requirement the refusal was about. Screen space is exact wherever the screen has
|
|
13
|
+
* the answer and has *no* answer where it does not, which as a terminal technique means inventing
|
|
14
|
+
* one from whatever happened to be in frame. Here it is an accelerator with something behind it:
|
|
15
|
+
* a world-space distance field whose error is bounded by its own resolution, and behind that a
|
|
16
|
+
* probe volume which is never wrong and only ever coarse.
|
|
17
|
+
*
|
|
18
|
+
* **The last level cannot fail, and that is the load-bearing sentence.** A ray with no answer
|
|
19
|
+
* leaves a shader nothing to write but what the buffer held, which is the previous frame and reads
|
|
20
|
+
* as a flicker. A probe volume is an interpolation between measurements — no ray to escape, no
|
|
21
|
+
* frame to leave — and `visibleProbes` falls back to the nearest probe even when every visibility
|
|
22
|
+
* weight has crushed to zero. So there are exactly three cases.
|
|
23
|
+
*
|
|
24
|
+
* **And the levels blend rather than switch.** Every boundary between them moves with the camera:
|
|
25
|
+
* the frame's edge does by definition, and the cascades are centred on the camera so their faces do
|
|
26
|
+
* too. A hard switch at a boundary that moves is a seam that sweeps across the picture whenever the
|
|
27
|
+
* camera turns, which is worse than either level's own error — an error that is wrong everywhere
|
|
28
|
+
* reads as a coarse solution, and one that changes abruptly along a surface reads as a crack.
|
|
29
|
+
*/
|
|
30
|
+
export declare const GI_SOURCE_SCREEN = 0;
|
|
31
|
+
export declare const GI_SOURCE_FIELD = 1;
|
|
32
|
+
export declare const GI_SOURCE_PROBES = 2;
|
|
33
|
+
export type GiSource = 0 | 1 | 2;
|
|
34
|
+
/** One indirect ray, as the shading path hands it over. */
|
|
35
|
+
export interface IndirectRay {
|
|
36
|
+
/** Where it starts, on the surface being shaded. */
|
|
37
|
+
readonly point: ReadonlyVec3;
|
|
38
|
+
/** That surface's normal, which both traces lift off along. */
|
|
39
|
+
readonly normal: ReadonlyVec3;
|
|
40
|
+
readonly direction: ReadonlyVec3;
|
|
41
|
+
/** Cone half-angle for the world trace. Zero is a mirror; wider is a rougher surface. */
|
|
42
|
+
readonly coneAngle: number;
|
|
43
|
+
}
|
|
44
|
+
/** Everything the three levels need, gathered so a per-ray call takes one object. */
|
|
45
|
+
export interface GiResources {
|
|
46
|
+
readonly eye: ReadonlyVec3;
|
|
47
|
+
readonly project: (x: number, y: number, z: number, uv: Float32Array) => boolean;
|
|
48
|
+
/** How far the eye is from whatever the frame drew, and `Infinity` where it drew nothing. */
|
|
49
|
+
readonly sceneDistance: (u: number, v: number) => number;
|
|
50
|
+
/** The radiance the frame holds at a screen position. */
|
|
51
|
+
readonly screenRadiance: (u: number, v: number, out: Float32Array) => void;
|
|
52
|
+
readonly field: GlobalField;
|
|
53
|
+
/** The radiance at a world point the field trace arrived at. */
|
|
54
|
+
readonly fieldRadiance: (x: number, y: number, z: number, out: Float32Array) => void;
|
|
55
|
+
readonly grid: ProbeGrid;
|
|
56
|
+
/** Null is the off switch, and it lands on the grid's own blend. See `probeVolume.ts`. */
|
|
57
|
+
readonly visibility: ProbeVisibility | null;
|
|
58
|
+
/** `probeChannels` floats a probe, layer-major. */
|
|
59
|
+
readonly probeValues: Float32Array;
|
|
60
|
+
readonly probeChannels: number;
|
|
61
|
+
readonly screenMarch?: ScreenSpaceMarch;
|
|
62
|
+
readonly fieldMarch?: FieldMarch;
|
|
63
|
+
}
|
|
64
|
+
export interface IndirectResult {
|
|
65
|
+
/** Always true. See the module header: there is no fourth case. */
|
|
66
|
+
hit: boolean;
|
|
67
|
+
/** Which level contributed most. The radiance is the blend, not this level alone. */
|
|
68
|
+
source: GiSource;
|
|
69
|
+
/** Three channels. */
|
|
70
|
+
readonly radiance: Float32Array;
|
|
71
|
+
/** How much of the answer came from each level, summing to one. */
|
|
72
|
+
readonly blend: Float32Array;
|
|
73
|
+
}
|
|
74
|
+
export declare function newIndirectResult(): IndirectResult;
|
|
75
|
+
/**
|
|
76
|
+
* Trace one indirect ray down the chain, and always answer.
|
|
77
|
+
*
|
|
78
|
+
* The screen answers as much as its distance from the edge of the frame allows; what is left goes
|
|
79
|
+
* to the world field, as much as *its* distance from the edge of the outermost cascade allows; and
|
|
80
|
+
* whatever is left after that goes to the probes, which have nothing behind them and take it all.
|
|
81
|
+
*/
|
|
82
|
+
export declare function traceIndirect(ray: IndirectRay, resources: GiResources, out: IndirectResult): IndirectResult;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/** The three levels in one place, and the rule that there is never a fourth. */
|
|
2
|
+
import { createProbeBlend } from '../probeGrid.js';
|
|
3
|
+
import { REFLECTION_EDGE_FADE, edgeFade, newScreenSpaceHit } from '../screenSpaceReflection.js';
|
|
4
|
+
import { GLOBAL_FIELD_BLEND } from './globalField.js';
|
|
5
|
+
import { sampleProbeVolume, visibleProbes } from './probeVolume.js';
|
|
6
|
+
import { FIELD_MARCH, newFieldHit, traceField } from './traceField.js';
|
|
7
|
+
import { GI_SCREEN_MARCH, traceScreen } from './traceScreen.js';
|
|
8
|
+
/**
|
|
9
|
+
* **Three levels, and the architecture is the fallback rather than any one of them.**
|
|
10
|
+
*
|
|
11
|
+
* `ROADMAP.md` refused screen-space global illumination, and this does not reverse that refusal —
|
|
12
|
+
* it satisfies the requirement the refusal was about. Screen space is exact wherever the screen has
|
|
13
|
+
* the answer and has *no* answer where it does not, which as a terminal technique means inventing
|
|
14
|
+
* one from whatever happened to be in frame. Here it is an accelerator with something behind it:
|
|
15
|
+
* a world-space distance field whose error is bounded by its own resolution, and behind that a
|
|
16
|
+
* probe volume which is never wrong and only ever coarse.
|
|
17
|
+
*
|
|
18
|
+
* **The last level cannot fail, and that is the load-bearing sentence.** A ray with no answer
|
|
19
|
+
* leaves a shader nothing to write but what the buffer held, which is the previous frame and reads
|
|
20
|
+
* as a flicker. A probe volume is an interpolation between measurements — no ray to escape, no
|
|
21
|
+
* frame to leave — and `visibleProbes` falls back to the nearest probe even when every visibility
|
|
22
|
+
* weight has crushed to zero. So there are exactly three cases.
|
|
23
|
+
*
|
|
24
|
+
* **And the levels blend rather than switch.** Every boundary between them moves with the camera:
|
|
25
|
+
* the frame's edge does by definition, and the cascades are centred on the camera so their faces do
|
|
26
|
+
* too. A hard switch at a boundary that moves is a seam that sweeps across the picture whenever the
|
|
27
|
+
* camera turns, which is worse than either level's own error — an error that is wrong everywhere
|
|
28
|
+
* reads as a coarse solution, and one that changes abruptly along a surface reads as a crack.
|
|
29
|
+
*/
|
|
30
|
+
export const GI_SOURCE_SCREEN = 0;
|
|
31
|
+
export const GI_SOURCE_FIELD = 1;
|
|
32
|
+
export const GI_SOURCE_PROBES = 2;
|
|
33
|
+
export function newIndirectResult() {
|
|
34
|
+
return {
|
|
35
|
+
hit: false,
|
|
36
|
+
source: GI_SOURCE_PROBES,
|
|
37
|
+
radiance: new Float32Array(3),
|
|
38
|
+
blend: new Float32Array(3),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Scratch, because this runs once per ray per pixel and the rule about that is absolute. */
|
|
42
|
+
const SCREEN_HIT = newScreenSpaceHit();
|
|
43
|
+
const FIELD_HIT = newFieldHit();
|
|
44
|
+
const PROBE_BLEND = createProbeBlend();
|
|
45
|
+
const LEVEL = new Float32Array(3);
|
|
46
|
+
const PROBE_OUT = new Float32Array(3);
|
|
47
|
+
/**
|
|
48
|
+
* Trace one indirect ray down the chain, and always answer.
|
|
49
|
+
*
|
|
50
|
+
* The screen answers as much as its distance from the edge of the frame allows; what is left goes
|
|
51
|
+
* to the world field, as much as *its* distance from the edge of the outermost cascade allows; and
|
|
52
|
+
* whatever is left after that goes to the probes, which have nothing behind them and take it all.
|
|
53
|
+
*/
|
|
54
|
+
export function traceIndirect(ray, resources, out) {
|
|
55
|
+
out.radiance.fill(0);
|
|
56
|
+
out.blend.fill(0);
|
|
57
|
+
out.hit = true;
|
|
58
|
+
/* Level 0. Weighted by how far the hit landed from the edge of the frame, which is the one
|
|
59
|
+
boundary a viewer is guaranteed to see move. */
|
|
60
|
+
let screen = 0;
|
|
61
|
+
if (traceScreen(ray.point, ray.normal, ray.direction, resources.eye, resources.screenMarch ?? GI_SCREEN_MARCH, resources.project, resources.sceneDistance, SCREEN_HIT)) {
|
|
62
|
+
/*
|
|
63
|
+
* **The *path*'s margin, not the hit's.** Fading only the hit leaves the seam exactly where it
|
|
64
|
+
* was: a ray that grazes the border and lands is faded, and the ray one pixel over leaves the
|
|
65
|
+
* frame and answers nothing at all, so the weight still steps from one to zero across a line
|
|
66
|
+
* that sweeps as the camera turns. See `ScreenSpaceHit.margin`.
|
|
67
|
+
*/
|
|
68
|
+
screen = edgeFade(SCREEN_HIT.margin, REFLECTION_EDGE_FADE);
|
|
69
|
+
if (screen > 0) {
|
|
70
|
+
resources.screenRadiance(SCREEN_HIT.u, SCREEN_HIT.v, LEVEL);
|
|
71
|
+
add(out.radiance, LEVEL, screen);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/* Level 1, for whatever the screen did not answer. Weighted the same way against the outermost
|
|
75
|
+
cascade's face, because that face is centred on the camera and therefore moves with it too. */
|
|
76
|
+
let fieldShare = 0;
|
|
77
|
+
const remaining = 1 - screen;
|
|
78
|
+
if (remaining > 0) {
|
|
79
|
+
const march = resources.fieldMarch ?? FIELD_MARCH;
|
|
80
|
+
const coned = march.coneAngle === ray.coneAngle ? march : { ...march, coneAngle: ray.coneAngle };
|
|
81
|
+
if (traceField(resources.field, ray.point, ray.normal, ray.direction, coned, FIELD_HIT)) {
|
|
82
|
+
fieldShare =
|
|
83
|
+
remaining * fieldConfidence(resources.field, FIELD_HIT.x, FIELD_HIT.y, FIELD_HIT.z);
|
|
84
|
+
if (fieldShare > 0) {
|
|
85
|
+
resources.fieldRadiance(FIELD_HIT.x, FIELD_HIT.y, FIELD_HIT.z, LEVEL);
|
|
86
|
+
add(out.radiance, LEVEL, fieldShare);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/*
|
|
91
|
+
* Level 2, for everything still unanswered. **It takes whatever is left without asking**, which
|
|
92
|
+
* is what "never wrong and only ever coarse" buys: there is no case where this declines.
|
|
93
|
+
*/
|
|
94
|
+
const probes = 1 - screen - fieldShare;
|
|
95
|
+
if (probes > 0) {
|
|
96
|
+
visibleProbes(resources.grid, resources.visibility, ray.point[0] ?? 0, ray.point[1] ?? 0, ray.point[2] ?? 0, PROBE_BLEND);
|
|
97
|
+
sampleProbeVolume(PROBE_BLEND, resources.probeValues, resources.probeChannels, PROBE_OUT);
|
|
98
|
+
add(out.radiance, PROBE_OUT, probes);
|
|
99
|
+
}
|
|
100
|
+
out.blend[0] = screen;
|
|
101
|
+
out.blend[1] = fieldShare;
|
|
102
|
+
out.blend[2] = probes;
|
|
103
|
+
out.source =
|
|
104
|
+
screen >= fieldShare && screen >= probes
|
|
105
|
+
? GI_SOURCE_SCREEN
|
|
106
|
+
: fieldShare >= probes
|
|
107
|
+
? GI_SOURCE_FIELD
|
|
108
|
+
: GI_SOURCE_PROBES;
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* How much a world-field hit is trusted, by how far inside the outermost cascade it landed.
|
|
113
|
+
*
|
|
114
|
+
* **The same band `sampleGlobalField` fades its own cascades over**, so the two do not disagree
|
|
115
|
+
* about where the field stops being the answer. A hit right on the outer face is a hit the field
|
|
116
|
+
* barely had the information for, and the probes are what carry it the rest of the way.
|
|
117
|
+
*/
|
|
118
|
+
function fieldConfidence(field, x, y, z) {
|
|
119
|
+
const outer = field.cascades[field.cascades.length - 1];
|
|
120
|
+
const band = GLOBAL_FIELD_BLEND * (outer.bounds[3] - outer.bounds[0]);
|
|
121
|
+
if (band <= 0)
|
|
122
|
+
return 1;
|
|
123
|
+
const p = [x, y, z];
|
|
124
|
+
let inside = Infinity;
|
|
125
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
126
|
+
inside = Math.min(inside, p[axis] - outer.bounds[axis], outer.bounds[axis + 3] - p[axis]);
|
|
127
|
+
}
|
|
128
|
+
return Math.min(1, Math.max(0, inside / band));
|
|
129
|
+
}
|
|
130
|
+
/** `target += source * weight`, three channels. */
|
|
131
|
+
function add(target, source, weight) {
|
|
132
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
133
|
+
target[channel] = target[channel] + source[channel] * weight;
|
|
134
|
+
}
|
|
135
|
+
}
|