@forgeax/engine-render 0.1.21 → 0.1.23
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 +150 -29
- package/dist/assembly/backend-contract.d.ts +13 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/factory.d.ts +14 -6
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +10 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/authoring.mjs +1 -1
- package/dist/{chunk-PCHBPYML.mjs → chunk-4L3P6DT5.mjs} +2 -2
- package/dist/chunk-4L3P6DT5.mjs.map +1 -0
- package/dist/{chunk-VFPLTKCU.mjs → chunk-4NPUASZR.mjs} +15 -4
- package/dist/chunk-4NPUASZR.mjs.map +1 -0
- package/dist/chunk-DIYW3M5D.mjs +5085 -0
- package/dist/chunk-DIYW3M5D.mjs.map +1 -0
- package/dist/chunk-FBC4R44J.mjs +1003 -0
- package/dist/chunk-FBC4R44J.mjs.map +1 -0
- package/dist/{chunk-FS2KNUQH.mjs → chunk-FF7K3FSO.mjs} +253 -489
- package/dist/chunk-FF7K3FSO.mjs.map +1 -0
- package/dist/chunk-HZUPSYW7.mjs +1104 -0
- package/dist/chunk-HZUPSYW7.mjs.map +1 -0
- package/dist/{chunk-6OPXDQ2E.mjs → chunk-QODA5UKU.mjs} +33 -294
- package/dist/chunk-QODA5UKU.mjs.map +1 -0
- package/dist/chunk-YALPS44A.mjs +296 -0
- package/dist/chunk-YALPS44A.mjs.map +1 -0
- package/dist/{chunk-5YADUCA7.mjs → chunk-ZUWKAZ2I.mjs} +585 -991
- package/dist/chunk-ZUWKAZ2I.mjs.map +1 -0
- package/dist/cluster-binner.d.ts +5 -0
- package/dist/cluster-binner.d.ts.map +1 -1
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts +4 -0
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/light-probe.d.ts +9 -0
- package/dist/components/light-probe.d.ts.map +1 -0
- package/dist/components/rect-area-light.d.ts +12 -0
- package/dist/components/rect-area-light.d.ts.map +1 -0
- package/dist/components/scene-instance.d.ts +4 -0
- package/dist/components/scene-instance.d.ts.map +1 -1
- package/dist/components/spot-light.d.ts +3 -0
- package/dist/components/spot-light.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +10437 -10044
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +8 -1
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/device/lifecycle-transaction.d.ts +2 -0
- package/dist/device/lifecycle-transaction.d.ts.map +1 -0
- package/dist/environment/lifecycle.d.ts.map +1 -1
- package/dist/errors/gpu-driven.d.ts +26 -0
- package/dist/errors/gpu-driven.d.ts.map +1 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/recover.d.ts +9 -1
- package/dist/errors/recover.d.ts.map +1 -1
- package/dist/errors/render.d.ts +26 -1
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +50 -0
- package/dist/extract/gpu-driven.d.ts.map +1 -0
- package/dist/extract/visibility.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +34 -0
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +63 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -0
- package/dist/gpu-driven/production-raster.d.ts +98 -2
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +64 -1
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts +14 -0
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +8 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-usage.d.ts +0 -1
- package/dist/gpu-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +3 -3
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +90 -5
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +163 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/internal.d.ts +9 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +23 -2
- package/dist/internal.mjs.map +1 -1
- package/dist/light-buffer-layout.d.ts +58 -149
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/materials.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +26 -12
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/light-frame.d.ts +3 -3
- package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/topology.d.ts +1 -1
- package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +2 -0
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +1 -1
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/prepare/extended-lighting/ltc-resources.d.ts +31 -0
- package/dist/prepare/extended-lighting/ltc-resources.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/resources.d.ts +72 -0
- package/dist/prepare/extended-lighting/resources.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/spot-modifiers.d.ts +42 -0
- package/dist/prepare/extended-lighting/spot-modifiers.d.ts.map +1 -0
- package/dist/prepare/extended-lighting/state.d.ts +24 -0
- package/dist/prepare/extended-lighting/state.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +8 -6
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +50 -10
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +7 -3
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-timing.d.ts +19 -7
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +0 -20
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +6 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +3 -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/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +57 -6
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +49 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +1 -2
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/reflection/inspection.d.ts +10 -0
- package/dist/reflection/inspection.d.ts.map +1 -0
- package/dist/reflection/projection.d.ts +49 -0
- package/dist/reflection/projection.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts +60 -0
- package/dist/reflection/record-owner.d.ts.map +1 -0
- package/dist/render-contract.d.ts +14 -8
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +7 -0
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +104 -16
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +12 -1
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/probe-blend-record.d.ts +22 -0
- package/dist/scene/probe-blend-record.d.ts.map +1 -0
- package/dist/scene/probe-blend.d.ts +142 -0
- package/dist/scene/probe-blend.d.ts.map +1 -0
- package/dist/scene/render-scene-types.d.ts +0 -6
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +103 -9
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/visibility/bounds.d.ts +20 -0
- package/dist/scene/visibility/bounds.d.ts.map +1 -0
- package/dist/scene/visibility/budget.d.ts +16 -0
- package/dist/scene/visibility/budget.d.ts.map +1 -0
- package/dist/scene/visibility/facet.d.ts +73 -0
- package/dist/scene/visibility/facet.d.ts.map +1 -0
- package/dist/scene/visibility/gpu-frame-timing.d.ts +41 -0
- package/dist/scene/visibility/gpu-frame-timing.d.ts.map +1 -0
- package/dist/scene/visibility/gpu-lod.d.ts +46 -0
- package/dist/scene/visibility/gpu-lod.d.ts.map +1 -0
- package/dist/scene/visibility/inspection.d.ts +132 -0
- package/dist/scene/visibility/inspection.d.ts.map +1 -0
- package/dist/scene/visibility/lod-selector.d.ts +25 -0
- package/dist/scene/visibility/lod-selector.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-confidence.d.ts +81 -0
- package/dist/scene/visibility/occlusion-confidence.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-pass.d.ts +57 -0
- package/dist/scene/visibility/occlusion-pass.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-query-pool.d.ts +57 -0
- package/dist/scene/visibility/occlusion-query-pool.d.ts.map +1 -0
- package/dist/scene/visibility/occlusion-runtime.d.ts +96 -0
- package/dist/scene/visibility/occlusion-runtime.d.ts.map +1 -0
- package/dist/scene/visibility/types.d.ts +34 -0
- package/dist/scene/visibility/types.d.ts.map +1 -0
- package/dist/ssr/admission.d.ts +122 -0
- package/dist/ssr/admission.d.ts.map +1 -0
- package/dist/ssr/composition.d.ts +32 -0
- package/dist/ssr/composition.d.ts.map +1 -0
- package/dist/ssr/errors.d.ts +39 -0
- package/dist/ssr/errors.d.ts.map +1 -0
- package/dist/ssr/identity.d.ts +13 -0
- package/dist/ssr/identity.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts +1 -1
- package/dist/temporal/frame-coordinator.d.ts +13 -0
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +2 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -5
- package/dist/temporal/view.d.ts +7 -0
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/tilemap-chunk-extract-system.d.ts +1 -1
- package/dist/typed-render-graph-primitives.d.ts +4 -0
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +8 -4
- package/dist/volume/passes.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +5 -0
- package/src/__tests__/detached-inspection.integration.test.ts +6 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +13 -1
- package/src/__tests__/direct-light-snapshot.unit.test.ts +59 -3
- package/src/__tests__/environment-generation.integration.test.ts +16 -0
- package/src/__tests__/evidence-identity.ts +13 -0
- package/src/__tests__/evidence-identity.unit.test.ts +24 -0
- package/src/__tests__/extended-lighting-recovery.browser.test.ts +14 -0
- package/src/__tests__/extended-lighting-recovery.dawn.test.ts +14 -0
- package/src/__tests__/extended-lighting-recovery.integration.test.ts +81 -0
- package/src/__tests__/extended-lighting-resources.unit.test.ts +51 -0
- package/src/__tests__/extended-lighting-zero-feature.integration.test.ts +52 -0
- package/src/__tests__/factory-contract.integration.test.ts +33 -0
- package/src/__tests__/frame-observation.contract.test.ts +15 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +1 -0
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +1 -0
- package/src/__tests__/gpu-driven-production.integration.test.ts +572 -9
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +66 -2
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +26 -1
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +89 -2
- package/src/__tests__/gpu-driven-view.browser.test.ts +3 -3
- package/src/__tests__/gpu-driven-view.dawn.test.ts +3 -3
- package/src/__tests__/gpu-driven-view.unit.test.ts +50 -0
- package/src/__tests__/gpu-lod-evidence.ts +188 -0
- package/src/__tests__/gpu-lod-fallback.integration.test.ts +10 -0
- package/src/__tests__/gpu-lod-indirect.integration.test.ts +40 -0
- package/src/__tests__/gpu-lod-parity.dawn.test.ts +29 -0
- package/src/__tests__/gpu-lod-readiness.integration.test.ts +52 -0
- package/src/__tests__/gpu-lod-schema.unit.test.ts +66 -0
- package/src/__tests__/gpu-lod-selector.unit.test.ts +28 -0
- package/src/__tests__/gpu-lod-silhouette.browser.test.ts +13 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +66 -1
- package/src/__tests__/gpu-scene.dawn.test.ts +8 -1
- package/src/__tests__/gpu-scene.unit.test.ts +49 -3
- package/src/__tests__/light-buffer-layout.unit.test.ts +113 -3
- package/src/__tests__/light-inspection.unit.test.ts +55 -0
- package/src/__tests__/light-resource-error.unit.test.ts +43 -0
- package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +109 -0
- package/src/__tests__/lod-bounds.unit.test.ts +42 -0
- package/src/__tests__/lod-cpu-lane.integration.test.ts +55 -0
- package/src/__tests__/lod-cpu-no-change.perf.test.ts +19 -0
- package/src/__tests__/lod-gpu-frame-timing.unit.test.ts +49 -0
- package/src/__tests__/lod-multi-view.integration.test.ts +63 -0
- package/src/__tests__/lod-occlusion-device-loss.integration.test.ts +27 -0
- package/src/__tests__/lod-occlusion-final-decision.integration.test.ts +47 -0
- package/src/__tests__/lod-occlusion-inspection.integration.test.ts +217 -0
- package/src/__tests__/lod-occlusion-inspection.schema.test-d.ts +14 -0
- package/src/__tests__/lod-occlusion-inspection.unit.test.ts +72 -0
- package/src/__tests__/lod-occlusion-visual-evidence.ts +91 -0
- package/src/__tests__/lod-occlusion.browser.test.ts +15 -0
- package/src/__tests__/lod-occlusion.dawn.test.ts +20 -0
- package/src/__tests__/lod-selector.profile.test.ts +35 -0
- package/src/__tests__/lod-selector.unit.test.ts +68 -0
- package/src/__tests__/lod-visibility-identity.unit.test.ts +198 -0
- package/src/__tests__/lod-visibility-lifecycle.integration.test.ts +132 -0
- package/src/__tests__/ltc-resources.unit.test.ts +32 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +17 -0
- package/src/__tests__/occlusion-confidence.unit.test.ts +168 -0
- package/src/__tests__/occlusion-invalidation.integration.test.ts +37 -0
- package/src/__tests__/occlusion-pass-routing.unit.test.ts +16 -0
- package/src/__tests__/occlusion-pass.dawn.test.ts +20 -0
- package/src/__tests__/occlusion-pass.integration.test.ts +43 -0
- package/src/__tests__/occlusion-pass.rhi-null.test.ts +22 -0
- package/src/__tests__/occlusion-query-pool.rhi-null.test.ts +21 -0
- package/src/__tests__/occlusion-query-pool.unit.test.ts +95 -0
- package/src/__tests__/occlusion-query-ticket.integration.test.ts +47 -0
- package/src/__tests__/occlusion-query.dawn.test.ts +16 -0
- package/src/__tests__/occlusion-query.evidence.ts +178 -0
- package/src/__tests__/occlusion-query.rhi-null.test.ts +31 -0
- package/src/__tests__/occlusion-runtime.unit.test.ts +120 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +196 -0
- package/src/__tests__/probe-bindings.unit.test.ts +200 -0
- package/src/__tests__/probe-blend-numeric.unit.test.ts +67 -0
- package/src/__tests__/probe-blend-oracle.unit.test.ts +136 -0
- package/src/__tests__/probe-capacity.integration.test.ts +97 -0
- package/src/__tests__/probe-recovery.integration.test.ts +104 -0
- package/src/__tests__/probe-scene-dirty.integration.test.ts +211 -0
- package/src/__tests__/profiler-phase-catalog.test.ts +4 -1
- package/src/__tests__/profiler-phase-token.test.ts +1 -0
- package/src/__tests__/rect-area-boundary.unit.test.ts +24 -0
- package/src/__tests__/reflection-fallback-graph.unit.test.ts +67 -0
- package/src/__tests__/reflection-fallback-inspection.unit.test.ts +77 -0
- package/src/__tests__/reflection-fallback-lifecycle.integration.test.ts +21 -0
- package/src/__tests__/reflection-fallback-projection.unit.test.ts +90 -0
- package/src/__tests__/reflection-fallback-source.unit.test.ts +32 -0
- package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
- package/src/__tests__/render-feature-docs.unit.test.ts +12 -7
- package/src/__tests__/render-graph-fallback.integration.test.ts +24 -0
- package/src/__tests__/render-perf/extended-lighting.bench.test.ts +66 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +447 -18
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +76 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +22 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +2 -2
- package/src/__tests__/spot-modifier-projection.unit.test.ts +70 -0
- package/src/__tests__/ssr-admission-matrix.integration.test.ts +206 -0
- package/src/__tests__/ssr-composition.unit.test.ts +46 -0
- package/src/__tests__/ssr-fallback-zero-work.bench.test.ts +94 -0
- package/src/__tests__/ssr-recovery-errors.unit.test.ts +58 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +67 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +7 -0
- package/src/__tests__/standard-lighting-inspection.integration.test.ts +1 -1
- package/src/__tests__/standard-lighting-write.unit.test.ts +66 -2
- package/src/__tests__/temporal-fallback-generation.integration.test.ts +16 -0
- package/src/__tests__/temporal-view.unit.test.ts +3 -3
- package/src/__tests__/transmission-prewarm.unit.test.ts +69 -0
- package/src/__tests__/visibility-budget.integration.test.ts +140 -0
- package/src/__tests__/visibility-budget.unit.test.ts +70 -0
- package/src/__tests__/visibility-extract.integration.test.ts +11 -6
- package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +6 -3
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +31 -28
- package/src/assembly/backend-contract.ts +13 -0
- package/src/assembly/device-feature-admission.ts +16 -9
- package/src/assembly/factory.ts +547 -93
- package/src/assembly/host-contract.ts +10 -0
- package/src/cluster-binner.ts +31 -0
- package/src/components/__tests__/lighting-authoring.unit.test.ts +39 -0
- package/src/components/__tests__/rect-area-light.unit.test.ts +33 -0
- package/src/components/index.ts +3 -0
- package/src/components/light-helpers.ts +52 -1
- package/src/components/light-probe.ts +13 -0
- package/src/components/rect-area-light.ts +15 -0
- package/src/components/scene-instance.ts +6 -0
- package/src/components/spot-light.ts +3 -0
- package/src/device/__tests__/extended-lighting-recovery.unit.test.ts +24 -0
- package/src/device/gpu-residency.ts +140 -9
- package/src/device/lifecycle-transaction.ts +12 -0
- package/src/environment/lifecycle.ts +8 -1
- package/src/errors/gpu-driven.ts +92 -0
- package/src/errors/index.ts +1 -0
- package/src/errors/recover.ts +32 -0
- package/src/errors/render.ts +55 -1
- package/src/extract/camera.ts +3 -3
- package/src/extract/gpu-driven.ts +108 -0
- package/src/extract/visibility.ts +0 -23
- package/src/gpu-driven/batch-topology.ts +206 -28
- package/src/gpu-driven/prepared-draw.ts +208 -0
- package/src/gpu-driven/production-raster.ts +953 -41
- package/src/gpu-driven/view-gpu.ts +411 -29
- package/src/gpu-scene-schema.ts +13 -0
- package/src/gpu-scene.ts +134 -65
- package/src/gpu-usage.ts +0 -1
- package/src/hdrp-buffers.ts +6 -6
- package/src/index.ts +74 -0
- package/src/inspection-types.ts +220 -1
- package/src/internal.ts +38 -0
- package/src/light-buffer-layout.ts +154 -324
- package/src/materials.ts +16 -2
- package/src/pbr-pipeline.ts +151 -45
- package/src/pipeline/standard-forward-lane.ts +47 -0
- package/src/pipeline/standard-lighting/graph.ts +2 -1
- package/src/pipeline/standard-lighting/light-frame.ts +3 -2
- package/src/pipeline/standard-lighting/topology.ts +1 -1
- package/src/pipeline/standard-lighting/transport.ts +2 -1
- package/src/pipeline/standard-pipeline.ts +2 -0
- package/src/pipeline-builder.ts +5 -2
- package/src/pipeline-spec.ts +12 -1
- package/src/plugin.ts +4 -0
- package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +56 -0
- package/src/prepare/extended-lighting/ltc-resources.ts +136 -0
- package/src/prepare/extended-lighting/resources.ts +389 -0
- package/src/prepare/extended-lighting/spot-modifiers.ts +194 -0
- package/src/prepare/extended-lighting/state.ts +92 -0
- package/src/record/__tests__/gpu-timing.unit.test.ts +177 -0
- package/src/record/__tests__/temporal-payload.unit.test.ts +23 -1
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +20 -1
- package/src/record/frame-lighting.ts +252 -53
- package/src/record/frame-snapshot.ts +57 -12
- package/src/record/frame.ts +410 -93
- package/src/record/gpu-timing.ts +108 -44
- package/src/record/helpers.ts +0 -58
- package/src/record/main-pass-geometry.ts +204 -39
- package/src/record/main-pass-material.ts +8 -1
- package/src/record/main-pass-sprite-draws.ts +52 -85
- package/src/record/mesh-ssbo.ts +47 -36
- package/src/record/render-context.ts +65 -4
- package/src/record/shadow-pass.ts +66 -40
- package/src/record/typed-frame-graph.ts +225 -83
- package/src/record/view-ubo.ts +14 -24
- package/src/reflection/inspection.ts +67 -0
- package/src/reflection/projection.ts +196 -1
- package/src/reflection/record-owner.ts +1283 -0
- package/src/render-contract.ts +17 -5
- package/src/render-pipeline.ts +5 -0
- package/src/render-system-extract.ts +493 -134
- package/src/render-system.ts +811 -615
- package/src/scene/__tests__/changed-block-summary.candidate.test.ts +36 -0
- package/src/scene/probe-blend-record.ts +54 -0
- package/src/scene/probe-blend.ts +813 -0
- package/src/scene/render-scene-types.ts +0 -7
- package/src/scene/render-scene.ts +848 -183
- package/src/scene/visibility/bounds.ts +57 -0
- package/src/scene/visibility/budget.ts +40 -0
- package/src/scene/visibility/facet.ts +400 -0
- package/src/scene/visibility/gpu-frame-timing.ts +101 -0
- package/src/scene/visibility/gpu-lod.ts +119 -0
- package/src/scene/visibility/inspection.ts +313 -0
- package/src/scene/visibility/lod-selector.ts +84 -0
- package/src/scene/visibility/occlusion-confidence.ts +478 -0
- package/src/scene/visibility/occlusion-pass.ts +165 -0
- package/src/scene/visibility/occlusion-query-pool.ts +178 -0
- package/src/scene/visibility/occlusion-runtime.ts +672 -0
- package/src/scene/visibility/types.ts +50 -0
- package/src/ssr/admission.ts +451 -0
- package/src/ssr/composition.ts +73 -0
- package/src/ssr/errors.ts +76 -0
- package/src/ssr/identity.ts +12 -0
- package/src/systems/skin-palette-allocator.ts +1 -1
- package/src/temporal/frame-coordinator.ts +26 -0
- package/src/temporal/frame.ts +14 -1
- package/src/temporal/view.ts +11 -0
- package/src/tilemap-chunk-extract-system.ts +4 -4
- package/src/typed-render-graph-primitives.ts +48 -0
- package/src/volume/passes.ts +46 -49
- package/dist/.tsbuildinfo +0 -1
- package/dist/chunk-5YADUCA7.mjs.map +0 -1
- package/dist/chunk-6OPXDQ2E.mjs.map +0 -1
- package/dist/chunk-BXBQQKFQ.mjs +0 -67
- package/dist/chunk-BXBQQKFQ.mjs.map +0 -1
- package/dist/chunk-FS2KNUQH.mjs.map +0 -1
- package/dist/chunk-PCHBPYML.mjs.map +0 -1
- package/dist/chunk-VFPLTKCU.mjs.map +0 -1
|
@@ -20,17 +20,41 @@ import {
|
|
|
20
20
|
GPU_BUFFER_USAGE_COPY_DST,
|
|
21
21
|
GPU_BUFFER_USAGE_COPY_SRC,
|
|
22
22
|
GPU_BUFFER_USAGE_INDIRECT,
|
|
23
|
+
GPU_BUFFER_USAGE_MAP_READ,
|
|
23
24
|
GPU_BUFFER_USAGE_STORAGE,
|
|
24
25
|
GPU_BUFFER_USAGE_UNIFORM,
|
|
25
26
|
} from '../gpu-usage';
|
|
26
27
|
import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
|
|
27
|
-
import type { SubmissionPlan } from './batch-topology';
|
|
28
|
+
import type { GpuDrivenCandidate, SubmissionPlan } from './batch-topology';
|
|
28
29
|
|
|
29
30
|
const COMPUTE_STAGE = 0x4;
|
|
30
31
|
const WORKGROUP_SIZE = 64;
|
|
31
|
-
const
|
|
32
|
-
const
|
|
32
|
+
const LOD_ROW_STRIDE = GPU_SCENE_LAYOUTS.lod.stride;
|
|
33
|
+
const LOD_ROW_CAPACITY = 8;
|
|
34
|
+
const CANDIDATE_STRIDE = 48 + LOD_ROW_CAPACITY * LOD_ROW_STRIDE;
|
|
35
|
+
const BATCH_STRIDE = 32;
|
|
33
36
|
const VIEW_BYTES = 112;
|
|
37
|
+
// visible + overflow + one bucket per selected LOD + selected/root index work.
|
|
38
|
+
// The two work counters let inspection report geometry reduction from the
|
|
39
|
+
// actual indirect draw ranges instead of treating submitted-instance count as
|
|
40
|
+
// a proxy for vertex/index work.
|
|
41
|
+
const COUNTER_WORDS = 2 + LOD_ROW_CAPACITY + 2;
|
|
42
|
+
const GEOMETRY_WORK_COUNTER_OFFSET = 2 + LOD_ROW_CAPACITY;
|
|
43
|
+
const ROOT_GEOMETRY_WORK_COUNTER_OFFSET = GEOMETRY_WORK_COUNTER_OFFSET + 1;
|
|
44
|
+
const COUNTER_STRIDE = COUNTER_WORDS * 4;
|
|
45
|
+
type GpuDrivenAdmissionCandidate = GpuDrivenCandidate & {
|
|
46
|
+
readonly submitAdmission?: boolean;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type GpuLodLane = 'gpu' | 'cpu';
|
|
50
|
+
|
|
51
|
+
export function selectGpuLodLane(caps: {
|
|
52
|
+
readonly compute: boolean;
|
|
53
|
+
readonly storageBuffer: boolean;
|
|
54
|
+
readonly indirectDrawing: boolean;
|
|
55
|
+
}): GpuLodLane {
|
|
56
|
+
return caps.compute && caps.storageBuffer && caps.indirectDrawing ? 'gpu' : 'cpu';
|
|
57
|
+
}
|
|
34
58
|
|
|
35
59
|
export const GPU_DRIVEN_VIEW_WGSL = /* wgsl */ `
|
|
36
60
|
struct PrimitiveRecord {
|
|
@@ -51,6 +75,17 @@ struct TransformRecord {
|
|
|
51
75
|
previousWorld: mat4x4<f32>,
|
|
52
76
|
};
|
|
53
77
|
|
|
78
|
+
struct LodRecord {
|
|
79
|
+
generation: u32,
|
|
80
|
+
level: u32,
|
|
81
|
+
firstIndex: u32,
|
|
82
|
+
indexCount: u32,
|
|
83
|
+
baseVertex: i32,
|
|
84
|
+
screenCoverage: f32,
|
|
85
|
+
hysteresis: f32,
|
|
86
|
+
ready: u32,
|
|
87
|
+
};
|
|
88
|
+
|
|
54
89
|
struct CandidateRecord {
|
|
55
90
|
primitiveIndex: u32,
|
|
56
91
|
generation: u32,
|
|
@@ -59,6 +94,15 @@ struct CandidateRecord {
|
|
|
59
94
|
batchIndex: u32,
|
|
60
95
|
visibleBase: u32,
|
|
61
96
|
visibleCapacity: u32,
|
|
97
|
+
// Admission is separate from selector membership: suppressed candidates
|
|
98
|
+
// still contribute to the all-candidate LOD histogram, but never enter the
|
|
99
|
+
// compacted visible stream.
|
|
100
|
+
submitAdmission: u32,
|
|
101
|
+
projectedHeight: f32,
|
|
102
|
+
previousLevel: u32,
|
|
103
|
+
historyValid: u32,
|
|
104
|
+
lodCount: u32,
|
|
105
|
+
lodRows: array<LodRecord, 8>,
|
|
62
106
|
};
|
|
63
107
|
|
|
64
108
|
struct InstanceRecord {
|
|
@@ -76,6 +120,7 @@ struct BatchRecord {
|
|
|
76
120
|
baseVertex: i32,
|
|
77
121
|
indirectIndex: u32,
|
|
78
122
|
indexed: u32,
|
|
123
|
+
candidateBase: u32,
|
|
79
124
|
};
|
|
80
125
|
|
|
81
126
|
struct ViewConstants {
|
|
@@ -86,6 +131,34 @@ struct ViewConstants {
|
|
|
86
131
|
pad1: u32,
|
|
87
132
|
};
|
|
88
133
|
|
|
134
|
+
fn selectLodLevel(
|
|
135
|
+
projectedHeight: f32,
|
|
136
|
+
previousLevel: u32,
|
|
137
|
+
historyValid: bool,
|
|
138
|
+
rows: array<LodRecord, 8>,
|
|
139
|
+
levelCount: u32,
|
|
140
|
+
) -> u32 {
|
|
141
|
+
// Row zero is the implicit root and has no transition threshold. Lower
|
|
142
|
+
// levels carry the absolute boundary that introduces the preceding row.
|
|
143
|
+
// This mirrors the CPU selector: height >= rows[1] selects root, height
|
|
144
|
+
// between rows[2] and rows[1] selects level 1, and so on.
|
|
145
|
+
var selected = levelCount - 1u;
|
|
146
|
+
for (var level = 1u; level < levelCount; level += 1u) {
|
|
147
|
+
if (projectedHeight >= rows[level].screenCoverage) {
|
|
148
|
+
selected = level - 1u;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (historyValid && previousLevel < levelCount && selected != previousLevel) {
|
|
153
|
+
var boundary = rows[previousLevel + 1u].screenCoverage;
|
|
154
|
+
if (selected < previousLevel) { boundary = rows[previousLevel].screenCoverage; }
|
|
155
|
+
let band = boundary * rows[previousLevel].hysteresis;
|
|
156
|
+
if (abs(projectedHeight - boundary) <= band) { selected = previousLevel; }
|
|
157
|
+
}
|
|
158
|
+
if (selected >= levelCount || rows[selected].ready == 0u) { return 0u; }
|
|
159
|
+
return selected;
|
|
160
|
+
}
|
|
161
|
+
|
|
89
162
|
@group(0) @binding(0) var<storage, read> primitives: array<PrimitiveRecord>;
|
|
90
163
|
@group(0) @binding(1) var<storage, read> instances: array<InstanceRecord>;
|
|
91
164
|
@group(0) @binding(2) var<storage, read> transforms: array<TransformRecord>;
|
|
@@ -96,11 +169,18 @@ struct ViewConstants {
|
|
|
96
169
|
@group(0) @binding(7) var<storage, read_write> visibleIndices: array<vec2<u32>>;
|
|
97
170
|
@group(0) @binding(8) var<storage, read_write> indirectArgs: array<u32>;
|
|
98
171
|
|
|
99
|
-
fn counterIndex(batchIndex: u32) -> u32 { return batchIndex *
|
|
100
|
-
fn overflowIndex(batchIndex: u32) -> u32 { return batchIndex
|
|
172
|
+
fn counterIndex(batchIndex: u32) -> u32 { return batchIndex * ${COUNTER_WORDS}u; }
|
|
173
|
+
fn overflowIndex(batchIndex: u32) -> u32 { return counterIndex(batchIndex) + 1u; }
|
|
174
|
+
fn lodCounterIndex(batchIndex: u32, level: u32) -> u32 {
|
|
175
|
+
return counterIndex(batchIndex) + 2u + level;
|
|
176
|
+
}
|
|
101
177
|
|
|
102
178
|
fn isVisible(primitive: PrimitiveRecord, world: mat4x4<f32>) -> bool {
|
|
103
|
-
|
|
179
|
+
// GPU Scene bounds are optional. A producer that has not published local
|
|
180
|
+
// bounds still belongs in the GPU lane; keep it conservatively visible
|
|
181
|
+
// instead of silently dropping a valid draw.
|
|
182
|
+
if ((primitive.flags & 5u) != 5u) { return false; }
|
|
183
|
+
if ((primitive.flags & 2u) == 0u) { return true; }
|
|
104
184
|
let localCenter = (primitive.localBoundsMin.xyz + primitive.localBoundsMax.xyz) * 0.5;
|
|
105
185
|
let localExtent = (primitive.localBoundsMax.xyz - primitive.localBoundsMin.xyz) * 0.5;
|
|
106
186
|
let worldCenter = (world * vec4<f32>(localCenter, 1.0)).xyz;
|
|
@@ -122,6 +202,11 @@ fn resetView(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
122
202
|
if (batchIndex >= view.batchCount) { return; }
|
|
123
203
|
atomicStore(&counters[counterIndex(batchIndex)], 0u);
|
|
124
204
|
atomicStore(&counters[overflowIndex(batchIndex)], 0u);
|
|
205
|
+
for (var level = 0u; level < ${LOD_ROW_CAPACITY}u; level += 1u) {
|
|
206
|
+
atomicStore(&counters[lodCounterIndex(batchIndex, level)], 0u);
|
|
207
|
+
}
|
|
208
|
+
atomicStore(&counters[counterIndex(batchIndex) + ${GEOMETRY_WORK_COUNTER_OFFSET}u], 0u);
|
|
209
|
+
atomicStore(&counters[counterIndex(batchIndex) + ${ROOT_GEOMETRY_WORK_COUNTER_OFFSET}u], 0u);
|
|
125
210
|
}
|
|
126
211
|
|
|
127
212
|
@compute @workgroup_size(${WORKGROUP_SIZE})
|
|
@@ -135,7 +220,28 @@ fn cullView(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
135
220
|
let instanceIndex = primitive.instanceStart + candidate.instanceOrdinal;
|
|
136
221
|
let instance = instances[instanceIndex];
|
|
137
222
|
let world = transforms[primitive.transformIndex].currentWorld * transforms[instance.transformIndex].currentWorld;
|
|
223
|
+
let selectedLod = selectLodLevel(
|
|
224
|
+
candidate.projectedHeight,
|
|
225
|
+
candidate.previousLevel,
|
|
226
|
+
candidate.historyValid != 0u,
|
|
227
|
+
candidate.lodRows,
|
|
228
|
+
candidate.lodCount,
|
|
229
|
+
);
|
|
230
|
+
if (candidate.lodRows[selectedLod].ready == 0u) { return; }
|
|
231
|
+
// Selector counters describe the complete LOD population, including
|
|
232
|
+
// candidates outside the frustum and candidates suppressed by the CPU
|
|
233
|
+
// visibility facet. Submission counters below remain compacted draw facts.
|
|
234
|
+
atomicAdd(&counters[lodCounterIndex(candidate.batchIndex, selectedLod)], 1u);
|
|
235
|
+
if (candidate.submitAdmission == 0u) { return; }
|
|
138
236
|
if (!isVisible(primitive, world)) { return; }
|
|
237
|
+
atomicAdd(
|
|
238
|
+
&counters[counterIndex(candidate.batchIndex) + ${GEOMETRY_WORK_COUNTER_OFFSET}u],
|
|
239
|
+
candidate.lodRows[selectedLod].indexCount,
|
|
240
|
+
);
|
|
241
|
+
atomicAdd(
|
|
242
|
+
&counters[counterIndex(candidate.batchIndex) + ${ROOT_GEOMETRY_WORK_COUNTER_OFFSET}u],
|
|
243
|
+
candidate.lodRows[0u].indexCount,
|
|
244
|
+
);
|
|
139
245
|
let localVisible = atomicAdd(&counters[counterIndex(candidate.batchIndex)], 1u);
|
|
140
246
|
if (localVisible >= candidate.visibleCapacity) {
|
|
141
247
|
atomicStore(&counters[overflowIndex(candidate.batchIndex)], 1u);
|
|
@@ -157,10 +263,13 @@ fn finalizeView(@builtin(global_invocation_id) id: vec3<u32>) {
|
|
|
157
263
|
batch.visibleCapacity,
|
|
158
264
|
);
|
|
159
265
|
let args = batch.indirectIndex * 5u;
|
|
160
|
-
|
|
266
|
+
let candidate = candidates[batch.candidateBase];
|
|
267
|
+
let selectedLod = selectLodLevel(candidate.projectedHeight, candidate.previousLevel, candidate.historyValid != 0u, candidate.lodRows, candidate.lodCount);
|
|
268
|
+
let lod = candidate.lodRows[selectedLod];
|
|
269
|
+
indirectArgs[args] = select(batch.count, lod.indexCount, candidate.lodCount > 0u);
|
|
161
270
|
indirectArgs[args + 1u] = visibleCount;
|
|
162
|
-
indirectArgs[args + 2u] = batch.first;
|
|
163
|
-
indirectArgs[args + 3u] = select(0u, bitcast<u32>(
|
|
271
|
+
indirectArgs[args + 2u] = select(batch.first, lod.firstIndex, candidate.lodCount > 0u && batch.indexed != 0u);
|
|
272
|
+
indirectArgs[args + 3u] = select(0u, bitcast<u32>(lod.baseVertex), candidate.lodCount > 0u && batch.indexed != 0u);
|
|
164
273
|
indirectArgs[args + 4u] = 0u;
|
|
165
274
|
}
|
|
166
275
|
`;
|
|
@@ -172,6 +281,7 @@ interface ViewBuffers {
|
|
|
172
281
|
readonly counters: Buffer;
|
|
173
282
|
readonly visible: Buffer;
|
|
174
283
|
readonly indirect: Buffer;
|
|
284
|
+
readonly lodReadback: Buffer;
|
|
175
285
|
}
|
|
176
286
|
|
|
177
287
|
type MutableViewBuffers = { -readonly [Name in keyof ViewBuffers]: ViewBuffers[Name] };
|
|
@@ -192,6 +302,8 @@ export interface GpuDrivenViewInspection {
|
|
|
192
302
|
readonly batchCount: number;
|
|
193
303
|
readonly visibleCapacity: number;
|
|
194
304
|
readonly candidateCapacity: number;
|
|
305
|
+
/** Allocated visible-index capacity; distinct from logical candidates. */
|
|
306
|
+
readonly visibleBufferCapacity: number;
|
|
195
307
|
readonly batchCapacity: number;
|
|
196
308
|
readonly indirectCapacity: number;
|
|
197
309
|
readonly updateCount: number;
|
|
@@ -204,10 +316,69 @@ export interface GpuDrivenViewInspection {
|
|
|
204
316
|
readonly resourceGeneration: number;
|
|
205
317
|
}
|
|
206
318
|
|
|
319
|
+
export interface GpuDrivenViewBufferCapacities {
|
|
320
|
+
readonly candidate: number;
|
|
321
|
+
readonly visible: number;
|
|
322
|
+
readonly batch: number;
|
|
323
|
+
readonly indirect: number;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface GpuDrivenLodSelectionInspection {
|
|
327
|
+
readonly candidateCount: number;
|
|
328
|
+
readonly batchCount: number;
|
|
329
|
+
readonly indirectDrawCount: number;
|
|
330
|
+
readonly visible: number;
|
|
331
|
+
readonly occluded: number;
|
|
332
|
+
readonly lodHistogram: readonly { readonly level: number; readonly count: number }[];
|
|
333
|
+
readonly batches: readonly {
|
|
334
|
+
readonly batchId: number;
|
|
335
|
+
readonly candidateCount: number;
|
|
336
|
+
readonly visible: number;
|
|
337
|
+
readonly occluded: number;
|
|
338
|
+
readonly lodHistogram: readonly { readonly level: number; readonly count: number }[];
|
|
339
|
+
}[];
|
|
340
|
+
readonly worldSelections?: readonly {
|
|
341
|
+
readonly worldKey: number;
|
|
342
|
+
readonly primitiveSlot: number;
|
|
343
|
+
readonly slotGeneration: number;
|
|
344
|
+
readonly candidateCount: number;
|
|
345
|
+
readonly visible: number;
|
|
346
|
+
readonly occluded: number;
|
|
347
|
+
readonly lodHistogram: readonly { readonly level: number; readonly count: number }[];
|
|
348
|
+
}[];
|
|
349
|
+
readonly geometryWork: number;
|
|
350
|
+
readonly rootGeometryWork: number;
|
|
351
|
+
}
|
|
352
|
+
|
|
207
353
|
function nextCapacity(required: number): number {
|
|
208
354
|
return 2 ** Math.ceil(Math.log2(Math.max(1, required)));
|
|
209
355
|
}
|
|
210
356
|
|
|
357
|
+
/** Selector batches with no admitted candidates never produce a raster draw. */
|
|
358
|
+
function admittedRasterBatchCount(plan: SubmissionPlan): number {
|
|
359
|
+
return plan.batches.reduce((count, batch) => count + (batch.visibleCapacity > 0 ? 1 : 0), 0);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Derive each GPU-driven allocation from its own logical capacity domain.
|
|
364
|
+
* Candidate records are dense; visible indices use the sparse, 64-entry
|
|
365
|
+
* aligned ranges assigned by BatchTopology. Keeping these values separate is
|
|
366
|
+
* what makes a large LOD candidate set bounded instead of multiplying the
|
|
367
|
+
* candidate record and inline LOD-row buffers by the visible address space.
|
|
368
|
+
*/
|
|
369
|
+
export function deriveGpuDrivenViewBufferCapacities(
|
|
370
|
+
plan: Pick<SubmissionPlan, 'candidateCount' | 'visibleCapacity' | 'batches'>,
|
|
371
|
+
): GpuDrivenViewBufferCapacities {
|
|
372
|
+
return Object.freeze({
|
|
373
|
+
candidate: nextCapacity(plan.candidateCount),
|
|
374
|
+
visible: nextCapacity(plan.visibleCapacity),
|
|
375
|
+
batch: nextCapacity(plan.batches.length),
|
|
376
|
+
indirect: nextCapacity(
|
|
377
|
+
plan.batches.reduce((maximum, batch) => Math.max(maximum, batch.batchId + 1), 0),
|
|
378
|
+
),
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
211
382
|
function bufferBinding(buffer: Buffer): {
|
|
212
383
|
readonly kind: 'buffer';
|
|
213
384
|
readonly value: { buffer: Buffer };
|
|
@@ -220,6 +391,7 @@ export class GpuDrivenView {
|
|
|
220
391
|
private supersededBuffers: ViewBuffers[] = [];
|
|
221
392
|
private bindGroup: BindGroup | undefined;
|
|
222
393
|
private candidateCapacity = 0;
|
|
394
|
+
private visibleBufferCapacity = 0;
|
|
223
395
|
private batchCapacity = 0;
|
|
224
396
|
private indirectCapacity = 0;
|
|
225
397
|
private plan: SubmissionPlan | undefined;
|
|
@@ -236,6 +408,8 @@ export class GpuDrivenView {
|
|
|
236
408
|
private bindGroupCreates = 0;
|
|
237
409
|
private bufferRebuilds = 0;
|
|
238
410
|
private resourceGeneration = 0;
|
|
411
|
+
private telemetryPending = false;
|
|
412
|
+
private lodSelection: GpuDrivenLodSelectionInspection | undefined;
|
|
239
413
|
|
|
240
414
|
private constructor(
|
|
241
415
|
private readonly device: RhiDevice,
|
|
@@ -314,22 +488,34 @@ export class GpuDrivenView {
|
|
|
314
488
|
this.batchUploadBytes = 0;
|
|
315
489
|
this.viewConstantsUploadBytes = 0;
|
|
316
490
|
this.bindGroupCreates = 0;
|
|
491
|
+
// The readback copy is part of the accepted graph and therefore executes
|
|
492
|
+
// every submitted frame, not only when that graph is (re)compiled. Keep
|
|
493
|
+
// the observation latch armed across per-frame view updates; clearing it
|
|
494
|
+
// here would make a cached graph silently discard all later LOD samples.
|
|
495
|
+
this.telemetryPending = true;
|
|
496
|
+
this.lodSelection = undefined;
|
|
317
497
|
const topologyChanged = this.plan !== plan;
|
|
318
498
|
let buffersRebuilt = false;
|
|
319
499
|
if (topologyChanged || this.buffers === undefined) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
);
|
|
500
|
+
// Candidate records and compacted visible indices are different
|
|
501
|
+
// capacity domains. LOD singleton batches align each visible segment
|
|
502
|
+
// to 64 entries, so plan.visibleCapacity can be much larger than the
|
|
503
|
+
// number of logical candidates. Never let that sparse address space
|
|
504
|
+
// inflate candidate/LOD-row storage allocations.
|
|
505
|
+
const capacities = deriveGpuDrivenViewBufferCapacities(plan);
|
|
326
506
|
if (
|
|
327
507
|
this.buffers === undefined ||
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
508
|
+
capacities.candidate > this.candidateCapacity ||
|
|
509
|
+
capacities.visible > this.visibleBufferCapacity ||
|
|
510
|
+
capacities.batch > this.batchCapacity ||
|
|
511
|
+
capacities.indirect > this.indirectCapacity
|
|
331
512
|
) {
|
|
332
|
-
const rebuilt = this.rebuildBuffers(
|
|
513
|
+
const rebuilt = this.rebuildBuffers(
|
|
514
|
+
capacities.candidate,
|
|
515
|
+
capacities.visible,
|
|
516
|
+
capacities.batch,
|
|
517
|
+
capacities.indirect,
|
|
518
|
+
);
|
|
333
519
|
if (!rebuilt.ok) return rebuilt;
|
|
334
520
|
buffersRebuilt = true;
|
|
335
521
|
}
|
|
@@ -353,7 +539,9 @@ export class GpuDrivenView {
|
|
|
353
539
|
batches.setInt32(batchOffset + 16, batch.key.baseVertex, true);
|
|
354
540
|
batches.setUint32(batchOffset + 20, batch.batchId, true);
|
|
355
541
|
batches.setUint32(batchOffset + 24, batch.key.drawKind === 'indexed' ? 1 : 0, true);
|
|
542
|
+
batches.setUint32(batchOffset + 28, candidateIndex, true);
|
|
356
543
|
for (const candidate of batch.candidates) {
|
|
544
|
+
const admissionCandidate = candidate as GpuDrivenAdmissionCandidate;
|
|
357
545
|
const candidateOffset = candidateIndex * CANDIDATE_STRIDE;
|
|
358
546
|
candidates.setUint32(candidateOffset, candidate.primitiveIndex, true);
|
|
359
547
|
candidates.setUint32(candidateOffset + 4, candidate.generation, true);
|
|
@@ -362,6 +550,40 @@ export class GpuDrivenView {
|
|
|
362
550
|
candidates.setUint32(candidateOffset + 16, batchIndex, true);
|
|
363
551
|
candidates.setUint32(candidateOffset + 20, batch.visibleBase, true);
|
|
364
552
|
candidates.setUint32(candidateOffset + 24, batch.visibleCapacity, true);
|
|
553
|
+
const coverages = candidate.lodCoverages ?? [1];
|
|
554
|
+
const ranges = [
|
|
555
|
+
{ first: batch.key.first, count: batch.key.count, baseVertex: batch.key.baseVertex },
|
|
556
|
+
...(candidate.lodRanges ?? []),
|
|
557
|
+
];
|
|
558
|
+
const lodCount = Math.min(LOD_ROW_CAPACITY, Math.max(1, coverages.length, ranges.length));
|
|
559
|
+
candidates.setUint32(
|
|
560
|
+
candidateOffset + 28,
|
|
561
|
+
admissionCandidate.submitAdmission === false ? 0 : 1,
|
|
562
|
+
true,
|
|
563
|
+
);
|
|
564
|
+
candidates.setFloat32(
|
|
565
|
+
candidateOffset + 32,
|
|
566
|
+
candidate.projectedHeight ?? Number.NaN,
|
|
567
|
+
true,
|
|
568
|
+
);
|
|
569
|
+
candidates.setUint32(candidateOffset + 36, 0, true);
|
|
570
|
+
candidates.setUint32(candidateOffset + 40, 0, true);
|
|
571
|
+
candidates.setUint32(candidateOffset + 44, lodCount, true);
|
|
572
|
+
for (let level = 0; level < lodCount; level += 1) {
|
|
573
|
+
const rowOffset = candidateOffset + 48 + level * LOD_ROW_STRIDE;
|
|
574
|
+
candidates.setUint32(rowOffset, candidate.generation, true);
|
|
575
|
+
candidates.setUint32(rowOffset + 4, level, true);
|
|
576
|
+
candidates.setUint32(rowOffset + 8, ranges[level]?.first ?? batch.key.first, true);
|
|
577
|
+
candidates.setUint32(rowOffset + 12, ranges[level]?.count ?? batch.key.count, true);
|
|
578
|
+
candidates.setInt32(
|
|
579
|
+
rowOffset + 16,
|
|
580
|
+
ranges[level]?.baseVertex ?? batch.key.baseVertex,
|
|
581
|
+
true,
|
|
582
|
+
);
|
|
583
|
+
candidates.setFloat32(rowOffset + 20, coverages[level] ?? 0, true);
|
|
584
|
+
candidates.setFloat32(rowOffset + 24, candidate.lodHysteresis ?? 0.08, true);
|
|
585
|
+
candidates.setUint32(rowOffset + 28, 1, true);
|
|
586
|
+
}
|
|
365
587
|
candidateIndex += 1;
|
|
366
588
|
}
|
|
367
589
|
}
|
|
@@ -434,6 +656,7 @@ export class GpuDrivenView {
|
|
|
434
656
|
batchCount: this.plan?.batches.length ?? 0,
|
|
435
657
|
visibleCapacity: this.plan?.visibleCapacity ?? 0,
|
|
436
658
|
candidateCapacity: this.candidateCapacity,
|
|
659
|
+
visibleBufferCapacity: this.visibleBufferCapacity,
|
|
437
660
|
batchCapacity: this.batchCapacity,
|
|
438
661
|
indirectCapacity: this.indirectCapacity,
|
|
439
662
|
updateCount: this.updateCount,
|
|
@@ -463,6 +686,88 @@ export class GpuDrivenView {
|
|
|
463
686
|
return 4;
|
|
464
687
|
}
|
|
465
688
|
|
|
689
|
+
/** Read selected levels written by the GPU cull pass for the last submit. */
|
|
690
|
+
async readLodSelection(): Promise<GpuDrivenLodSelectionInspection | undefined> {
|
|
691
|
+
const buffers = this.buffers;
|
|
692
|
+
const plan = this.plan;
|
|
693
|
+
if (buffers === undefined || plan === undefined || !this.telemetryPending) {
|
|
694
|
+
return this.lodSelection;
|
|
695
|
+
}
|
|
696
|
+
this.telemetryPending = false;
|
|
697
|
+
const mapped = await buffers.lodReadback.mapAsync(GPU_BUFFER_USAGE_MAP_READ);
|
|
698
|
+
if (!mapped.ok) return undefined;
|
|
699
|
+
const range = mapped.value.getMappedRange();
|
|
700
|
+
if (!range.ok) {
|
|
701
|
+
mapped.value.unmap();
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
try {
|
|
705
|
+
const values = new DataView(range.value);
|
|
706
|
+
let visible = 0;
|
|
707
|
+
let geometryWork = 0;
|
|
708
|
+
let rootGeometryWork = 0;
|
|
709
|
+
const histogram = new Map<number, number>();
|
|
710
|
+
for (let batchIndex = 0; batchIndex < plan.batches.length; batchIndex += 1) {
|
|
711
|
+
const offset = batchIndex * COUNTER_STRIDE;
|
|
712
|
+
visible += values.getUint32(offset, true);
|
|
713
|
+
geometryWork += values.getUint32(offset + GEOMETRY_WORK_COUNTER_OFFSET * 4, true);
|
|
714
|
+
rootGeometryWork += values.getUint32(offset + ROOT_GEOMETRY_WORK_COUNTER_OFFSET * 4, true);
|
|
715
|
+
for (let level = 0; level < LOD_ROW_CAPACITY; level += 1) {
|
|
716
|
+
const count = values.getUint32(offset + (2 + level) * 4, true);
|
|
717
|
+
if (count > 0) histogram.set(level, (histogram.get(level) ?? 0) + count);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
const candidateCount = plan.candidateCount;
|
|
721
|
+
this.lodSelection = Object.freeze({
|
|
722
|
+
candidateCount,
|
|
723
|
+
batchCount: plan.batches.length,
|
|
724
|
+
// Selector-only batches remain in the plan/readback but the production
|
|
725
|
+
// raster skips them. Report the actual nonzero-admission command count
|
|
726
|
+
// so a benchmark cannot mistake selector batches for raster draws.
|
|
727
|
+
indirectDrawCount: admittedRasterBatchCount(plan),
|
|
728
|
+
visible,
|
|
729
|
+
// A LOD histogram counts candidates that reached the selector, while
|
|
730
|
+
// `visible` is the actual compacted submission count. Occlusion is the
|
|
731
|
+
// difference between those candidate/visibility totals; using the
|
|
732
|
+
// histogram here would mislabel a visible candidate dropped only by an
|
|
733
|
+
// indirect-capacity overflow as occluded.
|
|
734
|
+
occluded: Math.max(0, candidateCount - visible),
|
|
735
|
+
lodHistogram: Object.freeze(
|
|
736
|
+
[...histogram.entries()]
|
|
737
|
+
.sort(([left], [right]) => left - right)
|
|
738
|
+
.map(([level, count]) => Object.freeze({ level, count })),
|
|
739
|
+
),
|
|
740
|
+
batches: Object.freeze(
|
|
741
|
+
plan.batches.map((batch, batchIndex) => {
|
|
742
|
+
const offset = batchIndex * COUNTER_STRIDE;
|
|
743
|
+
const batchHistogram = new Map<number, number>();
|
|
744
|
+
for (let level = 0; level < LOD_ROW_CAPACITY; level += 1) {
|
|
745
|
+
const count = values.getUint32(offset + (2 + level) * 4, true);
|
|
746
|
+
if (count > 0) batchHistogram.set(level, count);
|
|
747
|
+
}
|
|
748
|
+
const batchVisible = values.getUint32(offset, true);
|
|
749
|
+
return Object.freeze({
|
|
750
|
+
batchId: batch.batchId,
|
|
751
|
+
candidateCount: batch.candidates.length,
|
|
752
|
+
visible: batchVisible,
|
|
753
|
+
occluded: Math.max(0, batch.candidates.length - batchVisible),
|
|
754
|
+
lodHistogram: Object.freeze(
|
|
755
|
+
[...batchHistogram.entries()]
|
|
756
|
+
.sort(([left], [right]) => left - right)
|
|
757
|
+
.map(([level, count]) => Object.freeze({ level, count })),
|
|
758
|
+
),
|
|
759
|
+
});
|
|
760
|
+
}),
|
|
761
|
+
),
|
|
762
|
+
geometryWork,
|
|
763
|
+
rootGeometryWork,
|
|
764
|
+
});
|
|
765
|
+
return this.lodSelection;
|
|
766
|
+
} finally {
|
|
767
|
+
mapped.value.unmap();
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
466
771
|
addPasses<FrameCtx extends RenderGraphFrame>(
|
|
467
772
|
builder: RenderGraphBuilder<FrameCtx>,
|
|
468
773
|
): Result<GpuDrivenViewGraphResources, RenderGraphError> {
|
|
@@ -538,14 +843,21 @@ export class GpuDrivenView {
|
|
|
538
843
|
const counters = importBuffer(
|
|
539
844
|
'gpu-driven.counters',
|
|
540
845
|
buffers.counters,
|
|
541
|
-
this.batchCapacity *
|
|
846
|
+
this.batchCapacity * COUNTER_STRIDE,
|
|
542
847
|
GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_SRC,
|
|
543
848
|
);
|
|
544
849
|
if (!counters.ok) return counters;
|
|
850
|
+
const lodReadback = importBuffer(
|
|
851
|
+
'gpu-driven.lod-selection-readback',
|
|
852
|
+
buffers.lodReadback,
|
|
853
|
+
this.batchCapacity * COUNTER_STRIDE,
|
|
854
|
+
GPU_BUFFER_USAGE_MAP_READ | GPU_BUFFER_USAGE_COPY_DST,
|
|
855
|
+
);
|
|
856
|
+
if (!lodReadback.ok) return lodReadback;
|
|
545
857
|
const visible = importBuffer(
|
|
546
858
|
'gpu-driven.visible',
|
|
547
859
|
buffers.visible,
|
|
548
|
-
this.
|
|
860
|
+
this.visibleBufferCapacity * 8,
|
|
549
861
|
GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_SRC,
|
|
550
862
|
);
|
|
551
863
|
if (!visible.ok) return visible;
|
|
@@ -599,6 +911,21 @@ export class GpuDrivenView {
|
|
|
599
911
|
},
|
|
600
912
|
});
|
|
601
913
|
if (!finalize.ok) return finalize;
|
|
914
|
+
const readback = builder.addCopyPass('gpu-driven.lod-selection-readback', {
|
|
915
|
+
accesses: [
|
|
916
|
+
{ resource: counters.value, usage: 'copy-src' },
|
|
917
|
+
{ resource: lodReadback.value, usage: 'copy-dst' },
|
|
918
|
+
],
|
|
919
|
+
encode: ({ encoder, resources }) => {
|
|
920
|
+
encoder.copyBufferToBuffer(
|
|
921
|
+
resources.buffer(counters.value).unwrap(),
|
|
922
|
+
resources.buffer(lodReadback.value).unwrap(),
|
|
923
|
+
this.batchCapacity * COUNTER_STRIDE,
|
|
924
|
+
);
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
if (!readback.ok) return readback;
|
|
928
|
+
this.telemetryPending = true;
|
|
602
929
|
return ok({
|
|
603
930
|
visible: visible.value,
|
|
604
931
|
primitive: primitive.value,
|
|
@@ -619,6 +946,7 @@ export class GpuDrivenView {
|
|
|
619
946
|
this.buffers = undefined;
|
|
620
947
|
this.bindGroup = undefined;
|
|
621
948
|
this.plan = undefined;
|
|
949
|
+
this.visibleBufferCapacity = 0;
|
|
622
950
|
this.scenePrimitive = undefined;
|
|
623
951
|
this.sceneInstance = undefined;
|
|
624
952
|
this.sceneTransform = undefined;
|
|
@@ -643,10 +971,12 @@ export class GpuDrivenView {
|
|
|
643
971
|
|
|
644
972
|
private rebuildBuffers(
|
|
645
973
|
candidateCapacity: number,
|
|
974
|
+
visibleCapacity: number,
|
|
646
975
|
batchCapacity: number,
|
|
647
976
|
indirectCapacity: number,
|
|
648
977
|
): Result<void, RhiError> {
|
|
649
978
|
const nextCandidate = nextCapacity(candidateCapacity);
|
|
979
|
+
const nextVisible = nextCapacity(visibleCapacity);
|
|
650
980
|
const nextBatch = nextCapacity(batchCapacity);
|
|
651
981
|
const nextIndirect = nextCapacity(indirectCapacity);
|
|
652
982
|
const created: Partial<MutableViewBuffers> = {};
|
|
@@ -666,14 +996,64 @@ export class GpuDrivenView {
|
|
|
666
996
|
return ok(undefined);
|
|
667
997
|
};
|
|
668
998
|
const storage = GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_SRC;
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
999
|
+
const requests: readonly {
|
|
1000
|
+
readonly name: keyof ViewBuffers;
|
|
1001
|
+
readonly size: number;
|
|
1002
|
+
readonly usage: number;
|
|
1003
|
+
readonly storage: boolean;
|
|
1004
|
+
}[] = [
|
|
1005
|
+
{
|
|
1006
|
+
name: 'candidates',
|
|
1007
|
+
size: nextCandidate * CANDIDATE_STRIDE,
|
|
1008
|
+
usage: storage | GPU_BUFFER_USAGE_COPY_DST,
|
|
1009
|
+
storage: true,
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
name: 'batches',
|
|
1013
|
+
size: nextBatch * BATCH_STRIDE,
|
|
1014
|
+
usage: storage | GPU_BUFFER_USAGE_COPY_DST,
|
|
1015
|
+
storage: true,
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
name: 'view',
|
|
1019
|
+
size: VIEW_BYTES,
|
|
1020
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
1021
|
+
storage: false,
|
|
1022
|
+
},
|
|
1023
|
+
{ name: 'counters', size: nextBatch * COUNTER_STRIDE, usage: storage, storage: true },
|
|
1024
|
+
{ name: 'visible', size: nextVisible * 8, usage: storage, storage: true },
|
|
1025
|
+
{
|
|
1026
|
+
name: 'indirect',
|
|
1027
|
+
size: nextIndirect * 20,
|
|
1028
|
+
usage: storage | GPU_BUFFER_USAGE_INDIRECT,
|
|
1029
|
+
storage: true,
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
name: 'lodReadback',
|
|
1033
|
+
size: nextBatch * COUNTER_STRIDE,
|
|
1034
|
+
usage: GPU_BUFFER_USAGE_MAP_READ | GPU_BUFFER_USAGE_COPY_DST,
|
|
1035
|
+
storage: false,
|
|
1036
|
+
},
|
|
1037
|
+
];
|
|
1038
|
+
const maxBufferSize = Number(this.device.limits.maxBufferSize);
|
|
1039
|
+
const maxStorageBufferBindingSize = Number(this.device.limits.maxStorageBufferBindingSize);
|
|
1040
|
+
for (const request of requests) {
|
|
1041
|
+
const limit = request.storage ? maxStorageBufferBindingSize : maxBufferSize;
|
|
1042
|
+
if (!Number.isFinite(limit) || limit <= 0 || request.size <= limit) continue;
|
|
1043
|
+
return err(
|
|
1044
|
+
new RhiError({
|
|
1045
|
+
code: 'limit-exceeded',
|
|
1046
|
+
expected: `GPU-driven ${request.name} buffer (${request.size} B) fits within ${request.storage ? 'device.limits.maxStorageBufferBindingSize' : 'device.limits.maxBufferSize'} (${limit} B)`,
|
|
1047
|
+
hint: 'reduce the visible/candidate workload or split the GPU-driven buffer plan before retrying',
|
|
1048
|
+
detail: {
|
|
1049
|
+
maxStorageBufferBindingSize: limit,
|
|
1050
|
+
requestedBytes: request.size,
|
|
1051
|
+
},
|
|
1052
|
+
}),
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
for (const request of requests) {
|
|
1056
|
+
const result = allocate(request.name, request.size, request.usage);
|
|
677
1057
|
if (!result.ok) {
|
|
678
1058
|
for (const buffer of Object.values(created)) this.device.destroyBuffer(buffer);
|
|
679
1059
|
return result;
|
|
@@ -682,6 +1062,7 @@ export class GpuDrivenView {
|
|
|
682
1062
|
if (this.buffers !== undefined) this.supersededBuffers.push(this.buffers);
|
|
683
1063
|
this.buffers = created as ViewBuffers;
|
|
684
1064
|
this.candidateCapacity = nextCandidate;
|
|
1065
|
+
this.visibleBufferCapacity = nextVisible;
|
|
685
1066
|
this.batchCapacity = nextBatch;
|
|
686
1067
|
this.indirectCapacity = nextIndirect;
|
|
687
1068
|
this.bufferRebuilds += 1;
|
|
@@ -701,3 +1082,4 @@ export class GpuDrivenView {
|
|
|
701
1082
|
void this.device.queue.onSubmittedWorkDone().then(release, release);
|
|
702
1083
|
}
|
|
703
1084
|
}
|
|
1085
|
+
export { decideVisibility } from '../scene/visibility/occlusion-confidence';
|
package/src/gpu-scene-schema.ts
CHANGED
|
@@ -63,6 +63,19 @@ export function gpuSceneWgsl(layout: GpuSceneTableLayout): string {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export const GPU_SCENE_SCHEMAS = Object.freeze({
|
|
66
|
+
lod: {
|
|
67
|
+
name: 'GpuSceneLod',
|
|
68
|
+
fields: [
|
|
69
|
+
{ name: 'generation', type: 'u32' },
|
|
70
|
+
{ name: 'level', type: 'u32' },
|
|
71
|
+
{ name: 'firstIndex', type: 'u32' },
|
|
72
|
+
{ name: 'indexCount', type: 'u32' },
|
|
73
|
+
{ name: 'baseVertex', type: 'i32' },
|
|
74
|
+
{ name: 'screenCoverage', type: 'f32' },
|
|
75
|
+
{ name: 'hysteresis', type: 'f32' },
|
|
76
|
+
{ name: 'ready', type: 'u32' },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
66
79
|
primitive: {
|
|
67
80
|
name: 'GpuScenePrimitive',
|
|
68
81
|
fields: [
|