@forgeax/engine-render 0.1.32 → 0.1.34
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/README.md +1374 -142
- package/dist/assembly/analytic-fog-registration.d.ts +3 -0
- package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
- package/dist/assembly/backend-contract.d.ts +2 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/barrel-distortion-inspection.d.ts +3 -0
- package/dist/assembly/barrel-distortion-inspection.d.ts.map +1 -0
- package/dist/assembly/canvas-draw-guard.d.ts +3 -0
- package/dist/assembly/canvas-draw-guard.d.ts.map +1 -0
- package/dist/assembly/cloud-shadow-entry.d.ts +4 -0
- package/dist/assembly/cloud-shadow-entry.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-host.d.ts +4 -1
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -1
- package/dist/assembly/factory.d.ts +1 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/fxaa-registration.d.ts +4 -0
- package/dist/assembly/fxaa-registration.d.ts.map +1 -0
- package/dist/assembly/host-contract.d.ts +3 -3
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/lod-observation.d.ts +4 -0
- package/dist/assembly/lod-observation.d.ts.map +1 -0
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-helpers.d.ts +5 -0
- package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -1
- package/dist/assembly/material-shader-policy.d.ts +16 -2
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/material-shader-prewarm.d.ts.map +1 -1
- package/dist/assembly/motion-blur-registration.d.ts +3 -0
- package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts +2 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/render-feature-post-processes.d.ts +6 -0
- package/dist/assembly/render-feature-post-processes.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -1
- package/dist/assembly/renderer-frame-transaction.d.ts +12 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-inspection.d.ts +11 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -1
- package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
- package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
- package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready-per-pass.d.ts +39 -0
- package/dist/assembly/webgpu-ready-per-pass.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +2 -2
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-observation-owner.d.ts +35 -0
- package/dist/assembly/webgpu-renderer-observation-owner.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/barrel-distortion.d.ts +47 -0
- package/dist/barrel-distortion.d.ts.map +1 -0
- package/dist/bloom-admission.d.ts +13 -0
- package/dist/bloom-admission.d.ts.map +1 -0
- package/dist/{chunk-PA5DRJNI.mjs → chunk-ELJFJ2BU.mjs} +2 -2
- package/dist/chunk-ELJFJ2BU.mjs.map +1 -0
- package/dist/{chunk-JCIRYQ4V.mjs → chunk-HEB564WF.mjs} +10 -4
- package/dist/chunk-HEB564WF.mjs.map +1 -0
- package/dist/{chunk-JRIPCMYC.mjs → chunk-MKDIT2EM.mjs} +1584 -375
- package/dist/chunk-MKDIT2EM.mjs.map +1 -0
- package/dist/{chunk-S665NJNF.mjs → chunk-OCDLEI6I.mjs} +3 -3
- package/dist/{chunk-S665NJNF.mjs.map → chunk-OCDLEI6I.mjs.map} +1 -1
- package/dist/chunk-ODZWR6JQ.mjs +4927 -0
- package/dist/chunk-ODZWR6JQ.mjs.map +1 -0
- package/dist/{chunk-DRR7D3KX.mjs → chunk-SHFUFJ5N.mjs} +192 -18
- package/dist/chunk-SHFUFJ5N.mjs.map +1 -0
- package/dist/{chunk-4NNZQCL3.mjs → chunk-UI5SFODX.mjs} +223 -277
- package/dist/chunk-UI5SFODX.mjs.map +1 -0
- package/dist/{chunk-GWCEMDVR.mjs → chunk-UINHLCZA.mjs} +787 -80
- package/dist/chunk-UINHLCZA.mjs.map +1 -0
- package/dist/{chunk-6TFBXLRJ.mjs → chunk-WEX45HKH.mjs} +222 -337
- package/dist/chunk-WEX45HKH.mjs.map +1 -0
- package/dist/cloud/density.d.ts +55 -0
- package/dist/cloud/density.d.ts.map +1 -0
- package/dist/cloud/extract.d.ts +25 -0
- package/dist/cloud/extract.d.ts.map +1 -0
- package/dist/cloud/feature.d.ts +57 -0
- package/dist/cloud/feature.d.ts.map +1 -0
- package/dist/cloud/frame-context.d.ts +8 -0
- package/dist/cloud/frame-context.d.ts.map +1 -0
- package/dist/cloud/inspection.d.ts +59 -0
- package/dist/cloud/inspection.d.ts.map +1 -0
- package/dist/cloud/optics.d.ts +56 -0
- package/dist/cloud/optics.d.ts.map +1 -0
- package/dist/cloud/parameters.d.ts +73 -0
- package/dist/cloud/parameters.d.ts.map +1 -0
- package/dist/cloud/resources.d.ts +60 -0
- package/dist/cloud/resources.d.ts.map +1 -0
- package/dist/cloud/shadow.d.ts +42 -0
- package/dist/cloud/shadow.d.ts.map +1 -0
- package/dist/cloud/temporal.d.ts +74 -0
- package/dist/cloud/temporal.d.ts.map +1 -0
- package/dist/components/atmosphere.d.ts +2 -0
- package/dist/components/atmosphere.d.ts.map +1 -1
- package/dist/components/barrel-distortion.d.ts +13 -0
- package/dist/components/barrel-distortion.d.ts.map +1 -0
- package/dist/components/camera.d.ts +20 -4
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/cloud-layer.d.ts +29 -0
- package/dist/components/cloud-layer.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +20 -6
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/mesh-filter.d.ts +6 -5
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/motion-blur.d.ts +1 -0
- package/dist/components/motion-blur.d.ts.map +1 -1
- package/dist/construct-renderer.d.ts +1 -1
- package/dist/construct-renderer.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +9751 -3528
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +6 -0
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/dynamic-geometry.d.ts +5 -1
- package/dist/dynamic-geometry.d.ts.map +1 -1
- package/dist/environment/analytic-fog.d.ts +6 -0
- package/dist/environment/analytic-fog.d.ts.map +1 -0
- package/dist/errors/cloud.d.ts +59 -0
- package/dist/errors/cloud.d.ts.map +1 -0
- package/dist/errors/render.d.ts +23 -5
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/examples/single-layer-medium-surface.d.ts +47 -0
- package/dist/examples/single-layer-medium-surface.d.ts.map +1 -0
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +12 -1
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/environment.d.ts +2 -0
- package/dist/extract/environment.d.ts.map +1 -1
- package/dist/features/barrel-distortion.d.ts +29 -0
- package/dist/features/barrel-distortion.d.ts.map +1 -0
- package/dist/features/error-reporting.d.ts +17 -0
- package/dist/features/error-reporting.d.ts.map +1 -0
- package/dist/features/host.d.ts +15 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-feature.d.ts +20 -2
- package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
- package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
- package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
- package/dist/features/plan.d.ts +15 -4
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +9 -5
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +5 -0
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +1 -0
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/targets.d.ts +2 -0
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +44 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +15 -7
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-dirty-ranges.d.ts +11 -0
- package/dist/gpu-dirty-ranges.d.ts.map +1 -0
- package/dist/gpu-driven/batch-topology.d.ts +17 -0
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts +6 -0
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +7 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster-material.d.ts +178 -0
- package/dist/gpu-driven/production-raster-material.d.ts.map +1 -0
- package/dist/gpu-driven/production-raster.d.ts +50 -4
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/resource-allocation.d.ts +31 -0
- package/dist/gpu-driven/resource-allocation.d.ts.map +1 -0
- package/dist/gpu-driven/shadow-ownership.d.ts +14 -0
- package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +35 -2
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +11 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +2 -2
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +10 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +31 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +258 -8
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +178 -3
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instance-buffer-cache.d.ts +4 -4
- package/dist/instance-buffer-cache.d.ts.map +1 -1
- package/dist/instances.d.ts +49 -94
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +7 -7
- package/dist/pbr-pipeline.d.ts +19 -1
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/single-layer-medium-passes.d.ts +73 -0
- package/dist/pipeline/single-layer-medium-passes.d.ts.map +1 -0
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
- package/dist/pipeline/standard-output/types.d.ts +4 -2
- package/dist/pipeline/standard-output/types.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +28 -19
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +1 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +2 -0
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/barrel-distortion-frame.d.ts +15 -0
- package/dist/record/barrel-distortion-frame.d.ts.map +1 -0
- package/dist/record/dynamic-geometry-consumption.d.ts +10 -2
- package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -1
- package/dist/record/fold-buckets.d.ts +95 -0
- package/dist/record/fold-buckets.d.ts.map +1 -0
- package/dist/record/fold-instance-buffer.d.ts +6 -0
- package/dist/record/fold-instance-buffer.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +1 -0
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +40 -3
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +14 -62
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-draw-receipts.d.ts +11 -0
- package/dist/record/gpu-draw-receipts.d.ts.map +1 -0
- package/dist/record/gpu-timing.d.ts +8 -0
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +21 -5
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +7 -1
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +13 -2
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +6 -5
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/motion-blur-receipt.d.ts +11 -0
- package/dist/record/motion-blur-receipt.d.ts.map +1 -0
- package/dist/record/probe-blend-buffer.d.ts +16 -0
- package/dist/record/probe-blend-buffer.d.ts.map +1 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +34 -31
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -0
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +6 -4
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +22 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +11 -3
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/record/volume-params.d.ts +9 -0
- package/dist/record/volume-params.d.ts.map +1 -0
- package/dist/recovery/render-system-candidate.d.ts +10 -0
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +66 -8
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +106 -3
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +3 -2
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +24 -10
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-presentation.d.ts +23 -0
- package/dist/render-system-presentation.d.ts.map +1 -0
- package/dist/render-system-producer-roots.d.ts +10 -0
- package/dist/render-system-producer-roots.d.ts.map +1 -0
- package/dist/render-system-residency.d.ts +12 -0
- package/dist/render-system-residency.d.ts.map +1 -0
- package/dist/render-system.d.ts +26 -15
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/probe-blend.d.ts +27 -0
- package/dist/scene/probe-blend.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +14 -5
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/ssao-buffers.d.ts +9 -0
- package/dist/ssao-buffers.d.ts.map +1 -1
- package/dist/surface/dynamic-input.d.ts +133 -0
- package/dist/surface/dynamic-input.d.ts.map +1 -0
- package/dist/surface/gpu-driven.d.ts +41 -0
- package/dist/surface/gpu-driven.d.ts.map +1 -0
- package/dist/surface/submission-observation.d.ts +136 -0
- package/dist/surface/submission-observation.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +7 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts +13 -1
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/inspection.d.ts.map +1 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/target-coverage-attachment.d.ts.map +1 -1
- package/dist/temporal/view.d.ts +6 -1
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/transmission/single-layer-medium.d.ts +89 -0
- package/dist/transmission/single-layer-medium.d.ts.map +1 -0
- package/dist/typed-render-graph-primitives.d.ts +24 -9
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +9 -5
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/extract.d.ts +1 -1
- package/dist/volume/extract.d.ts.map +1 -1
- package/dist/volume/inspection.d.ts +2 -0
- package/dist/volume/inspection.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +4 -1
- package/dist/volume/passes.d.ts.map +1 -1
- package/dist/volume/resources.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +3 -1
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +22 -21
- package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
- package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
- package/src/__tests__/atmosphere-daylight.dawn.test.ts +72 -0
- package/src/__tests__/barrel-distortion-lkg.integration.test.ts +353 -0
- package/src/__tests__/barrel-distortion.dawn.test.ts +41 -0
- package/src/__tests__/barrel-distortion.unit.test.ts +364 -0
- package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
- package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
- package/src/__tests__/construct-renderer-identity.unit.test.ts +49 -0
- package/src/__tests__/detached-recovery-graph.unit.test.ts +277 -0
- package/src/__tests__/device-loss-fanout.unit.test.ts +36 -1
- package/src/__tests__/docs-gate-green.unit.test.ts +112 -1
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
- package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
- package/src/__tests__/environment-selection.unit.test.ts +12 -0
- package/src/__tests__/factory-contract.integration.test.ts +626 -113
- package/src/__tests__/feature-depth-input.dawn.test.ts +129 -2
- package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
- package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +14 -3
- package/src/__tests__/gpu-dirty-ranges.unit.test.ts +22 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +16 -3
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +12 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
- package/src/__tests__/gpu-driven-material-artifacts.unit.test.ts +143 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +2 -2
- package/src/__tests__/gpu-driven-production.integration.test.ts +482 -3
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +7 -6
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +103 -4
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +18 -2
- package/src/__tests__/gpu-driven-view.browser.test.ts +5 -8
- package/src/__tests__/gpu-driven-view.dawn.test.ts +6 -2
- package/src/__tests__/gpu-driven-view.unit.test.ts +1 -1
- package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +1 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -4
- package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +1 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
- package/src/__tests__/gpu-scene.unit.test.ts +210 -9
- package/src/__tests__/instance-allocation-failure.unit.test.ts +57 -0
- package/src/__tests__/instances-culling.integration.test.ts +10 -36
- package/src/__tests__/instances-store.unit.test.ts +105 -238
- package/src/__tests__/instances-world-ownership.integration.test.ts +62 -0
- package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +146 -0
- package/src/__tests__/material-shader-prewarm.unit.test.ts +59 -0
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +39 -1
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
- package/src/__tests__/morph-culling-reentry.dawn.test.ts +5 -5
- package/src/__tests__/morph-visual-evidence.integration.test.ts +8 -7
- package/src/__tests__/motion-blur-compute.dawn.test.ts +589 -0
- package/src/__tests__/motion-blur.pixel.integration.test.ts +2 -2
- package/src/__tests__/nineslice-handle-surface.unit.test.ts +4 -4
- package/src/__tests__/pbr-view-layout.unit.test.ts +9 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +58 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +21 -0
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +66 -2
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
- package/src/__tests__/probe-bindings.unit.test.ts +582 -1
- package/src/__tests__/probe-scene-dirty.integration.test.ts +28 -1
- package/src/__tests__/public-surface.test-d.ts +8 -0
- package/src/__tests__/receipt-observation-retirement.unit.test.ts +65 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +69 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +9 -0
- package/src/__tests__/render-feature-diagnostics.unit.test.ts +15 -2
- package/src/__tests__/render-feature-error-reporting.unit.test.ts +44 -0
- package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +10 -1
- package/src/__tests__/render-feature-retry.unit.test.ts +12 -1
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
- package/src/__tests__/render-scene-mixed-updates.integration.test.ts +145 -19
- package/src/__tests__/render-scene-projection.unit.test.ts +36 -11
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +37 -6
- package/src/__tests__/render-target-probe-soak.perf.test.ts +2 -2
- package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +3 -3
- package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +5 -2
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +41 -7
- package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +16 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +33 -11
- package/src/__tests__/resource-allocation.unit.test.ts +123 -0
- package/src/__tests__/shader-manifest-fixture.ts +4 -4
- package/src/__tests__/single-layer-medium-passes.unit.test.ts +275 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +70 -1
- package/src/__tests__/skin-palette-recycling.dawn.test.ts +97 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +17 -0
- package/src/__tests__/ssr-carrier-public-contract.unit.test.ts +21 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +4 -4
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +2 -2
- package/src/__tests__/ssr-gpu-dispatch.ts +1 -1
- package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +6 -2
- package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
- package/src/__tests__/standard-output-chain-contract.unit.test.ts +28 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +46 -1
- package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
- package/src/__tests__/standard-pipeline.integration.test.ts +200 -9
- package/src/__tests__/surface-dynamic-buffer-lifecycle.unit.test.ts +506 -0
- package/src/__tests__/surface-submission-observation.unit.test.ts +812 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +32 -0
- package/src/__tests__/temporal-performance.integration.test.ts +3 -3
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
- package/src/__tests__/temporal-view.unit.test.ts +2 -1
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
- package/src/__tests__/visibility-instances.integration.test.ts +7 -19
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +9 -20
- package/src/__tests__/volume-optics-oracle.unit.test.ts +7 -3
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +189 -15
- package/src/__tests__/volumetric-fog-errors.unit.test.ts +4 -3
- package/src/__tests__/volumetric-fog-multiple.unit.test.ts +55 -0
- package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +1 -1
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +558 -68
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +23 -5
- package/src/assembly/analytic-fog-registration.ts +14 -0
- package/src/assembly/backend-contract.ts +30 -0
- package/src/assembly/barrel-distortion-inspection.ts +22 -0
- package/src/assembly/canvas-draw-guard.ts +11 -0
- package/src/assembly/cloud-shadow-entry.ts +22 -0
- package/src/assembly/dynamic-geometry-host.ts +65 -37
- package/src/assembly/dynamic-geometry-runtime.ts +7 -1
- package/src/assembly/factory.ts +2 -30
- package/src/assembly/fxaa-registration.ts +17 -0
- package/src/assembly/host-contract.ts +3 -3
- package/src/assembly/lod-observation.ts +12 -0
- package/src/assembly/material/assembly.ts +25 -13
- package/src/assembly/material/pipeline-helpers.ts +53 -12
- package/src/assembly/material-shader-policy.ts +55 -0
- package/src/assembly/material-shader-prewarm.ts +21 -10
- package/src/assembly/motion-blur-registration.ts +17 -0
- package/src/assembly/recovery/device-loss-fanout.ts +13 -0
- package/src/assembly/recovery/renderer-recover.ts +41 -18
- package/src/assembly/render-feature-post-processes.ts +54 -0
- package/src/assembly/renderer-facade.ts +6 -1
- package/src/assembly/renderer-feature-inspection.ts +8 -2
- package/src/assembly/renderer-frame-transaction.ts +23 -0
- package/src/assembly/renderer-inspection.ts +28 -0
- package/src/assembly/skin-palette-owner.ts +1 -1
- package/src/assembly/temporal-post-process-prewarm.ts +40 -0
- package/src/assembly/webgpu-pbr-ready.ts +9 -1
- package/src/assembly/webgpu-ready-contract.ts +8 -3
- package/src/assembly/webgpu-ready-per-pass.ts +104 -0
- package/src/assembly/webgpu-ready.ts +296 -306
- package/src/assembly/webgpu-renderer-bootstrap.ts +0 -12
- package/src/assembly/webgpu-renderer-observation-owner.ts +222 -0
- package/src/assembly/webgpu-renderer.ts +228 -281
- package/src/assembly/webgpu-vertex-layouts.ts +1 -1
- package/src/barrel-distortion.ts +247 -0
- package/src/bloom-admission.ts +21 -0
- package/src/cloud/__tests__/cloud-density-parity.dawn.test.ts +173 -0
- package/src/cloud/__tests__/cloud-layer-framebuffer-y.dawn.test.ts +265 -0
- package/src/cloud/__tests__/cloud-layer.browser.test.ts +432 -0
- package/src/cloud/__tests__/cloud-layer.unit.test.ts +634 -0
- package/src/cloud/density.ts +600 -0
- package/src/cloud/extract.ts +110 -0
- package/src/cloud/feature.ts +1810 -0
- package/src/cloud/frame-context.ts +42 -0
- package/src/cloud/inspection.ts +130 -0
- package/src/cloud/optics.ts +285 -0
- package/src/cloud/parameters.ts +255 -0
- package/src/cloud/resources.ts +122 -0
- package/src/cloud/shadow.ts +167 -0
- package/src/cloud/temporal.ts +226 -0
- package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
- package/src/components/atmosphere.ts +4 -0
- package/src/components/barrel-distortion.ts +46 -0
- package/src/components/camera.ts +66 -10
- package/src/components/cloud-layer.ts +47 -0
- package/src/components/index.ts +2 -0
- package/src/components/instances.ts +18 -45
- package/src/components/mesh-filter.ts +11 -9
- package/src/components/motion-blur.ts +1 -0
- package/src/components/sprite-instances.ts +1 -1
- package/src/construct-renderer.ts +1 -1
- package/src/device/gpu-residency.ts +56 -12
- package/src/dynamic-geometry.ts +12 -1
- package/src/environment/analytic-fog.ts +44 -0
- package/src/environment/background.ts +2 -2
- package/src/errors/cloud.ts +103 -0
- package/src/errors/render.ts +58 -8
- package/src/examples/single-layer-medium-surface.ts +59 -0
- package/src/extract/camera.ts +35 -8
- package/src/extract/contracts.ts +12 -0
- package/src/extract/environment.ts +4 -0
- package/src/features/barrel-distortion.ts +172 -0
- package/src/features/error-reporting.ts +98 -0
- package/src/features/host.ts +138 -24
- package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
- package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
- package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
- package/src/features/motion-blur/motion-blur-params.ts +70 -2
- package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
- package/src/features/plan.ts +33 -8
- package/src/features/prepared-gpu-work.ts +86 -35
- package/src/features/render-graph-compute.ts +13 -5
- package/src/features/render-graph-contribution.ts +11 -0
- package/src/features/render-graph-raster.ts +45 -3
- package/src/features/targets.ts +66 -0
- package/src/features/types.ts +47 -0
- package/src/fullscreen-post-process-pass.ts +41 -9
- package/src/gpu-dirty-ranges.ts +48 -0
- package/src/gpu-driven/batch-topology.ts +53 -0
- package/src/gpu-driven/material-artifacts.ts +35 -32
- package/src/gpu-driven/prepared-draw.ts +27 -14
- package/src/gpu-driven/production-raster-material.ts +1894 -0
- package/src/gpu-driven/production-raster.ts +904 -787
- package/src/gpu-driven/resource-allocation.ts +142 -0
- package/src/gpu-driven/shadow-ownership.ts +71 -0
- package/src/gpu-driven/view-gpu.ts +181 -10
- package/src/gpu-scene.ts +259 -101
- package/src/hdrp-buffers.ts +4 -3
- package/src/ibl/skylight-bind-group.ts +49 -7
- package/src/index.ts +228 -9
- package/src/inspection-types.ts +222 -2
- package/src/instance-buffer-cache.ts +4 -4
- package/src/instances.ts +151 -492
- package/src/pbr-pipeline.ts +219 -13
- package/src/pipeline/single-layer-medium-passes.ts +267 -0
- package/src/pipeline/standard-forward-lane.ts +313 -57
- package/src/pipeline/standard-output/graph.ts +8 -2
- package/src/pipeline/standard-output/types.ts +12 -1
- package/src/pipeline/standard-pipeline.ts +291 -18
- package/src/pipeline/standard-post.ts +397 -102
- package/src/pipeline-builder.ts +1 -1
- package/src/pipeline-spec.ts +5 -5
- package/src/plugin.ts +4 -0
- package/src/points-lines/__tests__/points-lines.bench.ts +17 -8
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
- package/src/prepare/prepared-graphics-resolver.ts +2 -0
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +30 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +1 -1
- package/src/record/__tests__/temporal-payload.unit.test.ts +3 -2
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +11 -0
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +4 -3
- package/src/record/barrel-distortion-frame.ts +110 -0
- package/src/record/dynamic-geometry-consumption.ts +39 -9
- package/src/record/fold-buckets.ts +852 -0
- package/src/record/fold-instance-buffer.ts +51 -0
- package/src/record/frame-lighting.ts +36 -2
- package/src/record/frame-snapshot.ts +44 -4
- package/src/record/frame-targets.ts +10 -6
- package/src/record/frame.ts +373 -1099
- package/src/record/gpu-draw-receipts.ts +38 -0
- package/src/record/gpu-timing.ts +48 -5
- package/src/record/main-pass-geometry.ts +281 -83
- package/src/record/main-pass-material.ts +66 -4
- package/src/record/main-pass-sprite-draws.ts +13 -66
- package/src/record/main-pass.ts +156 -20
- package/src/record/mesh-ssbo.ts +43 -6
- package/src/record/motion-blur-receipt.ts +91 -0
- package/src/record/probe-blend-buffer.ts +180 -0
- package/src/record/recovery-pipeline.ts +35 -3
- package/src/record/render-context.ts +51 -31
- package/src/record/shadow-pass.ts +36 -2
- package/src/record/skybox-post-pass.ts +150 -200
- package/src/record/typed-frame-graph.ts +824 -44
- package/src/record/view-ubo.ts +26 -3
- package/src/record/volume-params.ts +225 -0
- package/src/recovery/render-system-candidate.ts +257 -52
- package/src/reflection/record-owner.ts +7 -3
- package/src/render-contract.ts +71 -8
- package/src/render-graph-primitives.ts +7 -1
- package/src/render-pipeline.ts +136 -1
- package/src/render-system-extract-tail.ts +72 -60
- package/src/render-system-extract.ts +117 -87
- package/src/render-system-presentation.ts +56 -0
- package/src/render-system-producer-roots.ts +96 -0
- package/src/render-system-residency.ts +159 -0
- package/src/render-system.ts +601 -482
- package/src/scene/probe-blend.ts +96 -4
- package/src/scene/render-scene.ts +431 -281
- package/src/ssao-buffers.ts +95 -2
- package/src/surface/__tests__/dynamic-input.unit.test.ts +374 -0
- package/src/surface/__tests__/gpu-driven.unit.test.ts +87 -0
- package/src/surface/dynamic-input.ts +658 -0
- package/src/surface/gpu-driven.ts +147 -0
- package/src/surface/submission-observation.ts +517 -0
- package/src/systems/skin-palette-allocator.ts +4 -1
- package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
- package/src/temporal/frame-coordinator.ts +8 -1
- package/src/temporal/frame.ts +20 -0
- package/src/temporal/gpu.ts +101 -17
- package/src/temporal/inspection.ts +7 -1
- package/src/temporal/standard-scene-data.ts +5 -0
- package/src/temporal/target-coverage-attachment.ts +4 -0
- package/src/temporal/view.ts +13 -2
- package/src/transmission/__tests__/single-layer-medium.unit.test.ts +149 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +12 -11
- package/src/transmission/single-layer-medium.ts +322 -0
- package/src/typed-render-graph-primitives.ts +191 -50
- package/src/volume/component.ts +10 -10
- package/src/volume/extract.ts +11 -5
- package/src/volume/inspection.ts +3 -0
- package/src/volume/passes.ts +95 -8
- package/src/volume/resources.ts +2 -1
- package/src/volume/temporal.ts +11 -0
- package/dist/chunk-4NNZQCL3.mjs.map +0 -1
- package/dist/chunk-6TFBXLRJ.mjs.map +0 -1
- package/dist/chunk-DRR7D3KX.mjs.map +0 -1
- package/dist/chunk-GWCEMDVR.mjs.map +0 -1
- package/dist/chunk-JCIRYQ4V.mjs.map +0 -1
- package/dist/chunk-JRIPCMYC.mjs.map +0 -1
- package/dist/chunk-LULCA3OF.mjs +0 -1713
- package/dist/chunk-LULCA3OF.mjs.map +0 -1
- package/dist/chunk-PA5DRJNI.mjs.map +0 -1
|
@@ -3,6 +3,8 @@ import { createShaderModule, rhi } from '@forgeax/engine-rhi-webgpu';
|
|
|
3
3
|
import { buildEngineShaderManifest } from '@forgeax/engine-vite-plugin-shader';
|
|
4
4
|
import { describe, expect, it } from 'vitest';
|
|
5
5
|
import { BYTES_PER_DIRECT_LIGHT_SLOT } from '../light-buffer-layout';
|
|
6
|
+
import { VIEW_UNIFORM_BYTES } from '../record/view-ubo';
|
|
7
|
+
import { VOLUMETRIC_FOG_PARAMS_BYTES } from '../volume/resources';
|
|
6
8
|
|
|
7
9
|
const MAP_READ = 0x0001;
|
|
8
10
|
const BUFFER_COPY_DST = 0x0008;
|
|
@@ -19,6 +21,13 @@ const BYTES_PER_PIXEL = 8;
|
|
|
19
21
|
const MAX_CLUSTER_LIGHTS = 256;
|
|
20
22
|
const CLUSTER_LIGHT_DATA_BYTES = MAX_CLUSTER_LIGHTS * BYTES_PER_DIRECT_LIGHT_SLOT;
|
|
21
23
|
const CLUSTER_UNIFORM_BYTES = 32;
|
|
24
|
+
const VOLUME_BOUNDS_MIN_Z = 0.1;
|
|
25
|
+
const VOLUME_BOUNDS_MAX_Z = 0.9;
|
|
26
|
+
const NON_UNIFORM_BOUNDS_MAX_Z = 8.1;
|
|
27
|
+
const MAX_NON_UNIFORM_BEER_ERROR = 0.001;
|
|
28
|
+
const NON_UNIFORM_EXTINCTION = 0.05;
|
|
29
|
+
const MIN_NON_UNIFORM_REFERENCE_SEPARATION = 0.05;
|
|
30
|
+
const MIN_NON_UNIFORM_GPU_SEPARATION = 0.05;
|
|
22
31
|
const engineManifest = await buildEngineShaderManifest();
|
|
23
32
|
|
|
24
33
|
interface StageValues {
|
|
@@ -33,6 +42,110 @@ interface VolumeStageReadbacks {
|
|
|
33
42
|
readonly integrate: StageValues;
|
|
34
43
|
}
|
|
35
44
|
|
|
45
|
+
interface StageReadbackOptions {
|
|
46
|
+
readonly owners?: number;
|
|
47
|
+
readonly secondDensity?: number;
|
|
48
|
+
readonly thinSeparated?: boolean;
|
|
49
|
+
readonly reverse?: boolean;
|
|
50
|
+
readonly sceneDepthClear?: number;
|
|
51
|
+
readonly densityByte?: number;
|
|
52
|
+
/** Optional RGBA8 density field for the production integrate stage. */
|
|
53
|
+
readonly densityData?: Uint8Array;
|
|
54
|
+
readonly boundsMinZ?: number;
|
|
55
|
+
readonly boundsMaxZ?: number;
|
|
56
|
+
readonly maxDistance?: number;
|
|
57
|
+
readonly viewDepthScale?: number;
|
|
58
|
+
readonly extinction?: number;
|
|
59
|
+
readonly frameIndex?: number;
|
|
60
|
+
/** Depth clear used by the stable shadow receiver fixture. */
|
|
61
|
+
readonly shadowDepthClear?: number;
|
|
62
|
+
/** Unshadowed punctual light isolates ray integration from packed visibility. */
|
|
63
|
+
readonly localLight?: 'point' | 'spot';
|
|
64
|
+
/** Uniform-step diagnostic; omitted means the untouched production shader. */
|
|
65
|
+
readonly integratorStepCount?: number;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface DensityField {
|
|
69
|
+
readonly data: Uint8Array;
|
|
70
|
+
readonly zRed: readonly number[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function createNonUniformDensityField(redValues?: readonly number[]): DensityField {
|
|
74
|
+
const data = new Uint8Array(4 * SIZE * SIZE * SIZE);
|
|
75
|
+
const zRed: number[] = [];
|
|
76
|
+
for (let z = 0; z < SIZE; z += 1) {
|
|
77
|
+
// Keep X/Y constant. The default field changes only the first Z texel;
|
|
78
|
+
// the fine-density probe supplies a faster, seam-matched sequence.
|
|
79
|
+
// The exercised camera ray crosses the first-to-second texel transition
|
|
80
|
+
// under all three production density scales, so flattening the field to
|
|
81
|
+
// its first texel produces a measurable, independently falsifiable result.
|
|
82
|
+
const red = redValues?.[z] ?? (z === 0 ? 128 : 255);
|
|
83
|
+
zRed.push(red / 255);
|
|
84
|
+
for (let y = 0; y < SIZE; y += 1) {
|
|
85
|
+
for (let x = 0; x < SIZE; x += 1) {
|
|
86
|
+
const offset = ((z * SIZE + y) * SIZE + x) * 4;
|
|
87
|
+
data[offset] = red;
|
|
88
|
+
data[offset + 1] = 255;
|
|
89
|
+
data[offset + 2] = 255;
|
|
90
|
+
data[offset + 3] = 255;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { data, zRed };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function createFlattenedDensityField(field: DensityField): DensityField {
|
|
98
|
+
const red = Math.round((field.zRed[0] ?? 0) * 255);
|
|
99
|
+
const data = new Uint8Array(4 * SIZE * SIZE * SIZE);
|
|
100
|
+
for (let z = 0; z < SIZE; z += 1) {
|
|
101
|
+
for (let y = 0; y < SIZE; y += 1) {
|
|
102
|
+
for (let x = 0; x < SIZE; x += 1) {
|
|
103
|
+
const offset = ((z * SIZE + y) * SIZE + x) * 4;
|
|
104
|
+
data[offset] = red;
|
|
105
|
+
data[offset + 1] = 255;
|
|
106
|
+
data[offset + 2] = 255;
|
|
107
|
+
data[offset + 3] = 255;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { data, zRed: Array.from({ length: SIZE }, () => red / 255) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function sampleClampedLinear(values: readonly number[], coordinate: number): number {
|
|
115
|
+
const scaled = coordinate * values.length - 0.5;
|
|
116
|
+
const lower = Math.floor(scaled);
|
|
117
|
+
const upper = lower + 1;
|
|
118
|
+
const interpolation = scaled - lower;
|
|
119
|
+
const clampIndex = (index: number): number => Math.max(0, Math.min(values.length - 1, index));
|
|
120
|
+
const lowerValue = values[clampIndex(lower)] ?? 0;
|
|
121
|
+
const upperValue = values[clampIndex(upper)] ?? 0;
|
|
122
|
+
return lowerValue + (upperValue - lowerValue) * interpolation;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function nonUniformScatteringDensity(field: DensityField, worldZ: number): number {
|
|
126
|
+
let grain = sampleClampedLinear(field.zRed, (worldZ * 0.1) % 1) + 0.5;
|
|
127
|
+
grain *= sampleClampedLinear(field.zRed, (worldZ * 0.05) % 1) + 0.5;
|
|
128
|
+
grain *= sampleClampedLinear(field.zRed, (worldZ * 0.02) % 1) + 0.5;
|
|
129
|
+
return Math.max(2 * grain - 1, 0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function nonUniformBeerReference(
|
|
133
|
+
field: DensityField,
|
|
134
|
+
extinction = 1,
|
|
135
|
+
sampleCount = 16_384,
|
|
136
|
+
boundsMinZ = VOLUME_BOUNDS_MIN_Z,
|
|
137
|
+
boundsMaxZ = VOLUME_BOUNDS_MAX_Z,
|
|
138
|
+
): number {
|
|
139
|
+
const interval = boundsMaxZ - boundsMinZ;
|
|
140
|
+
const segmentLength = interval / sampleCount;
|
|
141
|
+
let opticalDepth = 0;
|
|
142
|
+
for (let sample = 0; sample < sampleCount; sample += 1) {
|
|
143
|
+
const worldZ = boundsMinZ + (sample + 0.5) * segmentLength;
|
|
144
|
+
opticalDepth += nonUniformScatteringDensity(field, worldZ) * segmentLength;
|
|
145
|
+
}
|
|
146
|
+
return Math.exp(-extinction * opticalDepth);
|
|
147
|
+
}
|
|
148
|
+
|
|
36
149
|
function halfToFloat(bits: number): number {
|
|
37
150
|
const sign = (bits & 0x8000) === 0 ? 1 : -1;
|
|
38
151
|
const exponent = (bits >>> 10) & 0x1f;
|
|
@@ -42,12 +155,50 @@ function halfToFloat(bits: number): number {
|
|
|
42
155
|
return sign * 2 ** (exponent - 15) * (1 + fraction / 1024);
|
|
43
156
|
}
|
|
44
157
|
|
|
158
|
+
function firstStageValue(values: readonly number[], label: string): number {
|
|
159
|
+
const value = values[0];
|
|
160
|
+
if (value === undefined) throw new Error(`missing stage readback value: ${label}`);
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
|
|
45
164
|
function manifestShader(marker: string): string {
|
|
46
165
|
const entry = engineManifest.entries.find(({ wgsl }) => wgsl.includes(marker));
|
|
47
166
|
if (entry === undefined) throw new Error(`missing cooked shader entry: ${marker}`);
|
|
48
167
|
return entry.wgsl;
|
|
49
168
|
}
|
|
50
169
|
|
|
170
|
+
function productionIntegratorShader(stepCount?: number): string {
|
|
171
|
+
const source = manifestShader('fn volume_integrate');
|
|
172
|
+
if (stepCount === undefined) return source;
|
|
173
|
+
if (!Number.isInteger(stepCount) || stepCount < 1) {
|
|
174
|
+
throw new Error(`invalid production integrator step count: ${stepCount}`);
|
|
175
|
+
}
|
|
176
|
+
const functionStart = source.indexOf('fn volume_integrate');
|
|
177
|
+
if (functionStart < 0) throw new Error('production integrator function missing');
|
|
178
|
+
const prefix = source.slice(0, functionStart);
|
|
179
|
+
const body = source.slice(functionStart);
|
|
180
|
+
// The cooked manifest constant-folds the source-level loop count into the
|
|
181
|
+
// step-length divisor and loop guard. Replace both literals in that one
|
|
182
|
+
// production function to compile the same WGSL with a diagnostic schedule;
|
|
183
|
+
// an omitted override always exercises the unmodified production manifest.
|
|
184
|
+
// Diagnostic schedules disable local refinement to expose the old defect.
|
|
185
|
+
const productionCount = body.match(/< (\d+)u\b/)?.[1];
|
|
186
|
+
if (productionCount === undefined) throw new Error('production ray loop count missing');
|
|
187
|
+
const stepLengthMarker = new RegExp(`f32\\(${productionCount}u\\)`, 'g');
|
|
188
|
+
const loopGuardMarker = new RegExp(`< ${productionCount}u\\b`, 'g');
|
|
189
|
+
const stepLengthOccurrences = body.match(stepLengthMarker)?.length ?? 0;
|
|
190
|
+
const loopGuardOccurrences = body.match(loopGuardMarker)?.length ?? 0;
|
|
191
|
+
if (stepLengthOccurrences !== 1 || loopGuardOccurrences !== 1) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`production integrator step markers: length=${stepLengthOccurrences}, guard=${loopGuardOccurrences}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return `${prefix}${body
|
|
197
|
+
.replace(stepLengthMarker, `f32(${stepCount}u)`)
|
|
198
|
+
.replace(loopGuardMarker, `< ${stepCount}u`)
|
|
199
|
+
.replace(/select\(1u, 4u,/, 'select(1u, 1u,')} `;
|
|
200
|
+
}
|
|
201
|
+
|
|
51
202
|
function identityMatrix(target: Float32Array, offset: number): void {
|
|
52
203
|
target[offset] = 1;
|
|
53
204
|
target[offset + 5] = 1;
|
|
@@ -74,12 +225,19 @@ async function requestDevice(): Promise<RhiDevice> {
|
|
|
74
225
|
}
|
|
75
226
|
|
|
76
227
|
async function runInjectReadback(
|
|
77
|
-
|
|
78
|
-
densitySource: number | 'height-gradient' | 'depth-gradient' = 255,
|
|
79
|
-
heightOffset = 0,
|
|
80
|
-
frameIndex = 0,
|
|
81
|
-
slopedShadow = false,
|
|
228
|
+
options: StageReadbackOptions = {},
|
|
82
229
|
): Promise<VolumeStageReadbacks> {
|
|
230
|
+
const sceneDepthClear = options.sceneDepthClear ?? 1;
|
|
231
|
+
const densityByte = options.densityByte ?? 255;
|
|
232
|
+
const boundsMinZ = options.boundsMinZ ?? VOLUME_BOUNDS_MIN_Z;
|
|
233
|
+
const boundsMaxZ = options.boundsMaxZ ?? VOLUME_BOUNDS_MAX_Z;
|
|
234
|
+
const maxDistance = options.maxDistance ?? 2;
|
|
235
|
+
const viewDepthScale = options.viewDepthScale ?? 1;
|
|
236
|
+
const extinction = options.extinction ?? 1;
|
|
237
|
+
const frameIndex = options.frameIndex ?? 0;
|
|
238
|
+
const shadowDepthClear = options.shadowDepthClear ?? 1;
|
|
239
|
+
const integratorStepCount = options.integratorStepCount;
|
|
240
|
+
const owners = options.owners ?? 1;
|
|
83
241
|
const device = await requestDevice();
|
|
84
242
|
const moduleResult = await createShaderModule(device, {
|
|
85
243
|
code: manifestShader('fn volume_inject'),
|
|
@@ -91,7 +249,7 @@ async function runInjectReadback(
|
|
|
91
249
|
label: 'stage-readback.volume-temporal',
|
|
92
250
|
});
|
|
93
251
|
const integrateModule = await createShaderModule(device, {
|
|
94
|
-
code:
|
|
252
|
+
code: productionIntegratorShader(integratorStepCount),
|
|
95
253
|
label: 'stage-readback.volume-integrate',
|
|
96
254
|
});
|
|
97
255
|
if (!temporalModule.ok) throw temporalModule.error;
|
|
@@ -119,9 +277,17 @@ async function runInjectReadback(
|
|
|
119
277
|
usage: TEXTURE_BINDING | TEXTURE_COPY_DST,
|
|
120
278
|
textureBindingViewDimension: undefined,
|
|
121
279
|
});
|
|
280
|
+
const secondDensityTexture = device.createTexture({
|
|
281
|
+
label: 'stage-readback.second-density',
|
|
282
|
+
size: { width: SIZE, height: SIZE, depthOrArrayLayers: SIZE },
|
|
283
|
+
dimension: '3d',
|
|
284
|
+
format: 'rgba8unorm',
|
|
285
|
+
usage: TEXTURE_BINDING | TEXTURE_COPY_DST,
|
|
286
|
+
textureBindingViewDimension: undefined,
|
|
287
|
+
});
|
|
122
288
|
const outputTexture = device.createTexture({
|
|
123
289
|
label: 'stage-readback.froxel',
|
|
124
|
-
size: { width: SIZE, height: SIZE, depthOrArrayLayers: PACKED_LAYERS },
|
|
290
|
+
size: { width: SIZE, height: SIZE, depthOrArrayLayers: PACKED_LAYERS * owners },
|
|
125
291
|
dimension: '2d',
|
|
126
292
|
format: 'rgba8unorm',
|
|
127
293
|
usage: STORAGE_BINDING | TEXTURE_COPY_SRC | TEXTURE_BINDING,
|
|
@@ -166,6 +332,7 @@ async function runInjectReadback(
|
|
|
166
332
|
!sceneDepthTexture.ok ||
|
|
167
333
|
!shadowTexture.ok ||
|
|
168
334
|
!densityTexture.ok ||
|
|
335
|
+
!secondDensityTexture.ok ||
|
|
169
336
|
!outputTexture.ok ||
|
|
170
337
|
!historyTexture.ok ||
|
|
171
338
|
!temporalTexture.ok ||
|
|
@@ -179,6 +346,7 @@ async function runInjectReadback(
|
|
|
179
346
|
if (!sceneDepthView.ok || !shadowView.ok)
|
|
180
347
|
throw new Error('Dawn volume stage readback unavailable: depth view allocation failed');
|
|
181
348
|
const densityView = createView(device, densityTexture.value, '3d');
|
|
349
|
+
const secondDensityView = createView(device, secondDensityTexture.value, '3d');
|
|
182
350
|
const outputView = createView(device, outputTexture.value, '2d-array');
|
|
183
351
|
const historyView = createView(device, historyTexture.value, '2d');
|
|
184
352
|
const temporalView = createView(device, temporalTexture.value, '2d');
|
|
@@ -186,12 +354,12 @@ async function runInjectReadback(
|
|
|
186
354
|
const projectorView = createView(device, projectorTexture.value, '2d');
|
|
187
355
|
const viewBuffer = device.createBuffer({
|
|
188
356
|
label: 'stage-readback.view',
|
|
189
|
-
size:
|
|
357
|
+
size: VIEW_UNIFORM_BYTES,
|
|
190
358
|
usage: 0x0040 | BUFFER_COPY_DST,
|
|
191
359
|
});
|
|
192
360
|
const paramsBuffer = device.createBuffer({
|
|
193
361
|
label: 'stage-readback.params',
|
|
194
|
-
size:
|
|
362
|
+
size: VOLUMETRIC_FOG_PARAMS_BYTES,
|
|
195
363
|
usage: 0x0040 | BUFFER_COPY_DST,
|
|
196
364
|
});
|
|
197
365
|
const lightDataBuffer = device.createBuffer({
|
|
@@ -206,7 +374,7 @@ async function runInjectReadback(
|
|
|
206
374
|
});
|
|
207
375
|
const injectReadback = device.createBuffer({
|
|
208
376
|
label: 'stage-readback.inject',
|
|
209
|
-
size: BYTES_PER_ROW * SIZE * PACKED_LAYERS,
|
|
377
|
+
size: BYTES_PER_ROW * SIZE * PACKED_LAYERS * owners,
|
|
210
378
|
usage: MAP_READ | BUFFER_COPY_DST,
|
|
211
379
|
});
|
|
212
380
|
const temporalReadback = device.createBuffer({
|
|
@@ -230,30 +398,63 @@ async function runInjectReadback(
|
|
|
230
398
|
)
|
|
231
399
|
throw new Error('Dawn volume stage readback unavailable: readback allocation failed');
|
|
232
400
|
|
|
233
|
-
const viewData = new Float32Array(
|
|
401
|
+
const viewData = new Float32Array(VIEW_UNIFORM_BYTES / Float32Array.BYTES_PER_ELEMENT);
|
|
234
402
|
for (const offset of [0, 28, 44, 60, 76, 92, 196, 212]) identityMatrix(viewData, offset);
|
|
403
|
+
viewData[44 + 10] = viewDepthScale;
|
|
235
404
|
viewData[24] = 0;
|
|
236
|
-
viewData[25] =
|
|
237
|
-
viewData[44 + 13] = heightOffset;
|
|
405
|
+
viewData[25] = 0;
|
|
238
406
|
viewData[26] = -1;
|
|
239
|
-
|
|
407
|
+
// Enable one real shadow cascade. Otherwise the stability falsifier sees
|
|
408
|
+
// only unshadowed texels (the old overlong readback supplied fake zeros).
|
|
240
409
|
viewData[108] = 10;
|
|
241
|
-
|
|
410
|
+
viewData[124] = 1;
|
|
242
411
|
viewData[228] = 0.1;
|
|
243
412
|
viewData[229] = 10;
|
|
244
413
|
viewData[230] = 0;
|
|
245
414
|
viewData[235] = 1;
|
|
246
|
-
const paramsData = new Float32Array(
|
|
247
|
-
paramsData.set([-1,
|
|
248
|
-
paramsData.set([1,
|
|
249
|
-
paramsData.set([
|
|
415
|
+
const paramsData = new Float32Array(VOLUMETRIC_FOG_PARAMS_BYTES / 4);
|
|
416
|
+
paramsData.set([-1, -1, boundsMinZ, 0], 0);
|
|
417
|
+
paramsData.set([1, 1, boundsMaxZ, 0], 4);
|
|
418
|
+
paramsData.set([extinction, extinction, extinction, 0], 8);
|
|
250
419
|
paramsData.set([1, 1, 1, 0], 12);
|
|
251
420
|
paramsData.set([0, 0, 0, 0], 16);
|
|
252
|
-
paramsData.set([0, -1, 0,
|
|
421
|
+
paramsData.set([0, -1, 0, frameIndex], 20);
|
|
253
422
|
paramsData.set([1, 1, 1, 0], 24);
|
|
254
|
-
paramsData.set([
|
|
255
|
-
|
|
423
|
+
paramsData.set([maxDistance, 0, 0, 0], 28);
|
|
424
|
+
// `simulation.w` is the renderer-owned selected-sun cloud transmittance
|
|
425
|
+
// carrier. A fixture without a CloudLayer must retain the unshadowed
|
|
426
|
+
// #3197 baseline instead of reading the typed-array zero value.
|
|
427
|
+
paramsData[35] = 1;
|
|
256
428
|
const lightData = new Uint8Array(CLUSTER_LIGHT_DATA_BYTES);
|
|
429
|
+
if (options.localLight !== undefined) {
|
|
430
|
+
paramsData[30] = options.localLight === 'point' ? 1 : 2;
|
|
431
|
+
const light = new Float32Array(lightData.buffer);
|
|
432
|
+
light.set([0, 0, 5.37, 0, 1, 1, 1, Math.cos(Math.PI / 8), 0, 0, -1, Math.cos(Math.PI / 6)]);
|
|
433
|
+
// Zero shadow intensity makes this a pure punctual-light integral. No
|
|
434
|
+
// projected image or shadow map can hide an undersampled light peak.
|
|
435
|
+
new Uint32Array(lightData.buffer).set(
|
|
436
|
+
[options.localLight === 'point' ? 0 : 1, 0xffffffff, 0xffffffff, 0xffffffff],
|
|
437
|
+
16,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
for (let index = 0; index < owners; index++)
|
|
441
|
+
paramsData.set(paramsData.slice(0, 36), 36 * (index + 1));
|
|
442
|
+
paramsData[3] = owners;
|
|
443
|
+
if (options.secondDensity !== undefined || options.thinSeparated) {
|
|
444
|
+
for (let index = 0; index < owners; index++) paramsData[36 * (index + 1) + 7] = 1;
|
|
445
|
+
}
|
|
446
|
+
if (options.thinSeparated) {
|
|
447
|
+
paramsData[38] = 0.1;
|
|
448
|
+
paramsData[42] = 0.101;
|
|
449
|
+
paramsData[74] = 0.899;
|
|
450
|
+
paramsData[78] = 0.9;
|
|
451
|
+
for (const offset of [44, 80]) paramsData.set([100, 100, 100], offset);
|
|
452
|
+
}
|
|
453
|
+
if (options.reverse) {
|
|
454
|
+
const first = paramsData.slice(36, 72);
|
|
455
|
+
paramsData.copyWithin(36, 72, 108);
|
|
456
|
+
paramsData.set(first, 72);
|
|
457
|
+
}
|
|
257
458
|
const clusterUniformData = new Float32Array(8);
|
|
258
459
|
const clusterGrid = new Uint32Array(clusterUniformData.buffer);
|
|
259
460
|
clusterGrid[0] = 1;
|
|
@@ -270,22 +471,12 @@ async function runInjectReadback(
|
|
|
270
471
|
);
|
|
271
472
|
if (!viewWrite.ok || !paramsWrite.ok || !lightDataWrite.ok || !clusterUniformWrite.ok)
|
|
272
473
|
throw new Error('Dawn volume stage readback unavailable: uniform upload failed');
|
|
273
|
-
const densityData =
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
? Math.round((y / (SIZE - 1)) * 255)
|
|
280
|
-
: densitySource === 'depth-gradient'
|
|
281
|
-
? Math.round((z / (SIZE - 1)) * 255)
|
|
282
|
-
: densitySource;
|
|
283
|
-
densityData.fill(
|
|
284
|
-
value,
|
|
285
|
-
((z * SIZE + y) * SIZE + x) * 4,
|
|
286
|
-
((z * SIZE + y) * SIZE + x + 1) * 4,
|
|
287
|
-
);
|
|
288
|
-
}
|
|
474
|
+
const densityData =
|
|
475
|
+
options.densityData ?? new Uint8Array((BYTES_PER_PIXEL / 2) * SIZE * SIZE * SIZE);
|
|
476
|
+
if (options.densityData === undefined) densityData.fill(densityByte);
|
|
477
|
+
if (densityData.length !== (BYTES_PER_PIXEL / 2) * SIZE * SIZE * SIZE) {
|
|
478
|
+
throw new Error(`Dawn volume stage readback density field length: ${densityData.length}`);
|
|
479
|
+
}
|
|
289
480
|
const densityWrite = device.queue.writeTexture(
|
|
290
481
|
{ texture: densityTexture.value },
|
|
291
482
|
densityData,
|
|
@@ -294,6 +485,13 @@ async function runInjectReadback(
|
|
|
294
485
|
);
|
|
295
486
|
if (!densityWrite.ok)
|
|
296
487
|
throw new Error('Dawn volume stage readback unavailable: density upload failed');
|
|
488
|
+
const secondDensityWrite = device.queue.writeTexture(
|
|
489
|
+
{ texture: secondDensityTexture.value },
|
|
490
|
+
new Uint8Array(densityData.length).fill(options.secondDensity ?? 255),
|
|
491
|
+
{ offset: 0, bytesPerRow: SIZE * 4, rowsPerImage: SIZE },
|
|
492
|
+
{ width: SIZE, height: SIZE, depthOrArrayLayers: SIZE },
|
|
493
|
+
);
|
|
494
|
+
if (!secondDensityWrite.ok) throw secondDensityWrite.error;
|
|
297
495
|
const projectorWrite = device.queue.writeTexture(
|
|
298
496
|
{ texture: projectorTexture.value },
|
|
299
497
|
new Uint8Array([255, 255, 255, 255]),
|
|
@@ -317,6 +515,7 @@ async function runInjectReadback(
|
|
|
317
515
|
},
|
|
318
516
|
{ binding: 7, visibility: 4, buffer: { type: 'read-only-storage' } },
|
|
319
517
|
{ binding: 8, visibility: 4, buffer: { type: 'uniform' } },
|
|
518
|
+
{ binding: 9, visibility: 4, texture: { sampleType: 'depth', viewDimension: '2d' } },
|
|
320
519
|
],
|
|
321
520
|
});
|
|
322
521
|
if (!layout.ok) throw layout.error;
|
|
@@ -383,6 +582,13 @@ async function runInjectReadback(
|
|
|
383
582
|
{ binding: 11, visibility: 4, buffer: { type: 'uniform' } },
|
|
384
583
|
{ binding: 12, visibility: 4, texture: { sampleType: 'float', viewDimension: '2d' } },
|
|
385
584
|
{ binding: 13, visibility: 4, sampler: { type: 'filtering' } },
|
|
585
|
+
{ binding: 16, visibility: 4, texture: { sampleType: 'float', viewDimension: '2d' } },
|
|
586
|
+
{ binding: 17, visibility: 4, sampler: { type: 'filtering' } },
|
|
587
|
+
...Array.from({ length: 7 }, (_, index) => ({
|
|
588
|
+
binding: 18 + index,
|
|
589
|
+
visibility: 4,
|
|
590
|
+
texture: { sampleType: 'float' as const, viewDimension: '3d' as const },
|
|
591
|
+
})),
|
|
386
592
|
],
|
|
387
593
|
});
|
|
388
594
|
if (!temporalLayout.ok) throw temporalLayout.error;
|
|
@@ -421,6 +627,7 @@ async function runInjectReadback(
|
|
|
421
627
|
{ binding: 6, resource: { kind: 'textureView', value: outputView } },
|
|
422
628
|
{ binding: 7, resource: { kind: 'buffer', value: { buffer: lightDataBuffer.value } } },
|
|
423
629
|
{ binding: 8, resource: { kind: 'buffer', value: { buffer: clusterUniformBuffer.value } } },
|
|
630
|
+
{ binding: 9, resource: { kind: 'textureView', value: shadowView.value } },
|
|
424
631
|
],
|
|
425
632
|
});
|
|
426
633
|
if (!bindings.ok) throw bindings.error;
|
|
@@ -443,7 +650,10 @@ async function runInjectReadback(
|
|
|
443
650
|
{ binding: 3, resource: { kind: 'buffer', value: { buffer: paramsBuffer.value } } },
|
|
444
651
|
{ binding: 4, resource: { kind: 'buffer', value: { buffer: viewBuffer.value } } },
|
|
445
652
|
{ binding: 5, resource: { kind: 'sampler', value: densitySampler.value } },
|
|
446
|
-
{
|
|
653
|
+
{
|
|
654
|
+
binding: 6,
|
|
655
|
+
resource: { kind: 'textureView', value: options.reverse ? secondDensityView : densityView },
|
|
656
|
+
},
|
|
447
657
|
{ binding: 7, resource: { kind: 'sampler', value: densitySampler.value } },
|
|
448
658
|
{ binding: 8, resource: { kind: 'textureView', value: historyView } },
|
|
449
659
|
{ binding: 9, resource: { kind: 'textureView', value: temporalView } },
|
|
@@ -451,6 +661,15 @@ async function runInjectReadback(
|
|
|
451
661
|
{ binding: 11, resource: { kind: 'buffer', value: { buffer: clusterUniformBuffer.value } } },
|
|
452
662
|
{ binding: 12, resource: { kind: 'textureView', value: projectorView } },
|
|
453
663
|
{ binding: 13, resource: { kind: 'sampler', value: densitySampler.value } },
|
|
664
|
+
{ binding: 16, resource: { kind: 'textureView', value: projectorView } },
|
|
665
|
+
{ binding: 17, resource: { kind: 'sampler', value: densitySampler.value } },
|
|
666
|
+
...Array.from({ length: 7 }, (_, index) => ({
|
|
667
|
+
binding: 18 + index,
|
|
668
|
+
resource: {
|
|
669
|
+
kind: 'textureView' as const,
|
|
670
|
+
value: index === 0 && !options.reverse ? secondDensityView : densityView,
|
|
671
|
+
},
|
|
672
|
+
})),
|
|
454
673
|
],
|
|
455
674
|
});
|
|
456
675
|
if (!temporalBindings.ok) throw temporalBindings.error;
|
|
@@ -462,7 +681,7 @@ async function runInjectReadback(
|
|
|
462
681
|
colorAttachments: [],
|
|
463
682
|
depthStencilAttachment: {
|
|
464
683
|
view,
|
|
465
|
-
depthClearValue: index === 0 ? sceneDepthClear :
|
|
684
|
+
depthClearValue: index === 0 ? sceneDepthClear : shadowDepthClear,
|
|
466
685
|
depthLoadOp: 'clear',
|
|
467
686
|
depthStoreOp: 'store',
|
|
468
687
|
},
|
|
@@ -472,12 +691,12 @@ async function runInjectReadback(
|
|
|
472
691
|
const compute = encoder.value.beginComputePass();
|
|
473
692
|
compute.setPipeline(pipeline.value);
|
|
474
693
|
compute.setBindGroup(0, bindings.value);
|
|
475
|
-
compute.dispatchWorkgroups(1, 1, PACKED_LAYERS);
|
|
694
|
+
compute.dispatchWorkgroups(1, 1, PACKED_LAYERS * owners);
|
|
476
695
|
compute.end();
|
|
477
696
|
encoder.value.copyTextureToBuffer(
|
|
478
697
|
{ texture: outputTexture.value as never },
|
|
479
698
|
{ buffer: injectReadback.value as never, bytesPerRow: BYTES_PER_ROW, rowsPerImage: SIZE },
|
|
480
|
-
{ width: SIZE, height: SIZE, depthOrArrayLayers: PACKED_LAYERS },
|
|
699
|
+
{ width: SIZE, height: SIZE, depthOrArrayLayers: PACKED_LAYERS * owners },
|
|
481
700
|
);
|
|
482
701
|
const integrateCompute = encoder.value.beginComputePass();
|
|
483
702
|
integrateCompute.setPipeline(integratePipeline.value);
|
|
@@ -537,28 +756,152 @@ async function runInjectReadback(
|
|
|
537
756
|
return { red, green, alpha };
|
|
538
757
|
};
|
|
539
758
|
return {
|
|
540
|
-
inject: await decode(injectReadback.value,
|
|
759
|
+
inject: await decode(injectReadback.value, PACKED_LAYERS * owners, 4, false),
|
|
541
760
|
temporal: await decode(temporalReadback.value, 1, BYTES_PER_PIXEL, true),
|
|
542
761
|
integrate: await decode(integrateReadback.value, 1, BYTES_PER_PIXEL, true),
|
|
543
762
|
};
|
|
544
763
|
}
|
|
545
764
|
|
|
765
|
+
// Independent radiative-transfer quadrature: a homogeneous medium and an
|
|
766
|
+
// unshadowed punctual source. The near-light peak and spot cone make the old
|
|
767
|
+
// 24 midpoint schedule visibly wrong even though its Beer transmittance passes.
|
|
768
|
+
function punctualReference(kind: 'point' | 'spot', samples = 16_384): number[] {
|
|
769
|
+
const sigma = 0.01 * (2 * (128 / 255 + 0.5) ** 3 - 1);
|
|
770
|
+
return Array.from({ length: SIZE * SIZE }, (_, pixel) => {
|
|
771
|
+
const x = (((pixel % SIZE) + 0.5) / SIZE) * 2 - 1;
|
|
772
|
+
const y = 1 - ((Math.floor(pixel / SIZE) + 0.5) / SIZE) * 2;
|
|
773
|
+
const ds = 20 / samples;
|
|
774
|
+
let radiance = 0;
|
|
775
|
+
for (let i = 0; i < samples; i += 1) {
|
|
776
|
+
const s = (i + 0.5) * ds;
|
|
777
|
+
const z = 0.1 + s;
|
|
778
|
+
const distanceSquared = x * x + y * y + (5.37 - z) ** 2;
|
|
779
|
+
const cosine = (5.37 - z) / Math.sqrt(distanceSquared);
|
|
780
|
+
const t = Math.max(
|
|
781
|
+
0,
|
|
782
|
+
Math.min(
|
|
783
|
+
1,
|
|
784
|
+
(cosine - Math.cos(Math.PI / 6)) / (Math.cos(Math.PI / 8) - Math.cos(Math.PI / 6)),
|
|
785
|
+
),
|
|
786
|
+
);
|
|
787
|
+
const cone = kind === 'point' ? 1 : t * t * (3 - 2 * t);
|
|
788
|
+
radiance += ((sigma * Math.exp(-sigma * s) * cone) / distanceSquared) * ds;
|
|
789
|
+
}
|
|
790
|
+
return radiance;
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
546
794
|
describe('volumetric fog production stage readback on Dawn', () => {
|
|
547
795
|
it('keeps static shadow visibility stable across renderer frame phases', async () => {
|
|
548
|
-
const first = await runInjectReadback(
|
|
549
|
-
const next = await runInjectReadback(1,
|
|
796
|
+
const first = await runInjectReadback({ frameIndex: 0, shadowDepthClear: 0.5 });
|
|
797
|
+
const next = await runInjectReadback({ frameIndex: 1, shadowDepthClear: 0.5 });
|
|
798
|
+
expect(first.inject.red).toHaveLength(SIZE * SIZE * PACKED_LAYERS);
|
|
799
|
+
expect(first.inject).toEqual(next.inject);
|
|
550
800
|
expect(first.inject.red.some((value) => value === 0)).toBe(true);
|
|
551
801
|
expect(first.inject.red.some((value) => value > 0.9)).toBe(true);
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
it(
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
it.each([
|
|
805
|
+
'point',
|
|
806
|
+
'spot',
|
|
807
|
+
] as const)('resolves the %s light peak without coherent ray bands', async (localLight) => {
|
|
808
|
+
const reference = punctualReference(localLight);
|
|
809
|
+
const refined = punctualReference(localLight, 32_768);
|
|
810
|
+
expect(Math.max(...reference.map((v, i) => Math.abs(v - (refined[i] ?? 0)) / v))).toBeLessThan(
|
|
811
|
+
0.0001,
|
|
812
|
+
);
|
|
813
|
+
const options = {
|
|
814
|
+
localLight,
|
|
815
|
+
densityByte: 128,
|
|
816
|
+
extinction: 0.01,
|
|
817
|
+
boundsMaxZ: 20.1,
|
|
818
|
+
maxDistance: 20.1,
|
|
819
|
+
viewDepthScale: 21,
|
|
820
|
+
};
|
|
821
|
+
const maximumRelativeError = (values: VolumeStageReadbacks): number =>
|
|
822
|
+
Math.max(
|
|
823
|
+
...values.integrate.red.map(
|
|
824
|
+
(v, i) => Math.abs(v - (reference[i] ?? 0)) / (reference[i] ?? 1),
|
|
825
|
+
),
|
|
826
|
+
);
|
|
827
|
+
const production = await runInjectReadback(options);
|
|
828
|
+
const undersampled = await runInjectReadback({ ...options, integratorStepCount: 24 });
|
|
829
|
+
const resolved = await runInjectReadback({ ...options, integratorStepCount: 96 });
|
|
830
|
+
const refinedGpu = await runInjectReadback({ ...options, integratorStepCount: 384 });
|
|
831
|
+
const errors = {
|
|
832
|
+
refined: maximumRelativeError(refinedGpu),
|
|
833
|
+
production: maximumRelativeError(production),
|
|
834
|
+
undersampled: maximumRelativeError(undersampled),
|
|
835
|
+
resolved: maximumRelativeError(resolved),
|
|
836
|
+
};
|
|
837
|
+
// biome-ignore lint/suspicious/noConsole: production GPU spatial regression evidence.
|
|
838
|
+
console.info('[volumetric-fog-stage-readback] punctual-convergence', localLight, errors);
|
|
839
|
+
expect(errors.undersampled).toBeGreaterThan(0.1);
|
|
840
|
+
expect(errors.refined).toBeLessThan(0.003);
|
|
841
|
+
expect(errors.production).toBeLessThan(0.03);
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('retains fine density variation away from punctual light refinement', async () => {
|
|
845
|
+
// Matching end texels avoid an artificial discontinuity at the repeated
|
|
846
|
+
// texture seam. The interior stripes expose coarse-step density aliasing.
|
|
847
|
+
const field = createNonUniformDensityField([0, 255, 0, 255, 0, 255, 0, 0]);
|
|
848
|
+
const reference = nonUniformBeerReference(field, 0.05, 16_384, 0.1, 20.1);
|
|
849
|
+
const refined = nonUniformBeerReference(field, 0.05, 32_768, 0.1, 20.1);
|
|
850
|
+
expect(Math.abs(reference - refined)).toBeLessThan(0.0001);
|
|
851
|
+
const options = {
|
|
852
|
+
densityData: field.data,
|
|
853
|
+
extinction: 0.05,
|
|
854
|
+
boundsMaxZ: 20.1,
|
|
855
|
+
maxDistance: 20.1,
|
|
856
|
+
viewDepthScale: 21,
|
|
857
|
+
};
|
|
858
|
+
const production = firstStageValue(
|
|
859
|
+
(await runInjectReadback(options)).integrate.alpha,
|
|
860
|
+
'fine density',
|
|
861
|
+
);
|
|
862
|
+
const coarse = firstStageValue(
|
|
863
|
+
(await runInjectReadback({ ...options, integratorStepCount: 24 })).integrate.alpha,
|
|
864
|
+
'coarse density',
|
|
865
|
+
);
|
|
866
|
+
// biome-ignore lint/suspicious/noConsole: independent fine-density regression evidence.
|
|
867
|
+
console.info('[volumetric-fog-stage-readback] fine-density', { reference, production, coarse });
|
|
868
|
+
expect(Math.abs(coarse - reference)).toBeGreaterThan(0.005);
|
|
869
|
+
expect(Math.abs(production - reference)).toBeLessThan(0.002);
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
it('samples independent density textures and is invariant under owner permutation', async () => {
|
|
873
|
+
const transparent = await runInjectReadback({
|
|
874
|
+
sceneDepthClear: 0.57,
|
|
875
|
+
owners: 2,
|
|
876
|
+
secondDensity: 0,
|
|
877
|
+
});
|
|
878
|
+
const half = await runInjectReadback({ sceneDepthClear: 0.57, owners: 2, secondDensity: 128 });
|
|
879
|
+
const reversed = await runInjectReadback({
|
|
880
|
+
sceneDepthClear: 0.57,
|
|
881
|
+
owners: 2,
|
|
882
|
+
secondDensity: 128,
|
|
883
|
+
reverse: true,
|
|
884
|
+
});
|
|
885
|
+
expect(transparent.integrate.alpha[0]).toBeCloseTo(Math.exp(-0.47), 3);
|
|
886
|
+
expect(half.integrate.alpha[0]).toBeCloseTo(Math.exp(-0.47 * (1 + 128 / 255)), 3);
|
|
887
|
+
expect(reversed.integrate.alpha).toEqual(half.integrate.alpha);
|
|
888
|
+
expect(reversed.integrate.red).toEqual(half.integrate.red);
|
|
889
|
+
});
|
|
890
|
+
it('does not miss thin local volumes separated by empty space', async () => {
|
|
891
|
+
const values = await runInjectReadback({ owners: 2, thinSeparated: true });
|
|
892
|
+
expect(values.integrate.alpha[0]).toBeCloseTo(Math.exp(-0.002 * 100), 3);
|
|
893
|
+
});
|
|
894
|
+
it('adds overlapping optical depth instead of rejecting or selecting one volume', async () => {
|
|
895
|
+
const one = await runInjectReadback({ sceneDepthClear: 0.57 });
|
|
896
|
+
const two = await runInjectReadback({ sceneDepthClear: 0.57, owners: 2 });
|
|
897
|
+
const expected = Math.exp(-0.47 * (2 * 1.5 ** 3 - 1) * 2);
|
|
898
|
+
expect(two.integrate.alpha[0]).toBeCloseTo(expected, 3);
|
|
899
|
+
expect(two.integrate.alpha[0]).toBeLessThan(one.integrate.alpha[0] ?? 0);
|
|
900
|
+
expect(two.integrate.red[0]).toBeGreaterThan(one.integrate.red[0] ?? 0);
|
|
901
|
+
});
|
|
902
|
+
it('integrates all eight admitted owners through the production parameter layout', async () => {
|
|
903
|
+
const values = await runInjectReadback({ sceneDepthClear: 0.57, owners: 8, extinction: 0.1 });
|
|
904
|
+
expect(values.integrate.alpha[0]).toBeCloseTo(Math.exp(-0.47 * (2 * 1.5 ** 3 - 1) * 0.8), 3);
|
|
562
905
|
});
|
|
563
906
|
it('injects raw density then resolves a 2d integrated volume', async () => {
|
|
564
907
|
const values = await runInjectReadback();
|
|
@@ -580,23 +923,170 @@ describe('volumetric fog production stage readback on Dawn', () => {
|
|
|
580
923
|
});
|
|
581
924
|
|
|
582
925
|
it('clips the final ray step to the scene-depth segment', async () => {
|
|
583
|
-
const values = await runInjectReadback(0.57);
|
|
926
|
+
const values = await runInjectReadback({ sceneDepthClear: 0.57 });
|
|
584
927
|
const transmittance = values.integrate.alpha[0];
|
|
585
|
-
//
|
|
586
|
-
|
|
928
|
+
// The fixture uploads an all-white source texture. The authored Three
|
|
929
|
+
// smoke expression therefore evaluates to 2 * 1.5^3 - 1 = 5.75 before
|
|
930
|
+
// Beer-Lambert consumes it; only the final 0.47-unit segment is allowed
|
|
931
|
+
// to contribute after the scene-depth clip.
|
|
932
|
+
const sourceDensity = 2 * 1.5 ** 3 - 1;
|
|
933
|
+
expect(transmittance).toBeCloseTo(Math.exp(-0.47 * sourceDensity), 2);
|
|
587
934
|
expect(values.integrate.red[0]).toBeGreaterThan(0);
|
|
588
935
|
});
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
936
|
+
|
|
937
|
+
it('keeps a zero medium transparent and applies Beer-Lambert monotonically', async () => {
|
|
938
|
+
const empty = await runInjectReadback({ densityByte: 0 });
|
|
939
|
+
const medium = await runInjectReadback({ densityByte: 128 });
|
|
940
|
+
const dense = await runInjectReadback({ densityByte: 255 });
|
|
941
|
+
const emptyScattering = firstStageValue(empty.integrate.red, 'empty scattering');
|
|
942
|
+
const denseScattering = firstStageValue(dense.integrate.red, 'dense scattering');
|
|
943
|
+
const mediumScattering = firstStageValue(medium.integrate.red, 'medium scattering');
|
|
944
|
+
const emptyTransmission = firstStageValue(empty.integrate.alpha, 'empty transmission');
|
|
945
|
+
const mediumTransmission = firstStageValue(medium.integrate.alpha, 'medium transmission');
|
|
946
|
+
const denseTransmission = firstStageValue(dense.integrate.alpha, 'dense transmission');
|
|
947
|
+
expect(emptyScattering).toBeCloseTo(0, 2);
|
|
948
|
+
expect(emptyTransmission).toBeCloseTo(1, 2);
|
|
949
|
+
expect(denseTransmission).toBeLessThan(mediumTransmission);
|
|
950
|
+
expect(mediumTransmission).toBeLessThan(emptyTransmission);
|
|
951
|
+
expect(denseScattering).toBeGreaterThan(mediumScattering);
|
|
593
952
|
});
|
|
594
953
|
|
|
595
|
-
it
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
expect(
|
|
954
|
+
it('keeps extinction monotonic and bounds temporal jitter spread', async () => {
|
|
955
|
+
const lowExtinction = await runInjectReadback({ densityByte: 128, extinction: 0.25 });
|
|
956
|
+
const highExtinction = await runInjectReadback({ densityByte: 128, extinction: 1 });
|
|
957
|
+
const lowTransmission = firstStageValue(lowExtinction.integrate.alpha, 'low extinction');
|
|
958
|
+
const highTransmission = firstStageValue(highExtinction.integrate.alpha, 'high extinction');
|
|
959
|
+
expect(highTransmission).toBeLessThan(lowTransmission);
|
|
960
|
+
|
|
961
|
+
const samples: number[] = [];
|
|
962
|
+
for (let frameIndex = 0; frameIndex < 4; frameIndex += 1) {
|
|
963
|
+
const value = await runInjectReadback({ densityByte: 128, frameIndex });
|
|
964
|
+
const transmission = firstStageValue(value.integrate.alpha, `frame ${frameIndex}`);
|
|
965
|
+
expect(Number.isFinite(transmission)).toBe(true);
|
|
966
|
+
samples.push(transmission);
|
|
967
|
+
}
|
|
968
|
+
const spread = Math.max(...samples) - Math.min(...samples);
|
|
969
|
+
// Frame ordinals must not change a fixed World-time density integral.
|
|
970
|
+
// This remains separate from continuous-frame history and spatial accuracy.
|
|
971
|
+
expect(spread).toBeLessThan(0.2);
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it('converges production step-count variants to the homogeneous Beer oracle', async () => {
|
|
975
|
+
const sceneDepthClear = 0.57;
|
|
976
|
+
const sourceDensity = 2 * 1.5 ** 3 - 1;
|
|
977
|
+
const oracle = Math.exp(-0.47 * sourceDensity);
|
|
978
|
+
const transmissions = await Promise.all(
|
|
979
|
+
[6, 12, 24].map(async (integratorStepCount) => {
|
|
980
|
+
const values = await runInjectReadback({
|
|
981
|
+
sceneDepthClear,
|
|
982
|
+
integratorStepCount,
|
|
983
|
+
});
|
|
984
|
+
return firstStageValue(values.integrate.alpha, `${integratorStepCount}-step transmission`);
|
|
985
|
+
}),
|
|
986
|
+
);
|
|
987
|
+
for (const transmission of transmissions) {
|
|
988
|
+
expect(transmission).toBeCloseTo(oracle, 2);
|
|
989
|
+
}
|
|
990
|
+
expect(Math.max(...transmissions) - Math.min(...transmissions)).toBeLessThan(0.01);
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
it('converges production steps against an independent non-uniform Beer reference', async () => {
|
|
994
|
+
const field = createNonUniformDensityField();
|
|
995
|
+
const flattenedField = createFlattenedDensityField(field);
|
|
996
|
+
const reference = nonUniformBeerReference(
|
|
997
|
+
field,
|
|
998
|
+
NON_UNIFORM_EXTINCTION,
|
|
999
|
+
16_384,
|
|
1000
|
+
VOLUME_BOUNDS_MIN_Z,
|
|
1001
|
+
NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1002
|
+
);
|
|
1003
|
+
const refinedReference = nonUniformBeerReference(
|
|
1004
|
+
field,
|
|
1005
|
+
NON_UNIFORM_EXTINCTION,
|
|
1006
|
+
32_768,
|
|
1007
|
+
VOLUME_BOUNDS_MIN_Z,
|
|
1008
|
+
NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1009
|
+
);
|
|
1010
|
+
const flattenedReference = nonUniformBeerReference(
|
|
1011
|
+
flattenedField,
|
|
1012
|
+
NON_UNIFORM_EXTINCTION,
|
|
1013
|
+
16_384,
|
|
1014
|
+
VOLUME_BOUNDS_MIN_Z,
|
|
1015
|
+
NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1016
|
+
);
|
|
1017
|
+
expect(Math.abs(reference - refinedReference)).toBeLessThan(MAX_NON_UNIFORM_BEER_ERROR / 10);
|
|
1018
|
+
expect(Math.abs(reference - flattenedReference)).toBeGreaterThan(
|
|
1019
|
+
MIN_NON_UNIFORM_REFERENCE_SEPARATION,
|
|
1020
|
+
);
|
|
1021
|
+
const stepCounts = [24, 48, 96] as const;
|
|
1022
|
+
const transmissions = await Promise.all(
|
|
1023
|
+
stepCounts.map(async (integratorStepCount) => {
|
|
1024
|
+
const values = await runInjectReadback({
|
|
1025
|
+
boundsMaxZ: NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1026
|
+
densityData: field.data,
|
|
1027
|
+
extinction: NON_UNIFORM_EXTINCTION,
|
|
1028
|
+
integratorStepCount,
|
|
1029
|
+
maxDistance: NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1030
|
+
viewDepthScale: 10,
|
|
1031
|
+
});
|
|
1032
|
+
return firstStageValue(
|
|
1033
|
+
values.integrate.alpha,
|
|
1034
|
+
`${integratorStepCount}-step non-uniform transmission`,
|
|
1035
|
+
);
|
|
1036
|
+
}),
|
|
1037
|
+
);
|
|
1038
|
+
const flattenedValues = await runInjectReadback({
|
|
1039
|
+
boundsMaxZ: NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1040
|
+
densityData: flattenedField.data,
|
|
1041
|
+
extinction: NON_UNIFORM_EXTINCTION,
|
|
1042
|
+
integratorStepCount: 96,
|
|
1043
|
+
maxDistance: NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1044
|
+
viewDepthScale: 10,
|
|
1045
|
+
});
|
|
1046
|
+
const flattenedTransmission = firstStageValue(
|
|
1047
|
+
flattenedValues.integrate.alpha,
|
|
1048
|
+
'flattened-field transmission',
|
|
1049
|
+
);
|
|
1050
|
+
const defaultValues = await runInjectReadback({
|
|
1051
|
+
boundsMaxZ: NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1052
|
+
densityData: field.data,
|
|
1053
|
+
extinction: NON_UNIFORM_EXTINCTION,
|
|
1054
|
+
maxDistance: NON_UNIFORM_BOUNDS_MAX_Z,
|
|
1055
|
+
viewDepthScale: 10,
|
|
1056
|
+
});
|
|
1057
|
+
const defaultTransmission = firstStageValue(
|
|
1058
|
+
defaultValues.integrate.alpha,
|
|
1059
|
+
'production transmission',
|
|
1060
|
+
);
|
|
1061
|
+
const errors = transmissions.map((transmission) => Math.abs(transmission - reference));
|
|
1062
|
+
const flattenedError = Math.abs(flattenedTransmission - flattenedReference);
|
|
1063
|
+
const defaultError = Math.abs(defaultTransmission - reference);
|
|
1064
|
+
// biome-ignore lint/suspicious/noConsole: non-uniform production convergence receipt.
|
|
1065
|
+
console.info(
|
|
1066
|
+
'[volumetric-fog-stage-readback] non-uniform-convergence',
|
|
1067
|
+
JSON.stringify({
|
|
1068
|
+
reference,
|
|
1069
|
+
refinedReference,
|
|
1070
|
+
flattenedReference,
|
|
1071
|
+
stepCounts,
|
|
1072
|
+
transmissions,
|
|
1073
|
+
errors,
|
|
1074
|
+
flattenedTransmission,
|
|
1075
|
+
flattenedError,
|
|
1076
|
+
defaultTransmission,
|
|
1077
|
+
defaultError,
|
|
1078
|
+
}),
|
|
1079
|
+
);
|
|
1080
|
+
for (const error of errors) expect(error).toBeLessThan(MAX_NON_UNIFORM_BEER_ERROR);
|
|
1081
|
+
expect(flattenedError).toBeLessThan(MAX_NON_UNIFORM_BEER_ERROR);
|
|
1082
|
+
expect(defaultError).toBeLessThan(MAX_NON_UNIFORM_BEER_ERROR);
|
|
1083
|
+
expect(Math.abs((transmissions[2] ?? NaN) - flattenedTransmission)).toBeGreaterThan(
|
|
1084
|
+
MIN_NON_UNIFORM_GPU_SEPARATION,
|
|
1085
|
+
);
|
|
1086
|
+
const spread = Math.max(...transmissions) - Math.min(...transmissions);
|
|
1087
|
+
// rgba16float readback quantizes nearby step variants onto the same value,
|
|
1088
|
+
// so strict error ordering is not a stable production contract. Keep the
|
|
1089
|
+
// independent reference and require a bounded cross-step spread instead.
|
|
1090
|
+
expect(spread).toBeLessThan(MAX_NON_UNIFORM_BEER_ERROR);
|
|
601
1091
|
});
|
|
602
1092
|
});
|