@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
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { HANDLE_CUBE } from '@forgeax/engine-assets-runtime';
|
|
2
2
|
import { World } from '@forgeax/engine-ecs';
|
|
3
3
|
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
4
|
+
import { vec3 } from '@forgeax/engine-math';
|
|
4
5
|
import { RenderGraphBuilder } from '@forgeax/engine-render-graph';
|
|
6
|
+
import type { BindGroup } from '@forgeax/engine-rhi';
|
|
5
7
|
import { type RhiNullDevice, rhi } from '@forgeax/engine-rhi-null';
|
|
6
8
|
import { Transform } from '@forgeax/engine-scene';
|
|
7
9
|
import {
|
|
10
|
+
createStandardPbrArtifactReceipt,
|
|
8
11
|
DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
|
|
9
12
|
DEFAULT_UNLIT_PARAM_SCHEMA,
|
|
10
13
|
} from '@forgeax/engine-shader';
|
|
@@ -14,7 +17,12 @@ import { createRenderer as constructRenderer } from '../assembly/factory';
|
|
|
14
17
|
import { Camera, MeshFilter, MeshRenderer, PointLight } from '../components';
|
|
15
18
|
import type { MeshGpuHandles } from '../device/gpu-residency';
|
|
16
19
|
import { BatchTopology } from '../gpu-driven/batch-topology';
|
|
17
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
adaptStandardPbrFrameResources,
|
|
22
|
+
GpuDrivenProduction,
|
|
23
|
+
projectStandardPbrScene,
|
|
24
|
+
} from '../gpu-driven/production-raster';
|
|
25
|
+
import { GPU_DRIVEN_VIEW_WGSL } from '../gpu-driven/view-gpu';
|
|
18
26
|
import { GpuBuffer } from '../gpu-resource';
|
|
19
27
|
import { GpuScene } from '../gpu-scene';
|
|
20
28
|
import {
|
|
@@ -23,11 +31,13 @@ import {
|
|
|
23
31
|
GPU_BUFFER_USAGE_UNIFORM,
|
|
24
32
|
GPU_BUFFER_USAGE_VERTEX,
|
|
25
33
|
} from '../gpu-usage';
|
|
26
|
-
import { makeZeroCameraFallbackSnapshot } from '../record/frame-snapshot';
|
|
34
|
+
import { makeZeroCameraFallbackSnapshot, worldEntityKey } from '../record/frame-snapshot';
|
|
27
35
|
import type { RenderPipelineFrame } from '../render-pipeline';
|
|
28
36
|
import type { MaterialSnapshot, RenderableSnapshot } from '../render-system-extract';
|
|
29
37
|
import { RenderScene } from '../scene/render-scene';
|
|
30
38
|
|
|
39
|
+
const STANDARD_PBR_RECEIPT = createStandardPbrArtifactReceipt();
|
|
40
|
+
|
|
31
41
|
const EMPTY_RESOURCE_CLASS = JSON.stringify({ textures: [], samplers: [], video: [] });
|
|
32
42
|
const OUTPUT_TRANSFORM_WGSL =
|
|
33
43
|
'@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> { ' +
|
|
@@ -126,7 +136,7 @@ function snapshot(entityKey: number, assetHandle: number): RenderableSnapshot {
|
|
|
126
136
|
};
|
|
127
137
|
}
|
|
128
138
|
|
|
129
|
-
function mesh(device: RhiNullDevice, indexed = true): MeshGpuHandles {
|
|
139
|
+
function mesh(device: RhiNullDevice, indexed = true, withLod = false): MeshGpuHandles {
|
|
130
140
|
const vertex = device
|
|
131
141
|
.createBuffer({
|
|
132
142
|
size: 144,
|
|
@@ -168,10 +178,401 @@ function mesh(device: RhiNullDevice, indexed = true): MeshGpuHandles {
|
|
|
168
178
|
topology: 'triangle-list',
|
|
169
179
|
},
|
|
170
180
|
],
|
|
181
|
+
...(withLod
|
|
182
|
+
? {
|
|
183
|
+
lodRanges: [
|
|
184
|
+
[{ first: 0, count: 3, baseVertex: 0 }],
|
|
185
|
+
[{ first: 3, count: 1, baseVertex: 0 }],
|
|
186
|
+
],
|
|
187
|
+
}
|
|
188
|
+
: {}),
|
|
171
189
|
};
|
|
172
190
|
}
|
|
173
191
|
|
|
174
192
|
describe('GPU-driven production projection', () => {
|
|
193
|
+
it('retains suppressed LOD candidates for selector accounting while admitting only active draws', async () => {
|
|
194
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
195
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
196
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
197
|
+
const viewLayout = device
|
|
198
|
+
.createBindGroupLayout({
|
|
199
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
200
|
+
})
|
|
201
|
+
.unwrap();
|
|
202
|
+
const projection = new RenderScene();
|
|
203
|
+
const lodMeshGuid = '00000000-0000-7000-8000-000000000001' as never;
|
|
204
|
+
const makeLodSnapshot = (entityKey: number): RenderableSnapshot => {
|
|
205
|
+
const base = snapshot(entityKey, 3);
|
|
206
|
+
const draw = base.gpuDrivenDraws?.[0];
|
|
207
|
+
if (draw === undefined) throw new Error('LOD test snapshot has no draw');
|
|
208
|
+
return {
|
|
209
|
+
...base,
|
|
210
|
+
lods: [{ mesh: lodMeshGuid, screenCoverage: 0.5 }],
|
|
211
|
+
gpuDrivenDraws: [{ ...draw, lodRanges: [{ first: 3, count: 1, baseVertex: 0 }] }],
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
const delta = projection.apply([
|
|
215
|
+
{ kind: 'create', snapshot: makeLodSnapshot(1) },
|
|
216
|
+
{ kind: 'create', snapshot: makeLodSnapshot(2) },
|
|
217
|
+
]);
|
|
218
|
+
const availability = GpuScene.create(device, 2).unwrap();
|
|
219
|
+
expect(availability.status).toBe('available');
|
|
220
|
+
if (availability.status !== 'available') return;
|
|
221
|
+
availability.scene.sync(delta).unwrap();
|
|
222
|
+
const topology = new BatchTopology();
|
|
223
|
+
topology.rebuild(projection.slotsSnapshot());
|
|
224
|
+
const production = new GpuDrivenProduction(device, {
|
|
225
|
+
createShaderModule: () => ok(shader),
|
|
226
|
+
});
|
|
227
|
+
const prepared = production
|
|
228
|
+
.prepare({
|
|
229
|
+
scene: {
|
|
230
|
+
scene: availability.scene,
|
|
231
|
+
plan: topology.plan(),
|
|
232
|
+
slots: projection.slotsSnapshot(),
|
|
233
|
+
},
|
|
234
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
235
|
+
meshes: new Map([[3, mesh(device, true, true)]]),
|
|
236
|
+
viewBindGroupLayout: viewLayout,
|
|
237
|
+
meshResidencyEpoch: 1,
|
|
238
|
+
hdrp: false,
|
|
239
|
+
activeEntityKeys: new Set([worldEntityKey(0, 1)]),
|
|
240
|
+
activeEntityRevision: 1,
|
|
241
|
+
})
|
|
242
|
+
.unwrap();
|
|
243
|
+
expect(prepared?.entityKeys).toEqual(new Set([worldEntityKey(0, 1)]));
|
|
244
|
+
// Both candidates are uploaded to the selector (2 * 304 bytes), while
|
|
245
|
+
// only the active entity is admitted into the compacted draw stream.
|
|
246
|
+
expect(production.inspect()).toMatchObject({
|
|
247
|
+
candidateUploadBytes: 2 * 304,
|
|
248
|
+
batchCount: 1,
|
|
249
|
+
indirectDrawCount: 1,
|
|
250
|
+
});
|
|
251
|
+
if (prepared === undefined) return;
|
|
252
|
+
const projected = prepared
|
|
253
|
+
.project(new RenderGraphBuilder<RenderPipelineFrame>(), 'rgba8unorm', 1)
|
|
254
|
+
.unwrap();
|
|
255
|
+
expect(projected.accesses.length).toBeGreaterThan(0);
|
|
256
|
+
expect(production.inspect()).toMatchObject({
|
|
257
|
+
batchBindGroupCreates: 1,
|
|
258
|
+
indirectDrawCount: 1,
|
|
259
|
+
});
|
|
260
|
+
expect(production.inspect().indirectDrawCount).toBeGreaterThan(0);
|
|
261
|
+
expect(production.inspect().indirectDrawCount).toBeLessThanOrEqual(
|
|
262
|
+
production.inspect().batchCount,
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const suppressed = production
|
|
266
|
+
.prepare({
|
|
267
|
+
scene: {
|
|
268
|
+
scene: availability.scene,
|
|
269
|
+
plan: topology.plan(),
|
|
270
|
+
slots: projection.slotsSnapshot(),
|
|
271
|
+
},
|
|
272
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
273
|
+
meshes: new Map([[3, mesh(device, true, true)]]),
|
|
274
|
+
viewBindGroupLayout: viewLayout,
|
|
275
|
+
meshResidencyEpoch: 1,
|
|
276
|
+
hdrp: false,
|
|
277
|
+
activeEntityKeys: new Set(),
|
|
278
|
+
activeEntityRevision: 2,
|
|
279
|
+
})
|
|
280
|
+
.unwrap();
|
|
281
|
+
expect(suppressed?.topologySignature).not.toBe(prepared.topologySignature);
|
|
282
|
+
expect(production.inspect()).toMatchObject({
|
|
283
|
+
batchCount: 1,
|
|
284
|
+
indirectDrawCount: 0,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const restored = production
|
|
288
|
+
.prepare({
|
|
289
|
+
scene: {
|
|
290
|
+
scene: availability.scene,
|
|
291
|
+
plan: topology.plan(),
|
|
292
|
+
slots: projection.slotsSnapshot(),
|
|
293
|
+
},
|
|
294
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
295
|
+
meshes: new Map([[3, mesh(device, true, true)]]),
|
|
296
|
+
viewBindGroupLayout: viewLayout,
|
|
297
|
+
meshResidencyEpoch: 1,
|
|
298
|
+
hdrp: false,
|
|
299
|
+
activeEntityKeys: new Set([worldEntityKey(0, 1)]),
|
|
300
|
+
activeEntityRevision: 3,
|
|
301
|
+
})
|
|
302
|
+
.unwrap();
|
|
303
|
+
expect(restored?.topologySignature).not.toBe(suppressed?.topologySignature);
|
|
304
|
+
expect(production.inspect()).toMatchObject({
|
|
305
|
+
batchCount: 1,
|
|
306
|
+
indirectDrawCount: 1,
|
|
307
|
+
});
|
|
308
|
+
expect(GPU_DRIVEN_VIEW_WGSL.indexOf('atomicAdd(&counters[lodCounterIndex')).toBeLessThan(
|
|
309
|
+
GPU_DRIVEN_VIEW_WGSL.indexOf('if (candidate.submitAdmission == 0u)'),
|
|
310
|
+
);
|
|
311
|
+
expect(GPU_DRIVEN_VIEW_WGSL.indexOf('if (candidate.submitAdmission == 0u)')).toBeLessThan(
|
|
312
|
+
GPU_DRIVEN_VIEW_WGSL.indexOf('atomicAdd(&counters[counterIndex(candidate.batchIndex)], 1u)'),
|
|
313
|
+
);
|
|
314
|
+
production.dispose();
|
|
315
|
+
availability.scene.dispose();
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('keeps a suppressed-only batch in selector telemetry without raster resources', async () => {
|
|
319
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
320
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
321
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
322
|
+
const viewLayout = device
|
|
323
|
+
.createBindGroupLayout({
|
|
324
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
325
|
+
})
|
|
326
|
+
.unwrap();
|
|
327
|
+
const projection = new RenderScene();
|
|
328
|
+
const candidate = snapshot(1, 3);
|
|
329
|
+
const delta = projection.apply([{ kind: 'create', snapshot: candidate }]);
|
|
330
|
+
const availability = GpuScene.create(device, 1).unwrap();
|
|
331
|
+
expect(availability.status).toBe('available');
|
|
332
|
+
if (availability.status !== 'available') return;
|
|
333
|
+
availability.scene.sync(delta).unwrap();
|
|
334
|
+
const topology = new BatchTopology();
|
|
335
|
+
topology.rebuild(projection.slotsSnapshot());
|
|
336
|
+
const production = new GpuDrivenProduction(device, {
|
|
337
|
+
createShaderModule: () => ok(shader),
|
|
338
|
+
});
|
|
339
|
+
const prepared = production
|
|
340
|
+
.prepare({
|
|
341
|
+
scene: {
|
|
342
|
+
scene: availability.scene,
|
|
343
|
+
plan: topology.plan(),
|
|
344
|
+
slots: projection.slotsSnapshot(),
|
|
345
|
+
},
|
|
346
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
347
|
+
meshes: new Map([[3, mesh(device)]]),
|
|
348
|
+
viewBindGroupLayout: viewLayout,
|
|
349
|
+
meshResidencyEpoch: 1,
|
|
350
|
+
hdrp: false,
|
|
351
|
+
activeEntityKeys: new Set(),
|
|
352
|
+
activeEntityRevision: 1,
|
|
353
|
+
})
|
|
354
|
+
.unwrap();
|
|
355
|
+
expect(prepared).toBeDefined();
|
|
356
|
+
expect(production.requiresSceneRows()).toBe(true);
|
|
357
|
+
expect(production.inspect()).toMatchObject({
|
|
358
|
+
batchCount: 1,
|
|
359
|
+
indirectDrawCount: 0,
|
|
360
|
+
batchBindGroupCreates: 0,
|
|
361
|
+
});
|
|
362
|
+
if (prepared === undefined) return;
|
|
363
|
+
prepared.project(new RenderGraphBuilder<RenderPipelineFrame>(), 'rgba8unorm', 1).unwrap();
|
|
364
|
+
expect(production.inspect()).toMatchObject({
|
|
365
|
+
batchCount: 1,
|
|
366
|
+
indirectDrawCount: 0,
|
|
367
|
+
batchBindGroupCreates: 0,
|
|
368
|
+
});
|
|
369
|
+
production.dispose();
|
|
370
|
+
availability.scene.dispose();
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('keeps GPU batches World-homogeneous for same-submit counter attribution', async () => {
|
|
374
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
375
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
376
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
377
|
+
const viewLayout = device
|
|
378
|
+
.createBindGroupLayout({
|
|
379
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
380
|
+
})
|
|
381
|
+
.unwrap();
|
|
382
|
+
const projection = new RenderScene();
|
|
383
|
+
const first = snapshot(1, 3);
|
|
384
|
+
const second = { ...snapshot(2, 3), worldId: 1 };
|
|
385
|
+
const delta = projection.apply([
|
|
386
|
+
{ kind: 'create', snapshot: first },
|
|
387
|
+
{ kind: 'create', snapshot: second },
|
|
388
|
+
]);
|
|
389
|
+
const availability = GpuScene.create(device, 2).unwrap();
|
|
390
|
+
expect(availability.status).toBe('available');
|
|
391
|
+
if (availability.status !== 'available') return;
|
|
392
|
+
availability.scene.sync(delta).unwrap();
|
|
393
|
+
const topology = new BatchTopology();
|
|
394
|
+
topology.rebuild(projection.slotsSnapshot());
|
|
395
|
+
const production = new GpuDrivenProduction(device, {
|
|
396
|
+
createShaderModule: () => ok(shader),
|
|
397
|
+
});
|
|
398
|
+
const prepared = production
|
|
399
|
+
.prepare({
|
|
400
|
+
scene: {
|
|
401
|
+
scene: availability.scene,
|
|
402
|
+
plan: topology.plan(),
|
|
403
|
+
slots: projection.slotsSnapshot(),
|
|
404
|
+
},
|
|
405
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
406
|
+
meshes: new Map([[3, mesh(device)]]),
|
|
407
|
+
viewBindGroupLayout: viewLayout,
|
|
408
|
+
meshResidencyEpoch: 1,
|
|
409
|
+
hdrp: false,
|
|
410
|
+
})
|
|
411
|
+
.unwrap();
|
|
412
|
+
expect(prepared).toBeDefined();
|
|
413
|
+
expect(production.inspect()).toMatchObject({ batchCount: 2, indirectDrawCount: 2 });
|
|
414
|
+
production.dispose();
|
|
415
|
+
availability.scene.dispose();
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('uses renderer-local World keys for CPU validation telemetry', async () => {
|
|
419
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
420
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
421
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
422
|
+
const production = new GpuDrivenProduction(device, {
|
|
423
|
+
createShaderModule: () => ok(shader),
|
|
424
|
+
});
|
|
425
|
+
const gpuLodRow = {
|
|
426
|
+
...snapshot(7, 3),
|
|
427
|
+
worldId: 1,
|
|
428
|
+
lods: [{ mesh: '00000000-0000-7000-8000-000000000001' as never, screenCoverage: 0.5 }],
|
|
429
|
+
};
|
|
430
|
+
const plainFallbackRow = { ...snapshot(2, 3), worldId: 0 };
|
|
431
|
+
const gpuOwned = new Set([worldEntityKey(101, gpuLodRow.entityKey)]);
|
|
432
|
+
const worldKeys = [202, 101];
|
|
433
|
+
|
|
434
|
+
// A GPU-owned row is removed from validation before this telemetry hook;
|
|
435
|
+
// the remaining plain row must still be counted as the one CPU fallback.
|
|
436
|
+
production.recordCpuValidation([{ source: plainFallbackRow }], gpuOwned, worldKeys);
|
|
437
|
+
expect(production.inspect()).toMatchObject({
|
|
438
|
+
cpuFallbackDrawItems: 1,
|
|
439
|
+
validatedGpuOwnedRows: 0,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Prove the same renderer-local namespace recognizes the GPU row when a
|
|
443
|
+
// diagnostic caller supplies it directly, even though its extracted
|
|
444
|
+
// RenderableSnapshot still carries the reordered array index.
|
|
445
|
+
production.recordCpuValidation([{ source: gpuLodRow }], gpuOwned, worldKeys);
|
|
446
|
+
expect(production.inspect()).toMatchObject({
|
|
447
|
+
cpuFallbackDrawItems: 1,
|
|
448
|
+
validatedGpuOwnedRows: 1,
|
|
449
|
+
});
|
|
450
|
+
production.dispose();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('coalesces LOD candidates that share the selected geometry range', async () => {
|
|
454
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
455
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
456
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
457
|
+
const viewLayout = device
|
|
458
|
+
.createBindGroupLayout({
|
|
459
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
460
|
+
})
|
|
461
|
+
.unwrap();
|
|
462
|
+
const projection = new RenderScene();
|
|
463
|
+
const lodMeshGuid = '00000000-0000-7000-8000-000000000001' as never;
|
|
464
|
+
const makeLodSnapshot = (entityKey: number): RenderableSnapshot => {
|
|
465
|
+
const base = snapshot(entityKey, 3);
|
|
466
|
+
const draw = base.gpuDrivenDraws?.[0];
|
|
467
|
+
if (draw === undefined) throw new Error('LOD test snapshot has no draw');
|
|
468
|
+
return {
|
|
469
|
+
...base,
|
|
470
|
+
lods: [{ mesh: lodMeshGuid, screenCoverage: 0.5 }],
|
|
471
|
+
gpuDrivenDraws: [{ ...draw, lodRanges: [{ first: 3, count: 1, baseVertex: 0 }] }],
|
|
472
|
+
};
|
|
473
|
+
};
|
|
474
|
+
const delta = projection.apply([
|
|
475
|
+
{ kind: 'create', snapshot: makeLodSnapshot(1) },
|
|
476
|
+
{ kind: 'create', snapshot: makeLodSnapshot(2) },
|
|
477
|
+
]);
|
|
478
|
+
const availability = GpuScene.create(device, 2).unwrap();
|
|
479
|
+
expect(availability.status).toBe('available');
|
|
480
|
+
if (availability.status !== 'available') return;
|
|
481
|
+
availability.scene.sync(delta).unwrap();
|
|
482
|
+
const topology = new BatchTopology();
|
|
483
|
+
topology.rebuild(projection.slotsSnapshot());
|
|
484
|
+
const production = new GpuDrivenProduction(device, {
|
|
485
|
+
createShaderModule: () => ok(shader),
|
|
486
|
+
});
|
|
487
|
+
const prepared = production
|
|
488
|
+
.prepare({
|
|
489
|
+
scene: {
|
|
490
|
+
scene: availability.scene,
|
|
491
|
+
plan: topology.plan(),
|
|
492
|
+
slots: projection.slotsSnapshot(),
|
|
493
|
+
},
|
|
494
|
+
camera: { ...makeZeroCameraFallbackSnapshot(), position: vec3.create(0, 0, 5) },
|
|
495
|
+
meshes: new Map([[3, mesh(device, true, true)]]),
|
|
496
|
+
viewBindGroupLayout: viewLayout,
|
|
497
|
+
meshResidencyEpoch: 1,
|
|
498
|
+
hdrp: false,
|
|
499
|
+
})
|
|
500
|
+
.unwrap();
|
|
501
|
+
expect(prepared).toBeDefined();
|
|
502
|
+
expect(production.inspect()).toMatchObject({
|
|
503
|
+
batchCount: 1,
|
|
504
|
+
indirectDrawCount: 1,
|
|
505
|
+
candidateUploadBytes: 2 * 304,
|
|
506
|
+
});
|
|
507
|
+
production.dispose();
|
|
508
|
+
availability.scene.dispose();
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('projects scene rows from the receipt-selected scene-index entry', () => {
|
|
512
|
+
const result = projectStandardPbrScene({
|
|
513
|
+
artifact: {
|
|
514
|
+
material: 'forgeax::default-standard-pbr',
|
|
515
|
+
pass: 'forward',
|
|
516
|
+
wgsl: 'standard-pbr',
|
|
517
|
+
layoutIdentity: STANDARD_PBR_RECEIPT.reflection.layoutIdentity,
|
|
518
|
+
bindings: [],
|
|
519
|
+
deps: [],
|
|
520
|
+
vertexInputs: STANDARD_PBR_RECEIPT.vertexInputs.map((input) => ({ ...input })),
|
|
521
|
+
receipt: STANDARD_PBR_RECEIPT,
|
|
522
|
+
},
|
|
523
|
+
candidates: [
|
|
524
|
+
{
|
|
525
|
+
primitiveIndex: 3,
|
|
526
|
+
world: Float32Array.from([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2, 3, 4, 1]),
|
|
527
|
+
},
|
|
528
|
+
],
|
|
529
|
+
});
|
|
530
|
+
expect(result.ok).toBe(true);
|
|
531
|
+
if (!result.ok) return;
|
|
532
|
+
expect(result.value.entryPoint).toBe('vs_scene_index');
|
|
533
|
+
expect(result.value.rows[0]).toBe(1);
|
|
534
|
+
expect(result.value.rows[12]).toBe(2);
|
|
535
|
+
expect(result.value.rows[13]).toBe(3);
|
|
536
|
+
expect(result.value.rows[14]).toBe(4);
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
it('adapts the four existing Standard PBR groups from one receipt', () => {
|
|
540
|
+
const groups = {
|
|
541
|
+
view: { id: 'view' },
|
|
542
|
+
material: { id: 'material' },
|
|
543
|
+
mesh: { id: 'mesh' },
|
|
544
|
+
instances: { id: 'instances' },
|
|
545
|
+
} as unknown as {
|
|
546
|
+
readonly view: BindGroup;
|
|
547
|
+
readonly material: BindGroup;
|
|
548
|
+
readonly mesh: BindGroup;
|
|
549
|
+
readonly instances: BindGroup;
|
|
550
|
+
};
|
|
551
|
+
const result = adaptStandardPbrFrameResources({
|
|
552
|
+
artifact: {
|
|
553
|
+
material: 'forgeax::default-standard-pbr',
|
|
554
|
+
pass: 'forward',
|
|
555
|
+
wgsl: 'standard-pbr',
|
|
556
|
+
layoutIdentity: STANDARD_PBR_RECEIPT.reflection.layoutIdentity,
|
|
557
|
+
bindings: [],
|
|
558
|
+
deps: [],
|
|
559
|
+
vertexInputs: STANDARD_PBR_RECEIPT.vertexInputs.map((input) => ({ ...input })),
|
|
560
|
+
receipt: STANDARD_PBR_RECEIPT,
|
|
561
|
+
},
|
|
562
|
+
...groups,
|
|
563
|
+
});
|
|
564
|
+
expect(result.ok).toBe(true);
|
|
565
|
+
if (!result.ok) return;
|
|
566
|
+
expect(result.value.entryPoint).toBe('vs_scene_index');
|
|
567
|
+
expect(result.value.bindGroups).toEqual([
|
|
568
|
+
groups.view,
|
|
569
|
+
groups.material,
|
|
570
|
+
groups.mesh,
|
|
571
|
+
groups.instances,
|
|
572
|
+
]);
|
|
573
|
+
expect(result.value.resourceSlots).toEqual(STANDARD_PBR_RECEIPT.resourceSlots);
|
|
574
|
+
});
|
|
575
|
+
|
|
175
576
|
it('projects aligned multi-batch compute work into one indirect raster pass', async () => {
|
|
176
577
|
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
177
578
|
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
@@ -194,7 +595,7 @@ describe('GPU-driven production projection', () => {
|
|
|
194
595
|
const firstDraw = first.gpuDrivenDraws?.[0];
|
|
195
596
|
expect(firstDraw).toBeDefined();
|
|
196
597
|
if (firstDraw === undefined) return;
|
|
197
|
-
const second = snapshot(2, 4);
|
|
598
|
+
const second = { ...snapshot(2, 4), worldId: 1 };
|
|
198
599
|
const secondDraw = second.gpuDrivenDraws?.[0];
|
|
199
600
|
expect(secondDraw).toBeDefined();
|
|
200
601
|
if (secondDraw === undefined) return;
|
|
@@ -252,16 +653,17 @@ describe('GPU-driven production projection', () => {
|
|
|
252
653
|
meshes,
|
|
253
654
|
viewBindGroupLayout: viewLayout,
|
|
254
655
|
meshResidencyEpoch: 1,
|
|
656
|
+
hdrp: false,
|
|
255
657
|
})
|
|
256
658
|
.unwrap();
|
|
257
659
|
expect(prepared).toBeDefined();
|
|
258
660
|
if (prepared === undefined) return;
|
|
259
|
-
expect([...prepared.entityKeys]).toEqual([1, 2]);
|
|
661
|
+
expect([...prepared.entityKeys]).toEqual([worldEntityKey(0, 1), worldEntityKey(1, 2)]);
|
|
260
662
|
expect(production.inspect()).toMatchObject({
|
|
261
663
|
gpuOwnedSnapshotsMaterialized: 2,
|
|
262
664
|
filteredPlanBuilds: 1,
|
|
263
|
-
candidateUploadBytes:
|
|
264
|
-
batchUploadBytes:
|
|
665
|
+
candidateUploadBytes: 912,
|
|
666
|
+
batchUploadBytes: 96,
|
|
265
667
|
viewBindGroupCreates: 1,
|
|
266
668
|
});
|
|
267
669
|
|
|
@@ -276,6 +678,7 @@ describe('GPU-driven production projection', () => {
|
|
|
276
678
|
meshes,
|
|
277
679
|
viewBindGroupLayout: viewLayout,
|
|
278
680
|
meshResidencyEpoch: 1,
|
|
681
|
+
hdrp: false,
|
|
279
682
|
})
|
|
280
683
|
.unwrap();
|
|
281
684
|
expect(stable?.topologySignature).toBe(prepared.topologySignature);
|
|
@@ -287,10 +690,40 @@ describe('GPU-driven production projection', () => {
|
|
|
287
690
|
viewConstantsUploadBytes: 112,
|
|
288
691
|
viewBindGroupCreates: 0,
|
|
289
692
|
batchBindGroupCreates: 0,
|
|
693
|
+
indirectDrawCount: 3,
|
|
290
694
|
});
|
|
291
695
|
|
|
696
|
+
// The scene topology is unchanged, but projectedHeight is camera-owned
|
|
697
|
+
// input to the GPU LOD selector. A moved camera must rebuild the filtered
|
|
698
|
+
// candidate plan instead of reusing the previous view's heights.
|
|
699
|
+
const movedCamera = {
|
|
700
|
+
...makeZeroCameraFallbackSnapshot(),
|
|
701
|
+
position: vec3.create(0, 0, 6),
|
|
702
|
+
};
|
|
703
|
+
const moved = production
|
|
704
|
+
.prepare({
|
|
705
|
+
scene: {
|
|
706
|
+
scene: availability.scene,
|
|
707
|
+
plan: topology.plan(),
|
|
708
|
+
slots: projection.slotsSnapshot(),
|
|
709
|
+
},
|
|
710
|
+
camera: movedCamera,
|
|
711
|
+
meshes,
|
|
712
|
+
viewBindGroupLayout: viewLayout,
|
|
713
|
+
meshResidencyEpoch: 1,
|
|
714
|
+
hdrp: false,
|
|
715
|
+
})
|
|
716
|
+
.unwrap();
|
|
717
|
+
expect(moved).toBeDefined();
|
|
718
|
+
expect(production.inspect()).toMatchObject({
|
|
719
|
+
filteredPlanBuilds: 1,
|
|
720
|
+
candidateUploadBytes: 912,
|
|
721
|
+
batchUploadBytes: 96,
|
|
722
|
+
});
|
|
723
|
+
if (moved === undefined) return;
|
|
724
|
+
|
|
292
725
|
const graph = new RenderGraphBuilder<RenderPipelineFrame>();
|
|
293
|
-
const gpu =
|
|
726
|
+
const gpu = moved.project(graph, 'rgba8unorm', 1).unwrap();
|
|
294
727
|
const color = graph
|
|
295
728
|
.createTexture('gpu-driven-production-color', {
|
|
296
729
|
format: 'rgba8unorm',
|
|
@@ -328,6 +761,54 @@ describe('GPU-driven production projection', () => {
|
|
|
328
761
|
]);
|
|
329
762
|
expect(device.totalDispatchCount).toBe(3);
|
|
330
763
|
expect(device.totalDrawCount).toBe(3);
|
|
764
|
+
expect(production.inspect().indirectDrawCount).toBe(3);
|
|
765
|
+
expect(production.inspect().indirectDrawCount).toBeLessThanOrEqual(
|
|
766
|
+
production.inspect().batchCount,
|
|
767
|
+
);
|
|
768
|
+
|
|
769
|
+
// The facet projection is the same admission set consumed by the GPU
|
|
770
|
+
// candidate upload. A suppressed entity must disappear from the indirect
|
|
771
|
+
// plan without making the remaining entity fall back to CPU validation.
|
|
772
|
+
const activeOnly = production
|
|
773
|
+
.prepare({
|
|
774
|
+
scene: {
|
|
775
|
+
scene: availability.scene,
|
|
776
|
+
plan: topology.plan(),
|
|
777
|
+
slots: projection.slotsSnapshot(),
|
|
778
|
+
},
|
|
779
|
+
camera: movedCamera,
|
|
780
|
+
meshes,
|
|
781
|
+
viewBindGroupLayout: viewLayout,
|
|
782
|
+
meshResidencyEpoch: 1,
|
|
783
|
+
hdrp: false,
|
|
784
|
+
activeEntityKeys: new Set([worldEntityKey(0, 1)]),
|
|
785
|
+
activeEntityRevision: 1,
|
|
786
|
+
})
|
|
787
|
+
.unwrap();
|
|
788
|
+
expect(activeOnly?.entityKeys).toEqual(new Set([1]));
|
|
789
|
+
expect(activeOnly?.ownsAllRenderables).toBe(true);
|
|
790
|
+
|
|
791
|
+
const changedActive = production
|
|
792
|
+
.prepare({
|
|
793
|
+
scene: {
|
|
794
|
+
scene: availability.scene,
|
|
795
|
+
plan: topology.plan(),
|
|
796
|
+
slots: projection.slotsSnapshot(),
|
|
797
|
+
},
|
|
798
|
+
camera: movedCamera,
|
|
799
|
+
meshes,
|
|
800
|
+
viewBindGroupLayout: viewLayout,
|
|
801
|
+
meshResidencyEpoch: 1,
|
|
802
|
+
hdrp: false,
|
|
803
|
+
activeEntityKeys: new Set([worldEntityKey(1, 2)]),
|
|
804
|
+
activeEntityRevision: 2,
|
|
805
|
+
})
|
|
806
|
+
.unwrap();
|
|
807
|
+
expect(changedActive?.entityKeys).toEqual(new Set([worldEntityKey(1, 2)]));
|
|
808
|
+
// Production inspection counters are per-prepare-frame facts. The changed
|
|
809
|
+
// revision must trigger exactly one rebuild for this frame, not accumulate
|
|
810
|
+
// the prior frame's count.
|
|
811
|
+
expect(production.inspect().filteredPlanBuilds).toBe(1);
|
|
331
812
|
|
|
332
813
|
const replacement = GpuScene.create(device, 2).unwrap();
|
|
333
814
|
expect(replacement.status).toBe('available');
|
|
@@ -344,6 +825,7 @@ describe('GPU-driven production projection', () => {
|
|
|
344
825
|
meshes,
|
|
345
826
|
viewBindGroupLayout: viewLayout,
|
|
346
827
|
meshResidencyEpoch: 1,
|
|
828
|
+
hdrp: false,
|
|
347
829
|
})
|
|
348
830
|
.unwrap();
|
|
349
831
|
expect(replacementPrepared?.topologySignature).not.toBe(prepared.topologySignature);
|
|
@@ -353,6 +835,75 @@ describe('GPU-driven production projection', () => {
|
|
|
353
835
|
availability.scene.dispose();
|
|
354
836
|
});
|
|
355
837
|
|
|
838
|
+
it('keeps an ineligible production topology empty while the camera moves', async () => {
|
|
839
|
+
const adapter = (await rhi.requestAdapter()).unwrap();
|
|
840
|
+
const device = (await adapter.requestDevice()).unwrap() as RhiNullDevice;
|
|
841
|
+
const shader = (await rhi.createShaderModule(device, { code: 'synthetic' })).unwrap();
|
|
842
|
+
const viewLayout = device
|
|
843
|
+
.createBindGroupLayout({
|
|
844
|
+
entries: [{ binding: 0, visibility: 0x1, buffer: { type: 'uniform' } }],
|
|
845
|
+
})
|
|
846
|
+
.unwrap();
|
|
847
|
+
const base = snapshot(1, 3);
|
|
848
|
+
const material = {
|
|
849
|
+
...base.material,
|
|
850
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
851
|
+
};
|
|
852
|
+
const projection = new RenderScene();
|
|
853
|
+
const delta = projection.apply([
|
|
854
|
+
{ kind: 'create', snapshot: { ...base, material, materials: [material] } },
|
|
855
|
+
]);
|
|
856
|
+
const availability = GpuScene.create(device, 1).unwrap();
|
|
857
|
+
expect(availability.status).toBe('available');
|
|
858
|
+
if (availability.status !== 'available') return;
|
|
859
|
+
availability.scene.sync(delta).unwrap();
|
|
860
|
+
const topology = new BatchTopology();
|
|
861
|
+
topology.rebuild(projection.slotsSnapshot());
|
|
862
|
+
const production = new GpuDrivenProduction(device, {
|
|
863
|
+
createShaderModule: () => ok(shader),
|
|
864
|
+
});
|
|
865
|
+
const input = {
|
|
866
|
+
scene: {
|
|
867
|
+
scene: availability.scene,
|
|
868
|
+
plan: topology.plan(),
|
|
869
|
+
slots: projection.slotsSnapshot(),
|
|
870
|
+
},
|
|
871
|
+
meshes: new Map([[3, mesh(device)]]),
|
|
872
|
+
viewBindGroupLayout: viewLayout,
|
|
873
|
+
hdrp: false,
|
|
874
|
+
} as const;
|
|
875
|
+
|
|
876
|
+
expect(
|
|
877
|
+
production
|
|
878
|
+
.prepare({
|
|
879
|
+
...input,
|
|
880
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
881
|
+
meshResidencyEpoch: 1,
|
|
882
|
+
})
|
|
883
|
+
.unwrap(),
|
|
884
|
+
).toBeUndefined();
|
|
885
|
+
expect(production.requiresSceneRows()).toBe(false);
|
|
886
|
+
expect(production.inspect().filteredPlanBuilds).toBe(1);
|
|
887
|
+
expect(
|
|
888
|
+
production
|
|
889
|
+
.prepare({
|
|
890
|
+
...input,
|
|
891
|
+
camera: { ...makeZeroCameraFallbackSnapshot(), position: vec3.create(0, 0, 6) },
|
|
892
|
+
meshResidencyEpoch: 2,
|
|
893
|
+
})
|
|
894
|
+
.unwrap(),
|
|
895
|
+
).toBeUndefined();
|
|
896
|
+
expect(production.requiresSceneRows()).toBe(false);
|
|
897
|
+
expect(production.inspect()).toMatchObject({
|
|
898
|
+
filteredPlanBuilds: 0,
|
|
899
|
+
gpuOwnedSnapshotsMaterialized: 0,
|
|
900
|
+
gpuOwnedEntityCount: 0,
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
production.dispose();
|
|
904
|
+
availability.scene.dispose();
|
|
905
|
+
});
|
|
906
|
+
|
|
356
907
|
it('activates inside the renderer-owned clustered Standard graph and removes the CPU draw', async () => {
|
|
357
908
|
const canvas = {
|
|
358
909
|
width: 1,
|
|
@@ -401,7 +952,19 @@ describe('GPU-driven production projection', () => {
|
|
|
401
952
|
expect(renderer.perFramePassNames).toEqual(
|
|
402
953
|
expect.arrayContaining(['cluster-membership-producer', 'main', 'output-transform']),
|
|
403
954
|
);
|
|
404
|
-
expect(
|
|
955
|
+
expect(renderer.perFramePassNames).toContain('output-transform');
|
|
956
|
+
// Storage-buffer LDR frames keep the scene linear until the one output
|
|
957
|
+
// transform writes the encoded surface; the GPU-driven indirect raster
|
|
958
|
+
// draw and that fullscreen pass are the only draws in this topology.
|
|
959
|
+
// A plain mesh has no LOD relation, so it stays on the CPU visibility
|
|
960
|
+
// path for occlusion while GPU-driven raster + output-transform remain
|
|
961
|
+
// the only draws in this graph.
|
|
962
|
+
expect(device.totalDrawCount).toBe(2);
|
|
963
|
+
expect(renderer.inspect().lodOcclusion).toMatchObject({
|
|
964
|
+
fallback: { active: false },
|
|
965
|
+
degradation: { active: false },
|
|
966
|
+
pagePressure: { used: 0, capacity: 3 * 4096 },
|
|
967
|
+
});
|
|
405
968
|
expect(renderer.renderScene).toMatchObject({
|
|
406
969
|
worldEntitiesScanned: 0,
|
|
407
970
|
fullRebuilds: 1,
|