@driftengine/core 3.63.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +111 -0
- package/NOTICE +20 -0
- package/README.md +1 -1
- package/dist/core/loop.js +18 -2
- package/dist/core/storage.js +5 -0
- package/dist/geometry/meshBuilder.d.ts +1 -1
- package/dist/geometry/meshBuilder.js +6 -2
- package/dist/index.d.ts +69 -4
- package/dist/index.js +112 -2
- package/dist/input/input.js +6 -1
- package/dist/math/exact.d.ts +13 -3
- package/dist/math/exact.js +68 -3
- package/dist/math/frustum.d.ts +17 -2
- package/dist/math/frustum.js +24 -0
- package/dist/render/backend/api.d.ts +34 -1
- package/dist/render/backend/api.js +22 -1
- package/dist/render/backend/budget.d.ts +7 -6
- package/dist/render/backend/budget.js +7 -6
- package/dist/render/backend/createRenderer.d.ts +57 -0
- package/dist/render/backend/createRenderer.js +51 -2
- package/dist/render/backend/materialChanges.d.ts +41 -0
- package/dist/render/backend/materialChanges.js +38 -0
- package/dist/render/backend/select.js +8 -0
- package/dist/render/backend/webgl2/renderer.d.ts +80 -1
- package/dist/render/backend/webgl2/renderer.js +208 -10
- package/dist/render/backend/webgpu/buffers.d.ts +20 -1
- package/dist/render/backend/webgpu/buffers.js +116 -58
- package/dist/render/backend/webgpu/fieldCompose.d.ts +156 -0
- package/dist/render/backend/webgpu/fieldCompose.js +396 -0
- package/dist/render/backend/webgpu/giFieldPass.d.ts +66 -0
- package/dist/render/backend/webgpu/giFieldPass.js +137 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.d.ts +728 -0
- package/dist/render/backend/webgpu/gpuDrivenPass.js +2473 -0
- package/dist/render/backend/webgpu/linePass.d.ts +4 -14
- package/dist/render/backend/webgpu/linePass.js +4 -33
- package/dist/render/backend/webgpu/probeBake.d.ts +148 -0
- package/dist/render/backend/webgpu/probeBake.js +493 -0
- package/dist/render/backend/webgpu/renderer.d.ts +379 -24
- package/dist/render/backend/webgpu/renderer.js +1369 -76
- package/dist/render/backend/webgpu/scatterPass.js +15 -1
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +19 -9
- package/dist/render/backend/webgpu/surfaceTexturePass.js +58 -32
- package/dist/render/causticsRenderer.d.ts +3 -1
- package/dist/render/causticsRenderer.js +4 -1
- package/dist/render/depthConvention.d.ts +12 -0
- package/dist/render/depthConvention.js +14 -0
- package/dist/render/deviceFeatures.d.ts +32 -0
- package/dist/render/deviceFeatures.js +41 -0
- package/dist/render/fog.d.ts +79 -0
- package/dist/render/fog.js +138 -0
- package/dist/render/frame/alias.d.ts +30 -0
- package/dist/render/frame/alias.js +61 -0
- package/dist/render/frame/deps.d.ts +37 -0
- package/dist/render/frame/deps.js +122 -0
- package/dist/render/frame/flushGraph.d.ts +43 -0
- package/dist/render/frame/flushGraph.js +84 -0
- package/dist/render/frame/graphSchedule.d.ts +49 -0
- package/dist/render/frame/graphSchedule.js +120 -0
- package/dist/render/frame/index.d.ts +15 -0
- package/dist/render/frame/index.js +8 -0
- package/dist/render/frame/lifetime.d.ts +24 -0
- package/dist/render/frame/lifetime.js +36 -0
- package/dist/render/frame/passTimings.d.ts +42 -0
- package/dist/render/frame/passTimings.js +38 -0
- package/dist/render/frame/validate.d.ts +15 -0
- package/dist/render/frame/validate.js +22 -0
- package/dist/render/frame/virtual.d.ts +44 -0
- package/dist/render/frame/virtual.js +61 -0
- package/dist/render/gi/chain.d.ts +82 -0
- package/dist/render/gi/chain.js +135 -0
- package/dist/render/gi/denoise.d.ts +114 -0
- package/dist/render/gi/denoise.js +169 -0
- package/dist/render/gi/fieldScene.d.ts +125 -0
- package/dist/render/gi/fieldScene.js +219 -0
- package/dist/render/gi/globalField.d.ts +157 -0
- package/dist/render/gi/globalField.js +373 -0
- package/dist/render/gi/probeGridFit.d.ts +24 -0
- package/dist/render/gi/probeGridFit.js +71 -0
- package/dist/render/gi/probeTrace.d.ts +82 -0
- package/dist/render/gi/probeTrace.js +128 -0
- package/dist/render/gi/probeVolume.d.ts +136 -0
- package/dist/render/gi/probeVolume.js +191 -0
- package/dist/render/gi/reflection.d.ts +73 -0
- package/dist/render/gi/reflection.js +109 -0
- package/dist/render/gi/traceField.d.ts +91 -0
- package/dist/render/gi/traceField.js +129 -0
- package/dist/render/gi/traceScreen.d.ts +68 -0
- package/dist/render/gi/traceScreen.js +82 -0
- package/dist/render/gpuScene.d.ts +38 -0
- package/dist/render/gpuScene.js +41 -0
- package/dist/render/gpudriven/alphaTest.d.ts +28 -0
- package/dist/render/gpudriven/alphaTest.js +32 -0
- package/dist/render/gpudriven/clusterUpload.d.ts +59 -0
- package/dist/render/gpudriven/clusterUpload.js +75 -0
- package/dist/render/gpudriven/clusterWorld.d.ts +57 -0
- package/dist/render/gpudriven/clusterWorld.js +155 -0
- package/dist/render/gpudriven/compact.d.ts +51 -0
- package/dist/render/gpudriven/compact.js +68 -0
- package/dist/render/gpudriven/cullClusters.d.ts +129 -0
- package/dist/render/gpudriven/cullClusters.js +237 -0
- package/dist/render/gpudriven/decodeTables.d.ts +64 -0
- package/dist/render/gpudriven/decodeTables.js +219 -0
- package/dist/render/gpudriven/frustum.d.ts +25 -0
- package/dist/render/gpudriven/frustum.js +59 -0
- package/dist/render/gpudriven/geometryHarness.d.ts +15 -0
- package/dist/render/gpudriven/geometryHarness.js +31 -0
- package/dist/render/gpudriven/hzb.d.ts +60 -0
- package/dist/render/gpudriven/hzb.js +97 -0
- package/dist/render/gpudriven/ibl.d.ts +85 -0
- package/dist/render/gpudriven/ibl.js +129 -0
- package/dist/render/gpudriven/indirect.d.ts +67 -0
- package/dist/render/gpudriven/indirect.js +77 -0
- package/dist/render/gpudriven/instances.d.ts +43 -0
- package/dist/render/gpudriven/instances.js +172 -0
- package/dist/render/gpudriven/lit.d.ts +165 -0
- package/dist/render/gpudriven/lit.js +165 -0
- package/dist/render/gpudriven/lodCut.d.ts +28 -0
- package/dist/render/gpudriven/lodCut.js +38 -0
- package/dist/render/gpudriven/materialBin.d.ts +60 -0
- package/dist/render/gpudriven/materialBin.js +108 -0
- package/dist/render/gpudriven/materialTable.d.ts +88 -0
- package/dist/render/gpudriven/materialTable.js +115 -0
- package/dist/render/gpudriven/pipeline.d.ts +135 -0
- package/dist/render/gpudriven/pipeline.js +237 -0
- package/dist/render/gpudriven/rangeAlloc.d.ts +50 -0
- package/dist/render/gpudriven/rangeAlloc.js +112 -0
- package/dist/render/gpudriven/sceneUpload.d.ts +96 -0
- package/dist/render/gpudriven/sceneUpload.js +115 -0
- package/dist/render/gpudriven/shadeBins.d.ts +92 -0
- package/dist/render/gpudriven/shadeBins.js +182 -0
- package/dist/render/gpudriven/shadow.d.ts +136 -0
- package/dist/render/gpudriven/shadow.js +224 -0
- package/dist/render/gpudriven/shadowCamera.d.ts +87 -0
- package/dist/render/gpudriven/shadowCamera.js +345 -0
- package/dist/render/gpudriven/streamScene.d.ts +194 -0
- package/dist/render/gpudriven/streamScene.js +459 -0
- package/dist/render/gpudriven/surfaceFrame.d.ts +25 -0
- package/dist/render/gpudriven/surfaceFrame.js +51 -0
- package/dist/render/gpudriven/twoPhase.d.ts +122 -0
- package/dist/render/gpudriven/twoPhase.js +160 -0
- package/dist/render/gpudriven/visbuffer.d.ts +25 -0
- package/dist/render/gpudriven/visbuffer.js +37 -0
- package/dist/render/halfFloat.d.ts +10 -0
- package/dist/render/halfFloat.js +23 -4
- package/dist/render/inference/attentionTiled.d.ts +27 -0
- package/dist/render/inference/attentionTiled.js +117 -0
- package/dist/render/inference/denseKernels.d.ts +29 -0
- package/dist/render/inference/denseKernels.js +312 -0
- package/dist/render/inference/device.d.ts +21 -0
- package/dist/render/inference/device.js +32 -0
- package/dist/render/inference/deviceGraph.d.ts +34 -0
- package/dist/render/inference/deviceGraph.js +17 -0
- package/dist/render/inference/fuse.d.ts +15 -0
- package/dist/render/inference/fuse.js +37 -0
- package/dist/render/inference/kernelKit.d.ts +52 -0
- package/dist/render/inference/kernelKit.js +70 -0
- package/dist/render/inference/kernels.d.ts +3 -0
- package/dist/render/inference/kernels.js +11 -0
- package/dist/render/inference/plan.d.ts +13 -0
- package/dist/render/inference/plan.js +18 -0
- package/dist/render/inference/reuse.d.ts +39 -0
- package/dist/render/inference/reuse.js +65 -0
- package/dist/render/inference/runner.d.ts +31 -0
- package/dist/render/inference/runner.js +175 -0
- package/dist/render/inference/schedule.d.ts +39 -0
- package/dist/render/inference/schedule.js +59 -0
- package/dist/render/inference/shapeKernels.d.ts +10 -0
- package/dist/render/inference/shapeKernels.js +80 -0
- package/dist/render/inference/spatialKernels.d.ts +21 -0
- package/dist/render/inference/spatialKernels.js +179 -0
- package/dist/render/inference/splatRaster.wgsl.d.ts +75 -0
- package/dist/render/inference/splatRaster.wgsl.js +337 -0
- package/dist/render/inference/splatTiles.d.ts +79 -0
- package/dist/render/inference/splatTiles.js +161 -0
- package/dist/render/lineBatch.d.ts +2 -5
- package/dist/render/lineBatch.js +4 -24
- package/dist/render/mesh.d.ts +4 -0
- package/dist/render/mesh.js +45 -1
- package/dist/render/pass.d.ts +145 -8
- package/dist/render/prefilterEnvMap.d.ts +22 -0
- package/dist/render/prefilterEnvMap.js +31 -0
- package/dist/render/readback.d.ts +30 -0
- package/dist/render/readback.js +47 -0
- package/dist/render/recon/clamp.d.ts +36 -0
- package/dist/render/recon/clamp.js +92 -0
- package/dist/render/recon/disocclusion.d.ts +60 -0
- package/dist/render/recon/disocclusion.js +109 -0
- package/dist/render/recon/frameGen.d.ts +81 -0
- package/dist/render/recon/frameGen.js +150 -0
- package/dist/render/recon/frameSizes.d.ts +34 -0
- package/dist/render/recon/frameSizes.js +58 -0
- package/dist/render/recon/jitter.d.ts +60 -0
- package/dist/render/recon/jitter.js +118 -0
- package/dist/render/recon/motionVectors.d.ts +55 -0
- package/dist/render/recon/motionVectors.js +107 -0
- package/dist/render/recon/reproject.d.ts +41 -0
- package/dist/render/recon/reproject.js +131 -0
- package/dist/render/recon/resolve.d.ts +90 -0
- package/dist/render/recon/resolve.js +313 -0
- package/dist/render/recon/tier.d.ts +24 -0
- package/dist/render/recon/tier.js +8 -0
- package/dist/render/reflectionProbe.d.ts +26 -0
- package/dist/render/reflectionProbe.js +33 -0
- package/dist/render/renderQuality.d.ts +69 -0
- package/dist/render/renderQuality.js +22 -0
- package/dist/render/screenSpaceReflection.d.ts +23 -0
- package/dist/render/screenSpaceReflection.js +22 -3
- package/dist/render/sdfFont.js +2 -0
- package/dist/render/segmentQuads.d.ts +13 -0
- package/dist/render/segmentQuads.js +32 -0
- package/dist/render/shaders/flat/lobes.d.ts +27 -0
- package/dist/render/shaders/flat/lobes.js +33 -3
- package/dist/render/shaders/flat/preamble.d.ts +7 -0
- package/dist/render/shaders/flat/preamble.js +24 -1
- package/dist/render/shaders/flat/probeGrid.d.ts +1 -1
- package/dist/render/shaders/flat/probeGrid.js +71 -2
- package/dist/render/shaders/generated/flat.wgsl.d.ts +48 -8
- package/dist/render/shaders/generated/flat.wgsl.js +64 -24
- package/dist/render/shaders/gi/composeField.wgsl.d.ts +56 -0
- package/dist/render/shaders/gi/composeField.wgsl.js +231 -0
- package/dist/render/shaders/gi/marchField.wgsl.d.ts +30 -0
- package/dist/render/shaders/gi/marchField.wgsl.js +141 -0
- package/dist/render/shaders/gi/probeBake.wgsl.d.ts +113 -0
- package/dist/render/shaders/gi/probeBake.wgsl.js +635 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.d.ts +72 -0
- package/dist/render/shaders/gi/probeTrace.wgsl.js +261 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.d.ts +49 -0
- package/dist/render/shaders/gi/probeVolume.wgsl.js +242 -0
- package/dist/render/shaders/gi/sampleField.wgsl.d.ts +32 -0
- package/dist/render/shaders/gi/sampleField.wgsl.js +134 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.d.ts +26 -0
- package/dist/render/shaders/gpudriven/blendRaster.wgsl.js +339 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.d.ts +29 -0
- package/dist/render/shaders/gpudriven/compact.wgsl.js +58 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.d.ts +90 -0
- package/dist/render/shaders/gpudriven/cull.wgsl.js +423 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.d.ts +72 -0
- package/dist/render/shaders/gpudriven/decode.wgsl.js +349 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/environment.wgsl.js +137 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/fog.wgsl.js +107 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.d.ts +13 -0
- package/dist/render/shaders/gpudriven/frameBlock.wgsl.js +62 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.d.ts +51 -0
- package/dist/render/shaders/gpudriven/materialBin.wgsl.js +112 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.d.ts +25 -0
- package/dist/render/shaders/gpudriven/materialTable.wgsl.js +43 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.d.ts +18 -0
- package/dist/render/shaders/gpudriven/meshTransform.wgsl.js +37 -0
- package/dist/render/shaders/gpudriven/present.wgsl.d.ts +43 -0
- package/dist/render/shaders/gpudriven/present.wgsl.js +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.d.ts +127 -0
- package/dist/render/shaders/gpudriven/shade.wgsl.js +874 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.d.ts +44 -0
- package/dist/render/shaders/gpudriven/shadow.wgsl.js +308 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.d.ts +28 -0
- package/dist/render/shaders/gpudriven/visbufferRaster.wgsl.js +245 -0
- package/dist/render/shaders/network.wgsl.d.ts +101 -0
- package/dist/render/shaders/network.wgsl.js +206 -0
- package/dist/render/shaders/recon/motion.wgsl.d.ts +44 -0
- package/dist/render/shaders/recon/motion.wgsl.js +96 -0
- package/dist/render/shaders/recon/resolve.wgsl.d.ts +51 -0
- package/dist/render/shaders/recon/resolve.wgsl.js +733 -0
- package/dist/render/temporalAa.js +8 -33
- package/dist/render/textLayout.js +12 -6
- package/dist/render/textRenderer.d.ts +3 -1
- package/dist/render/textRenderer.js +4 -1
- package/dist/render/translucentQueue.d.ts +6 -0
- package/dist/render/translucentQueue.js +11 -0
- package/dist/render/windStreakRenderer.d.ts +2 -1
- package/dist/render/windStreakRenderer.js +3 -1
- package/dist/world/cell.d.ts +63 -0
- package/dist/world/cell.js +181 -0
- package/dist/world/cellStream.d.ts +103 -0
- package/dist/world/cellStream.js +198 -0
- package/dist/world/freeze.d.ts +81 -0
- package/dist/world/freeze.js +114 -0
- package/dist/world/rebase.d.ts +29 -0
- package/dist/world/rebase.js +41 -0
- package/package.json +8 -3
- package/scripts/frames.mjs +92 -0
- package/scripts/gpuCompute.mjs +323 -0
- package/scripts/png.d.mts +16 -0
- package/scripts/png.mjs +25 -0
- package/src/core/loop.ts +19 -2
- package/src/core/storage.ts +5 -0
- package/src/geometry/meshBuilder.ts +5 -1
- package/src/index.ts +246 -4
- package/src/input/input.ts +6 -1
- package/src/math/exact.ts +69 -3
- package/src/math/frustum.ts +52 -3
- package/src/render/backend/api.ts +42 -1
- package/src/render/backend/budget.ts +7 -6
- package/src/render/backend/createRenderer.ts +92 -3
- package/src/render/backend/materialChanges.ts +54 -0
- package/src/render/backend/select.ts +8 -0
- package/src/render/backend/webgl2/renderer.ts +211 -9
- package/src/render/backend/webgpu/buffers.ts +182 -69
- package/src/render/backend/webgpu/fieldCompose.ts +482 -0
- package/src/render/backend/webgpu/giFieldPass.ts +200 -0
- package/src/render/backend/webgpu/gpuDrivenPass.ts +2993 -0
- package/src/render/backend/webgpu/linePass.ts +4 -39
- package/src/render/backend/webgpu/probeBake.ts +588 -0
- package/src/render/backend/webgpu/renderer.ts +1481 -76
- package/src/render/backend/webgpu/scatterPass.ts +15 -1
- package/src/render/backend/webgpu/surfaceTexturePass.ts +63 -36
- package/src/render/causticsRenderer.ts +5 -2
- package/src/render/depthConvention.ts +15 -0
- package/src/render/deviceFeatures.ts +77 -0
- package/src/render/fog.ts +185 -0
- package/src/render/frame/alias.ts +97 -0
- package/src/render/frame/deps.ts +155 -0
- package/src/render/frame/flushGraph.ts +135 -0
- package/src/render/frame/graphSchedule.ts +170 -0
- package/src/render/frame/index.ts +52 -0
- package/src/render/frame/lifetime.ts +61 -0
- package/src/render/frame/passTimings.ts +69 -0
- package/src/render/frame/validate.ts +40 -0
- package/src/render/frame/virtual.ts +91 -0
- package/src/render/gi/chain.ts +225 -0
- package/src/render/gi/denoise.ts +276 -0
- package/src/render/gi/fieldScene.ts +264 -0
- package/src/render/gi/globalField.ts +543 -0
- package/src/render/gi/probeGridFit.ts +89 -0
- package/src/render/gi/probeTrace.ts +143 -0
- package/src/render/gi/probeVolume.ts +288 -0
- package/src/render/gi/reflection.ts +163 -0
- package/src/render/gi/traceField.ts +223 -0
- package/src/render/gi/traceScreen.ts +106 -0
- package/src/render/gpuScene.ts +80 -0
- package/src/render/gpudriven/alphaTest.ts +33 -0
- package/src/render/gpudriven/clusterUpload.ts +123 -0
- package/src/render/gpudriven/clusterWorld.ts +239 -0
- package/src/render/gpudriven/compact.ts +80 -0
- package/src/render/gpudriven/cullClusters.ts +333 -0
- package/src/render/gpudriven/decodeTables.ts +332 -0
- package/src/render/gpudriven/frustum.ts +70 -0
- package/src/render/gpudriven/geometryHarness.ts +51 -0
- package/src/render/gpudriven/hzb.ts +113 -0
- package/src/render/gpudriven/ibl.ts +147 -0
- package/src/render/gpudriven/indirect.ts +89 -0
- package/src/render/gpudriven/instances.ts +234 -0
- package/src/render/gpudriven/lit.ts +329 -0
- package/src/render/gpudriven/lodCut.ts +62 -0
- package/src/render/gpudriven/materialBin.ts +131 -0
- package/src/render/gpudriven/materialTable.ts +164 -0
- package/src/render/gpudriven/pipeline.ts +336 -0
- package/src/render/gpudriven/rangeAlloc.ts +122 -0
- package/src/render/gpudriven/sceneUpload.ts +173 -0
- package/src/render/gpudriven/shadeBins.ts +222 -0
- package/src/render/gpudriven/shadow.ts +285 -0
- package/src/render/gpudriven/shadowCamera.ts +406 -0
- package/src/render/gpudriven/streamScene.ts +575 -0
- package/src/render/gpudriven/surfaceFrame.ts +68 -0
- package/src/render/gpudriven/twoPhase.ts +214 -0
- package/src/render/gpudriven/visbuffer.ts +41 -0
- package/src/render/halfFloat.ts +22 -4
- package/src/render/inference/attentionTiled.ts +132 -0
- package/src/render/inference/denseKernels.ts +369 -0
- package/src/render/inference/device.ts +44 -0
- package/src/render/inference/deviceGraph.ts +39 -0
- package/src/render/inference/fuse.ts +47 -0
- package/src/render/inference/kernelKit.ts +120 -0
- package/src/render/inference/kernels.ts +18 -0
- package/src/render/inference/plan.ts +26 -0
- package/src/render/inference/reuse.ts +79 -0
- package/src/render/inference/runner.ts +204 -0
- package/src/render/inference/schedule.ts +80 -0
- package/src/render/inference/shapeKernels.ts +112 -0
- package/src/render/inference/spatialKernels.ts +220 -0
- package/src/render/inference/splatRaster.wgsl.ts +348 -0
- package/src/render/inference/splatTiles.ts +186 -0
- package/src/render/lineBatch.ts +4 -24
- package/src/render/mesh.ts +46 -1
- package/src/render/pass.ts +155 -10
- package/src/render/prefilterEnvMap.ts +33 -0
- package/src/render/readback.ts +73 -0
- package/src/render/recon/clamp.ts +113 -0
- package/src/render/recon/disocclusion.ts +143 -0
- package/src/render/recon/frameGen.ts +191 -0
- package/src/render/recon/frameSizes.ts +64 -0
- package/src/render/recon/jitter.ts +126 -0
- package/src/render/recon/motionVectors.ts +138 -0
- package/src/render/recon/reproject.ts +179 -0
- package/src/render/recon/resolve.ts +444 -0
- package/src/render/recon/tier.ts +31 -0
- package/src/render/reflectionProbe.ts +34 -0
- package/src/render/renderQuality.ts +87 -0
- package/src/render/screenSpaceReflection.ts +37 -3
- package/src/render/sdfFont.ts +2 -0
- package/src/render/segmentQuads.ts +39 -0
- package/src/render/shaders/flat/lobes.ts +35 -3
- package/src/render/shaders/flat/preamble.ts +25 -1
- package/src/render/shaders/flat/probeGrid.ts +77 -2
- package/src/render/shaders/generated/flat.wgsl.ts +64 -24
- package/src/render/shaders/gi/composeField.wgsl.ts +239 -0
- package/src/render/shaders/gi/marchField.wgsl.ts +146 -0
- package/src/render/shaders/gi/probeBake.wgsl.ts +649 -0
- package/src/render/shaders/gi/probeTrace.wgsl.ts +269 -0
- package/src/render/shaders/gi/probeVolume.wgsl.ts +246 -0
- package/src/render/shaders/gi/sampleField.wgsl.ts +140 -0
- package/src/render/shaders/gpudriven/blendRaster.wgsl.ts +343 -0
- package/src/render/shaders/gpudriven/compact.wgsl.ts +59 -0
- package/src/render/shaders/gpudriven/cull.wgsl.ts +433 -0
- package/src/render/shaders/gpudriven/decode.wgsl.ts +373 -0
- package/src/render/shaders/gpudriven/environment.wgsl.ts +141 -0
- package/src/render/shaders/gpudriven/fog.wgsl.ts +110 -0
- package/src/render/shaders/gpudriven/frameBlock.wgsl.ts +62 -0
- package/src/render/shaders/gpudriven/materialBin.wgsl.ts +116 -0
- package/src/render/shaders/gpudriven/materialTable.wgsl.ts +46 -0
- package/src/render/shaders/gpudriven/meshTransform.wgsl.ts +37 -0
- package/src/render/shaders/gpudriven/present.wgsl.ts +129 -0
- package/src/render/shaders/gpudriven/shade.wgsl.ts +884 -0
- package/src/render/shaders/gpudriven/shadow.wgsl.ts +322 -0
- package/src/render/shaders/gpudriven/visbufferRaster.wgsl.ts +251 -0
- package/src/render/shaders/network.wgsl.ts +238 -0
- package/src/render/shaders/recon/motion.wgsl.ts +101 -0
- package/src/render/shaders/recon/resolve.wgsl.ts +741 -0
- package/src/render/temporalAa.ts +9 -33
- package/src/render/textLayout.ts +12 -6
- package/src/render/textRenderer.ts +5 -2
- package/src/render/translucentQueue.ts +11 -0
- package/src/render/windStreakRenderer.ts +4 -2
- package/src/world/cell.ts +231 -0
- package/src/world/cellStream.ts +278 -0
- package/src/world/freeze.ts +157 -0
- package/src/world/rebase.ts +56 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running a WGSL compute shader and reading its buffers back.
|
|
3
|
+
*
|
|
4
|
+
* **This is what lets a compute shader have a test.** The repository's discipline for anything
|
|
5
|
+
* written twice — the splat shader, the decode interpreter, the heightfield triangulation — is one
|
|
6
|
+
* authored expression, two consumers, and a test that they have not drifted. Every compute pass in
|
|
7
|
+
* the GPU-driven pipeline is arithmetic written once in TypeScript as the reference and once in
|
|
8
|
+
* WGSL for the device, and until now there was no way to compare them: the TypeScript half had
|
|
9
|
+
* tests and the WGSL half had a generator.
|
|
10
|
+
*
|
|
11
|
+
* **A page rather than a headless device, because WebGPU needs a secure context.** `navigator.gpu`
|
|
12
|
+
* is not exposed on `about:blank` — `browser.mjs`'s own header records that, and records that the
|
|
13
|
+
* first reading of the absence was that the card had no adapter. `http://localhost` is a secure
|
|
14
|
+
* context, so this serves one blank page from a loopback server of its own. No dev server, no
|
|
15
|
+
* build step, nothing to start first.
|
|
16
|
+
*
|
|
17
|
+
* **One browser for many shaders.** Launching Chrome costs a second or two and a parity check runs
|
|
18
|
+
* hundreds of cases, so `openGpuCompute` hands back a handle that keeps the device and the page.
|
|
19
|
+
*
|
|
20
|
+
* Not a `*.test.mjs`: it needs a GPU, and `npm run test:scripts` runs on machines that have none.
|
|
21
|
+
* The checks that use it are run by hand, the way `probe-check.mjs` and `ibl-check.mjs` are.
|
|
22
|
+
*/
|
|
23
|
+
import { createServer } from 'node:http';
|
|
24
|
+
|
|
25
|
+
import { launch } from './browser.mjs';
|
|
26
|
+
import { connect } from './cdp.mjs';
|
|
27
|
+
|
|
28
|
+
const PAGE = `<!doctype html><meta charset="utf-8"><title>gpu compute</title><body></body>`;
|
|
29
|
+
|
|
30
|
+
/** A loopback server serving one blank page, which is all a secure context needs. */
|
|
31
|
+
async function servePage() {
|
|
32
|
+
const server = createServer((request, response) => {
|
|
33
|
+
response.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
34
|
+
response.end(PAGE);
|
|
35
|
+
});
|
|
36
|
+
await new Promise((resolve) => {
|
|
37
|
+
server.listen(0, '127.0.0.1', resolve);
|
|
38
|
+
});
|
|
39
|
+
const { port } = server.address();
|
|
40
|
+
return {
|
|
41
|
+
url: `http://localhost:${port}/`,
|
|
42
|
+
close: () =>
|
|
43
|
+
new Promise((resolve) => {
|
|
44
|
+
server.close(resolve);
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The script the page runs, as a string, because it executes in the browser rather than here.
|
|
51
|
+
*
|
|
52
|
+
* Buffers cross as plain number arrays. That costs a copy per call and buys not having to agree
|
|
53
|
+
* about a binary encoding across the CDP boundary — at the sizes a parity check uses, hundreds of
|
|
54
|
+
* spheres rather than millions, the copy is not the cost that matters.
|
|
55
|
+
*/
|
|
56
|
+
function dispatchScript(request) {
|
|
57
|
+
return `(async () => {
|
|
58
|
+
if (!('gpu' in navigator)) return { error: 'no navigator.gpu' };
|
|
59
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
60
|
+
if (adapter === null) return { error: 'no adapter' };
|
|
61
|
+
const request = ${JSON.stringify(request)};
|
|
62
|
+
/* Asked for by name and refused by name: a check that quietly ran without its feature did not run. */
|
|
63
|
+
const missing = request.features.filter((feature) => !adapter.features.has(feature));
|
|
64
|
+
if (missing.length > 0) return { error: 'the adapter lacks ' + missing.join(', ') };
|
|
65
|
+
const device = await adapter.requestDevice({ requiredFeatures: request.features });
|
|
66
|
+
|
|
67
|
+
const errors = [];
|
|
68
|
+
device.addEventListener('uncapturederror', (event) => errors.push(String(event.error.message)));
|
|
69
|
+
|
|
70
|
+
const module = device.createShaderModule({ code: request.wgsl });
|
|
71
|
+
const info = await module.getCompilationInfo();
|
|
72
|
+
const fatal = info.messages.filter((message) => message.type === 'error');
|
|
73
|
+
if (fatal.length > 0) {
|
|
74
|
+
return { error: fatal.map((m) => \`\${m.lineNum}:\${m.linePos} \${m.message}\`).join('\\n') };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const entries = [];
|
|
78
|
+
const bindings = [];
|
|
79
|
+
const readbacks = [];
|
|
80
|
+
for (let at = 0; at < request.buffers.length; at += 1) {
|
|
81
|
+
const spec = request.buffers[at];
|
|
82
|
+
if (spec.kind === 'texture2dArray') {
|
|
83
|
+
const texture = device.createTexture({
|
|
84
|
+
size: [spec.size, spec.size, spec.layers],
|
|
85
|
+
format: 'rgba8unorm',
|
|
86
|
+
mipLevelCount: spec.levels.length,
|
|
87
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
|
88
|
+
});
|
|
89
|
+
for (let level = 0; level < spec.levels.length; level += 1) {
|
|
90
|
+
const edge = Math.max(1, spec.size >> level);
|
|
91
|
+
device.queue.writeTexture(
|
|
92
|
+
{ texture, mipLevel: level },
|
|
93
|
+
new Uint8Array(spec.levels[level]),
|
|
94
|
+
{ bytesPerRow: edge * 4, rowsPerImage: edge },
|
|
95
|
+
[edge, edge, spec.layers],
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
entries.push({ binding: at, resource: texture.createView({ dimension: '2d-array' }) });
|
|
99
|
+
bindings.push({
|
|
100
|
+
binding: at,
|
|
101
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
102
|
+
texture: { sampleType: 'float', viewDimension: '2d-array' },
|
|
103
|
+
});
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (spec.kind === 'texture2d') {
|
|
107
|
+
const texture = device.createTexture({
|
|
108
|
+
size: [spec.width, spec.height, 1],
|
|
109
|
+
format: spec.format,
|
|
110
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
|
111
|
+
});
|
|
112
|
+
const channels = spec.format === 'r32float' ? 1 : 4;
|
|
113
|
+
device.queue.writeTexture(
|
|
114
|
+
{ texture },
|
|
115
|
+
new Float32Array(spec.values),
|
|
116
|
+
{ bytesPerRow: spec.width * channels * 4, rowsPerImage: spec.height },
|
|
117
|
+
[spec.width, spec.height, 1],
|
|
118
|
+
);
|
|
119
|
+
entries.push({ binding: at, resource: texture.createView() });
|
|
120
|
+
bindings.push({
|
|
121
|
+
binding: at,
|
|
122
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
123
|
+
/* A 32-bit float format is not filterable, so a shader reading one loads its texels. */
|
|
124
|
+
texture: { sampleType: 'unfilterable-float', viewDimension: '2d' },
|
|
125
|
+
});
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (spec.kind === 'sampler') {
|
|
129
|
+
const sampler = device.createSampler({
|
|
130
|
+
magFilter: 'linear',
|
|
131
|
+
minFilter: 'linear',
|
|
132
|
+
mipmapFilter: 'linear',
|
|
133
|
+
addressModeU: spec.address,
|
|
134
|
+
addressModeV: spec.address,
|
|
135
|
+
});
|
|
136
|
+
entries.push({ binding: at, resource: sampler });
|
|
137
|
+
bindings.push({ binding: at, visibility: GPUShaderStage.COMPUTE, sampler: { type: 'filtering' } });
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const Ctor =
|
|
141
|
+
spec.type === 'u32' ? Uint32Array
|
|
142
|
+
: spec.type === 'i32' ? Int32Array
|
|
143
|
+
: spec.type === 'f16' ? Uint16Array
|
|
144
|
+
: Float32Array;
|
|
145
|
+
/*
|
|
146
|
+
* An f16 buffer is written as the half-precision bits the caller hands over, and padded to
|
|
147
|
+
* four bytes, which is the least a storage binding may be sized in.
|
|
148
|
+
*/
|
|
149
|
+
const count = spec.values.length > 0 ? spec.values.length : spec.length;
|
|
150
|
+
const values = new Ctor(spec.type === 'f16' ? Math.ceil(count / 2) * 2 : count);
|
|
151
|
+
if (spec.values.length > 0) values.set(spec.values);
|
|
152
|
+
/* A uniform binding is sized in whole sixteen-byte rows, and is never read back. */
|
|
153
|
+
const uniform = spec.kind === 'uniform';
|
|
154
|
+
const buffer = device.createBuffer({
|
|
155
|
+
size: uniform
|
|
156
|
+
? Math.max(16, Math.ceil(values.byteLength / 16) * 16)
|
|
157
|
+
: Math.max(4, values.byteLength),
|
|
158
|
+
usage: uniform
|
|
159
|
+
? GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
160
|
+
: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
|
|
161
|
+
});
|
|
162
|
+
device.queue.writeBuffer(buffer, 0, values);
|
|
163
|
+
entries.push({ binding: at, resource: { buffer } });
|
|
164
|
+
bindings.push({
|
|
165
|
+
binding: at,
|
|
166
|
+
visibility: GPUShaderStage.COMPUTE,
|
|
167
|
+
buffer: { type: uniform ? 'uniform' : spec.readOnly ? 'read-only-storage' : 'storage' },
|
|
168
|
+
});
|
|
169
|
+
if (spec.read && !uniform) readbacks.push({ at, buffer, size: values.byteLength, type: spec.type });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const layout = device.createBindGroupLayout({ entries: bindings });
|
|
173
|
+
const pipeline = device.createComputePipeline({
|
|
174
|
+
layout: device.createPipelineLayout({ bindGroupLayouts: [layout] }),
|
|
175
|
+
compute: { module, entryPoint: request.entryPoint },
|
|
176
|
+
});
|
|
177
|
+
const group = device.createBindGroup({ layout, entries });
|
|
178
|
+
|
|
179
|
+
const encoder = device.createCommandEncoder();
|
|
180
|
+
const pass = encoder.beginComputePass();
|
|
181
|
+
pass.setPipeline(pipeline);
|
|
182
|
+
pass.setBindGroup(0, group);
|
|
183
|
+
pass.dispatchWorkgroups(request.workgroups[0], request.workgroups[1], request.workgroups[2]);
|
|
184
|
+
pass.end();
|
|
185
|
+
|
|
186
|
+
const staging = readbacks.map((entry) => {
|
|
187
|
+
const copy = device.createBuffer({
|
|
188
|
+
size: entry.size,
|
|
189
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
190
|
+
});
|
|
191
|
+
encoder.copyBufferToBuffer(entry.buffer, 0, copy, 0, entry.size);
|
|
192
|
+
return copy;
|
|
193
|
+
});
|
|
194
|
+
device.queue.submit([encoder.finish()]);
|
|
195
|
+
await device.queue.onSubmittedWorkDone();
|
|
196
|
+
|
|
197
|
+
const out = {};
|
|
198
|
+
for (let at = 0; at < readbacks.length; at += 1) {
|
|
199
|
+
await staging[at].mapAsync(GPUMapMode.READ);
|
|
200
|
+
const entry = readbacks[at];
|
|
201
|
+
const Ctor = entry.type === 'u32' ? Uint32Array : entry.type === 'i32' ? Int32Array : Float32Array;
|
|
202
|
+
out[entry.at] = Array.from(new Ctor(staging[at].getMappedRange().slice(0)));
|
|
203
|
+
staging[at].unmap();
|
|
204
|
+
}
|
|
205
|
+
return errors.length > 0 ? { error: errors.join('\\n'), buffers: out } : { buffers: out };
|
|
206
|
+
})()`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Open a browser with a WebGPU device on it.
|
|
211
|
+
*
|
|
212
|
+
* `run` takes the shader, the buffers it binds at group 0 in order, and the workgroup counts, and
|
|
213
|
+
* hands back whichever buffers were asked to be read. Buffers are declared rather than inferred:
|
|
214
|
+
* a harness that guessed which binding was an output would guess wrong on the first shader that
|
|
215
|
+
* writes into what it reads.
|
|
216
|
+
*/
|
|
217
|
+
export async function openGpuCompute(options = {}) {
|
|
218
|
+
const page = await servePage();
|
|
219
|
+
const browser = await launch(options.launch ?? {});
|
|
220
|
+
const client = await connect(await browser.port);
|
|
221
|
+
const tab = await client.page(page.url, 320, 240);
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
/**
|
|
225
|
+
* `features` are the device features the shader needs, `'shader-f16'` for a half-precision one;
|
|
226
|
+
* an adapter without one of them is an error rather than a device created without it.
|
|
227
|
+
*
|
|
228
|
+
* Each entry of `buffers` is bound at the binding its index names, as one of four kinds:
|
|
229
|
+
*
|
|
230
|
+
* - `'storage'`, the default — `type`, `values` or `length`, `read`, `readOnly`. An `'f16'`
|
|
231
|
+
* buffer takes half-precision bits as its values, and is never read back.
|
|
232
|
+
* - `'uniform'` — `{ kind: 'uniform', type: 'u32' | 'f32', values }`, bound as a uniform
|
|
233
|
+
* buffer padded to whole sixteen-byte rows, and never read back.
|
|
234
|
+
* - `'texture2dArray'` — `{ kind: 'texture2dArray', size, layers, levels }`: an `rgba8unorm`
|
|
235
|
+
* array of `size`-square layers with `levels.length` mip levels, where `levels[k]` is every
|
|
236
|
+
* layer's RGBA bytes at level `k`, layer after layer.
|
|
237
|
+
* - `'texture2d'` — `{ kind: 'texture2d', width, height, format, values }`: one mip level of
|
|
238
|
+
* `'r32float'` or `'rgba32float'`, written from `values` as float32, row after row. **Bound
|
|
239
|
+
* as `unfilterable-float`**, because no 32-bit float format is filterable without an optional
|
|
240
|
+
* feature — a shader reading one uses `textureLoad` and does any interpolation itself, which
|
|
241
|
+
* is what makes a comparison against a reference exact rather than a tolerance on whatever
|
|
242
|
+
* the device's sampler rounds to.
|
|
243
|
+
* - `'sampler'` — `{ kind: 'sampler', address: 'clamp-to-edge' | 'repeat' }`, linear in
|
|
244
|
+
* magnification, minification and between levels.
|
|
245
|
+
*
|
|
246
|
+
* @param {{ wgsl: string, entryPoint?: string, workgroups?: number[], features?: string[],
|
|
247
|
+
* buffers: { kind?: 'storage'|'uniform'|'texture2dArray'|'texture2d'|'sampler',
|
|
248
|
+
* type?: 'f32'|'f16'|'u32'|'i32', values?: number[], length?: number,
|
|
249
|
+
* read?: boolean, readOnly?: boolean, size?: number, layers?: number,
|
|
250
|
+
* levels?: ArrayLike<number>[], address?: string,
|
|
251
|
+
* width?: number, height?: number,
|
|
252
|
+
* format?: 'r32float'|'rgba32float' }[] }} request
|
|
253
|
+
*/
|
|
254
|
+
async run(request) {
|
|
255
|
+
const prepared = {
|
|
256
|
+
wgsl: request.wgsl,
|
|
257
|
+
entryPoint: request.entryPoint ?? 'main',
|
|
258
|
+
features: request.features ?? [],
|
|
259
|
+
workgroups: [
|
|
260
|
+
request.workgroups?.[0] ?? 1,
|
|
261
|
+
request.workgroups?.[1] ?? 1,
|
|
262
|
+
request.workgroups?.[2] ?? 1,
|
|
263
|
+
],
|
|
264
|
+
buffers: request.buffers.map((buffer) => ({
|
|
265
|
+
kind: buffer.kind ?? 'storage',
|
|
266
|
+
type: buffer.type ?? 'f32',
|
|
267
|
+
values: buffer.values === undefined ? [] : Array.from(buffer.values),
|
|
268
|
+
length: buffer.length ?? 0,
|
|
269
|
+
read: buffer.read === true,
|
|
270
|
+
readOnly: buffer.readOnly === true,
|
|
271
|
+
size: buffer.size ?? 0,
|
|
272
|
+
layers: buffer.layers ?? 0,
|
|
273
|
+
width: buffer.width ?? 0,
|
|
274
|
+
height: buffer.height ?? 0,
|
|
275
|
+
format: buffer.format ?? 'rgba32float',
|
|
276
|
+
levels:
|
|
277
|
+
buffer.levels === undefined ? [] : buffer.levels.map((level) => Array.from(level)),
|
|
278
|
+
address: buffer.address ?? 'clamp-to-edge',
|
|
279
|
+
})),
|
|
280
|
+
};
|
|
281
|
+
const result = await tab.eval(dispatchScript(prepared));
|
|
282
|
+
const parsed = typeof result === 'string' ? JSON.parse(result) : result;
|
|
283
|
+
if (parsed.error !== undefined && parsed.buffers === undefined) {
|
|
284
|
+
throw new Error(`gpu compute: ${parsed.error}`);
|
|
285
|
+
}
|
|
286
|
+
if (parsed.error !== undefined) throw new Error(`gpu compute: ${parsed.error}`);
|
|
287
|
+
return parsed.buffers;
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* What the page's adapter says it is — vendor, architecture, whether it is a fallback — so a
|
|
292
|
+
* figure a check prints names the device that produced it, and a software adapter is seen.
|
|
293
|
+
*/
|
|
294
|
+
async adapter() {
|
|
295
|
+
const result = await tab.eval(`(async () => {
|
|
296
|
+
const adapter = await navigator.gpu?.requestAdapter();
|
|
297
|
+
if (!adapter) return { error: 'no adapter' };
|
|
298
|
+
const info = adapter.info ?? {};
|
|
299
|
+
return {
|
|
300
|
+
vendor: info.vendor ?? '',
|
|
301
|
+
architecture: info.architecture ?? '',
|
|
302
|
+
description: info.description ?? '',
|
|
303
|
+
fallback: adapter.isFallbackAdapter === true || info.isFallbackAdapter === true,
|
|
304
|
+
features: [...adapter.features],
|
|
305
|
+
};
|
|
306
|
+
})()`);
|
|
307
|
+
const parsed = typeof result === 'string' ? JSON.parse(result) : result;
|
|
308
|
+
if (parsed.error !== undefined) throw new Error(`gpu compute: ${parsed.error}`);
|
|
309
|
+
return parsed;
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
async close() {
|
|
313
|
+
try {
|
|
314
|
+
await tab.close();
|
|
315
|
+
} catch {
|
|
316
|
+
/* the page may already be gone; the browser close below is what matters */
|
|
317
|
+
}
|
|
318
|
+
await client.close?.();
|
|
319
|
+
await browser.close();
|
|
320
|
+
await page.close();
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
package/scripts/png.d.mts
CHANGED
|
@@ -15,3 +15,19 @@ export function readPng(path: string): DecodedPng;
|
|
|
15
15
|
* Node-side only — `node:zlib` is the whole of it, and nothing a browser loads links this.
|
|
16
16
|
*/
|
|
17
17
|
export function encodePng(width: number, height: number, rgba: Buffer | Uint8Array): Buffer;
|
|
18
|
+
|
|
19
|
+
/** An image as four bytes a pixel, row-major. */
|
|
20
|
+
export interface RgbaImage {
|
|
21
|
+
readonly width: number;
|
|
22
|
+
readonly height: number;
|
|
23
|
+
readonly rgba: Uint8Array;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A decoded PNG as four bytes a pixel, whether it was written with three channels or four. */
|
|
27
|
+
export function rgbaOf(decoded: DecodedPng): RgbaImage;
|
|
28
|
+
|
|
29
|
+
/** A rectangle of a four-byte-a-pixel image, as a new image. */
|
|
30
|
+
export function crop(
|
|
31
|
+
image: RgbaImage,
|
|
32
|
+
rect: { readonly x: number; readonly y: number; readonly width: number; readonly height: number },
|
|
33
|
+
): RgbaImage;
|
package/scripts/png.mjs
CHANGED
|
@@ -199,3 +199,28 @@ function chunk(type, body) {
|
|
|
199
199
|
out.writeUInt32BE(crc32(out.subarray(4, 8 + body.length)), 8 + body.length);
|
|
200
200
|
return out;
|
|
201
201
|
}
|
|
202
|
+
|
|
203
|
+
/** A decoded PNG as four bytes a pixel, whether it was written with three channels or four. */
|
|
204
|
+
export function rgbaOf(decoded) {
|
|
205
|
+
const count = decoded.width * decoded.height;
|
|
206
|
+
if (decoded.channels === 4)
|
|
207
|
+
return { width: decoded.width, height: decoded.height, rgba: decoded.pixels };
|
|
208
|
+
const rgba = new Uint8Array(count * 4);
|
|
209
|
+
for (let at = 0; at < count; at += 1) {
|
|
210
|
+
rgba[at * 4] = decoded.pixels[at * decoded.channels];
|
|
211
|
+
rgba[at * 4 + 1] = decoded.pixels[at * decoded.channels + 1];
|
|
212
|
+
rgba[at * 4 + 2] = decoded.pixels[at * decoded.channels + 2];
|
|
213
|
+
rgba[at * 4 + 3] = 255;
|
|
214
|
+
}
|
|
215
|
+
return { width: decoded.width, height: decoded.height, rgba };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** A rectangle of a four-byte-a-pixel image, as a new image. */
|
|
219
|
+
export function crop(image, rect) {
|
|
220
|
+
const rgba = new Uint8Array(rect.width * rect.height * 4);
|
|
221
|
+
for (let y = 0; y < rect.height; y += 1) {
|
|
222
|
+
const from = ((rect.y + y) * image.width + rect.x) * 4;
|
|
223
|
+
rgba.set(image.rgba.subarray(from, from + rect.width * 4), y * rect.width * 4);
|
|
224
|
+
}
|
|
225
|
+
return { width: rect.width, height: rect.height, rgba };
|
|
226
|
+
}
|
package/src/core/loop.ts
CHANGED
|
@@ -124,7 +124,19 @@ export function startLoop(hooks: LoopHooks, options: LoopOptions = {}): () => vo
|
|
|
124
124
|
const fixedDt = options.fixedDt ?? 1 / 60;
|
|
125
125
|
const maxFrameTime = options.maxFrameTime ?? 0.25;
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
/**
|
|
128
|
+
* When the last frame arrived, on the timebase the frames themselves use.
|
|
129
|
+
*
|
|
130
|
+
* **Null until the first frame whenever a source was supplied, because that source owns the
|
|
131
|
+
* timebase and this loop has no way to ask what it is.** Seeding from `performance.now()`
|
|
132
|
+
* regardless is what this did, and for the window it is right — a `DOMHighResTimeStamp` and
|
|
133
|
+
* `performance.now()` share an origin. For anything else it mixes two clocks: the synthetic
|
|
134
|
+
* session in `@driftengine/xr` counts from its own zero, so the first delta became sixteen
|
|
135
|
+
* milliseconds minus a process uptime, the accumulator went as negative as that, and the
|
|
136
|
+
* simulation stopped ticking until it climbed back — one dead frame per sixteen milliseconds the
|
|
137
|
+
* process had been alive. A caller that supplies the frames supplies the timebase.
|
|
138
|
+
*/
|
|
139
|
+
let last: number | null = options.frameSource === undefined ? performance.now() : null;
|
|
128
140
|
let accumulator = 0;
|
|
129
141
|
/**
|
|
130
142
|
* Fixed steps taken, offset by `startTick`.
|
|
@@ -145,8 +157,10 @@ export function startLoop(hooks: LoopHooks, options: LoopOptions = {}): () => vo
|
|
|
145
157
|
* another, because the two run at different rates and share no accumulator worth carrying across.
|
|
146
158
|
*/
|
|
147
159
|
const source: FrameSource = options.frameSource ?? {
|
|
160
|
+
// platform: browser default — `LoopOptions.frameSource` is the seam
|
|
148
161
|
requestAnimationFrame: (callback) => requestAnimationFrame(callback),
|
|
149
162
|
cancelAnimationFrame: (handle) => {
|
|
163
|
+
// platform: browser default — as above
|
|
150
164
|
cancelAnimationFrame(handle);
|
|
151
165
|
},
|
|
152
166
|
};
|
|
@@ -154,6 +168,9 @@ export function startLoop(hooks: LoopHooks, options: LoopOptions = {}): () => vo
|
|
|
154
168
|
const frame = (now: number, xrFrame?: unknown): void => {
|
|
155
169
|
rafId = source.requestAnimationFrame(frame);
|
|
156
170
|
|
|
171
|
+
/* The first frame of a supplied source establishes the origin, so it advances nothing. */
|
|
172
|
+
const previous = last ?? now;
|
|
173
|
+
|
|
157
174
|
/*
|
|
158
175
|
* **Held: draw nothing, advance nothing, and bank nothing.**
|
|
159
176
|
*
|
|
@@ -173,7 +190,7 @@ export function startLoop(hooks: LoopHooks, options: LoopOptions = {}): () => vo
|
|
|
173
190
|
return;
|
|
174
191
|
}
|
|
175
192
|
|
|
176
|
-
let frameDt = (now -
|
|
193
|
+
let frameDt = (now - previous) / 1000;
|
|
177
194
|
last = now;
|
|
178
195
|
const wallDt = frameDt;
|
|
179
196
|
if (frameDt > maxFrameTime) frameDt = maxFrameTime;
|
package/src/core/storage.ts
CHANGED
|
@@ -65,6 +65,7 @@ export class BrowserStore implements KeyValueStore {
|
|
|
65
65
|
read(key: string): string | null {
|
|
66
66
|
if (!this.available) return this.fallback.read(key);
|
|
67
67
|
try {
|
|
68
|
+
// platform: browser default — `KeyValueStore` is the seam; see `MemoryStore` beside it
|
|
68
69
|
return localStorage.getItem(key);
|
|
69
70
|
} catch {
|
|
70
71
|
return this.fallback.read(key);
|
|
@@ -75,6 +76,7 @@ export class BrowserStore implements KeyValueStore {
|
|
|
75
76
|
this.fallback.write(key, value);
|
|
76
77
|
if (!this.available) return;
|
|
77
78
|
try {
|
|
79
|
+
// platform: browser default — as above
|
|
78
80
|
localStorage.setItem(key, value);
|
|
79
81
|
} catch {
|
|
80
82
|
// Quota exceeded or blocked mid-session; the memory copy still stands.
|
|
@@ -85,6 +87,7 @@ export class BrowserStore implements KeyValueStore {
|
|
|
85
87
|
this.fallback.remove(key);
|
|
86
88
|
if (!this.available) return;
|
|
87
89
|
try {
|
|
90
|
+
// platform: browser default — as above
|
|
88
91
|
localStorage.removeItem(key);
|
|
89
92
|
} catch {
|
|
90
93
|
// As above.
|
|
@@ -97,7 +100,9 @@ function probeLocalStorage(): boolean {
|
|
|
97
100
|
// Reading is not enough of a probe: some browsers allow the read and throw
|
|
98
101
|
// only on write, which would strand a store that reports itself healthy.
|
|
99
102
|
const probe = '__driftengine_probe__';
|
|
103
|
+
// platform: feature probe — a blocked store must be found by trying it
|
|
100
104
|
localStorage.setItem(probe, '1');
|
|
105
|
+
// platform: feature probe — as above
|
|
101
106
|
localStorage.removeItem(probe);
|
|
102
107
|
return true;
|
|
103
108
|
} catch {
|
|
@@ -808,8 +808,12 @@ export class MeshBuilder {
|
|
|
808
808
|
emissive = 0,
|
|
809
809
|
segments = 12,
|
|
810
810
|
rings = 6,
|
|
811
|
+
/* `addCapsule` has taken one since it existed; forwarding it is what was missing, and a
|
|
812
|
+
sphere that cannot carry the specular a box can is the reason no page here could draw a
|
|
813
|
+
polished surface under a direct light. */
|
|
814
|
+
specular = 0,
|
|
811
815
|
): this {
|
|
812
|
-
return this.addCapsule(center, radius, 0, color, emissive, segments, rings);
|
|
816
|
+
return this.addCapsule(center, radius, 0, color, emissive, segments, rings, specular);
|
|
813
817
|
}
|
|
814
818
|
|
|
815
819
|
/** Add a faceted cylinder aligned to one principal axis. */
|