@driftengine/core 3.63.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +111 -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 +69 -4
- package/dist/index.js +112 -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 +19 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
- 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 +246 -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 +63 -36
- 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,575 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame's geometry, which arrives and leaves while the frame loop runs.
|
|
3
|
+
*
|
|
4
|
+
* **`GpuDrivenPass` uploaded its scene in a method called `buildStatic`**, and its constructor said
|
|
5
|
+
* why: this pipeline's geometry does not move. A voxel world streams chunks as a player walks and
|
|
6
|
+
* remeshes one on every block they break, so it has to.
|
|
7
|
+
*
|
|
8
|
+
* **Four `RangeAlloc`s over what the pass binds**: six arrays the scene keeps — the cluster and
|
|
9
|
+
* mesh records a frame's culling reads — and two it never keeps. The vertices and the indices go
|
|
10
|
+
* through a `GeometrySink` straight into the device's buffers when a mesh is placed, because
|
|
11
|
+
* holding them as well doubled what a world costs, and at the voxel sandbox's radius 32 the copy
|
|
12
|
+
* was an ArrayBuffer Chrome will not allocate. A mesh claims a run of each, writes its rows, and
|
|
13
|
+
* hands the runs back when it leaves.
|
|
14
|
+
*
|
|
15
|
+
* **One path, not two.** The static case is a scene filled once and never emptied — `streamingScene`
|
|
16
|
+
* is that, and it is what every caller of `buildGpuDrivenScene` plus `new GpuDrivenPass` becomes.
|
|
17
|
+
* Two paths would mean the three rigs in `DRAFT_SCENES` exercise the path no published scene uses,
|
|
18
|
+
* which is how a control stops being a control; `streamScene.test.ts` asserts this class packs a
|
|
19
|
+
* static scene byte for byte where `buildGpuDrivenScene` packed it.
|
|
20
|
+
*
|
|
21
|
+
* **`add` allocates only when a mesh larger than any before it arrives**, because it is called
|
|
22
|
+
* from a frame loop when a player walks: the scratch it interleaves into grows to the largest mesh
|
|
23
|
+
* and is reused. A scene filled before its pass mounts is the exception, and it is the static
|
|
24
|
+
* case: it holds each mesh until the pass attaches, and nothing after. `heldBytes` says which.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { worldClustersInto } from './clusterWorld.ts';
|
|
28
|
+
import { cullCutoff } from './cullClusters.ts';
|
|
29
|
+
import { INSTANCE_FLOATS, instanceSphereInto } from './instances.ts';
|
|
30
|
+
import { RangeAlloc } from './rangeAlloc.ts';
|
|
31
|
+
import { sceneShadowBounds } from './shadowCamera.ts';
|
|
32
|
+
import { GPU_DRIVEN_VERTEX_FLOATS, glowOf } from './sceneUpload.ts';
|
|
33
|
+
|
|
34
|
+
import type { GpuDrivenMesh } from './sceneUpload.ts';
|
|
35
|
+
|
|
36
|
+
/** Eight floats a cluster in `cull`, matching `clusterWorld.ts` and the cull shader's stride. */
|
|
37
|
+
const CULL_FLOATS = 8;
|
|
38
|
+
/** Six floats a cluster in `lod`, matching the LOD shader's `i * 6u`. */
|
|
39
|
+
const LOD_FLOATS = 6;
|
|
40
|
+
/** Four words a cluster in `meta`: index offset, index count, cluster id, mesh index. */
|
|
41
|
+
const META_WORDS = 4;
|
|
42
|
+
/** Sixteen floats a mesh. */
|
|
43
|
+
const TRANSFORM_FLOATS = 16;
|
|
44
|
+
|
|
45
|
+
/** How many units of each axis a scene may hold. Declared once; a claim past it is refused. */
|
|
46
|
+
export interface StreamCapacity {
|
|
47
|
+
readonly vertices: number;
|
|
48
|
+
readonly indices: number;
|
|
49
|
+
readonly clusters: number;
|
|
50
|
+
readonly meshes: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** What `add` hands back, and what `remove` needs to give the room back. */
|
|
54
|
+
export interface StreamHandle {
|
|
55
|
+
readonly mesh: number;
|
|
56
|
+
readonly vertexBase: number;
|
|
57
|
+
readonly vertexCount: number;
|
|
58
|
+
readonly indexBase: number;
|
|
59
|
+
readonly indexCount: number;
|
|
60
|
+
readonly clusterBase: number;
|
|
61
|
+
readonly clusterCount: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** One buffer's changed run, in units of that buffer's record. */
|
|
65
|
+
export interface DirtySpan {
|
|
66
|
+
readonly from: number;
|
|
67
|
+
readonly to: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface StreamUpload {
|
|
71
|
+
readonly clusters: readonly DirtySpan[];
|
|
72
|
+
readonly meshes: readonly DirtySpan[];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Where a scene's vertices and indices go, since it keeps no copy of them.
|
|
77
|
+
*
|
|
78
|
+
* **Handed a scratch array, and a sink must copy it before returning.** `rows` and `values` may be
|
|
79
|
+
* longer than `count` records: the scene interleaves every mesh into the same two arrays and
|
|
80
|
+
* reuses them on the next add, which is what keeps `add` from allocating in a frame loop.
|
|
81
|
+
* `queue.writeBuffer` copies when it is called, so `GpuDrivenPass` hands them straight on.
|
|
82
|
+
*/
|
|
83
|
+
export interface GeometrySink {
|
|
84
|
+
/** `count` vertices of twelve floats each, from the start of `rows`, placed at `firstVertex`. */
|
|
85
|
+
writeVertices(firstVertex: number, rows: Float32Array, count: number): void;
|
|
86
|
+
/** `count` indices from the start of `values`, already rebased onto the scene, at `firstIndex`. */
|
|
87
|
+
writeIndices(firstIndex: number, values: Uint32Array, count: number): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** A mesh's geometry, held until a sink is attached and dropped then. */
|
|
91
|
+
interface Staged {
|
|
92
|
+
readonly vertexBase: number;
|
|
93
|
+
readonly rows: Float32Array;
|
|
94
|
+
readonly vertexCount: number;
|
|
95
|
+
readonly indexBase: number;
|
|
96
|
+
readonly values: Uint32Array;
|
|
97
|
+
readonly indexCount: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Four words a removed mesh while its ranges wait: vertex base and count, index base and count. */
|
|
101
|
+
const WAITING_WORDS = 4;
|
|
102
|
+
|
|
103
|
+
/** What a second pass is told. See `attach`. */
|
|
104
|
+
const SPENT =
|
|
105
|
+
"[driftengine] a streaming scene writes its geometry to one pass's device and keeps no copy, " +
|
|
106
|
+
"and this one's has already gone to a pass; build the scene again for a second pass";
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* A list of changed runs, kept sorted and merged.
|
|
110
|
+
*
|
|
111
|
+
* **The same invariant `RangeAlloc` keeps over its holes, for the same reason**: `takeDirty` has to
|
|
112
|
+
* answer the same way however the frame's adds were ordered, or a capture depends on history.
|
|
113
|
+
*/
|
|
114
|
+
class DirtyList {
|
|
115
|
+
private spans: { from: number; to: number }[] = [];
|
|
116
|
+
|
|
117
|
+
mark(from: number, to: number): void {
|
|
118
|
+
if (to <= from) return;
|
|
119
|
+
let index = 0;
|
|
120
|
+
while (index < this.spans.length && (this.spans[index] as DirtySpan).from < from) index += 1;
|
|
121
|
+
this.spans.splice(index, 0, { from, to });
|
|
122
|
+
|
|
123
|
+
/* Merge backwards then forwards, so three touching runs become one. */
|
|
124
|
+
const previous = index > 0 ? this.spans[index - 1] : undefined;
|
|
125
|
+
if (previous !== undefined && previous.to >= from) {
|
|
126
|
+
previous.to = Math.max(previous.to, to);
|
|
127
|
+
this.spans.splice(index, 1);
|
|
128
|
+
index -= 1;
|
|
129
|
+
}
|
|
130
|
+
let merged = this.spans[index] as { from: number; to: number };
|
|
131
|
+
while (index + 1 < this.spans.length) {
|
|
132
|
+
const next = this.spans[index + 1] as DirtySpan;
|
|
133
|
+
if (next.from > merged.to) break;
|
|
134
|
+
merged.to = Math.max(merged.to, next.to);
|
|
135
|
+
this.spans.splice(index + 1, 1);
|
|
136
|
+
merged = this.spans[index] as { from: number; to: number };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
take(): DirtySpan[] {
|
|
141
|
+
const out = this.spans;
|
|
142
|
+
this.spans = [];
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export class StreamingScene {
|
|
148
|
+
readonly capacity: StreamCapacity;
|
|
149
|
+
|
|
150
|
+
/** Eight floats a cluster: world centre, radius, cone axis, cutoff. */
|
|
151
|
+
readonly cull: Float32Array;
|
|
152
|
+
/** Six floats a cluster: world centre, radius, own error, parent error. */
|
|
153
|
+
readonly lod: Float32Array;
|
|
154
|
+
/** Four words a cluster: index offset, index count, cluster id, mesh index. */
|
|
155
|
+
readonly meta: Uint32Array;
|
|
156
|
+
readonly materialOf: Uint32Array;
|
|
157
|
+
readonly transforms: Float32Array;
|
|
158
|
+
readonly instanceSpheres: Float32Array;
|
|
159
|
+
|
|
160
|
+
private readonly vertexAlloc: RangeAlloc;
|
|
161
|
+
private readonly indexAlloc: RangeAlloc;
|
|
162
|
+
private readonly clusterAlloc: RangeAlloc;
|
|
163
|
+
private readonly meshAlloc: RangeAlloc;
|
|
164
|
+
|
|
165
|
+
private readonly dirtyClusters = new DirtyList();
|
|
166
|
+
private readonly dirtyMeshes = new DirtyList();
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Scratch `worldClustersInto` needs, sized once.
|
|
170
|
+
*
|
|
171
|
+
* It wants `bounds` as four floats a cluster — centre and radius together — and `cones` as four,
|
|
172
|
+
* where a `ClusterSource` keeps `boundsCentre` / `boundsRadius` and `coneAxis` / `coneCutoff` as
|
|
173
|
+
* separate arrays. Packing them is a copy a frame loop must not allocate for.
|
|
174
|
+
*/
|
|
175
|
+
private readonly boundScratch: Float32Array;
|
|
176
|
+
private readonly coneScratch: Float32Array;
|
|
177
|
+
private readonly errorScratch: Float32Array;
|
|
178
|
+
private readonly meshScratch: Uint32Array;
|
|
179
|
+
|
|
180
|
+
private live = 0;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* **Where the geometry is.** Held a mesh at a time until a pass attaches (`staging`), written
|
|
184
|
+
* straight to that pass's buffers (`attached`), or nowhere once the pass has gone (`spent`).
|
|
185
|
+
*/
|
|
186
|
+
private state: 'staging' | 'attached' | 'spent' = 'staging';
|
|
187
|
+
private sink: GeometrySink | null = null;
|
|
188
|
+
private readonly staged = new Map<number, Staged>();
|
|
189
|
+
/* What an attached scene interleaves through: grown to the largest mesh, never shrunk. */
|
|
190
|
+
private rowScratch = new Float32Array(0);
|
|
191
|
+
private indexScratch = new Uint32Array(0);
|
|
192
|
+
/* Geometry ranges a removal freed while attached, given back at the next `takeDirty`. */
|
|
193
|
+
private waiting: Uint32Array;
|
|
194
|
+
private waitingCount = 0;
|
|
195
|
+
|
|
196
|
+
constructor(capacity: StreamCapacity) {
|
|
197
|
+
const clusters = Math.max(0, Math.floor(capacity.clusters));
|
|
198
|
+
const meshes = Math.max(0, Math.floor(capacity.meshes));
|
|
199
|
+
this.capacity = {
|
|
200
|
+
vertices: Math.max(0, Math.floor(capacity.vertices)),
|
|
201
|
+
indices: Math.max(0, Math.floor(capacity.indices)),
|
|
202
|
+
clusters,
|
|
203
|
+
meshes,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
this.cull = new Float32Array(clusters * CULL_FLOATS);
|
|
207
|
+
this.lod = new Float32Array(clusters * LOD_FLOATS);
|
|
208
|
+
this.meta = new Uint32Array(clusters * META_WORDS);
|
|
209
|
+
this.materialOf = new Uint32Array(clusters);
|
|
210
|
+
this.transforms = new Float32Array(meshes * TRANSFORM_FLOATS);
|
|
211
|
+
this.instanceSpheres = new Float32Array(meshes * INSTANCE_FLOATS);
|
|
212
|
+
|
|
213
|
+
this.vertexAlloc = new RangeAlloc(this.capacity.vertices);
|
|
214
|
+
this.indexAlloc = new RangeAlloc(this.capacity.indices);
|
|
215
|
+
this.clusterAlloc = new RangeAlloc(clusters);
|
|
216
|
+
this.meshAlloc = new RangeAlloc(meshes);
|
|
217
|
+
|
|
218
|
+
this.boundScratch = new Float32Array(clusters * 4);
|
|
219
|
+
this.coneScratch = new Float32Array(clusters * 4);
|
|
220
|
+
this.errorScratch = new Float32Array(clusters * 2);
|
|
221
|
+
this.meshScratch = new Uint32Array(clusters);
|
|
222
|
+
/* Room for every mesh the scene can hold being removed in one frame; `hold` grows it past. */
|
|
223
|
+
this.waiting = new Uint32Array(meshes * WAITING_WORDS);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** How many clusters currently draw. A removed cluster's slot is still allocated until released. */
|
|
227
|
+
get liveClusters(): number {
|
|
228
|
+
return this.live;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Room left on each axis, which `scripts/stream-fragmentation.mjs` and its tests read. */
|
|
232
|
+
get freeVertices(): number {
|
|
233
|
+
return this.vertexAlloc.capacity - this.vertexAlloc.used;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** The worst each axis ever reached, which is the occupancy a fragmentation run reports. */
|
|
237
|
+
get highWater(): StreamCapacity {
|
|
238
|
+
return {
|
|
239
|
+
vertices: this.vertexAlloc.highWater,
|
|
240
|
+
indices: this.indexAlloc.highWater,
|
|
241
|
+
clusters: this.clusterAlloc.highWater,
|
|
242
|
+
meshes: this.meshAlloc.highWater,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The geometry this scene holds on the CPU, in bytes: what is staged for a pass that has not
|
|
248
|
+
* attached, and the two scratch arrays an attached scene interleaves through. **Nothing past
|
|
249
|
+
* that** — which is the whole of what `GeometrySink` is for, as a number a test and a readout can
|
|
250
|
+
* read.
|
|
251
|
+
*/
|
|
252
|
+
get heldBytes(): number {
|
|
253
|
+
let bytes = this.rowScratch.byteLength + this.indexScratch.byteLength;
|
|
254
|
+
for (const entry of this.staged.values()) {
|
|
255
|
+
bytes += entry.rows.byteLength + entry.values.byteLength;
|
|
256
|
+
}
|
|
257
|
+
return bytes;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Give the scene the one place its geometry goes, and send it everything staged so far.
|
|
262
|
+
*
|
|
263
|
+
* **Once, and a second time is refused by name.** The scene keeps no copy of what it has sent,
|
|
264
|
+
* so a second pass would bind two empty buffers and draw nothing — silently, which is the one
|
|
265
|
+
* answer this cannot give. The dirty journal already made a scene one pass's: two passes would
|
|
266
|
+
* each take its spans and each miss the other's.
|
|
267
|
+
*/
|
|
268
|
+
attach(sink: GeometrySink): void {
|
|
269
|
+
if (this.state !== 'staging') throw new Error(SPENT);
|
|
270
|
+
this.state = 'attached';
|
|
271
|
+
this.sink = sink;
|
|
272
|
+
for (const entry of this.staged.values()) {
|
|
273
|
+
sink.writeVertices(entry.vertexBase, entry.rows, entry.vertexCount);
|
|
274
|
+
sink.writeIndices(entry.indexBase, entry.values, entry.indexCount);
|
|
275
|
+
}
|
|
276
|
+
this.staged.clear();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The pass has gone. Adds and removes still keep the records, and the geometry goes nowhere.
|
|
281
|
+
*
|
|
282
|
+
* **Called by `GpuDrivenPass.dispose` before it destroys its buffers**, because a write to a
|
|
283
|
+
* destroyed buffer is a device error; and nothing waits after it, because nothing reads.
|
|
284
|
+
*/
|
|
285
|
+
detach(): void {
|
|
286
|
+
this.state = 'spent';
|
|
287
|
+
this.sink = null;
|
|
288
|
+
this.staged.clear();
|
|
289
|
+
this.releaseWaiting();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Place a mesh, or refuse.
|
|
294
|
+
*
|
|
295
|
+
* **Every claim before any write, and a rollback if the last one fails.** A partial add is a mesh
|
|
296
|
+
* whose clusters point at vertices another mesh owns, which draws somebody else's geometry rather
|
|
297
|
+
* than failing — the shape of mistake `packClusters` documents one level up.
|
|
298
|
+
*
|
|
299
|
+
* **`null` rather than an exception**, because a streaming consumer calls this from a frame loop
|
|
300
|
+
* when the player walks: an exception there is a crashed demo where a refusal is a chunk that
|
|
301
|
+
* arrives a frame later.
|
|
302
|
+
*/
|
|
303
|
+
add(mesh: GpuDrivenMesh, transform: ArrayLike<number>, material: number): StreamHandle | null {
|
|
304
|
+
const vertexCount = mesh.positions.length / 3;
|
|
305
|
+
const indexCount = mesh.clusters.indices.length;
|
|
306
|
+
const clusterCount = mesh.clusters.count;
|
|
307
|
+
|
|
308
|
+
const vertexBase = this.vertexAlloc.claim(vertexCount);
|
|
309
|
+
const indexBase = vertexBase < 0 ? -1 : this.indexAlloc.claim(indexCount);
|
|
310
|
+
const clusterBase = indexBase < 0 ? -1 : this.clusterAlloc.claim(clusterCount);
|
|
311
|
+
const slot = clusterBase < 0 ? -1 : this.meshAlloc.claim(1);
|
|
312
|
+
if (vertexBase < 0 || indexBase < 0 || clusterBase < 0 || slot < 0) {
|
|
313
|
+
if (clusterBase >= 0) this.clusterAlloc.release(clusterBase, clusterCount);
|
|
314
|
+
if (indexBase >= 0) this.indexAlloc.release(indexBase, indexCount);
|
|
315
|
+
if (vertexBase >= 0) this.vertexAlloc.release(vertexBase, vertexCount);
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
for (let i = 0; i < TRANSFORM_FLOATS; i += 1) {
|
|
320
|
+
this.transforms[slot * TRANSFORM_FLOATS + i] = transform[i] ?? 0;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/* The interleave the pass used to do at construction, in the order its shaders index. */
|
|
324
|
+
const uvs = mesh.uvs;
|
|
325
|
+
const glow = glowOf(mesh, 'added');
|
|
326
|
+
const rows = this.rowsFor(vertexCount);
|
|
327
|
+
for (let v = 0; v < vertexCount; v += 1) {
|
|
328
|
+
const to = v * GPU_DRIVEN_VERTEX_FLOATS;
|
|
329
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
330
|
+
rows[to + axis] = mesh.positions[v * 3 + axis] as number;
|
|
331
|
+
rows[to + 3 + axis] = mesh.normals[v * 3 + axis] as number;
|
|
332
|
+
rows[to + 6 + axis] = mesh.colours[v * 3 + axis] as number;
|
|
333
|
+
}
|
|
334
|
+
rows[to + 9] = uvs === undefined ? 0 : (uvs[v * 2] as number);
|
|
335
|
+
rows[to + 10] = uvs === undefined ? 0 : (uvs[v * 2 + 1] as number);
|
|
336
|
+
rows[to + 11] = glow === undefined ? 0 : (glow[v] as number);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* **Rebased onto the scene**, because the indices arrive addressing the mesh they came from. */
|
|
340
|
+
const values = this.valuesFor(indexCount);
|
|
341
|
+
for (let i = 0; i < indexCount; i += 1) {
|
|
342
|
+
values[i] = (mesh.clusters.indices[i] as number) + vertexBase;
|
|
343
|
+
}
|
|
344
|
+
this.place(slot, vertexBase, rows, vertexCount, indexBase, values, indexCount);
|
|
345
|
+
|
|
346
|
+
for (let c = 0; c < clusterCount; c += 1) {
|
|
347
|
+
const record = clusterBase + c;
|
|
348
|
+
this.meta[record * META_WORDS] = indexBase + (mesh.clusters.triangleOffsets[c] as number) * 3;
|
|
349
|
+
this.meta[record * META_WORDS + 1] = (mesh.clusters.triangleCounts[c] as number) * 3;
|
|
350
|
+
/* Its own position, which is what a pixel in the visibility buffer resolves through. */
|
|
351
|
+
this.meta[record * META_WORDS + 2] = record;
|
|
352
|
+
this.meta[record * META_WORDS + 3] = slot;
|
|
353
|
+
this.materialOf[record] = material;
|
|
354
|
+
|
|
355
|
+
for (let axis = 0; axis < 3; axis += 1) {
|
|
356
|
+
this.boundScratch[c * 4 + axis] = mesh.clusters.boundsCentre[c * 3 + axis] as number;
|
|
357
|
+
this.coneScratch[c * 4 + axis] = mesh.clusters.coneAxis[c * 3 + axis] as number;
|
|
358
|
+
}
|
|
359
|
+
this.boundScratch[c * 4 + 3] = mesh.clusters.boundsRadius[c] as number;
|
|
360
|
+
/* Carried as a cosine, read by the cull as a sine: see `cullCutoff`. */
|
|
361
|
+
this.coneScratch[c * 4 + 3] = cullCutoff(mesh.clusters.coneCutoff[c] as number);
|
|
362
|
+
this.errorScratch[c * 2] = mesh.clusters.ownError[c] as number;
|
|
363
|
+
this.errorScratch[c * 2 + 1] = mesh.clusters.parentError[c] as number;
|
|
364
|
+
this.meshScratch[c] = slot;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/*
|
|
368
|
+
* **The bounds are computed here, at the moment the mesh is placed.** `cull` holds them already
|
|
369
|
+
* multiplied by the transform, and the pass's own constructor records what happened when it did
|
|
370
|
+
* not: every rig stood at the origin, so nothing ever said so.
|
|
371
|
+
*/
|
|
372
|
+
worldClustersInto(
|
|
373
|
+
{
|
|
374
|
+
bounds: this.boundScratch,
|
|
375
|
+
cones: this.coneScratch,
|
|
376
|
+
errors: this.errorScratch,
|
|
377
|
+
meshOf: this.meshScratch,
|
|
378
|
+
count: clusterCount,
|
|
379
|
+
},
|
|
380
|
+
this.transforms,
|
|
381
|
+
this.cull,
|
|
382
|
+
this.lod,
|
|
383
|
+
clusterBase,
|
|
384
|
+
);
|
|
385
|
+
instanceSphereInto(this.cull, clusterBase, clusterCount, this.instanceSpheres, slot);
|
|
386
|
+
|
|
387
|
+
this.dirtyClusters.mark(clusterBase, clusterBase + clusterCount);
|
|
388
|
+
this.dirtyMeshes.mark(slot, slot + 1);
|
|
389
|
+
this.live += clusterCount;
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
mesh: slot,
|
|
393
|
+
vertexBase,
|
|
394
|
+
vertexCount,
|
|
395
|
+
indexBase,
|
|
396
|
+
indexCount,
|
|
397
|
+
clusterBase,
|
|
398
|
+
clusterCount,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/** Take a mesh out. Its ranges go back and its clusters stop drawing. */
|
|
403
|
+
remove(handle: StreamHandle): void {
|
|
404
|
+
for (let c = 0; c < handle.clusterCount; c += 1) {
|
|
405
|
+
/*
|
|
406
|
+
* **The index count, not the offset.** Zero indices is a cluster that draws nothing and that
|
|
407
|
+
* `instanceCull` rejects before it can write an instance; a zeroed offset is a cluster that
|
|
408
|
+
* draws somebody else's first triangle.
|
|
409
|
+
*/
|
|
410
|
+
this.meta[(handle.clusterBase + c) * META_WORDS + 1] = 0;
|
|
411
|
+
/*
|
|
412
|
+
* **And its world bounds, because `sceneBounds` reads them.** A released cluster whose cull
|
|
413
|
+
* record still holds where it used to stand keeps the light's box stretched over a mesh that
|
|
414
|
+
* is not there — which is a shadow map spread across empty space rather than anything that
|
|
415
|
+
* fails. A radius of zero is what `sceneShadowBounds` skips.
|
|
416
|
+
*/
|
|
417
|
+
for (let f = 0; f < CULL_FLOATS; f += 1)
|
|
418
|
+
this.cull[(handle.clusterBase + c) * CULL_FLOATS + f] = 0;
|
|
419
|
+
}
|
|
420
|
+
for (let i = 0; i < INSTANCE_FLOATS; i += 1) {
|
|
421
|
+
this.instanceSpheres[handle.mesh * INSTANCE_FLOATS + i] = 0;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
this.clusterAlloc.release(handle.clusterBase, handle.clusterCount);
|
|
425
|
+
this.meshAlloc.release(handle.mesh, 1);
|
|
426
|
+
/* Removed before a pass attached: nothing of it is ever sent. */
|
|
427
|
+
this.staged.delete(handle.mesh);
|
|
428
|
+
if (this.state === 'attached') {
|
|
429
|
+
this.hold(handle);
|
|
430
|
+
} else {
|
|
431
|
+
this.indexAlloc.release(handle.indexBase, handle.indexCount);
|
|
432
|
+
this.vertexAlloc.release(handle.vertexBase, handle.vertexCount);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
this.dirtyClusters.mark(handle.clusterBase, handle.clusterBase + handle.clusterCount);
|
|
436
|
+
this.dirtyMeshes.mark(handle.mesh, handle.mesh + 1);
|
|
437
|
+
this.live -= handle.clusterCount;
|
|
438
|
+
|
|
439
|
+
/*
|
|
440
|
+
* **The geometry is deliberately not cleared.** Nothing reads it once no cluster points at it,
|
|
441
|
+
* and not clearing it is what makes a removal a handful of stores rather than a device write —
|
|
442
|
+
* which matters, because a player walking away causes one every few frames.
|
|
443
|
+
*/
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** What changed since the last call, then cleared. An unchanged frame uploads nothing. */
|
|
447
|
+
takeDirty(): StreamUpload {
|
|
448
|
+
/* The uploads the caller is about to make are what stop anything reading a waiting range. */
|
|
449
|
+
this.releaseWaiting();
|
|
450
|
+
return {
|
|
451
|
+
clusters: this.dirtyClusters.take(),
|
|
452
|
+
meshes: this.dirtyMeshes.take(),
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Where a mesh is interleaved: a fresh array while it has to be held until a pass attaches, and
|
|
458
|
+
* the one scratch otherwise, grown only when a mesh larger than any before it arrives.
|
|
459
|
+
*/
|
|
460
|
+
private rowsFor(vertexCount: number): Float32Array {
|
|
461
|
+
const floats = vertexCount * GPU_DRIVEN_VERTEX_FLOATS;
|
|
462
|
+
if (this.state === 'staging') return new Float32Array(floats);
|
|
463
|
+
if (this.rowScratch.length < floats) this.rowScratch = new Float32Array(floats);
|
|
464
|
+
return this.rowScratch;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
private valuesFor(indexCount: number): Uint32Array {
|
|
468
|
+
if (this.state === 'staging') return new Uint32Array(indexCount);
|
|
469
|
+
if (this.indexScratch.length < indexCount) this.indexScratch = new Uint32Array(indexCount);
|
|
470
|
+
return this.indexScratch;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/** Hand a placed mesh's geometry on, or hold it for the pass that has not attached yet. */
|
|
474
|
+
private place(
|
|
475
|
+
slot: number,
|
|
476
|
+
vertexBase: number,
|
|
477
|
+
rows: Float32Array,
|
|
478
|
+
vertexCount: number,
|
|
479
|
+
indexBase: number,
|
|
480
|
+
values: Uint32Array,
|
|
481
|
+
indexCount: number,
|
|
482
|
+
): void {
|
|
483
|
+
if (this.state === 'staging') {
|
|
484
|
+
this.staged.set(slot, { vertexBase, rows, vertexCount, indexBase, values, indexCount });
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const sink = this.sink;
|
|
488
|
+
if (sink === null) return;
|
|
489
|
+
sink.writeVertices(vertexBase, rows, vertexCount);
|
|
490
|
+
sink.writeIndices(indexBase, values, indexCount);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Keep a removed mesh's vertex and index ranges from the allocator until the next `takeDirty`.
|
|
495
|
+
*
|
|
496
|
+
* **Because the geometry is written when a mesh is added, not when the frame is recorded.** The
|
|
497
|
+
* pass records its frame at `beginFrame` and submits it at `endFrame`, and a queue write lands
|
|
498
|
+
* before the frame it was made during runs. A mesh added in that window into a range freed that
|
|
499
|
+
* frame would be drawn through the removed mesh's clusters, which the device holds until the
|
|
500
|
+
* next upload. `takeDirty` is that upload's first step, so from then on nothing reads the range.
|
|
501
|
+
* The cluster and mesh slots do not wait: their records travel together through the journal.
|
|
502
|
+
*/
|
|
503
|
+
private hold(handle: StreamHandle): void {
|
|
504
|
+
if ((this.waitingCount + 1) * WAITING_WORDS > this.waiting.length) {
|
|
505
|
+
const grown = new Uint32Array(Math.max(WAITING_WORDS, this.waiting.length * 2));
|
|
506
|
+
grown.set(this.waiting);
|
|
507
|
+
this.waiting = grown;
|
|
508
|
+
}
|
|
509
|
+
const at = this.waitingCount * WAITING_WORDS;
|
|
510
|
+
this.waiting[at] = handle.vertexBase;
|
|
511
|
+
this.waiting[at + 1] = handle.vertexCount;
|
|
512
|
+
this.waiting[at + 2] = handle.indexBase;
|
|
513
|
+
this.waiting[at + 3] = handle.indexCount;
|
|
514
|
+
this.waitingCount += 1;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
private releaseWaiting(): void {
|
|
518
|
+
const waiting = this.waiting;
|
|
519
|
+
for (let i = 0; i < this.waitingCount; i += 1) {
|
|
520
|
+
const at = i * WAITING_WORDS;
|
|
521
|
+
this.vertexAlloc.release(waiting[at] as number, waiting[at + 1] as number);
|
|
522
|
+
this.indexAlloc.release(waiting[at + 2] as number, waiting[at + 3] as number);
|
|
523
|
+
}
|
|
524
|
+
this.waitingCount = 0;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* A bounding sphere over every live cluster: centre in floats 0 to 2, radius in float 3.
|
|
529
|
+
*
|
|
530
|
+
* **`sceneShadowBounds` itself rather than a second spelling of it**, and that is not tidiness —
|
|
531
|
+
* the first version of this method wrote a *box* into the same four floats the pass reads a
|
|
532
|
+
* sphere out of, so the shadow camera was fitted to a sphere centred on the box's minimum corner
|
|
533
|
+
* with `maxX` as its radius. Two GPU-driven rigs lost their shadows and the maintainer saw it on
|
|
534
|
+
* the page before the capture diff was read.
|
|
535
|
+
*
|
|
536
|
+
* The function's own header already says why there is one copy of this arithmetic: a light's box
|
|
537
|
+
* that disagrees with the cull's bounds clips casters out of a map the cull believes are in it.
|
|
538
|
+
* A second copy here was the same mistake one level up.
|
|
539
|
+
*/
|
|
540
|
+
sceneBounds(out: Float32Array): void {
|
|
541
|
+
sceneShadowBounds(this.cull, CULL_FLOATS, this.capacity.clusters, out);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* A scene filled once from a list of meshes, which is the static case.
|
|
547
|
+
*
|
|
548
|
+
* With no capacity given it sizes itself to exactly what the meshes need, so a static scene
|
|
549
|
+
* allocates nothing spare and packs where `buildGpuDrivenScene` packed.
|
|
550
|
+
*/
|
|
551
|
+
export function streamingScene(
|
|
552
|
+
meshes: readonly GpuDrivenMesh[],
|
|
553
|
+
transforms: Float32Array,
|
|
554
|
+
capacity?: StreamCapacity,
|
|
555
|
+
): StreamingScene {
|
|
556
|
+
let vertices = 0;
|
|
557
|
+
let indices = 0;
|
|
558
|
+
let clusters = 0;
|
|
559
|
+
for (const mesh of meshes) {
|
|
560
|
+
vertices += mesh.positions.length / 3;
|
|
561
|
+
indices += mesh.clusters.indices.length;
|
|
562
|
+
clusters += mesh.clusters.count;
|
|
563
|
+
}
|
|
564
|
+
const scene = new StreamingScene(
|
|
565
|
+
capacity ?? { vertices, indices, clusters, meshes: meshes.length },
|
|
566
|
+
);
|
|
567
|
+
meshes.forEach((mesh, at) => {
|
|
568
|
+
scene.add(
|
|
569
|
+
mesh,
|
|
570
|
+
transforms.subarray(at * TRANSFORM_FLOATS, at * TRANSFORM_FLOATS + TRANSFORM_FLOATS),
|
|
571
|
+
mesh.material,
|
|
572
|
+
);
|
|
573
|
+
});
|
|
574
|
+
return scene;
|
|
575
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** What a textured pixel needs from its triangle that a compute invocation cannot take for itself. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* **Two derivatives the forward path gets from the hardware and this pipeline has to build.** A
|
|
5
|
+
* fragment shader takes `dFdx` and `dFdy`; a compute invocation has no neighbours. The shading
|
|
6
|
+
* pass already has the analytic barycentric gradients — `SHADE_SURFACE_WGSL` builds them for the
|
|
7
|
+
* shadow's receiver plane — and any attribute's screen gradient is those weights applied to its
|
|
8
|
+
* three corners. The UV's picks a mip level; the UV's and the world position's together build the
|
|
9
|
+
* tangent frame a normal map is read through.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The mip level a pixel's texture footprint asks for: the log of the longer of the two screen-axis
|
|
14
|
+
* UV steps, in texels. The common isotropic form; a device choosing its own level for an
|
|
15
|
+
* implicit-derivative sample may differ in the last bits, which is stated where the two pipelines
|
|
16
|
+
* are compared rather than hidden here.
|
|
17
|
+
*/
|
|
18
|
+
export function surfaceLod(
|
|
19
|
+
dudx: number,
|
|
20
|
+
dvdx: number,
|
|
21
|
+
dudy: number,
|
|
22
|
+
dvdy: number,
|
|
23
|
+
size: number,
|
|
24
|
+
): number {
|
|
25
|
+
const footprint = Math.max(Math.hypot(dudx, dvdx), Math.hypot(dudy, dvdy)) * size;
|
|
26
|
+
return Math.max(Math.log2(Math.max(footprint, 1e-30)), 0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* `tangentFrame.ts`'s derived branch, from analytic gradients instead of `dFdx` and `dFdy`.
|
|
31
|
+
*
|
|
32
|
+
* **Term for term, including the determinant's sign and the one scale for both axes**, because the
|
|
33
|
+
* forward path's comment records what each is worth: without the sign the frame points the other
|
|
34
|
+
* way on one backend, and two scales shear a stretched UV layout. The screen axes here are x to the
|
|
35
|
+
* right and y up, as the shading pass measures them; the fold makes the answer the same either way.
|
|
36
|
+
*/
|
|
37
|
+
export function derivedTangentFrame(
|
|
38
|
+
n: readonly [number, number, number],
|
|
39
|
+
dp1: readonly [number, number, number],
|
|
40
|
+
dp2: readonly [number, number, number],
|
|
41
|
+
duv1: readonly [number, number],
|
|
42
|
+
duv2: readonly [number, number],
|
|
43
|
+
out: Float32Array,
|
|
44
|
+
): Float32Array {
|
|
45
|
+
const p2x = dp2[1] * n[2] - dp2[2] * n[1];
|
|
46
|
+
const p2y = dp2[2] * n[0] - dp2[0] * n[2];
|
|
47
|
+
const p2z = dp2[0] * n[1] - dp2[1] * n[0];
|
|
48
|
+
const p1x = n[1] * dp1[2] - n[2] * dp1[1];
|
|
49
|
+
const p1y = n[2] * dp1[0] - n[0] * dp1[2];
|
|
50
|
+
const p1z = n[0] * dp1[1] - n[1] * dp1[0];
|
|
51
|
+
const tx = p2x * duv1[0] + p1x * duv2[0];
|
|
52
|
+
const ty = p2y * duv1[0] + p1y * duv2[0];
|
|
53
|
+
const tz = p2z * duv1[0] + p1z * duv2[0];
|
|
54
|
+
const bx = p2x * duv1[1] + p1x * duv2[1];
|
|
55
|
+
const by = p2y * duv1[1] + p1y * duv2[1];
|
|
56
|
+
const bz = p2z * duv1[1] + p1z * duv2[1];
|
|
57
|
+
const det = duv1[0] * duv2[1] - duv2[0] * duv1[1];
|
|
58
|
+
const handed = det < 0 ? -1 : 1;
|
|
59
|
+
const scale =
|
|
60
|
+
handed / Math.sqrt(Math.max(tx * tx + ty * ty + tz * tz, bx * bx + by * by + bz * bz, 1e-12));
|
|
61
|
+
out[0] = tx * scale;
|
|
62
|
+
out[1] = ty * scale;
|
|
63
|
+
out[2] = tz * scale;
|
|
64
|
+
out[3] = bx * scale;
|
|
65
|
+
out[4] = by * scale;
|
|
66
|
+
out[5] = bz * scale;
|
|
67
|
+
return out;
|
|
68
|
+
}
|