@forgeax/engine-render 0.1.20 → 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 +365 -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 +18 -0
- package/dist/assembly/device-feature-admission.d.ts.map +1 -0
- package/dist/assembly/directional-shadow-inspection.d.ts +38 -0
- package/dist/assembly/directional-shadow-inspection.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +30 -19
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +19 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/render-target-host.d.ts +46 -0
- package/dist/assembly/render-target-host.d.ts.map +1 -0
- package/dist/assets/render-asset-port.d.ts.map +1 -1
- package/dist/authoring.mjs +4 -1
- package/dist/authoring.mjs.map +1 -1
- package/dist/capture/cube-views.d.ts +21 -0
- package/dist/capture/cube-views.d.ts.map +1 -0
- package/dist/capture/scheduler.d.ts +54 -0
- package/dist/capture/scheduler.d.ts.map +1 -0
- 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-CDTWTHZR.mjs → chunk-FF7K3FSO.mjs} +474 -168
- 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-TBQJOK2L.mjs → chunk-QODA5UKU.mjs} +321 -269
- 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-4XINDLAY.mjs → chunk-ZUWKAZ2I.mjs} +1963 -2178
- 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/camera.d.ts +1 -0
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/cube-camera.d.ts +20 -0
- package/dist/components/cube-camera.d.ts.map +1 -0
- package/dist/components/directional-light.d.ts +11 -2
- package/dist/components/directional-light.d.ts.map +1 -1
- package/dist/components/directional-shadow-filter.d.ts +25 -0
- package/dist/components/directional-shadow-filter.d.ts.map +1 -0
- package/dist/components/index.d.ts +5 -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/reflection-probe.d.ts +17 -0
- package/dist/components/reflection-probe.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 +14836 -9620
- 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/frame.d.ts.map +1 -1
- package/dist/environment/lifecycle.d.ts +5 -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 +166 -93
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/directional-shadow-projection.d.ts +23 -0
- package/dist/extract/directional-shadow-projection.d.ts.map +1 -0
- package/dist/extract/environment.d.ts +14 -0
- package/dist/extract/environment.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/features/fullscreen.d.ts.map +1 -1
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +10 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +13 -2
- package/dist/fullscreen-post-process-pass.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 +97 -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 +21 -19
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +4 -0
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/kernel-cache.d.ts +36 -0
- package/dist/ibl/kernel-cache.d.ts.map +1 -0
- package/dist/index.d.ts +39 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +91 -6
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +214 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances-derived-bounds.d.ts +50 -0
- package/dist/instances-derived-bounds.d.ts.map +1 -0
- 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 -140
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/materials.d.ts.map +1 -1
- package/dist/mesh-material-bindings.d.ts +54 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +38 -14
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts +2 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-lighting/errors.d.ts +4 -0
- package/dist/pipeline/standard-lighting/errors.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/graph.d.ts +24 -0
- package/dist/pipeline/standard-lighting/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/inspection.d.ts +38 -0
- package/dist/pipeline/standard-lighting/inspection.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/layout.d.ts +28 -0
- package/dist/pipeline/standard-lighting/layout.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/light-frame.d.ts +33 -0
- package/dist/pipeline/standard-lighting/light-frame.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/membership.d.ts +14 -0
- package/dist/pipeline/standard-lighting/membership.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/prepare.d.ts +16 -0
- package/dist/pipeline/standard-lighting/prepare.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/topology.d.ts +45 -0
- package/dist/pipeline/standard-lighting/topology.d.ts.map +1 -0
- package/dist/pipeline/standard-lighting/transport.d.ts +36 -0
- package/dist/pipeline/standard-lighting/transport.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts +4 -13
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline/standard-profile.d.ts +5 -26
- package/dist/pipeline/standard-profile.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 +40 -7
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/point-shadow-inspection.d.ts +21 -0
- package/dist/point-shadow-inspection.d.ts.map +1 -0
- 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 +33 -36
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +78 -24
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts +5 -0
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +16 -6
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/gpu-pass-timing/contract.d.ts +96 -0
- package/dist/record/gpu-pass-timing/contract.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/errors.d.ts +18 -0
- package/dist/record/gpu-pass-timing/errors.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/index.d.ts +8 -0
- package/dist/record/gpu-pass-timing/index.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/parser.d.ts +11 -0
- package/dist/record/gpu-pass-timing/parser.d.ts.map +1 -0
- package/dist/record/gpu-pass-timing/session.d.ts +43 -0
- package/dist/record/gpu-pass-timing/session.d.ts.map +1 -0
- package/dist/record/gpu-timing.d.ts +20 -3
- 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 +25 -2
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +2 -0
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +98 -6
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +119 -4
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +7 -5
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/reflection/filter.d.ts +22 -0
- package/dist/reflection/filter.d.ts.map +1 -0
- package/dist/reflection/gpu-table.d.ts +25 -0
- package/dist/reflection/gpu-table.d.ts.map +1 -0
- package/dist/reflection/inspection.d.ts +10 -0
- package/dist/reflection/inspection.d.ts.map +1 -0
- package/dist/reflection/projection.d.ts +140 -0
- package/dist/reflection/projection.d.ts.map +1 -0
- 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 +62 -14
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +1 -0
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +49 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +160 -31
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +28 -2
- package/dist/render-system.d.ts.map +1 -1
- package/dist/renderer.d.ts +43 -0
- package/dist/renderer.d.ts.map +1 -0
- 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 +117 -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/shadow-atlas.d.ts +3 -3
- package/dist/shadow-atlas.d.ts.map +1 -1
- package/dist/ssao-buffers.d.ts +3 -0
- package/dist/ssao-buffers.d.ts.map +1 -1
- 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/targets/contracts.d.ts +66 -0
- package/dist/targets/contracts.d.ts.map +1 -0
- package/dist/targets/graph-projection.d.ts +29 -0
- package/dist/targets/graph-projection.d.ts.map +1 -0
- package/dist/targets/material-source.d.ts +34 -0
- package/dist/targets/material-source.d.ts.map +1 -0
- package/dist/targets/owner.d.ts +32 -0
- package/dist/targets/owner.d.ts.map +1 -0
- package/dist/targets/physical.d.ts +18 -0
- package/dist/targets/physical.d.ts.map +1 -0
- package/dist/targets/readback.d.ts +39 -0
- package/dist/targets/readback.d.ts.map +1 -0
- 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 +5 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +1 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/passes.d.ts +9 -5
- package/dist/volume/passes.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/bloom-exact-zero.unit.test.ts +48 -19
- package/src/__tests__/bloom-lifecycle.integration.test.ts +41 -13
- package/src/__tests__/camera-target-consumption.unit.test.ts +53 -0
- package/src/__tests__/cluster-binner.unit.test.ts +35 -0
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +12 -2
- package/src/__tests__/cube-camera-scheduler.unit.test.ts +70 -0
- package/src/__tests__/cube-camera-single-submit.perf.test.ts +75 -0
- package/src/__tests__/cube-camera-view-matrices.unit.test.ts +41 -0
- package/src/__tests__/cube-camera-vocabulary.unit.test.ts +47 -0
- package/src/__tests__/cube-camera.integration.test.ts +42 -0
- package/src/__tests__/detached-inspection.integration.test.ts +6 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +85 -0
- package/src/__tests__/direct-light-snapshot.unit.test.ts +62 -6
- package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
- 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 +288 -29
- package/src/__tests__/fog-component.unit.test.ts +67 -0
- package/src/__tests__/fog.integration.test.ts +86 -0
- package/src/__tests__/frame-observation.contract.test.ts +15 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +146 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -8
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +18 -1
- package/src/__tests__/gpu-driven-production.integration.test.ts +579 -17
- 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 +101 -6
- 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-pass-timing-browser-runner.ts +281 -0
- package/src/__tests__/gpu-pass-timing-coexistence.integration.test.ts +80 -0
- package/src/__tests__/gpu-pass-timing-contract.unit.test.ts +243 -0
- package/src/__tests__/gpu-pass-timing-exact-zero.unit.test.ts +131 -0
- package/src/__tests__/gpu-pass-timing-lifecycle.integration.test.ts +230 -0
- package/src/__tests__/gpu-pass-timing-observation.unit.test.ts +233 -0
- package/src/__tests__/gpu-pass-timing-session.unit.test.ts +438 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +26 -0
- package/src/__tests__/gpu-pass-timing.rhi-null.unit.test.ts +125 -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__/hdrp-group2-visibility.unit.test.ts +24 -0
- package/src/__tests__/instances-culling.integration.test.ts +94 -0
- package/src/__tests__/instances-derived-bounds.unit.test.ts +118 -0
- 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__/material-observation.integration.test.ts +101 -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__/pbr-view-layout.unit.test.ts +16 -0
- package/src/__tests__/point-shadow-inspection.unit.test.ts +60 -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__/public-surface.test-d.ts +10 -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-authoring.unit.test.ts +71 -0
- package/src/__tests__/reflection-probe-filter.unit.test.ts +54 -0
- package/src/__tests__/reflection-probe-ibl-topology.unit.test.ts +45 -0
- package/src/__tests__/reflection-probe-owner-placement.unit.test.ts +39 -0
- package/src/__tests__/reflection-probe-pass-encode.integration.test.ts +101 -0
- package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +120 -0
- package/src/__tests__/reflection-probe-recovery.integration.test.ts +55 -0
- package/src/__tests__/reflection-probe-selection.unit.test.ts +46 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +19 -3
- 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__/render-target-admission.unit.test.ts +80 -0
- package/src/__tests__/render-target-graph-projection.unit.test.ts +102 -0
- package/src/__tests__/render-target-inspection.unit.test.ts +52 -0
- package/src/__tests__/render-target-material-source.test-d.ts +30 -0
- package/src/__tests__/render-target-material-source.unit.test.ts +97 -0
- package/src/__tests__/render-target-owner-placement.unit.test.ts +203 -0
- package/src/__tests__/render-target-owner.unit.test.ts +122 -0
- package/src/__tests__/render-target-probe-soak.perf.test.ts +94 -0
- package/src/__tests__/render-target-public-schema.json +107 -0
- package/src/__tests__/render-target-public-schema.unit.test.ts +91 -0
- package/src/__tests__/render-target-public-surface.test-d.ts +83 -0
- package/src/__tests__/render-target-readback.integration.test.ts +90 -0
- package/src/__tests__/render-target-recovery.integration.test.ts +85 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +124 -0
- package/src/__tests__/renderer-lifecycle.integration.test.ts +14 -0
- package/src/__tests__/shader-stage-surface.unit.test.ts +19 -4
- package/src/__tests__/skin-motion-regression.unit.test.ts +154 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +13 -4
- 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-capability.integration.test.ts +63 -0
- package/src/__tests__/standard-cluster-transport.unit.test.ts +121 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +299 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +15 -5
- package/src/__tests__/standard-lighting-inspection.integration.test.ts +138 -0
- package/src/__tests__/standard-lighting-ssot.unit.test.ts +18 -0
- package/src/__tests__/standard-lighting-write.unit.test.ts +441 -0
- package/src/__tests__/standard-lighting.integration.test.ts +67 -0
- package/src/__tests__/standard-pipeline.dawn.test.ts +206 -5
- package/src/__tests__/standard-pipeline.integration.test.ts +181 -150
- package/src/__tests__/standard-probe-binding.unit.test.ts +25 -0
- package/src/__tests__/standard-profile-oracle.unit.test.ts +23 -38
- 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__/typed-pipeline-topology.unit.test.ts +395 -14
- package/src/__tests__/urp-pipeline-targets.unit.test.ts +2 -2
- 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__/visibility-resolver.unit.test.ts +15 -0
- 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 +80 -0
- package/src/assembly/directional-shadow-inspection.ts +122 -0
- package/src/assembly/factory.ts +1411 -366
- package/src/assembly/host-contract.ts +35 -0
- package/src/assembly/render-target-host.ts +847 -0
- package/src/assets/asset-decoders.ts +1 -1
- package/src/assets/render-asset-port.ts +7 -1
- package/src/capture/cube-views.ts +69 -0
- package/src/capture/scheduler.ts +265 -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/__tests__/schema-contract.unit.test.ts +12 -0
- package/src/components/__tests__/schema-parity.unit.test.ts +9 -0
- package/src/components/camera.ts +4 -0
- package/src/components/cube-camera.ts +52 -0
- package/src/components/directional-light.ts +22 -2
- package/src/components/directional-shadow-filter.ts +57 -0
- package/src/components/index.ts +5 -0
- package/src/components/light-helpers.ts +133 -8
- package/src/components/light-probe.ts +13 -0
- package/src/components/rect-area-light.ts +15 -0
- package/src/components/reflection-probe.ts +49 -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/frame.ts +6 -24
- package/src/environment/lifecycle.ts +20 -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 +341 -143
- package/src/extract/camera.ts +3 -3
- package/src/extract/directional-shadow-projection.ts +61 -0
- package/src/extract/environment.ts +57 -0
- package/src/extract/gpu-driven.ts +108 -0
- package/src/extract/visibility.ts +2 -1
- package/src/features/fullscreen.ts +3 -0
- package/src/features/targets.ts +4 -1
- package/src/features/types.ts +10 -0
- package/src/fullscreen-post-process-pass.ts +43 -2
- package/src/gpu-driven/batch-topology.ts +207 -28
- package/src/gpu-driven/prepared-draw.ts +208 -0
- package/src/gpu-driven/production-raster.ts +953 -43
- package/src/gpu-driven/view-gpu.ts +411 -29
- package/src/gpu-scene-schema.ts +13 -0
- package/src/gpu-scene.ts +140 -64
- package/src/gpu-usage.ts +0 -1
- package/src/hdrp-buffers.ts +169 -63
- package/src/ibl/IblPipelineCache.ts +11 -0
- package/src/ibl/kernel-cache.ts +88 -0
- package/src/index.ts +222 -0
- package/src/inspection-types.ts +276 -1
- package/src/instances-derived-bounds.ts +171 -0
- package/src/internal.ts +38 -0
- package/src/light-buffer-layout.ts +155 -310
- package/src/materials.ts +16 -2
- package/src/mesh-material-bindings.ts +132 -0
- package/src/pbr-pipeline.ts +255 -60
- package/src/pipeline/standard-forward-lane.ts +93 -16
- package/src/pipeline/standard-lighting/errors.ts +6 -0
- package/src/pipeline/standard-lighting/graph.ts +158 -0
- package/src/pipeline/standard-lighting/inspection.ts +88 -0
- package/src/pipeline/standard-lighting/layout.ts +31 -0
- package/src/pipeline/standard-lighting/light-frame.ts +35 -0
- package/src/pipeline/standard-lighting/membership.ts +43 -0
- package/src/pipeline/standard-lighting/prepare.ts +37 -0
- package/src/pipeline/standard-lighting/topology.ts +120 -0
- package/src/pipeline/standard-lighting/transport.ts +78 -0
- package/src/pipeline/standard-pipeline.ts +124 -193
- package/src/pipeline/standard-post.ts +26 -7
- package/src/pipeline/standard-profile.ts +17 -47
- package/src/pipeline-builder.ts +5 -2
- package/src/pipeline-spec.ts +95 -21
- package/src/plugin.ts +4 -0
- package/src/point-shadow-inspection.ts +55 -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 +64 -1
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +20 -1
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
- package/src/record/frame-lighting.ts +591 -452
- package/src/record/frame-snapshot.ts +92 -39
- package/src/record/frame-targets.ts +17 -0
- package/src/record/frame.ts +662 -158
- package/src/record/gpu-pass-timing/contract.ts +180 -0
- package/src/record/gpu-pass-timing/errors.ts +34 -0
- package/src/record/gpu-pass-timing/index.ts +37 -0
- package/src/record/gpu-pass-timing/parser.ts +97 -0
- package/src/record/gpu-pass-timing/session.ts +938 -0
- package/src/record/gpu-timing.ts +112 -39
- package/src/record/helpers.ts +0 -58
- package/src/record/main-pass-geometry.ts +324 -119
- package/src/record/main-pass-material.ts +103 -10
- package/src/record/main-pass-sprite-draws.ts +99 -103
- package/src/record/main-pass.ts +71 -15
- package/src/record/mesh-ssbo.ts +47 -36
- package/src/record/render-context.ts +136 -3
- package/src/record/shadow-pass.ts +83 -68
- package/src/record/skybox-post-pass.ts +20 -4
- package/src/record/typed-frame-graph.ts +1034 -43
- package/src/record/view-ubo.ts +72 -47
- package/src/reflection/filter.ts +81 -0
- package/src/reflection/gpu-table.ts +44 -0
- package/src/reflection/inspection.ts +67 -0
- package/src/reflection/projection.ts +476 -0
- package/src/reflection/record-owner.ts +1283 -0
- package/src/render-contract.ts +93 -10
- package/src/render-graph-primitives.ts +9 -4
- package/src/render-pipeline.ts +122 -2
- package/src/render-system-extract.ts +1041 -225
- package/src/render-system.ts +1310 -85
- package/src/renderer.ts +222 -0
- 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 +942 -171
- 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/shadow-atlas.ts +5 -5
- package/src/ssao-buffers.ts +9 -2
- 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/targets/contracts.ts +182 -0
- package/src/targets/graph-projection.ts +169 -0
- package/src/targets/material-source.ts +143 -0
- package/src/targets/owner.ts +214 -0
- package/src/targets/physical.ts +18 -0
- package/src/targets/readback.ts +153 -0
- 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/transmission/__tests__/standard-transmission.dawn.test.ts +59 -20
- package/src/typed-render-graph-primitives.ts +53 -3
- package/src/typed-shadow-passes.ts +46 -40
- package/src/volume/passes.ts +61 -58
- package/dist/.tsbuildinfo +0 -1
- package/dist/chunk-4XINDLAY.mjs.map +0 -1
- package/dist/chunk-CDTWTHZR.mjs.map +0 -1
- package/dist/chunk-M2MP4ZOU.mjs +0 -73
- package/dist/chunk-M2MP4ZOU.mjs.map +0 -1
- package/dist/chunk-PCHBPYML.mjs.map +0 -1
- package/dist/chunk-TBQJOK2L.mjs.map +0 -1
- package/dist/chunk-VFPLTKCU.mjs.map +0 -1
|
@@ -2,40 +2,48 @@
|
|
|
2
2
|
// feat-20260704 M5/w31: further-split from frame.ts (AC-05 <=1500 lines/file).
|
|
3
3
|
// Pure leaf helpers invoked once each from recordFrame; behavior verbatim.
|
|
4
4
|
|
|
5
|
-
import { mat4,
|
|
5
|
+
import { mat4, vec3 } from '@forgeax/engine-math';
|
|
6
6
|
import {
|
|
7
7
|
type BindGroup,
|
|
8
8
|
type BindGroupEntry,
|
|
9
9
|
type BindGroupLayout,
|
|
10
|
+
type RhiDevice,
|
|
10
11
|
RhiError,
|
|
11
12
|
type TextureView,
|
|
12
13
|
} from '@forgeax/engine-rhi';
|
|
13
|
-
import { toShared } from '@forgeax/engine-types';
|
|
14
|
-
import { bin, type ClusterBinProfilePhase, type ClusterBinProfileRunner } from '../cluster-binner';
|
|
14
|
+
import { err, ok, type Result, toShared } from '@forgeax/engine-types';
|
|
15
15
|
import {
|
|
16
|
-
HdrpIndexListOverflowError,
|
|
17
|
-
HdrpLightBudgetExceededError,
|
|
18
16
|
PointShadowAtlasBoundsViolationError,
|
|
19
17
|
PointShadowAtlasUninitializedError,
|
|
18
|
+
type RenderError,
|
|
19
|
+
RendererOperationError,
|
|
20
|
+
StandardClusterTransportUnavailableError,
|
|
20
21
|
} from '../errors/render';
|
|
21
22
|
import {
|
|
22
23
|
createHdrpClusterMembershipBindGroup,
|
|
23
24
|
createHdrpUnifiedBindGroup,
|
|
24
25
|
getOrCreateHdrpBuffers,
|
|
25
|
-
HDRP_UNIFORM_LIGHT_CAPACITY,
|
|
26
26
|
packClusterUniform,
|
|
27
27
|
} from '../hdrp-buffers';
|
|
28
|
+
import { DIRECT_LIGHT_SLOT_FLOAT_COUNT, packDirectLightSlot } from '../light-buffer-layout';
|
|
28
29
|
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from '../
|
|
38
|
-
import {
|
|
30
|
+
type PreparedStandardLighting,
|
|
31
|
+
prepareStandardLighting,
|
|
32
|
+
} from '../pipeline/standard-lighting/prepare';
|
|
33
|
+
import type { StandardTopologyInputValue } from '../pipeline/standard-lighting/topology';
|
|
34
|
+
import {
|
|
35
|
+
type StandardClusterTransport,
|
|
36
|
+
selectStandardClusterTransport,
|
|
37
|
+
} from '../pipeline/standard-lighting/transport';
|
|
38
|
+
import { DEFAULT_CLUSTER_GRID, DEFAULT_STANDARD_PROFILE } from '../pipeline/standard-profile';
|
|
39
|
+
import {
|
|
40
|
+
COOKIE_MATRIX_BYTES,
|
|
41
|
+
COOKIE_SLICE_SIZE,
|
|
42
|
+
IES_SLICE_HEIGHT,
|
|
43
|
+
IES_SLICE_WIDTH,
|
|
44
|
+
} from '../prepare/extended-lighting/resources';
|
|
45
|
+
import type { ReflectionProbeTable } from '../reflection/gpu-table';
|
|
46
|
+
import type { ReflectionProbeSelectionResult } from '../reflection/projection';
|
|
39
47
|
import type { CameraSnapshot, RenderHdrpClusterPhase } from '../render-contract';
|
|
40
48
|
import type {
|
|
41
49
|
DirectionalLightSnapshot,
|
|
@@ -44,22 +52,58 @@ import type {
|
|
|
44
52
|
SkyboxSnapshot,
|
|
45
53
|
SkylightSnapshot,
|
|
46
54
|
} from '../render-system-extract';
|
|
47
|
-
import {
|
|
55
|
+
import {
|
|
56
|
+
SHADOW_ATLAS_DEFAULT_FACE_SIZE,
|
|
57
|
+
SHADOW_ATLAS_DEFAULT_LAYERS,
|
|
58
|
+
ShadowAtlas,
|
|
59
|
+
} from '../shadow-atlas';
|
|
48
60
|
import { getOrCreateSsaoBuffers } from '../ssao-buffers';
|
|
49
61
|
import { getSsaoParameters } from '../ssao-config';
|
|
62
|
+
// extendedLighting keeps Spot modifier factors on the existing light record
|
|
63
|
+
// path; no additional render pass or submit owner is introduced.
|
|
50
64
|
import type { BindGroupCounts, RenderFrameState } from './frame-snapshot';
|
|
51
65
|
import {
|
|
52
66
|
computeProjectionMatrix,
|
|
53
67
|
computeViewMatrix,
|
|
54
68
|
isLitMaterialSnapshot,
|
|
55
69
|
warnMultiLightDirectional,
|
|
56
|
-
warnMultiLightPoint,
|
|
57
|
-
warnMultiLightSpot,
|
|
58
70
|
} from './helpers';
|
|
59
71
|
import { getOrCreateFromChain, MESH_SSBO_BYTES, MESH_UBO_FULL_ARRAY_BYTES } from './mesh-ssbo';
|
|
60
72
|
import type { PipelineState, RecordProfileRunner, RenderSystemInternals } from './render-context';
|
|
61
73
|
import { POINTS_LINES_VIEW_BYTES, VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
62
74
|
|
|
75
|
+
export interface StandardReflectionProbeBinding {
|
|
76
|
+
readonly probeIndex: number | undefined;
|
|
77
|
+
readonly useSkylight: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function resolveReflectionProbeBinding(
|
|
81
|
+
selection: ReflectionProbeSelectionResult,
|
|
82
|
+
table?: ReflectionProbeTable,
|
|
83
|
+
): StandardReflectionProbeBinding {
|
|
84
|
+
if (selection.kind !== 'probe') return { probeIndex: undefined, useSkylight: true };
|
|
85
|
+
const primitiveKey = `${selection.worldId}:${selection.entityKey}`;
|
|
86
|
+
const tableIndex = table?.cpuIndexByPrimitive(primitiveKey);
|
|
87
|
+
return { probeIndex: tableIndex ?? selection.entityKey, useSkylight: false };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function projectDeviceOwnedBindGroup<T>(
|
|
91
|
+
state:
|
|
92
|
+
| {
|
|
93
|
+
readonly device: RhiDevice;
|
|
94
|
+
readonly layout: BindGroupLayout;
|
|
95
|
+
readonly bindGroup: T;
|
|
96
|
+
}
|
|
97
|
+
| null
|
|
98
|
+
| undefined,
|
|
99
|
+
device: RhiDevice,
|
|
100
|
+
layout: BindGroupLayout | null,
|
|
101
|
+
): T | null {
|
|
102
|
+
return state !== null && state !== undefined && state.device === device && state.layout === layout
|
|
103
|
+
? state.bindGroup
|
|
104
|
+
: null;
|
|
105
|
+
}
|
|
106
|
+
|
|
63
107
|
function runHdrpClusterProfilePhase<T>(
|
|
64
108
|
runner: RecordProfileRunner | undefined,
|
|
65
109
|
phase: RenderHdrpClusterPhase,
|
|
@@ -68,6 +112,26 @@ function runHdrpClusterProfilePhase<T>(
|
|
|
68
112
|
return runner === undefined ? action() : runner(phase, action);
|
|
69
113
|
}
|
|
70
114
|
|
|
115
|
+
function mapClusterUploadFailure(
|
|
116
|
+
resource: string,
|
|
117
|
+
error: {
|
|
118
|
+
readonly code: string;
|
|
119
|
+
readonly expected: string;
|
|
120
|
+
readonly hint: string;
|
|
121
|
+
readonly detail?: unknown;
|
|
122
|
+
},
|
|
123
|
+
): RendererOperationError<'device-operation-failed'> {
|
|
124
|
+
return new RendererOperationError('device-operation-failed', {
|
|
125
|
+
operation: 'draw',
|
|
126
|
+
cause: {
|
|
127
|
+
code: error.code,
|
|
128
|
+
expected: `queue.writeBuffer succeeds for Standard Cluster ${resource}`,
|
|
129
|
+
hint: `repair the device write for Standard Cluster ${resource} and retry the frame`,
|
|
130
|
+
detail: error,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
71
135
|
/**
|
|
72
136
|
* feat-20260704 M3/w18: per-frame bind-group cache resolution, extracted
|
|
73
137
|
* verbatim from `recordFrame`. Walks the handle-identity WeakMap caches
|
|
@@ -92,25 +156,45 @@ export function buildPerFrameBindGroups(
|
|
|
92
156
|
readonly projectorSampler?: import('@forgeax/engine-rhi').Sampler | undefined;
|
|
93
157
|
},
|
|
94
158
|
includeView = true,
|
|
159
|
+
standardLighting?: StandardTopologyInputValue,
|
|
95
160
|
): {
|
|
96
161
|
viewBindGroup: BindGroup | null;
|
|
97
162
|
meshBindGroup: BindGroup | null;
|
|
98
163
|
hdrpClusterBindGroup: BindGroup | null;
|
|
99
164
|
hdrpClusterMembershipBindGroup: BindGroup | null;
|
|
100
165
|
} {
|
|
101
|
-
// View main (#1) chain = b0(viewUniformBuffer),
|
|
102
|
-
// b2(spotLightsBuffer), b3(graph shadowDepth view or
|
|
166
|
+
// View main (#1) chain = b0(viewUniformBuffer), b3(graph shadowDepth view or
|
|
103
167
|
// shadowFallbackTextureView), b4(shadowSampler), b5(atlas view), b6
|
|
104
|
-
// (shadowParams); variant 'view-main'.
|
|
168
|
+
// (shadowParams); variant 'view-main'. Local lights are never mirrored in
|
|
169
|
+
// the view group: Standard consumes the one Cluster group(2) payload.
|
|
105
170
|
// Mesh (#2) chain = inner b0 buffer (meshStorageBuffer.buffer); variant 'mesh'.
|
|
106
171
|
let viewBindGroup: BindGroup | null = null;
|
|
107
172
|
let meshBindGroup: BindGroup | null = null;
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
173
|
+
// Standard clustered group(2) bind group. Non-null when the prepared
|
|
174
|
+
// topology selects the cluster transport and its persistent allocation
|
|
175
|
+
// succeeds; consumed by recordMainPass at `setBindGroup(2, ...)`.
|
|
111
176
|
let hdrpClusterBindGroup: BindGroup | null = null;
|
|
112
|
-
|
|
113
|
-
|
|
177
|
+
// The prepared Standard topology is the per-frame authority for whether
|
|
178
|
+
// group(2) carries Cluster storage. A missing declaration is the prewarm /
|
|
179
|
+
// non-Standard path and must not allocate or bind the Cluster bundle based
|
|
180
|
+
// on the install-time pipeline identity alone.
|
|
181
|
+
const clusteredLighting = standardLighting?.kind === 'clustered';
|
|
182
|
+
const membershipLayout = pipelineState.hdrpClusterMembershipBindGroupLayout;
|
|
183
|
+
const membership = frameState.hdrpClusterMembership;
|
|
184
|
+
const expectedHdrpBuffers =
|
|
185
|
+
hasValidated && clusteredLighting
|
|
186
|
+
? getOrCreateHdrpBuffers(internals, frameState.installedPipelineConfig?.clusterGrid)
|
|
187
|
+
: null;
|
|
188
|
+
const currentMembership =
|
|
189
|
+
expectedHdrpBuffers !== null &&
|
|
190
|
+
membership?.clusterGridBuffer === expectedHdrpBuffers.clusterGridBuffer
|
|
191
|
+
? projectDeviceOwnedBindGroup(membership, internals.device, membershipLayout)
|
|
192
|
+
: null;
|
|
193
|
+
if (membership !== null && currentMembership === null) {
|
|
194
|
+
frameState.hdrpClusterMembership = null;
|
|
195
|
+
}
|
|
196
|
+
const hdrpClusterMembershipBindGroup: BindGroup | null = clusteredLighting
|
|
197
|
+
? currentMembership
|
|
114
198
|
: null;
|
|
115
199
|
if (hasValidated) {
|
|
116
200
|
// Shadow atlas view comes from the typed graph target.
|
|
@@ -153,6 +237,47 @@ export function buildPerFrameBindGroups(
|
|
|
153
237
|
graphSpotShadowView !== undefined
|
|
154
238
|
? graphSpotShadowView
|
|
155
239
|
: pipelineState.shadowFallbackTextureView;
|
|
240
|
+
const extendedLighting = pipelineState.extendedLightingAvailable ?? true;
|
|
241
|
+
const iesProfileTextureView = pipelineState.iesProfileTextureView;
|
|
242
|
+
const cookieTextureView = pipelineState.cookieTextureView;
|
|
243
|
+
const cookieMatrixBuffer = pipelineState.cookieMatrixBuffer;
|
|
244
|
+
const ltcLambertTextureView = pipelineState.ltcLambertTextureView;
|
|
245
|
+
const ltcGgxTextureView = pipelineState.ltcGgxTextureView;
|
|
246
|
+
const extendedLightingResources =
|
|
247
|
+
extendedLighting &&
|
|
248
|
+
iesProfileTextureView !== undefined &&
|
|
249
|
+
cookieTextureView !== undefined &&
|
|
250
|
+
cookieMatrixBuffer !== undefined &&
|
|
251
|
+
ltcLambertTextureView !== undefined &&
|
|
252
|
+
ltcGgxTextureView !== undefined
|
|
253
|
+
? {
|
|
254
|
+
sampler: pipelineState.defaultSampler,
|
|
255
|
+
iesProfileTextureView,
|
|
256
|
+
cookieTextureView,
|
|
257
|
+
ltcLambertTextureView,
|
|
258
|
+
ltcGgxTextureView,
|
|
259
|
+
cookieMatrixBuffer,
|
|
260
|
+
}
|
|
261
|
+
: undefined;
|
|
262
|
+
if (extendedLighting && extendedLightingResources === undefined) {
|
|
263
|
+
return {
|
|
264
|
+
viewBindGroup: null,
|
|
265
|
+
meshBindGroup: null,
|
|
266
|
+
hdrpClusterBindGroup: null,
|
|
267
|
+
hdrpClusterMembershipBindGroup,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
const extendedLightingViews =
|
|
271
|
+
extendedLightingResources !== undefined
|
|
272
|
+
? [
|
|
273
|
+
extendedLightingResources.sampler,
|
|
274
|
+
extendedLightingResources.iesProfileTextureView,
|
|
275
|
+
extendedLightingResources.cookieTextureView,
|
|
276
|
+
extendedLightingResources.ltcLambertTextureView,
|
|
277
|
+
extendedLightingResources.ltcGgxTextureView,
|
|
278
|
+
extendedLightingResources.cookieMatrixBuffer,
|
|
279
|
+
]
|
|
280
|
+
: [];
|
|
156
281
|
const projectorAvailable = pipelineState.projectorAvailable !== false;
|
|
157
282
|
const b11View = graphTargets?.projector ?? pipelineState.defaultWhiteTextureView;
|
|
158
283
|
const b12Sampler = graphTargets?.projectorSampler ?? pipelineState.defaultSampler;
|
|
@@ -160,14 +285,15 @@ export function buildPerFrameBindGroups(
|
|
|
160
285
|
frameState.viewBindGroupCache,
|
|
161
286
|
[
|
|
162
287
|
pipelineState.viewUniformBuffer,
|
|
163
|
-
pipelineState.pointLightsBuffer,
|
|
164
|
-
pipelineState.spotLightsBuffer,
|
|
165
288
|
b3View,
|
|
166
289
|
shadowSampler,
|
|
167
290
|
b5View,
|
|
168
291
|
pipelineState.shadowParamsBuffer,
|
|
169
292
|
b8View,
|
|
170
|
-
...
|
|
293
|
+
...extendedLightingViews,
|
|
294
|
+
...(extendedLightingResources === undefined && projectorAvailable
|
|
295
|
+
? [b11View, b12Sampler]
|
|
296
|
+
: []),
|
|
171
297
|
pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
172
298
|
],
|
|
173
299
|
'view-main',
|
|
@@ -180,20 +306,6 @@ export function buildPerFrameBindGroups(
|
|
|
180
306
|
value: { buffer: pipelineState.viewUniformBuffer, size: VIEW_UNIFORM_BYTES },
|
|
181
307
|
},
|
|
182
308
|
},
|
|
183
|
-
{
|
|
184
|
-
binding: 1,
|
|
185
|
-
resource: {
|
|
186
|
-
kind: 'buffer',
|
|
187
|
-
value: { buffer: pipelineState.pointLightsBuffer },
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
binding: 2,
|
|
192
|
-
resource: {
|
|
193
|
-
kind: 'buffer',
|
|
194
|
-
value: { buffer: pipelineState.spotLightsBuffer },
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
309
|
{
|
|
198
310
|
binding: 3,
|
|
199
311
|
resource: {
|
|
@@ -263,12 +375,61 @@ export function buildPerFrameBindGroups(
|
|
|
263
375
|
},
|
|
264
376
|
},
|
|
265
377
|
},
|
|
266
|
-
...(projectorAvailable
|
|
378
|
+
...(extendedLightingResources === undefined && projectorAvailable
|
|
267
379
|
? [
|
|
268
380
|
{ binding: 11, resource: { kind: 'textureView' as const, value: b11View } },
|
|
269
381
|
{ binding: 12, resource: { kind: 'sampler' as const, value: b12Sampler } },
|
|
270
382
|
]
|
|
271
383
|
: []),
|
|
384
|
+
...(extendedLightingResources !== undefined
|
|
385
|
+
? [
|
|
386
|
+
{
|
|
387
|
+
binding: 9,
|
|
388
|
+
resource: {
|
|
389
|
+
kind: 'sampler' as const,
|
|
390
|
+
value: extendedLightingResources.sampler,
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
binding: 11,
|
|
395
|
+
resource: {
|
|
396
|
+
kind: 'textureView' as const,
|
|
397
|
+
value: extendedLightingResources.iesProfileTextureView,
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
binding: 12,
|
|
402
|
+
resource: {
|
|
403
|
+
kind: 'textureView' as const,
|
|
404
|
+
value: extendedLightingResources.cookieTextureView,
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
binding: 13,
|
|
409
|
+
resource: {
|
|
410
|
+
kind: 'textureView' as const,
|
|
411
|
+
value: extendedLightingResources.ltcLambertTextureView,
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
binding: 14,
|
|
416
|
+
resource: {
|
|
417
|
+
kind: 'textureView' as const,
|
|
418
|
+
value: extendedLightingResources.ltcGgxTextureView,
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
binding: 15,
|
|
423
|
+
resource: {
|
|
424
|
+
kind: 'buffer' as const,
|
|
425
|
+
value: {
|
|
426
|
+
buffer: extendedLightingResources.cookieMatrixBuffer,
|
|
427
|
+
size: COOKIE_MATRIX_BYTES,
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
]
|
|
432
|
+
: []),
|
|
272
433
|
// feat-20260625-spot-light-shadow-mapping w25: the per-spot
|
|
273
434
|
// fragment-read lightViewProj matrices fold into the View UBO
|
|
274
435
|
// (binding 0, `view.spotLightViewProj`) — no standalone binding 9
|
|
@@ -335,11 +496,8 @@ export function buildPerFrameBindGroups(
|
|
|
335
496
|
// alongside `meshBindGroupCache` keyed on the mesh SSBO + cluster
|
|
336
497
|
// buffer identities; cache invalidates on a buffer-grow event the same
|
|
337
498
|
// way the mesh path does (handle id rotation).
|
|
338
|
-
if (
|
|
339
|
-
const hdrpBuffers =
|
|
340
|
-
internals,
|
|
341
|
-
frameState.installedPipelineConfig?.clusterGrid,
|
|
342
|
-
);
|
|
499
|
+
if (clusteredLighting) {
|
|
500
|
+
const hdrpBuffers = expectedHdrpBuffers;
|
|
343
501
|
if (hdrpBuffers !== null) {
|
|
344
502
|
// D-3: buffer dimension uses the inner `.buffer` (same constraint
|
|
345
503
|
// as the mesh path) so a grow event rotates the chain key.
|
|
@@ -378,13 +536,11 @@ export function buildPerFrameBindGroups(
|
|
|
378
536
|
|
|
379
537
|
/**
|
|
380
538
|
* feat-20260704 M3/w18: per-frame lighting preparation, extracted verbatim from
|
|
381
|
-
* `recordFrame`. (1)
|
|
382
|
-
*
|
|
383
|
-
* point + spot shadow snapshot lists onto frameState for the URP shadow caster
|
|
539
|
+
* `recordFrame`. (1) pin the point + spot shadow snapshot lists onto frameState
|
|
540
|
+
* for the shadow caster
|
|
384
541
|
* pass closures + lazy-allocate the point-shadow cube_array atlas on first
|
|
385
|
-
* non-empty frame (AC-09 zero-shadow zero-alloc). (3)
|
|
386
|
-
*
|
|
387
|
-
* spot arrays, and totalLightCount.
|
|
542
|
+
* non-empty frame (AC-09 zero-shadow zero-alloc). (3) derive the directional
|
|
543
|
+
* fallback and the unified local-light corpus used by Standard Cluster.
|
|
388
544
|
*
|
|
389
545
|
* @internal
|
|
390
546
|
*/
|
|
@@ -392,12 +548,22 @@ export function prepareFrameLighting(
|
|
|
392
548
|
internals: RenderSystemInternals,
|
|
393
549
|
frameState: RenderFrameState,
|
|
394
550
|
lights: ExtractedLights,
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
551
|
+
camera: CameraSnapshot,
|
|
552
|
+
pipelineState?: Pick<
|
|
553
|
+
PipelineState,
|
|
554
|
+
'hdrpClusterMembershipPipeline' | 'hdrpClusterMembershipBindGroupLayout'
|
|
555
|
+
>,
|
|
556
|
+
): Result<
|
|
557
|
+
{
|
|
558
|
+
light: DirectionalLightSnapshot;
|
|
559
|
+
pointLights: ExtractedLights['point'];
|
|
560
|
+
spotLights: ExtractedLights['spot'];
|
|
561
|
+
rectLights: ExtractedLights['rect'];
|
|
562
|
+
totalLightCount: number;
|
|
563
|
+
standard: StandardTopologyInputValue;
|
|
564
|
+
},
|
|
565
|
+
RenderError
|
|
566
|
+
> {
|
|
401
567
|
if (lights.directionalCount > 1) {
|
|
402
568
|
warnMultiLightDirectional(frameState, lights.directionalCount);
|
|
403
569
|
}
|
|
@@ -408,21 +574,6 @@ export function prepareFrameLighting(
|
|
|
408
574
|
// defaults true on the single component, so the warn condition can never
|
|
409
575
|
// arise. The error class + RuntimeErrorCode member have been removed in M4 (m4-t2).
|
|
410
576
|
|
|
411
|
-
// feat-20260608-cluster-lighting M6 / w23 (F-4 fix): URP-only multi-light
|
|
412
|
-
// warn. HDRP supports 256 punctual lights via SSBO; the 4-slot first-slice
|
|
413
|
-
// cap is irrelevant under HDRP, so gate on `!isHdrpActive` to silence noise.
|
|
414
|
-
// Sibling tweak-20260608-rhi-hdr-renderable-caps-and-warn-once (#320)
|
|
415
|
-
// extracted the warn into warnMultiLight{Point,Spot} helpers (warn-once
|
|
416
|
-
// dedup); we keep the helpers and add the HDRP gate on top.
|
|
417
|
-
if (!frameState.isHdrpActive) {
|
|
418
|
-
if (lights.point.length > LIGHT_ARRAY_MAX_SLOTS) {
|
|
419
|
-
warnMultiLightPoint(frameState, lights.point.length);
|
|
420
|
-
}
|
|
421
|
-
if (lights.spot.length > LIGHT_ARRAY_MAX_SLOTS) {
|
|
422
|
-
warnMultiLightSpot(frameState, lights.spot.length);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
577
|
// feat-20260612-point-light-shadows-urp-hdrp M3 / T-M3-2 (plan-strategy §D-1):
|
|
427
578
|
// project lights.pointShadow onto frameState so the URP point shadow caster
|
|
428
579
|
// pass can read the snapshot list during graph execute. Lazy-allocate the
|
|
@@ -438,10 +589,10 @@ export function prepareFrameLighting(
|
|
|
438
589
|
if (lights.pointShadow.length > 0) {
|
|
439
590
|
if (frameState.pointShadowAtlas === null) {
|
|
440
591
|
const firstSnap = lights.pointShadow[0];
|
|
441
|
-
const faceSize = firstSnap?.mapSize ??
|
|
592
|
+
const faceSize = firstSnap?.mapSize ?? SHADOW_ATLAS_DEFAULT_FACE_SIZE;
|
|
442
593
|
frameState.pointShadowAtlas = new ShadowAtlas(internals.device, {
|
|
443
594
|
faceSize,
|
|
444
|
-
layers:
|
|
595
|
+
layers: SHADOW_ATLAS_DEFAULT_LAYERS,
|
|
445
596
|
});
|
|
446
597
|
}
|
|
447
598
|
try {
|
|
@@ -462,14 +613,16 @@ export function prepareFrameLighting(
|
|
|
462
613
|
//
|
|
463
614
|
// - lights.directional : feeds the View UBO at slot [16..23]
|
|
464
615
|
// (lightDir + lightColor; existing path).
|
|
465
|
-
// - lights.point[]
|
|
466
|
-
//
|
|
616
|
+
// - lights.point/spot/rect[] : packed into the one unified Standard
|
|
617
|
+
// Cluster payload below. No per-kind light
|
|
618
|
+
// transport exists at the renderer boundary.
|
|
467
619
|
//
|
|
468
620
|
// Each variant carries the discriminant `kind` so the record-time packing
|
|
469
621
|
// call sites can run exhaustive switch (charter P2 + AC-03).
|
|
470
622
|
const directionalLight: DirectionalLightSnapshot | undefined = lights.directional;
|
|
471
623
|
const pointLights = lights.point;
|
|
472
624
|
const spotLights = lights.spot;
|
|
625
|
+
const rectLights = lights.rect;
|
|
473
626
|
|
|
474
627
|
// Case C: 0 DirectionalLight = legitimate scene; the View UBO falls
|
|
475
628
|
// back to a zero-intensity directional payload so the shader's
|
|
@@ -483,74 +636,191 @@ export function prepareFrameLighting(
|
|
|
483
636
|
};
|
|
484
637
|
|
|
485
638
|
const totalLightCount =
|
|
486
|
-
(directionalLight !== undefined ? 1 : 0) +
|
|
639
|
+
(directionalLight !== undefined ? 1 : 0) +
|
|
640
|
+
pointLights.length +
|
|
641
|
+
spotLights.length +
|
|
642
|
+
rectLights.length;
|
|
643
|
+
|
|
644
|
+
const clusterGrid = frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
|
|
645
|
+
const local = [
|
|
646
|
+
...pointLights.map((source) => ({
|
|
647
|
+
kind: 'point' as const,
|
|
648
|
+
shadowed: source.shadowAtlasLayer !== undefined && source.shadowAtlasLayer >= 0,
|
|
649
|
+
position: source.position,
|
|
650
|
+
range:
|
|
651
|
+
Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0
|
|
652
|
+
? Math.sqrt(1 / source.invRangeSquared)
|
|
653
|
+
: 1000,
|
|
654
|
+
source,
|
|
655
|
+
})),
|
|
656
|
+
...spotLights.map((source) => ({
|
|
657
|
+
kind: 'spot' as const,
|
|
658
|
+
shadowed:
|
|
659
|
+
source.castShadow && source.shadowAtlasTile >= 0 && source.lightViewProj !== undefined,
|
|
660
|
+
position: source.position,
|
|
661
|
+
range:
|
|
662
|
+
Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0
|
|
663
|
+
? Math.sqrt(1 / source.invRangeSquared)
|
|
664
|
+
: 1000,
|
|
665
|
+
source,
|
|
666
|
+
})),
|
|
667
|
+
...rectLights.map((source) => ({
|
|
668
|
+
kind: 'rect-area' as const,
|
|
669
|
+
shadowed: false,
|
|
670
|
+
position: source.position,
|
|
671
|
+
range:
|
|
672
|
+
(Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0
|
|
673
|
+
? Math.sqrt(1 / source.invRangeSquared)
|
|
674
|
+
: 1000) + Math.hypot(source.halfWidth, source.halfHeight),
|
|
675
|
+
source,
|
|
676
|
+
})),
|
|
677
|
+
];
|
|
678
|
+
const prepared = prepareStandardLighting({
|
|
679
|
+
directional: directionalLight,
|
|
680
|
+
local,
|
|
681
|
+
view: computeViewMatrix(camera),
|
|
682
|
+
projection: computeProjectionMatrix(camera),
|
|
683
|
+
near: camera.near,
|
|
684
|
+
far: camera.far,
|
|
685
|
+
grid: clusterGrid,
|
|
686
|
+
lightCount: internals.standardProfile?.lightCount ?? DEFAULT_STANDARD_PROFILE.lightCount,
|
|
687
|
+
renderPath: internals.standardProfile?.renderPath ?? DEFAULT_STANDARD_PROFILE.renderPath,
|
|
688
|
+
});
|
|
689
|
+
if (!prepared.ok) return prepared;
|
|
690
|
+
// Storage-capable Standard frames keep the same Cluster ABI even when the
|
|
691
|
+
// local-light corpus is empty. Directional-only scenes still need the
|
|
692
|
+
// unified mesh group(2) contract so their PBR draws do not select a second
|
|
693
|
+
// local-light layout; the existing transport emits a zero-light payload and
|
|
694
|
+
// the graph remains structurally identical. The no-storage backend has no
|
|
695
|
+
// storage-backed local-light representation, so a directional-only frame
|
|
696
|
+
// remains the sole no-local transport case.
|
|
697
|
+
if (prepared.value.local.length === 0 && !internals.device.caps.storageBuffer) {
|
|
698
|
+
return ok({
|
|
699
|
+
light,
|
|
700
|
+
pointLights,
|
|
701
|
+
spotLights,
|
|
702
|
+
rectLights,
|
|
703
|
+
totalLightCount,
|
|
704
|
+
standard: { kind: 'no-local-lights', prepared: prepared.value },
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
const transport = selectStandardClusterTransport(
|
|
708
|
+
{
|
|
709
|
+
compute: internals.device.caps.compute,
|
|
710
|
+
storageBuffer: internals.device.caps.storageBuffer,
|
|
711
|
+
membershipPipelineReady:
|
|
712
|
+
pipelineState !== undefined &&
|
|
713
|
+
pipelineState.hdrpClusterMembershipPipeline !== null &&
|
|
714
|
+
pipelineState.hdrpClusterMembershipBindGroupLayout !== null,
|
|
715
|
+
},
|
|
716
|
+
prepared.value,
|
|
717
|
+
);
|
|
718
|
+
if (!transport.ok) return transport;
|
|
487
719
|
|
|
488
|
-
return {
|
|
720
|
+
return ok({
|
|
721
|
+
light,
|
|
722
|
+
pointLights,
|
|
723
|
+
spotLights,
|
|
724
|
+
rectLights,
|
|
725
|
+
totalLightCount,
|
|
726
|
+
standard: { kind: 'clustered', prepared: prepared.value, transport: transport.value },
|
|
727
|
+
});
|
|
489
728
|
}
|
|
490
729
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
* @internal
|
|
503
|
-
*/
|
|
504
|
-
export function writePointSpotLightBuffers(
|
|
730
|
+
function modifierDigest(data: Uint8Array): number {
|
|
731
|
+
let hash = 2166136261;
|
|
732
|
+
for (let index = 0; index < data.length; index += 1) {
|
|
733
|
+
hash ^= data[index] ?? 0;
|
|
734
|
+
hash = Math.imul(hash, 16777619);
|
|
735
|
+
}
|
|
736
|
+
return hash >>> 0;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/** Upload authored Spot modifier payloads into the renderer-owned array lane. */
|
|
740
|
+
export function writeSpotModifierTextures(
|
|
505
741
|
internals: RenderSystemInternals,
|
|
506
742
|
pipelineState: PipelineState,
|
|
507
|
-
|
|
743
|
+
spots: readonly ExtractedLights['spot'][number][],
|
|
508
744
|
): void {
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
)
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const slot = pointSlots[i];
|
|
522
|
-
if (slot === undefined) continue;
|
|
523
|
-
const packed = packPointLight(slot);
|
|
524
|
-
const offset = LIGHT_ARRAY_HEADER_BYTES + i * POINT_LIGHT_STD430_BYTES;
|
|
525
|
-
const writeRes = internals.device.queue.writeBuffer(
|
|
526
|
-
pipelineState.pointLightsBuffer,
|
|
527
|
-
offset,
|
|
528
|
-
packed,
|
|
529
|
-
);
|
|
530
|
-
if (!writeRes.ok) throw writeRes.error;
|
|
745
|
+
const iesTexture = pipelineState.iesProfileTexture;
|
|
746
|
+
const cookieTexture = pipelineState.cookieTexture;
|
|
747
|
+
const cookieMatrixBuffer = pipelineState.cookieMatrixBuffer;
|
|
748
|
+
if (iesTexture === undefined && cookieTexture === undefined && cookieMatrixBuffer === undefined)
|
|
749
|
+
return;
|
|
750
|
+
let state = pipelineState.spotModifierUploadState;
|
|
751
|
+
if (state === undefined) {
|
|
752
|
+
state = { ies: new Map(), cookie: new Map(), cookieMatrix: new Map() };
|
|
753
|
+
pipelineState.spotModifierUploadState = state;
|
|
754
|
+
} else if (state.cookieMatrix === undefined) {
|
|
755
|
+
state = { ...state, cookieMatrix: new Map() };
|
|
756
|
+
pipelineState.spotModifierUploadState = state;
|
|
531
757
|
}
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
758
|
+
for (const spot of spots) {
|
|
759
|
+
if (spot.iesProfileSlice !== undefined && spot.iesProfileData !== undefined && iesTexture) {
|
|
760
|
+
if (spot.iesProfileData.byteLength === IES_SLICE_WIDTH * IES_SLICE_HEIGHT * 2) {
|
|
761
|
+
const digest = modifierDigest(spot.iesProfileData);
|
|
762
|
+
if (state.ies.get(spot.iesProfileSlice) !== digest) {
|
|
763
|
+
const result = internals.device.queue.writeTexture(
|
|
764
|
+
{ texture: iesTexture, mipLevel: 0, origin: { x: 0, y: 0, z: spot.iesProfileSlice } },
|
|
765
|
+
spot.iesProfileData,
|
|
766
|
+
{ offset: 0, bytesPerRow: IES_SLICE_WIDTH * 2, rowsPerImage: IES_SLICE_HEIGHT },
|
|
767
|
+
{ width: IES_SLICE_WIDTH, height: IES_SLICE_HEIGHT, depthOrArrayLayers: 1 },
|
|
768
|
+
);
|
|
769
|
+
if (!result.ok) throw result.error;
|
|
770
|
+
state.ies.set(spot.iesProfileSlice, digest);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
// Authored Cookie and the SpotLight projector share the same
|
|
775
|
+
// fixed 256x256 array transport on the extended topology. Their shader
|
|
776
|
+
// projection remains distinct: Cookie uses cookieMatrices, projector
|
|
777
|
+
// uses View.spotLightViewProj. Upload both identities when a frame carries
|
|
778
|
+
// both fields, while the direct-light metadata selects the one in use.
|
|
779
|
+
for (const modifier of [
|
|
780
|
+
{
|
|
781
|
+
slice: spot.cookieSlice,
|
|
782
|
+
data: spot.cookieData,
|
|
783
|
+
matrix: spot.cookieMatrix,
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
slice: spot.projectorSlice,
|
|
787
|
+
data: spot.projectorData,
|
|
788
|
+
matrix: spot.projectorMatrix,
|
|
789
|
+
},
|
|
790
|
+
]) {
|
|
791
|
+
if (modifier.slice !== undefined && modifier.data !== undefined && cookieTexture) {
|
|
792
|
+
if (modifier.data.byteLength === COOKIE_SLICE_SIZE * COOKIE_SLICE_SIZE * 4) {
|
|
793
|
+
const digest = modifierDigest(modifier.data);
|
|
794
|
+
if (state.cookie.get(modifier.slice) !== digest) {
|
|
795
|
+
const result = internals.device.queue.writeTexture(
|
|
796
|
+
{ texture: cookieTexture, mipLevel: 0, origin: { x: 0, y: 0, z: modifier.slice } },
|
|
797
|
+
modifier.data,
|
|
798
|
+
{ offset: 0, bytesPerRow: COOKIE_SLICE_SIZE * 4, rowsPerImage: COOKIE_SLICE_SIZE },
|
|
799
|
+
{ width: COOKIE_SLICE_SIZE, height: COOKIE_SLICE_SIZE, depthOrArrayLayers: 1 },
|
|
800
|
+
);
|
|
801
|
+
if (!result.ok) throw result.error;
|
|
802
|
+
state.cookie.set(modifier.slice, digest);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (modifier.slice !== undefined && modifier.matrix !== undefined && cookieMatrixBuffer) {
|
|
807
|
+
const matrixBytes = new Uint8Array(
|
|
808
|
+
modifier.matrix.buffer,
|
|
809
|
+
modifier.matrix.byteOffset,
|
|
810
|
+
modifier.matrix.byteLength,
|
|
811
|
+
);
|
|
812
|
+
const digest = modifierDigest(matrixBytes);
|
|
813
|
+
if (state.cookieMatrix.get(modifier.slice) !== digest) {
|
|
814
|
+
const result = internals.device.queue.writeBuffer(
|
|
815
|
+
cookieMatrixBuffer,
|
|
816
|
+
modifier.slice * 16 * Float32Array.BYTES_PER_ELEMENT,
|
|
817
|
+
modifier.matrix,
|
|
818
|
+
);
|
|
819
|
+
if (!result.ok) throw result.error;
|
|
820
|
+
state.cookieMatrix.set(modifier.slice, digest);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
554
824
|
}
|
|
555
825
|
}
|
|
556
826
|
|
|
@@ -637,17 +907,9 @@ export function warnZeroLightStandard(
|
|
|
637
907
|
}
|
|
638
908
|
|
|
639
909
|
/**
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
* uploads
|
|
643
|
-
* pipeline is active. A zero-punctual frame explicitly clears the persistent
|
|
644
|
-
* cluster buffers so stale membership cannot leak across frames; direct and
|
|
645
|
-
* non-clustered paths remain untouched.
|
|
646
|
-
*
|
|
647
|
-
* feat-20260608-cluster-lighting M5 / w21 + M6 / w23 + r2 fix-up: fail-soft
|
|
648
|
-
* semantics preserved verbatim — index-list-overflow and light-budget-exceeded
|
|
649
|
-
* fire once per frame (via `frameState.hdrpOncePerFrameFired`) and continue
|
|
650
|
-
* rendering.
|
|
910
|
+
* Write the one prepared Standard Cluster payload into the persistent buffers.
|
|
911
|
+
* Membership has already been derived by `prepareFrameLighting`; this function
|
|
912
|
+
* only packs/uploads that immutable result and never re-runs the binner.
|
|
651
913
|
*
|
|
652
914
|
* @internal
|
|
653
915
|
*/
|
|
@@ -655,254 +917,104 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
655
917
|
internals: RenderSystemInternals,
|
|
656
918
|
frameState: RenderFrameState,
|
|
657
919
|
camera: CameraSnapshot,
|
|
658
|
-
|
|
659
|
-
|
|
920
|
+
prepared: PreparedStandardLighting,
|
|
921
|
+
transport: StandardClusterTransport,
|
|
660
922
|
profilePhase?: RecordProfileRunner,
|
|
661
|
-
|
|
923
|
+
gpuMembershipPipelineReady = false,
|
|
662
924
|
membershipBindGroupLayout: BindGroupLayout | null = null,
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
? (clusterSsaoConfig.intensity ?? 1.0)
|
|
675
|
-
: 0;
|
|
676
|
-
const clusterUniformPayload = packClusterUniform(
|
|
677
|
-
grid,
|
|
678
|
-
camera.near,
|
|
679
|
-
camera.far,
|
|
680
|
-
clusterSsaoIntensity,
|
|
681
|
-
0,
|
|
682
|
-
lightCapacity,
|
|
925
|
+
projectorLightSlotIndex?: number,
|
|
926
|
+
pipelineState?: PipelineState,
|
|
927
|
+
): Result<void, RenderError> {
|
|
928
|
+
const configuredClusterGrid = prepared.layout.grid;
|
|
929
|
+
const gpuMembership = transport.producer === 'gpu';
|
|
930
|
+
if (gpuMembership && (!gpuMembershipPipelineReady || membershipBindGroupLayout === null)) {
|
|
931
|
+
return err(
|
|
932
|
+
new StandardClusterTransportUnavailableError(
|
|
933
|
+
prepared.local.length,
|
|
934
|
+
'the selected GPU Cluster producer requires a ready membership pipeline and bind-group layout',
|
|
935
|
+
),
|
|
683
936
|
);
|
|
684
|
-
const clusterUniformUpload = internals.device.queue.writeBuffer(
|
|
685
|
-
hdrpBuffers.clusterUniformBuffer,
|
|
686
|
-
0,
|
|
687
|
-
new Uint8Array(clusterUniformPayload),
|
|
688
|
-
);
|
|
689
|
-
if (!clusterUniformUpload.ok) internals.errorRegistry.fire(clusterUniformUpload.error);
|
|
690
|
-
const clusterGridClear = internals.device.queue.writeBuffer(
|
|
691
|
-
hdrpBuffers.clusterGridBuffer,
|
|
692
|
-
0,
|
|
693
|
-
new Uint8Array(hdrpBuffers.clusterGridBytes),
|
|
694
|
-
);
|
|
695
|
-
if (!clusterGridClear.ok) internals.errorRegistry.fire(clusterGridClear.error);
|
|
696
|
-
const lightIndexListClear = internals.device.queue.writeBuffer(
|
|
697
|
-
hdrpBuffers.lightIndexListBuffer,
|
|
698
|
-
0,
|
|
699
|
-
new Uint8Array(hdrpBuffers.lightIndexListBytes),
|
|
700
|
-
);
|
|
701
|
-
if (!lightIndexListClear.ok) internals.errorRegistry.fire(lightIndexListClear.error);
|
|
702
|
-
return;
|
|
703
937
|
}
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
if (pointLights.length >= HDRP_LIGHT_BUDGET) {
|
|
715
|
-
effectivePointLights = pointLights.slice(0, HDRP_LIGHT_BUDGET);
|
|
716
|
-
effectiveSpotLights = [];
|
|
717
|
-
} else {
|
|
718
|
-
effectivePointLights = pointLights;
|
|
719
|
-
effectiveSpotLights = spotLights.slice(0, HDRP_LIGHT_BUDGET - pointLights.length);
|
|
720
|
-
}
|
|
938
|
+
const hdrpBuffers = getOrCreateHdrpBuffers(internals, configuredClusterGrid);
|
|
939
|
+
if (hdrpBuffers === null) {
|
|
940
|
+
return err(
|
|
941
|
+
new StandardClusterTransportUnavailableError(
|
|
942
|
+
prepared.local.length,
|
|
943
|
+
'the selected Standard Cluster transport could not allocate its persistent buffers',
|
|
944
|
+
),
|
|
945
|
+
);
|
|
721
946
|
}
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
947
|
+
const existingMembership = projectDeviceOwnedBindGroup(
|
|
948
|
+
frameState.hdrpClusterMembership,
|
|
949
|
+
internals.device,
|
|
950
|
+
membershipBindGroupLayout,
|
|
951
|
+
);
|
|
952
|
+
const membershipMatchesBuffers =
|
|
953
|
+
existingMembership !== null &&
|
|
954
|
+
frameState.hdrpClusterMembership?.clusterGridBuffer === hdrpBuffers.clusterGridBuffer;
|
|
955
|
+
if (!membershipMatchesBuffers) frameState.hdrpClusterMembership = null;
|
|
726
956
|
if (
|
|
727
957
|
gpuMembership &&
|
|
728
958
|
membershipBindGroupLayout !== null &&
|
|
729
|
-
frameState.
|
|
959
|
+
frameState.hdrpClusterMembership === null
|
|
730
960
|
) {
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
961
|
+
const bindGroup = createHdrpClusterMembershipBindGroup(
|
|
962
|
+
internals,
|
|
963
|
+
hdrpBuffers,
|
|
964
|
+
membershipBindGroupLayout,
|
|
965
|
+
);
|
|
966
|
+
if (bindGroup === null) {
|
|
967
|
+
return err(
|
|
968
|
+
new StandardClusterTransportUnavailableError(
|
|
969
|
+
prepared.local.length,
|
|
970
|
+
'the selected GPU Cluster producer could not create its membership bind group',
|
|
971
|
+
),
|
|
737
972
|
);
|
|
738
973
|
}
|
|
739
|
-
|
|
974
|
+
frameState.hdrpClusterMembership = {
|
|
975
|
+
device: internals.device,
|
|
976
|
+
layout: membershipBindGroupLayout,
|
|
977
|
+
bindGroup,
|
|
978
|
+
clusterGridBuffer: hdrpBuffers.clusterGridBuffer,
|
|
979
|
+
};
|
|
740
980
|
}
|
|
741
981
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
profilePhase,
|
|
750
|
-
`record/scene-state/hdrp-cluster/binner/${phase}` as RenderHdrpClusterPhase,
|
|
751
|
-
action,
|
|
752
|
-
);
|
|
753
|
-
const {
|
|
754
|
-
hdrpLights,
|
|
755
|
-
clusterGrid,
|
|
756
|
-
gridX,
|
|
757
|
-
gridY,
|
|
758
|
-
gridZ,
|
|
759
|
-
clusterGridBuf,
|
|
760
|
-
lightIndexListBuf,
|
|
761
|
-
projMatrix,
|
|
762
|
-
viewMatrix,
|
|
763
|
-
} = runHdrpClusterProfilePhase(
|
|
764
|
-
profilePhase,
|
|
765
|
-
'record/scene-state/hdrp-cluster/binner/input-preparation',
|
|
766
|
-
() => {
|
|
767
|
-
const hdrpLights: Array<{ position: Vec3; range: number }> = [];
|
|
768
|
-
for (const pl of effectivePointLights) {
|
|
769
|
-
const range =
|
|
770
|
-
Number.isFinite(pl.invRangeSquared) && pl.invRangeSquared > 0
|
|
771
|
-
? Math.sqrt(1 / pl.invRangeSquared)
|
|
772
|
-
: 1000;
|
|
773
|
-
hdrpLights.push({ position: pl.position, range });
|
|
774
|
-
}
|
|
775
|
-
for (const sl of effectiveSpotLights) {
|
|
776
|
-
const range =
|
|
777
|
-
Number.isFinite(sl.invRangeSquared) && sl.invRangeSquared > 0
|
|
778
|
-
? Math.sqrt(1 / sl.invRangeSquared)
|
|
779
|
-
: 1000;
|
|
780
|
-
hdrpLights.push({ position: sl.position, range });
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
const clusterGrid =
|
|
784
|
-
frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
|
|
785
|
-
const gridX = clusterGrid.x;
|
|
786
|
-
const gridY = clusterGrid.y;
|
|
787
|
-
const gridZ = clusterGrid.z;
|
|
788
|
-
const clusterCount = gridX * gridY * gridZ;
|
|
789
|
-
|
|
790
|
-
const projMatrix = computeProjectionMatrix(camera);
|
|
791
|
-
const viewMatrix = computeViewMatrix(camera);
|
|
792
|
-
|
|
793
|
-
const clusterGridBuf =
|
|
794
|
-
frameState.hdrpClusterGridScratch === null ||
|
|
795
|
-
frameState.hdrpClusterGridScratch.length < clusterCount * 2
|
|
796
|
-
? new Uint32Array(clusterCount * 2)
|
|
797
|
-
: frameState.hdrpClusterGridScratch;
|
|
798
|
-
frameState.hdrpClusterGridScratch = clusterGridBuf;
|
|
799
|
-
const lightIndexListBuf =
|
|
800
|
-
frameState.hdrpLightIndexListScratch === null ||
|
|
801
|
-
frameState.hdrpLightIndexListScratch.length < LIGHT_INDEX_LIST_CAPACITY
|
|
802
|
-
? new Uint32Array(LIGHT_INDEX_LIST_CAPACITY)
|
|
803
|
-
: frameState.hdrpLightIndexListScratch;
|
|
804
|
-
frameState.hdrpLightIndexListScratch = lightIndexListBuf;
|
|
805
|
-
|
|
806
|
-
return {
|
|
807
|
-
hdrpLights,
|
|
808
|
-
clusterGrid,
|
|
809
|
-
gridX,
|
|
810
|
-
gridY,
|
|
811
|
-
gridZ,
|
|
812
|
-
clusterGridBuf,
|
|
813
|
-
lightIndexListBuf,
|
|
814
|
-
projMatrix,
|
|
815
|
-
viewMatrix,
|
|
816
|
-
};
|
|
817
|
-
},
|
|
818
|
-
);
|
|
982
|
+
if (pipelineState !== undefined) {
|
|
983
|
+
const preparedSpots: ExtractedLights['spot'][number][] = [];
|
|
984
|
+
for (const local of prepared.local) {
|
|
985
|
+
if (local.source?.kind === 'spot') preparedSpots.push(local.source);
|
|
986
|
+
}
|
|
987
|
+
writeSpotModifierTextures(internals, pipelineState, preparedSpots);
|
|
988
|
+
}
|
|
819
989
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
viewMatrix,
|
|
827
|
-
projMatrix,
|
|
828
|
-
{ x: gridX, y: gridY, z: gridZ },
|
|
829
|
-
camera.near,
|
|
830
|
-
camera.far,
|
|
831
|
-
clusterGridBuf,
|
|
832
|
-
lightIndexListBuf,
|
|
833
|
-
LIGHT_INDEX_LIST_CAPACITY,
|
|
834
|
-
frameState.hdrpClusterBinScratch,
|
|
835
|
-
binnerProfile,
|
|
836
|
-
{ writeMembership: !gpuMembership },
|
|
837
|
-
),
|
|
838
|
-
);
|
|
990
|
+
const gridX = prepared.layout.grid.x;
|
|
991
|
+
const gridY = prepared.layout.grid.y;
|
|
992
|
+
const gridZ = prepared.layout.grid.z;
|
|
993
|
+
const clusterGridBuf = prepared.clusterGrid;
|
|
994
|
+
const lightIndexListBuf = prepared.lightIndexList;
|
|
995
|
+
const lightIndexCount = prepared.membershipEntryCount;
|
|
839
996
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
// Falsify injection point: FORGEAX_HDRP_FALSIFY_CLUSTER_GRID_ZERO
|
|
849
|
-
// zeroes the cluster_grid buffer so every fragment culls every light.
|
|
850
|
-
// Used by hello-hdrp-lighting smoke FALSIFY=cluster-grid-zero to
|
|
851
|
-
// prove the smoke has discriminability (must FAIL vs baseline).
|
|
852
|
-
// Read process via globalThis to keep this file @types/node-free.
|
|
853
|
-
const envFalsify = (globalThis as { process?: { env?: Record<string, string | undefined> } })
|
|
854
|
-
.process?.env;
|
|
855
|
-
if (envFalsify?.FORGEAX_HDRP_FALSIFY_CLUSTER_GRID_ZERO) {
|
|
856
|
-
clusterGridBuf.fill(0);
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
return {
|
|
860
|
-
clusterGrid,
|
|
861
|
-
gridX,
|
|
862
|
-
gridY,
|
|
863
|
-
gridZ,
|
|
864
|
-
clusterGridBuf,
|
|
865
|
-
lightIndexListBuf,
|
|
866
|
-
lightIndexCount: binResult.ok ? binResult.value : 0,
|
|
867
|
-
};
|
|
868
|
-
});
|
|
997
|
+
// Falsify injection point retained for the real prepared payload. It edits
|
|
998
|
+
// only this frame's projection and never creates a second membership owner.
|
|
999
|
+
const envFalsify = (globalThis as { process?: { env?: Record<string, string | undefined> } })
|
|
1000
|
+
.process?.env;
|
|
1001
|
+
if (envFalsify?.FORGEAX_HDRP_FALSIFY_CLUSTER_GRID_ZERO) clusterGridBuf.fill(0);
|
|
869
1002
|
|
|
870
1003
|
const hdrpPayload = runHdrpClusterProfilePhase(
|
|
871
1004
|
profilePhase,
|
|
872
1005
|
'record/scene-state/hdrp-cluster/payload-packing',
|
|
873
1006
|
() => {
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
const lightCapacity = hdrpBuffers.storageBuffer ? 256 : HDRP_UNIFORM_LIGHT_CAPACITY;
|
|
878
|
-
const lightDataPayload = new Float32Array(lightCapacity * 16);
|
|
1007
|
+
const lightDataPayload = new Float32Array(
|
|
1008
|
+
prepared.layout.lightDataSlotCount * DIRECT_LIGHT_SLOT_FLOAT_COUNT,
|
|
1009
|
+
);
|
|
879
1010
|
let slotIdx = 0;
|
|
880
|
-
for (const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
const layer = pl.shadowAtlasLayer;
|
|
888
|
-
const packed =
|
|
889
|
-
layer !== undefined &&
|
|
890
|
-
layer >= 0 &&
|
|
891
|
-
pl.shadowNear !== undefined &&
|
|
892
|
-
pl.shadowFar !== undefined
|
|
893
|
-
? packLightSlot(pl, {
|
|
894
|
-
shadowAtlasLayer: layer,
|
|
895
|
-
near: pl.shadowNear,
|
|
896
|
-
far: pl.shadowFar,
|
|
897
|
-
})
|
|
898
|
-
: packLightSlot(pl);
|
|
899
|
-
lightDataPayload.set(packed, slotIdx * 16);
|
|
900
|
-
slotIdx += 1;
|
|
901
|
-
}
|
|
902
|
-
for (const sl of effectiveSpotLights) {
|
|
903
|
-
if (slotIdx >= lightCapacity) break;
|
|
904
|
-
const packed = packLightSlot(sl);
|
|
905
|
-
lightDataPayload.set(packed, slotIdx * 16);
|
|
1011
|
+
for (const local of prepared.local) {
|
|
1012
|
+
const source = local.source;
|
|
1013
|
+
if (source === undefined) return null;
|
|
1014
|
+
const projectorSelected =
|
|
1015
|
+
projectorLightSlotIndex === undefined ? undefined : projectorLightSlotIndex === slotIdx;
|
|
1016
|
+
const packed = packDirectLightSlot(source, projectorSelected);
|
|
1017
|
+
lightDataPayload.set(packed, slotIdx * DIRECT_LIGHT_SLOT_FLOAT_COUNT);
|
|
906
1018
|
slotIdx += 1;
|
|
907
1019
|
}
|
|
908
1020
|
|
|
@@ -922,51 +1034,67 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
922
1034
|
camera.near,
|
|
923
1035
|
camera.far,
|
|
924
1036
|
clusterSsaoIntensity,
|
|
925
|
-
|
|
926
|
-
lightCapacity,
|
|
1037
|
+
transport.admittedLightCount,
|
|
927
1038
|
);
|
|
928
1039
|
|
|
929
1040
|
return {
|
|
930
1041
|
hdrpBuffers,
|
|
931
|
-
lightDataUploadPayload: lightDataPayload.subarray(
|
|
1042
|
+
lightDataUploadPayload: lightDataPayload.subarray(
|
|
1043
|
+
0,
|
|
1044
|
+
slotIdx * DIRECT_LIGHT_SLOT_FLOAT_COUNT,
|
|
1045
|
+
),
|
|
932
1046
|
clusterUniformPayload: new Uint8Array(clusterUniformPayload),
|
|
933
1047
|
};
|
|
934
1048
|
},
|
|
935
1049
|
);
|
|
936
1050
|
|
|
937
|
-
|
|
938
|
-
|
|
1051
|
+
if (hdrpPayload === null) {
|
|
1052
|
+
return err(
|
|
1053
|
+
new StandardClusterTransportUnavailableError(
|
|
1054
|
+
prepared.local.length,
|
|
1055
|
+
'the prepared Standard Cluster payload could not be materialized',
|
|
1056
|
+
),
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
const bufferUpload = runHdrpClusterProfilePhase(
|
|
1061
|
+
profilePhase,
|
|
1062
|
+
'record/scene-state/hdrp-cluster/buffer-upload',
|
|
1063
|
+
(): Result<void, RenderError> => {
|
|
939
1064
|
const { hdrpBuffers, lightDataUploadPayload, clusterUniformPayload } = hdrpPayload;
|
|
940
1065
|
const lightDataUpload = internals.device.queue.writeBuffer(
|
|
941
1066
|
hdrpBuffers.lightDataBuffer,
|
|
942
1067
|
0,
|
|
943
1068
|
lightDataUploadPayload,
|
|
944
1069
|
);
|
|
945
|
-
if (!lightDataUpload.ok)
|
|
1070
|
+
if (!lightDataUpload.ok)
|
|
1071
|
+
return err(mapClusterUploadFailure('light-data', lightDataUpload.error));
|
|
946
1072
|
|
|
947
|
-
if (
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
hdrpBuffers.lightBoundsBuffer,
|
|
951
|
-
0,
|
|
952
|
-
frameState.hdrpClusterBinScratch.lightBounds.subarray(0, effectiveLightCount * 6),
|
|
953
|
-
);
|
|
954
|
-
if (!lightBoundsUpload.ok) internals.errorRegistry.fire(lightBoundsUpload.error);
|
|
955
|
-
}
|
|
956
|
-
const clusterGridUpload = internals.device.queue.writeBuffer(
|
|
957
|
-
hdrpBuffers.clusterGridBuffer,
|
|
1073
|
+
if (gpuMembership) {
|
|
1074
|
+
const lightBoundsUpload = internals.device.queue.writeBuffer(
|
|
1075
|
+
hdrpBuffers.lightBoundsBuffer,
|
|
958
1076
|
0,
|
|
959
|
-
|
|
1077
|
+
prepared.lightBounds,
|
|
960
1078
|
);
|
|
961
|
-
if (!
|
|
1079
|
+
if (!lightBoundsUpload.ok)
|
|
1080
|
+
return err(mapClusterUploadFailure('light-bounds', lightBoundsUpload.error));
|
|
1081
|
+
}
|
|
1082
|
+
const clusterGridUpload = internals.device.queue.writeBuffer(
|
|
1083
|
+
hdrpBuffers.clusterGridBuffer,
|
|
1084
|
+
0,
|
|
1085
|
+
clusterGridBuf.subarray(0, gridX * gridY * gridZ * 2),
|
|
1086
|
+
);
|
|
1087
|
+
if (!clusterGridUpload.ok)
|
|
1088
|
+
return err(mapClusterUploadFailure('cluster-grid', clusterGridUpload.error));
|
|
962
1089
|
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1090
|
+
if (!gpuMembership && lightIndexCount > 0) {
|
|
1091
|
+
const lightIndexListUpload = internals.device.queue.writeBuffer(
|
|
1092
|
+
hdrpBuffers.lightIndexListBuffer,
|
|
1093
|
+
0,
|
|
1094
|
+
lightIndexListBuf.subarray(0, lightIndexCount),
|
|
1095
|
+
);
|
|
1096
|
+
if (!lightIndexListUpload.ok) {
|
|
1097
|
+
return err(mapClusterUploadFailure('light-index-list', lightIndexListUpload.error));
|
|
970
1098
|
}
|
|
971
1099
|
}
|
|
972
1100
|
|
|
@@ -975,56 +1103,67 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
975
1103
|
0,
|
|
976
1104
|
clusterUniformPayload,
|
|
977
1105
|
);
|
|
978
|
-
if (!clusterUniformUpload.ok)
|
|
979
|
-
|
|
1106
|
+
if (!clusterUniformUpload.ok) {
|
|
1107
|
+
return err(mapClusterUploadFailure('cluster-uniform', clusterUniformUpload.error));
|
|
1108
|
+
}
|
|
1109
|
+
return ok(undefined);
|
|
1110
|
+
},
|
|
1111
|
+
);
|
|
1112
|
+
if (!bufferUpload.ok) return bufferUpload;
|
|
980
1113
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1114
|
+
// ── feat-20260612-hdrp-ssao M1 / w6 + M7 / w33 ───────────────
|
|
1115
|
+
// Per-frame SSAO uniform write (plan-strategy D-1 + D-C):
|
|
1116
|
+
// view + projection + inverseProjection at offsets 0/64/128 +
|
|
1117
|
+
// intensityPad (vec4 — x=intensity, y=radius, z=bias) at offset 192;
|
|
1118
|
+
// total 256 B (matches host SSAO_UNIFORM_BYTES + WGSL struct).
|
|
1119
|
+
// Single writeBuffer covers all four fields so one queue entry
|
|
1120
|
+
// updates the entire UBO.
|
|
1121
|
+
// Separate from View UBO (592 B invariant); does not affect
|
|
1122
|
+
// material PSO bytecode.
|
|
1123
|
+
//
|
|
1124
|
+
// Writes when HDRP is active; config.ssao?.enabled guard comes in
|
|
1125
|
+
// M4 / w19 after the config.ssao type narrowing is added.
|
|
1126
|
+
const ssaoUpload = runHdrpClusterProfilePhase(
|
|
1127
|
+
profilePhase,
|
|
1128
|
+
'record/scene-state/hdrp-cluster/buffer-upload',
|
|
1129
|
+
(): Result<void, RenderError> => {
|
|
994
1130
|
const ssaoBufs = getOrCreateSsaoBuffers(internals);
|
|
995
|
-
if (ssaoBufs
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1131
|
+
if (ssaoBufs === null) return ok(undefined);
|
|
1132
|
+
const sProj = computeProjectionMatrix(camera);
|
|
1133
|
+
const sView = computeViewMatrix(camera);
|
|
1134
|
+
// inverseProjection = inverse(projection): NDC -> view-space.
|
|
1135
|
+
const invProjOnly = mat4.create();
|
|
1136
|
+
mat4.invert(invProjOnly, sProj);
|
|
1001
1137
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1138
|
+
// Float32Array of 64 (256 B): 3 mat4 (48) + intensityPad vec4 (4)
|
|
1139
|
+
// + 12 trailing padding floats. We only fill the declared region.
|
|
1140
|
+
const ssaoUniformPayload = new Float32Array(64);
|
|
1141
|
+
ssaoUniformPayload.set(sView, 0);
|
|
1142
|
+
ssaoUniformPayload.set(sProj, 16);
|
|
1143
|
+
ssaoUniformPayload.set(invProjOnly, 32);
|
|
1144
|
+
// intensityPad carries the same resolved values as the graph SSAO
|
|
1145
|
+
// calc pass; this early write keeps the shared UBO valid before the
|
|
1146
|
+
// typed fullscreen pass executes.
|
|
1147
|
+
const ssaoConfig = frameState.installedPipelineConfig?.ssao;
|
|
1148
|
+
const parameters = getSsaoParameters(
|
|
1149
|
+
ssaoConfig !== undefined && ssaoConfig.enabled === true ? ssaoConfig : undefined,
|
|
1150
|
+
);
|
|
1151
|
+
ssaoUniformPayload[48] = parameters.intensity;
|
|
1152
|
+
ssaoUniformPayload[49] = parameters.radius;
|
|
1153
|
+
ssaoUniformPayload[50] = parameters.bias;
|
|
1018
1154
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1155
|
+
const ssaoUniformRes = internals.device.queue.writeBuffer(
|
|
1156
|
+
ssaoBufs.uniformBuffer,
|
|
1157
|
+
0,
|
|
1158
|
+
ssaoUniformPayload,
|
|
1159
|
+
);
|
|
1160
|
+
if (!ssaoUniformRes.ok)
|
|
1161
|
+
return err(mapClusterUploadFailure('ssao-uniform', ssaoUniformRes.error));
|
|
1162
|
+
return ok(undefined);
|
|
1163
|
+
},
|
|
1164
|
+
);
|
|
1165
|
+
if (!ssaoUpload.ok) return ssaoUpload;
|
|
1166
|
+
return ok(undefined);
|
|
1028
1167
|
}
|
|
1029
1168
|
|
|
1030
1169
|
/**
|