@forgeax/engine-render 0.1.20 → 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 +21 -18
- 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-4XINDLAY.mjs → chunk-5YADUCA7.mjs} +1552 -1361
- package/dist/chunk-5YADUCA7.mjs.map +1 -0
- package/dist/{chunk-TBQJOK2L.mjs → chunk-6OPXDQ2E.mjs} +364 -51
- package/dist/chunk-6OPXDQ2E.mjs.map +1 -0
- package/dist/{chunk-M2MP4ZOU.mjs → chunk-BXBQQKFQ.mjs} +3 -9
- package/dist/chunk-BXBQQKFQ.mjs.map +1 -0
- package/dist/{chunk-CDTWTHZR.mjs → chunk-FS2KNUQH.mjs} +548 -6
- 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/construct-renderer.mjs +5826 -1003
- package/dist/construct-renderer.mjs.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 +141 -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/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 +28 -3
- 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 +10 -1
- 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 +14 -4
- 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-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 +27 -32
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +28 -14
- 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 +10 -4
- 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 +8 -3
- package/dist/record/gpu-timing.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +22 -1
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +2 -0
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +43 -2
- 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 +72 -4
- 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 +49 -7
- 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 +42 -2
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +58 -17
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +17 -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/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/index.mjs +3 -3
- package/dist/typed-render-graph-primitives.d.ts +1 -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 +2 -2
- 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 +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__/material-observation.integration.test.ts +101 -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__/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 +18 -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 +48 -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 +132 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +11 -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 -38
- 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/assembly/device-feature-admission.ts +73 -0
- package/src/assembly/directional-shadow-inspection.ts +122 -0
- package/src/assembly/factory.ts +877 -286
- 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 +81 -7
- package/src/components/reflection-probe.ts +49 -0
- package/src/environment/frame.ts +6 -24
- package/src/environment/lifecycle.ts +12 -0
- package/src/errors/render.ts +286 -142
- 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/hdrp-buffers.ts +165 -59
- package/src/ibl/IblPipelineCache.ts +11 -0
- package/src/ibl/kernel-cache.ts +88 -0
- package/src/index.ts +148 -0
- package/src/inspection-types.ts +56 -0
- package/src/instances-derived-bounds.ts +171 -0
- package/src/light-buffer-layout.ts +20 -5
- package/src/mesh-material-bindings.ts +132 -0
- package/src/pbr-pipeline.ts +106 -17
- package/src/pipeline/standard-forward-lane.ts +46 -16
- 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 +122 -193
- package/src/pipeline/standard-post.ts +26 -7
- package/src/pipeline/standard-profile.ts +17 -47
- 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 +367 -427
- package/src/record/frame-snapshot.ts +35 -27
- package/src/record/frame-targets.ts +17 -0
- package/src/record/frame.ts +261 -74
- 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 +21 -12
- package/src/record/main-pass-geometry.ts +120 -80
- package/src/record/main-pass-material.ts +95 -9
- package/src/record/main-pass-sprite-draws.ts +54 -25
- package/src/record/main-pass.ts +71 -15
- package/src/record/render-context.ts +72 -0
- package/src/record/shadow-pass.ts +17 -28
- package/src/record/skybox-post-pass.ts +20 -4
- package/src/record/typed-frame-graph.ts +893 -44
- 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 +76 -5
- package/src/render-graph-primitives.ts +9 -4
- package/src/render-pipeline.ts +117 -2
- package/src/render-system-extract.ts +557 -100
- package/src/render-system.ts +1095 -66
- package/src/renderer.ts +222 -0
- package/src/scene/render-scene.ts +115 -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/transmission/__tests__/standard-transmission.dawn.test.ts +59 -20
- package/src/typed-render-graph-primitives.ts +5 -3
- package/src/typed-shadow-passes.ts +46 -40
- package/src/volume/passes.ts +15 -9
- package/dist/chunk-4XINDLAY.mjs.map +0 -1
- package/dist/chunk-CDTWTHZR.mjs.map +0 -1
- package/dist/chunk-M2MP4ZOU.mjs.map +0 -1
- package/dist/chunk-TBQJOK2L.mjs.map +0 -1
|
@@ -2,40 +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
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 { packLightSlot } 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 type { ReflectionProbeTable } from '../reflection/gpu-table';
|
|
40
|
+
import type { ReflectionProbeSelectionResult } from '../reflection/projection';
|
|
39
41
|
import type { CameraSnapshot, RenderHdrpClusterPhase } from '../render-contract';
|
|
40
42
|
import type {
|
|
41
43
|
DirectionalLightSnapshot,
|
|
@@ -44,7 +46,11 @@ import type {
|
|
|
44
46
|
SkyboxSnapshot,
|
|
45
47
|
SkylightSnapshot,
|
|
46
48
|
} from '../render-system-extract';
|
|
47
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
SHADOW_ATLAS_DEFAULT_FACE_SIZE,
|
|
51
|
+
SHADOW_ATLAS_DEFAULT_LAYERS,
|
|
52
|
+
ShadowAtlas,
|
|
53
|
+
} from '../shadow-atlas';
|
|
48
54
|
import { getOrCreateSsaoBuffers } from '../ssao-buffers';
|
|
49
55
|
import { getSsaoParameters } from '../ssao-config';
|
|
50
56
|
import type { BindGroupCounts, RenderFrameState } from './frame-snapshot';
|
|
@@ -53,13 +59,43 @@ import {
|
|
|
53
59
|
computeViewMatrix,
|
|
54
60
|
isLitMaterialSnapshot,
|
|
55
61
|
warnMultiLightDirectional,
|
|
56
|
-
warnMultiLightPoint,
|
|
57
|
-
warnMultiLightSpot,
|
|
58
62
|
} from './helpers';
|
|
59
63
|
import { getOrCreateFromChain, MESH_SSBO_BYTES, MESH_UBO_FULL_ARRAY_BYTES } from './mesh-ssbo';
|
|
60
64
|
import type { PipelineState, RecordProfileRunner, RenderSystemInternals } from './render-context';
|
|
61
65
|
import { POINTS_LINES_VIEW_BYTES, VIEW_UNIFORM_BYTES } from './view-ubo';
|
|
62
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
|
+
|
|
63
99
|
function runHdrpClusterProfilePhase<T>(
|
|
64
100
|
runner: RecordProfileRunner | undefined,
|
|
65
101
|
phase: RenderHdrpClusterPhase,
|
|
@@ -68,6 +104,26 @@ function runHdrpClusterProfilePhase<T>(
|
|
|
68
104
|
return runner === undefined ? action() : runner(phase, action);
|
|
69
105
|
}
|
|
70
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
|
+
|
|
71
127
|
/**
|
|
72
128
|
* feat-20260704 M3/w18: per-frame bind-group cache resolution, extracted
|
|
73
129
|
* verbatim from `recordFrame`. Walks the handle-identity WeakMap caches
|
|
@@ -92,6 +148,7 @@ export function buildPerFrameBindGroups(
|
|
|
92
148
|
readonly projectorSampler?: import('@forgeax/engine-rhi').Sampler | undefined;
|
|
93
149
|
},
|
|
94
150
|
includeView = true,
|
|
151
|
+
standardLighting?: StandardTopologyInputValue,
|
|
95
152
|
): {
|
|
96
153
|
viewBindGroup: BindGroup | null;
|
|
97
154
|
meshBindGroup: BindGroup | null;
|
|
@@ -105,12 +162,31 @@ export function buildPerFrameBindGroups(
|
|
|
105
162
|
// Mesh (#2) chain = inner b0 buffer (meshStorageBuffer.buffer); variant 'mesh'.
|
|
106
163
|
let viewBindGroup: BindGroup | null = null;
|
|
107
164
|
let meshBindGroup: BindGroup | null = null;
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
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, ...)`.
|
|
111
168
|
let hdrpClusterBindGroup: BindGroup | null = null;
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
114
190
|
: null;
|
|
115
191
|
if (hasValidated) {
|
|
116
192
|
// Shadow atlas view comes from the typed graph target.
|
|
@@ -335,11 +411,8 @@ export function buildPerFrameBindGroups(
|
|
|
335
411
|
// alongside `meshBindGroupCache` keyed on the mesh SSBO + cluster
|
|
336
412
|
// buffer identities; cache invalidates on a buffer-grow event the same
|
|
337
413
|
// way the mesh path does (handle id rotation).
|
|
338
|
-
if (
|
|
339
|
-
const hdrpBuffers =
|
|
340
|
-
internals,
|
|
341
|
-
frameState.installedPipelineConfig?.clusterGrid,
|
|
342
|
-
);
|
|
414
|
+
if (clusteredLighting) {
|
|
415
|
+
const hdrpBuffers = expectedHdrpBuffers;
|
|
343
416
|
if (hdrpBuffers !== null) {
|
|
344
417
|
// D-3: buffer dimension uses the inner `.buffer` (same constraint
|
|
345
418
|
// as the mesh path) so a grow event rotates the chain key.
|
|
@@ -378,8 +451,7 @@ export function buildPerFrameBindGroups(
|
|
|
378
451
|
|
|
379
452
|
/**
|
|
380
453
|
* feat-20260704 M3/w18: per-frame lighting preparation, extracted verbatim from
|
|
381
|
-
* `recordFrame`. (1)
|
|
382
|
-
* over `LIGHT_ARRAY_MAX_SLOTS`; HDRP gated off — 256 SSBO lights). (2) pin the
|
|
454
|
+
* `recordFrame`. (1) pin the
|
|
383
455
|
* point + spot shadow snapshot lists onto frameState for the URP shadow caster
|
|
384
456
|
* pass closures + lazy-allocate the point-shadow cube_array atlas on first
|
|
385
457
|
* non-empty frame (AC-09 zero-shadow zero-alloc). (3) destructure ExtractedLights
|
|
@@ -392,12 +464,21 @@ export function prepareFrameLighting(
|
|
|
392
464
|
internals: RenderSystemInternals,
|
|
393
465
|
frameState: RenderFrameState,
|
|
394
466
|
lights: ExtractedLights,
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
+
> {
|
|
401
482
|
if (lights.directionalCount > 1) {
|
|
402
483
|
warnMultiLightDirectional(frameState, lights.directionalCount);
|
|
403
484
|
}
|
|
@@ -408,21 +489,6 @@ export function prepareFrameLighting(
|
|
|
408
489
|
// defaults true on the single component, so the warn condition can never
|
|
409
490
|
// arise. The error class + RuntimeErrorCode member have been removed in M4 (m4-t2).
|
|
410
491
|
|
|
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
492
|
// feat-20260612-point-light-shadows-urp-hdrp M3 / T-M3-2 (plan-strategy §D-1):
|
|
427
493
|
// project lights.pointShadow onto frameState so the URP point shadow caster
|
|
428
494
|
// pass can read the snapshot list during graph execute. Lazy-allocate the
|
|
@@ -438,10 +504,10 @@ export function prepareFrameLighting(
|
|
|
438
504
|
if (lights.pointShadow.length > 0) {
|
|
439
505
|
if (frameState.pointShadowAtlas === null) {
|
|
440
506
|
const firstSnap = lights.pointShadow[0];
|
|
441
|
-
const faceSize = firstSnap?.mapSize ??
|
|
507
|
+
const faceSize = firstSnap?.mapSize ?? SHADOW_ATLAS_DEFAULT_FACE_SIZE;
|
|
442
508
|
frameState.pointShadowAtlas = new ShadowAtlas(internals.device, {
|
|
443
509
|
faceSize,
|
|
444
|
-
layers:
|
|
510
|
+
layers: SHADOW_ATLAS_DEFAULT_LAYERS,
|
|
445
511
|
});
|
|
446
512
|
}
|
|
447
513
|
try {
|
|
@@ -485,73 +551,77 @@ export function prepareFrameLighting(
|
|
|
485
551
|
const totalLightCount =
|
|
486
552
|
(directionalLight !== undefined ? 1 : 0) + pointLights.length + spotLights.length;
|
|
487
553
|
|
|
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
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
+
});
|
|
531
604
|
}
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
:
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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,
|
|
541
615
|
);
|
|
542
|
-
if (!
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
packed,
|
|
552
|
-
);
|
|
553
|
-
if (!writeRes.ok) throw writeRes.error;
|
|
554
|
-
}
|
|
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
|
+
});
|
|
555
625
|
}
|
|
556
626
|
|
|
557
627
|
/**
|
|
@@ -637,17 +707,9 @@ export function warnZeroLightStandard(
|
|
|
637
707
|
}
|
|
638
708
|
|
|
639
709
|
/**
|
|
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.
|
|
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.
|
|
651
713
|
*
|
|
652
714
|
* @internal
|
|
653
715
|
*/
|
|
@@ -655,253 +717,107 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
655
717
|
internals: RenderSystemInternals,
|
|
656
718
|
frameState: RenderFrameState,
|
|
657
719
|
camera: CameraSnapshot,
|
|
658
|
-
|
|
659
|
-
|
|
720
|
+
prepared: PreparedStandardLighting,
|
|
721
|
+
transport: StandardClusterTransport,
|
|
660
722
|
profilePhase?: RecordProfileRunner,
|
|
661
|
-
|
|
723
|
+
gpuMembershipPipelineReady = false,
|
|
662
724
|
membershipBindGroupLayout: BindGroupLayout | null = null,
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
clusterSsaoConfig !== undefined && clusterSsaoConfig.enabled === true
|
|
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,
|
|
683
|
-
);
|
|
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),
|
|
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
|
+
),
|
|
700
735
|
);
|
|
701
|
-
if (!lightIndexListClear.ok) internals.errorRegistry.fire(lightIndexListClear.error);
|
|
702
|
-
return;
|
|
703
736
|
}
|
|
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
|
-
}
|
|
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
|
+
);
|
|
721
745
|
}
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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;
|
|
726
755
|
if (
|
|
727
756
|
gpuMembership &&
|
|
728
757
|
membershipBindGroupLayout !== null &&
|
|
729
|
-
frameState.
|
|
758
|
+
frameState.hdrpClusterMembership === null
|
|
730
759
|
) {
|
|
731
|
-
const
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
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
|
+
),
|
|
737
771
|
);
|
|
738
772
|
}
|
|
739
|
-
|
|
773
|
+
frameState.hdrpClusterMembership = {
|
|
774
|
+
device: internals.device,
|
|
775
|
+
layout: membershipBindGroupLayout,
|
|
776
|
+
bindGroup,
|
|
777
|
+
clusterGridBuffer: hdrpBuffers.clusterGridBuffer,
|
|
778
|
+
};
|
|
740
779
|
}
|
|
741
780
|
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
runHdrpClusterProfilePhase(
|
|
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
|
-
);
|
|
819
|
-
|
|
820
|
-
const binResult = runHdrpClusterProfilePhase(
|
|
821
|
-
profilePhase,
|
|
822
|
-
'record/scene-state/hdrp-cluster/binner/bin-core',
|
|
823
|
-
() =>
|
|
824
|
-
bin(
|
|
825
|
-
hdrpLights,
|
|
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
|
-
);
|
|
839
|
-
|
|
840
|
-
if (!binResult.ok && !frameState.hdrpOncePerFrameFired.has('hdrp-index-list-overflow')) {
|
|
841
|
-
frameState.hdrpOncePerFrameFired.add('hdrp-index-list-overflow');
|
|
842
|
-
const detail = binResult.error.detail;
|
|
843
|
-
internals.errorRegistry.fire(
|
|
844
|
-
new HdrpIndexListOverflowError(detail.actual, detail.capacity),
|
|
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
|
-
}
|
|
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;
|
|
858
787
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
clusterGridBuf,
|
|
865
|
-
lightIndexListBuf,
|
|
866
|
-
lightIndexCount: binResult.ok ? binResult.value : 0,
|
|
867
|
-
};
|
|
868
|
-
});
|
|
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);
|
|
869
793
|
|
|
870
794
|
const hdrpPayload = runHdrpClusterProfilePhase(
|
|
871
795
|
profilePhase,
|
|
872
796
|
'record/scene-state/hdrp-cluster/payload-packing',
|
|
873
797
|
() => {
|
|
874
|
-
const
|
|
875
|
-
if (hdrpBuffers === null) return null;
|
|
876
|
-
|
|
877
|
-
const lightCapacity = hdrpBuffers.storageBuffer ? 256 : HDRP_UNIFORM_LIGHT_CAPACITY;
|
|
878
|
-
const lightDataPayload = new Float32Array(lightCapacity * 16);
|
|
798
|
+
const lightDataPayload = new Float32Array(prepared.layout.lightDataSlotCount * 16);
|
|
879
799
|
let slotIdx = 0;
|
|
880
|
-
for (const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
// extract's join pass when the entity carries PointLightShadow;
|
|
886
|
-
// sentinel -1 means no shadow (unshadowed evalPoint path).
|
|
887
|
-
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;
|
|
888
805
|
const packed =
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
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);
|
|
905
821
|
lightDataPayload.set(packed, slotIdx * 16);
|
|
906
822
|
slotIdx += 1;
|
|
907
823
|
}
|
|
@@ -922,8 +838,7 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
922
838
|
camera.near,
|
|
923
839
|
camera.far,
|
|
924
840
|
clusterSsaoIntensity,
|
|
925
|
-
|
|
926
|
-
lightCapacity,
|
|
841
|
+
transport.admittedLightCount,
|
|
927
842
|
);
|
|
928
843
|
|
|
929
844
|
return {
|
|
@@ -934,39 +849,53 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
934
849
|
},
|
|
935
850
|
);
|
|
936
851
|
|
|
937
|
-
|
|
938
|
-
|
|
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> => {
|
|
939
865
|
const { hdrpBuffers, lightDataUploadPayload, clusterUniformPayload } = hdrpPayload;
|
|
940
866
|
const lightDataUpload = internals.device.queue.writeBuffer(
|
|
941
867
|
hdrpBuffers.lightDataBuffer,
|
|
942
868
|
0,
|
|
943
869
|
lightDataUploadPayload,
|
|
944
870
|
);
|
|
945
|
-
if (!lightDataUpload.ok)
|
|
871
|
+
if (!lightDataUpload.ok)
|
|
872
|
+
return err(mapClusterUploadFailure('light-data', lightDataUpload.error));
|
|
946
873
|
|
|
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,
|
|
874
|
+
if (gpuMembership) {
|
|
875
|
+
const lightBoundsUpload = internals.device.queue.writeBuffer(
|
|
876
|
+
hdrpBuffers.lightBoundsBuffer,
|
|
958
877
|
0,
|
|
959
|
-
|
|
878
|
+
prepared.lightBounds,
|
|
960
879
|
);
|
|
961
|
-
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));
|
|
962
890
|
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
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));
|
|
970
899
|
}
|
|
971
900
|
}
|
|
972
901
|
|
|
@@ -975,56 +904,67 @@ export function writeHdrpClusterAndSsaoBuffers(
|
|
|
975
904
|
0,
|
|
976
905
|
clusterUniformPayload,
|
|
977
906
|
);
|
|
978
|
-
if (!clusterUniformUpload.ok)
|
|
979
|
-
|
|
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;
|
|
980
914
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
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> => {
|
|
994
931
|
const ssaoBufs = getOrCreateSsaoBuffers(internals);
|
|
995
|
-
if (ssaoBufs
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
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);
|
|
1001
938
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
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;
|
|
1018
955
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
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);
|
|
1028
968
|
}
|
|
1029
969
|
|
|
1030
970
|
/**
|