@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
|
@@ -177,7 +177,21 @@ export function scatterDepthPipeline(cache, device, layout, shadowFormat, depthF
|
|
|
177
177
|
targets: [],
|
|
178
178
|
},
|
|
179
179
|
primitive: { topology: 'triangle-list', cullMode: 'none' },
|
|
180
|
-
|
|
180
|
+
/*
|
|
181
|
+
* **`less`, not the frame's `DEPTH_COMPARE`, because a shadow map is not reversed.**
|
|
182
|
+
* `depthConvention.ts` says so in as many words: the cascades are orthographic, where depth
|
|
183
|
+
* is already linear and a float buffer gains nothing, so `SHADOW_DEPTH_CLEAR` is 1 and
|
|
184
|
+
* `beginShadowPass` clears to 1. This pipeline took the scene's compare, which under
|
|
185
|
+
* `REVERSED_DEPTH` is `greater` — and a fragment at depth 0.3 tested with `greater` against a
|
|
186
|
+
* map cleared to 1 fails. **Nothing was ever written**, so a scatter batch cast no shadow at
|
|
187
|
+
* all on this backend: no error, no warning, an ordinary-looking frame.
|
|
188
|
+
*
|
|
189
|
+
* `depthPass.ts` has had `'less'` for the mesh path all along, which is why meshes cast and
|
|
190
|
+
* scatter did not, and why it took a scene whose only caster is a scatter batch to show it.
|
|
191
|
+
* `demo/windField.ts` is that scene — turning directional shadows off there changed 2,628
|
|
192
|
+
* pixels on WebGL2 and 0 on WebGPU.
|
|
193
|
+
*/
|
|
194
|
+
depthStencil: { format: shadowFormat, depthWriteEnabled: true, depthCompare: 'less' },
|
|
181
195
|
}));
|
|
182
196
|
}
|
|
183
197
|
/**
|
|
@@ -34,23 +34,33 @@ export declare function generateMipChain(device: GPUDevice, pipelines: MipPipeli
|
|
|
34
34
|
export declare class GpuSurfaceTexture {
|
|
35
35
|
private readonly device;
|
|
36
36
|
private readonly pipelines;
|
|
37
|
-
readonly view: GPUTextureView;
|
|
38
37
|
readonly sampler: GPUSampler;
|
|
39
38
|
private texture;
|
|
39
|
+
private current;
|
|
40
40
|
private readonly format;
|
|
41
41
|
private readonly mipmapped;
|
|
42
|
-
private
|
|
43
|
-
private
|
|
44
|
-
private
|
|
42
|
+
private levels;
|
|
43
|
+
private width;
|
|
44
|
+
private height;
|
|
45
45
|
constructor(device: GPUDevice, pipelines: PipelineCache, source: TexImageSource, options?: SurfaceTextureOptions);
|
|
46
|
+
/** The view every binding of this image holds. A new one when `update` changes the size. */
|
|
47
|
+
get view(): GPUTextureView;
|
|
46
48
|
/**
|
|
47
|
-
* Replace the pixels, keeping the
|
|
49
|
+
* Replace the pixels, keeping the handle and its sampler.
|
|
48
50
|
*
|
|
49
|
-
*
|
|
50
|
-
* rebuild — the same argument `surfaceTexture.ts`
|
|
51
|
-
* whole image and rebuilds the chain.
|
|
51
|
+
* **At the same size, the texture and its view are kept**, so a binding a draw loop already holds
|
|
52
|
+
* stays valid and the swap is a swap rather than a rebuild — the same argument `surfaceTexture.ts`
|
|
53
|
+
* makes. Not a hot path: it re-uploads the whole image and rebuilds the chain.
|
|
54
|
+
*
|
|
55
|
+
* **At another size, a texture of that size replaces it**, which is what WebGL2's `texImage2D`
|
|
56
|
+
* does by itself. This used to keep the old size and copy that much of the new image — its
|
|
57
|
+
* top-left corner, stretched — and a model loader updating a small preview with the real image
|
|
58
|
+
* hit that whenever the preview's decode won the race. The view is new then, and the texture it
|
|
59
|
+
* replaced is handed back rather than destroyed: a draw recorded earlier in this frame may still
|
|
60
|
+
* read it, so the renderer destroys it once nothing can, and drops every binding of the old view.
|
|
52
61
|
*/
|
|
53
|
-
update(source: TexImageSource):
|
|
62
|
+
update(source: TexImageSource): GPUTexture | null;
|
|
63
|
+
private allocate;
|
|
54
64
|
dispose(): void;
|
|
55
65
|
private upload;
|
|
56
66
|
/**
|
|
@@ -127,9 +127,9 @@ function sourceSize(source) {
|
|
|
127
127
|
export class GpuSurfaceTexture {
|
|
128
128
|
device;
|
|
129
129
|
pipelines;
|
|
130
|
-
view;
|
|
131
130
|
sampler;
|
|
132
131
|
texture;
|
|
132
|
+
current;
|
|
133
133
|
format;
|
|
134
134
|
mipmapped;
|
|
135
135
|
levels;
|
|
@@ -139,35 +139,15 @@ export class GpuSurfaceTexture {
|
|
|
139
139
|
this.device = device;
|
|
140
140
|
this.pipelines = pipelines;
|
|
141
141
|
const { width, height } = sourceSize(source);
|
|
142
|
-
this.width = width;
|
|
143
|
-
this.height = height;
|
|
144
142
|
this.mipmapped = options.mipmap ?? true;
|
|
145
|
-
this.levels = this.mipmapped ? mipLevelCount(width, height) : 1;
|
|
146
143
|
/* `SRGB8_ALPHA8`'s equivalent. Decoded in the sampler, before filtering, which is the only
|
|
147
144
|
place it is correct — `surfaceTexture.ts` makes the argument in full. */
|
|
148
145
|
this.format = (options.colorSpace ?? 'linear') === 'srgb' ? 'rgba8unorm-srgb' : 'rgba8unorm';
|
|
149
|
-
this.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
/*
|
|
155
|
-
* **`RENDER_ATTACHMENT` is required by `copyExternalImageToTexture`, not by the mip
|
|
156
|
-
* chain**, and the difference matters enough to state.
|
|
157
|
-
*
|
|
158
|
-
* This used to say the flag was there "because building the chain renders into levels 1
|
|
159
|
-
* and below", which is true and is not the reason. The upload itself demands it: Dawn
|
|
160
|
-
* rejects a destination without `CopyDst | RenderAttachment` outright, whatever its mip
|
|
161
|
-
* count. Gating it on `levels > 1` therefore breaks every unmipped texture, and the read
|
|
162
|
-
* of the old comment that led there cost a broken build to disprove.
|
|
163
|
-
*
|
|
164
|
-
* So it cannot be dropped to make a sampled-only allocation, and anything that wants one
|
|
165
|
-
* has to leave `copyExternalImageToTexture` behind and write raw pixels instead, which
|
|
166
|
-
* trades a driver cost for a CPU readback.
|
|
167
|
-
*/
|
|
168
|
-
usage: 0x2 | 0x4 | 0x10, // COPY_DST | TEXTURE_BINDING | RENDER_ATTACHMENT
|
|
169
|
-
});
|
|
170
|
-
this.view = this.texture.createView();
|
|
146
|
+
this.width = width;
|
|
147
|
+
this.height = height;
|
|
148
|
+
this.levels = this.mipmapped ? mipLevelCount(width, height) : 1;
|
|
149
|
+
this.texture = this.allocate();
|
|
150
|
+
this.current = this.texture.createView();
|
|
171
151
|
const wrap = (options.wrap ?? 'repeat') === 'repeat' ? 'repeat' : 'clamp-to-edge';
|
|
172
152
|
/*
|
|
173
153
|
* Nearest where the caller said their pixels are the subject. `mipmapFilter` stays as it was:
|
|
@@ -200,17 +180,63 @@ export class GpuSurfaceTexture {
|
|
|
200
180
|
});
|
|
201
181
|
this.upload(source);
|
|
202
182
|
}
|
|
183
|
+
/** The view every binding of this image holds. A new one when `update` changes the size. */
|
|
184
|
+
get view() {
|
|
185
|
+
return this.current;
|
|
186
|
+
}
|
|
203
187
|
/**
|
|
204
|
-
* Replace the pixels, keeping the
|
|
188
|
+
* Replace the pixels, keeping the handle and its sampler.
|
|
189
|
+
*
|
|
190
|
+
* **At the same size, the texture and its view are kept**, so a binding a draw loop already holds
|
|
191
|
+
* stays valid and the swap is a swap rather than a rebuild — the same argument `surfaceTexture.ts`
|
|
192
|
+
* makes. Not a hot path: it re-uploads the whole image and rebuilds the chain.
|
|
205
193
|
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
194
|
+
* **At another size, a texture of that size replaces it**, which is what WebGL2's `texImage2D`
|
|
195
|
+
* does by itself. This used to keep the old size and copy that much of the new image — its
|
|
196
|
+
* top-left corner, stretched — and a model loader updating a small preview with the real image
|
|
197
|
+
* hit that whenever the preview's decode won the race. The view is new then, and the texture it
|
|
198
|
+
* replaced is handed back rather than destroyed: a draw recorded earlier in this frame may still
|
|
199
|
+
* read it, so the renderer destroys it once nothing can, and drops every binding of the old view.
|
|
209
200
|
*/
|
|
210
201
|
update(source) {
|
|
211
|
-
|
|
212
|
-
|
|
202
|
+
const replaced = this.texture;
|
|
203
|
+
if (replaced === null)
|
|
204
|
+
return null;
|
|
205
|
+
const { width, height } = sourceSize(source);
|
|
206
|
+
if (width === this.width && height === this.height) {
|
|
207
|
+
this.upload(source);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
this.width = width;
|
|
211
|
+
this.height = height;
|
|
212
|
+
this.levels = this.mipmapped ? mipLevelCount(width, height) : 1;
|
|
213
|
+
this.texture = this.allocate();
|
|
214
|
+
this.current = this.texture.createView();
|
|
213
215
|
this.upload(source);
|
|
216
|
+
return replaced;
|
|
217
|
+
}
|
|
218
|
+
allocate() {
|
|
219
|
+
return this.device.createTexture({
|
|
220
|
+
label: 'surface.texture',
|
|
221
|
+
size: [this.width, this.height],
|
|
222
|
+
format: this.format,
|
|
223
|
+
mipLevelCount: this.levels,
|
|
224
|
+
/*
|
|
225
|
+
* **`RENDER_ATTACHMENT` is required by `copyExternalImageToTexture`, not by the mip
|
|
226
|
+
* chain**, and the difference matters enough to state.
|
|
227
|
+
*
|
|
228
|
+
* This used to say the flag was there "because building the chain renders into levels 1
|
|
229
|
+
* and below", which is true and is not the reason. The upload itself demands it: Dawn
|
|
230
|
+
* rejects a destination without `CopyDst | RenderAttachment` outright, whatever its mip
|
|
231
|
+
* count. Gating it on `levels > 1` therefore breaks every unmipped texture, and the read
|
|
232
|
+
* of the old comment that led there cost a broken build to disprove.
|
|
233
|
+
*
|
|
234
|
+
* So it cannot be dropped to make a sampled-only allocation, and anything that wants one
|
|
235
|
+
* has to leave `copyExternalImageToTexture` behind and write raw pixels instead, which
|
|
236
|
+
* trades a driver cost for a CPU readback.
|
|
237
|
+
*/
|
|
238
|
+
usage: 0x2 | 0x4 | 0x10, // COPY_DST | TEXTURE_BINDING | RENDER_ATTACHMENT
|
|
239
|
+
});
|
|
214
240
|
}
|
|
215
241
|
dispose() {
|
|
216
242
|
if (this.texture === null)
|
|
@@ -57,7 +57,9 @@ export declare class CausticsRenderer {
|
|
|
57
57
|
* pass: this is light arriving at a surface, not the surface.
|
|
58
58
|
*
|
|
59
59
|
* @param strength scales the whole effect; 1 is the calibrated default.
|
|
60
|
+
*
|
|
61
|
+
* Answers whether it drew, which the renderer counts on its frame budget.
|
|
60
62
|
*/
|
|
61
|
-
draw(gl: WebGL2RenderingContext, camera: FrameView, timeSeconds: number, directionalDir: Vec3, directionalColor: Vec3, ambient: Vec3, atmosphere: Atmosphere, underwaterEnabled: boolean, windX?: number, windZ?: number, strength?: number):
|
|
63
|
+
draw(gl: WebGL2RenderingContext, camera: FrameView, timeSeconds: number, directionalDir: Vec3, directionalColor: Vec3, ambient: Vec3, atmosphere: Atmosphere, underwaterEnabled: boolean, windX?: number, windZ?: number, strength?: number): boolean;
|
|
62
64
|
dispose(gl: WebGL2RenderingContext): void;
|
|
63
65
|
}
|
|
@@ -62,10 +62,12 @@ export class CausticsRenderer {
|
|
|
62
62
|
* pass: this is light arriving at a surface, not the surface.
|
|
63
63
|
*
|
|
64
64
|
* @param strength scales the whole effect; 1 is the calibrated default.
|
|
65
|
+
*
|
|
66
|
+
* Answers whether it drew, which the renderer counts on its frame budget.
|
|
65
67
|
*/
|
|
66
68
|
draw(gl, camera, timeSeconds, directionalDir, directionalColor, ambient, atmosphere, underwaterEnabled, windX = 0, windZ = 0, strength = 1) {
|
|
67
69
|
if (this.vertexCount === 0)
|
|
68
|
-
return;
|
|
70
|
+
return false;
|
|
69
71
|
const u = this.uniforms;
|
|
70
72
|
gl.useProgram(this.program);
|
|
71
73
|
gl.uniformMatrix4fv(u['uViewProj'] ?? null, false, camera.viewProjection);
|
|
@@ -109,6 +111,7 @@ export class CausticsRenderer {
|
|
|
109
111
|
gl.enable(gl.CULL_FACE);
|
|
110
112
|
gl.depthMask(true);
|
|
111
113
|
gl.disable(gl.BLEND);
|
|
114
|
+
return true;
|
|
112
115
|
}
|
|
113
116
|
dispose(gl) {
|
|
114
117
|
for (const buffer of this.buffers)
|
|
@@ -139,6 +139,18 @@ export declare function conventionalDepth(stored: number): number;
|
|
|
139
139
|
* `%s` is the name of the depth variable at the call site.
|
|
140
140
|
*/
|
|
141
141
|
export declare function glslSceneDepthToNdc(depth: string): string;
|
|
142
|
+
/**
|
|
143
|
+
* The same conversion as WGSL, for the hand-written compute shaders that have no GLSL to generate
|
|
144
|
+
* from.
|
|
145
|
+
*
|
|
146
|
+
* **It is the same text, and that is the point of it being here.** The reconstruction resolve picks
|
|
147
|
+
* the *nearest* of nine depths and dilates that texel's motion, which is the rule every temporal
|
|
148
|
+
* upscaler uses because an edge's motion belongs to the nearer of the two surfaces meeting there.
|
|
149
|
+
* Under a reversed buffer the nearest is the largest number, so a shader reading the attachment
|
|
150
|
+
* without this conversion picks the furthest and drags the background's motion over every
|
|
151
|
+
* silhouette — a defect that draws a plausible picture and moves the wrong way.
|
|
152
|
+
*/
|
|
153
|
+
export declare function wgslSceneDepthToNdc(depth: string): string;
|
|
142
154
|
/**
|
|
143
155
|
* The clip-space z of the far plane, as GLSL, for geometry that writes a fixed depth.
|
|
144
156
|
*
|
|
@@ -145,6 +145,20 @@ export function conventionalDepth(stored) {
|
|
|
145
145
|
export function glslSceneDepthToNdc(depth) {
|
|
146
146
|
return REVERSED_DEPTH ? `(1.0 - ${depth} * 2.0)` : `(${depth} * 2.0 - 1.0)`;
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* The same conversion as WGSL, for the hand-written compute shaders that have no GLSL to generate
|
|
150
|
+
* from.
|
|
151
|
+
*
|
|
152
|
+
* **It is the same text, and that is the point of it being here.** The reconstruction resolve picks
|
|
153
|
+
* the *nearest* of nine depths and dilates that texel's motion, which is the rule every temporal
|
|
154
|
+
* upscaler uses because an edge's motion belongs to the nearer of the two surfaces meeting there.
|
|
155
|
+
* Under a reversed buffer the nearest is the largest number, so a shader reading the attachment
|
|
156
|
+
* without this conversion picks the furthest and drags the background's motion over every
|
|
157
|
+
* silhouette — a defect that draws a plausible picture and moves the wrong way.
|
|
158
|
+
*/
|
|
159
|
+
export function wgslSceneDepthToNdc(depth) {
|
|
160
|
+
return REVERSED_DEPTH ? `(1.0 - ${depth} * 2.0)` : `(${depth} * 2.0 - 1.0)`;
|
|
161
|
+
}
|
|
148
162
|
/**
|
|
149
163
|
* The clip-space z of the far plane, as GLSL, for geometry that writes a fixed depth.
|
|
150
164
|
*
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** The subset of a device this needs. A real `GPUDevice` satisfies it. */
|
|
2
|
+
export interface DeviceShape {
|
|
3
|
+
readonly features: {
|
|
4
|
+
has(name: string): boolean;
|
|
5
|
+
};
|
|
6
|
+
readonly limits: Readonly<Record<string, number | undefined>>;
|
|
7
|
+
}
|
|
8
|
+
export interface GpuCapabilities {
|
|
9
|
+
/** Half-precision arithmetic in shaders. */
|
|
10
|
+
readonly shaderF16: boolean;
|
|
11
|
+
/** Subgroup operations, which make a compute reduction far cheaper where present. */
|
|
12
|
+
readonly subgroups: boolean;
|
|
13
|
+
/** Timestamp queries. Absent by default in more browsers than people expect. */
|
|
14
|
+
readonly timestampQuery: boolean;
|
|
15
|
+
/** Whether this backend can be driven from buffers the GPU wrote. See `indirectSupport`. */
|
|
16
|
+
readonly indirect: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The three compressed families, reported separately.
|
|
19
|
+
*
|
|
20
|
+
* **Separately, because a device may have exactly one.** Desktop parts carry block compression,
|
|
21
|
+
* mobile carries one of the other two, and a single `compressed: boolean` would be true on
|
|
22
|
+
* every device and useful on none.
|
|
23
|
+
*/
|
|
24
|
+
readonly compressedBc: boolean;
|
|
25
|
+
readonly compressedEtc2: boolean;
|
|
26
|
+
readonly compressedAstc: boolean;
|
|
27
|
+
/** Limits, with an absent one reported as 0 so arithmetic on it is safe. */
|
|
28
|
+
readonly maxStorageBufferBytes: number;
|
|
29
|
+
readonly maxComputeWorkgroupStorageBytes: number;
|
|
30
|
+
readonly maxComputeInvocationsPerWorkgroup: number;
|
|
31
|
+
}
|
|
32
|
+
export declare function probeCapabilities(device: DeviceShape, backend: 'webgl2' | 'webgpu'): GpuCapabilities;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What this device can do, asked once, in one place.
|
|
3
|
+
*
|
|
4
|
+
* **Not `gpuCapability.ts`, which answers a different question.** That module identifies the
|
|
5
|
+
* *part* — a renderer string, matched against families known to struggle — so that a consumer can
|
|
6
|
+
* choose a quality profile. This one reads the optional features and limits a device actually
|
|
7
|
+
* reports. A weak part may support half precision and a strong one may not; neither answer
|
|
8
|
+
* predicts the other.
|
|
9
|
+
*
|
|
10
|
+
* **One place, because the alternative is each subsystem asking the device itself.** Every
|
|
11
|
+
* capability below has more than one consumer waiting for it, and a feature string spelled two
|
|
12
|
+
* ways in two subsystems is a capability that is present in one and absent in the other on the
|
|
13
|
+
* same machine.
|
|
14
|
+
*
|
|
15
|
+
* **Takes the device's shape as a parameter rather than reaching for a global**, which is the
|
|
16
|
+
* rule `AGENTS.md` applies to persistence applied here — and is what lets every branch below be
|
|
17
|
+
* asserted without a graphics device.
|
|
18
|
+
*/
|
|
19
|
+
import { indirectSupport } from './backend/api.js';
|
|
20
|
+
function limit(device, name) {
|
|
21
|
+
/*
|
|
22
|
+
* Zero rather than undefined for a limit the device does not report. A consumer sizing a buffer
|
|
23
|
+
* against `undefined` gets NaN and allocates nothing while believing it allocated something;
|
|
24
|
+
* against 0 it gets a refusal it can see.
|
|
25
|
+
*/
|
|
26
|
+
return device.limits[name] ?? 0;
|
|
27
|
+
}
|
|
28
|
+
export function probeCapabilities(device, backend) {
|
|
29
|
+
return {
|
|
30
|
+
shaderF16: device.features.has('shader-f16'),
|
|
31
|
+
subgroups: device.features.has('subgroups'),
|
|
32
|
+
timestampQuery: device.features.has('timestamp-query'),
|
|
33
|
+
indirect: indirectSupport(backend),
|
|
34
|
+
compressedBc: device.features.has('texture-compression-bc'),
|
|
35
|
+
compressedEtc2: device.features.has('texture-compression-etc2'),
|
|
36
|
+
compressedAstc: device.features.has('texture-compression-astc'),
|
|
37
|
+
maxStorageBufferBytes: limit(device, 'maxStorageBufferBindingSize'),
|
|
38
|
+
maxComputeWorkgroupStorageBytes: limit(device, 'maxComputeWorkgroupStorageSize'),
|
|
39
|
+
maxComputeInvocationsPerWorkgroup: limit(device, 'maxComputeInvocationsPerWorkgroup'),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The medium between the camera and everything it can see, as arithmetic.
|
|
3
|
+
*
|
|
4
|
+
* **`shaders/fog.ts` was the only copy and it is GLSL**, which two things now make a problem. The
|
|
5
|
+
* GPU-driven pipeline is written in WGSL and cannot include it, so the second pipeline drew no fog
|
|
6
|
+
* at all; and a term that exists only as a shader is a term nothing can disagree with, which is the
|
|
7
|
+
* argument `gpudriven/lit.ts` and `gpudriven/alphaTest.ts` were both written under.
|
|
8
|
+
*
|
|
9
|
+
* So this is the expression in TypeScript, and both shaders are checked against it:
|
|
10
|
+
* `scripts/gpu-parity.mjs` runs the WGSL over generated cases, and `shaders/fog.ts` stays exactly
|
|
11
|
+
* as it is. **This is a second reader of one decision rather than a second decision** — every
|
|
12
|
+
* guard, every constant and the order of the two mixes are the GLSL's, and where a line looks odd
|
|
13
|
+
* that file's header is the long version of why.
|
|
14
|
+
*
|
|
15
|
+
* **What it does not carry** is the uniform block. `shaders/fog.ts` declares ten uniforms because a
|
|
16
|
+
* block that declares everything it uses can be pasted into any program; a function takes them as
|
|
17
|
+
* one argument instead, and the backends keep their own packing.
|
|
18
|
+
*/
|
|
19
|
+
import { type Atmosphere } from './atmosphere.ts';
|
|
20
|
+
/** A colour as three numbers, matching `render/orderIndependent.ts`'s own `Rgb`. */
|
|
21
|
+
export type Rgb = readonly [number, number, number];
|
|
22
|
+
/** Beer-Lambert through a haze layer that thins with height. `uFogMode` 0. */
|
|
23
|
+
export declare const MEDIUM_FOG = 0;
|
|
24
|
+
/** A ramp between two distances, which is a look rather than a medium. `uFogMode` 1. */
|
|
25
|
+
export declare const LINEAR_FOG = 1;
|
|
26
|
+
export interface FogOptions {
|
|
27
|
+
readonly colour: Rgb;
|
|
28
|
+
/** Extinction per metre **at the camera's own height**, not at the base. */
|
|
29
|
+
readonly density: number;
|
|
30
|
+
/** Reciprocal scale height of the haze, per metre. 0 is a uniform medium. */
|
|
31
|
+
readonly heightFalloff: number;
|
|
32
|
+
/** The camera's height, so a fragment's own height is one subtraction away. */
|
|
33
|
+
readonly eyeY: number;
|
|
34
|
+
readonly underwaterColour: Rgb;
|
|
35
|
+
readonly underwaterDensity: number;
|
|
36
|
+
readonly underwaterFactor: number;
|
|
37
|
+
/** `MEDIUM_FOG` integrates a medium; `LINEAR_FOG` ramps between near and far. */
|
|
38
|
+
readonly mode: number;
|
|
39
|
+
/** Linear mode only: where haze begins, and where it is total. */
|
|
40
|
+
readonly near: number;
|
|
41
|
+
readonly far: number;
|
|
42
|
+
}
|
|
43
|
+
/** What distance converges toward: the day's haze, or the water column. */
|
|
44
|
+
export declare function mediumColour(fog: FogOptions): [number, number, number];
|
|
45
|
+
/**
|
|
46
|
+
* How much of a surface standing at `pointY`, `dist` metres away, is lost to the medium between.
|
|
47
|
+
* 0 is perfectly clear, 1 is the medium's own colour.
|
|
48
|
+
*/
|
|
49
|
+
export declare function mediumFog(fog: FogOptions, dist: number, pointY: number): number;
|
|
50
|
+
/** `FogOptions` with its fields writable, which is what a caller holds and refills a frame at a time. */
|
|
51
|
+
export interface FogTarget {
|
|
52
|
+
colour: [number, number, number];
|
|
53
|
+
density: number;
|
|
54
|
+
heightFalloff: number;
|
|
55
|
+
eyeY: number;
|
|
56
|
+
underwaterColour: [number, number, number];
|
|
57
|
+
underwaterDensity: number;
|
|
58
|
+
underwaterFactor: number;
|
|
59
|
+
mode: number;
|
|
60
|
+
near: number;
|
|
61
|
+
far: number;
|
|
62
|
+
}
|
|
63
|
+
/** A target for `atmosphereFog`, allocated once, outside the frame loop. */
|
|
64
|
+
export declare function createFogTarget(): FogTarget;
|
|
65
|
+
/**
|
|
66
|
+
* The haze an `Environment` makes at a camera's height, as `GpuDrivenView.fog` takes it.
|
|
67
|
+
*
|
|
68
|
+
* **The forward path's own decision, handed over rather than made again.** A consumer drawing
|
|
69
|
+
* both pipelines in one frame has one environment and needs one haze from it, and
|
|
70
|
+
* `resolveAtmosphere` is where the forward path decides which medium the camera is in and what it
|
|
71
|
+
* looks like — the density at the eye, the linear ramp's two radii, how far under water the eye
|
|
72
|
+
* is. This calls it and copies the answer across field for field, so the two pipelines cannot
|
|
73
|
+
* disagree about where the fog begins or whether the camera is submerged. Both mode numbers are
|
|
74
|
+
* the shaders' `uFogMode`, which is why no mapping stands between them.
|
|
75
|
+
*
|
|
76
|
+
* @param underwater The renderer's `underwaterAtmosphere` quality, which gates the second medium
|
|
77
|
+
* on the forward path; pass the same.
|
|
78
|
+
*/
|
|
79
|
+
export declare function atmosphereFog(atmosphere: Atmosphere, eyeY: number, underwater: boolean, out: FogTarget): FogTarget;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The medium between the camera and everything it can see, as arithmetic.
|
|
3
|
+
*
|
|
4
|
+
* **`shaders/fog.ts` was the only copy and it is GLSL**, which two things now make a problem. The
|
|
5
|
+
* GPU-driven pipeline is written in WGSL and cannot include it, so the second pipeline drew no fog
|
|
6
|
+
* at all; and a term that exists only as a shader is a term nothing can disagree with, which is the
|
|
7
|
+
* argument `gpudriven/lit.ts` and `gpudriven/alphaTest.ts` were both written under.
|
|
8
|
+
*
|
|
9
|
+
* So this is the expression in TypeScript, and both shaders are checked against it:
|
|
10
|
+
* `scripts/gpu-parity.mjs` runs the WGSL over generated cases, and `shaders/fog.ts` stays exactly
|
|
11
|
+
* as it is. **This is a second reader of one decision rather than a second decision** — every
|
|
12
|
+
* guard, every constant and the order of the two mixes are the GLSL's, and where a line looks odd
|
|
13
|
+
* that file's header is the long version of why.
|
|
14
|
+
*
|
|
15
|
+
* **What it does not carry** is the uniform block. `shaders/fog.ts` declares ten uniforms because a
|
|
16
|
+
* block that declares everything it uses can be pasted into any program; a function takes them as
|
|
17
|
+
* one argument instead, and the backends keep their own packing.
|
|
18
|
+
*/
|
|
19
|
+
import { resolveAtmosphere } from './atmosphere.js';
|
|
20
|
+
/** Beer-Lambert through a haze layer that thins with height. `uFogMode` 0. */
|
|
21
|
+
export const MEDIUM_FOG = 0;
|
|
22
|
+
/** A ramp between two distances, which is a look rather than a medium. `uFogMode` 1. */
|
|
23
|
+
export const LINEAR_FOG = 1;
|
|
24
|
+
/**
|
|
25
|
+
* Linear interpolation **written the way `mix` is defined**, which is not the same arithmetic as
|
|
26
|
+
* the fused form.
|
|
27
|
+
*
|
|
28
|
+
* GLSL and WGSL both define `mix(x, y, a)` as `x * (1 - a) + y * a`. Written `x + (y - x) * a` the
|
|
29
|
+
* two agree to within rounding everywhere except the endpoints, where the fused form is off by an
|
|
30
|
+
* ulp — `0.82 + (0.2 - 0.82) * 1` is 0.20000000000000007 rather than 0.2. That is invisible in a
|
|
31
|
+
* picture and fatal in a parity check, and it is what the first run of this file's tests found: the
|
|
32
|
+
* reference and the device have to be the same *expression*, not the same function.
|
|
33
|
+
*/
|
|
34
|
+
function mix(x, y, a) {
|
|
35
|
+
return x * (1 - a) + y * a;
|
|
36
|
+
}
|
|
37
|
+
/** What distance converges toward: the day's haze, or the water column. */
|
|
38
|
+
export function mediumColour(fog) {
|
|
39
|
+
const t = fog.underwaterFactor;
|
|
40
|
+
return [
|
|
41
|
+
mix(fog.colour[0], fog.underwaterColour[0], t),
|
|
42
|
+
mix(fog.colour[1], fog.underwaterColour[1], t),
|
|
43
|
+
mix(fog.colour[2], fog.underwaterColour[2], t),
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* How much of a surface standing at `pointY`, `dist` metres away, is lost to the medium between.
|
|
48
|
+
* 0 is perfectly clear, 1 is the medium's own colour.
|
|
49
|
+
*/
|
|
50
|
+
export function mediumFog(fog, dist, pointY) {
|
|
51
|
+
/*
|
|
52
|
+
* **A depth ramp rather than a medium, for worlds authored against one.** Before `near` the air
|
|
53
|
+
* is perfectly clear, which Beer-Lambert can never be, and height falloff has no meaning here
|
|
54
|
+
* because a ramp is defined on distance alone. `shaders/fog.ts` argues the whole of it.
|
|
55
|
+
*/
|
|
56
|
+
if (fog.mode === LINEAR_FOG) {
|
|
57
|
+
/* A consumer asking for a hard cut writes `far === near`; the guard is why that is a cut. */
|
|
58
|
+
const span = Math.max(fog.far - fog.near, 1e-4);
|
|
59
|
+
const ramp = Math.min(1, Math.max(0, (dist - fog.near) / span));
|
|
60
|
+
if (fog.underwaterFactor <= 0)
|
|
61
|
+
return ramp;
|
|
62
|
+
const wetLinear = fog.underwaterDensity * dist;
|
|
63
|
+
return mix(ramp, 1 - Math.pow(2, -wetLinear * wetLinear * 1.442695), fog.underwaterFactor);
|
|
64
|
+
}
|
|
65
|
+
const t = (pointY - fog.eyeY) * fog.heightFalloff;
|
|
66
|
+
/*
|
|
67
|
+
* `(1 - e^-t)/t`, which tends to 1 as the ray levels out. **Guarded rather than branched**: at
|
|
68
|
+
* `t = 0` the quotient is 0/0, and 1e-4 of a scale height is 8 mm.
|
|
69
|
+
*/
|
|
70
|
+
const denom = Math.abs(t) < 1e-4 ? 1e-4 : t;
|
|
71
|
+
const air = 1 - Math.exp((-fog.density * dist * (1 - Math.exp(-denom))) / denom);
|
|
72
|
+
if (fog.underwaterFactor <= 0)
|
|
73
|
+
return air;
|
|
74
|
+
/* Water is its own medium and keeps its own curve, so crossing the surface changes nothing
|
|
75
|
+
about how the water column reads. */
|
|
76
|
+
const wet = fog.underwaterDensity * dist;
|
|
77
|
+
return mix(air, 1 - Math.pow(2, -wet * wet * 1.442695), fog.underwaterFactor);
|
|
78
|
+
}
|
|
79
|
+
/** A target for `atmosphereFog`, allocated once, outside the frame loop. */
|
|
80
|
+
export function createFogTarget() {
|
|
81
|
+
return {
|
|
82
|
+
colour: [0, 0, 0],
|
|
83
|
+
density: 0,
|
|
84
|
+
heightFalloff: 0,
|
|
85
|
+
eyeY: 0,
|
|
86
|
+
underwaterColour: [0, 0, 0],
|
|
87
|
+
underwaterDensity: 0,
|
|
88
|
+
underwaterFactor: 0,
|
|
89
|
+
mode: MEDIUM_FOG,
|
|
90
|
+
near: 0,
|
|
91
|
+
far: 1,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** `resolveAtmosphere`'s own target, reused, because this runs a frame at a time. */
|
|
95
|
+
const RESOLVED = {
|
|
96
|
+
fogColor: new Float32Array(3),
|
|
97
|
+
underwaterColor: new Float32Array(3),
|
|
98
|
+
fogDensity: 0,
|
|
99
|
+
fogHeightFalloff: 0,
|
|
100
|
+
fogEyeY: 0,
|
|
101
|
+
fogMode: 0,
|
|
102
|
+
fogNear: 0,
|
|
103
|
+
fogFar: 1,
|
|
104
|
+
underwaterFogDensity: 0,
|
|
105
|
+
underwaterFactor: 0,
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* The haze an `Environment` makes at a camera's height, as `GpuDrivenView.fog` takes it.
|
|
109
|
+
*
|
|
110
|
+
* **The forward path's own decision, handed over rather than made again.** A consumer drawing
|
|
111
|
+
* both pipelines in one frame has one environment and needs one haze from it, and
|
|
112
|
+
* `resolveAtmosphere` is where the forward path decides which medium the camera is in and what it
|
|
113
|
+
* looks like — the density at the eye, the linear ramp's two radii, how far under water the eye
|
|
114
|
+
* is. This calls it and copies the answer across field for field, so the two pipelines cannot
|
|
115
|
+
* disagree about where the fog begins or whether the camera is submerged. Both mode numbers are
|
|
116
|
+
* the shaders' `uFogMode`, which is why no mapping stands between them.
|
|
117
|
+
*
|
|
118
|
+
* @param underwater The renderer's `underwaterAtmosphere` quality, which gates the second medium
|
|
119
|
+
* on the forward path; pass the same.
|
|
120
|
+
*/
|
|
121
|
+
export function atmosphereFog(atmosphere, eyeY, underwater, out) {
|
|
122
|
+
const medium = resolveAtmosphere(atmosphere, eyeY, underwater, RESOLVED);
|
|
123
|
+
out.colour[0] = medium.fogColor[0];
|
|
124
|
+
out.colour[1] = medium.fogColor[1];
|
|
125
|
+
out.colour[2] = medium.fogColor[2];
|
|
126
|
+
out.density = medium.fogDensity;
|
|
127
|
+
out.heightFalloff = medium.fogHeightFalloff;
|
|
128
|
+
out.eyeY = medium.fogEyeY;
|
|
129
|
+
out.underwaterColour[0] = medium.underwaterColor[0];
|
|
130
|
+
out.underwaterColour[1] = medium.underwaterColor[1];
|
|
131
|
+
out.underwaterColour[2] = medium.underwaterColor[2];
|
|
132
|
+
out.underwaterDensity = medium.underwaterFogDensity;
|
|
133
|
+
out.underwaterFactor = medium.underwaterFactor;
|
|
134
|
+
out.mode = medium.fogMode;
|
|
135
|
+
out.near = medium.fogNear;
|
|
136
|
+
out.far = medium.fogFar;
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where each transient resource lives in the frame's arena, so that two which are never alive
|
|
3
|
+
* together share the memory.
|
|
4
|
+
*
|
|
5
|
+
* **First fit over slots, in declaration order.** Not best fit and not a bin-packing search:
|
|
6
|
+
* the input is tens of resources whose order is already meaningful, the packing runs once per
|
|
7
|
+
* frame composition rather than per frame, and a first-fit result is stable — which matters far
|
|
8
|
+
* more than a few saved bytes, because an allocator whose output moves between frames moves
|
|
9
|
+
* every binding with it.
|
|
10
|
+
*
|
|
11
|
+
* A slot records the widest resource it has held and the node at which its current occupant
|
|
12
|
+
* dies. A later resource may take the slot when it is born strictly after that node.
|
|
13
|
+
*/
|
|
14
|
+
import type { Lifetimes } from './lifetime.ts';
|
|
15
|
+
import type { VirtualTable } from './virtual.ts';
|
|
16
|
+
export interface AliasPlan {
|
|
17
|
+
/** Byte offset into the arena per identifier, or -1 when nothing writes it. */
|
|
18
|
+
offsets: Int32Array;
|
|
19
|
+
/** Byte offset of each slot, parallel to `slotEnd` and `slotSize`. */
|
|
20
|
+
slotAt: Int32Array;
|
|
21
|
+
/** Node at which each slot's current occupant is last read. */
|
|
22
|
+
slotEnd: Int32Array;
|
|
23
|
+
/** Widest resource each slot has held. */
|
|
24
|
+
slotSize: Int32Array;
|
|
25
|
+
/** How many slots are in use. */
|
|
26
|
+
slots: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function createAliasPlan(capacity: number): AliasPlan;
|
|
29
|
+
export declare function planAliases(table: VirtualTable, lifetimes: Lifetimes, out: AliasPlan): number;
|
|
30
|
+
export declare function offsetOf(plan: AliasPlan, id: number): number;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { firstWrite, lastRead } from './lifetime.js';
|
|
2
|
+
import { virtualBytes } from './virtual.js';
|
|
3
|
+
export function createAliasPlan(capacity) {
|
|
4
|
+
return {
|
|
5
|
+
offsets: new Int32Array(capacity),
|
|
6
|
+
slotAt: new Int32Array(capacity),
|
|
7
|
+
slotEnd: new Int32Array(capacity),
|
|
8
|
+
slotSize: new Int32Array(capacity),
|
|
9
|
+
slots: 0,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** Alignment every slot starts on. 256 is the widest uniform-buffer alignment WebGPU requires. */
|
|
13
|
+
const ALIGN = 256;
|
|
14
|
+
function aligned(bytes) {
|
|
15
|
+
return Math.ceil(bytes / ALIGN) * ALIGN;
|
|
16
|
+
}
|
|
17
|
+
export function planAliases(table, lifetimes, out) {
|
|
18
|
+
out.offsets.fill(-1);
|
|
19
|
+
out.slots = 0;
|
|
20
|
+
let total = 0;
|
|
21
|
+
for (let id = 0; id < table.count; id += 1) {
|
|
22
|
+
const born = firstWrite(lifetimes, id);
|
|
23
|
+
if (born === -1)
|
|
24
|
+
continue;
|
|
25
|
+
const dies = lastRead(lifetimes, id);
|
|
26
|
+
const size = aligned(virtualBytes(table, id));
|
|
27
|
+
let placed = -1;
|
|
28
|
+
for (let slot = 0; slot < out.slots; slot += 1) {
|
|
29
|
+
if ((out.slotEnd[slot] ?? 0) < born) {
|
|
30
|
+
placed = slot;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (placed === -1) {
|
|
35
|
+
placed = out.slots;
|
|
36
|
+
out.slotAt[placed] = total;
|
|
37
|
+
out.slotSize[placed] = size;
|
|
38
|
+
total += size;
|
|
39
|
+
out.slots = placed + 1;
|
|
40
|
+
}
|
|
41
|
+
else if (size > (out.slotSize[placed] ?? 0)) {
|
|
42
|
+
/*
|
|
43
|
+
* The slot has to widen, and widening in place would overlap whatever follows it. Retiring
|
|
44
|
+
* the slot and opening a new one is correct and costs one wasted slot; compacting would
|
|
45
|
+
* move every offset after it, which is the stability the header refuses to give up.
|
|
46
|
+
*/
|
|
47
|
+
out.slotEnd[placed] = Number.MAX_SAFE_INTEGER;
|
|
48
|
+
placed = out.slots;
|
|
49
|
+
out.slotAt[placed] = total;
|
|
50
|
+
out.slotSize[placed] = size;
|
|
51
|
+
total += size;
|
|
52
|
+
out.slots = placed + 1;
|
|
53
|
+
}
|
|
54
|
+
out.offsets[id] = out.slotAt[placed] ?? 0;
|
|
55
|
+
out.slotEnd[placed] = dies;
|
|
56
|
+
}
|
|
57
|
+
return total;
|
|
58
|
+
}
|
|
59
|
+
export function offsetOf(plan, id) {
|
|
60
|
+
return plan.offsets[id] ?? -1;
|
|
61
|
+
}
|