@forgeax/engine-render 0.1.19 → 0.1.21
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 +215 -0
- package/dist/.tsbuildinfo +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 +34 -20
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +9 -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 +3 -0
- 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-RJRUS55L.mjs → chunk-5YADUCA7.mjs} +1896 -1624
- package/dist/chunk-5YADUCA7.mjs.map +1 -0
- package/dist/{chunk-LOSO4CTV.mjs → chunk-6OPXDQ2E.mjs} +423 -46
- package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
- package/dist/{chunk-IPXCABEP.mjs → chunk-BXBQQKFQ.mjs} +3 -9
- package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
- package/dist/chunk-FS2KNUQH.mjs +1294 -0
- package/dist/chunk-FS2KNUQH.mjs.map +1 -0
- 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 +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/reflection-probe.d.ts +17 -0
- package/dist/components/reflection-probe.d.ts.map +1 -0
- package/dist/components/spot-light.d.ts +12 -0
- package/dist/components/spot-light.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +8552 -1612
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +19 -2
- package/dist/device/gpu-residency.d.ts.map +1 -1
- 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/render.d.ts +209 -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/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.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +0 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-usage.d.ts +2 -0
- package/dist/gpu-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +19 -17
- 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 +35 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +51 -0
- 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.mjs +1 -1
- package/dist/light-buffer-layout.d.ts +30 -13
- package/dist/light-buffer-layout.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 +20 -7
- 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 +12 -20
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +40 -7
- package/dist/pipeline-spec.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/record/frame-lighting.d.ts +29 -32
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +65 -15
- 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 +12 -5
- 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 +37 -0
- package/dist/record/gpu-timing.d.ts.map +1 -0
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +23 -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/render-context.d.ts +48 -3
- 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 +81 -5
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +7 -4
- 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/projection.d.ts +91 -0
- package/dist/reflection/projection.d.ts.map +1 -0
- package/dist/render-contract.d.ts +114 -7
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-data.d.ts +10 -6
- package/dist/render-data.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 +78 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +113 -20
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +24 -3
- package/dist/render-system.d.ts.map +1 -1
- package/dist/renderer-inspect.d.ts +29 -0
- package/dist/renderer-inspect.d.ts.map +1 -0
- package/dist/renderer.d.ts +43 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/scene/render-scene.d.ts +14 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- 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/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.d.ts +44 -0
- package/dist/temporal/frame.d.ts.map +1 -0
- package/dist/temporal/index.mjs +3 -3
- package/dist/typed-render-graph-primitives.d.ts +6 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-atlas.d.ts +10 -0
- package/dist/typed-shadow-atlas.d.ts.map +1 -0
- package/dist/typed-shadow-passes.d.ts +1 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/capability.d.ts +57 -0
- package/dist/volume/capability.d.ts.map +1 -0
- package/dist/volume/component.d.ts +61 -0
- package/dist/volume/component.d.ts.map +1 -0
- package/dist/volume/extract.d.ts +11 -0
- package/dist/volume/extract.d.ts.map +1 -0
- package/dist/volume/inspection.d.ts +38 -0
- package/dist/volume/inspection.d.ts.map +1 -0
- package/dist/volume/passes.d.ts +38 -0
- package/dist/volume/passes.d.ts.map +1 -0
- package/dist/volume/recovery.d.ts +38 -0
- package/dist/volume/recovery.d.ts.map +1 -0
- package/dist/volume/resources.d.ts +57 -0
- package/dist/volume/resources.d.ts.map +1 -0
- package/dist/volume/temporal.d.ts +35 -0
- package/dist/volume/temporal.d.ts.map +1 -0
- 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 +7 -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__/device-feature-admission.unit.test.ts +73 -0
- package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -3
- package/src/__tests__/docs-gate-green.unit.test.ts +38 -0
- package/src/__tests__/factory-contract.integration.test.ts +255 -29
- package/src/__tests__/fog-component.unit.test.ts +67 -0
- package/src/__tests__/fog.integration.test.ts +86 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +37 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +10 -9
- package/src/__tests__/gpu-driven-indirect-raster.dawn.test.ts +17 -1
- package/src/__tests__/gpu-driven-production.integration.test.ts +16 -17
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +12 -4
- 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__/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__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -14
- package/src/__tests__/material-observation.integration.test.ts +101 -0
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
- 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__/public-surface.test-d.ts +10 -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-pass-encode.integration.test.ts +101 -0
- package/src/__tests__/reflection-probe-performance.perf.test.ts +60 -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 +23 -3
- package/src/__tests__/render-feature-docs.unit.test.ts +1 -1
- 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 +76 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +1 -1
- 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 +132 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +32 -2
- 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 +232 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +8 -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 +377 -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 -36
- 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-resolver.unit.test.ts +15 -0
- package/src/__tests__/volume-optics-oracle.unit.test.ts +85 -0
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +655 -0
- package/src/__tests__/volumetric-fog-component.unit.test.ts +71 -0
- package/src/__tests__/volumetric-fog-composite-stage-readback.dawn.test.ts +376 -0
- package/src/__tests__/volumetric-fog-errors.unit.test.ts +51 -0
- package/src/__tests__/volumetric-fog-inspection.unit.test.ts +77 -0
- package/src/__tests__/volumetric-fog-light-capability.unit.test.ts +19 -0
- package/src/__tests__/volumetric-fog-light-consumers.integration.test.ts +18 -0
- package/src/__tests__/volumetric-fog-light-errors.unit.test.ts +17 -0
- package/src/__tests__/volumetric-fog-light-topology.unit.test.ts +21 -0
- package/src/__tests__/volumetric-fog-light-transaction.integration.test.ts +53 -0
- package/src/__tests__/volumetric-fog-m9-watcher.integration.test.ts +27 -0
- package/src/__tests__/volumetric-fog-point-light-owner.integration.test.ts +44 -0
- package/src/__tests__/volumetric-fog-point-spot-accumulation.integration.test.ts +49 -0
- package/src/__tests__/volumetric-fog-point-spot-contract.unit.test.ts +28 -0
- package/src/__tests__/volumetric-fog-point-spot-lkg.integration.test.ts +65 -0
- package/src/__tests__/volumetric-fog-point-spot-selection.integration.test.ts +49 -0
- package/src/__tests__/volumetric-fog-point-spot-temporal.unit.test.ts +55 -0
- package/src/__tests__/volumetric-fog-projector-contract.unit.test.ts +22 -0
- package/src/__tests__/volumetric-fog-projector-inspection.unit.test.ts +12 -0
- package/src/__tests__/volumetric-fog-projector-surface-volume.integration.test.ts +11 -0
- package/src/__tests__/volumetric-fog-projector-transaction.integration.test.ts +26 -0
- package/src/__tests__/volumetric-fog-recovery.integration.test.ts +47 -0
- package/src/__tests__/volumetric-fog-selected-light-resolution.integration.test.ts +63 -0
- package/src/__tests__/volumetric-fog-spot-closure.integration.test.ts +26 -0
- package/src/__tests__/volumetric-fog-spot-contract.test-d.ts +21 -0
- package/src/__tests__/volumetric-fog-spot-contract.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-spot-frame-optics.integration.test.ts +14 -0
- package/src/__tests__/volumetric-fog-spot-optics.integration.test.ts +29 -0
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +550 -0
- package/src/__tests__/volumetric-fog-temporal-selected-light.unit.test.ts +33 -0
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +45 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +237 -0
- package/src/__tests__/volumetric-fog-transparent.integration.test.ts +27 -0
- package/src/assembly/device-feature-admission.ts +73 -0
- package/src/assembly/directional-shadow-inspection.ts +122 -0
- package/src/assembly/factory.ts +1093 -246
- package/src/assembly/host-contract.ts +25 -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/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 +2 -0
- package/src/components/light-helpers.ts +85 -7
- package/src/components/reflection-probe.ts +49 -0
- package/src/components/spot-light.ts +22 -3
- package/src/device/gpu-residency.ts +168 -58
- package/src/environment/frame.ts +6 -24
- package/src/environment/lifecycle.ts +12 -0
- package/src/errors/render.ts +405 -143
- package/src/extract/directional-shadow-projection.ts +61 -0
- package/src/extract/environment.ts +57 -0
- package/src/extract/visibility.ts +25 -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 +1 -0
- package/src/gpu-driven/production-raster.ts +1 -3
- package/src/gpu-scene.ts +8 -1
- package/src/gpu-usage.ts +2 -0
- package/src/hdrp-buffers.ts +165 -59
- package/src/ibl/IblPipelineCache.ts +11 -0
- package/src/ibl/kernel-cache.ts +88 -0
- package/src/index.ts +186 -1
- package/src/inspection-types.ts +56 -0
- package/src/instances-derived-bounds.ts +171 -0
- package/src/light-buffer-layout.ts +74 -26
- package/src/mesh-material-bindings.ts +132 -0
- package/src/pbr-pipeline.ts +139 -27
- package/src/pipeline/standard-forward-lane.ts +65 -15
- package/src/pipeline/standard-lighting/errors.ts +6 -0
- package/src/pipeline/standard-lighting/graph.ts +157 -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 +34 -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 +77 -0
- package/src/pipeline/standard-pipeline.ts +141 -192
- package/src/pipeline/standard-post.ts +26 -7
- package/src/pipeline/standard-profile.ts +23 -30
- package/src/pipeline-spec.ts +84 -21
- package/src/point-shadow-inspection.ts +55 -0
- package/src/record/__tests__/temporal-payload.unit.test.ts +42 -1
- package/src/record/__tests__/transparent-group2-selection.unit.test.ts +73 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +40 -0
- package/src/record/frame-lighting.ts +470 -516
- package/src/record/frame-snapshot.ts +76 -28
- package/src/record/frame-targets.ts +23 -2
- package/src/record/frame.ts +1169 -79
- 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 +233 -0
- package/src/record/main-pass-geometry.ts +120 -80
- package/src/record/main-pass-material.ts +101 -11
- package/src/record/main-pass-sprite-draws.ts +54 -25
- package/src/record/main-pass.ts +71 -15
- package/src/record/render-context.ts +77 -0
- package/src/record/shadow-pass.ts +104 -66
- package/src/record/skybox-post-pass.ts +20 -4
- package/src/record/typed-frame-graph.ts +1016 -32
- package/src/record/view-ubo.ts +63 -28
- package/src/reflection/filter.ts +81 -0
- package/src/reflection/gpu-table.ts +44 -0
- package/src/reflection/projection.ts +281 -0
- package/src/render-contract.ts +147 -4
- package/src/render-data.ts +50 -22
- package/src/render-graph-primitives.ts +9 -4
- package/src/render-pipeline.ts +161 -2
- package/src/render-system-extract.ts +886 -103
- package/src/render-system.ts +1174 -66
- package/src/renderer-inspect.ts +66 -0
- package/src/renderer.ts +222 -0
- package/src/scene/render-scene.ts +117 -9
- package/src/shadow-atlas.ts +5 -5
- package/src/ssao-buffers.ts +9 -2
- 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.ts +128 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +61 -23
- package/src/typed-render-graph-primitives.ts +34 -2
- package/src/typed-shadow-atlas.ts +17 -0
- package/src/typed-shadow-passes.ts +47 -40
- package/src/volume/capability.ts +173 -0
- package/src/volume/component.ts +110 -0
- package/src/volume/extract.ts +21 -0
- package/src/volume/inspection.ts +78 -0
- package/src/volume/passes.ts +686 -0
- package/src/volume/recovery.ts +86 -0
- package/src/volume/resources.ts +236 -0
- package/src/volume/temporal.ts +89 -0
- package/dist/chunk-IPXCABEP.mjs.map +0 -1
- package/dist/chunk-LOSO4CTV.mjs.map +0 -1
- package/dist/chunk-RJRUS55L.mjs.map +0 -1
- package/dist/chunk-YTQ5C7YQ.mjs +0 -584
- package/dist/chunk-YTQ5C7YQ.mjs.map +0 -1
|
@@ -2,39 +2,42 @@
|
|
|
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
|
+
type BindGroupEntry,
|
|
8
9
|
type BindGroupLayout,
|
|
10
|
+
type RhiDevice,
|
|
9
11
|
RhiError,
|
|
10
12
|
type TextureView,
|
|
11
13
|
} from '@forgeax/engine-rhi';
|
|
12
|
-
import { toShared } from '@forgeax/engine-types';
|
|
13
|
-
import { bin, type ClusterBinProfilePhase, type ClusterBinProfileRunner } from '../cluster-binner';
|
|
14
|
+
import { err, ok, type Result, toShared } from '@forgeax/engine-types';
|
|
14
15
|
import {
|
|
15
|
-
HdrpIndexListOverflowError,
|
|
16
|
-
HdrpLightBudgetExceededError,
|
|
17
16
|
PointShadowAtlasBoundsViolationError,
|
|
18
17
|
PointShadowAtlasUninitializedError,
|
|
18
|
+
type RenderError,
|
|
19
|
+
RendererOperationError,
|
|
20
|
+
StandardClusterTransportUnavailableError,
|
|
19
21
|
} from '../errors/render';
|
|
20
22
|
import {
|
|
21
23
|
createHdrpClusterMembershipBindGroup,
|
|
22
24
|
createHdrpUnifiedBindGroup,
|
|
23
25
|
getOrCreateHdrpBuffers,
|
|
24
|
-
HDRP_UNIFORM_LIGHT_CAPACITY,
|
|
25
26
|
packClusterUniform,
|
|
26
27
|
} from '../hdrp-buffers';
|
|
28
|
+
import { packLightSlot } from '../light-buffer-layout';
|
|
27
29
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} from '../
|
|
37
|
-
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 type { ReflectionProbeTable } from '../reflection/gpu-table';
|
|
40
|
+
import type { ReflectionProbeSelectionResult } from '../reflection/projection';
|
|
38
41
|
import type { CameraSnapshot, RenderHdrpClusterPhase } from '../render-contract';
|
|
39
42
|
import type {
|
|
40
43
|
DirectionalLightSnapshot,
|
|
@@ -43,7 +46,11 @@ import type {
|
|
|
43
46
|
SkyboxSnapshot,
|
|
44
47
|
SkylightSnapshot,
|
|
45
48
|
} from '../render-system-extract';
|
|
46
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
SHADOW_ATLAS_DEFAULT_FACE_SIZE,
|
|
51
|
+
SHADOW_ATLAS_DEFAULT_LAYERS,
|
|
52
|
+
ShadowAtlas,
|
|
53
|
+
} from '../shadow-atlas';
|
|
47
54
|
import { getOrCreateSsaoBuffers } from '../ssao-buffers';
|
|
48
55
|
import { getSsaoParameters } from '../ssao-config';
|
|
49
56
|
import type { BindGroupCounts, RenderFrameState } from './frame-snapshot';
|
|
@@ -52,13 +59,43 @@ import {
|
|
|
52
59
|
computeViewMatrix,
|
|
53
60
|
isLitMaterialSnapshot,
|
|
54
61
|
warnMultiLightDirectional,
|
|
55
|
-
warnMultiLightPoint,
|
|
56
|
-
warnMultiLightSpot,
|
|
57
62
|
} from './helpers';
|
|
58
63
|
import { getOrCreateFromChain, MESH_SSBO_BYTES, MESH_UBO_FULL_ARRAY_BYTES } from './mesh-ssbo';
|
|
59
64
|
import type { PipelineState, RecordProfileRunner, RenderSystemInternals } from './render-context';
|
|
60
65
|
import { POINTS_LINES_VIEW_BYTES, VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
61
66
|
|
|
67
|
+
export interface StandardReflectionProbeBinding {
|
|
68
|
+
readonly probeIndex: number | undefined;
|
|
69
|
+
readonly useSkylight: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function resolveReflectionProbeBinding(
|
|
73
|
+
selection: ReflectionProbeSelectionResult,
|
|
74
|
+
table?: ReflectionProbeTable,
|
|
75
|
+
): StandardReflectionProbeBinding {
|
|
76
|
+
if (selection.kind !== 'probe') return { probeIndex: undefined, useSkylight: true };
|
|
77
|
+
const primitiveKey = `${selection.worldId}:${selection.entityKey}`;
|
|
78
|
+
const tableIndex = table?.cpuIndexByPrimitive(primitiveKey);
|
|
79
|
+
return { probeIndex: tableIndex ?? selection.entityKey, useSkylight: false };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function projectDeviceOwnedBindGroup<T>(
|
|
83
|
+
state:
|
|
84
|
+
| {
|
|
85
|
+
readonly device: RhiDevice;
|
|
86
|
+
readonly layout: BindGroupLayout;
|
|
87
|
+
readonly bindGroup: T;
|
|
88
|
+
}
|
|
89
|
+
| null
|
|
90
|
+
| undefined,
|
|
91
|
+
device: RhiDevice,
|
|
92
|
+
layout: BindGroupLayout | null,
|
|
93
|
+
): T | null {
|
|
94
|
+
return state !== null && state !== undefined && state.device === device && state.layout === layout
|
|
95
|
+
? state.bindGroup
|
|
96
|
+
: null;
|
|
97
|
+
}
|
|
98
|
+
|
|
62
99
|
function runHdrpClusterProfilePhase<T>(
|
|
63
100
|
runner: RecordProfileRunner | undefined,
|
|
64
101
|
phase: RenderHdrpClusterPhase,
|
|
@@ -67,6 +104,26 @@ function runHdrpClusterProfilePhase<T>(
|
|
|
67
104
|
return runner === undefined ? action() : runner(phase, action);
|
|
68
105
|
}
|
|
69
106
|
|
|
107
|
+
function mapClusterUploadFailure(
|
|
108
|
+
resource: string,
|
|
109
|
+
error: {
|
|
110
|
+
readonly code: string;
|
|
111
|
+
readonly expected: string;
|
|
112
|
+
readonly hint: string;
|
|
113
|
+
readonly detail?: unknown;
|
|
114
|
+
},
|
|
115
|
+
): RendererOperationError<'device-operation-failed'> {
|
|
116
|
+
return new RendererOperationError('device-operation-failed', {
|
|
117
|
+
operation: 'draw',
|
|
118
|
+
cause: {
|
|
119
|
+
code: error.code,
|
|
120
|
+
expected: `queue.writeBuffer succeeds for Standard Cluster ${resource}`,
|
|
121
|
+
hint: `repair the device write for Standard Cluster ${resource} and retry the frame`,
|
|
122
|
+
detail: error,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
70
127
|
/**
|
|
71
128
|
* feat-20260704 M3/w18: per-frame bind-group cache resolution, extracted
|
|
72
129
|
* verbatim from `recordFrame`. Walks the handle-identity WeakMap caches
|
|
@@ -87,8 +144,11 @@ export function buildPerFrameBindGroups(
|
|
|
87
144
|
graphTargets?: {
|
|
88
145
|
readonly directionalShadow?: TextureView | undefined;
|
|
89
146
|
readonly spotShadow?: TextureView | undefined;
|
|
147
|
+
readonly projector?: TextureView | undefined;
|
|
148
|
+
readonly projectorSampler?: import('@forgeax/engine-rhi').Sampler | undefined;
|
|
90
149
|
},
|
|
91
150
|
includeView = true,
|
|
151
|
+
standardLighting?: StandardTopologyInputValue,
|
|
92
152
|
): {
|
|
93
153
|
viewBindGroup: BindGroup | null;
|
|
94
154
|
meshBindGroup: BindGroup | null;
|
|
@@ -102,12 +162,31 @@ export function buildPerFrameBindGroups(
|
|
|
102
162
|
// Mesh (#2) chain = inner b0 buffer (meshStorageBuffer.buffer); variant 'mesh'.
|
|
103
163
|
let viewBindGroup: BindGroup | null = null;
|
|
104
164
|
let meshBindGroup: BindGroup | null = null;
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
165
|
+
// Standard clustered group(2) bind group. Non-null when the prepared
|
|
166
|
+
// topology selects the cluster transport and its persistent allocation
|
|
167
|
+
// succeeds; consumed by recordMainPass at `setBindGroup(2, ...)`.
|
|
108
168
|
let hdrpClusterBindGroup: BindGroup | null = null;
|
|
109
|
-
|
|
110
|
-
|
|
169
|
+
// The prepared Standard topology is the per-frame authority for whether
|
|
170
|
+
// group(2) carries Cluster storage. A missing declaration is the prewarm /
|
|
171
|
+
// non-Standard path and must not allocate or bind the Cluster bundle based
|
|
172
|
+
// on the install-time pipeline identity alone.
|
|
173
|
+
const clusteredLighting = standardLighting?.kind === 'clustered';
|
|
174
|
+
const membershipLayout = pipelineState.hdrpClusterMembershipBindGroupLayout;
|
|
175
|
+
const membership = frameState.hdrpClusterMembership;
|
|
176
|
+
const expectedHdrpBuffers =
|
|
177
|
+
hasValidated && clusteredLighting
|
|
178
|
+
? getOrCreateHdrpBuffers(internals, frameState.installedPipelineConfig?.clusterGrid)
|
|
179
|
+
: null;
|
|
180
|
+
const currentMembership =
|
|
181
|
+
expectedHdrpBuffers !== null &&
|
|
182
|
+
membership?.clusterGridBuffer === expectedHdrpBuffers.clusterGridBuffer
|
|
183
|
+
? projectDeviceOwnedBindGroup(membership, internals.device, membershipLayout)
|
|
184
|
+
: null;
|
|
185
|
+
if (membership !== null && currentMembership === null) {
|
|
186
|
+
frameState.hdrpClusterMembership = null;
|
|
187
|
+
}
|
|
188
|
+
const hdrpClusterMembershipBindGroup: BindGroup | null = clusteredLighting
|
|
189
|
+
? currentMembership
|
|
111
190
|
: null;
|
|
112
191
|
if (hasValidated) {
|
|
113
192
|
// Shadow atlas view comes from the typed graph target.
|
|
@@ -150,6 +229,9 @@ export function buildPerFrameBindGroups(
|
|
|
150
229
|
graphSpotShadowView !== undefined
|
|
151
230
|
? graphSpotShadowView
|
|
152
231
|
: pipelineState.shadowFallbackTextureView;
|
|
232
|
+
const projectorAvailable = pipelineState.projectorAvailable !== false;
|
|
233
|
+
const b11View = graphTargets?.projector ?? pipelineState.defaultWhiteTextureView;
|
|
234
|
+
const b12Sampler = graphTargets?.projectorSampler ?? pipelineState.defaultSampler;
|
|
153
235
|
viewBindGroup = getOrCreateFromChain(
|
|
154
236
|
frameState.viewBindGroupCache,
|
|
155
237
|
[
|
|
@@ -161,110 +243,118 @@ export function buildPerFrameBindGroups(
|
|
|
161
243
|
b5View,
|
|
162
244
|
pipelineState.shadowParamsBuffer,
|
|
163
245
|
b8View,
|
|
246
|
+
...(projectorAvailable ? [b11View, b12Sampler] : []),
|
|
164
247
|
pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
165
248
|
],
|
|
166
249
|
'view-main',
|
|
167
250
|
() => {
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
resource: {
|
|
175
|
-
kind: 'buffer',
|
|
176
|
-
value: { buffer: pipelineState.viewUniformBuffer, size: VIEW_UNIFORM_BYTES },
|
|
177
|
-
},
|
|
251
|
+
const entries: BindGroupEntry[] = [
|
|
252
|
+
{
|
|
253
|
+
binding: 0,
|
|
254
|
+
resource: {
|
|
255
|
+
kind: 'buffer',
|
|
256
|
+
value: { buffer: pipelineState.viewUniformBuffer, size: VIEW_UNIFORM_BYTES },
|
|
178
257
|
},
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
},
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
binding: 1,
|
|
261
|
+
resource: {
|
|
262
|
+
kind: 'buffer',
|
|
263
|
+
value: { buffer: pipelineState.pointLightsBuffer },
|
|
185
264
|
},
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
},
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
binding: 2,
|
|
268
|
+
resource: {
|
|
269
|
+
kind: 'buffer',
|
|
270
|
+
value: { buffer: pipelineState.spotLightsBuffer },
|
|
192
271
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
binding: 3,
|
|
275
|
+
resource: {
|
|
276
|
+
kind: 'textureView',
|
|
277
|
+
value: b3View,
|
|
199
278
|
},
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
binding: 4,
|
|
282
|
+
resource: {
|
|
283
|
+
kind: 'sampler',
|
|
284
|
+
value: shadowSampler,
|
|
206
285
|
},
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
286
|
+
},
|
|
287
|
+
// feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1:
|
|
288
|
+
// cube_array shadow atlas view (real ShadowAtlas when point
|
|
289
|
+
// shadows are active; else 1x1x6 fallback).
|
|
290
|
+
{
|
|
291
|
+
binding: 5,
|
|
292
|
+
resource: {
|
|
293
|
+
kind: 'textureView',
|
|
294
|
+
value: b5View,
|
|
216
295
|
},
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
},
|
|
296
|
+
},
|
|
297
|
+
// feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1:
|
|
298
|
+
// shadowParams UBO (`array<vec4<f32>, 4>` = 64 B). Lane N
|
|
299
|
+
// stores `(near, far, 1/(far-near), 0)` for the point light
|
|
300
|
+
// with shadowAtlasLayer === N. Updated per frame from
|
|
301
|
+
// `pointShadowSnapshots` below.
|
|
302
|
+
{
|
|
303
|
+
binding: 6,
|
|
304
|
+
resource: {
|
|
305
|
+
kind: 'buffer',
|
|
306
|
+
value: { buffer: pipelineState.shadowParamsBuffer },
|
|
228
307
|
},
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
},
|
|
308
|
+
},
|
|
309
|
+
// feat-20260613-csm-cascaded-shadow-maps M5 / w28 (rebased to
|
|
310
|
+
// binding 7 on 2026-06-13 to make room for point-shadow 5/6):
|
|
311
|
+
// forward shaders declare binding 7 in common.wgsl (shared
|
|
312
|
+
// view BGL) but never reference it; only shadow_caster.wgsl
|
|
313
|
+
// reads it. Host writes a stable singleton buffer so every
|
|
314
|
+
// forward bind group entry stays populated.
|
|
315
|
+
{
|
|
316
|
+
binding: 7,
|
|
317
|
+
resource: {
|
|
318
|
+
kind: 'buffer',
|
|
319
|
+
value: { buffer: pipelineState.shadowCasterCascadeBuffer },
|
|
241
320
|
},
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
321
|
+
},
|
|
322
|
+
// feat-20260625-spot-light-shadow-mapping M3 / w21 (D-5):
|
|
323
|
+
// spot shadow 2D atlas (real spotShadowDepth view when spot
|
|
324
|
+
// shadows run this frame, else the 1x1 depth fallback). Always-on.
|
|
325
|
+
{
|
|
326
|
+
binding: 8,
|
|
327
|
+
resource: {
|
|
328
|
+
kind: 'textureView',
|
|
329
|
+
value: b8View,
|
|
251
330
|
},
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
binding: 10,
|
|
334
|
+
resource: {
|
|
335
|
+
kind: 'buffer',
|
|
336
|
+
value: {
|
|
337
|
+
buffer: pipelineState.pointsLinesViewBuffer ?? pipelineState.viewUniformBuffer,
|
|
338
|
+
size: POINTS_LINES_VIEW_BYTES,
|
|
260
339
|
},
|
|
261
340
|
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
341
|
+
},
|
|
342
|
+
...(projectorAvailable
|
|
343
|
+
? [
|
|
344
|
+
{ binding: 11, resource: { kind: 'textureView' as const, value: b11View } },
|
|
345
|
+
{ binding: 12, resource: { kind: 'sampler' as const, value: b12Sampler } },
|
|
346
|
+
]
|
|
347
|
+
: []),
|
|
348
|
+
// feat-20260625-spot-light-shadow-mapping w25: the per-spot
|
|
349
|
+
// fragment-read lightViewProj matrices fold into the View UBO
|
|
350
|
+
// (binding 0, `view.spotLightViewProj`) — no standalone binding 9
|
|
351
|
+
// (WebGL2 fragment uniform-buffer budget). binding 10 is the
|
|
352
|
+
// dedicated vertex-only Points/Lines viewport UBO.
|
|
353
|
+
];
|
|
354
|
+
const viewBindGroupResult = internals.device.createBindGroup({
|
|
355
|
+
label: 'pbr-view-bg',
|
|
356
|
+
layout: pipelineState.viewBindGroupLayout,
|
|
357
|
+
entries,
|
|
268
358
|
});
|
|
269
359
|
if (!viewBindGroupResult.ok) throw viewBindGroupResult.error;
|
|
270
360
|
return viewBindGroupResult.value;
|
|
@@ -321,11 +411,8 @@ export function buildPerFrameBindGroups(
|
|
|
321
411
|
// alongside `meshBindGroupCache` keyed on the mesh SSBO + cluster
|
|
322
412
|
// buffer identities; cache invalidates on a buffer-grow event the same
|
|
323
413
|
// way the mesh path does (handle id rotation).
|
|
324
|
-
if (
|
|
325
|
-
const hdrpBuffers =
|
|
326
|
-
internals,
|
|
327
|
-
frameState.installedPipelineConfig?.clusterGrid,
|
|
328
|
-
);
|
|
414
|
+
if (clusteredLighting) {
|
|
415
|
+
const hdrpBuffers = expectedHdrpBuffers;
|
|
329
416
|
if (hdrpBuffers !== null) {
|
|
330
417
|
// D-3: buffer dimension uses the inner `.buffer` (same constraint
|
|
331
418
|
// as the mesh path) so a grow event rotates the chain key.
|
|
@@ -364,8 +451,7 @@ export function buildPerFrameBindGroups(
|
|
|
364
451
|
|
|
365
452
|
/**
|
|
366
453
|
* feat-20260704 M3/w18: per-frame lighting preparation, extracted verbatim from
|
|
367
|
-
* `recordFrame`. (1)
|
|
368
|
-
* over `LIGHT_ARRAY_MAX_SLOTS`; HDRP gated off — 256 SSBO lights). (2) pin the
|
|
454
|
+
* `recordFrame`. (1) pin the
|
|
369
455
|
* point + spot shadow snapshot lists onto frameState for the URP shadow caster
|
|
370
456
|
* pass closures + lazy-allocate the point-shadow cube_array atlas on first
|
|
371
457
|
* non-empty frame (AC-09 zero-shadow zero-alloc). (3) destructure ExtractedLights
|
|
@@ -378,12 +464,21 @@ export function prepareFrameLighting(
|
|
|
378
464
|
internals: RenderSystemInternals,
|
|
379
465
|
frameState: RenderFrameState,
|
|
380
466
|
lights: ExtractedLights,
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
467
|
+
camera: CameraSnapshot,
|
|
468
|
+
pipelineState?: Pick<
|
|
469
|
+
PipelineState,
|
|
470
|
+
'hdrpClusterMembershipPipeline' | 'hdrpClusterMembershipBindGroupLayout'
|
|
471
|
+
>,
|
|
472
|
+
): Result<
|
|
473
|
+
{
|
|
474
|
+
light: DirectionalLightSnapshot;
|
|
475
|
+
pointLights: ExtractedLights['point'];
|
|
476
|
+
spotLights: ExtractedLights['spot'];
|
|
477
|
+
totalLightCount: number;
|
|
478
|
+
standard: StandardTopologyInputValue;
|
|
479
|
+
},
|
|
480
|
+
RenderError
|
|
481
|
+
> {
|
|
387
482
|
if (lights.directionalCount > 1) {
|
|
388
483
|
warnMultiLightDirectional(frameState, lights.directionalCount);
|
|
389
484
|
}
|
|
@@ -394,21 +489,6 @@ export function prepareFrameLighting(
|
|
|
394
489
|
// defaults true on the single component, so the warn condition can never
|
|
395
490
|
// arise. The error class + RuntimeErrorCode member have been removed in M4 (m4-t2).
|
|
396
491
|
|
|
397
|
-
// feat-20260608-cluster-lighting M6 / w23 (F-4 fix): URP-only multi-light
|
|
398
|
-
// warn. HDRP supports 256 punctual lights via SSBO; the 4-slot first-slice
|
|
399
|
-
// cap is irrelevant under HDRP, so gate on `!isHdrpActive` to silence noise.
|
|
400
|
-
// Sibling tweak-20260608-rhi-hdr-renderable-caps-and-warn-once (#320)
|
|
401
|
-
// extracted the warn into warnMultiLight{Point,Spot} helpers (warn-once
|
|
402
|
-
// dedup); we keep the helpers and add the HDRP gate on top.
|
|
403
|
-
if (!frameState.isHdrpActive) {
|
|
404
|
-
if (lights.point.length > LIGHT_ARRAY_MAX_SLOTS) {
|
|
405
|
-
warnMultiLightPoint(frameState, lights.point.length);
|
|
406
|
-
}
|
|
407
|
-
if (lights.spot.length > LIGHT_ARRAY_MAX_SLOTS) {
|
|
408
|
-
warnMultiLightSpot(frameState, lights.spot.length);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
492
|
// feat-20260612-point-light-shadows-urp-hdrp M3 / T-M3-2 (plan-strategy §D-1):
|
|
413
493
|
// project lights.pointShadow onto frameState so the URP point shadow caster
|
|
414
494
|
// pass can read the snapshot list during graph execute. Lazy-allocate the
|
|
@@ -424,10 +504,10 @@ export function prepareFrameLighting(
|
|
|
424
504
|
if (lights.pointShadow.length > 0) {
|
|
425
505
|
if (frameState.pointShadowAtlas === null) {
|
|
426
506
|
const firstSnap = lights.pointShadow[0];
|
|
427
|
-
const faceSize = firstSnap?.mapSize ??
|
|
507
|
+
const faceSize = firstSnap?.mapSize ?? SHADOW_ATLAS_DEFAULT_FACE_SIZE;
|
|
428
508
|
frameState.pointShadowAtlas = new ShadowAtlas(internals.device, {
|
|
429
509
|
faceSize,
|
|
430
|
-
layers:
|
|
510
|
+
layers: SHADOW_ATLAS_DEFAULT_LAYERS,
|
|
431
511
|
});
|
|
432
512
|
}
|
|
433
513
|
try {
|
|
@@ -471,73 +551,77 @@ export function prepareFrameLighting(
|
|
|
471
551
|
const totalLightCount =
|
|
472
552
|
(directionalLight !== undefined ? 1 : 0) + pointLights.length + spotLights.length;
|
|
473
553
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
554
|
+
const clusterGrid = frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
|
|
555
|
+
const local = [
|
|
556
|
+
...pointLights.map((source) => ({
|
|
557
|
+
kind: 'point' as const,
|
|
558
|
+
shadowed: source.shadowAtlasLayer !== undefined && source.shadowAtlasLayer >= 0,
|
|
559
|
+
position: source.position,
|
|
560
|
+
range:
|
|
561
|
+
Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0
|
|
562
|
+
? Math.sqrt(1 / source.invRangeSquared)
|
|
563
|
+
: 1000,
|
|
564
|
+
source,
|
|
565
|
+
})),
|
|
566
|
+
...spotLights.map((source) => ({
|
|
567
|
+
kind: 'spot' as const,
|
|
568
|
+
shadowed:
|
|
569
|
+
source.castShadow && source.shadowAtlasTile >= 0 && source.lightViewProj !== undefined,
|
|
570
|
+
position: source.position,
|
|
571
|
+
range:
|
|
572
|
+
Number.isFinite(source.invRangeSquared) && source.invRangeSquared > 0
|
|
573
|
+
? Math.sqrt(1 / source.invRangeSquared)
|
|
574
|
+
: 1000,
|
|
575
|
+
source,
|
|
576
|
+
})),
|
|
577
|
+
];
|
|
578
|
+
const prepared = prepareStandardLighting({
|
|
579
|
+
directional: directionalLight,
|
|
580
|
+
local,
|
|
581
|
+
view: computeViewMatrix(camera),
|
|
582
|
+
projection: computeProjectionMatrix(camera),
|
|
583
|
+
near: camera.near,
|
|
584
|
+
far: camera.far,
|
|
585
|
+
grid: clusterGrid,
|
|
586
|
+
lightCount: internals.standardProfile?.lightCount ?? DEFAULT_STANDARD_PROFILE.lightCount,
|
|
587
|
+
renderPath: internals.standardProfile?.renderPath ?? DEFAULT_STANDARD_PROFILE.renderPath,
|
|
588
|
+
});
|
|
589
|
+
if (!prepared.ok) return prepared;
|
|
590
|
+
// Storage-capable Standard frames keep the same Cluster ABI even when the
|
|
591
|
+
// local-light corpus is empty. Directional-only scenes still need the
|
|
592
|
+
// unified mesh group(2) contract so their PBR draws do not select a second
|
|
593
|
+
// direct layout; the existing transport emits a zero-light payload and the
|
|
594
|
+
// graph remains structurally identical. The non-storage backend is the
|
|
595
|
+
// sole direct exception because no proven Cluster representation exists.
|
|
596
|
+
if (prepared.value.local.length === 0 && !internals.device.caps.storageBuffer) {
|
|
597
|
+
return ok({
|
|
598
|
+
light,
|
|
599
|
+
pointLights,
|
|
600
|
+
spotLights,
|
|
601
|
+
totalLightCount,
|
|
602
|
+
standard: { kind: 'no-local-lights', prepared: prepared.value },
|
|
603
|
+
});
|
|
517
604
|
}
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
:
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
605
|
+
const transport = selectStandardClusterTransport(
|
|
606
|
+
{
|
|
607
|
+
compute: internals.device.caps.compute,
|
|
608
|
+
storageBuffer: internals.device.caps.storageBuffer,
|
|
609
|
+
membershipPipelineReady:
|
|
610
|
+
pipelineState !== undefined &&
|
|
611
|
+
pipelineState.hdrpClusterMembershipPipeline !== null &&
|
|
612
|
+
pipelineState.hdrpClusterMembershipBindGroupLayout !== null,
|
|
613
|
+
},
|
|
614
|
+
prepared.value,
|
|
527
615
|
);
|
|
528
|
-
if (!
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
packed,
|
|
538
|
-
);
|
|
539
|
-
if (!writeRes.ok) throw writeRes.error;
|
|
540
|
-
}
|
|
616
|
+
if (!transport.ok) return transport;
|
|
617
|
+
|
|
618
|
+
return ok({
|
|
619
|
+
light,
|
|
620
|
+
pointLights,
|
|
621
|
+
spotLights,
|
|
622
|
+
totalLightCount,
|
|
623
|
+
standard: { kind: 'clustered', prepared: prepared.value, transport: transport.value },
|
|
624
|
+
});
|
|
541
625
|
}
|
|
542
626
|
|
|
543
627
|
/**
|
|
@@ -623,17 +707,9 @@ export function warnZeroLightStandard(
|
|
|
623
707
|
}
|
|
624
708
|
|
|
625
709
|
/**
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
* uploads
|
|
629
|
-
* pipeline is active. A zero-punctual frame explicitly clears the persistent
|
|
630
|
-
* cluster buffers so stale membership cannot leak across frames; direct and
|
|
631
|
-
* non-clustered paths remain untouched.
|
|
632
|
-
*
|
|
633
|
-
* feat-20260608-cluster-lighting M5 / w21 + M6 / w23 + r2 fix-up: fail-soft
|
|
634
|
-
* semantics preserved verbatim — index-list-overflow and light-budget-exceeded
|
|
635
|
-
* fire once per frame (via `frameState.hdrpOncePerFrameFired`) and continue
|
|
636
|
-
* rendering.
|
|
710
|
+
* Write the one prepared Standard Cluster payload into the persistent buffers.
|
|
711
|
+
* Membership has already been derived by `prepareFrameLighting`; this function
|
|
712
|
+
* only packs/uploads that immutable result and never re-runs the binner.
|
|
637
713
|
*
|
|
638
714
|
* @internal
|
|
639
715
|
*/
|
|
@@ -641,253 +717,107 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
641
717
|
internals: RenderSystemInternals,
|
|
642
718
|
frameState: RenderFrameState,
|
|
643
719
|
camera: CameraSnapshot,
|
|
644
|
-
|
|
645
|
-
|
|
720
|
+
prepared: PreparedStandardLighting,
|
|
721
|
+
transport: StandardClusterTransport,
|
|
646
722
|
profilePhase?: RecordProfileRunner,
|
|
647
|
-
|
|
723
|
+
gpuMembershipPipelineReady = false,
|
|
648
724
|
membershipBindGroupLayout: BindGroupLayout | null = null,
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
clusterSsaoConfig !== undefined && clusterSsaoConfig.enabled === true
|
|
660
|
-
? (clusterSsaoConfig.intensity ?? 1.0)
|
|
661
|
-
: 0;
|
|
662
|
-
const clusterUniformPayload = packClusterUniform(
|
|
663
|
-
grid,
|
|
664
|
-
camera.near,
|
|
665
|
-
camera.far,
|
|
666
|
-
clusterSsaoIntensity,
|
|
667
|
-
0,
|
|
668
|
-
lightCapacity,
|
|
725
|
+
projectorLightSlotIndex?: number,
|
|
726
|
+
): Result<void, RenderError> {
|
|
727
|
+
const configuredClusterGrid = prepared.layout.grid;
|
|
728
|
+
const gpuMembership = transport.producer === 'gpu';
|
|
729
|
+
if (gpuMembership && (!gpuMembershipPipelineReady || membershipBindGroupLayout === null)) {
|
|
730
|
+
return err(
|
|
731
|
+
new StandardClusterTransportUnavailableError(
|
|
732
|
+
prepared.local.length,
|
|
733
|
+
'the selected GPU Cluster producer requires a ready membership pipeline and bind-group layout',
|
|
734
|
+
),
|
|
669
735
|
);
|
|
670
|
-
const clusterUniformUpload = internals.device.queue.writeBuffer(
|
|
671
|
-
hdrpBuffers.clusterUniformBuffer,
|
|
672
|
-
0,
|
|
673
|
-
new Uint8Array(clusterUniformPayload),
|
|
674
|
-
);
|
|
675
|
-
if (!clusterUniformUpload.ok) internals.errorRegistry.fire(clusterUniformUpload.error);
|
|
676
|
-
const clusterGridClear = internals.device.queue.writeBuffer(
|
|
677
|
-
hdrpBuffers.clusterGridBuffer,
|
|
678
|
-
0,
|
|
679
|
-
new Uint8Array(hdrpBuffers.clusterGridBytes),
|
|
680
|
-
);
|
|
681
|
-
if (!clusterGridClear.ok) internals.errorRegistry.fire(clusterGridClear.error);
|
|
682
|
-
const lightIndexListClear = internals.device.queue.writeBuffer(
|
|
683
|
-
hdrpBuffers.lightIndexListBuffer,
|
|
684
|
-
0,
|
|
685
|
-
new Uint8Array(hdrpBuffers.lightIndexListBytes),
|
|
686
|
-
);
|
|
687
|
-
if (!lightIndexListClear.ok) internals.errorRegistry.fire(lightIndexListClear.error);
|
|
688
|
-
return;
|
|
689
736
|
}
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
if (pointLights.length >= HDRP_LIGHT_BUDGET) {
|
|
701
|
-
effectivePointLights = pointLights.slice(0, HDRP_LIGHT_BUDGET);
|
|
702
|
-
effectiveSpotLights = [];
|
|
703
|
-
} else {
|
|
704
|
-
effectivePointLights = pointLights;
|
|
705
|
-
effectiveSpotLights = spotLights.slice(0, HDRP_LIGHT_BUDGET - pointLights.length);
|
|
706
|
-
}
|
|
737
|
+
const hdrpBuffers = getOrCreateHdrpBuffers(internals, configuredClusterGrid);
|
|
738
|
+
if (hdrpBuffers === null) {
|
|
739
|
+
return err(
|
|
740
|
+
new StandardClusterTransportUnavailableError(
|
|
741
|
+
prepared.local.length,
|
|
742
|
+
'the selected Standard Cluster transport could not allocate its persistent buffers',
|
|
743
|
+
),
|
|
744
|
+
);
|
|
707
745
|
}
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
746
|
+
const existingMembership = projectDeviceOwnedBindGroup(
|
|
747
|
+
frameState.hdrpClusterMembership,
|
|
748
|
+
internals.device,
|
|
749
|
+
membershipBindGroupLayout,
|
|
750
|
+
);
|
|
751
|
+
const membershipMatchesBuffers =
|
|
752
|
+
existingMembership !== null &&
|
|
753
|
+
frameState.hdrpClusterMembership?.clusterGridBuffer === hdrpBuffers.clusterGridBuffer;
|
|
754
|
+
if (!membershipMatchesBuffers) frameState.hdrpClusterMembership = null;
|
|
712
755
|
if (
|
|
713
756
|
gpuMembership &&
|
|
714
757
|
membershipBindGroupLayout !== null &&
|
|
715
|
-
frameState.
|
|
758
|
+
frameState.hdrpClusterMembership === null
|
|
716
759
|
) {
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
760
|
+
const bindGroup = createHdrpClusterMembershipBindGroup(
|
|
761
|
+
internals,
|
|
762
|
+
hdrpBuffers,
|
|
763
|
+
membershipBindGroupLayout,
|
|
764
|
+
);
|
|
765
|
+
if (bindGroup === null) {
|
|
766
|
+
return err(
|
|
767
|
+
new StandardClusterTransportUnavailableError(
|
|
768
|
+
prepared.local.length,
|
|
769
|
+
'the selected GPU Cluster producer could not create its membership bind group',
|
|
770
|
+
),
|
|
723
771
|
);
|
|
724
772
|
}
|
|
725
|
-
|
|
773
|
+
frameState.hdrpClusterMembership = {
|
|
774
|
+
device: internals.device,
|
|
775
|
+
layout: membershipBindGroupLayout,
|
|
776
|
+
bindGroup,
|
|
777
|
+
clusterGridBuffer: hdrpBuffers.clusterGridBuffer,
|
|
778
|
+
};
|
|
726
779
|
}
|
|
727
780
|
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
runHdrpClusterProfilePhase(
|
|
735
|
-
profilePhase,
|
|
736
|
-
`record/scene-state/hdrp-cluster/binner/${phase}` as RenderHdrpClusterPhase,
|
|
737
|
-
action,
|
|
738
|
-
);
|
|
739
|
-
const {
|
|
740
|
-
hdrpLights,
|
|
741
|
-
clusterGrid,
|
|
742
|
-
gridX,
|
|
743
|
-
gridY,
|
|
744
|
-
gridZ,
|
|
745
|
-
clusterGridBuf,
|
|
746
|
-
lightIndexListBuf,
|
|
747
|
-
projMatrix,
|
|
748
|
-
viewMatrix,
|
|
749
|
-
} = runHdrpClusterProfilePhase(
|
|
750
|
-
profilePhase,
|
|
751
|
-
'record/scene-state/hdrp-cluster/binner/input-preparation',
|
|
752
|
-
() => {
|
|
753
|
-
const hdrpLights: Array<{ position: Vec3; range: number }> = [];
|
|
754
|
-
for (const pl of effectivePointLights) {
|
|
755
|
-
const range =
|
|
756
|
-
Number.isFinite(pl.invRangeSquared) && pl.invRangeSquared > 0
|
|
757
|
-
? Math.sqrt(1 / pl.invRangeSquared)
|
|
758
|
-
: 1000;
|
|
759
|
-
hdrpLights.push({ position: pl.position, range });
|
|
760
|
-
}
|
|
761
|
-
for (const sl of effectiveSpotLights) {
|
|
762
|
-
const range =
|
|
763
|
-
Number.isFinite(sl.invRangeSquared) && sl.invRangeSquared > 0
|
|
764
|
-
? Math.sqrt(1 / sl.invRangeSquared)
|
|
765
|
-
: 1000;
|
|
766
|
-
hdrpLights.push({ position: sl.position, range });
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
const clusterGrid =
|
|
770
|
-
frameState.installedPipelineConfig?.clusterGrid ?? DEFAULT_CLUSTER_GRID;
|
|
771
|
-
const gridX = clusterGrid.x;
|
|
772
|
-
const gridY = clusterGrid.y;
|
|
773
|
-
const gridZ = clusterGrid.z;
|
|
774
|
-
const clusterCount = gridX * gridY * gridZ;
|
|
775
|
-
|
|
776
|
-
const projMatrix = computeProjectionMatrix(camera);
|
|
777
|
-
const viewMatrix = computeViewMatrix(camera);
|
|
778
|
-
|
|
779
|
-
const clusterGridBuf =
|
|
780
|
-
frameState.hdrpClusterGridScratch === null ||
|
|
781
|
-
frameState.hdrpClusterGridScratch.length < clusterCount * 2
|
|
782
|
-
? new Uint32Array(clusterCount * 2)
|
|
783
|
-
: frameState.hdrpClusterGridScratch;
|
|
784
|
-
frameState.hdrpClusterGridScratch = clusterGridBuf;
|
|
785
|
-
const lightIndexListBuf =
|
|
786
|
-
frameState.hdrpLightIndexListScratch === null ||
|
|
787
|
-
frameState.hdrpLightIndexListScratch.length < LIGHT_INDEX_LIST_CAPACITY
|
|
788
|
-
? new Uint32Array(LIGHT_INDEX_LIST_CAPACITY)
|
|
789
|
-
: frameState.hdrpLightIndexListScratch;
|
|
790
|
-
frameState.hdrpLightIndexListScratch = lightIndexListBuf;
|
|
791
|
-
|
|
792
|
-
return {
|
|
793
|
-
hdrpLights,
|
|
794
|
-
clusterGrid,
|
|
795
|
-
gridX,
|
|
796
|
-
gridY,
|
|
797
|
-
gridZ,
|
|
798
|
-
clusterGridBuf,
|
|
799
|
-
lightIndexListBuf,
|
|
800
|
-
projMatrix,
|
|
801
|
-
viewMatrix,
|
|
802
|
-
};
|
|
803
|
-
},
|
|
804
|
-
);
|
|
805
|
-
|
|
806
|
-
const binResult = runHdrpClusterProfilePhase(
|
|
807
|
-
profilePhase,
|
|
808
|
-
'record/scene-state/hdrp-cluster/binner/bin-core',
|
|
809
|
-
() =>
|
|
810
|
-
bin(
|
|
811
|
-
hdrpLights,
|
|
812
|
-
viewMatrix,
|
|
813
|
-
projMatrix,
|
|
814
|
-
{ x: gridX, y: gridY, z: gridZ },
|
|
815
|
-
camera.near,
|
|
816
|
-
camera.far,
|
|
817
|
-
clusterGridBuf,
|
|
818
|
-
lightIndexListBuf,
|
|
819
|
-
LIGHT_INDEX_LIST_CAPACITY,
|
|
820
|
-
frameState.hdrpClusterBinScratch,
|
|
821
|
-
binnerProfile,
|
|
822
|
-
{ writeMembership: !gpuMembership },
|
|
823
|
-
),
|
|
824
|
-
);
|
|
825
|
-
|
|
826
|
-
if (!binResult.ok && !frameState.hdrpOncePerFrameFired.has('hdrp-index-list-overflow')) {
|
|
827
|
-
frameState.hdrpOncePerFrameFired.add('hdrp-index-list-overflow');
|
|
828
|
-
const detail = binResult.error.detail;
|
|
829
|
-
internals.errorRegistry.fire(
|
|
830
|
-
new HdrpIndexListOverflowError(detail.actual, detail.capacity),
|
|
831
|
-
);
|
|
832
|
-
}
|
|
781
|
+
const gridX = prepared.layout.grid.x;
|
|
782
|
+
const gridY = prepared.layout.grid.y;
|
|
783
|
+
const gridZ = prepared.layout.grid.z;
|
|
784
|
+
const clusterGridBuf = prepared.clusterGrid;
|
|
785
|
+
const lightIndexListBuf = prepared.lightIndexList;
|
|
786
|
+
const lightIndexCount = prepared.membershipEntryCount;
|
|
833
787
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
const envFalsify = (globalThis as { process?: { env?: Record<string, string | undefined> } })
|
|
840
|
-
.process?.env;
|
|
841
|
-
if (envFalsify?.FORGEAX_HDRP_FALSIFY_CLUSTER_GRID_ZERO) {
|
|
842
|
-
clusterGridBuf.fill(0);
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
return {
|
|
846
|
-
clusterGrid,
|
|
847
|
-
gridX,
|
|
848
|
-
gridY,
|
|
849
|
-
gridZ,
|
|
850
|
-
clusterGridBuf,
|
|
851
|
-
lightIndexListBuf,
|
|
852
|
-
lightIndexCount: binResult.ok ? binResult.value : 0,
|
|
853
|
-
};
|
|
854
|
-
});
|
|
788
|
+
// Falsify injection point retained for the real prepared payload. It edits
|
|
789
|
+
// only this frame's projection and never creates a second membership owner.
|
|
790
|
+
const envFalsify = (globalThis as { process?: { env?: Record<string, string | undefined> } })
|
|
791
|
+
.process?.env;
|
|
792
|
+
if (envFalsify?.FORGEAX_HDRP_FALSIFY_CLUSTER_GRID_ZERO) clusterGridBuf.fill(0);
|
|
855
793
|
|
|
856
794
|
const hdrpPayload = runHdrpClusterProfilePhase(
|
|
857
795
|
profilePhase,
|
|
858
796
|
'record/scene-state/hdrp-cluster/payload-packing',
|
|
859
797
|
() => {
|
|
860
|
-
const
|
|
861
|
-
if (hdrpBuffers === null) return null;
|
|
862
|
-
|
|
863
|
-
const lightCapacity = hdrpBuffers.storageBuffer ? 256 : HDRP_UNIFORM_LIGHT_CAPACITY;
|
|
864
|
-
const lightDataPayload = new Float32Array(lightCapacity * 16);
|
|
798
|
+
const lightDataPayload = new Float32Array(prepared.layout.lightDataSlotCount * 16);
|
|
865
799
|
let slotIdx = 0;
|
|
866
|
-
for (const
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
// extract's join pass when the entity carries PointLightShadow;
|
|
872
|
-
// sentinel -1 means no shadow (unshadowed evalPoint path).
|
|
873
|
-
const layer = pl.shadowAtlasLayer;
|
|
800
|
+
for (const local of prepared.local) {
|
|
801
|
+
const source = local.source;
|
|
802
|
+
if (source === undefined) return null;
|
|
803
|
+
const projectorSelected =
|
|
804
|
+
projectorLightSlotIndex !== undefined && projectorLightSlotIndex === slotIdx;
|
|
874
805
|
const packed =
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
const packed = packLightSlot(sl);
|
|
806
|
+
source.kind === 'point' &&
|
|
807
|
+
source.shadowAtlasLayer !== undefined &&
|
|
808
|
+
source.shadowAtlasLayer >= 0 &&
|
|
809
|
+
source.shadowNear !== undefined &&
|
|
810
|
+
source.shadowFar !== undefined
|
|
811
|
+
? packLightSlot(
|
|
812
|
+
source,
|
|
813
|
+
{
|
|
814
|
+
shadowAtlasLayer: source.shadowAtlasLayer,
|
|
815
|
+
near: source.shadowNear,
|
|
816
|
+
far: source.shadowFar,
|
|
817
|
+
},
|
|
818
|
+
projectorSelected,
|
|
819
|
+
)
|
|
820
|
+
: packLightSlot(source, undefined, projectorSelected);
|
|
891
821
|
lightDataPayload.set(packed, slotIdx * 16);
|
|
892
822
|
slotIdx += 1;
|
|
893
823
|
}
|
|
@@ -908,8 +838,7 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
908
838
|
camera.near,
|
|
909
839
|
camera.far,
|
|
910
840
|
clusterSsaoIntensity,
|
|
911
|
-
|
|
912
|
-
lightCapacity,
|
|
841
|
+
transport.admittedLightCount,
|
|
913
842
|
);
|
|
914
843
|
|
|
915
844
|
return {
|
|
@@ -920,39 +849,53 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
920
849
|
},
|
|
921
850
|
);
|
|
922
851
|
|
|
923
|
-
|
|
924
|
-
|
|
852
|
+
if (hdrpPayload === null) {
|
|
853
|
+
return err(
|
|
854
|
+
new StandardClusterTransportUnavailableError(
|
|
855
|
+
prepared.local.length,
|
|
856
|
+
'the prepared Standard Cluster payload could not be materialized',
|
|
857
|
+
),
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const bufferUpload = runHdrpClusterProfilePhase(
|
|
862
|
+
profilePhase,
|
|
863
|
+
'record/scene-state/hdrp-cluster/buffer-upload',
|
|
864
|
+
(): Result<void, RenderError> => {
|
|
925
865
|
const { hdrpBuffers, lightDataUploadPayload, clusterUniformPayload } = hdrpPayload;
|
|
926
866
|
const lightDataUpload = internals.device.queue.writeBuffer(
|
|
927
867
|
hdrpBuffers.lightDataBuffer,
|
|
928
868
|
0,
|
|
929
869
|
lightDataUploadPayload,
|
|
930
870
|
);
|
|
931
|
-
if (!lightDataUpload.ok)
|
|
871
|
+
if (!lightDataUpload.ok)
|
|
872
|
+
return err(mapClusterUploadFailure('light-data', lightDataUpload.error));
|
|
932
873
|
|
|
933
|
-
if (
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
hdrpBuffers.lightBoundsBuffer,
|
|
937
|
-
0,
|
|
938
|
-
frameState.hdrpClusterBinScratch.lightBounds.subarray(0, effectiveLightCount * 6),
|
|
939
|
-
);
|
|
940
|
-
if (!lightBoundsUpload.ok) internals.errorRegistry.fire(lightBoundsUpload.error);
|
|
941
|
-
}
|
|
942
|
-
const clusterGridUpload = internals.device.queue.writeBuffer(
|
|
943
|
-
hdrpBuffers.clusterGridBuffer,
|
|
874
|
+
if (gpuMembership) {
|
|
875
|
+
const lightBoundsUpload = internals.device.queue.writeBuffer(
|
|
876
|
+
hdrpBuffers.lightBoundsBuffer,
|
|
944
877
|
0,
|
|
945
|
-
|
|
878
|
+
prepared.lightBounds,
|
|
946
879
|
);
|
|
947
|
-
if (!
|
|
880
|
+
if (!lightBoundsUpload.ok)
|
|
881
|
+
return err(mapClusterUploadFailure('light-bounds', lightBoundsUpload.error));
|
|
882
|
+
}
|
|
883
|
+
const clusterGridUpload = internals.device.queue.writeBuffer(
|
|
884
|
+
hdrpBuffers.clusterGridBuffer,
|
|
885
|
+
0,
|
|
886
|
+
clusterGridBuf.subarray(0, gridX * gridY * gridZ * 2),
|
|
887
|
+
);
|
|
888
|
+
if (!clusterGridUpload.ok)
|
|
889
|
+
return err(mapClusterUploadFailure('cluster-grid', clusterGridUpload.error));
|
|
948
890
|
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
891
|
+
if (!gpuMembership && lightIndexCount > 0) {
|
|
892
|
+
const lightIndexListUpload = internals.device.queue.writeBuffer(
|
|
893
|
+
hdrpBuffers.lightIndexListBuffer,
|
|
894
|
+
0,
|
|
895
|
+
lightIndexListBuf.subarray(0, lightIndexCount),
|
|
896
|
+
);
|
|
897
|
+
if (!lightIndexListUpload.ok) {
|
|
898
|
+
return err(mapClusterUploadFailure('light-index-list', lightIndexListUpload.error));
|
|
956
899
|
}
|
|
957
900
|
}
|
|
958
901
|
|
|
@@ -961,56 +904,67 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
961
904
|
0,
|
|
962
905
|
clusterUniformPayload,
|
|
963
906
|
);
|
|
964
|
-
if (!clusterUniformUpload.ok)
|
|
965
|
-
|
|
907
|
+
if (!clusterUniformUpload.ok) {
|
|
908
|
+
return err(mapClusterUploadFailure('cluster-uniform', clusterUniformUpload.error));
|
|
909
|
+
}
|
|
910
|
+
return ok(undefined);
|
|
911
|
+
},
|
|
912
|
+
);
|
|
913
|
+
if (!bufferUpload.ok) return bufferUpload;
|
|
966
914
|
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
915
|
+
// ── feat-20260612-hdrp-ssao M1 / w6 + M7 / w33 ───────────────
|
|
916
|
+
// Per-frame SSAO uniform write (plan-strategy D-1 + D-C):
|
|
917
|
+
// view + projection + inverseProjection at offsets 0/64/128 +
|
|
918
|
+
// intensityPad (vec4 — x=intensity, y=radius, z=bias) at offset 192;
|
|
919
|
+
// total 256 B (matches host SSAO_UNIFORM_BYTES + WGSL struct).
|
|
920
|
+
// Single writeBuffer covers all four fields so one queue entry
|
|
921
|
+
// updates the entire UBO.
|
|
922
|
+
// Separate from View UBO (592 B invariant); does not affect
|
|
923
|
+
// material PSO bytecode.
|
|
924
|
+
//
|
|
925
|
+
// Writes when HDRP is active; config.ssao?.enabled guard comes in
|
|
926
|
+
// M4 / w19 after the config.ssao type narrowing is added.
|
|
927
|
+
const ssaoUpload = runHdrpClusterProfilePhase(
|
|
928
|
+
profilePhase,
|
|
929
|
+
'record/scene-state/hdrp-cluster/buffer-upload',
|
|
930
|
+
(): Result<void, RenderError> => {
|
|
980
931
|
const ssaoBufs = getOrCreateSsaoBuffers(internals);
|
|
981
|
-
if (ssaoBufs
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
932
|
+
if (ssaoBufs === null) return ok(undefined);
|
|
933
|
+
const sProj = computeProjectionMatrix(camera);
|
|
934
|
+
const sView = computeViewMatrix(camera);
|
|
935
|
+
// inverseProjection = inverse(projection): NDC -> view-space.
|
|
936
|
+
const invProjOnly = mat4.create();
|
|
937
|
+
mat4.invert(invProjOnly, sProj);
|
|
987
938
|
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
939
|
+
// Float32Array of 64 (256 B): 3 mat4 (48) + intensityPad vec4 (4)
|
|
940
|
+
// + 12 trailing padding floats. We only fill the declared region.
|
|
941
|
+
const ssaoUniformPayload = new Float32Array(64);
|
|
942
|
+
ssaoUniformPayload.set(sView, 0);
|
|
943
|
+
ssaoUniformPayload.set(sProj, 16);
|
|
944
|
+
ssaoUniformPayload.set(invProjOnly, 32);
|
|
945
|
+
// intensityPad carries the same resolved values as the graph SSAO
|
|
946
|
+
// calc pass; this early write keeps the shared UBO valid before the
|
|
947
|
+
// typed fullscreen pass executes.
|
|
948
|
+
const ssaoConfig = frameState.installedPipelineConfig?.ssao;
|
|
949
|
+
const parameters = getSsaoParameters(
|
|
950
|
+
ssaoConfig !== undefined && ssaoConfig.enabled === true ? ssaoConfig : undefined,
|
|
951
|
+
);
|
|
952
|
+
ssaoUniformPayload[48] = parameters.intensity;
|
|
953
|
+
ssaoUniformPayload[49] = parameters.radius;
|
|
954
|
+
ssaoUniformPayload[50] = parameters.bias;
|
|
1004
955
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
956
|
+
const ssaoUniformRes = internals.device.queue.writeBuffer(
|
|
957
|
+
ssaoBufs.uniformBuffer,
|
|
958
|
+
0,
|
|
959
|
+
ssaoUniformPayload,
|
|
960
|
+
);
|
|
961
|
+
if (!ssaoUniformRes.ok)
|
|
962
|
+
return err(mapClusterUploadFailure('ssao-uniform', ssaoUniformRes.error));
|
|
963
|
+
return ok(undefined);
|
|
964
|
+
},
|
|
965
|
+
);
|
|
966
|
+
if (!ssaoUpload.ok) return ssaoUpload;
|
|
967
|
+
return ok(undefined);
|
|
1014
968
|
}
|
|
1015
969
|
|
|
1016
970
|
/**
|