@driftengine/core 3.63.0 → 4.1.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 +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,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GPU-driven frame, as an order and a set of dependencies.
|
|
3
|
+
*
|
|
4
|
+
* **Every stage of Wave 2A exists on its own and this is what says how they fit together.** The
|
|
5
|
+
* frame's colour is cleared, the directional map is drawn, the meshes wholly outside the view are
|
|
6
|
+
* flagged, the cut through the cluster graph is chosen without them and phase one culls what was
|
|
7
|
+
* visible last frame, phase one draws, the depth pyramid is reduced from that, phase two culls the
|
|
8
|
+
* rest against it and draws, the visibility is copied where a dispatch can read it, binned by
|
|
9
|
+
* material, and each bin is shaded. Twelve stages, and the edges between them are what stop a
|
|
10
|
+
* scheduler reordering the ones that must not move — see `twoPhase.ts` for the one that matters
|
|
11
|
+
* most.
|
|
12
|
+
*
|
|
13
|
+
* **The pipeline is opt-in and WebGPU only.** `gpuDrivenSupported` is `indirectSupport`, which is
|
|
14
|
+
* false on WebGL2 and not a thing a fallback can paper over: without an indirect draw the counts
|
|
15
|
+
* this frame produces would have to come back to the processor, and a readback is slower than the
|
|
16
|
+
* draw it would replace. Selecting it on a backend that cannot run it **fails at creation** rather
|
|
17
|
+
* than falling back, because a silent fallback means a consumer ships believing they have a
|
|
18
|
+
* pipeline they do not.
|
|
19
|
+
*
|
|
20
|
+
* **This is what `GpuDrivenPass` is scheduled by**, since 2026-09-17: the pass records these stages
|
|
21
|
+
* into a `Deps` table each frame, `scheduleGraph` decides which of them run, and the pass encodes
|
|
22
|
+
* the ones it kept in the order it kept them. Until then this was a description beside an encoder
|
|
23
|
+
* with its own fixed order — and the description had drifted, naming a separate instance cull the
|
|
24
|
+
* encoder never had and a colour both draws wrote when neither does. The instance cull is a stage
|
|
25
|
+
* of the encoder now, 2026-09-17, which is `wave2a`'s fifth task finally reaching the frame. Binding real buffers and
|
|
26
|
+
* issuing real dispatches stays the backend's; what moved here is the decision about what runs.
|
|
27
|
+
*/
|
|
28
|
+
import { indirectSupport } from '../backend/api.js';
|
|
29
|
+
import { recordDeps } from '../frame/deps.js';
|
|
30
|
+
/** Whether a backend can run this pipeline at all. */
|
|
31
|
+
export function gpuDrivenSupported(backend) {
|
|
32
|
+
return indirectSupport(backend);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Why a backend cannot, in the words a consumer sees.
|
|
36
|
+
*
|
|
37
|
+
* A sentence rather than a code, because the one thing the reader needs is that this is a property
|
|
38
|
+
* of the backend and not of their scene — and `''` where it can.
|
|
39
|
+
*/
|
|
40
|
+
export function gpuDrivenRefusal(backend) {
|
|
41
|
+
if (gpuDrivenSupported(backend))
|
|
42
|
+
return '';
|
|
43
|
+
return (`the gpu-driven pipeline needs indirect draws and dispatches, which ${backend} does not have. ` +
|
|
44
|
+
'Emulating them through a readback is slower than the draw it would replace, so this fails ' +
|
|
45
|
+
'rather than falling back: a silent fallback ships a consumer a pipeline they do not have.');
|
|
46
|
+
}
|
|
47
|
+
/** The identifiers one frame uses. A frame is its own graph, so they start at zero. */
|
|
48
|
+
export const GPU_DRIVEN_RESOURCES = {
|
|
49
|
+
clusters: 0,
|
|
50
|
+
history: 1,
|
|
51
|
+
shadowMap: 2,
|
|
52
|
+
cut: 3,
|
|
53
|
+
keep: 4,
|
|
54
|
+
listOne: 5,
|
|
55
|
+
listTwo: 6,
|
|
56
|
+
drawn: 7,
|
|
57
|
+
depth: 8,
|
|
58
|
+
visibility: 9,
|
|
59
|
+
visibilityCopy: 10,
|
|
60
|
+
hzb: 11,
|
|
61
|
+
binCounts: 12,
|
|
62
|
+
binOffsets: 13,
|
|
63
|
+
binCursors: 14,
|
|
64
|
+
binPixels: 15,
|
|
65
|
+
colour: 16,
|
|
66
|
+
instances: 17,
|
|
67
|
+
blendList: 18,
|
|
68
|
+
oitAccum: 19,
|
|
69
|
+
oitReveal: 20,
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* What each stage is called, in the order they are recorded — one render or compute pass each, or
|
|
73
|
+
* one copy between them.
|
|
74
|
+
*/
|
|
75
|
+
export const GPU_DRIVEN_PASSES = [
|
|
76
|
+
'clearColour',
|
|
77
|
+
'shadow',
|
|
78
|
+
'instanceCull',
|
|
79
|
+
'cut',
|
|
80
|
+
'phaseOneDraw',
|
|
81
|
+
'pyramid',
|
|
82
|
+
'phaseTwoCull',
|
|
83
|
+
'phaseTwoDraw',
|
|
84
|
+
'blendCull',
|
|
85
|
+
'blendDraw',
|
|
86
|
+
'visibilityCopy',
|
|
87
|
+
'bin',
|
|
88
|
+
'cursorCopy',
|
|
89
|
+
'shade',
|
|
90
|
+
'blendResolve',
|
|
91
|
+
];
|
|
92
|
+
/**
|
|
93
|
+
* **Every stage is ordered, so none merges with a neighbour.** Two adjacent compute stages could
|
|
94
|
+
* share a pass as far as their results go; they do not, because each is bracketed by its own pair
|
|
95
|
+
* of timestamps and a pair brackets a pass. What the schedule decides here is which stages run,
|
|
96
|
+
* and it checks the order the names give against the edges the stages declare.
|
|
97
|
+
*/
|
|
98
|
+
export const GPU_DRIVEN_ORDERED = new Uint8Array(GPU_DRIVEN_PASSES.length).fill(1);
|
|
99
|
+
/**
|
|
100
|
+
* The frame's edges, stage by stage. The claim is here, and five parts of it are load-bearing
|
|
101
|
+
* enough to name:
|
|
102
|
+
*
|
|
103
|
+
* - `cut` reads the flags `instanceCull` wrote, so a mesh wholly outside the view has its clusters
|
|
104
|
+
* left out of the cut before the cluster cull or the compaction reads one of them.
|
|
105
|
+
* - `pyramid` reads the depth `phaseOneDraw` wrote, and `phaseTwoCull` reads the pyramid. Together
|
|
106
|
+
* those are "the occlusion is tested against depth from this frame", which is the whole reason
|
|
107
|
+
* the frame has two halves — and `cut` reads no pyramid at all, which is the other half of it.
|
|
108
|
+
* - `cut` and `phaseTwoCull` both read the history and both write the next one: the history
|
|
109
|
+
* decides which half judges a cluster, never whether one does.
|
|
110
|
+
* - `cursorCopy` reads the offsets `bin` wrote and `shade` reads the cursors. The scatter cannot
|
|
111
|
+
* start before the sum has, and neither ordering is a comment anybody has to obey.
|
|
112
|
+
* - `shade` reads the map only where the frame is shadowed — `unshadowedShade` — so an unshadowed
|
|
113
|
+
* frame's map is something nothing reads, and `scheduleGraph` culls the stage that draws it.
|
|
114
|
+
*/
|
|
115
|
+
function edgesFor(res) {
|
|
116
|
+
const table = {
|
|
117
|
+
clearColour: [[], [res.colour]],
|
|
118
|
+
shadow: [[res.clusters], [res.shadowMap]],
|
|
119
|
+
instanceCull: [[res.clusters], [res.instances]],
|
|
120
|
+
cut: [
|
|
121
|
+
[res.clusters, res.history, res.instances],
|
|
122
|
+
[res.cut, res.keep, res.listOne, res.drawn],
|
|
123
|
+
],
|
|
124
|
+
phaseOneDraw: [
|
|
125
|
+
[res.listOne, res.clusters],
|
|
126
|
+
[res.depth, res.visibility],
|
|
127
|
+
],
|
|
128
|
+
pyramid: [[res.depth], [res.hzb]],
|
|
129
|
+
phaseTwoCull: [
|
|
130
|
+
[res.hzb, res.cut, res.clusters, res.history],
|
|
131
|
+
[res.keep, res.listTwo, res.drawn],
|
|
132
|
+
],
|
|
133
|
+
phaseTwoDraw: [
|
|
134
|
+
[res.listTwo, res.clusters, res.depth, res.visibility],
|
|
135
|
+
[res.depth, res.visibility],
|
|
136
|
+
],
|
|
137
|
+
/*
|
|
138
|
+
* **The cut's second run, over the other half of the scene.** It reads what the first run read
|
|
139
|
+
* and writes the same working buffers, which is safe because it runs after both draws have
|
|
140
|
+
* taken what they needed from them — and it writes a list of its own, which is what the blended
|
|
141
|
+
* draw reads. It reads the pyramid too: a pane wholly behind a wall is occluded exactly as an
|
|
142
|
+
* opaque cluster there would be.
|
|
143
|
+
*/
|
|
144
|
+
blendCull: [
|
|
145
|
+
[res.clusters, res.instances, res.hzb],
|
|
146
|
+
[res.cut, res.keep, res.blendList],
|
|
147
|
+
],
|
|
148
|
+
/*
|
|
149
|
+
* **It reads the depth the opaque half wrote and does not write it.** A pane behind a wall is
|
|
150
|
+
* rejected at the wall; nothing here writes depth, so two panes do not hide each other and
|
|
151
|
+
* there is something left to blend.
|
|
152
|
+
*/
|
|
153
|
+
blendDraw: [
|
|
154
|
+
[res.blendList, res.clusters, res.depth],
|
|
155
|
+
[res.oitAccum, res.oitReveal],
|
|
156
|
+
],
|
|
157
|
+
visibilityCopy: [[res.visibility], [res.visibilityCopy]],
|
|
158
|
+
bin: [[res.visibilityCopy], [res.binCounts, res.binOffsets]],
|
|
159
|
+
cursorCopy: [[res.binOffsets], [res.binCursors]],
|
|
160
|
+
shade: [
|
|
161
|
+
[res.visibilityCopy, res.binOffsets, res.binCursors, res.clusters, res.colour, res.shadowMap],
|
|
162
|
+
[res.binPixels, res.colour],
|
|
163
|
+
],
|
|
164
|
+
/*
|
|
165
|
+
* **After the shading, because it composites over what the shading wrote.** The opaque picture
|
|
166
|
+
* is the background a transparent layer is blended against, so this is the last thing the
|
|
167
|
+
* frame does to its own colour target.
|
|
168
|
+
*/
|
|
169
|
+
blendResolve: [[res.oitAccum, res.oitReveal, res.colour], [res.colour]],
|
|
170
|
+
};
|
|
171
|
+
return {
|
|
172
|
+
stages: GPU_DRIVEN_PASSES.map((name) => table[name]),
|
|
173
|
+
unshadowedShade: table.shade[0].filter((id) => id !== res.shadowMap),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/* Built once a resource table, so a frame records without constructing an edge list. */
|
|
177
|
+
const EDGES = new WeakMap();
|
|
178
|
+
const SHADE = GPU_DRIVEN_PASSES.indexOf('shade');
|
|
179
|
+
/**
|
|
180
|
+
* The three stages a frame with nothing transparent does not record at all.
|
|
181
|
+
*
|
|
182
|
+
* **Culled by giving them no edges rather than by filtering a read**, which is how the shadow map
|
|
183
|
+
* is culled one line above and does not work here: `blendResolve` writes the colour, the colour is
|
|
184
|
+
* what the frame is kept alive for, so a stage that writes it survives any amount of filtering. A
|
|
185
|
+
* stage with no writes is read by nothing and the scheduler drops it, which is the same answer
|
|
186
|
+
* reached the only way that is true for this one.
|
|
187
|
+
*/
|
|
188
|
+
const BLEND_STAGES = [
|
|
189
|
+
GPU_DRIVEN_PASSES.indexOf('blendCull'),
|
|
190
|
+
GPU_DRIVEN_PASSES.indexOf('blendDraw'),
|
|
191
|
+
GPU_DRIVEN_PASSES.indexOf('blendResolve'),
|
|
192
|
+
];
|
|
193
|
+
const NOTHING = [];
|
|
194
|
+
/**
|
|
195
|
+
* Record the whole frame, in order, and answer the index its first stage landed at.
|
|
196
|
+
*
|
|
197
|
+
* Allocates nothing once a resource table has been seen, which is what lets `GpuDrivenPass` call it
|
|
198
|
+
* every frame. Stage `i` of `GPU_DRIVEN_PASSES` is node `first + i`.
|
|
199
|
+
*/
|
|
200
|
+
export function recordGpuDrivenStages(deps, res, shape) {
|
|
201
|
+
let edges = EDGES.get(res);
|
|
202
|
+
if (edges === undefined) {
|
|
203
|
+
edges = edgesFor(res);
|
|
204
|
+
EDGES.set(res, edges);
|
|
205
|
+
}
|
|
206
|
+
const first = deps.count;
|
|
207
|
+
for (let stage = 0; stage < edges.stages.length; stage += 1) {
|
|
208
|
+
if (!shape.blended && BLEND_STAGES.includes(stage)) {
|
|
209
|
+
recordDeps(deps, NOTHING, NOTHING);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const [reads, writes] = edges.stages[stage];
|
|
213
|
+
recordDeps(deps, stage === SHADE && !shape.shadowed ? edges.unshadowedShade : reads, writes);
|
|
214
|
+
}
|
|
215
|
+
return first;
|
|
216
|
+
}
|
|
217
|
+
/** Record the whole frame and answer where each pass went, by name. For a reader, not a frame. */
|
|
218
|
+
export function recordGpuDrivenFrame(deps, res, shape) {
|
|
219
|
+
const first = recordGpuDrivenStages(deps, res, shape);
|
|
220
|
+
return Object.fromEntries(GPU_DRIVEN_PASSES.map((name, index) => [name, first + index]));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* What a frame leaves behind, which is what `scheduleGraph` is asked to keep alive.
|
|
224
|
+
*
|
|
225
|
+
* The colour, which the blit presents, and the history, which next frame's phase one reads. The
|
|
226
|
+
* depth, the pyramid, the visibility buffer and every bin are this frame's working set and nothing
|
|
227
|
+
* outside reads them. Naming more than is needed keeps resources alive that could have been
|
|
228
|
+
* aliased — which is `alias.ts`'s whole subject — and naming fewer culls a pass that was doing
|
|
229
|
+
* something.
|
|
230
|
+
*/
|
|
231
|
+
export function gpuDrivenLive(res) {
|
|
232
|
+
return [res.colour, res.drawn];
|
|
233
|
+
}
|
|
234
|
+
/** What a frame reads without having written: the scene, and last frame's history. */
|
|
235
|
+
export function gpuDrivenImports(res) {
|
|
236
|
+
return [res.clusters, res.history];
|
|
237
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A free list over one axis of units, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* **Four of these make a streaming scene** — vertices, indices, clusters and meshes — and none of
|
|
5
|
+
* them needs to know what a cluster is. Keeping it that way is what lets "a returned range is
|
|
6
|
+
* reusable" be a test that does not build a mesh.
|
|
7
|
+
*
|
|
8
|
+
* **Best fit rather than first fit**, because the workload that motivated this remeshes a chunk
|
|
9
|
+
* into a slightly different size on every block a player breaks. First fit hands the front of the
|
|
10
|
+
* largest hole to whatever asks next, so the large holes disappear first and the request that
|
|
11
|
+
* needed one is refused while the total says there is room.
|
|
12
|
+
*
|
|
13
|
+
* **Refusal is `-1` rather than an exception**, because a streaming consumer calls `claim` from a
|
|
14
|
+
* frame loop: an exception there is a crashed demo where a refusal is a chunk that arrives a frame
|
|
15
|
+
* later.
|
|
16
|
+
*/
|
|
17
|
+
export declare class RangeAlloc {
|
|
18
|
+
/**
|
|
19
|
+
* The free runs, kept sorted by `at` and never touching.
|
|
20
|
+
*
|
|
21
|
+
* **Sorted and coalesced is an invariant rather than a convenience**: it is what makes the
|
|
22
|
+
* layout independent of the order ranges were released in, which a capture would otherwise
|
|
23
|
+
* expose as an image that differs between runs with nothing failing.
|
|
24
|
+
*/
|
|
25
|
+
private holes;
|
|
26
|
+
private readonly total;
|
|
27
|
+
private usedUnits;
|
|
28
|
+
private high;
|
|
29
|
+
constructor(capacity: number);
|
|
30
|
+
get capacity(): number;
|
|
31
|
+
get used(): number;
|
|
32
|
+
/** The most units ever live at once. `scripts/stream-fragmentation.mjs` reads this. */
|
|
33
|
+
get highWater(): number;
|
|
34
|
+
/**
|
|
35
|
+
* The first unit of a run of `units`, or -1 where no single run is long enough.
|
|
36
|
+
*
|
|
37
|
+
* A request for nothing succeeds at 0 and occupies nothing: a mesh with no clusters is legal,
|
|
38
|
+
* and `buildGpuDrivenScene`'s header says what dropping one would do to every mesh after it.
|
|
39
|
+
*/
|
|
40
|
+
claim(units: number): number;
|
|
41
|
+
/**
|
|
42
|
+
* Give a run back, merging it with whatever it now touches.
|
|
43
|
+
*
|
|
44
|
+
* **Merged on the way in rather than swept later**, which is what keeps the invariant true at
|
|
45
|
+
* every moment a caller could look and makes the order releases arrived in stop mattering.
|
|
46
|
+
*/
|
|
47
|
+
release(at: number, units: number): void;
|
|
48
|
+
/** Back to one free run, keeping the high-water mark, which is a record of the run so far. */
|
|
49
|
+
reset(): void;
|
|
50
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A free list over one axis of units, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* **Four of these make a streaming scene** — vertices, indices, clusters and meshes — and none of
|
|
5
|
+
* them needs to know what a cluster is. Keeping it that way is what lets "a returned range is
|
|
6
|
+
* reusable" be a test that does not build a mesh.
|
|
7
|
+
*
|
|
8
|
+
* **Best fit rather than first fit**, because the workload that motivated this remeshes a chunk
|
|
9
|
+
* into a slightly different size on every block a player breaks. First fit hands the front of the
|
|
10
|
+
* largest hole to whatever asks next, so the large holes disappear first and the request that
|
|
11
|
+
* needed one is refused while the total says there is room.
|
|
12
|
+
*
|
|
13
|
+
* **Refusal is `-1` rather than an exception**, because a streaming consumer calls `claim` from a
|
|
14
|
+
* frame loop: an exception there is a crashed demo where a refusal is a chunk that arrives a frame
|
|
15
|
+
* later.
|
|
16
|
+
*/
|
|
17
|
+
export class RangeAlloc {
|
|
18
|
+
/**
|
|
19
|
+
* The free runs, kept sorted by `at` and never touching.
|
|
20
|
+
*
|
|
21
|
+
* **Sorted and coalesced is an invariant rather than a convenience**: it is what makes the
|
|
22
|
+
* layout independent of the order ranges were released in, which a capture would otherwise
|
|
23
|
+
* expose as an image that differs between runs with nothing failing.
|
|
24
|
+
*/
|
|
25
|
+
holes;
|
|
26
|
+
total;
|
|
27
|
+
usedUnits = 0;
|
|
28
|
+
high = 0;
|
|
29
|
+
constructor(capacity) {
|
|
30
|
+
const units = Math.max(0, Math.floor(capacity));
|
|
31
|
+
this.total = units;
|
|
32
|
+
this.holes = units > 0 ? [{ at: 0, units }] : [];
|
|
33
|
+
}
|
|
34
|
+
get capacity() {
|
|
35
|
+
return this.total;
|
|
36
|
+
}
|
|
37
|
+
get used() {
|
|
38
|
+
return this.usedUnits;
|
|
39
|
+
}
|
|
40
|
+
/** The most units ever live at once. `scripts/stream-fragmentation.mjs` reads this. */
|
|
41
|
+
get highWater() {
|
|
42
|
+
return this.high;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The first unit of a run of `units`, or -1 where no single run is long enough.
|
|
46
|
+
*
|
|
47
|
+
* A request for nothing succeeds at 0 and occupies nothing: a mesh with no clusters is legal,
|
|
48
|
+
* and `buildGpuDrivenScene`'s header says what dropping one would do to every mesh after it.
|
|
49
|
+
*/
|
|
50
|
+
claim(units) {
|
|
51
|
+
const want = Math.max(0, Math.floor(units));
|
|
52
|
+
if (want === 0)
|
|
53
|
+
return 0;
|
|
54
|
+
let best = -1;
|
|
55
|
+
for (let i = 0; i < this.holes.length; i += 1) {
|
|
56
|
+
const hole = this.holes[i];
|
|
57
|
+
if (hole.units < want)
|
|
58
|
+
continue;
|
|
59
|
+
if (best < 0 || hole.units < this.holes[best].units)
|
|
60
|
+
best = i;
|
|
61
|
+
}
|
|
62
|
+
if (best < 0)
|
|
63
|
+
return -1;
|
|
64
|
+
const hole = this.holes[best];
|
|
65
|
+
const at = hole.at;
|
|
66
|
+
if (hole.units === want)
|
|
67
|
+
this.holes.splice(best, 1);
|
|
68
|
+
else {
|
|
69
|
+
hole.at += want;
|
|
70
|
+
hole.units -= want;
|
|
71
|
+
}
|
|
72
|
+
this.usedUnits += want;
|
|
73
|
+
if (this.usedUnits > this.high)
|
|
74
|
+
this.high = this.usedUnits;
|
|
75
|
+
return at;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Give a run back, merging it with whatever it now touches.
|
|
79
|
+
*
|
|
80
|
+
* **Merged on the way in rather than swept later**, which is what keeps the invariant true at
|
|
81
|
+
* every moment a caller could look and makes the order releases arrived in stop mattering.
|
|
82
|
+
*/
|
|
83
|
+
release(at, units) {
|
|
84
|
+
const count = Math.max(0, Math.floor(units));
|
|
85
|
+
if (count === 0)
|
|
86
|
+
return;
|
|
87
|
+
const start = Math.max(0, Math.floor(at));
|
|
88
|
+
let index = 0;
|
|
89
|
+
while (index < this.holes.length && this.holes[index].at < start)
|
|
90
|
+
index += 1;
|
|
91
|
+
this.holes.splice(index, 0, { at: start, units: count });
|
|
92
|
+
this.usedUnits = Math.max(0, this.usedUnits - count);
|
|
93
|
+
/* Merge with the one before and the one after, in that order, so three runs become one. */
|
|
94
|
+
const previous = index > 0 ? this.holes[index - 1] : null;
|
|
95
|
+
if (previous !== null && previous.at + previous.units === start) {
|
|
96
|
+
previous.units += count;
|
|
97
|
+
this.holes.splice(index, 1);
|
|
98
|
+
index -= 1;
|
|
99
|
+
}
|
|
100
|
+
const merged = this.holes[index];
|
|
101
|
+
const next = index + 1 < this.holes.length ? this.holes[index + 1] : null;
|
|
102
|
+
if (next !== null && merged.at + merged.units === next.at) {
|
|
103
|
+
merged.units += next.units;
|
|
104
|
+
this.holes.splice(index + 1, 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Back to one free run, keeping the high-water mark, which is a record of the run so far. */
|
|
108
|
+
reset() {
|
|
109
|
+
this.holes = this.total > 0 ? [{ at: 0, units: this.total }] : [];
|
|
110
|
+
this.usedUnits = 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scene's meshes packed into the one set of buffers a GPU-driven frame reads.
|
|
3
|
+
*
|
|
4
|
+
* **`clusterUpload.ts` packs the clusters and this packs everything they point at.** That split is
|
|
5
|
+
* not arbitrary: the cluster packing is about identifiers and spans and has no idea a vertex
|
|
6
|
+
* exists, and it rebases each mesh's index *offsets* by the indices already written while leaving
|
|
7
|
+
* the index *values* alone. Which is right — a mesh's indices address that mesh's vertices — and
|
|
8
|
+
* is exactly wrong the moment two meshes share one buffer, which is the whole point of the
|
|
9
|
+
* pipeline. Every mesh after the first would address vertex zero of the *scene*, a real vertex
|
|
10
|
+
* belonging to the first mesh, drawn in the wrong place with nothing reporting anything.
|
|
11
|
+
*
|
|
12
|
+
* So the rebasing happens here, where the vertex base is known, and the packer is handed sources
|
|
13
|
+
* whose indices are already scene-global.
|
|
14
|
+
*
|
|
15
|
+
* **Three parallel arrays rather than one interleaved vertex.** The raster shader reads
|
|
16
|
+
* `positions[index * 3]` and the shading pass reads normals and colours the same way; an
|
|
17
|
+
* interleaved stride would put the layout in three shaders instead of one place, and the memory
|
|
18
|
+
* this saves is not the memory the pipeline is short of.
|
|
19
|
+
*
|
|
20
|
+
* **Colour is a vertex attribute, because that is what this engine's geometry carries.**
|
|
21
|
+
* `MeshData` has `colors` and most of it is untextured, so the shading pass interpolates them the
|
|
22
|
+
* way the forward path does rather than inventing a material system to sit beside the one that
|
|
23
|
+
* exists. What a *material* then selects is the shading each bin gets — see `materialBin.ts` — and
|
|
24
|
+
* a mesh names one.
|
|
25
|
+
*/
|
|
26
|
+
import { type ClusterBuffers, type ClusterSource } from './clusterUpload.ts';
|
|
27
|
+
/**
|
|
28
|
+
* Floats a vertex in the buffer the GPU-driven pass uploads: position, normal, colour, uv, glow.
|
|
29
|
+
*
|
|
30
|
+
* **One number read by four consumers** — the upload, the raster, the blended raster and the
|
|
31
|
+
* shading pass — and `vertexLayout.test.ts` holds all four to it. It was nine until UVs arrived on
|
|
32
|
+
* 2026-09-17, and eleven until the voxel sandbox's block light needed a glow a vertex on 2026-09-18.
|
|
33
|
+
*/
|
|
34
|
+
export declare const GPU_DRIVEN_VERTEX_FLOATS = 12;
|
|
35
|
+
/** One mesh of a GPU-driven scene: its vertices, its clusters, and which material shades it. */
|
|
36
|
+
export interface GpuDrivenMesh {
|
|
37
|
+
/** Three floats a vertex. */
|
|
38
|
+
readonly positions: Float32Array;
|
|
39
|
+
/** Three floats a vertex. */
|
|
40
|
+
readonly normals: Float32Array;
|
|
41
|
+
/** Three floats a vertex. */
|
|
42
|
+
readonly colours: Float32Array;
|
|
43
|
+
/**
|
|
44
|
+
* Two floats a vertex. Optional: a mesh without them uploads zeros, and a textured material on
|
|
45
|
+
* it reads one texel everywhere and turns no normal — a statement about the mesh, and what the
|
|
46
|
+
* forward path does with an absent attribute.
|
|
47
|
+
*/
|
|
48
|
+
readonly uvs?: Float32Array;
|
|
49
|
+
/**
|
|
50
|
+
* One float a vertex, added to the material's own emissive. Optional: a mesh without it glows as
|
|
51
|
+
* its material says and nothing more, which is every mesh before 2026-09-18.
|
|
52
|
+
*
|
|
53
|
+
* **For light a colour cannot carry.** The voxel sandbox folds occlusion and sky light into the
|
|
54
|
+
* vertex colour and keeps block light here, because it has to survive the sun going down and a
|
|
55
|
+
* colour is multiplied by the sun. The forward path reads the same attribute from `MeshData`.
|
|
56
|
+
*/
|
|
57
|
+
readonly emissive?: Float32Array;
|
|
58
|
+
/** This mesh's clusters, with indices addressing *this mesh's* vertices. */
|
|
59
|
+
readonly clusters: ClusterSource;
|
|
60
|
+
/** Which material bin every cluster of this mesh shades in. */
|
|
61
|
+
readonly material: number;
|
|
62
|
+
}
|
|
63
|
+
export interface GpuDrivenScene {
|
|
64
|
+
/** Every mesh's positions, concatenated in the order they were given. */
|
|
65
|
+
readonly positions: Float32Array;
|
|
66
|
+
readonly normals: Float32Array;
|
|
67
|
+
readonly colours: Float32Array;
|
|
68
|
+
/** Two floats a vertex, zeros where a mesh carried none. */
|
|
69
|
+
readonly uvs: Float32Array;
|
|
70
|
+
/** One float a vertex, zeros where a mesh carried none. */
|
|
71
|
+
readonly emissive: Float32Array;
|
|
72
|
+
/** The clusters, with indices addressing `positions` rather than any one mesh. */
|
|
73
|
+
readonly clusters: ClusterBuffers;
|
|
74
|
+
/** One material a cluster, from the mesh it came from. */
|
|
75
|
+
readonly materialOf: Uint32Array;
|
|
76
|
+
/** Where each mesh's vertices begin. One entry a mesh, including meshes with no clusters. */
|
|
77
|
+
readonly vertexBase: Uint32Array;
|
|
78
|
+
readonly vertexCount: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A mesh's per-vertex glow, refused by name when it is not one float a vertex.
|
|
82
|
+
*
|
|
83
|
+
* Shared by both packers, because a glow of the wrong length read one float a vertex is somebody
|
|
84
|
+
* else's torchlight on the next mesh along — or `NaN` past the end, which lights nothing and says
|
|
85
|
+
* nothing about why.
|
|
86
|
+
*/
|
|
87
|
+
export declare function glowOf(mesh: GpuDrivenMesh, at: number | string): Float32Array | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Pack a scene.
|
|
90
|
+
*
|
|
91
|
+
* **A mesh with no clusters still takes its slot**, in `vertexBase` and in the mesh numbering the
|
|
92
|
+
* clusters carry. `meshOf` indexes the caller's transform array, so dropping an empty mesh shifts
|
|
93
|
+
* every mesh after it and each of their clusters draws with somebody else's matrix — the same
|
|
94
|
+
* reasoning `packClusters` already gives, one level up.
|
|
95
|
+
*/
|
|
96
|
+
export declare function buildGpuDrivenScene(meshes: readonly GpuDrivenMesh[]): GpuDrivenScene;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scene's meshes packed into the one set of buffers a GPU-driven frame reads.
|
|
3
|
+
*
|
|
4
|
+
* **`clusterUpload.ts` packs the clusters and this packs everything they point at.** That split is
|
|
5
|
+
* not arbitrary: the cluster packing is about identifiers and spans and has no idea a vertex
|
|
6
|
+
* exists, and it rebases each mesh's index *offsets* by the indices already written while leaving
|
|
7
|
+
* the index *values* alone. Which is right — a mesh's indices address that mesh's vertices — and
|
|
8
|
+
* is exactly wrong the moment two meshes share one buffer, which is the whole point of the
|
|
9
|
+
* pipeline. Every mesh after the first would address vertex zero of the *scene*, a real vertex
|
|
10
|
+
* belonging to the first mesh, drawn in the wrong place with nothing reporting anything.
|
|
11
|
+
*
|
|
12
|
+
* So the rebasing happens here, where the vertex base is known, and the packer is handed sources
|
|
13
|
+
* whose indices are already scene-global.
|
|
14
|
+
*
|
|
15
|
+
* **Three parallel arrays rather than one interleaved vertex.** The raster shader reads
|
|
16
|
+
* `positions[index * 3]` and the shading pass reads normals and colours the same way; an
|
|
17
|
+
* interleaved stride would put the layout in three shaders instead of one place, and the memory
|
|
18
|
+
* this saves is not the memory the pipeline is short of.
|
|
19
|
+
*
|
|
20
|
+
* **Colour is a vertex attribute, because that is what this engine's geometry carries.**
|
|
21
|
+
* `MeshData` has `colors` and most of it is untextured, so the shading pass interpolates them the
|
|
22
|
+
* way the forward path does rather than inventing a material system to sit beside the one that
|
|
23
|
+
* exists. What a *material* then selects is the shading each bin gets — see `materialBin.ts` — and
|
|
24
|
+
* a mesh names one.
|
|
25
|
+
*/
|
|
26
|
+
import { packClusters } from './clusterUpload.js';
|
|
27
|
+
/**
|
|
28
|
+
* Floats a vertex in the buffer the GPU-driven pass uploads: position, normal, colour, uv, glow.
|
|
29
|
+
*
|
|
30
|
+
* **One number read by four consumers** — the upload, the raster, the blended raster and the
|
|
31
|
+
* shading pass — and `vertexLayout.test.ts` holds all four to it. It was nine until UVs arrived on
|
|
32
|
+
* 2026-09-17, and eleven until the voxel sandbox's block light needed a glow a vertex on 2026-09-18.
|
|
33
|
+
*/
|
|
34
|
+
export const GPU_DRIVEN_VERTEX_FLOATS = 12;
|
|
35
|
+
/**
|
|
36
|
+
* A mesh's per-vertex glow, refused by name when it is not one float a vertex.
|
|
37
|
+
*
|
|
38
|
+
* Shared by both packers, because a glow of the wrong length read one float a vertex is somebody
|
|
39
|
+
* else's torchlight on the next mesh along — or `NaN` past the end, which lights nothing and says
|
|
40
|
+
* nothing about why.
|
|
41
|
+
*/
|
|
42
|
+
export function glowOf(mesh, at) {
|
|
43
|
+
const glow = mesh.emissive;
|
|
44
|
+
if (glow === undefined)
|
|
45
|
+
return undefined;
|
|
46
|
+
const count = mesh.positions.length / 3;
|
|
47
|
+
if (glow.length !== count) {
|
|
48
|
+
throw new Error(`[driftengine] mesh ${at} carries ${glow.length} emissive values for ${count} vertices`);
|
|
49
|
+
}
|
|
50
|
+
return glow;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Pack a scene.
|
|
54
|
+
*
|
|
55
|
+
* **A mesh with no clusters still takes its slot**, in `vertexBase` and in the mesh numbering the
|
|
56
|
+
* clusters carry. `meshOf` indexes the caller's transform array, so dropping an empty mesh shifts
|
|
57
|
+
* every mesh after it and each of their clusters draws with somebody else's matrix — the same
|
|
58
|
+
* reasoning `packClusters` already gives, one level up.
|
|
59
|
+
*/
|
|
60
|
+
export function buildGpuDrivenScene(meshes) {
|
|
61
|
+
let vertices = 0;
|
|
62
|
+
let clusters = 0;
|
|
63
|
+
const vertexBase = new Uint32Array(meshes.length);
|
|
64
|
+
for (let at = 0; at < meshes.length; at += 1) {
|
|
65
|
+
const mesh = meshes[at];
|
|
66
|
+
vertexBase[at] = vertices;
|
|
67
|
+
vertices += mesh.positions.length / 3;
|
|
68
|
+
clusters += mesh.clusters.count;
|
|
69
|
+
}
|
|
70
|
+
const positions = new Float32Array(vertices * 3);
|
|
71
|
+
const normals = new Float32Array(vertices * 3);
|
|
72
|
+
const colours = new Float32Array(vertices * 3);
|
|
73
|
+
const uvs = new Float32Array(vertices * 2);
|
|
74
|
+
const emissive = new Float32Array(vertices);
|
|
75
|
+
const rebased = [];
|
|
76
|
+
const materialOf = new Uint32Array(clusters);
|
|
77
|
+
let cluster = 0;
|
|
78
|
+
for (let at = 0; at < meshes.length; at += 1) {
|
|
79
|
+
const mesh = meshes[at];
|
|
80
|
+
const base = vertexBase[at];
|
|
81
|
+
positions.set(mesh.positions, base * 3);
|
|
82
|
+
normals.set(mesh.normals, base * 3);
|
|
83
|
+
colours.set(mesh.colours, base * 3);
|
|
84
|
+
if (mesh.uvs !== undefined) {
|
|
85
|
+
const count = mesh.positions.length / 3;
|
|
86
|
+
if (mesh.uvs.length !== count * 2) {
|
|
87
|
+
throw new Error(`[driftengine] mesh ${at} carries ${mesh.uvs.length} uv values for ${count} vertices`);
|
|
88
|
+
}
|
|
89
|
+
uvs.set(mesh.uvs, base * 2);
|
|
90
|
+
}
|
|
91
|
+
const glow = glowOf(mesh, at);
|
|
92
|
+
if (glow !== undefined)
|
|
93
|
+
emissive.set(glow, base);
|
|
94
|
+
const indices = new Uint32Array(mesh.clusters.indices.length);
|
|
95
|
+
for (let i = 0; i < indices.length; i += 1) {
|
|
96
|
+
indices[i] = mesh.clusters.indices[i] + base;
|
|
97
|
+
}
|
|
98
|
+
rebased.push({ ...mesh.clusters, indices });
|
|
99
|
+
for (let c = 0; c < mesh.clusters.count; c += 1) {
|
|
100
|
+
materialOf[cluster] = mesh.material;
|
|
101
|
+
cluster += 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
positions,
|
|
106
|
+
normals,
|
|
107
|
+
colours,
|
|
108
|
+
uvs,
|
|
109
|
+
emissive,
|
|
110
|
+
clusters: packClusters(rebased),
|
|
111
|
+
materialOf,
|
|
112
|
+
vertexBase,
|
|
113
|
+
vertexCount: vertices,
|
|
114
|
+
};
|
|
115
|
+
}
|