@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
|
@@ -5,6 +5,7 @@ import type { RenderPipelineAsset } from '@forgeax/engine-types';
|
|
|
5
5
|
import type { RecoverFailure } from '../errors/recover';
|
|
6
6
|
import type { ObservationUnavailableError, RenderError } from '../errors/render';
|
|
7
7
|
import type { RenderFeature, RenderFeatureDiagnostics } from '../features/types';
|
|
8
|
+
import type { GpuDrivenProductionInspection, LodOcclusionInspection } from '../inspection-types';
|
|
8
9
|
import type { MeshMaterialBindingObservation } from '../mesh-material-bindings';
|
|
9
10
|
import type { FrameObservation, FrameObservationOptions } from '../record/frame';
|
|
10
11
|
import type { CurrentGraphTarget, GraphTargetCaptureRequest } from '../record/frame-snapshot';
|
|
@@ -69,6 +70,15 @@ export interface RendererHostImplementation {
|
|
|
69
70
|
destroyRenderTarget(target: RenderTarget): RenderResult<void, RenderError>;
|
|
70
71
|
setProfile(profile: RenderProfile): RenderResult<void, RenderError>;
|
|
71
72
|
inspect(): RenderInspection;
|
|
73
|
+
/**
|
|
74
|
+
* Internal bounded inspection for high-cardinality producers. The public
|
|
75
|
+
* snapshot also carries the full persistent render-scene record table, which
|
|
76
|
+
* is intentionally not materialized for per-frame performance evidence.
|
|
77
|
+
*/
|
|
78
|
+
inspectLodOcclusion(): {
|
|
79
|
+
readonly lodOcclusion: LodOcclusionInspection | undefined;
|
|
80
|
+
readonly gpuDriven: GpuDrivenProductionInspection;
|
|
81
|
+
};
|
|
72
82
|
drawFrame(request: RenderFrameInput): RenderResult<FrameReceipt, RhiError | RenderError>;
|
|
73
83
|
observe(
|
|
74
84
|
receipt: FrameReceipt,
|
package/src/cluster-binner.ts
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
import { type Mat4, type Vec3, vec3 } from '@forgeax/engine-math';
|
|
29
29
|
import { err, ok, type Result } from '@forgeax/engine-rhi';
|
|
30
|
+
import type { RectAreaWorldFrame } from './render-system-extract';
|
|
30
31
|
|
|
31
32
|
// ── types ──────────────────────────────────────────────────────────────────
|
|
32
33
|
|
|
@@ -174,6 +175,36 @@ export function deriveCullingRadius(range: number, intensity: number, threshold
|
|
|
174
175
|
return Math.min(derived, 1000);
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
/** Conservative bounding-sphere radius for a finite Rect and its light range. */
|
|
179
|
+
export function rectAreaClusterRadius(frame: RectAreaWorldFrame, range: number): number {
|
|
180
|
+
return Math.max(0, range) + Math.hypot(frame.halfWidth, frame.halfHeight);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Test the finite Rect's closest-point distance against a strict range window. */
|
|
184
|
+
export function rectAreaIsInRange(
|
|
185
|
+
frame: RectAreaWorldFrame,
|
|
186
|
+
point: ArrayLike<number>,
|
|
187
|
+
range: number,
|
|
188
|
+
): boolean {
|
|
189
|
+
const toPoint = vec3.create(
|
|
190
|
+
(point[0] ?? 0) - (frame.center[0] ?? 0),
|
|
191
|
+
(point[1] ?? 0) - (frame.center[1] ?? 0),
|
|
192
|
+
(point[2] ?? 0) - (frame.center[2] ?? 0),
|
|
193
|
+
);
|
|
194
|
+
const localX = Math.min(
|
|
195
|
+
frame.halfWidth,
|
|
196
|
+
Math.max(-frame.halfWidth, vec3.dot(toPoint, frame.axisX)),
|
|
197
|
+
);
|
|
198
|
+
const localY = Math.min(
|
|
199
|
+
frame.halfHeight,
|
|
200
|
+
Math.max(-frame.halfHeight, vec3.dot(toPoint, frame.axisY)),
|
|
201
|
+
);
|
|
202
|
+
const dx = (toPoint[0] ?? 0) - (frame.axisX[0] ?? 0) * localX - (frame.axisY[0] ?? 0) * localY;
|
|
203
|
+
const dy = (toPoint[1] ?? 0) - (frame.axisX[1] ?? 0) * localX - (frame.axisY[1] ?? 0) * localY;
|
|
204
|
+
const dz = (toPoint[2] ?? 0) - (frame.axisX[2] ?? 0) * localX - (frame.axisY[2] ?? 0) * localY;
|
|
205
|
+
return dx * dx + dy * dy + dz * dz < range * range;
|
|
206
|
+
}
|
|
207
|
+
|
|
177
208
|
// ── view_z_to_z_slice ──────────────────────────────────────────────────────
|
|
178
209
|
|
|
179
210
|
/**
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
LightProbe,
|
|
4
|
+
RectAreaLight,
|
|
5
|
+
SpotLight,
|
|
6
|
+
validateLightProbeData,
|
|
7
|
+
validateRectAreaLightData,
|
|
8
|
+
} from '../index';
|
|
9
|
+
|
|
10
|
+
describe('lighting authoring contracts', () => {
|
|
11
|
+
it('exposes only the declared Rect, Spot modifier, and Probe fields', () => {
|
|
12
|
+
expect(Object.keys(RectAreaLight.fields)).toEqual([
|
|
13
|
+
'color',
|
|
14
|
+
'intensity',
|
|
15
|
+
'width',
|
|
16
|
+
'height',
|
|
17
|
+
'range',
|
|
18
|
+
]);
|
|
19
|
+
expect(Object.keys(LightProbe.fields)).toEqual(['irradiance', 'radius']);
|
|
20
|
+
expect(SpotLight.fields.iesProfile.type).toBe('shared<IesProfileAsset>');
|
|
21
|
+
expect(SpotLight.fields.cookie.type).toBe('shared<TextureAsset>');
|
|
22
|
+
expect(SpotLight.fields.rollDeg.default).toBe(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('keeps defaults and rejects invalid Rect/Probe facts before rendering', () => {
|
|
26
|
+
expect(RectAreaLight.fields.width.default).toBe(1);
|
|
27
|
+
expect(RectAreaLight.fields.height.default).toBe(1);
|
|
28
|
+
expect(LightProbe.fields.irradiance.type).toBe('array<f32, 27>');
|
|
29
|
+
expect(LightProbe.fields.radius.default).toBe(1e-4);
|
|
30
|
+
|
|
31
|
+
expect(validateRectAreaLightData({}).ok).toBe(true);
|
|
32
|
+
expect(validateRectAreaLightData({ width: 0 }).ok).toBe(false);
|
|
33
|
+
expect(validateRectAreaLightData({ intensity: Number.NaN }).ok).toBe(false);
|
|
34
|
+
expect(validateLightProbeData({ irradiance: new Float32Array(26), radius: 1 }).ok).toBe(false);
|
|
35
|
+
expect(validateLightProbeData({ irradiance: new Float32Array(27), radius: 1e-5 }).ok).toBe(
|
|
36
|
+
false,
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { buildRectAreaWorldFrame, rectAreaFacesPoint } from '../../render-system-extract';
|
|
3
|
+
|
|
4
|
+
describe('RectAreaLight world frame', () => {
|
|
5
|
+
it('normalizes world axes while preserving authored half extents', () => {
|
|
6
|
+
const frame = buildRectAreaWorldFrame({
|
|
7
|
+
center: [1, 2, 3],
|
|
8
|
+
axisX: [0, 2, 0],
|
|
9
|
+
axisY: [-3, 0, 0],
|
|
10
|
+
width: 4,
|
|
11
|
+
height: 2,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
expect(Array.from(frame.axisX)).toEqual([0, 1, 0]);
|
|
15
|
+
expect(Array.from(frame.axisY)).toEqual([-1, 0, 0]);
|
|
16
|
+
expect(Array.from(frame.normal)).toEqual([0, 0, 1]);
|
|
17
|
+
expect(frame.halfWidth).toBe(2);
|
|
18
|
+
expect(frame.halfHeight).toBe(1);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('defines the front from the world-axis cross product', () => {
|
|
22
|
+
const frame = buildRectAreaWorldFrame({
|
|
23
|
+
center: [0, 0, 0],
|
|
24
|
+
axisX: [1, 0, 0],
|
|
25
|
+
axisY: [0, 1, 0],
|
|
26
|
+
width: 2,
|
|
27
|
+
height: 2,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(rectAreaFacesPoint(frame, [0, 0, 1])).toBe(true);
|
|
31
|
+
expect(rectAreaFacesPoint(frame, [0, 0, -1])).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
package/src/components/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ export * from './directional-light';
|
|
|
5
5
|
export * from './fog';
|
|
6
6
|
export { Instances, type InstancesData } from './instances';
|
|
7
7
|
export { Layer } from './layer';
|
|
8
|
+
export * from './light-helpers';
|
|
9
|
+
export { LightProbe } from './light-probe';
|
|
8
10
|
export { Lines } from './lines';
|
|
9
11
|
export * from './mesh-filter';
|
|
10
12
|
export * from './mesh-renderer';
|
|
@@ -18,6 +20,7 @@ export {
|
|
|
18
20
|
pointShapeFromU32,
|
|
19
21
|
} from './points';
|
|
20
22
|
export { PostProcessParams } from './post-process-params';
|
|
23
|
+
export { RectAreaLight } from './rect-area-light';
|
|
21
24
|
export * from './reflection-probe';
|
|
22
25
|
export {
|
|
23
26
|
SceneInstance,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// transparent to AI users) + D-S5 (Layer 1 host-side range = 0 ->
|
|
15
15
|
// invRangeSquared = 1e8 protects the 0 * Infinity = NaN intermediate).
|
|
16
16
|
|
|
17
|
-
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
17
|
+
import { err, ok, R_MIN, type Result } from '@forgeax/engine-types';
|
|
18
18
|
import { SpawnLightInvalidBoundsError } from '../errors/ecs-validation';
|
|
19
19
|
import { ShadowInvalidConfigError } from '../errors/render';
|
|
20
20
|
import { DirectionalShadowFilterValue } from './directional-shadow-filter';
|
|
@@ -264,3 +264,54 @@ export function validatePointLightShadowData(
|
|
|
264
264
|
}
|
|
265
265
|
return ok(undefined);
|
|
266
266
|
}
|
|
267
|
+
|
|
268
|
+
/** Validate rectangular area-light authoring facts before renderer admission. */
|
|
269
|
+
export function validateRectAreaLightData(
|
|
270
|
+
data: Readonly<Record<string, unknown>>,
|
|
271
|
+
): LightValidationResult {
|
|
272
|
+
const intensity = (data.intensity as number | undefined) ?? 1;
|
|
273
|
+
if (typeof intensity !== 'number' || !Number.isFinite(intensity) || intensity < 0) {
|
|
274
|
+
return err(new SpawnLightInvalidBoundsError('RectAreaLight', 'intensity', intensity));
|
|
275
|
+
}
|
|
276
|
+
const color = (data.color as ArrayLike<number> | undefined) ?? [1, 1, 1];
|
|
277
|
+
if (
|
|
278
|
+
color.length !== 3 ||
|
|
279
|
+
Array.from(color).some(
|
|
280
|
+
(value) => typeof value !== 'number' || !Number.isFinite(value) || value < 0,
|
|
281
|
+
)
|
|
282
|
+
) {
|
|
283
|
+
return err(new SpawnLightInvalidBoundsError('RectAreaLight', 'color', Array.from(color)));
|
|
284
|
+
}
|
|
285
|
+
for (const field of ['width', 'height', 'range'] as const) {
|
|
286
|
+
const value = (data[field] as number | undefined) ?? (field === 'range' ? 10 : 1);
|
|
287
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
|
|
288
|
+
return err(new SpawnLightInvalidBoundsError('RectAreaLight', field, value));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return ok(undefined);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Validate the fixed 27-value SH payload and minimum finite radius. */
|
|
295
|
+
export function validateLightProbeData(
|
|
296
|
+
data: Readonly<Record<string, unknown>>,
|
|
297
|
+
): LightValidationResult {
|
|
298
|
+
const irradiance = data.irradiance as ArrayLike<number> | undefined;
|
|
299
|
+
if (
|
|
300
|
+
irradiance === undefined ||
|
|
301
|
+
irradiance.length !== 27 ||
|
|
302
|
+
Array.from(irradiance).some((value) => typeof value !== 'number' || !Number.isFinite(value))
|
|
303
|
+
) {
|
|
304
|
+
return err(
|
|
305
|
+
new SpawnLightInvalidBoundsError(
|
|
306
|
+
'LightProbe',
|
|
307
|
+
'irradiance',
|
|
308
|
+
irradiance === undefined ? [] : Array.from(irradiance),
|
|
309
|
+
),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
const radius = (data.radius as number | undefined) ?? R_MIN;
|
|
313
|
+
if (typeof radius !== 'number' || !Number.isFinite(radius) || radius < R_MIN) {
|
|
314
|
+
return err(new SpawnLightInvalidBoundsError('LightProbe', 'radius', radius));
|
|
315
|
+
}
|
|
316
|
+
return ok(undefined);
|
|
317
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @forgeax/engine-render - local diffuse LightProbe authoring facts.
|
|
2
|
+
|
|
3
|
+
import { defineComponent } from '@forgeax/engine-ecs';
|
|
4
|
+
import { R_MIN } from '@forgeax/engine-types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Local diffuse irradiance probe. Transform supplies position; the 27 SH
|
|
8
|
+
* values are nine RGB groups in the fixed real-SH order.
|
|
9
|
+
*/
|
|
10
|
+
export const LightProbe = defineComponent('LightProbe', {
|
|
11
|
+
irradiance: { type: 'array<f32, 27>' },
|
|
12
|
+
radius: { type: 'f32', default: R_MIN },
|
|
13
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// @forgeax/engine-render - single-sided rectangular area-light authoring facts.
|
|
2
|
+
|
|
3
|
+
import { defineComponent } from '@forgeax/engine-ecs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Single-sided rectangular area light. Transform supplies the center and
|
|
7
|
+
* world orientation; width and height remain authored emitter dimensions.
|
|
8
|
+
*/
|
|
9
|
+
export const RectAreaLight = defineComponent('RectAreaLight', {
|
|
10
|
+
color: { type: 'array<f32, 3>', default: new Float32Array([1, 1, 1]) },
|
|
11
|
+
intensity: { type: 'f32', default: 1 },
|
|
12
|
+
width: { type: 'f32', default: 1 },
|
|
13
|
+
height: { type: 'f32', default: 1 },
|
|
14
|
+
range: { type: 'f32', default: 10 },
|
|
15
|
+
});
|
|
@@ -94,6 +94,12 @@ export interface SceneInstanceState {
|
|
|
94
94
|
*/
|
|
95
95
|
readonly source: import('@forgeax/engine-types').Handle<'SceneAsset', 'shared'>;
|
|
96
96
|
|
|
97
|
+
/** Author scene identity used by instance-relative SceneEntityRef resolution. */
|
|
98
|
+
readonly sceneSourceKey?: string;
|
|
99
|
+
|
|
100
|
+
/** Stable author bindingKey to live Entity mapping for this concrete instance. */
|
|
101
|
+
readonly bindings: Map<string, EntityHandle>;
|
|
102
|
+
|
|
97
103
|
/**
|
|
98
104
|
* Reverse mapping (live `Entity` -> source `LocalEntityId`). Used by
|
|
99
105
|
* `world.setSceneOverride` to validate the member entity belongs to this
|
|
@@ -124,6 +124,9 @@ export const SpotLight = defineComponent('SpotLight', {
|
|
|
124
124
|
range: { type: 'f32', default: 10.0 },
|
|
125
125
|
innerConeDeg: { type: 'f32', default: 0 },
|
|
126
126
|
outerConeDeg: { type: 'f32', default: 45 },
|
|
127
|
+
iesProfile: { type: 'shared<IesProfileAsset>' },
|
|
128
|
+
cookie: { type: 'shared<TextureAsset>' },
|
|
129
|
+
rollDeg: { type: 'f32', default: 0 },
|
|
127
130
|
// Shadow opt-out gate: defaults to true so zero-config spawns cast shadows.
|
|
128
131
|
castShadow: { type: 'bool', default: true },
|
|
129
132
|
// 7 shadow fields aligned with the shared light shadow vocabulary.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
createExtendedLightingResourceCandidate,
|
|
4
|
+
inspectExtendedLightingResources,
|
|
5
|
+
} from '../../prepare/extended-lighting/resources';
|
|
6
|
+
import { DeviceScope } from '../device-scope';
|
|
7
|
+
|
|
8
|
+
describe('extended lighting device recovery', () => {
|
|
9
|
+
it('retires the old scope and rebuilds one accepted resource generation', async () => {
|
|
10
|
+
const oldScope = DeviceScope.create(8, 'renderer');
|
|
11
|
+
const oldResource = oldScope.ref('texture', { id: 'old' });
|
|
12
|
+
const candidate = await createExtendedLightingResourceCandidate({
|
|
13
|
+
device: undefined,
|
|
14
|
+
scope: oldScope,
|
|
15
|
+
iesSliceCount: 0,
|
|
16
|
+
cookieSliceCount: 0,
|
|
17
|
+
cookieMatrices: 0,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(candidate.ok).toBe(false);
|
|
21
|
+
expect(oldResource.isCurrent(oldScope)).toBe(true);
|
|
22
|
+
expect(inspectExtendedLightingResources(oldScope).generation).toBe(8);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -324,6 +324,123 @@ interface CubemapGpuEntry {
|
|
|
324
324
|
faceViews: readonly TextureView[];
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
interface PackedMeshLodGeometry {
|
|
328
|
+
readonly vertices: Float32Array;
|
|
329
|
+
readonly indices: Uint16Array | Uint32Array | undefined;
|
|
330
|
+
readonly lodRanges: readonly (readonly MeshGpuRange[])[] | undefined;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function packMeshLodGeometry(
|
|
334
|
+
root: TypesMeshAsset,
|
|
335
|
+
rootData: MeshRenderData,
|
|
336
|
+
lowerMeshes: readonly TypesMeshAsset[],
|
|
337
|
+
): Result<PackedMeshLodGeometry, RhiError> {
|
|
338
|
+
if (lowerMeshes.length === 0) {
|
|
339
|
+
return ok({ vertices: root.vertices, indices: root.indices, lodRanges: undefined });
|
|
340
|
+
}
|
|
341
|
+
const rootStride = rootData.layoutProjection.arrayStride;
|
|
342
|
+
const allMeshes = [root, ...lowerMeshes];
|
|
343
|
+
const lowerData: MeshRenderData[] = [];
|
|
344
|
+
for (const lower of lowerMeshes) {
|
|
345
|
+
const projected = deriveRenderDataMesh(lower);
|
|
346
|
+
if (!projected.ok) {
|
|
347
|
+
return err(
|
|
348
|
+
new RhiError({
|
|
349
|
+
code: 'asset-not-registered',
|
|
350
|
+
expected: 'every MeshAsset LOD to project to a compatible GPU layout',
|
|
351
|
+
hint: 'repair the lower-detail MeshAsset attributes before drawing the LOD chain',
|
|
352
|
+
}),
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
lowerData.push(projected.value);
|
|
356
|
+
}
|
|
357
|
+
if (
|
|
358
|
+
lowerData.some(
|
|
359
|
+
(data) =>
|
|
360
|
+
data.layoutProjection.arrayStride !== rootStride ||
|
|
361
|
+
data.submeshes.length !== rootData.submeshes.length ||
|
|
362
|
+
data.indexFormat !== rootData.indexFormat,
|
|
363
|
+
)
|
|
364
|
+
) {
|
|
365
|
+
return err(
|
|
366
|
+
new RhiError({
|
|
367
|
+
code: 'asset-not-registered',
|
|
368
|
+
expected: 'LOD meshes to share vertex/index layout and material-slot topology',
|
|
369
|
+
hint: 'reimport LOD meshes with matching attributes, index format, and submesh slots',
|
|
370
|
+
}),
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
if (allMeshes.some((mesh) => (mesh.indices === undefined) !== (root.indices === undefined))) {
|
|
374
|
+
return err(
|
|
375
|
+
new RhiError({
|
|
376
|
+
code: 'asset-not-registered',
|
|
377
|
+
expected: 'all LOD meshes to use the same indexed or non-indexed draw mode',
|
|
378
|
+
hint: 'reimport the LOD chain with consistent index data',
|
|
379
|
+
}),
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
const vertices = new Float32Array(allMeshes.reduce((sum, mesh) => sum + mesh.vertices.length, 0));
|
|
383
|
+
const ranges: MeshGpuRange[][] = [];
|
|
384
|
+
let vertexCursor = 0;
|
|
385
|
+
let indexCursor = 0;
|
|
386
|
+
const indexed = root.indices !== undefined;
|
|
387
|
+
const sourceIndices: number[] = [];
|
|
388
|
+
let requiresUint32 = root.indices instanceof Uint32Array;
|
|
389
|
+
for (let level = 0; level < allMeshes.length; level += 1) {
|
|
390
|
+
const mesh = allMeshes[level];
|
|
391
|
+
const data = level === 0 ? rootData : lowerData[level - 1];
|
|
392
|
+
if (mesh === undefined || data === undefined) continue;
|
|
393
|
+
vertices.set(mesh.vertices, vertexCursor);
|
|
394
|
+
const levelRanges: MeshGpuRange[] = [];
|
|
395
|
+
for (let submeshIndex = 0; submeshIndex < data.submeshes.length; submeshIndex += 1) {
|
|
396
|
+
const submesh = data.submeshes[submeshIndex];
|
|
397
|
+
const rootSubmesh = rootData.submeshes[submeshIndex];
|
|
398
|
+
if (
|
|
399
|
+
submesh === undefined ||
|
|
400
|
+
rootSubmesh === undefined ||
|
|
401
|
+
submesh.materialSlot !== rootSubmesh.materialSlot ||
|
|
402
|
+
submesh.topology !== rootSubmesh.topology
|
|
403
|
+
) {
|
|
404
|
+
return err(
|
|
405
|
+
new RhiError({
|
|
406
|
+
code: 'asset-not-registered',
|
|
407
|
+
expected: 'LOD submeshes to preserve root material-slot and topology identity',
|
|
408
|
+
hint: 'repair LOD material slots and primitive topology before import',
|
|
409
|
+
}),
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
levelRanges.push(
|
|
413
|
+
indexed
|
|
414
|
+
? {
|
|
415
|
+
first: indexCursor + submesh.indexOffset,
|
|
416
|
+
count: submesh.indexCount,
|
|
417
|
+
baseVertex: vertexCursor / (rootStride / 4),
|
|
418
|
+
}
|
|
419
|
+
: { first: vertexCursor / (rootStride / 4), count: submesh.vertexCount, baseVertex: 0 },
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
ranges.push(levelRanges);
|
|
423
|
+
if (indexed) {
|
|
424
|
+
const indices = mesh.indices;
|
|
425
|
+
if (indices === undefined) continue;
|
|
426
|
+
for (const value of indices) {
|
|
427
|
+
// Each LOD range carries its vertex cursor as baseVertex, so keep
|
|
428
|
+
// indices local to that LOD and apply the offset exactly once at draw.
|
|
429
|
+
sourceIndices.push(value);
|
|
430
|
+
if (value > 0xffff || indices instanceof Uint32Array) requiresUint32 = true;
|
|
431
|
+
}
|
|
432
|
+
indexCursor += indices.length;
|
|
433
|
+
}
|
|
434
|
+
vertexCursor += mesh.vertices.length;
|
|
435
|
+
}
|
|
436
|
+
const indices = indexed
|
|
437
|
+
? requiresUint32
|
|
438
|
+
? Uint32Array.from(sourceIndices)
|
|
439
|
+
: Uint16Array.from(sourceIndices)
|
|
440
|
+
: undefined;
|
|
441
|
+
return ok({ vertices, indices, lodRanges: ranges });
|
|
442
|
+
}
|
|
443
|
+
|
|
327
444
|
export interface MeshGpuHandles {
|
|
328
445
|
readonly vertexBuffer: GpuBuffer;
|
|
329
446
|
/**
|
|
@@ -374,6 +491,14 @@ export interface MeshGpuHandles {
|
|
|
374
491
|
* For single-submesh meshes this is a 1-element array (byte-identical to pre-M4).
|
|
375
492
|
*/
|
|
376
493
|
readonly submeshes: readonly import('@forgeax/engine-types').Submesh[];
|
|
494
|
+
/** Packed root + lower-detail geometry ranges in the shared mesh buffers. */
|
|
495
|
+
readonly lodRanges?: readonly (readonly MeshGpuRange[])[];
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export interface MeshGpuRange {
|
|
499
|
+
readonly first: number;
|
|
500
|
+
readonly count: number;
|
|
501
|
+
readonly baseVertex: number;
|
|
377
502
|
}
|
|
378
503
|
|
|
379
504
|
/**
|
|
@@ -951,6 +1076,7 @@ export class GpuResidencyCache {
|
|
|
951
1076
|
handle: Handle<'MeshAsset', 'shared'>,
|
|
952
1077
|
pod: TypesMeshAsset,
|
|
953
1078
|
worldId?: World | number,
|
|
1079
|
+
lodMeshes?: readonly TypesMeshAsset[],
|
|
954
1080
|
): Result<MeshGpuHandles, RhiError | AssetError | ImageError>;
|
|
955
1081
|
ensureResident(
|
|
956
1082
|
handle: Handle<'TextureAsset', 'shared'>,
|
|
@@ -961,6 +1087,7 @@ export class GpuResidencyCache {
|
|
|
961
1087
|
handle: Handle<'MeshAsset', 'shared'> | Handle<'TextureAsset', 'shared'>,
|
|
962
1088
|
pod: TypesMeshAsset | TextureAsset,
|
|
963
1089
|
worldId: World | number = 0,
|
|
1090
|
+
lodMeshes: readonly TypesMeshAsset[] = [],
|
|
964
1091
|
): Result<TextureGpuEntry | MeshGpuHandles, RhiError | AssetError | ImageError> {
|
|
965
1092
|
const id = handleSlot(handle);
|
|
966
1093
|
// Hit: O(1) cache lookup, never re-projects (AC-09 -- deriveRenderData*
|
|
@@ -977,7 +1104,7 @@ export class GpuResidencyCache {
|
|
|
977
1104
|
if (existing !== undefined) return ok(existing);
|
|
978
1105
|
const projected = deriveRenderDataMesh(pod);
|
|
979
1106
|
if (!projected.ok) return projected;
|
|
980
|
-
return this.uploadMeshById(key, pod, projected.value);
|
|
1107
|
+
return this.uploadMeshById(key, pod, projected.value, lodMeshes);
|
|
981
1108
|
}
|
|
982
1109
|
case 'texture': {
|
|
983
1110
|
const texKey = this.worldKey(id, worldId);
|
|
@@ -1702,6 +1829,7 @@ export class GpuResidencyCache {
|
|
|
1702
1829
|
id: string | number,
|
|
1703
1830
|
mesh: TypesMeshAsset,
|
|
1704
1831
|
renderData: MeshRenderData,
|
|
1832
|
+
lodMeshes: readonly TypesMeshAsset[] = [],
|
|
1705
1833
|
): Result<MeshGpuHandles, RhiError> {
|
|
1706
1834
|
const device = this.gpuDevice;
|
|
1707
1835
|
if (device === undefined) {
|
|
@@ -1714,13 +1842,15 @@ export class GpuResidencyCache {
|
|
|
1714
1842
|
);
|
|
1715
1843
|
}
|
|
1716
1844
|
|
|
1717
|
-
const
|
|
1845
|
+
const packed = packMeshLodGeometry(mesh, renderData, lodMeshes);
|
|
1846
|
+
if (!packed.ok) return packed;
|
|
1847
|
+
const { vertices, indices, lodRanges } = packed.value;
|
|
1718
1848
|
const indexBytesUnpadded = indices === undefined ? 0 : indices.byteLength;
|
|
1719
|
-
const indexBytes =
|
|
1849
|
+
const indexBytes = ((indexBytesUnpadded + 3) >> 2) << 2;
|
|
1720
1850
|
|
|
1721
1851
|
const vboResult = device.createBuffer({
|
|
1722
1852
|
label: `mesh-${id}-vbo`,
|
|
1723
|
-
size:
|
|
1853
|
+
size: vertices.byteLength,
|
|
1724
1854
|
usage: renderData.vertexUsage,
|
|
1725
1855
|
mappedAtCreation: false,
|
|
1726
1856
|
});
|
|
@@ -1740,7 +1870,7 @@ export class GpuResidencyCache {
|
|
|
1740
1870
|
ibo = iboResult.value;
|
|
1741
1871
|
}
|
|
1742
1872
|
|
|
1743
|
-
const vboWriteResult = device.queue.writeBuffer(vbo, 0,
|
|
1873
|
+
const vboWriteResult = device.queue.writeBuffer(vbo, 0, vertices);
|
|
1744
1874
|
if (!vboWriteResult.ok) return err(vboWriteResult.error);
|
|
1745
1875
|
|
|
1746
1876
|
if (indices !== undefined) {
|
|
@@ -1766,10 +1896,10 @@ export class GpuResidencyCache {
|
|
|
1766
1896
|
const entry: MeshGpuHandles = {
|
|
1767
1897
|
vertexBuffer: this.wrapBuf(vbo),
|
|
1768
1898
|
indexBuffer: ibo === null ? null : this.wrapBuf(ibo),
|
|
1769
|
-
vboBytes:
|
|
1899
|
+
vboBytes: vertices.byteLength,
|
|
1770
1900
|
iboBytes: indices === undefined ? 0 : indexBytes,
|
|
1771
|
-
indexCount:
|
|
1772
|
-
indexFormat:
|
|
1901
|
+
indexCount: mesh.indices === undefined ? 0 : mesh.indices.length,
|
|
1902
|
+
indexFormat: indices instanceof Uint32Array ? 'uint32' : 'uint16',
|
|
1773
1903
|
layout: renderData.layoutProjection.attributes.some(
|
|
1774
1904
|
(attribute) => attribute.key === 'skinIndex' || attribute.key === 'skinWeight',
|
|
1775
1905
|
)
|
|
@@ -1778,9 +1908,10 @@ export class GpuResidencyCache {
|
|
|
1778
1908
|
layoutProjection: renderData.layoutProjection,
|
|
1779
1909
|
uvSetCount: 1 + extraUvSets,
|
|
1780
1910
|
vertexCount: mesh.vertices.byteLength / renderData.layoutProjection.arrayStride,
|
|
1781
|
-
indexed: indices !== undefined,
|
|
1911
|
+
indexed: mesh.indices !== undefined,
|
|
1782
1912
|
topology: renderData.submeshes[0]?.topology ?? 'triangle-list',
|
|
1783
1913
|
submeshes: renderData.submeshes,
|
|
1914
|
+
...(lodRanges === undefined ? {} : { lodRanges }),
|
|
1784
1915
|
};
|
|
1785
1916
|
this.meshGpuHandles.set(id, entry);
|
|
1786
1917
|
this.meshResidencyEpoch += 1;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export {
|
|
2
|
+
type DeviceResourceKind,
|
|
3
|
+
DeviceResourceRef,
|
|
4
|
+
DeviceScope,
|
|
5
|
+
type DeviceScopeReceipt,
|
|
6
|
+
type DeviceScopeState,
|
|
7
|
+
type LifecycleFailure,
|
|
8
|
+
type LifecycleResourceSpec,
|
|
9
|
+
type LifecycleResult,
|
|
10
|
+
LifecycleTransaction,
|
|
11
|
+
type ResourceRef,
|
|
12
|
+
} from './device-scope';
|
|
@@ -42,7 +42,11 @@ export class EnvironmentLifecycle {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
isActive(generation: EnvironmentGeneration): boolean {
|
|
45
|
-
return
|
|
45
|
+
return (
|
|
46
|
+
this.active?.generation === generation.generation &&
|
|
47
|
+
this.active.signature === generation.signature &&
|
|
48
|
+
this.active.scope === generation.scope
|
|
49
|
+
);
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
discard(generation: EnvironmentGeneration): void {
|
|
@@ -106,6 +110,9 @@ export class EnvironmentLifecycle {
|
|
|
106
110
|
),
|
|
107
111
|
);
|
|
108
112
|
}
|
|
113
|
+
if (this.active?.signature === frame.signature && this.active.scope.isAlive()) {
|
|
114
|
+
return ok(this.active.lane === lane ? this.active : this.laneView(this.active, lane));
|
|
115
|
+
}
|
|
109
116
|
const existing = this.candidates.get(frame.signature);
|
|
110
117
|
if (existing?.scope.isAlive()) {
|
|
111
118
|
return ok(existing.lane === lane ? existing : this.laneView(existing, lane));
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { MaterialShaderArtifactReceipt } from '@forgeax/engine-shader';
|
|
2
|
+
|
|
3
|
+
export type GpuDrivenPreparationErrorCode =
|
|
4
|
+
| 'missing-material-receipt'
|
|
5
|
+
| 'missing-uv'
|
|
6
|
+
| 'reflection-mismatch'
|
|
7
|
+
| 'vertex-input-mismatch'
|
|
8
|
+
| 'alpha-mask-mismatch'
|
|
9
|
+
| 'resource-not-ready'
|
|
10
|
+
| 'stale-generation'
|
|
11
|
+
| 'skin-receipt-mismatch';
|
|
12
|
+
|
|
13
|
+
export type GpuDrivenPreparationReason =
|
|
14
|
+
| 'material-receipt-missing'
|
|
15
|
+
| 'uv-set-missing'
|
|
16
|
+
| 'reflection-receipt-mismatch'
|
|
17
|
+
| 'vertex-semantic-mismatch'
|
|
18
|
+
| 'alpha-mask-receipt-missing'
|
|
19
|
+
| 'material-resource-missing'
|
|
20
|
+
| 'generation-stale'
|
|
21
|
+
| 'skin-address-missing';
|
|
22
|
+
|
|
23
|
+
export interface GpuDrivenPreparationErrorDetail {
|
|
24
|
+
readonly reason: GpuDrivenPreparationReason;
|
|
25
|
+
readonly owner: 'material' | 'geometry' | 'skin' | 'generation';
|
|
26
|
+
readonly expected?: string;
|
|
27
|
+
readonly actual?: string;
|
|
28
|
+
readonly expectedGeneration?: number;
|
|
29
|
+
readonly actualGeneration?: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const ERROR_POLICY: Readonly<
|
|
33
|
+
Record<GpuDrivenPreparationErrorCode, { readonly expected: string; readonly hint: string }>
|
|
34
|
+
> = {
|
|
35
|
+
'missing-material-receipt': {
|
|
36
|
+
expected: 'a producer-owned Standard PBR material receipt is present',
|
|
37
|
+
hint: 'cook and load the Standard PBR artifact, then retry preparation',
|
|
38
|
+
},
|
|
39
|
+
'missing-uv': {
|
|
40
|
+
expected: 'the geometry exposes every UV set named by the material receipt',
|
|
41
|
+
hint: 'repair the geometry UV semantic or route the draw to the CPU semantic lane',
|
|
42
|
+
},
|
|
43
|
+
'reflection-mismatch': {
|
|
44
|
+
expected: 'the loaded reflection receipt matches the artifact layout identity',
|
|
45
|
+
hint: 'rebuild the material artifact and reflection from one source, then retry',
|
|
46
|
+
},
|
|
47
|
+
'vertex-input-mismatch': {
|
|
48
|
+
expected: 'geometry vertex semantics match the material reflection receipt',
|
|
49
|
+
hint: 'repair the geometry vertex semantic layout or route the draw to the CPU lane',
|
|
50
|
+
},
|
|
51
|
+
'alpha-mask-mismatch': {
|
|
52
|
+
expected: 'the Alpha Mask cutoff is present in the material receipt',
|
|
53
|
+
hint: 'declare alphaCutoff in the Standard PBR material schema and recook it',
|
|
54
|
+
},
|
|
55
|
+
'resource-not-ready': {
|
|
56
|
+
expected: 'all material resource slots have resolved handles',
|
|
57
|
+
hint: 'resolve the declared texture and sampler resources before recording',
|
|
58
|
+
},
|
|
59
|
+
'stale-generation': {
|
|
60
|
+
expected: 'receipt and prepared inputs belong to the requested generation',
|
|
61
|
+
hint: 'discard stale GPU facts, rebuild the producer receipt, and retry the same draw',
|
|
62
|
+
},
|
|
63
|
+
'skin-receipt-mismatch': {
|
|
64
|
+
expected: 'skinned geometry has a current palette address receipt',
|
|
65
|
+
hint: 'rebuild the Skin palette receipt for this generation before recording',
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export class GpuDrivenPreparationError extends Error {
|
|
70
|
+
readonly code: GpuDrivenPreparationErrorCode;
|
|
71
|
+
readonly expected: string;
|
|
72
|
+
readonly hint: string;
|
|
73
|
+
readonly detail: GpuDrivenPreparationErrorDetail;
|
|
74
|
+
|
|
75
|
+
constructor(code: GpuDrivenPreparationErrorCode, detail: GpuDrivenPreparationErrorDetail) {
|
|
76
|
+
const policy = ERROR_POLICY[code];
|
|
77
|
+
super(`[GpuDrivenPreparationError ${code}] ${policy.expected}`);
|
|
78
|
+
this.name = 'GpuDrivenPreparationError';
|
|
79
|
+
this.code = code;
|
|
80
|
+
this.expected = policy.expected;
|
|
81
|
+
this.hint = policy.hint;
|
|
82
|
+
this.detail = detail;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface GpuDrivenPreparedMaterial {
|
|
87
|
+
readonly receipt: MaterialShaderArtifactReceipt;
|
|
88
|
+
readonly resourceSlots: MaterialShaderArtifactReceipt['resourceSlots'];
|
|
89
|
+
readonly uvSets: MaterialShaderArtifactReceipt['uvSets'];
|
|
90
|
+
readonly vertexInputs: MaterialShaderArtifactReceipt['vertexInputs'];
|
|
91
|
+
readonly alphaMask: MaterialShaderArtifactReceipt['alphaMask'];
|
|
92
|
+
}
|
package/src/errors/index.ts
CHANGED