@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
package/src/assembly/factory.ts
CHANGED
|
@@ -54,12 +54,17 @@ import type {
|
|
|
54
54
|
RhiDevice,
|
|
55
55
|
Sampler,
|
|
56
56
|
ShaderModule,
|
|
57
|
+
TextureView,
|
|
57
58
|
} from '@forgeax/engine-rhi';
|
|
58
59
|
import { err, ok, RhiError, validateDrawArgs } from '@forgeax/engine-rhi';
|
|
59
60
|
import { registerPropagateTransforms } from '@forgeax/engine-scene';
|
|
60
61
|
import {
|
|
62
|
+
createStandardPbrArtifactReceipt,
|
|
61
63
|
DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
|
|
62
64
|
findVariantByKey,
|
|
65
|
+
LTC_TABLE_HEIGHT,
|
|
66
|
+
LTC_TABLE_WIDTH,
|
|
67
|
+
LTC_TABLES,
|
|
63
68
|
type MaterialShaderEntry,
|
|
64
69
|
type MaterialShaderManifestEntry,
|
|
65
70
|
ShaderCatalog,
|
|
@@ -130,13 +135,7 @@ import {
|
|
|
130
135
|
LostListenerRegistry,
|
|
131
136
|
RhiErrorListenerRegistry,
|
|
132
137
|
} from '../lifecycle';
|
|
133
|
-
import {
|
|
134
|
-
assertStorageBufferCap,
|
|
135
|
-
LIGHT_ARRAY_HEADER_BYTES,
|
|
136
|
-
LIGHT_ARRAY_MAX_SLOTS,
|
|
137
|
-
POINT_LIGHT_STD430_BYTES,
|
|
138
|
-
SPOT_LIGHT_STD430_BYTES,
|
|
139
|
-
} from '../light-buffer-layout';
|
|
138
|
+
import { assertStorageBufferCap } from '../light-buffer-layout';
|
|
140
139
|
import {
|
|
141
140
|
buildPbrMaterialUserRegionEntries,
|
|
142
141
|
buildPbrPipelineLayouts,
|
|
@@ -169,6 +168,22 @@ import {
|
|
|
169
168
|
PipelineSpecError,
|
|
170
169
|
} from '../pipeline-spec';
|
|
171
170
|
import { POINTS_LINES_MATERIAL_SHADER_ID } from '../points-lines/record';
|
|
171
|
+
import type { ExtendedLightingResourceCandidate } from '../prepare/extended-lighting/resources';
|
|
172
|
+
import {
|
|
173
|
+
COOKIE_MATRIX_BYTES,
|
|
174
|
+
COOKIE_SLICE_SIZE,
|
|
175
|
+
deriveExtendedLightingCapability,
|
|
176
|
+
EXTENDED_LIGHTING_TOPOLOGY,
|
|
177
|
+
extendedLightingSampledTextureCapacityAvailable,
|
|
178
|
+
IES_SLICE_HEIGHT,
|
|
179
|
+
IES_SLICE_WIDTH,
|
|
180
|
+
} from '../prepare/extended-lighting/resources';
|
|
181
|
+
import { createCookieProjectionMatrixData } from '../prepare/extended-lighting/spot-modifiers';
|
|
182
|
+
import {
|
|
183
|
+
createExtendedLightingState,
|
|
184
|
+
projectExtendedLightingInspection,
|
|
185
|
+
promoteExtendedLightingCandidate,
|
|
186
|
+
} from '../prepare/extended-lighting/state';
|
|
172
187
|
import type { FrameObservationOptions } from '../record/frame';
|
|
173
188
|
import type { GpuPassTimingReason } from '../record/gpu-pass-timing/errors.js';
|
|
174
189
|
import {
|
|
@@ -309,8 +324,8 @@ function adaptMipmapShaderModuleFactory(
|
|
|
309
324
|
|
|
310
325
|
// Re-export registerAdvanceAnimationPlayer so consumers can wire it.
|
|
311
326
|
// Re-export registerPropagateTransforms so consumers can wire the
|
|
312
|
-
//
|
|
313
|
-
// derived
|
|
327
|
+
// GlobalTransform.world mat4 derivation (audio listener sync + picking read the
|
|
328
|
+
// derived GlobalTransform.world from scripts).
|
|
314
329
|
|
|
315
330
|
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w16 (D-4): a single
|
|
316
331
|
// shared empty ArrayBuffer used by `buildPipelineContext` to synthesize the
|
|
@@ -449,7 +464,7 @@ function toGpuVertexBufferLayouts(
|
|
|
449
464
|
* Bundler-layer injection accepted by `createRenderer` (and proxied by
|
|
450
465
|
* `createApp`) as the optional third argument.
|
|
451
466
|
*
|
|
452
|
-
* feat-20260608-create-app-param-surface-trim / M2 / D-3: aggregates the
|
|
467
|
+
* feat-20260608-create-app-param-surface-trim / M2 / D-3: aggregates the three
|
|
453
468
|
* host-injected build-tool channels:
|
|
454
469
|
*
|
|
455
470
|
* - shaderManifestUrl: the URL the host's vite-plugin-shader emit step
|
|
@@ -463,6 +478,9 @@ function toGpuVertexBufferLayouts(
|
|
|
463
478
|
* AssetRegistry third ctor slot so DDC-miss assets can lazy-import.
|
|
464
479
|
* Absent => shipped form (a DDC miss fails fast with `asset-not-imported`).
|
|
465
480
|
*
|
|
481
|
+
* - build: exact checkout revision emitted by the build-tool adapter and
|
|
482
|
+
* carried into renderer-owned inspection identity.
|
|
483
|
+
*
|
|
466
484
|
* The interface is duplicated structurally in `@forgeax/engine-app`
|
|
467
485
|
* (BundlerOptions exported from packages/app); demos consume through the app
|
|
468
486
|
* package, the engine accepts via this local minimal shape so packages/runtime
|
|
@@ -473,6 +491,8 @@ function toGpuVertexBufferLayouts(
|
|
|
473
491
|
export interface BundlerOptions {
|
|
474
492
|
readonly importTransport?: ImportTransport | undefined;
|
|
475
493
|
readonly shaderManifestUrl?: string | undefined;
|
|
494
|
+
/** Exact checkout revision emitted by the build-tool adapter. */
|
|
495
|
+
readonly build?: string | undefined;
|
|
476
496
|
}
|
|
477
497
|
|
|
478
498
|
// ─── Backend pack — M3 auto-select internal shape ───────────────────────────
|
|
@@ -541,25 +561,42 @@ function directionalPcssCapable(backendKind: RhiDevice['caps']['backendKind']):
|
|
|
541
561
|
export function selectHdrpPbrPrewarmVariants(
|
|
542
562
|
manifestEntry: MaterialShaderManifestEntry | undefined,
|
|
543
563
|
storageBufferCapable: boolean,
|
|
544
|
-
|
|
564
|
+
extendedLightingShaderAvailableOrTransmissionCapable = true,
|
|
565
|
+
transmissionCapable = true,
|
|
545
566
|
directionalPcssAvailable = true,
|
|
546
567
|
projectorAvailable: boolean = true,
|
|
547
568
|
): readonly MaterialShaderManifestEntry['variants'][number][] {
|
|
569
|
+
const variants = manifestEntry?.variants ?? [];
|
|
570
|
+
const hasExtendedLightingAxis = variants.some(
|
|
571
|
+
(variant) => 'EXTENDED_LIGHTING_AVAILABLE' in variant.defines,
|
|
572
|
+
);
|
|
573
|
+
const extendedLightingShaderAvailable = hasExtendedLightingAxis
|
|
574
|
+
? extendedLightingShaderAvailableOrTransmissionCapable
|
|
575
|
+
: true;
|
|
576
|
+
const effectiveTransmissionCapable = hasExtendedLightingAxis
|
|
577
|
+
? transmissionCapable
|
|
578
|
+
: extendedLightingShaderAvailableOrTransmissionCapable;
|
|
548
579
|
// HDRP's clustered group(2) ABI is storage-buffer backed. A downlevel
|
|
549
580
|
// WebGL2 device must stay on the URP variant; prewarming an HDRP source
|
|
550
581
|
// here would advertise an inactive route and can exceed its fragment
|
|
551
582
|
// uniform-buffer budget before the first frame.
|
|
552
583
|
if (!storageBufferCapable) return [];
|
|
553
584
|
return (
|
|
554
|
-
|
|
585
|
+
variants.filter(
|
|
555
586
|
(variant) =>
|
|
556
587
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
588
|
+
(!('EXTENDED_LIGHTING_AVAILABLE' in variant.defines) ||
|
|
589
|
+
variant.defines.EXTENDED_LIGHTING_AVAILABLE === extendedLightingShaderAvailable) &&
|
|
557
590
|
variant.defines.CLUSTER_FORWARD_AVAILABLE === true &&
|
|
558
591
|
(!('DIRECTIONAL_PCSS_AVAILABLE' in variant.defines) ||
|
|
559
592
|
variant.defines.DIRECTIONAL_PCSS_AVAILABLE === directionalPcssAvailable) &&
|
|
560
593
|
(!('PROJECTOR_AVAILABLE' in variant.defines) ||
|
|
561
594
|
variant.defines.PROJECTOR_AVAILABLE === projectorAvailable) &&
|
|
562
|
-
|
|
595
|
+
// ProbeBlend is an object-level opt-in. Boot/prewarm modules must
|
|
596
|
+
// stay on the no-probe ABI; a probe record selects its true variant
|
|
597
|
+
// later together with the probe-enabled slot-3 BGL.
|
|
598
|
+
variant.defines.PROBE_BLEND_AVAILABLE !== true &&
|
|
599
|
+
(effectiveTransmissionCapable || variant.defines.TRANSMISSION_AVAILABLE !== true),
|
|
563
600
|
) ?? []
|
|
564
601
|
);
|
|
565
602
|
}
|
|
@@ -575,7 +612,9 @@ export function selectSkinPrewarmVariants(
|
|
|
575
612
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
576
613
|
// The clustered skin ABI is only active on the storage-backed tier;
|
|
577
614
|
// storage=false must never prewarm or expose it as a usable variant.
|
|
578
|
-
(storageBufferCapable || variant.defines.CLUSTER_FORWARD_AVAILABLE !== true)
|
|
615
|
+
(storageBufferCapable || variant.defines.CLUSTER_FORWARD_AVAILABLE !== true) &&
|
|
616
|
+
// ProbeBlend is selected per object, never by the boot skin module.
|
|
617
|
+
variant.defines.PROBE_BLEND_AVAILABLE !== true,
|
|
579
618
|
) ?? []
|
|
580
619
|
);
|
|
581
620
|
}
|
|
@@ -590,6 +629,7 @@ export function selectStandardPbrTransmissionPrewarmVariants(
|
|
|
590
629
|
storageBufferCapable: boolean,
|
|
591
630
|
directionalPcssAvailable = true,
|
|
592
631
|
projectorAvailable: boolean = true,
|
|
632
|
+
extendedLightingShaderAvailable = true,
|
|
593
633
|
): readonly MaterialShaderManifestEntry['variants'][number][] {
|
|
594
634
|
if (manifestEntry === undefined) {
|
|
595
635
|
throw new Error('Standard material shader manifest row is missing');
|
|
@@ -605,6 +645,9 @@ export function selectStandardPbrTransmissionPrewarmVariants(
|
|
|
605
645
|
variant.defines.DIRECTIONAL_PCSS_AVAILABLE === directionalPcssAvailable) &&
|
|
606
646
|
(!('PROJECTOR_AVAILABLE' in variant.defines) ||
|
|
607
647
|
variant.defines.PROJECTOR_AVAILABLE === projectorAvailable) &&
|
|
648
|
+
(!('EXTENDED_LIGHTING_AVAILABLE' in variant.defines) ||
|
|
649
|
+
variant.defines.EXTENDED_LIGHTING_AVAILABLE === extendedLightingShaderAvailable) &&
|
|
650
|
+
variant.defines.PROBE_BLEND_AVAILABLE !== true &&
|
|
608
651
|
variant.defines.TRANSMISSION_AVAILABLE === transmissionAvailable,
|
|
609
652
|
);
|
|
610
653
|
if (declared === undefined) {
|
|
@@ -632,18 +675,24 @@ export function selectNoColorPbrVariant(
|
|
|
632
675
|
manifestEntry: MaterialShaderManifestEntry | undefined,
|
|
633
676
|
storageBufferCapable: boolean,
|
|
634
677
|
variantSet: string | undefined,
|
|
678
|
+
extendedLightingShaderAvailable = true,
|
|
635
679
|
directionalPcssAvailable = true,
|
|
636
680
|
projectorAvailable: boolean = true,
|
|
637
681
|
): MaterialShaderManifestEntry['variants'][number] | undefined {
|
|
638
682
|
const hdrpRequested = variantSet === '' || requestsClusterVariant(variantSet);
|
|
683
|
+
const probeBlendRequested = requestsProbeBlendVariant(variantSet);
|
|
639
684
|
return manifestEntry?.variants.find(
|
|
640
685
|
(variant) =>
|
|
641
686
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
687
|
+
(!('EXTENDED_LIGHTING_AVAILABLE' in variant.defines) ||
|
|
688
|
+
variant.defines.EXTENDED_LIGHTING_AVAILABLE === extendedLightingShaderAvailable) &&
|
|
642
689
|
variant.defines.VERTEX_COLOR_AVAILABLE === false &&
|
|
643
690
|
(!('DIRECTIONAL_PCSS_AVAILABLE' in variant.defines) ||
|
|
644
691
|
variant.defines.DIRECTIONAL_PCSS_AVAILABLE === directionalPcssAvailable) &&
|
|
645
692
|
(!('PROJECTOR_AVAILABLE' in variant.defines) ||
|
|
646
693
|
variant.defines.PROJECTOR_AVAILABLE === projectorAvailable) &&
|
|
694
|
+
(!('PROBE_BLEND_AVAILABLE' in variant.defines) ||
|
|
695
|
+
variant.defines.PROBE_BLEND_AVAILABLE === probeBlendRequested) &&
|
|
647
696
|
(!('CLUSTER_FORWARD_AVAILABLE' in variant.defines) ||
|
|
648
697
|
variant.defines.CLUSTER_FORWARD_AVAILABLE === hdrpRequested),
|
|
649
698
|
);
|
|
@@ -654,6 +703,11 @@ function requestsClusterVariant(variantSet: string | undefined): boolean {
|
|
|
654
703
|
return variantSet.includes('CLUSTER_FORWARD_AVAILABLE=true');
|
|
655
704
|
}
|
|
656
705
|
|
|
706
|
+
function requestsProbeBlendVariant(variantSet: string | undefined): boolean {
|
|
707
|
+
if (variantSet === undefined) return false;
|
|
708
|
+
return variantSet.includes('PROBE_BLEND_AVAILABLE=true');
|
|
709
|
+
}
|
|
710
|
+
|
|
657
711
|
export type MaterialShaderBindingContract = RenderFeatureMaterialShaderBindingContract;
|
|
658
712
|
export type MaterialShaderVertexInputContract = 'none' | 'render-material';
|
|
659
713
|
|
|
@@ -778,14 +832,18 @@ export function shouldDeferMissingPreparedMaterialShader(
|
|
|
778
832
|
export function selectPipelineLayoutForVariant(
|
|
779
833
|
state: {
|
|
780
834
|
readonly pbrPipelineLayout: PipelineLayout | null;
|
|
835
|
+
readonly pbrProbePipelineLayout?: PipelineLayout | null;
|
|
781
836
|
readonly hdrpPbrPipelineLayout: PipelineLayout | null;
|
|
837
|
+
readonly hdrpProbePbrPipelineLayout?: PipelineLayout | null;
|
|
782
838
|
readonly pbrSkinPipelineLayout: PipelineLayout | null;
|
|
839
|
+
readonly pbrSkinProbePipelineLayout?: PipelineLayout | null;
|
|
783
840
|
readonly hdrpSkinPipelineLayout?: PipelineLayout | null;
|
|
784
841
|
} | null,
|
|
785
|
-
|
|
842
|
+
variantSet: string | undefined,
|
|
786
843
|
layoutKind?: LayoutKind,
|
|
787
844
|
): PipelineLayout | null {
|
|
788
845
|
if (state === null) return null;
|
|
846
|
+
const probeBlendVariant = variantSet?.includes('PROBE_BLEND_AVAILABLE=true') === true;
|
|
789
847
|
// bug-20260611: skin layout selection takes precedence over HDRP variant
|
|
790
848
|
// resolution. HDRP × skin is OOS-1 (plan-strategy R-2 — left for a
|
|
791
849
|
// dedicated feat); when an HDRP-variant skin call ever lands, we fail
|
|
@@ -793,7 +851,9 @@ export function selectPipelineLayoutForVariant(
|
|
|
793
851
|
// (charter P3 explicit failure, mirrors memory anchor
|
|
794
852
|
// `hdrp-active-must-not-fallback-to-urp-pipeline`).
|
|
795
853
|
if (layoutKind === 'pbr-skin') {
|
|
796
|
-
return
|
|
854
|
+
return probeBlendVariant
|
|
855
|
+
? (state.pbrSkinProbePipelineLayout ?? null)
|
|
856
|
+
: state.pbrSkinPipelineLayout;
|
|
797
857
|
}
|
|
798
858
|
if (layoutKind === 'hdrp-skin') {
|
|
799
859
|
return state.hdrpSkinPipelineLayout ?? null;
|
|
@@ -818,14 +878,19 @@ export function selectPipelineLayoutForVariant(
|
|
|
818
878
|
// the renderer is otherwise running its HDRP lighting variant; allowing the
|
|
819
879
|
// variant key to re-select HDRP here recreates the mesh-vs-cluster mismatch.
|
|
820
880
|
if (layoutKind === 'pbr') {
|
|
821
|
-
return state.pbrPipelineLayout;
|
|
881
|
+
return probeBlendVariant ? (state.pbrProbePipelineLayout ?? null) : state.pbrPipelineLayout;
|
|
822
882
|
}
|
|
823
883
|
// Group(2) contract/layout kind is the sole layout authority. Variant keys
|
|
824
884
|
// identify WGSL artifacts, but must not independently select a bind-group
|
|
825
885
|
// layout; this prevents an empty/expanded key from bypassing the resolved
|
|
826
886
|
// storage contract.
|
|
827
887
|
const isHdrpVariant = layoutKind === 'hdrp-pbr';
|
|
828
|
-
|
|
888
|
+
if (isHdrpVariant) {
|
|
889
|
+
return probeBlendVariant
|
|
890
|
+
? (state.hdrpProbePbrPipelineLayout ?? null)
|
|
891
|
+
: state.hdrpPbrPipelineLayout;
|
|
892
|
+
}
|
|
893
|
+
return probeBlendVariant ? (state.pbrProbePipelineLayout ?? null) : state.pbrPipelineLayout;
|
|
829
894
|
}
|
|
830
895
|
|
|
831
896
|
/**
|
|
@@ -841,12 +906,13 @@ export function resolveMaterialShaderVariantSet(
|
|
|
841
906
|
}>,
|
|
842
907
|
backendKind: RhiDevice['caps']['backendKind'],
|
|
843
908
|
storageBuffer: boolean,
|
|
844
|
-
|
|
909
|
+
maxSampledTexturesPerShaderStageOrProjectorAvailable?: number | boolean,
|
|
845
910
|
): string | undefined {
|
|
846
911
|
const declaredAxes = new Set(variants.flatMap((variant) => Object.keys(variant.defines)));
|
|
847
912
|
const declaresCapabilityVariants =
|
|
848
913
|
declaredAxes.has('WEBGL2_COMPAT') ||
|
|
849
914
|
declaredAxes.has('STORAGE_BUFFER_AVAILABLE') ||
|
|
915
|
+
declaredAxes.has('EXTENDED_LIGHTING_AVAILABLE') ||
|
|
850
916
|
declaredAxes.has('DIRECTIONAL_PCSS_AVAILABLE') ||
|
|
851
917
|
declaredAxes.has('PROJECTOR_AVAILABLE');
|
|
852
918
|
if (!declaresCapabilityVariants) return requestedVariantSet;
|
|
@@ -856,6 +922,22 @@ export function resolveMaterialShaderVariantSet(
|
|
|
856
922
|
const separator = part.indexOf('=');
|
|
857
923
|
if (separator > 0) requested[part.slice(0, separator)] = part.slice(separator + 1) === 'true';
|
|
858
924
|
}
|
|
925
|
+
const sampledTextureLimit =
|
|
926
|
+
typeof maxSampledTexturesPerShaderStageOrProjectorAvailable === 'number'
|
|
927
|
+
? maxSampledTexturesPerShaderStageOrProjectorAvailable
|
|
928
|
+
: undefined;
|
|
929
|
+
const extendedLightingAvailable =
|
|
930
|
+
sampledTextureLimit === undefined
|
|
931
|
+
? true
|
|
932
|
+
: backendKind !== 'wgpu-webgl2' &&
|
|
933
|
+
storageBuffer &&
|
|
934
|
+
extendedLightingSampledTextureCapacityAvailable(sampledTextureLimit);
|
|
935
|
+
const projectorAvailable =
|
|
936
|
+
typeof maxSampledTexturesPerShaderStageOrProjectorAvailable === 'boolean'
|
|
937
|
+
? maxSampledTexturesPerShaderStageOrProjectorAvailable
|
|
938
|
+
: sampledTextureLimit === undefined
|
|
939
|
+
? true
|
|
940
|
+
: sampledTextureLimit >= STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
859
941
|
const filtered: Record<string, boolean> = {};
|
|
860
942
|
for (const axis of declaredAxes) {
|
|
861
943
|
filtered[axis] =
|
|
@@ -871,13 +953,22 @@ export function resolveMaterialShaderVariantSet(
|
|
|
871
953
|
? requestedVariantSet === '' || (requested[axis] ?? false)
|
|
872
954
|
: axis === 'VERTEX_COLOR_AVAILABLE'
|
|
873
955
|
? requestedVariantSet === '' || (requested[axis] ?? false)
|
|
874
|
-
: axis === '
|
|
875
|
-
?
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
956
|
+
: axis === 'EXTENDED_LIGHTING_AVAILABLE'
|
|
957
|
+
? // This is a backend-owned ABI axis, just like projector
|
|
958
|
+
// and directional PCSS. An omitted caller request must
|
|
959
|
+
// inherit the device-admitted view layout; defaulting it
|
|
960
|
+
// to false pairs an extended view BGL with the projector
|
|
961
|
+
// WGSL and fails WebGPU validation at binding 11.
|
|
962
|
+
extendedLightingAvailable
|
|
963
|
+
: axis === 'PROBE_BLEND_AVAILABLE'
|
|
964
|
+
? storageBuffer && (requested[axis] ?? false)
|
|
965
|
+
: axis === 'TRANSMISSION_AVAILABLE'
|
|
966
|
+
? (requested[axis] ?? false)
|
|
967
|
+
: axis === 'DIRECTIONAL_PCSS_AVAILABLE'
|
|
968
|
+
? directionalPcssCapable(backendKind)
|
|
969
|
+
: axis === 'PROJECTOR_AVAILABLE'
|
|
970
|
+
? projectorAvailable
|
|
971
|
+
: (requested[axis] ?? false);
|
|
881
972
|
}
|
|
882
973
|
const sorted = Object.entries(filtered).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
883
974
|
return sorted.every(([, value]) => value)
|
|
@@ -907,8 +998,9 @@ export async function createRenderer(
|
|
|
907
998
|
options?: RendererOptions,
|
|
908
999
|
// feat-20260608-create-app-param-surface-trim / M2 / D-3: the third arg is
|
|
909
1000
|
// now BundlerOptions -- the build-tool injection channel that aggregates
|
|
910
|
-
// (a) shaderManifestUrl (vite-plugin-shader emit URL)
|
|
911
|
-
// (dev-only AssetRegistry transport from feat-20260604)
|
|
1001
|
+
// (a) shaderManifestUrl (vite-plugin-shader emit URL), (b) importTransport,
|
|
1002
|
+
// (dev-only AssetRegistry transport from feat-20260604), and (c) build
|
|
1003
|
+
// identity for renderer-owned inspection evidence. All fields are
|
|
912
1004
|
// optional so {} is a valid call shape. Absent => both shipped-form
|
|
913
1005
|
// fallbacks fire: shaderManifestUrl falls back to '/shaders/manifest.json'
|
|
914
1006
|
// (D-2 q5-A, ~30 lines below); importTransport stays undefined (AssetRegistry
|
|
@@ -995,7 +1087,11 @@ function attachDeviceLostFanout(
|
|
|
995
1087
|
},
|
|
996
1088
|
): void {
|
|
997
1089
|
const { lostRegistry, errorRegistry, healthRegistry, onDeviceLost } = registries;
|
|
998
|
-
|
|
1090
|
+
// Backend fixtures may compose a deterministic loss signal with the native
|
|
1091
|
+
// promise so the real recovery path can be exercised without manufacturing
|
|
1092
|
+
// a second renderer health state. Production packs leave this hook absent.
|
|
1093
|
+
const lost = pack.instrumentation?.deviceLost?.(device) ?? device.lost;
|
|
1094
|
+
lost
|
|
999
1095
|
.then((info) => {
|
|
1000
1096
|
const safe = {
|
|
1001
1097
|
reason: info?.reason ?? 'unknown',
|
|
@@ -1227,10 +1323,8 @@ interface WebGPURendererInternals {
|
|
|
1227
1323
|
options: RendererOptions | undefined;
|
|
1228
1324
|
/**
|
|
1229
1325
|
* feat-20260608-create-app-param-surface-trim / M2 / D-3: BundlerOptions
|
|
1230
|
-
* forwarded verbatim from createRenderer's third arg. Carries
|
|
1231
|
-
*
|
|
1232
|
-
* createRenderer falls back to '/shaders/manifest.json') and the
|
|
1233
|
-
* importTransport that previously rode a dedicated internal slot.
|
|
1326
|
+
* forwarded verbatim from createRenderer's third arg. Carries the
|
|
1327
|
+
* host-injected shaderManifestUrl, importTransport, and exact build identity.
|
|
1234
1328
|
*/
|
|
1235
1329
|
bundler: BundlerOptions | undefined;
|
|
1236
1330
|
lostRegistry: LostListenerRegistry;
|
|
@@ -1457,6 +1551,7 @@ async function makeWebGPURenderer(
|
|
|
1457
1551
|
// lives in a standalone store; `assets` keeps the CPU POD registry only.
|
|
1458
1552
|
const gpuStore = new GpuResidencyCache();
|
|
1459
1553
|
let activeDeviceScope = DeviceScope.create(0, 'renderer');
|
|
1554
|
+
let extendedLightingState = createExtendedLightingState(activeDeviceScope.generation);
|
|
1460
1555
|
const gpuPassTimingOptions = internals.options?.gpuPassTiming;
|
|
1461
1556
|
const timingRetentionFrames =
|
|
1462
1557
|
gpuPassTimingOptions?.retentionFrames !== undefined &&
|
|
@@ -1916,8 +2011,7 @@ async function makeWebGPURenderer(
|
|
|
1916
2011
|
[],
|
|
1917
2012
|
internals.device.caps.backendKind,
|
|
1918
2013
|
internals.device.caps.storageBuffer,
|
|
1919
|
-
|
|
1920
|
-
STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES,
|
|
2014
|
+
internals.device.limits.maxSampledTexturesPerShaderStage,
|
|
1921
2015
|
);
|
|
1922
2016
|
}
|
|
1923
2017
|
const variants = manifestEntry.variants;
|
|
@@ -1929,14 +2023,14 @@ async function makeWebGPURenderer(
|
|
|
1929
2023
|
const projectorAvailable =
|
|
1930
2024
|
(internals.device.limits.maxSampledTexturesPerShaderStage ?? 0) >=
|
|
1931
2025
|
STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
1932
|
-
const cacheKey = `${requestedVariantSet ?? '\u0000'}|${internals.device.caps.backendKind}|${internals.device.caps.storageBuffer ? '1' : '0'}|${projectorAvailable ? '1' : '0'}`;
|
|
2026
|
+
const cacheKey = `${requestedVariantSet ?? '\u0000'}|${internals.device.caps.backendKind}|${internals.device.caps.storageBuffer ? '1' : '0'}|${internals.device.limits.maxSampledTexturesPerShaderStage ?? 'unknown'}|${projectorAvailable ? '1' : '0'}`;
|
|
1933
2027
|
if (byRequest.has(cacheKey)) return byRequest.get(cacheKey);
|
|
1934
2028
|
const resolved = resolveMaterialShaderVariantSet(
|
|
1935
2029
|
requestedVariantSet,
|
|
1936
2030
|
variants,
|
|
1937
2031
|
internals.device.caps.backendKind,
|
|
1938
2032
|
internals.device.caps.storageBuffer,
|
|
1939
|
-
|
|
2033
|
+
internals.device.limits.maxSampledTexturesPerShaderStage,
|
|
1940
2034
|
);
|
|
1941
2035
|
byRequest.set(cacheKey, resolved);
|
|
1942
2036
|
return resolved;
|
|
@@ -1962,6 +2056,43 @@ async function makeWebGPURenderer(
|
|
|
1962
2056
|
// `internals.device` / `internals.pack` are read live, so a rebuild after a
|
|
1963
2057
|
// device swap compiles against the new device.
|
|
1964
2058
|
let tonemapRegistered = false;
|
|
2059
|
+
const promotePipelineExtendedLighting = (state: PipelineState, scope: DeviceScope): void => {
|
|
2060
|
+
if (
|
|
2061
|
+
state.extendedLightingAvailable !== true ||
|
|
2062
|
+
state.iesProfileTexture === undefined ||
|
|
2063
|
+
state.cookieTexture === undefined ||
|
|
2064
|
+
state.cookieMatrixBuffer === undefined ||
|
|
2065
|
+
state.iesProfileTextureView === undefined ||
|
|
2066
|
+
state.cookieTextureView === undefined ||
|
|
2067
|
+
state.ltcLambertTextureView === undefined ||
|
|
2068
|
+
state.ltcGgxTextureView === undefined
|
|
2069
|
+
) {
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
// The pipeline builder owns these already-resident GPU handles. Project
|
|
2073
|
+
// that completed bundle into the inspection state so the public receipt
|
|
2074
|
+
// cannot report an empty candidate while the record path is consuming the
|
|
2075
|
+
// same IES/Cookie/LTC resources. The candidate is metadata-only here; no
|
|
2076
|
+
// second GPU allocation or lifecycle owner is introduced.
|
|
2077
|
+
const candidate: ExtendedLightingResourceCandidate = {
|
|
2078
|
+
topology: EXTENDED_LIGHTING_TOPOLOGY,
|
|
2079
|
+
generation: scope.generation,
|
|
2080
|
+
scope,
|
|
2081
|
+
iesSliceCount: 0,
|
|
2082
|
+
cookieSliceCount: 0,
|
|
2083
|
+
cookieMatrices: 0,
|
|
2084
|
+
sampler: state.defaultSampler,
|
|
2085
|
+
iesTexture: state.iesProfileTexture,
|
|
2086
|
+
cookieTexture: state.cookieTexture,
|
|
2087
|
+
cookieMatrixBuffer: state.cookieMatrixBuffer,
|
|
2088
|
+
descriptorBytes:
|
|
2089
|
+
IES_SLICE_WIDTH * IES_SLICE_HEIGHT * 2 * 32 +
|
|
2090
|
+
COOKIE_SLICE_SIZE * COOKIE_SLICE_SIZE * 4 * 32 +
|
|
2091
|
+
COOKIE_MATRIX_BYTES,
|
|
2092
|
+
uploadCount: 5,
|
|
2093
|
+
};
|
|
2094
|
+
extendedLightingState = promoteExtendedLightingCandidate(extendedLightingState, candidate);
|
|
2095
|
+
};
|
|
1965
2096
|
let fxaaRegistered = false;
|
|
1966
2097
|
let temporalPostProcessesRegistered = false;
|
|
1967
2098
|
const buildPipeline = (scope: DeviceScope = activeDeviceScope): Promise<PipelineState> =>
|
|
@@ -2056,6 +2187,7 @@ async function makeWebGPURenderer(
|
|
|
2056
2187
|
const ready: Promise<Result<void, RhiError>> = buildPipeline().then(
|
|
2057
2188
|
(state): Result<void, RhiError> => {
|
|
2058
2189
|
pipelineState = state;
|
|
2190
|
+
promotePipelineExtendedLighting(state, activeDeviceScope);
|
|
2059
2191
|
readySettled = true;
|
|
2060
2192
|
markRendererBootstrapStage('renderer-initialized');
|
|
2061
2193
|
return ok(undefined);
|
|
@@ -2101,6 +2233,7 @@ async function makeWebGPURenderer(
|
|
|
2101
2233
|
pbrManifestEntry,
|
|
2102
2234
|
internals.device.caps.storageBuffer,
|
|
2103
2235
|
variantSet,
|
|
2236
|
+
deriveExtendedLightingCapability(internals.device).admitted,
|
|
2104
2237
|
directionalPcssCapable(internals.device.caps.backendKind),
|
|
2105
2238
|
(internals.device.limits.maxSampledTexturesPerShaderStage ?? 0) >=
|
|
2106
2239
|
STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES,
|
|
@@ -2366,6 +2499,7 @@ async function makeWebGPURenderer(
|
|
|
2366
2499
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2367
2500
|
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
2368
2501
|
layoutKindOverride?: LayoutKind,
|
|
2502
|
+
additionalColorFormats?: readonly GPUTextureFormat[],
|
|
2369
2503
|
): {
|
|
2370
2504
|
readonly pipeline: RenderPipeline;
|
|
2371
2505
|
readonly group2Contract: PipelineGroup2Contract;
|
|
@@ -2401,6 +2535,10 @@ async function makeWebGPURenderer(
|
|
|
2401
2535
|
{
|
|
2402
2536
|
...ctx,
|
|
2403
2537
|
colorFormat: isHdr ? HDR_COLOR_ATTACHMENT_FORMAT : ldrColorFormat,
|
|
2538
|
+
colorFormats:
|
|
2539
|
+
additionalColorFormats === undefined
|
|
2540
|
+
? undefined
|
|
2541
|
+
: [isHdr ? HDR_COLOR_ATTACHMENT_FORMAT : ldrColorFormat, ...additionalColorFormats],
|
|
2404
2542
|
depthFormat:
|
|
2405
2543
|
depthFormatOverride === null ? undefined : (depthFormatOverride ?? DEPTH_TEXTURE_FORMAT),
|
|
2406
2544
|
label,
|
|
@@ -2492,6 +2630,7 @@ async function makeWebGPURenderer(
|
|
|
2492
2630
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2493
2631
|
shaderModuleMode: RenderFeatureShaderModuleMode = 'validated',
|
|
2494
2632
|
layoutKindOverride?: LayoutKind,
|
|
2633
|
+
additionalColorFormats?: readonly GPUTextureFormat[],
|
|
2495
2634
|
): {
|
|
2496
2635
|
readonly pipeline: RenderPipeline;
|
|
2497
2636
|
readonly group2Contract: PipelineGroup2Contract;
|
|
@@ -2575,10 +2714,14 @@ async function makeWebGPURenderer(
|
|
|
2575
2714
|
// getPreferredCanvasFormat returns rgba8unorm (dawn-node, lavapipe, wgpu-wasm GLES),
|
|
2576
2715
|
// forcing a redundant second PSO build on first-frame URP record path.
|
|
2577
2716
|
const colorFormat: GPUTextureFormat = isHdr ? 'rgba16float' : ldrColorFormat;
|
|
2717
|
+
const attachmentFormats =
|
|
2718
|
+
additionalColorFormats === undefined
|
|
2719
|
+
? colorFormatsForPassKind(passKind, colorFormat)
|
|
2720
|
+
: [colorFormat, ...additionalColorFormats];
|
|
2578
2721
|
const spec: PipelineSpec = {
|
|
2579
2722
|
shader: { id: materialShaderId, passKind, variantSet: effectiveVariantSet },
|
|
2580
2723
|
attachments: {
|
|
2581
|
-
colorFormats:
|
|
2724
|
+
colorFormats: attachmentFormats,
|
|
2582
2725
|
depthFormat:
|
|
2583
2726
|
passKind === 'shadow-caster' || passKind === 'point-shadow-caster'
|
|
2584
2727
|
? 'depth32float'
|
|
@@ -2667,6 +2810,8 @@ async function makeWebGPURenderer(
|
|
|
2667
2810
|
vertexLayout,
|
|
2668
2811
|
vertexLayoutProjection,
|
|
2669
2812
|
shaderModuleMode,
|
|
2813
|
+
layoutKindOverride,
|
|
2814
|
+
additionalColorFormats,
|
|
2670
2815
|
);
|
|
2671
2816
|
}
|
|
2672
2817
|
const pbrEntry = findStandardPbrEntry(effectiveVariantSet);
|
|
@@ -2721,6 +2866,8 @@ async function makeWebGPURenderer(
|
|
|
2721
2866
|
vertexLayout,
|
|
2722
2867
|
vertexLayoutProjection,
|
|
2723
2868
|
shaderModuleMode,
|
|
2869
|
+
layoutKindOverride,
|
|
2870
|
+
additionalColorFormats,
|
|
2724
2871
|
);
|
|
2725
2872
|
}
|
|
2726
2873
|
// feat-20260609 M4 / w31: resolve variant WGSL from manifest when
|
|
@@ -2815,6 +2962,7 @@ async function makeWebGPURenderer(
|
|
|
2815
2962
|
vertexLayoutProjection,
|
|
2816
2963
|
shaderModuleMode,
|
|
2817
2964
|
layoutKindOverride,
|
|
2965
|
+
additionalColorFormats,
|
|
2818
2966
|
);
|
|
2819
2967
|
};
|
|
2820
2968
|
const getMaterialShaderPipelineEntry = (
|
|
@@ -2827,6 +2975,37 @@ async function makeWebGPURenderer(
|
|
|
2827
2975
|
const lookup = getShader().findMaterialArtifact(materialShaderId);
|
|
2828
2976
|
return lookup.ok ? lookup.value.paramSchema : undefined;
|
|
2829
2977
|
};
|
|
2978
|
+
const getMaterialShaderArtifact = (materialShaderId: string) => {
|
|
2979
|
+
if (
|
|
2980
|
+
materialShaderId !== 'forgeax::default-standard-pbr' &&
|
|
2981
|
+
materialShaderId !== 'forgeax::default-standard-pbr-skin'
|
|
2982
|
+
) {
|
|
2983
|
+
return undefined;
|
|
2984
|
+
}
|
|
2985
|
+
const lookup = getShader().findMaterialArtifact(materialShaderId);
|
|
2986
|
+
if (!lookup.ok) return undefined;
|
|
2987
|
+
const receipt = createStandardPbrArtifactReceipt(
|
|
2988
|
+
materialShaderId === 'forgeax::default-standard-pbr-skin',
|
|
2989
|
+
);
|
|
2990
|
+
const uvSetCount = materialShaderUvSetCounts.get(materialShaderId);
|
|
2991
|
+
return {
|
|
2992
|
+
material: materialShaderId,
|
|
2993
|
+
pass: 'forward',
|
|
2994
|
+
wgsl: lookup.value.source,
|
|
2995
|
+
layoutIdentity: receipt.reflection.layoutIdentity,
|
|
2996
|
+
...(uvSetCount === undefined ? {} : { uvSetCount }),
|
|
2997
|
+
bindings: [],
|
|
2998
|
+
deps: [],
|
|
2999
|
+
vertexInputs: receipt.vertexInputs.map(
|
|
3000
|
+
(input): Readonly<Record<string, unknown>> => ({
|
|
3001
|
+
semantic: input.semantic,
|
|
3002
|
+
location: input.location,
|
|
3003
|
+
format: input.format,
|
|
3004
|
+
}),
|
|
3005
|
+
),
|
|
3006
|
+
receipt,
|
|
3007
|
+
};
|
|
3008
|
+
};
|
|
2830
3009
|
// feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1): expose the per-shader
|
|
2831
3010
|
// material BGL so the record stage creates the material bind group against
|
|
2832
3011
|
// the matching paramSchema-derived layout. Returns undefined only for
|
|
@@ -2931,6 +3110,13 @@ async function makeWebGPURenderer(
|
|
|
2931
3110
|
);
|
|
2932
3111
|
const renderSystem: RenderSystem = createRenderSystem({
|
|
2933
3112
|
canvas: internals.canvas,
|
|
3113
|
+
// Keep inspection submits tied to the exact build-tool revision. This
|
|
3114
|
+
// bridge is independent of the SSR identity and is required by
|
|
3115
|
+
// renderer-owned LOD World attribution.
|
|
3116
|
+
get build() {
|
|
3117
|
+
return internals.bundler?.build;
|
|
3118
|
+
},
|
|
3119
|
+
ssrIdentity: internals.options?.ssrIdentity,
|
|
2934
3120
|
get standardProfile() {
|
|
2935
3121
|
return activeProfile;
|
|
2936
3122
|
},
|
|
@@ -2960,6 +3146,9 @@ async function makeWebGPURenderer(
|
|
|
2960
3146
|
get device() {
|
|
2961
3147
|
return internals.device;
|
|
2962
3148
|
},
|
|
3149
|
+
get beforeSubmit() {
|
|
3150
|
+
return internals.pack.instrumentation?.beforeSubmit;
|
|
3151
|
+
},
|
|
2963
3152
|
get deviceScope() {
|
|
2964
3153
|
return activeDeviceScope;
|
|
2965
3154
|
},
|
|
@@ -2986,6 +3175,7 @@ async function makeWebGPURenderer(
|
|
|
2986
3175
|
getMaterialShaderPipelineEntry,
|
|
2987
3176
|
getMaterialShaderBindingContract: getCachedMaterialShaderBindingContract,
|
|
2988
3177
|
getParamSchema,
|
|
3178
|
+
getMaterialShaderArtifact,
|
|
2989
3179
|
getMaterialBindGroupLayout,
|
|
2990
3180
|
metrics,
|
|
2991
3181
|
// feat-20260609 M4 / T-10-a: post-process pipeline factory (CONCERN-1 fix).
|
|
@@ -3166,6 +3356,12 @@ async function makeWebGPURenderer(
|
|
|
3166
3356
|
);
|
|
3167
3357
|
}
|
|
3168
3358
|
},
|
|
3359
|
+
inspectLodOcclusion() {
|
|
3360
|
+
return {
|
|
3361
|
+
lodOcclusion: renderSystem.lodOcclusionInspection,
|
|
3362
|
+
gpuDriven: renderSystem.gpuDrivenInspection,
|
|
3363
|
+
};
|
|
3364
|
+
},
|
|
3169
3365
|
inspect(): RenderInspection {
|
|
3170
3366
|
const health = internals.healthRegistry.getLastSnapshot();
|
|
3171
3367
|
const state = disposed
|
|
@@ -3211,9 +3407,11 @@ async function makeWebGPURenderer(
|
|
|
3211
3407
|
visibilityStats: Object.freeze({ ...renderSystem.visibilityStats }),
|
|
3212
3408
|
renderScene: renderSystem.renderScene,
|
|
3213
3409
|
reflectionProbes: renderSystem.reflectionProbes,
|
|
3410
|
+
ssrDependencies: renderSystem.ssrDependencies,
|
|
3214
3411
|
environment: renderSystem.environment,
|
|
3215
3412
|
temporal: renderSystem.temporal,
|
|
3216
3413
|
bloom: renderSystem.bloom,
|
|
3414
|
+
extendedLighting: projectExtendedLightingInspection(extendedLightingState),
|
|
3217
3415
|
directionalShadow,
|
|
3218
3416
|
...(renderSystem.temporalTargetInspection === undefined
|
|
3219
3417
|
? {}
|
|
@@ -3221,6 +3419,9 @@ async function makeWebGPURenderer(
|
|
|
3221
3419
|
...(renderSystem.motionBlurInspection === undefined
|
|
3222
3420
|
? {}
|
|
3223
3421
|
: { motionBlur: Object.freeze(renderSystem.motionBlurInspection) }),
|
|
3422
|
+
...(renderSystem.lodOcclusionInspection === undefined
|
|
3423
|
+
? {}
|
|
3424
|
+
: { lodOcclusion: Object.freeze(renderSystem.lodOcclusionInspection) }),
|
|
3224
3425
|
...(renderSystem.transmission === undefined
|
|
3225
3426
|
? {}
|
|
3226
3427
|
: { transmission: renderSystem.transmission }),
|
|
@@ -3731,12 +3932,15 @@ async function makeWebGPURenderer(
|
|
|
3731
3932
|
}
|
|
3732
3933
|
const timingCompletion = timingHost.gpuPassTimingSubmittedWork;
|
|
3733
3934
|
timingHost.gpuPassTimingSubmittedWork = undefined;
|
|
3935
|
+
const reflectionFallbackCompletion = renderSystem.reflectionFallbackCompletion;
|
|
3734
3936
|
internals.pack.instrumentation?.onFrameBoundary?.();
|
|
3735
3937
|
const receiptFrameId = ++frameId;
|
|
3736
3938
|
const receiptGeneration = activeDeviceScope.generation;
|
|
3737
|
-
const
|
|
3738
|
-
|
|
3739
|
-
|
|
3939
|
+
const queueCompletion = timingCompletion ?? internals.device.queue.onSubmittedWorkDone();
|
|
3940
|
+
const completed: FrameReceipt['completed'] = Promise.all([
|
|
3941
|
+
queueCompletion,
|
|
3942
|
+
reflectionFallbackCompletion ?? Promise.resolve(),
|
|
3943
|
+
])
|
|
3740
3944
|
.then(() => ok(undefined))
|
|
3741
3945
|
.catch((cause: unknown) =>
|
|
3742
3946
|
err(
|
|
@@ -3858,6 +4062,7 @@ async function makeWebGPURenderer(
|
|
|
3858
4062
|
}),
|
|
3859
4063
|
);
|
|
3860
4064
|
}
|
|
4065
|
+
await renderSystem.observeLodOcclusion();
|
|
3861
4066
|
const targetReadbacks =
|
|
3862
4067
|
request.targetReadbacks === undefined || request.targetReadbacks.length === 0
|
|
3863
4068
|
? undefined
|
|
@@ -4283,6 +4488,7 @@ async function makeWebGPURenderer(
|
|
|
4283
4488
|
const previousDeviceScope = activeDeviceScope;
|
|
4284
4489
|
activeDeviceScope = recoveryScope;
|
|
4285
4490
|
pipelineState = candidatePipelineState;
|
|
4491
|
+
promotePipelineExtendedLighting(candidatePipelineState, activeDeviceScope);
|
|
4286
4492
|
currentReceipts.clear();
|
|
4287
4493
|
gpuStore.bindDeviceScope(activeDeviceScope);
|
|
4288
4494
|
renderTargetHost.recover();
|
|
@@ -5333,6 +5539,7 @@ async function prepareMaterialShaders(
|
|
|
5333
5539
|
if (!capCheck.ok) throw capCheck.error;
|
|
5334
5540
|
storageBufferCapable = !webgl2Downlevel && capCheck.value;
|
|
5335
5541
|
}
|
|
5542
|
+
const extendedLightingShaderAvailable = deriveExtendedLightingCapability(rhiDevice).admitted;
|
|
5336
5543
|
// The shared PBR view/material ABI consumes the WebGPU minimum of 16
|
|
5337
5544
|
// fragment sampled textures. The authored SpotLight projector is the
|
|
5338
5545
|
// optional 17th texture; select its shader/BGL pair from the same limit so
|
|
@@ -5385,6 +5592,15 @@ async function prepareMaterialShaders(
|
|
|
5385
5592
|
if (msEntry.variants.some((v) => 'VERTEX_COLOR_AVAILABLE' in v.defines)) {
|
|
5386
5593
|
variantDefines.VERTEX_COLOR_AVAILABLE = false;
|
|
5387
5594
|
}
|
|
5595
|
+
if (msEntry.variants.some((v) => 'PROBE_BLEND_AVAILABLE' in v.defines)) {
|
|
5596
|
+
// ProbeBlendRecord is an object-level opt-in, not a device capability.
|
|
5597
|
+
// The boot/default module must stay on the no-probe ABI; record-stage
|
|
5598
|
+
// entries with a retained record request the explicit true variant.
|
|
5599
|
+
variantDefines.PROBE_BLEND_AVAILABLE = false;
|
|
5600
|
+
}
|
|
5601
|
+
if (msEntry.variants.some((v) => 'EXTENDED_LIGHTING_AVAILABLE' in v.defines)) {
|
|
5602
|
+
variantDefines.EXTENDED_LIGHTING_AVAILABLE = extendedLightingShaderAvailable;
|
|
5603
|
+
}
|
|
5388
5604
|
if (
|
|
5389
5605
|
msEntry.identifier === 'forgeax::default-standard-pbr' &&
|
|
5390
5606
|
msEntry.variants.some((v) => 'PROJECTOR_AVAILABLE' in v.defines)
|
|
@@ -5397,6 +5613,17 @@ async function prepareMaterialShaders(
|
|
|
5397
5613
|
) {
|
|
5398
5614
|
variantDefines.TRANSMISSION_AVAILABLE = false;
|
|
5399
5615
|
}
|
|
5616
|
+
// The fallback MRT is a record-time opt-in. Boot registration must select
|
|
5617
|
+
// the ordinary single-target Standard artifact when the manifest declares
|
|
5618
|
+
// the axis; record-time topology derives the true branch only after the
|
|
5619
|
+
// fallback attachment has been admitted. Omitting this false value asks
|
|
5620
|
+
// the closed manifest for a non-existent partial key.
|
|
5621
|
+
if (
|
|
5622
|
+
msEntry.identifier === 'forgeax::default-standard-pbr' &&
|
|
5623
|
+
msEntry.variants.some((v) => 'REFLECTION_FALLBACK_AVAILABLE' in v.defines)
|
|
5624
|
+
) {
|
|
5625
|
+
variantDefines.REFLECTION_FALLBACK_AVAILABLE = false;
|
|
5626
|
+
}
|
|
5400
5627
|
// bug-20260708 M2 (a): PER_INSTANCE_REGION axis default = false at boot.
|
|
5401
5628
|
// Per-entity spritePH path (`main-pass-sprite-draws.ts:511-513`) expects
|
|
5402
5629
|
// `variantSet=undefined` to resolve to the PIR=false shader source (64B
|
|
@@ -5597,6 +5824,7 @@ async function buildReadyWebGPU(
|
|
|
5597
5824
|
const capCheck = assertStorageBufferCap(capValue);
|
|
5598
5825
|
storageBufferCapable = capCheck.ok ? !webgl2Downlevel && capCheck.value : !webgl2Downlevel;
|
|
5599
5826
|
}
|
|
5827
|
+
const extendedLightingShaderAvailable = deriveExtendedLightingCapability(rhiDevice).admitted;
|
|
5600
5828
|
const projectorAvailable =
|
|
5601
5829
|
(rhiDevice.limits.maxSampledTexturesPerShaderStage ?? 0) >=
|
|
5602
5830
|
STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
@@ -5868,7 +6096,7 @@ async function buildReadyWebGPU(
|
|
|
5868
6096
|
// bug-20260610: WebGL2 fallback path — when storageBufferCapable=false
|
|
5869
6097
|
// the variant-aware Step 1b registers material shaders with the
|
|
5870
6098
|
// STORAGE_BUFFER_AVAILABLE=false WGSL (uniform fallback for meshes /
|
|
5871
|
-
//
|
|
6099
|
+
// meshes / instances), but Step 2 above triaged pbr/unlit/sprite/
|
|
5872
6100
|
// pbr-skin/msdf-text from the FLAT `registry.entries()` map which only
|
|
5873
6101
|
// contains the default (storage) variant. Compiling those with
|
|
5874
6102
|
// STORAGE_BUFFER_AVAILABLE=true while the pipeline layout is built
|
|
@@ -5909,6 +6137,9 @@ async function buildReadyWebGPU(
|
|
|
5909
6137
|
directionalPcssCapable(rhiDevice.caps.backendKind)) &&
|
|
5910
6138
|
(!('PROJECTOR_AVAILABLE' in variant.defines) ||
|
|
5911
6139
|
variant.defines.PROJECTOR_AVAILABLE === projectorAvailable) &&
|
|
6140
|
+
(!('REFLECTION_FALLBACK_AVAILABLE' in variant.defines) ||
|
|
6141
|
+
variant.defines.REFLECTION_FALLBACK_AVAILABLE === false) &&
|
|
6142
|
+
variant.defines.PROBE_BLEND_AVAILABLE !== true &&
|
|
5912
6143
|
(!('SKINNING_DISABLED' in variant.defines) ||
|
|
5913
6144
|
variant.defines.SKINNING_DISABLED === true),
|
|
5914
6145
|
);
|
|
@@ -5949,19 +6180,24 @@ async function buildReadyWebGPU(
|
|
|
5949
6180
|
const exact = ms.variants.find(
|
|
5950
6181
|
(variant) =>
|
|
5951
6182
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
6183
|
+
(!('EXTENDED_LIGHTING_AVAILABLE' in variant.defines) ||
|
|
6184
|
+
variant.defines.EXTENDED_LIGHTING_AVAILABLE === extendedLightingShaderAvailable) &&
|
|
5952
6185
|
(!('CLUSTER_FORWARD_AVAILABLE' in variant.defines) ||
|
|
5953
6186
|
variant.defines.CLUSTER_FORWARD_AVAILABLE === standardBootClusterAxis()) &&
|
|
5954
6187
|
(!('VERTEX_COLOR_AVAILABLE' in variant.defines) ||
|
|
5955
6188
|
variant.defines.VERTEX_COLOR_AVAILABLE === false) &&
|
|
5956
6189
|
(!('PROJECTOR_AVAILABLE' in variant.defines) ||
|
|
5957
6190
|
variant.defines.PROJECTOR_AVAILABLE === projectorAvailable) &&
|
|
6191
|
+
variant.defines.PROBE_BLEND_AVAILABLE !== true &&
|
|
5958
6192
|
(!('SKINNING_DISABLED' in variant.defines) ||
|
|
5959
6193
|
variant.defines.SKINNING_DISABLED === true) &&
|
|
5960
6194
|
(!('DIRECTIONAL_PCSS_AVAILABLE' in variant.defines) ||
|
|
5961
6195
|
variant.defines.DIRECTIONAL_PCSS_AVAILABLE ===
|
|
5962
6196
|
directionalPcssCapable(rhiDevice.caps.backendKind)) &&
|
|
5963
6197
|
(!('TRANSMISSION_AVAILABLE' in variant.defines) ||
|
|
5964
|
-
variant.defines.TRANSMISSION_AVAILABLE === false)
|
|
6198
|
+
variant.defines.TRANSMISSION_AVAILABLE === false) &&
|
|
6199
|
+
(!('REFLECTION_FALLBACK_AVAILABLE' in variant.defines) ||
|
|
6200
|
+
variant.defines.REFLECTION_FALLBACK_AVAILABLE === false),
|
|
5965
6201
|
);
|
|
5966
6202
|
const variant = exact === undefined ? undefined : findVariantByKey(ms, exact.definesKey);
|
|
5967
6203
|
if (variant !== undefined) {
|
|
@@ -6224,6 +6460,7 @@ async function buildReadyWebGPU(
|
|
|
6224
6460
|
storageBufferCapable,
|
|
6225
6461
|
directionalPcssCapable(rhiDevice.caps.backendKind),
|
|
6226
6462
|
projectorAvailable,
|
|
6463
|
+
extendedLightingShaderAvailable,
|
|
6227
6464
|
)
|
|
6228
6465
|
: [];
|
|
6229
6466
|
|
|
@@ -6261,6 +6498,7 @@ async function buildReadyWebGPU(
|
|
|
6261
6498
|
for (const hdrpVariant of selectHdrpPbrPrewarmVariants(
|
|
6262
6499
|
pbrManifestEntry,
|
|
6263
6500
|
storageBufferCapable,
|
|
6501
|
+
extendedLightingShaderAvailable,
|
|
6264
6502
|
transmissionCapable,
|
|
6265
6503
|
directionalPcssCapable(rhiDevice.caps.backendKind),
|
|
6266
6504
|
projectorAvailable,
|
|
@@ -6845,6 +7083,7 @@ async function buildReadyWebGPU(
|
|
|
6845
7083
|
ok(
|
|
6846
7084
|
buildPbrPipelineLayouts(rhiDevice, {
|
|
6847
7085
|
storageBuffer: storageBufferCapable,
|
|
7086
|
+
extendedLighting: extendedLightingShaderAvailable,
|
|
6848
7087
|
projectorAvailable,
|
|
6849
7088
|
}),
|
|
6850
7089
|
),
|
|
@@ -6857,6 +7096,7 @@ async function buildReadyWebGPU(
|
|
|
6857
7096
|
const materialBglResult = ok(pbrLayouts.value.materialBgl);
|
|
6858
7097
|
const meshArrayBglResult = ok(pbrLayouts.value.meshArrayBgl);
|
|
6859
7098
|
const instancesBglResult = ok(pbrLayouts.value.instancesBgl);
|
|
7099
|
+
const probeInstancesBglResult = ok(pbrLayouts.value.probeInstancesBgl);
|
|
6860
7100
|
const pipelineLayoutResult = ok(pbrLayouts.value.pipelineLayout);
|
|
6861
7101
|
|
|
6862
7102
|
// feat-20260609-hdrp-cluster-fragment-ggx M4.5 / w36 (D-10 option A):
|
|
@@ -6869,6 +7109,7 @@ async function buildReadyWebGPU(
|
|
|
6869
7109
|
// selectPipelineLayoutForVariant propagates that null so the clustered
|
|
6870
7110
|
// material remains unavailable instead of binding an incompatible layout.
|
|
6871
7111
|
let hdrpPbrPipelineLayoutHandle: PipelineLayout | null = null;
|
|
7112
|
+
let hdrpProbePbrPipelineLayoutHandle: PipelineLayout | null = null;
|
|
6872
7113
|
// The cluster layout is storage-buffer backed. On the WebGL2/fallback
|
|
6873
7114
|
// capability tier its uniform-buffer projection exceeds the backend's
|
|
6874
7115
|
// fragment-stage limit, and HDRP is not a valid route there anyway.
|
|
@@ -6888,6 +7129,23 @@ async function buildReadyWebGPU(
|
|
|
6888
7129
|
if (hdrpPlRes.ok) {
|
|
6889
7130
|
hdrpPbrPipelineLayoutHandle = hdrpPlRes.value;
|
|
6890
7131
|
}
|
|
7132
|
+
// ProbeBlendRecord adds a fragment-visible storage binding and is not a
|
|
7133
|
+
// legal downlevel extension of the uniform-fallback/WebGL2 layout. Keep
|
|
7134
|
+
// the HDRP probe PSO on the same capability gate as the standard probe
|
|
7135
|
+
// PSO; otherwise layout construction itself exceeds GLES' uniform
|
|
7136
|
+
// buffer limit before a probe draw can be rejected cleanly.
|
|
7137
|
+
if (storageBufferCapable) {
|
|
7138
|
+
const hdrpProbePlRes = rhiDevice.createPipelineLayout({
|
|
7139
|
+
label: 'hdrp-probe-pbr-pl',
|
|
7140
|
+
bindGroupLayouts: [
|
|
7141
|
+
viewBglResult.value,
|
|
7142
|
+
materialBglResult.value,
|
|
7143
|
+
hdrpUnifiedBglRes.value,
|
|
7144
|
+
probeInstancesBglResult.value,
|
|
7145
|
+
],
|
|
7146
|
+
});
|
|
7147
|
+
if (hdrpProbePlRes.ok) hdrpProbePbrPipelineLayoutHandle = hdrpProbePlRes.value;
|
|
7148
|
+
}
|
|
6891
7149
|
}
|
|
6892
7150
|
}
|
|
6893
7151
|
|
|
@@ -6953,6 +7211,7 @@ async function buildReadyWebGPU(
|
|
|
6953
7211
|
// explicit failure -- no silent fallback to URP layout, mirroring memory
|
|
6954
7212
|
// anchor `hdrp-active-must-not-fallback-to-urp-pipeline`).
|
|
6955
7213
|
let pbrSkinPipelineLayoutHandle: PipelineLayout | null = null;
|
|
7214
|
+
let pbrSkinProbePipelineLayoutHandle: PipelineLayout | null = null;
|
|
6956
7215
|
// feat-20260611 R2 / M8 / w28: capture the 3-binding skin mesh-array BGL
|
|
6957
7216
|
// produced by `buildPbrSkinLayouts` so the record stage can build a BG
|
|
6958
7217
|
// matching `pbr-skin-pl` (pipeline-layout BGL[2] is this 3-entry skin
|
|
@@ -6976,6 +7235,7 @@ async function buildReadyWebGPU(
|
|
|
6976
7235
|
);
|
|
6977
7236
|
if (skinLayoutsResult.ok) {
|
|
6978
7237
|
pbrSkinPipelineLayoutHandle = skinLayoutsResult.value.pipelineLayout;
|
|
7238
|
+
pbrSkinProbePipelineLayoutHandle = skinLayoutsResult.value.probePipelineLayout;
|
|
6979
7239
|
pbrSkinMeshBindGroupLayoutHandle = skinLayoutsResult.value.meshArrayBgl;
|
|
6980
7240
|
}
|
|
6981
7241
|
}
|
|
@@ -7342,54 +7602,6 @@ async function buildReadyWebGPU(
|
|
|
7342
7602
|
// count >= 2 in createRenderer.ts — definition + this exposure).
|
|
7343
7603
|
setGrowMeshSsboHook(meshSsboController.growMeshSsbo, meshSsboController.state);
|
|
7344
7604
|
|
|
7345
|
-
// feat-20260519-light-casters-point-spot-pbr M3 / w20 (D-S1 + D-S2):
|
|
7346
|
-
// PointLight + SpotLight std430 storage buffers. Header 16 B (count u32 +
|
|
7347
|
-
// 12 B pad) + 4-slot first-slice cap (32 B / 48 B per slot). Sized
|
|
7348
|
-
// generously so that the buffer.size never changes once allocated; per-
|
|
7349
|
-
// frame writeBuffer overwrites the entire range (D-S6 full rewrite +
|
|
7350
|
-
// charter P3 explicit failure: stable buffer.size means no orphan slot
|
|
7351
|
-
// reuse).
|
|
7352
|
-
const POINT_LIGHTS_BUFFER_BYTES =
|
|
7353
|
-
LIGHT_ARRAY_HEADER_BYTES + POINT_LIGHT_STD430_BYTES * LIGHT_ARRAY_MAX_SLOTS;
|
|
7354
|
-
const SPOT_LIGHTS_BUFFER_BYTES =
|
|
7355
|
-
LIGHT_ARRAY_HEADER_BYTES + SPOT_LIGHT_STD430_BYTES * LIGHT_ARRAY_MAX_SLOTS;
|
|
7356
|
-
// bug-20260610: WebGL2 fallback — pointLights / spotLights buffers must
|
|
7357
|
-
// be UNIFORM-usage (the storage variant of the shader is gated by
|
|
7358
|
-
// STORAGE_BUFFER_AVAILABLE; the uniform-fallback path expects uniform
|
|
7359
|
-
// buffers). Without this swap, the buffer-creation succeeds (driver still
|
|
7360
|
-
// accepts STORAGE flag), but `createBindGroup` validation rejects the
|
|
7361
|
-
// mismatch: `Usage flags BufferUsages(COPY_DST | STORAGE) ... do not
|
|
7362
|
-
// contain required usage flags BufferUsages(UNIFORM)`.
|
|
7363
|
-
const LIGHTS_BUFFER_USAGE = storageBufferCapable
|
|
7364
|
-
? GPU_BUFFER_USAGE_STORAGE | GPU_BUFFER_USAGE_COPY_DST
|
|
7365
|
-
: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST;
|
|
7366
|
-
const pointLightsBufferResult = runShimSyncStep(
|
|
7367
|
-
() =>
|
|
7368
|
-
rhiDevice.createBuffer({
|
|
7369
|
-
label: 'pbr-point-lights-ssbo',
|
|
7370
|
-
size: POINT_LIGHTS_BUFFER_BYTES,
|
|
7371
|
-
usage: LIGHTS_BUFFER_USAGE,
|
|
7372
|
-
mappedAtCreation: false,
|
|
7373
|
-
}),
|
|
7374
|
-
'webgpu-runtime-error',
|
|
7375
|
-
'createBuffer (point-lights ssbo) succeeded',
|
|
7376
|
-
'check device.limits.maxStorageBufferBindingSize',
|
|
7377
|
-
);
|
|
7378
|
-
if (!pointLightsBufferResult.ok) throw pointLightsBufferResult.error;
|
|
7379
|
-
const spotLightsBufferResult = runShimSyncStep(
|
|
7380
|
-
() =>
|
|
7381
|
-
rhiDevice.createBuffer({
|
|
7382
|
-
label: 'pbr-spot-lights-ssbo',
|
|
7383
|
-
size: SPOT_LIGHTS_BUFFER_BYTES,
|
|
7384
|
-
usage: LIGHTS_BUFFER_USAGE,
|
|
7385
|
-
mappedAtCreation: false,
|
|
7386
|
-
}),
|
|
7387
|
-
'webgpu-runtime-error',
|
|
7388
|
-
'createBuffer (spot-lights ssbo) succeeded',
|
|
7389
|
-
'check device.limits.maxStorageBufferBindingSize',
|
|
7390
|
-
);
|
|
7391
|
-
if (!spotLightsBufferResult.ok) throw spotLightsBufferResult.error;
|
|
7392
|
-
|
|
7393
7605
|
// feat-20260513-instanced-mesh M3 (T-M3-2): identity-mat4 fallback
|
|
7394
7606
|
// storage buffer. Single 64-byte storage buffer carrying one identity
|
|
7395
7607
|
// mat4 column-major. Renderables without an `Instances` component bind
|
|
@@ -7569,6 +7781,225 @@ async function buildReadyWebGPU(
|
|
|
7569
7781
|
);
|
|
7570
7782
|
if (!fallbackTextureViewResult.ok) throw fallbackTextureViewResult.error;
|
|
7571
7783
|
|
|
7784
|
+
let iesModifierFallbackTexture: import('@forgeax/engine-rhi').Texture | undefined;
|
|
7785
|
+
let cookieModifierFallbackTexture: import('@forgeax/engine-rhi').Texture | undefined;
|
|
7786
|
+
let cookieModifierFallbackMatrixBuffer: Buffer | undefined;
|
|
7787
|
+
let iesModifierFallbackView: TextureView | undefined;
|
|
7788
|
+
let cookieModifierFallbackView: TextureView | undefined;
|
|
7789
|
+
let residentLtcLambertView: TextureView | undefined;
|
|
7790
|
+
let residentLtcGgxView: TextureView | undefined;
|
|
7791
|
+
const requireExtendedResource = <T>(value: T | undefined, name: string): T => {
|
|
7792
|
+
if (value === undefined) {
|
|
7793
|
+
throw new Error(`extended-lighting resource missing: ${name}`);
|
|
7794
|
+
}
|
|
7795
|
+
return value;
|
|
7796
|
+
};
|
|
7797
|
+
|
|
7798
|
+
if (extendedLightingShaderAvailable) {
|
|
7799
|
+
// The direct-light modifier ABI is a 32-layer array. It is created only
|
|
7800
|
+
// for the matching extended-lighting topology; the no-extension variant
|
|
7801
|
+
// has no IES/Cookie/LTC declarations or sampled resources at all.
|
|
7802
|
+
const modifierArrayLayers = 32;
|
|
7803
|
+
const iesModifierBytesPerRow = IES_SLICE_WIDTH * 2;
|
|
7804
|
+
const cookieModifierBytesPerRow = COOKIE_SLICE_SIZE * 4;
|
|
7805
|
+
const iesModifierFallbackData = new Uint8Array(
|
|
7806
|
+
iesModifierBytesPerRow * IES_SLICE_HEIGHT * modifierArrayLayers,
|
|
7807
|
+
);
|
|
7808
|
+
for (let offset = 0; offset < iesModifierFallbackData.length; offset += 2) {
|
|
7809
|
+
iesModifierFallbackData[offset] = 0;
|
|
7810
|
+
iesModifierFallbackData[offset + 1] = 0x3c;
|
|
7811
|
+
}
|
|
7812
|
+
const cookieModifierFallbackData = new Uint8Array(
|
|
7813
|
+
cookieModifierBytesPerRow * COOKIE_SLICE_SIZE * modifierArrayLayers,
|
|
7814
|
+
);
|
|
7815
|
+
cookieModifierFallbackData.fill(0xff);
|
|
7816
|
+
const iesModifierFallbackResult = runShimSyncStep(
|
|
7817
|
+
() =>
|
|
7818
|
+
rhiDevice.createTexture({
|
|
7819
|
+
label: 'fallback-ies-profile-array',
|
|
7820
|
+
size: {
|
|
7821
|
+
width: IES_SLICE_WIDTH,
|
|
7822
|
+
height: IES_SLICE_HEIGHT,
|
|
7823
|
+
depthOrArrayLayers: modifierArrayLayers,
|
|
7824
|
+
},
|
|
7825
|
+
mipLevelCount: 1,
|
|
7826
|
+
sampleCount: 1,
|
|
7827
|
+
dimension: '2d',
|
|
7828
|
+
format: 'r16float',
|
|
7829
|
+
usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST,
|
|
7830
|
+
viewFormats: [],
|
|
7831
|
+
textureBindingViewDimension: undefined,
|
|
7832
|
+
}),
|
|
7833
|
+
'webgpu-runtime-error',
|
|
7834
|
+
'createTexture (fallback IES array) succeeded',
|
|
7835
|
+
'check array-layer and texture-binding limits',
|
|
7836
|
+
);
|
|
7837
|
+
if (!iesModifierFallbackResult.ok) throw iesModifierFallbackResult.error;
|
|
7838
|
+
const cookieModifierFallbackResult = runShimSyncStep(
|
|
7839
|
+
() =>
|
|
7840
|
+
rhiDevice.createTexture({
|
|
7841
|
+
label: 'fallback-cookie-array',
|
|
7842
|
+
size: {
|
|
7843
|
+
width: COOKIE_SLICE_SIZE,
|
|
7844
|
+
height: COOKIE_SLICE_SIZE,
|
|
7845
|
+
depthOrArrayLayers: modifierArrayLayers,
|
|
7846
|
+
},
|
|
7847
|
+
mipLevelCount: 1,
|
|
7848
|
+
sampleCount: 1,
|
|
7849
|
+
dimension: '2d',
|
|
7850
|
+
format: 'rgba8unorm',
|
|
7851
|
+
usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST,
|
|
7852
|
+
viewFormats: [],
|
|
7853
|
+
textureBindingViewDimension: undefined,
|
|
7854
|
+
}),
|
|
7855
|
+
'webgpu-runtime-error',
|
|
7856
|
+
'createTexture (fallback Cookie array) succeeded',
|
|
7857
|
+
'check array-layer and texture-binding limits',
|
|
7858
|
+
);
|
|
7859
|
+
if (!cookieModifierFallbackResult.ok) throw cookieModifierFallbackResult.error;
|
|
7860
|
+
const iesWrite = runShimSyncStep(
|
|
7861
|
+
() =>
|
|
7862
|
+
queue.writeTexture(
|
|
7863
|
+
{ texture: iesModifierFallbackResult.value, mipLevel: 0, origin: { x: 0, y: 0, z: 0 } },
|
|
7864
|
+
iesModifierFallbackData,
|
|
7865
|
+
{ offset: 0, bytesPerRow: iesModifierBytesPerRow, rowsPerImage: IES_SLICE_HEIGHT },
|
|
7866
|
+
{
|
|
7867
|
+
width: IES_SLICE_WIDTH,
|
|
7868
|
+
height: IES_SLICE_HEIGHT,
|
|
7869
|
+
depthOrArrayLayers: modifierArrayLayers,
|
|
7870
|
+
},
|
|
7871
|
+
),
|
|
7872
|
+
'queue-write-buffer-out-of-bounds',
|
|
7873
|
+
'queue.writeTexture (fallback IES array) succeeded',
|
|
7874
|
+
'verify modifier IES array row alignment',
|
|
7875
|
+
);
|
|
7876
|
+
if (!iesWrite.ok) throw iesWrite.error;
|
|
7877
|
+
const cookieWrite = runShimSyncStep(
|
|
7878
|
+
() =>
|
|
7879
|
+
queue.writeTexture(
|
|
7880
|
+
{
|
|
7881
|
+
texture: cookieModifierFallbackResult.value,
|
|
7882
|
+
mipLevel: 0,
|
|
7883
|
+
origin: { x: 0, y: 0, z: 0 },
|
|
7884
|
+
},
|
|
7885
|
+
cookieModifierFallbackData,
|
|
7886
|
+
{ offset: 0, bytesPerRow: cookieModifierBytesPerRow, rowsPerImage: COOKIE_SLICE_SIZE },
|
|
7887
|
+
{
|
|
7888
|
+
width: COOKIE_SLICE_SIZE,
|
|
7889
|
+
height: COOKIE_SLICE_SIZE,
|
|
7890
|
+
depthOrArrayLayers: modifierArrayLayers,
|
|
7891
|
+
},
|
|
7892
|
+
),
|
|
7893
|
+
'queue-write-buffer-out-of-bounds',
|
|
7894
|
+
'queue.writeTexture (fallback Cookie array) succeeded',
|
|
7895
|
+
'verify modifier Cookie array row alignment',
|
|
7896
|
+
);
|
|
7897
|
+
if (!cookieWrite.ok) throw cookieWrite.error;
|
|
7898
|
+
const iesModifierFallbackViewResult = runShimSyncStep(
|
|
7899
|
+
() =>
|
|
7900
|
+
rhiDevice.createTextureView(iesModifierFallbackResult.value, {
|
|
7901
|
+
label: 'fallback-ies-profile-array-view',
|
|
7902
|
+
dimension: '2d-array',
|
|
7903
|
+
arrayLayerCount: modifierArrayLayers,
|
|
7904
|
+
}),
|
|
7905
|
+
'webgpu-runtime-error',
|
|
7906
|
+
'createTextureView (fallback IES array) succeeded',
|
|
7907
|
+
'verify 2d-array view matches modifier BGL',
|
|
7908
|
+
);
|
|
7909
|
+
if (!iesModifierFallbackViewResult.ok) throw iesModifierFallbackViewResult.error;
|
|
7910
|
+
iesModifierFallbackTexture = iesModifierFallbackResult.value;
|
|
7911
|
+
iesModifierFallbackView = iesModifierFallbackViewResult.value;
|
|
7912
|
+
const cookieModifierFallbackViewResult = runShimSyncStep(
|
|
7913
|
+
() =>
|
|
7914
|
+
rhiDevice.createTextureView(cookieModifierFallbackResult.value, {
|
|
7915
|
+
label: 'fallback-cookie-array-view',
|
|
7916
|
+
dimension: '2d-array',
|
|
7917
|
+
arrayLayerCount: modifierArrayLayers,
|
|
7918
|
+
}),
|
|
7919
|
+
'webgpu-runtime-error',
|
|
7920
|
+
'createTextureView (fallback Cookie array) succeeded',
|
|
7921
|
+
'verify 2d-array view matches modifier BGL',
|
|
7922
|
+
);
|
|
7923
|
+
if (!cookieModifierFallbackViewResult.ok) throw cookieModifierFallbackViewResult.error;
|
|
7924
|
+
cookieModifierFallbackTexture = cookieModifierFallbackResult.value;
|
|
7925
|
+
cookieModifierFallbackView = cookieModifierFallbackViewResult.value;
|
|
7926
|
+
|
|
7927
|
+
const cookieMatrixFallbackData = createCookieProjectionMatrixData(modifierArrayLayers);
|
|
7928
|
+
const cookieMatrixBufferResult = runShimSyncStep(
|
|
7929
|
+
() =>
|
|
7930
|
+
rhiDevice.createBuffer({
|
|
7931
|
+
label: 'fallback-cookie-matrices',
|
|
7932
|
+
size: COOKIE_MATRIX_BYTES,
|
|
7933
|
+
usage: GPU_BUFFER_USAGE_COPY_DST | GPU_BUFFER_USAGE_UNIFORM,
|
|
7934
|
+
mappedAtCreation: false,
|
|
7935
|
+
}),
|
|
7936
|
+
'webgpu-runtime-error',
|
|
7937
|
+
'createBuffer (fallback Cookie matrices) succeeded',
|
|
7938
|
+
'check uniform-buffer size and usage support',
|
|
7939
|
+
);
|
|
7940
|
+
if (!cookieMatrixBufferResult.ok) throw cookieMatrixBufferResult.error;
|
|
7941
|
+
const cookieMatrixWrite = runShimSyncStep(
|
|
7942
|
+
() =>
|
|
7943
|
+
rhiDevice.queue.writeBuffer(cookieMatrixBufferResult.value, 0, cookieMatrixFallbackData),
|
|
7944
|
+
'queue-write-buffer-out-of-bounds',
|
|
7945
|
+
'queue.writeBuffer (fallback Cookie matrices) succeeded',
|
|
7946
|
+
'verify Cookie matrix payload alignment',
|
|
7947
|
+
);
|
|
7948
|
+
if (!cookieMatrixWrite.ok) throw cookieMatrixWrite.error;
|
|
7949
|
+
cookieModifierFallbackMatrixBuffer = cookieMatrixBufferResult.value;
|
|
7950
|
+
|
|
7951
|
+
const createResidentLtcView = (label: string, table: Uint16Array): TextureView => {
|
|
7952
|
+
const textureResult = runShimSyncStep(
|
|
7953
|
+
() =>
|
|
7954
|
+
rhiDevice.createTexture({
|
|
7955
|
+
label,
|
|
7956
|
+
size: { width: LTC_TABLE_WIDTH, height: LTC_TABLE_HEIGHT, depthOrArrayLayers: 1 },
|
|
7957
|
+
mipLevelCount: 1,
|
|
7958
|
+
sampleCount: 1,
|
|
7959
|
+
dimension: '2d',
|
|
7960
|
+
format: 'rgba16float',
|
|
7961
|
+
usage: GPU_TEXTURE_USAGE_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_DST,
|
|
7962
|
+
viewFormats: [],
|
|
7963
|
+
textureBindingViewDimension: undefined,
|
|
7964
|
+
}),
|
|
7965
|
+
'webgpu-runtime-error',
|
|
7966
|
+
`createTexture (${label}) succeeded`,
|
|
7967
|
+
'check resident LTC table texture capabilities',
|
|
7968
|
+
);
|
|
7969
|
+
if (!textureResult.ok) throw textureResult.error;
|
|
7970
|
+
const uploadResult = runShimSyncStep(
|
|
7971
|
+
() =>
|
|
7972
|
+
queue.writeTexture(
|
|
7973
|
+
{ texture: textureResult.value, mipLevel: 0, origin: { x: 0, y: 0, z: 0 } },
|
|
7974
|
+
new Uint8Array(table.buffer, table.byteOffset, table.byteLength),
|
|
7975
|
+
{ offset: 0, bytesPerRow: LTC_TABLE_WIDTH * 8, rowsPerImage: LTC_TABLE_HEIGHT },
|
|
7976
|
+
{ width: LTC_TABLE_WIDTH, height: LTC_TABLE_HEIGHT, depthOrArrayLayers: 1 },
|
|
7977
|
+
),
|
|
7978
|
+
'queue-write-buffer-out-of-bounds',
|
|
7979
|
+
`queue.writeTexture (${label}) succeeded`,
|
|
7980
|
+
'verify resident LTC table row alignment',
|
|
7981
|
+
);
|
|
7982
|
+
if (!uploadResult.ok) throw uploadResult.error;
|
|
7983
|
+
const viewResult = runShimSyncStep(
|
|
7984
|
+
() =>
|
|
7985
|
+
rhiDevice.createTextureView(textureResult.value, {
|
|
7986
|
+
label: `${label}-view`,
|
|
7987
|
+
dimension: '2d',
|
|
7988
|
+
}),
|
|
7989
|
+
'webgpu-runtime-error',
|
|
7990
|
+
`createTextureView (${label}) succeeded`,
|
|
7991
|
+
'verify resident LTC view matches shader binding',
|
|
7992
|
+
);
|
|
7993
|
+
if (!viewResult.ok) throw viewResult.error;
|
|
7994
|
+
return viewResult.value;
|
|
7995
|
+
};
|
|
7996
|
+
residentLtcLambertView = createResidentLtcView(
|
|
7997
|
+
'extended-lighting-ltc-lambert',
|
|
7998
|
+
LTC_TABLES.lambert,
|
|
7999
|
+
);
|
|
8000
|
+
residentLtcGgxView = createResidentLtcView('extended-lighting-ltc-ggx', LTC_TABLES.ggx);
|
|
8001
|
+
}
|
|
8002
|
+
|
|
7572
8003
|
// Normal-slot fallback: 1x1 RGBA8 (128, 128, 255, 255). pbr.wgsl decodes
|
|
7573
8004
|
// sample.rg * 2 - 1 + z = sqrt(1 - x^2 - y^2), so RG=(128,128)=0.5 maps
|
|
7574
8005
|
// to tangent (0, 0, 1) -- zero perturbation when normalTexture is absent.
|
|
@@ -9389,6 +9820,8 @@ async function buildReadyWebGPU(
|
|
|
9389
9820
|
// standard pipeline fields above).
|
|
9390
9821
|
pbrPipelineLayout:
|
|
9391
9822
|
unlitModule !== null && pbrModule !== null ? pipelineLayoutResult.value : null,
|
|
9823
|
+
pbrProbePipelineLayout:
|
|
9824
|
+
unlitModule !== null && pbrModule !== null ? pbrLayouts.value.probePipelineLayout : null,
|
|
9392
9825
|
// feat-20260609-hdrp-cluster-fragment-ggx M4.5 / w36 (D-10 option A):
|
|
9393
9826
|
// HDRP-variant PipelineLayout (4-BGL chain with HDRP unified 7-slot
|
|
9394
9827
|
// group(2) BGL substituted for the URP 1-slot mesh-array BGL). Built
|
|
@@ -9398,6 +9831,8 @@ async function buildReadyWebGPU(
|
|
|
9398
9831
|
// `selectPipelineLayoutForVariant` based on the caller's variantSet.
|
|
9399
9832
|
hdrpPbrPipelineLayout:
|
|
9400
9833
|
unlitModule !== null && pbrModule !== null ? hdrpPbrPipelineLayoutHandle : null,
|
|
9834
|
+
hdrpProbePbrPipelineLayout:
|
|
9835
|
+
unlitModule !== null && pbrModule !== null ? hdrpProbePbrPipelineLayoutHandle : null,
|
|
9401
9836
|
hdrpClusterMembershipPipeline,
|
|
9402
9837
|
hdrpClusterMembershipBindGroupLayout,
|
|
9403
9838
|
// bug-20260611-skin-pipeline-layout: skin-variant PipelineLayout (4-BGL
|
|
@@ -9409,6 +9844,8 @@ async function buildReadyWebGPU(
|
|
|
9409
9844
|
// `LayoutKind = 'pbr-skin'`.
|
|
9410
9845
|
pbrSkinPipelineLayout:
|
|
9411
9846
|
unlitModule !== null && pbrModule !== null ? pbrSkinPipelineLayoutHandle : null,
|
|
9847
|
+
pbrSkinProbePipelineLayout:
|
|
9848
|
+
unlitModule !== null && pbrModule !== null ? pbrSkinProbePipelineLayoutHandle : null,
|
|
9412
9849
|
// feat-20260611 R2 / M8 / w28 (IS-14): record-stage skin BG plumbing.
|
|
9413
9850
|
// Mirror the gating used by `pbrSkinPipelineLayout` -- both fields
|
|
9414
9851
|
// come from the same `buildPbrSkinLayouts` call so they live or die
|
|
@@ -9435,6 +9872,7 @@ async function buildReadyWebGPU(
|
|
|
9435
9872
|
colorAttachmentFormat: swapChainFormats.view,
|
|
9436
9873
|
surfaceProfile: surfaceViewFormats ? 'dual-view' : 'raw-only',
|
|
9437
9874
|
viewBindGroupLayout: viewBglResult.value,
|
|
9875
|
+
extendedLightingAvailable: extendedLightingShaderAvailable,
|
|
9438
9876
|
projectorAvailable,
|
|
9439
9877
|
materialBindGroupLayout: materialBglResult.value,
|
|
9440
9878
|
meshBindGroupLayout: meshArrayBglResult.value,
|
|
@@ -9450,9 +9888,8 @@ async function buildReadyWebGPU(
|
|
|
9450
9888
|
// `{ buffer: Buffer; sizeInBytes: number }` in T-M2-06 to match.
|
|
9451
9889
|
materialUniformBuffer: meshSsboState.material,
|
|
9452
9890
|
meshStorageBuffer: meshSsboState.mesh,
|
|
9453
|
-
pointLightsBuffer: pointLightsBufferResult.value,
|
|
9454
|
-
spotLightsBuffer: spotLightsBufferResult.value,
|
|
9455
9891
|
instancesBindGroupLayout: instancesBglResult.value,
|
|
9892
|
+
probeInstancesBindGroupLayout: probeInstancesBglResult.value,
|
|
9456
9893
|
identityInstanceBuffer: identityInstanceResult.value,
|
|
9457
9894
|
defaultSampler: defaultSamplerResult.value,
|
|
9458
9895
|
nearestSampler: nearestSamplerResult.value,
|
|
@@ -9475,6 +9912,23 @@ async function buildReadyWebGPU(
|
|
|
9475
9912
|
// feat-20260520-directional-light-shadow-mapping M2 / w14 (D-1):
|
|
9476
9913
|
// 1x1 depth32float fallback bound at viewBindGroup binding(3).
|
|
9477
9914
|
shadowFallbackTextureView: shadowFallbackViewResult.value,
|
|
9915
|
+
...(extendedLightingShaderAvailable
|
|
9916
|
+
? {
|
|
9917
|
+
iesProfileTexture: requireExtendedResource(iesModifierFallbackTexture, 'ies texture'),
|
|
9918
|
+
cookieTexture: requireExtendedResource(cookieModifierFallbackTexture, 'cookie texture'),
|
|
9919
|
+
cookieMatrixBuffer: requireExtendedResource(
|
|
9920
|
+
cookieModifierFallbackMatrixBuffer,
|
|
9921
|
+
'cookie matrix buffer',
|
|
9922
|
+
),
|
|
9923
|
+
iesProfileTextureView: requireExtendedResource(iesModifierFallbackView, 'ies view'),
|
|
9924
|
+
cookieTextureView: requireExtendedResource(cookieModifierFallbackView, 'cookie view'),
|
|
9925
|
+
ltcLambertTextureView: requireExtendedResource(
|
|
9926
|
+
residentLtcLambertView,
|
|
9927
|
+
'ltc lambert view',
|
|
9928
|
+
),
|
|
9929
|
+
ltcGgxTextureView: requireExtendedResource(residentLtcGgxView, 'ltc ggx view'),
|
|
9930
|
+
}
|
|
9931
|
+
: {}),
|
|
9478
9932
|
// feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1: 1x1x6
|
|
9479
9933
|
// depth32float cube_array fallback bound at viewBindGroup binding(5)
|
|
9480
9934
|
// when no PointLightShadow snapshots are active. Always-present
|