@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
package/src/render-system.ts
CHANGED
|
@@ -21,10 +21,21 @@ import type { World } from '@forgeax/engine-ecs';
|
|
|
21
21
|
import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
22
22
|
import type { RecorderSession } from '@forgeax/engine-profiler';
|
|
23
23
|
import type { CompiledRenderGraphInfo } from '@forgeax/engine-render-graph';
|
|
24
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
BindGroup,
|
|
26
|
+
BindGroupEntry,
|
|
27
|
+
BindGroupLayout,
|
|
28
|
+
Buffer,
|
|
29
|
+
RenderPipeline,
|
|
30
|
+
Sampler,
|
|
31
|
+
Texture,
|
|
32
|
+
TextureFormat,
|
|
33
|
+
TextureView,
|
|
34
|
+
} from '@forgeax/engine-rhi';
|
|
25
35
|
import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
|
|
26
36
|
import {
|
|
27
37
|
derive,
|
|
38
|
+
deriveTextureLayout,
|
|
28
39
|
type Handle,
|
|
29
40
|
type MaterialAsset,
|
|
30
41
|
type MaterialTextureValue,
|
|
@@ -32,8 +43,14 @@ import {
|
|
|
32
43
|
type RenderPipelineAsset,
|
|
33
44
|
RenderQueue,
|
|
34
45
|
type SamplerAsset,
|
|
46
|
+
type TextureAsset,
|
|
35
47
|
toShared,
|
|
36
48
|
} from '@forgeax/engine-types';
|
|
49
|
+
import {
|
|
50
|
+
type DirectionalShadowInspectionSource,
|
|
51
|
+
directionalShadowProfileFromQuality,
|
|
52
|
+
projectDirectionalShadowInspectionSource,
|
|
53
|
+
} from './assembly/directional-shadow-inspection';
|
|
37
54
|
import { createClusterBinScratch } from './cluster-binner';
|
|
38
55
|
import { EnvironmentLifecycle } from './environment/lifecycle';
|
|
39
56
|
import {
|
|
@@ -57,11 +74,17 @@ import {
|
|
|
57
74
|
DEPTH_MIN_PARAMS_BYTE_SIZE,
|
|
58
75
|
entryHasDepthRead,
|
|
59
76
|
type PostProcessShaderEntry,
|
|
77
|
+
postProcessShaderEntrySignature,
|
|
78
|
+
postProcessShaderModuleLabel,
|
|
79
|
+
postProcessShaderPipelineLabel,
|
|
60
80
|
} from './fullscreen-post-process-pass';
|
|
61
81
|
import { GpuDrivenProduction } from './gpu-driven/production-raster';
|
|
62
82
|
import type {
|
|
63
83
|
BloomInspection,
|
|
84
|
+
DirectionalShadowInspection,
|
|
64
85
|
MotionBlurInspection,
|
|
86
|
+
ReflectionProbeInspection,
|
|
87
|
+
ReflectionProbeSelectionInspection,
|
|
65
88
|
RenderSceneInspection,
|
|
66
89
|
TemporalTargetInspection,
|
|
67
90
|
TransmissionInspection,
|
|
@@ -85,29 +108,54 @@ import { inspectVolumetricFog } from './volume/inspection';
|
|
|
85
108
|
export type { RenderSceneInspection } from './inspection-types';
|
|
86
109
|
|
|
87
110
|
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
111
|
+
import { mat4, vec3 } from '@forgeax/engine-math';
|
|
112
|
+
import { buildCubeCameraFaceViews } from './capture/cube-views';
|
|
113
|
+
import { createCubeCaptureScheduler } from './capture/scheduler';
|
|
114
|
+
import {
|
|
115
|
+
createFaceUniformsBuffer,
|
|
116
|
+
createPrefilterUniformsBuffer,
|
|
117
|
+
writeAllFaceUniforms,
|
|
118
|
+
writeAllPrefilterUniforms,
|
|
119
|
+
} from './device/gpu-residency';
|
|
88
120
|
import type { EnvironmentInspection } from './environment/inspection';
|
|
121
|
+
import {
|
|
122
|
+
GPU_TEXTURE_USAGE_COPY_SRC,
|
|
123
|
+
GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING,
|
|
124
|
+
} from './gpu-texture-usage';
|
|
89
125
|
import {
|
|
90
126
|
GPU_BUFFER_USAGE_COPY_DST,
|
|
91
127
|
GPU_BUFFER_USAGE_INDEX,
|
|
92
128
|
GPU_BUFFER_USAGE_UNIFORM,
|
|
93
129
|
GPU_BUFFER_USAGE_VERTEX,
|
|
94
130
|
} from './gpu-usage';
|
|
131
|
+
import { resetHdrpBuffers } from './hdrp-buffers';
|
|
132
|
+
import {
|
|
133
|
+
CUBEMAP_FACE_VERTICES,
|
|
134
|
+
createIblPipelines,
|
|
135
|
+
getOrCreateIblCache,
|
|
136
|
+
PREFILTER_MIP_LEVELS,
|
|
137
|
+
PREFILTER_SIZE,
|
|
138
|
+
} from './ibl/IblPipelineCache';
|
|
95
139
|
import { assembleMaterialWithSkylightEntries } from './ibl/skylight-bind-group';
|
|
96
140
|
import { disposeInstanceBuffers, disposeTransientInstanceBuffers } from './instance-buffer-cache';
|
|
97
|
-
import type { MeshMaterialBindingObservation } from './mesh-material-bindings';
|
|
98
|
-
import { validateClusterGrid } from './pipeline/standard-pipeline';
|
|
99
141
|
import {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
142
|
+
type MeshMaterialBindingObservation,
|
|
143
|
+
type MeshMaterialBindingPreparationFailure,
|
|
144
|
+
type MeshMaterialBindingResidency,
|
|
145
|
+
projectMeshMaterialBindingObservation,
|
|
146
|
+
} from './mesh-material-bindings';
|
|
147
|
+
import type { StandardLightingInspection } from './pipeline/standard-lighting/inspection';
|
|
148
|
+
import { validateClusterGrid } from './pipeline/standard-pipeline';
|
|
149
|
+
import { DEFAULT_CLUSTER_GRID } from './pipeline/standard-profile';
|
|
104
150
|
import { PipelineSpecError } from './pipeline-spec';
|
|
151
|
+
import type { PointShadowInspection } from './point-shadow-inspection';
|
|
105
152
|
import { PostProcessError } from './post-process-errors';
|
|
106
153
|
import {
|
|
107
154
|
createPreparedGraphicsResolver,
|
|
108
155
|
type PreparedGraphicsResolver,
|
|
109
156
|
} from './prepare/prepared-graphics-resolver';
|
|
110
157
|
import {
|
|
158
|
+
type CubeCaptureFrameInput,
|
|
111
159
|
type FrameObservation,
|
|
112
160
|
type FrameObservationOptions,
|
|
113
161
|
observeCurrentFrame,
|
|
@@ -121,14 +169,35 @@ import type {
|
|
|
121
169
|
RenderFrameState,
|
|
122
170
|
} from './record/frame-snapshot';
|
|
123
171
|
import { getTextureIdentity } from './record/frame-snapshot';
|
|
172
|
+
import type { GpuPassTimingReason } from './record/gpu-pass-timing/errors';
|
|
173
|
+
import type {
|
|
174
|
+
GpuPassTimingCapture,
|
|
175
|
+
GpuPassTimingFrameIdentity,
|
|
176
|
+
GpuPassTimingSession,
|
|
177
|
+
} from './record/gpu-pass-timing/session';
|
|
124
178
|
import type { GpuTimingCapture } from './record/gpu-timing';
|
|
125
179
|
import { applyParamSnapshotToUbo, residentTextureView } from './record/main-pass-material';
|
|
126
|
-
import type { RenderSystemInternals } from './record/render-context';
|
|
180
|
+
import type { ReflectionProbeRecordState, RenderSystemInternals } from './record/render-context';
|
|
181
|
+
import type { CubeCaptureGraphState, ReflectionProbeGraphWork } from './record/typed-frame-graph';
|
|
127
182
|
import {
|
|
128
183
|
type RenderFeatureGraphCandidate,
|
|
129
184
|
resetRenderFeatureGraphState,
|
|
130
185
|
settleVolumetricFogGraphCandidate,
|
|
131
186
|
} from './record/typed-frame-graph';
|
|
187
|
+
import { REFLECTION_PROBE_VIEW_SLOT_BASE, VIEW_UNIFORM_SLOT_STRIDE } from './record/view-ubo';
|
|
188
|
+
import {
|
|
189
|
+
advanceProbeFilter,
|
|
190
|
+
commitProbeFilterStep,
|
|
191
|
+
createProbeFilterState,
|
|
192
|
+
type ProbeFilterState,
|
|
193
|
+
} from './reflection/filter';
|
|
194
|
+
import { buildReflectionProbeTable } from './reflection/gpu-table';
|
|
195
|
+
import {
|
|
196
|
+
admitReflectionProbe,
|
|
197
|
+
DEFAULT_REFLECTION_PROBE_LIMITS,
|
|
198
|
+
type ReflectionProbeFact,
|
|
199
|
+
type ReflectionProbeSelectionResult,
|
|
200
|
+
} from './reflection/projection';
|
|
132
201
|
import {
|
|
133
202
|
type CameraSnapshot,
|
|
134
203
|
type DrawOwnerOptions,
|
|
@@ -137,7 +206,13 @@ import {
|
|
|
137
206
|
type RenderPhaseSkipReason,
|
|
138
207
|
type RenderRecordPhase,
|
|
139
208
|
} from './render-contract';
|
|
140
|
-
import type {
|
|
209
|
+
import type {
|
|
210
|
+
DispatchEntry,
|
|
211
|
+
ExtractedFrame,
|
|
212
|
+
ExtractedLights,
|
|
213
|
+
MaterialSnapshot,
|
|
214
|
+
RenderableSnapshot,
|
|
215
|
+
} from './render-system-extract';
|
|
141
216
|
import { extractFrames } from './render-system-extract';
|
|
142
217
|
import { PersistentRenderScene } from './scene/render-scene';
|
|
143
218
|
import { resolveSsaoParameters } from './ssao-config';
|
|
@@ -148,6 +223,7 @@ import {
|
|
|
148
223
|
TRANSPARENT_SORT_MODE_LAYER_YZ,
|
|
149
224
|
TRANSPARENT_SORT_MODE_LAYER_Z,
|
|
150
225
|
} from './systems/transparent-sort-config';
|
|
226
|
+
import type { RenderTarget } from './targets/contracts';
|
|
151
227
|
import { createTemporalFrameTransaction, type TemporalFrame } from './temporal/frame';
|
|
152
228
|
import { retireTemporalGpuState } from './temporal/gpu';
|
|
153
229
|
import { inspectTemporal, type TemporalInspection } from './temporal/inspection';
|
|
@@ -163,6 +239,21 @@ import {
|
|
|
163
239
|
inspectTransmissionFromAdmission,
|
|
164
240
|
} from './transmission/inspection';
|
|
165
241
|
|
|
242
|
+
type TimingSessionHost = RenderSystemInternals & {
|
|
243
|
+
gpuPassTimingSession?: GpuPassTimingSession | undefined;
|
|
244
|
+
gpuPassTimingCapture?: GpuPassTimingCapture | undefined;
|
|
245
|
+
gpuPassTimingFrameIdentity?: GpuPassTimingFrameIdentity | undefined;
|
|
246
|
+
gpuPassTimingBeginReason?: GpuPassTimingReason | undefined;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/** Attach the Render-owned timing session without widening the public renderer surface. */
|
|
250
|
+
export function attachGpuPassTimingSession(
|
|
251
|
+
internals: object,
|
|
252
|
+
session: GpuPassTimingSession | undefined,
|
|
253
|
+
): void {
|
|
254
|
+
(internals as TimingSessionHost).gpuPassTimingSession = session;
|
|
255
|
+
}
|
|
256
|
+
|
|
166
257
|
export type {
|
|
167
258
|
_InternalRenderPipelineContext,
|
|
168
259
|
_StandardForwardSceneView,
|
|
@@ -354,6 +445,9 @@ export interface RenderSystem {
|
|
|
354
445
|
readonly visibilityStats: { explicitlyHidden: number };
|
|
355
446
|
/** Persistent scene maintenance evidence from the ordinary single-World path. */
|
|
356
447
|
readonly renderScene: RenderSceneInspection;
|
|
448
|
+
readonly reflectionProbes: ReflectionProbeInspection;
|
|
449
|
+
/** True while a progressive CubeCamera candidate still owns unfinished faces. */
|
|
450
|
+
isCubeCapturePending(target: RenderTarget): boolean;
|
|
357
451
|
readonly environment: EnvironmentInspection;
|
|
358
452
|
readonly temporal: TemporalInspection;
|
|
359
453
|
readonly bloom: BloomInspection;
|
|
@@ -361,6 +455,12 @@ export interface RenderSystem {
|
|
|
361
455
|
readonly temporalTargetInspection: TemporalTargetInspection | undefined;
|
|
362
456
|
/** Last completed transmission candidate facts, detached from GPU handles. */
|
|
363
457
|
readonly transmission: TransmissionInspection | undefined;
|
|
458
|
+
/** Single production projection of Directional author/extract/record facts. */
|
|
459
|
+
readonly directionalShadow: DirectionalShadowInspection;
|
|
460
|
+
/** Last prepared Standard transport facts, detached from GPU handles. */
|
|
461
|
+
readonly standardLightingInspection: StandardLightingInspection | undefined;
|
|
462
|
+
/** Last submitted point-shadow atlas budget facts, detached from GPU handles. */
|
|
463
|
+
readonly pointShadowInspection: PointShadowInspection | undefined;
|
|
364
464
|
/** Retained Points/Lines authoring facts from the single scene projection. */
|
|
365
465
|
readonly pointsLinesSnapshots: readonly PointsLinesRetainedSnapshot[];
|
|
366
466
|
/** Current mesh-slot provenance and active diagnostics from the last frame. */
|
|
@@ -492,6 +592,175 @@ type PointsLinesGpuResources = {
|
|
|
492
592
|
};
|
|
493
593
|
type PointsLinesPreparationAdapter = PointsLinesLanePreparationAdapter<PointsLinesGpuResources>;
|
|
494
594
|
|
|
595
|
+
function preparationFailureFrom(error: unknown): MeshMaterialBindingPreparationFailure | undefined {
|
|
596
|
+
if (typeof error !== 'object' || error === null) return undefined;
|
|
597
|
+
const value = error as {
|
|
598
|
+
readonly code?: unknown;
|
|
599
|
+
readonly expected?: unknown;
|
|
600
|
+
readonly hint?: unknown;
|
|
601
|
+
readonly detail?: unknown;
|
|
602
|
+
};
|
|
603
|
+
if (
|
|
604
|
+
typeof value.code !== 'string' ||
|
|
605
|
+
typeof value.expected !== 'string' ||
|
|
606
|
+
typeof value.hint !== 'string'
|
|
607
|
+
) {
|
|
608
|
+
return undefined;
|
|
609
|
+
}
|
|
610
|
+
const detail =
|
|
611
|
+
typeof value.detail === 'object' && value.detail !== null
|
|
612
|
+
? { ...(value.detail as Record<string, unknown>) }
|
|
613
|
+
: undefined;
|
|
614
|
+
return {
|
|
615
|
+
code: value.code,
|
|
616
|
+
expected: value.expected,
|
|
617
|
+
hint: value.hint,
|
|
618
|
+
...(detail === undefined ? {} : { detail }),
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function materialTextureHandles(
|
|
623
|
+
material: MaterialSnapshot,
|
|
624
|
+
): readonly Handle<'TextureAsset', 'shared'>[] {
|
|
625
|
+
const handles = new Map<number, Handle<'TextureAsset', 'shared'>>();
|
|
626
|
+
const add = (handle: Handle<'TextureAsset', 'shared'> | undefined): void => {
|
|
627
|
+
if (handle !== undefined) handles.set(Number(handle), handle);
|
|
628
|
+
};
|
|
629
|
+
for (const handle of material.textureHandles?.values() ?? []) add(handle);
|
|
630
|
+
add(material.baseColorTexture);
|
|
631
|
+
add(material.metallicRoughnessTexture);
|
|
632
|
+
add(material.normalTexture);
|
|
633
|
+
add(material.emissiveTexture);
|
|
634
|
+
add(material.occlusionTexture);
|
|
635
|
+
return [...handles.values()];
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function materialSamplerHandles(
|
|
639
|
+
material: MaterialSnapshot,
|
|
640
|
+
): readonly Handle<'SamplerAsset', 'shared'>[] {
|
|
641
|
+
const handles = new Map<number, Handle<'SamplerAsset', 'shared'>>();
|
|
642
|
+
for (const handle of material.samplerHandles?.values() ?? []) {
|
|
643
|
+
handles.set(Number(handle), handle);
|
|
644
|
+
}
|
|
645
|
+
return [...handles.values()];
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Project the producer-owned material and GPU store state into the one
|
|
650
|
+
* renderer observation surface. This is intentionally recomputed from the
|
|
651
|
+
* current frame's detached material snapshots; no readiness map is retained
|
|
652
|
+
* by RenderSystem. A producer failure with an already resident resource is
|
|
653
|
+
* reported as last-known-good, preserving recovery context without copying
|
|
654
|
+
* the producer's lifecycle state.
|
|
655
|
+
*/
|
|
656
|
+
function observeMaterialResidency(
|
|
657
|
+
world: World | undefined,
|
|
658
|
+
material: MaterialSnapshot,
|
|
659
|
+
internals: RenderSystemInternals,
|
|
660
|
+
): MeshMaterialBindingResidency {
|
|
661
|
+
const textures: Array<{ readonly handle: number; readonly mipLevelCount: number }> = [];
|
|
662
|
+
const samplers: Array<{ readonly handle: number; readonly resident: boolean }> = [];
|
|
663
|
+
let pending = false;
|
|
664
|
+
let resident = false;
|
|
665
|
+
let failure: MeshMaterialBindingPreparationFailure | undefined;
|
|
666
|
+
const keepFailure = (error: unknown): void => {
|
|
667
|
+
if (failure === undefined) failure = preparationFailureFrom(error);
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
if (world === undefined) {
|
|
671
|
+
pending = true;
|
|
672
|
+
} else {
|
|
673
|
+
const materialHandle = material.materialHandle;
|
|
674
|
+
if (materialHandle !== undefined && materialHandle !== 0) {
|
|
675
|
+
const materialResult = resolveAssetHandle<MaterialAsset>(
|
|
676
|
+
world,
|
|
677
|
+
toShared<'MaterialAsset'>(materialHandle),
|
|
678
|
+
);
|
|
679
|
+
if (!materialResult.ok) {
|
|
680
|
+
keepFailure(materialResult.error);
|
|
681
|
+
} else {
|
|
682
|
+
const guid = internals.assets.guidOf(materialResult.value);
|
|
683
|
+
if (guid !== undefined) {
|
|
684
|
+
const readiness = internals.assets.getMaterialReadiness(guid);
|
|
685
|
+
if (readiness?.status === 'Error') keepFailure(readiness.error);
|
|
686
|
+
const load = internals.assets.loadState.get(guid);
|
|
687
|
+
if (load?.status === 'provisional') pending = true;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
for (const handle of materialTextureHandles(material)) {
|
|
693
|
+
const existing = internals.gpuStore.getTextureGpuView(handle, world);
|
|
694
|
+
const pod = resolveAssetHandle<TextureAsset>(world, handle);
|
|
695
|
+
if (!pod.ok) {
|
|
696
|
+
keepFailure(pod.error);
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
let view = existing;
|
|
700
|
+
if (view === undefined) {
|
|
701
|
+
const prepared = internals.gpuStore.ensureResident(handle, pod.value, world);
|
|
702
|
+
if (!prepared.ok) keepFailure(prepared.error);
|
|
703
|
+
view = internals.gpuStore.getTextureGpuView(handle, world);
|
|
704
|
+
}
|
|
705
|
+
if (view === undefined) {
|
|
706
|
+
pending = failure === undefined;
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
const textureLayout = deriveTextureLayout({
|
|
710
|
+
shape: pod.value.shape,
|
|
711
|
+
format: pod.value.format,
|
|
712
|
+
mips: pod.value.mips,
|
|
713
|
+
});
|
|
714
|
+
if (!textureLayout.ok) {
|
|
715
|
+
keepFailure(textureLayout.error);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
resident = true;
|
|
719
|
+
textures.push({
|
|
720
|
+
handle: Number(handle),
|
|
721
|
+
mipLevelCount: Math.max(1, textureLayout.value.levels.length),
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
for (const handle of materialSamplerHandles(material)) {
|
|
726
|
+
const pod = resolveAssetHandle<SamplerAsset>(world, handle);
|
|
727
|
+
if (!pod.ok) {
|
|
728
|
+
keepFailure(pod.error);
|
|
729
|
+
samplers.push({ handle: Number(handle), resident: false });
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
const prepared = internals.gpuStore.ensureSamplerResident(handle, pod.value, world);
|
|
733
|
+
if (!prepared.ok) {
|
|
734
|
+
keepFailure(prepared.error);
|
|
735
|
+
samplers.push({ handle: Number(handle), resident: false });
|
|
736
|
+
} else {
|
|
737
|
+
resident = true;
|
|
738
|
+
samplers.push({ handle: Number(handle), resident: true });
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
// Video views are supplied by the dynamic host store at record time. They
|
|
742
|
+
// are not static TextureAsset residents, so this bounded projection stays
|
|
743
|
+
// pending until a dynamic observation contract exists rather than guessing
|
|
744
|
+
// from the VideoAsset GUID.
|
|
745
|
+
if ((material.videoTextureFields?.size ?? 0) > 0) pending = true;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const readiness =
|
|
749
|
+
failure === undefined
|
|
750
|
+
? pending
|
|
751
|
+
? 'pending'
|
|
752
|
+
: 'ready'
|
|
753
|
+
: resident
|
|
754
|
+
? 'last-known-good'
|
|
755
|
+
: 'failed';
|
|
756
|
+
return {
|
|
757
|
+
readiness,
|
|
758
|
+
samplers,
|
|
759
|
+
textures,
|
|
760
|
+
...(failure === undefined ? {} : { preparationFailure: failure }),
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
|
|
495
764
|
/**
|
|
496
765
|
* The Standard renderer's single Points/Lines prepare owner.
|
|
497
766
|
*
|
|
@@ -753,6 +1022,551 @@ class StandardPointsLinesOwner implements PointsLinesRecordOwner {
|
|
|
753
1022
|
}
|
|
754
1023
|
}
|
|
755
1024
|
|
|
1025
|
+
type ProbeCubeSet = {
|
|
1026
|
+
readonly texture: Texture;
|
|
1027
|
+
readonly cubeView: TextureView;
|
|
1028
|
+
readonly faceViews: readonly TextureView[];
|
|
1029
|
+
readonly mipViews: readonly (readonly TextureView[])[];
|
|
1030
|
+
readonly depthTexture: Texture;
|
|
1031
|
+
readonly depthView: TextureView;
|
|
1032
|
+
readonly size: number;
|
|
1033
|
+
};
|
|
1034
|
+
|
|
1035
|
+
type ProbeGpuResource = {
|
|
1036
|
+
fact: ReflectionProbeFact;
|
|
1037
|
+
index: number;
|
|
1038
|
+
filter: ProbeFilterState;
|
|
1039
|
+
rawFaceCursor: number;
|
|
1040
|
+
readonly raw: ProbeCubeSet;
|
|
1041
|
+
active: ProbeCubeSet | undefined;
|
|
1042
|
+
candidate: ProbeCubeSet;
|
|
1043
|
+
spare: ProbeCubeSet | undefined;
|
|
1044
|
+
readonly sampler: Sampler;
|
|
1045
|
+
readonly uniformBuffer: Buffer;
|
|
1046
|
+
readonly filterGroup1: BindGroup;
|
|
1047
|
+
pending?: {
|
|
1048
|
+
readonly rawCaptureFace: number | undefined;
|
|
1049
|
+
readonly step: ReturnType<typeof advanceProbeFilter>;
|
|
1050
|
+
};
|
|
1051
|
+
};
|
|
1052
|
+
|
|
1053
|
+
type ProbeSharedResources = {
|
|
1054
|
+
readonly faceUniforms: Buffer;
|
|
1055
|
+
readonly prefilterUniforms: Buffer;
|
|
1056
|
+
readonly vertexBuffer: Buffer;
|
|
1057
|
+
readonly sampler: Sampler;
|
|
1058
|
+
readonly filterPipeline: RenderPipeline;
|
|
1059
|
+
readonly cubeGroup1Bgl: BindGroupLayout;
|
|
1060
|
+
readonly prefilterGroup0Bgl: BindGroupLayout;
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
/** Renderer-owned raw capture and incremental PMREM/LKG promotion owner. */
|
|
1064
|
+
class ReflectionProbeRecordOwner {
|
|
1065
|
+
private readonly resources = new Map<string, ProbeGpuResource>();
|
|
1066
|
+
private shared: ProbeSharedResources | undefined;
|
|
1067
|
+
private pipelineWarmup: Promise<void> | undefined;
|
|
1068
|
+
private pipelineWarmupFailure: string | undefined;
|
|
1069
|
+
private pipelineWarmupAttempts = 0;
|
|
1070
|
+
private lastFactCount = 0;
|
|
1071
|
+
private lastAcceptedCount = 0;
|
|
1072
|
+
private lastScheduledRawFaces = 0;
|
|
1073
|
+
private lastScheduledFilteredSteps = 0;
|
|
1074
|
+
private lastSelection: ReflectionProbeSelectionInspection = { kind: 'skylight' };
|
|
1075
|
+
private readonly outputFormat: TextureFormat;
|
|
1076
|
+
|
|
1077
|
+
constructor(private readonly internals: RenderSystemInternals) {
|
|
1078
|
+
this.outputFormat = internals.device.caps.rgba16floatRenderable ? 'rgba16float' : 'rgba8unorm';
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
inspect(): ReflectionProbeInspection {
|
|
1082
|
+
let rawFacesCaptured = 0;
|
|
1083
|
+
let filteredStepsCompleted = 0;
|
|
1084
|
+
const filteredMipLevels = new Set<number>();
|
|
1085
|
+
let activeCount = 0;
|
|
1086
|
+
for (const resource of this.resources.values()) {
|
|
1087
|
+
rawFacesCaptured += resource.rawFaceCursor;
|
|
1088
|
+
filteredStepsCompleted += resource.filter.cursor;
|
|
1089
|
+
const completedMipCount = Math.min(
|
|
1090
|
+
PREFILTER_MIP_LEVELS,
|
|
1091
|
+
Math.floor(resource.filter.cursor / resource.filter.faceCount),
|
|
1092
|
+
);
|
|
1093
|
+
for (let mip = 0; mip < completedMipCount; mip += 1) filteredMipLevels.add(mip);
|
|
1094
|
+
if (resource.active !== undefined) activeCount += 1;
|
|
1095
|
+
}
|
|
1096
|
+
return {
|
|
1097
|
+
selection: this.lastSelection,
|
|
1098
|
+
factCount: this.lastFactCount,
|
|
1099
|
+
acceptedCount: this.lastAcceptedCount,
|
|
1100
|
+
activeCount,
|
|
1101
|
+
rawFacesCaptured,
|
|
1102
|
+
filteredStepsCompleted,
|
|
1103
|
+
filteredMipLevels: [...filteredMipLevels].sort((a, b) => a - b),
|
|
1104
|
+
scheduledRawFaces: this.lastScheduledRawFaces,
|
|
1105
|
+
scheduledFilteredSteps: this.lastScheduledFilteredSteps,
|
|
1106
|
+
pipelineReady: this.shared !== undefined,
|
|
1107
|
+
pipelineWarmupAttempts: this.pipelineWarmupAttempts,
|
|
1108
|
+
...(this.pipelineWarmupFailure === undefined
|
|
1109
|
+
? {}
|
|
1110
|
+
: { pipelineWarmupFailure: this.pipelineWarmupFailure }),
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
private ensurePipelineWarmup(): void {
|
|
1115
|
+
if (this.shared !== undefined || this.pipelineWarmup !== undefined) return;
|
|
1116
|
+
const factory = this.internals.shaderModuleFactory;
|
|
1117
|
+
if (factory === undefined) return;
|
|
1118
|
+
const attempt = this.pipelineWarmupAttempts + 1;
|
|
1119
|
+
this.pipelineWarmupAttempts = attempt;
|
|
1120
|
+
this.pipelineWarmup = createIblPipelines(
|
|
1121
|
+
this.internals.deviceScope,
|
|
1122
|
+
this.internals.device,
|
|
1123
|
+
(device, descriptor) => {
|
|
1124
|
+
const labeled = {
|
|
1125
|
+
...descriptor,
|
|
1126
|
+
label: `${descriptor.label ?? 'ibl'}-reflection-probe-${attempt}`,
|
|
1127
|
+
};
|
|
1128
|
+
return this.internals.createShaderModule === undefined
|
|
1129
|
+
? Promise.resolve(factory.createShaderModule(labeled))
|
|
1130
|
+
: this.internals.createShaderModule(device, labeled);
|
|
1131
|
+
},
|
|
1132
|
+
this.outputFormat,
|
|
1133
|
+
).then((result) => {
|
|
1134
|
+
if (!result.ok) {
|
|
1135
|
+
this.pipelineWarmupFailure = `${result.error.code}: ${result.error.hint}`;
|
|
1136
|
+
console.warn('[forgeax] ReflectionProbe IBL warmup retry', this.pipelineWarmupFailure);
|
|
1137
|
+
this.pipelineWarmup = undefined;
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
this.shared = this.createSharedResources(result.value.prefilterPipeline);
|
|
1141
|
+
if (this.shared === undefined) {
|
|
1142
|
+
this.pipelineWarmupFailure = 'renderer-owned IBL resources could not be allocated';
|
|
1143
|
+
console.warn('[forgeax] ReflectionProbe IBL resource allocation retry');
|
|
1144
|
+
this.pipelineWarmup = undefined;
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
prepare(
|
|
1150
|
+
facts: readonly ReflectionProbeFact[],
|
|
1151
|
+
selections: ReadonlyMap<string, ReflectionProbeSelectionResult>,
|
|
1152
|
+
displayCamera: CameraSnapshot | undefined,
|
|
1153
|
+
): ReflectionProbeRecordState {
|
|
1154
|
+
this.lastFactCount = facts.length;
|
|
1155
|
+
this.lastScheduledRawFaces = 0;
|
|
1156
|
+
this.lastScheduledFilteredSteps = 0;
|
|
1157
|
+
const selectedProbe = [...selections.values()]
|
|
1158
|
+
.filter(
|
|
1159
|
+
(selection): selection is Extract<ReflectionProbeSelectionResult, { kind: 'probe' }> =>
|
|
1160
|
+
selection.kind === 'probe',
|
|
1161
|
+
)
|
|
1162
|
+
.sort((a, b) => a.worldId - b.worldId || a.entityKey - b.entityKey)[0];
|
|
1163
|
+
this.lastSelection =
|
|
1164
|
+
selectedProbe === undefined
|
|
1165
|
+
? { kind: 'skylight' }
|
|
1166
|
+
: { kind: 'probe', worldId: selectedProbe.worldId, entityKey: selectedProbe.entityKey };
|
|
1167
|
+
if (facts.length > 0) this.ensurePipelineWarmup();
|
|
1168
|
+
void this.pipelineWarmup;
|
|
1169
|
+
const ordered = facts
|
|
1170
|
+
.slice()
|
|
1171
|
+
.sort((a, b) => a.worldId - b.worldId || a.entityKey - b.entityKey);
|
|
1172
|
+
let acceptedCount = 0;
|
|
1173
|
+
let acceptedBytes = 0;
|
|
1174
|
+
const accepted = new Map<string, ProbeGpuResource>();
|
|
1175
|
+
for (const fact of ordered) {
|
|
1176
|
+
const admission = admitReflectionProbe(
|
|
1177
|
+
fact,
|
|
1178
|
+
{ acceptedCount, acceptedBytes },
|
|
1179
|
+
DEFAULT_REFLECTION_PROBE_LIMITS,
|
|
1180
|
+
);
|
|
1181
|
+
if (!admission.ok) continue;
|
|
1182
|
+
const key = `${fact.worldId}:${fact.entityKey}`;
|
|
1183
|
+
const resource = this.resourceFor(key, fact, acceptedCount);
|
|
1184
|
+
if (resource === undefined) continue;
|
|
1185
|
+
acceptedCount += 1;
|
|
1186
|
+
acceptedBytes = admission.acceptedBytes;
|
|
1187
|
+
accepted.set(key, resource);
|
|
1188
|
+
}
|
|
1189
|
+
for (const [key, resource] of this.resources) {
|
|
1190
|
+
if (!accepted.has(key)) {
|
|
1191
|
+
this.destroyProbeResource(resource);
|
|
1192
|
+
this.resources.delete(key);
|
|
1193
|
+
} else resource.index = [...accepted.keys()].indexOf(key);
|
|
1194
|
+
}
|
|
1195
|
+
const rows = [...accepted.values()]
|
|
1196
|
+
.map((resource) => {
|
|
1197
|
+
const active = resource.active;
|
|
1198
|
+
if (active === undefined) return undefined;
|
|
1199
|
+
return {
|
|
1200
|
+
primitiveKey: `${resource.fact.worldId}:${resource.fact.entityKey}`,
|
|
1201
|
+
index: resource.index,
|
|
1202
|
+
worldId: resource.fact.worldId,
|
|
1203
|
+
entityKey: resource.fact.entityKey,
|
|
1204
|
+
center: resource.fact.center,
|
|
1205
|
+
halfExtents: resource.fact.halfExtents,
|
|
1206
|
+
intensity: resource.fact.intensity,
|
|
1207
|
+
generation: resource.filter.activeGeneration,
|
|
1208
|
+
filteredView: active.cubeView,
|
|
1209
|
+
sampler: resource.sampler,
|
|
1210
|
+
uniformBuffer: resource.uniformBuffer,
|
|
1211
|
+
};
|
|
1212
|
+
})
|
|
1213
|
+
.filter((row): row is NonNullable<typeof row> => row !== undefined);
|
|
1214
|
+
const table = buildReflectionProbeTable(rows);
|
|
1215
|
+
const work: ReflectionProbeGraphWork[] = [];
|
|
1216
|
+
for (const resource of accepted.values()) {
|
|
1217
|
+
const rawCaptureFace =
|
|
1218
|
+
resource.rawFaceCursor < resource.raw.faceViews.length ? resource.rawFaceCursor : undefined;
|
|
1219
|
+
const step =
|
|
1220
|
+
resource.rawFaceCursor >= resource.raw.faceViews.length
|
|
1221
|
+
? advanceProbeFilter(resource.filter)
|
|
1222
|
+
: undefined;
|
|
1223
|
+
if (this.shared === undefined) continue;
|
|
1224
|
+
if (rawCaptureFace === undefined && step === undefined) continue;
|
|
1225
|
+
const filterGroup0 =
|
|
1226
|
+
step === undefined
|
|
1227
|
+
? undefined
|
|
1228
|
+
: this.internals.device.createBindGroup({
|
|
1229
|
+
label: `reflection-probe-${resource.index}-pmrem-${step.mipLevel}-${step.faceIndex}`,
|
|
1230
|
+
layout: this.shared.prefilterGroup0Bgl,
|
|
1231
|
+
entries: [
|
|
1232
|
+
{
|
|
1233
|
+
binding: 0,
|
|
1234
|
+
resource: {
|
|
1235
|
+
kind: 'buffer',
|
|
1236
|
+
value: {
|
|
1237
|
+
buffer: this.shared.faceUniforms,
|
|
1238
|
+
offset: step.faceIndex * 256,
|
|
1239
|
+
size: 64,
|
|
1240
|
+
},
|
|
1241
|
+
},
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
binding: 1,
|
|
1245
|
+
resource: {
|
|
1246
|
+
kind: 'buffer',
|
|
1247
|
+
value: {
|
|
1248
|
+
buffer: this.shared.prefilterUniforms,
|
|
1249
|
+
offset: (step.mipLevel * 6 + step.faceIndex) * 256,
|
|
1250
|
+
size: 16,
|
|
1251
|
+
},
|
|
1252
|
+
},
|
|
1253
|
+
},
|
|
1254
|
+
],
|
|
1255
|
+
});
|
|
1256
|
+
if (filterGroup0 !== undefined && !filterGroup0.ok) continue;
|
|
1257
|
+
if (resource.pending !== undefined) continue;
|
|
1258
|
+
resource.pending = { rawCaptureFace, step };
|
|
1259
|
+
if (rawCaptureFace !== undefined) this.lastScheduledRawFaces += 1;
|
|
1260
|
+
if (step !== undefined) this.lastScheduledFilteredSteps += 1;
|
|
1261
|
+
const face =
|
|
1262
|
+
rawCaptureFace === undefined
|
|
1263
|
+
? undefined
|
|
1264
|
+
: buildCubeCameraFaceViews({
|
|
1265
|
+
position: resource.fact.center,
|
|
1266
|
+
near: 0.1,
|
|
1267
|
+
far: 100,
|
|
1268
|
+
})[rawCaptureFace];
|
|
1269
|
+
const faceCamera =
|
|
1270
|
+
face === undefined || displayCamera === undefined
|
|
1271
|
+
? undefined
|
|
1272
|
+
: {
|
|
1273
|
+
...displayCamera,
|
|
1274
|
+
position: vec3.create(
|
|
1275
|
+
resource.fact.center[0] ?? 0,
|
|
1276
|
+
resource.fact.center[1] ?? 0,
|
|
1277
|
+
resource.fact.center[2] ?? 0,
|
|
1278
|
+
),
|
|
1279
|
+
world: mat4.invert(mat4.create(), face.view),
|
|
1280
|
+
fov: Math.PI / 2,
|
|
1281
|
+
aspect: 1,
|
|
1282
|
+
near: face.near,
|
|
1283
|
+
far: face.far,
|
|
1284
|
+
projection: 'perspective' as const,
|
|
1285
|
+
};
|
|
1286
|
+
work.push({
|
|
1287
|
+
probeIndex: resource.index,
|
|
1288
|
+
rawTexture: resource.raw.texture,
|
|
1289
|
+
rawCubeView: resource.raw.cubeView,
|
|
1290
|
+
rawFaceViews: resource.raw.faceViews,
|
|
1291
|
+
rawDepthTexture: resource.raw.depthTexture,
|
|
1292
|
+
filteredTexture: resource.candidate.texture,
|
|
1293
|
+
filteredCubeView: resource.candidate.cubeView,
|
|
1294
|
+
filteredFaceViewsByMip: resource.candidate.mipViews,
|
|
1295
|
+
outputFormat: this.outputFormat,
|
|
1296
|
+
sampler: resource.sampler,
|
|
1297
|
+
filterPipeline: this.shared.filterPipeline,
|
|
1298
|
+
filterGroup0: filterGroup0?.value,
|
|
1299
|
+
filterGroup1: resource.filterGroup1,
|
|
1300
|
+
cubeVertexBuffer: this.shared.vertexBuffer,
|
|
1301
|
+
rawCaptureFace,
|
|
1302
|
+
rawDepthView: resource.raw.depthView,
|
|
1303
|
+
rawSize: resource.raw.size,
|
|
1304
|
+
...(faceCamera === undefined ? {} : { faceCamera }),
|
|
1305
|
+
...(rawCaptureFace === undefined
|
|
1306
|
+
? {}
|
|
1307
|
+
: {
|
|
1308
|
+
viewBindGroupDynamicOffset:
|
|
1309
|
+
(REFLECTION_PROBE_VIEW_SLOT_BASE + resource.index) * VIEW_UNIFORM_SLOT_STRIDE,
|
|
1310
|
+
}),
|
|
1311
|
+
filteredSize: resource.candidate.size,
|
|
1312
|
+
step,
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
return {
|
|
1316
|
+
table,
|
|
1317
|
+
selections,
|
|
1318
|
+
graph: { work },
|
|
1319
|
+
completeSubmission: (submitted, completed) => this.completeSubmission(submitted, completed),
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
private completeSubmission(submitted: boolean, completed?: Promise<unknown>): void {
|
|
1324
|
+
if (completed !== undefined) {
|
|
1325
|
+
void completed.then(
|
|
1326
|
+
() => this.completeSubmission(submitted),
|
|
1327
|
+
() => this.completeSubmission(false),
|
|
1328
|
+
);
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
for (const resource of this.resources.values()) {
|
|
1332
|
+
const pending = resource.pending;
|
|
1333
|
+
delete resource.pending;
|
|
1334
|
+
if (!submitted || pending === undefined) continue;
|
|
1335
|
+
if (pending.rawCaptureFace !== undefined) {
|
|
1336
|
+
resource.rawFaceCursor = Math.min(
|
|
1337
|
+
resource.rawFaceCursor + 1,
|
|
1338
|
+
resource.raw.faceViews.length,
|
|
1339
|
+
);
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
if (pending.step === undefined) continue;
|
|
1343
|
+
resource.filter = commitProbeFilterStep(resource.filter, true);
|
|
1344
|
+
if (resource.filter.cursor >= resource.filter.faceCount * resource.filter.mipCount) {
|
|
1345
|
+
const previous = resource.active;
|
|
1346
|
+
resource.active = resource.candidate;
|
|
1347
|
+
resource.candidate = resource.spare ?? previous ?? resource.candidate;
|
|
1348
|
+
resource.spare = undefined;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
dispose(): void {
|
|
1354
|
+
for (const resource of this.resources.values()) {
|
|
1355
|
+
this.destroyProbeResource(resource);
|
|
1356
|
+
}
|
|
1357
|
+
this.resources.clear();
|
|
1358
|
+
if (this.shared !== undefined) {
|
|
1359
|
+
this.internals.device.destroyBuffer(this.shared.faceUniforms);
|
|
1360
|
+
this.internals.device.destroyBuffer(this.shared.prefilterUniforms);
|
|
1361
|
+
this.internals.device.destroyBuffer(this.shared.vertexBuffer);
|
|
1362
|
+
}
|
|
1363
|
+
this.shared = undefined;
|
|
1364
|
+
this.pipelineWarmup = undefined;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
private destroyProbeResource(resource: ProbeGpuResource): void {
|
|
1368
|
+
const sets = new Set<ProbeCubeSet>([resource.raw, resource.candidate]);
|
|
1369
|
+
if (resource.active !== undefined) sets.add(resource.active);
|
|
1370
|
+
if (resource.spare !== undefined) sets.add(resource.spare);
|
|
1371
|
+
for (const set of sets) this.destroyCubeSet(set);
|
|
1372
|
+
this.internals.device.destroyBuffer(resource.uniformBuffer);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
private destroyCubeSet(set: ProbeCubeSet): void {
|
|
1376
|
+
this.internals.device.destroyTexture(set.texture);
|
|
1377
|
+
this.internals.device.destroyTexture(set.depthTexture);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
private resourceFor(
|
|
1381
|
+
key: string,
|
|
1382
|
+
fact: ReflectionProbeFact,
|
|
1383
|
+
index: number,
|
|
1384
|
+
): ProbeGpuResource | undefined {
|
|
1385
|
+
const existing = this.resources.get(key);
|
|
1386
|
+
if (existing !== undefined) {
|
|
1387
|
+
if (existing.fact.revision !== fact.revision) {
|
|
1388
|
+
existing.fact = fact;
|
|
1389
|
+
existing.filter = createProbeFilterState({
|
|
1390
|
+
probeIndex: index,
|
|
1391
|
+
mipCount: PREFILTER_MIP_LEVELS,
|
|
1392
|
+
activeGeneration: existing.filter.activeGeneration,
|
|
1393
|
+
});
|
|
1394
|
+
existing.rawFaceCursor = 0;
|
|
1395
|
+
} else {
|
|
1396
|
+
existing.fact = fact;
|
|
1397
|
+
existing.index = index;
|
|
1398
|
+
}
|
|
1399
|
+
return existing;
|
|
1400
|
+
}
|
|
1401
|
+
if (this.shared === undefined) return undefined;
|
|
1402
|
+
const raw = this.createCubeSet(`reflection-probe-${index}-raw`, fact.resolution);
|
|
1403
|
+
const candidate = this.createCubeSet(`reflection-probe-${index}-candidate`, fact.resolution);
|
|
1404
|
+
const spare = this.createCubeSet(`reflection-probe-${index}-spare`, fact.resolution);
|
|
1405
|
+
if (raw === undefined || candidate === undefined || spare === undefined) return undefined;
|
|
1406
|
+
const uniformBuffer = this.internals.device.createBuffer({
|
|
1407
|
+
label: `reflection-probe-${index}-uniforms`,
|
|
1408
|
+
size: 32,
|
|
1409
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
1410
|
+
mappedAtCreation: false,
|
|
1411
|
+
});
|
|
1412
|
+
if (!uniformBuffer.ok) return undefined;
|
|
1413
|
+
const payload = new Float32Array([
|
|
1414
|
+
-Math.max(0, fact.intensity),
|
|
1415
|
+
fact.center[0],
|
|
1416
|
+
fact.center[1],
|
|
1417
|
+
fact.center[2],
|
|
1418
|
+
fact.halfExtents[0],
|
|
1419
|
+
fact.halfExtents[1],
|
|
1420
|
+
fact.halfExtents[2],
|
|
1421
|
+
0,
|
|
1422
|
+
]);
|
|
1423
|
+
const written = this.internals.device.queue.writeBuffer(uniformBuffer.value, 0, payload);
|
|
1424
|
+
if (!written.ok) return undefined;
|
|
1425
|
+
const filterGroup1 = this.internals.device.createBindGroup({
|
|
1426
|
+
label: `reflection-probe-${index}-filter-input`,
|
|
1427
|
+
layout: this.shared.cubeGroup1Bgl,
|
|
1428
|
+
entries: [
|
|
1429
|
+
{ binding: 0, resource: { kind: 'textureView', value: raw.cubeView } },
|
|
1430
|
+
{ binding: 1, resource: { kind: 'sampler', value: this.shared.sampler } },
|
|
1431
|
+
],
|
|
1432
|
+
});
|
|
1433
|
+
if (!filterGroup1.ok) return undefined;
|
|
1434
|
+
const resource: ProbeGpuResource = {
|
|
1435
|
+
fact,
|
|
1436
|
+
index,
|
|
1437
|
+
filter: createProbeFilterState({ probeIndex: index, mipCount: PREFILTER_MIP_LEVELS }),
|
|
1438
|
+
rawFaceCursor: 0,
|
|
1439
|
+
raw,
|
|
1440
|
+
active: undefined,
|
|
1441
|
+
candidate,
|
|
1442
|
+
spare,
|
|
1443
|
+
sampler: this.shared.sampler,
|
|
1444
|
+
uniformBuffer: uniformBuffer.value,
|
|
1445
|
+
filterGroup1: filterGroup1.value,
|
|
1446
|
+
};
|
|
1447
|
+
this.resources.set(key, resource);
|
|
1448
|
+
return resource;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
private createSharedResources(filterPipeline: RenderPipeline): ProbeSharedResources | undefined {
|
|
1452
|
+
const faceUniforms = createFaceUniformsBuffer(this.internals.device);
|
|
1453
|
+
const prefilterUniforms = createPrefilterUniformsBuffer(this.internals.device);
|
|
1454
|
+
if (!faceUniforms.ok || !prefilterUniforms.ok) return undefined;
|
|
1455
|
+
if (!writeAllFaceUniforms(this.internals.device, faceUniforms.value).ok) return undefined;
|
|
1456
|
+
if (!writeAllPrefilterUniforms(this.internals.device, prefilterUniforms.value).ok)
|
|
1457
|
+
return undefined;
|
|
1458
|
+
const vertexBuffer = this.internals.device.createBuffer({
|
|
1459
|
+
label: 'reflection-probe-capture-cube-vertices',
|
|
1460
|
+
size: CUBEMAP_FACE_VERTICES.byteLength,
|
|
1461
|
+
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1462
|
+
mappedAtCreation: false,
|
|
1463
|
+
});
|
|
1464
|
+
if (!vertexBuffer.ok) return undefined;
|
|
1465
|
+
const vertexWrite = this.internals.device.queue.writeBuffer(
|
|
1466
|
+
vertexBuffer.value,
|
|
1467
|
+
0,
|
|
1468
|
+
CUBEMAP_FACE_VERTICES,
|
|
1469
|
+
);
|
|
1470
|
+
if (!vertexWrite.ok) return undefined;
|
|
1471
|
+
const sampler = this.internals.device.createSampler({
|
|
1472
|
+
label: 'reflection-probe-pmrem-sampler',
|
|
1473
|
+
magFilter: 'linear',
|
|
1474
|
+
minFilter: 'linear',
|
|
1475
|
+
mipmapFilter: 'linear',
|
|
1476
|
+
addressModeU: 'clamp-to-edge',
|
|
1477
|
+
addressModeV: 'clamp-to-edge',
|
|
1478
|
+
addressModeW: 'clamp-to-edge',
|
|
1479
|
+
});
|
|
1480
|
+
const cache = getOrCreateIblCache(this.internals.deviceScope);
|
|
1481
|
+
if (
|
|
1482
|
+
!sampler.ok ||
|
|
1483
|
+
cache.cubeGroup1Bgl === undefined ||
|
|
1484
|
+
cache.prefilterGroup0Bgl === undefined
|
|
1485
|
+
) {
|
|
1486
|
+
return undefined;
|
|
1487
|
+
}
|
|
1488
|
+
return {
|
|
1489
|
+
faceUniforms: faceUniforms.value,
|
|
1490
|
+
prefilterUniforms: prefilterUniforms.value,
|
|
1491
|
+
vertexBuffer: vertexBuffer.value,
|
|
1492
|
+
sampler: sampler.value,
|
|
1493
|
+
filterPipeline,
|
|
1494
|
+
cubeGroup1Bgl: cache.cubeGroup1Bgl,
|
|
1495
|
+
prefilterGroup0Bgl: cache.prefilterGroup0Bgl,
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
private createCubeSet(label: string, requestedSize: number): ProbeCubeSet | undefined {
|
|
1500
|
+
const size = Math.min(Math.max(1, Math.floor(requestedSize)), PREFILTER_SIZE);
|
|
1501
|
+
const mipCount = Math.min(PREFILTER_MIP_LEVELS, Math.floor(Math.log2(size)) + 1);
|
|
1502
|
+
const texture = this.internals.device.createTexture({
|
|
1503
|
+
label,
|
|
1504
|
+
size: { width: size, height: size, depthOrArrayLayers: 6 },
|
|
1505
|
+
mipLevelCount: mipCount,
|
|
1506
|
+
sampleCount: 1,
|
|
1507
|
+
dimension: '2d',
|
|
1508
|
+
format: this.outputFormat,
|
|
1509
|
+
usage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT_AND_TEXTURE_BINDING | GPU_TEXTURE_USAGE_COPY_SRC,
|
|
1510
|
+
viewFormats: [],
|
|
1511
|
+
textureBindingViewDimension: undefined,
|
|
1512
|
+
});
|
|
1513
|
+
if (!texture.ok) return undefined;
|
|
1514
|
+
const cubeView = this.internals.device.createTextureView(texture.value, {
|
|
1515
|
+
label: `${label}-cube`,
|
|
1516
|
+
dimension: 'cube',
|
|
1517
|
+
baseMipLevel: 0,
|
|
1518
|
+
mipLevelCount: mipCount,
|
|
1519
|
+
arrayLayerCount: 6,
|
|
1520
|
+
});
|
|
1521
|
+
if (!cubeView.ok) return undefined;
|
|
1522
|
+
const faceViews: TextureView[] = [];
|
|
1523
|
+
const mipViews: TextureView[][] = [];
|
|
1524
|
+
for (let mip = 0; mip < mipCount; mip += 1) {
|
|
1525
|
+
const faces: TextureView[] = [];
|
|
1526
|
+
for (let face = 0; face < 6; face += 1) {
|
|
1527
|
+
const view = this.internals.device.createTextureView(texture.value, {
|
|
1528
|
+
label: `${label}-mip${mip}-face${face}`,
|
|
1529
|
+
dimension: '2d',
|
|
1530
|
+
baseMipLevel: mip,
|
|
1531
|
+
mipLevelCount: 1,
|
|
1532
|
+
baseArrayLayer: face,
|
|
1533
|
+
arrayLayerCount: 1,
|
|
1534
|
+
});
|
|
1535
|
+
if (!view.ok) return undefined;
|
|
1536
|
+
if (mip === 0) faceViews.push(view.value);
|
|
1537
|
+
faces.push(view.value);
|
|
1538
|
+
}
|
|
1539
|
+
mipViews.push(faces);
|
|
1540
|
+
}
|
|
1541
|
+
const depthTexture = this.internals.device.createTexture({
|
|
1542
|
+
label: `${label}-depth`,
|
|
1543
|
+
size: { width: size, height: size, depthOrArrayLayers: 1 },
|
|
1544
|
+
mipLevelCount: 1,
|
|
1545
|
+
sampleCount: 1,
|
|
1546
|
+
dimension: '2d',
|
|
1547
|
+
format: 'depth24plus-stencil8',
|
|
1548
|
+
usage: 0x10,
|
|
1549
|
+
viewFormats: [],
|
|
1550
|
+
textureBindingViewDimension: undefined,
|
|
1551
|
+
});
|
|
1552
|
+
if (!depthTexture.ok) return undefined;
|
|
1553
|
+
const depthView = this.internals.device.createTextureView(depthTexture.value, {
|
|
1554
|
+
label: `${label}-depth-view`,
|
|
1555
|
+
dimension: '2d',
|
|
1556
|
+
});
|
|
1557
|
+
if (!depthView.ok) return undefined;
|
|
1558
|
+
return {
|
|
1559
|
+
texture: texture.value,
|
|
1560
|
+
cubeView: cubeView.value,
|
|
1561
|
+
faceViews,
|
|
1562
|
+
mipViews,
|
|
1563
|
+
depthTexture: depthTexture.value,
|
|
1564
|
+
depthView: depthView.value,
|
|
1565
|
+
size,
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
756
1570
|
export function createRenderSystem(internals: RenderSystemInternals): RenderSystem {
|
|
757
1571
|
const phaseCatalogRegistration = internals.profiler?.registerPhaseCatalog(
|
|
758
1572
|
'render',
|
|
@@ -763,6 +1577,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
763
1577
|
let preparedWorlds: readonly World[] = [];
|
|
764
1578
|
let latestCamera: CameraSnapshot | undefined;
|
|
765
1579
|
const pointsLinesOwner = new StandardPointsLinesOwner(internals);
|
|
1580
|
+
const reflectionProbeOwner = new ReflectionProbeRecordOwner(internals);
|
|
1581
|
+
const cubeCaptureScheduler = createCubeCaptureScheduler({ maxFacesPerFrame: 1 });
|
|
1582
|
+
const cubeCaptureState: CubeCaptureGraphState = { work: [] };
|
|
766
1583
|
const persistentRenderScene = new PersistentRenderScene({
|
|
767
1584
|
getDevice: () => internals.device,
|
|
768
1585
|
onGpuError: (error) => internals.errorRegistry.fire(error),
|
|
@@ -964,6 +1781,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
964
1781
|
directionalShadowCacheRecorded: false,
|
|
965
1782
|
compiledFrameGraph: null,
|
|
966
1783
|
compiledFrameGraphTopologyKey: null,
|
|
1784
|
+
compiledFrameGraphGeneration: 0,
|
|
967
1785
|
volumetricFogPreviousGraph: null,
|
|
968
1786
|
volumetricFogPreviousGraphKey: null,
|
|
969
1787
|
volumetricFogCandidateGraph: null,
|
|
@@ -995,7 +1813,10 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
995
1813
|
hdrpClusterBinScratch: createClusterBinScratch(),
|
|
996
1814
|
hdrpClusterGridScratch: null,
|
|
997
1815
|
hdrpLightIndexListScratch: null,
|
|
998
|
-
|
|
1816
|
+
hdrpClusterMembership: null,
|
|
1817
|
+
standardLightingGraphSignature: '',
|
|
1818
|
+
standardLightingInspection: undefined,
|
|
1819
|
+
pointShadowInspection: undefined,
|
|
999
1820
|
transientInstanceBuffers: [],
|
|
1000
1821
|
warnedZeroLightStandard: false,
|
|
1001
1822
|
warnedMultiLightDirectional: false,
|
|
@@ -1051,10 +1872,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1051
1872
|
// (its topology is frame-invariant). Standard configuration overwrites this with the
|
|
1052
1873
|
// resolved asset config on every swap so the active graph reads its current config.
|
|
1053
1874
|
installedPipelineConfig: undefined,
|
|
1054
|
-
|
|
1055
|
-
// warn dedup set for hdrp-light-budget-exceeded / hdrp-index-list-overflow.
|
|
1056
|
-
isHdrpActive: false,
|
|
1057
|
-
hdrpOncePerFrameFired: new Set(),
|
|
1875
|
+
standardOncePerFrameFired: new Set(),
|
|
1058
1876
|
// feat-20260612-point-light-shadows-urp-hdrp M3 / T-M3-2 (plan-strategy §D-1):
|
|
1059
1877
|
// cube_array shadow atlas + per-frame snapshot list. Atlas is null until the
|
|
1060
1878
|
// first frame whose extracted lights.pointShadow is non-empty (zero-shadow
|
|
@@ -1073,6 +1891,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1073
1891
|
spotShadowSnapshots: [],
|
|
1074
1892
|
};
|
|
1075
1893
|
let directFrameId = 0;
|
|
1894
|
+
let lastDirectionalShadowLights: ExtractedLights | undefined;
|
|
1895
|
+
let lastDirectionalShadowCandidate: 'accepted' | 'failed' = 'failed';
|
|
1896
|
+
let lastDirectionalShadowError: DirectionalShadowInspectionSource['error'];
|
|
1076
1897
|
|
|
1077
1898
|
function beginProfilePhase(session: RecorderSession | undefined, phase: RenderPhase): boolean {
|
|
1078
1899
|
if (session === undefined) return false;
|
|
@@ -1133,66 +1954,124 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1133
1954
|
let activeFeaturePostProcessEntries: ReadonlyMap<string, PostProcessShaderEntry> = new Map();
|
|
1134
1955
|
const lookupPostProcess = (id: string): PostProcessShaderEntry | undefined =>
|
|
1135
1956
|
builtinPostProcessEntries.get(id) ?? activeFeaturePostProcessEntries.get(id);
|
|
1957
|
+
const invalidatePostProcessModule = (id: string): void => {
|
|
1958
|
+
const entry = lookupPostProcess(id);
|
|
1959
|
+
if (entry !== undefined) {
|
|
1960
|
+
internals.invalidateShaderModule?.(postProcessShaderModuleLabel(id, entry.source));
|
|
1961
|
+
}
|
|
1962
|
+
};
|
|
1136
1963
|
// feat-20260621 M-A2 / w8: expose the eager-created per-id params UBO through
|
|
1137
1964
|
// the narrow runtime surface so dispatchFullscreenPass can writeBuffer the
|
|
1138
1965
|
// per-frame snapshot + bind it at group(1) binding(2).
|
|
1139
1966
|
const getPostProcessParamsBuffer = (id: string): Buffer | undefined =>
|
|
1140
1967
|
postProcessParamsBuffers.get(id);
|
|
1141
|
-
|
|
1968
|
+
interface PostProcessParamsCandidate {
|
|
1969
|
+
readonly buffers: ReadonlyMap<string, Buffer>;
|
|
1970
|
+
readonly created: readonly Buffer[];
|
|
1971
|
+
}
|
|
1972
|
+
const postProcessParamsByteSize = (entry: PostProcessShaderEntry): number =>
|
|
1973
|
+
entry.params?.byteSize ?? (entryHasDepthRead(entry) ? DEPTH_MIN_PARAMS_BYTE_SIZE : 0);
|
|
1974
|
+
const destroyPostProcessParams = (buffers: readonly Buffer[]): void => {
|
|
1975
|
+
for (const buffer of buffers) {
|
|
1976
|
+
const destroyed = internals.device.destroyBuffer(buffer);
|
|
1977
|
+
if (!destroyed.ok) internals.errorRegistry.fire(destroyed.error);
|
|
1978
|
+
}
|
|
1979
|
+
};
|
|
1980
|
+
const preparePostProcessParamsResources = (
|
|
1142
1981
|
featureEntries: ReadonlyMap<string, PostProcessShaderEntry>,
|
|
1143
|
-
):
|
|
1982
|
+
): Result<PostProcessParamsCandidate, RhiError> => {
|
|
1144
1983
|
const declarations = new Map<string, PostProcessShaderEntry>([
|
|
1145
1984
|
...builtinPostProcessEntries.entries(),
|
|
1146
1985
|
...featureEntries.entries(),
|
|
1147
1986
|
]);
|
|
1987
|
+
const next = new Map<string, Buffer>();
|
|
1988
|
+
const allocated: Buffer[] = [];
|
|
1989
|
+
const discardCreated = (): void => destroyPostProcessParams(allocated);
|
|
1148
1990
|
for (const [id, entry] of declarations) {
|
|
1149
1991
|
if (id === 'forgeax.taa-resolve') continue;
|
|
1150
|
-
const byteSize =
|
|
1151
|
-
|
|
1152
|
-
|
|
1992
|
+
const byteSize = postProcessParamsByteSize(entry);
|
|
1993
|
+
if (byteSize === 0) continue;
|
|
1994
|
+
const existing = postProcessParamsBuffers.get(id);
|
|
1995
|
+
const accepted = lookupPostProcess(id);
|
|
1996
|
+
if (
|
|
1997
|
+
existing !== undefined &&
|
|
1998
|
+
accepted !== undefined &&
|
|
1999
|
+
postProcessShaderEntrySignature(accepted) === postProcessShaderEntrySignature(entry)
|
|
2000
|
+
) {
|
|
2001
|
+
next.set(id, existing);
|
|
2002
|
+
continue;
|
|
2003
|
+
}
|
|
1153
2004
|
const created = internals.device.createBuffer({
|
|
1154
2005
|
label: `post-process-params-${id}`,
|
|
1155
2006
|
size: byteSize,
|
|
1156
2007
|
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
1157
2008
|
mappedAtCreation: false,
|
|
1158
2009
|
});
|
|
1159
|
-
if (!created.ok)
|
|
2010
|
+
if (!created.ok) {
|
|
2011
|
+
discardCreated();
|
|
2012
|
+
return err(created.error);
|
|
2013
|
+
}
|
|
1160
2014
|
const buffer = created.value;
|
|
2015
|
+
allocated.push(buffer);
|
|
1161
2016
|
if (entry.params !== undefined) {
|
|
1162
2017
|
const written = internals.device.queue.writeBuffer(buffer, 0, entry.params.defaultValue);
|
|
1163
2018
|
if (!written.ok) {
|
|
1164
|
-
|
|
1165
|
-
|
|
2019
|
+
discardCreated();
|
|
2020
|
+
return err(written.error);
|
|
1166
2021
|
}
|
|
1167
2022
|
}
|
|
1168
|
-
|
|
2023
|
+
next.set(id, buffer);
|
|
1169
2024
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
2025
|
+
return ok({ buffers: next, created: Object.freeze([...allocated]) });
|
|
2026
|
+
};
|
|
2027
|
+
const discardPostProcessParamsCandidate = (candidate: PostProcessParamsCandidate): void => {
|
|
2028
|
+
destroyPostProcessParams(candidate.created);
|
|
2029
|
+
};
|
|
2030
|
+
const retirePostProcessParams = (buffers: readonly Buffer[]): void => {
|
|
2031
|
+
if (buffers.length === 0) return;
|
|
2032
|
+
const release = (): void => destroyPostProcessParams(buffers);
|
|
2033
|
+
try {
|
|
2034
|
+
void internals.device.queue.onSubmittedWorkDone().then(release, release);
|
|
2035
|
+
} catch {
|
|
2036
|
+
release();
|
|
2037
|
+
}
|
|
2038
|
+
};
|
|
2039
|
+
const acceptPostProcessParamsCandidate = (candidate: PostProcessParamsCandidate): void => {
|
|
2040
|
+
const previous = [...postProcessParamsBuffers.values()];
|
|
2041
|
+
const next = new Set(candidate.buffers.values());
|
|
2042
|
+
postProcessParamsBuffers.clear();
|
|
2043
|
+
for (const [id, buffer] of candidate.buffers) {
|
|
2044
|
+
postProcessParamsBuffers.set(id, buffer);
|
|
1174
2045
|
}
|
|
2046
|
+
retirePostProcessParams(previous.filter((buffer) => !next.has(buffer)));
|
|
1175
2047
|
};
|
|
1176
|
-
// feat-20260609 M4 / T-10-a: post-process pipeline cache (
|
|
2048
|
+
// feat-20260609 M4 / T-10-a: post-process pipeline cache (declaration|colorFormat -> RhiRenderPipeline).
|
|
1177
2049
|
// Solves CONCERN-1: dispatcher previously passed `pipeline=null` to
|
|
1178
2050
|
// built.createHandle because per-frame execute closures cannot await async
|
|
1179
2051
|
// shader compile. The cache here delegates the actual build to
|
|
1180
2052
|
// `internals.buildPostProcessPipeline` (sync wrapper over the shared shader
|
|
1181
|
-
// adapter; 1-frame warmup), then memoizes by
|
|
2053
|
+
// adapter; 1-frame warmup), then memoizes by declaration identity + format.
|
|
1182
2054
|
const postProcessPipelineCache = new Map<string, RenderPipeline>();
|
|
1183
2055
|
const getPostProcessPipeline = (
|
|
1184
2056
|
id: string,
|
|
1185
2057
|
bgl: BindGroupLayout,
|
|
1186
2058
|
colorFormat: GPUTextureFormat,
|
|
2059
|
+
entryOverride?: PostProcessShaderEntry,
|
|
1187
2060
|
): RenderPipeline | null => {
|
|
1188
|
-
const
|
|
2061
|
+
const entry = entryOverride ?? lookupPostProcess(id);
|
|
2062
|
+
if (entry === undefined) return null;
|
|
2063
|
+
const signature = postProcessShaderEntrySignature(entry);
|
|
2064
|
+
const key = `${id}|${colorFormat}|${signature}`;
|
|
1189
2065
|
const cached = postProcessPipelineCache.get(key);
|
|
1190
2066
|
if (cached !== undefined) return cached;
|
|
1191
|
-
const entry = lookupPostProcess(id);
|
|
1192
|
-
if (entry === undefined) return null;
|
|
1193
2067
|
const factory = internals.buildPostProcessPipeline;
|
|
1194
2068
|
if (factory === undefined) return null;
|
|
1195
|
-
const built = factory(
|
|
2069
|
+
const built = factory(
|
|
2070
|
+
entry,
|
|
2071
|
+
bgl,
|
|
2072
|
+
colorFormat,
|
|
2073
|
+
postProcessShaderPipelineLabel(id, entry.source),
|
|
2074
|
+
);
|
|
1196
2075
|
if (built === null) return null;
|
|
1197
2076
|
postProcessPipelineCache.set(key, built);
|
|
1198
2077
|
return built;
|
|
@@ -1202,6 +2081,14 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1202
2081
|
if (key.startsWith(`${id}|`)) postProcessPipelineCache.delete(key);
|
|
1203
2082
|
}
|
|
1204
2083
|
};
|
|
2084
|
+
const clearPostProcessPipelineEntry = (id: string, entry: PostProcessShaderEntry): void => {
|
|
2085
|
+
const suffix = `|${postProcessShaderEntrySignature(entry)}`;
|
|
2086
|
+
for (const key of postProcessPipelineCache.keys()) {
|
|
2087
|
+
if (key.startsWith(`${id}|`) && key.endsWith(suffix)) {
|
|
2088
|
+
postProcessPipelineCache.delete(key);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
};
|
|
1205
2092
|
Object.assign(internals, {
|
|
1206
2093
|
lookupPostProcess,
|
|
1207
2094
|
getPostProcessParamsBuffer,
|
|
@@ -1376,7 +2263,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1376
2263
|
if (buffer === undefined) return group;
|
|
1377
2264
|
return ok({
|
|
1378
2265
|
handle: group.value,
|
|
1379
|
-
dynamicOffsets: [0],
|
|
2266
|
+
dynamicOffsets: [0, 0],
|
|
1380
2267
|
release: () => internals.device.destroyBuffer(buffer),
|
|
1381
2268
|
});
|
|
1382
2269
|
};
|
|
@@ -1387,8 +2274,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1387
2274
|
const preparedResolverFactory = (
|
|
1388
2275
|
input: RenderFeaturePreparedGraphicsResolverInput,
|
|
1389
2276
|
): PreparedGraphicsResolver => {
|
|
1390
|
-
activeFeaturePostProcessEntries = input.fullscreenEffects;
|
|
1391
|
-
ensurePostProcessParamsResources(input.fullscreenEffects);
|
|
1392
2277
|
return createPreparedGraphicsResolver({
|
|
1393
2278
|
device: internals.device,
|
|
1394
2279
|
featureIdentity: input.featureIdentity,
|
|
@@ -1412,6 +2297,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1412
2297
|
descriptor.shader,
|
|
1413
2298
|
fullscreen.bindGroupLayout,
|
|
1414
2299
|
descriptor.colorFormats[0] as GPUTextureFormat,
|
|
2300
|
+
postProcessEntry,
|
|
1415
2301
|
);
|
|
1416
2302
|
if (pipeline !== null && pipeline !== undefined) {
|
|
1417
2303
|
preparedPipelineIds.set(pipeline as object, descriptor.shader);
|
|
@@ -1580,6 +2466,12 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1580
2466
|
gpuDriven: gpuDrivenProduction.inspect(),
|
|
1581
2467
|
};
|
|
1582
2468
|
},
|
|
2469
|
+
get reflectionProbes(): ReflectionProbeInspection {
|
|
2470
|
+
return reflectionProbeOwner.inspect();
|
|
2471
|
+
},
|
|
2472
|
+
isCubeCapturePending(target: RenderTarget): boolean {
|
|
2473
|
+
return cubeCaptureScheduler.inspect(target).candidateGeneration !== undefined;
|
|
2474
|
+
},
|
|
1583
2475
|
get environment(): EnvironmentInspection {
|
|
1584
2476
|
return environmentLifecycle.inspect();
|
|
1585
2477
|
},
|
|
@@ -1672,6 +2564,49 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1672
2564
|
get transmission(): TransmissionInspection | undefined {
|
|
1673
2565
|
return lastTransmissionInspection;
|
|
1674
2566
|
},
|
|
2567
|
+
get directionalShadow(): DirectionalShadowInspection {
|
|
2568
|
+
const lights = lastDirectionalShadowLights;
|
|
2569
|
+
const pipelineState = internals.getPipelineState();
|
|
2570
|
+
const graph = frameState.compiledFrameGraph?.inspect();
|
|
2571
|
+
const requested = directionalShadowProfileFromQuality(lights?.directionalShadowQuality);
|
|
2572
|
+
const mapSize = pipelineState?.perPassResources.shadowMapSize ?? 0;
|
|
2573
|
+
const cascadeCount = lights?.cascadeCount ?? 0;
|
|
2574
|
+
const shadowReady = frameState.currentDirectionalShadowView !== null;
|
|
2575
|
+
const atlasBytes =
|
|
2576
|
+
shadowReady && mapSize > 0 && cascadeCount > 0
|
|
2577
|
+
? mapSize * mapSize * Math.ceil(Math.sqrt(cascadeCount)) ** 2 * 4
|
|
2578
|
+
: 0;
|
|
2579
|
+
return projectDirectionalShadowInspectionSource({
|
|
2580
|
+
requested,
|
|
2581
|
+
candidate: lastDirectionalShadowCandidate,
|
|
2582
|
+
...(frameState.directionalShadowCache === null
|
|
2583
|
+
? {}
|
|
2584
|
+
: {
|
|
2585
|
+
lastKnownGood: directionalShadowProfileFromQuality(
|
|
2586
|
+
frameState.directionalShadowCache.directionalShadowQuality,
|
|
2587
|
+
),
|
|
2588
|
+
}),
|
|
2589
|
+
cascadeCount,
|
|
2590
|
+
mapSize,
|
|
2591
|
+
atlasBytes,
|
|
2592
|
+
writerPasses:
|
|
2593
|
+
graph?.passes.filter((pass) => pass.name.startsWith('shadowCascade')).length ?? 0,
|
|
2594
|
+
shadowAngularRadius: lights?.directionalCsmConfig?.shadowAngularRadius,
|
|
2595
|
+
maxPenumbraTexels: lights?.directionalCsmConfig?.maxPenumbraTexels,
|
|
2596
|
+
deviceGeneration: internals.deviceScope.generation,
|
|
2597
|
+
graphGeneration: frameState.perFrameGraph?.graphGeneration ?? frameState.graphGeneration,
|
|
2598
|
+
shadowReady,
|
|
2599
|
+
...(lastDirectionalShadowError === undefined ? {} : { error: lastDirectionalShadowError }),
|
|
2600
|
+
});
|
|
2601
|
+
},
|
|
2602
|
+
get standardLightingInspection(): StandardLightingInspection | undefined {
|
|
2603
|
+
const inspection = frameState.standardLightingInspection;
|
|
2604
|
+
return inspection === undefined ? undefined : Object.freeze({ ...inspection });
|
|
2605
|
+
},
|
|
2606
|
+
get pointShadowInspection(): PointShadowInspection | undefined {
|
|
2607
|
+
const inspection = frameState.pointShadowInspection;
|
|
2608
|
+
return inspection === undefined ? undefined : Object.freeze({ ...inspection });
|
|
2609
|
+
},
|
|
1675
2610
|
get pointsLinesSnapshots(): readonly PointsLinesRetainedSnapshot[] {
|
|
1676
2611
|
return persistentRenderScene.pointsLinesSnapshots();
|
|
1677
2612
|
},
|
|
@@ -1755,6 +2690,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1755
2690
|
});
|
|
1756
2691
|
const {
|
|
1757
2692
|
cameras,
|
|
2693
|
+
cubeCameras,
|
|
1758
2694
|
lights,
|
|
1759
2695
|
environment,
|
|
1760
2696
|
environmentReady,
|
|
@@ -1766,10 +2702,19 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1766
2702
|
skybox,
|
|
1767
2703
|
skyboxCount,
|
|
1768
2704
|
fog,
|
|
2705
|
+
fogFailure,
|
|
1769
2706
|
frustumStats,
|
|
1770
2707
|
visibilityStats,
|
|
1771
2708
|
postProcessParams,
|
|
1772
2709
|
} = frame;
|
|
2710
|
+
lastDirectionalShadowLights = lights;
|
|
2711
|
+
lastDirectionalShadowCandidate = 'failed';
|
|
2712
|
+
lastDirectionalShadowError = lights.directionalShadowError;
|
|
2713
|
+
const reflectionProbeRecord = reflectionProbeOwner.prepare(
|
|
2714
|
+
frame.reflectionProbes ?? [],
|
|
2715
|
+
persistentRenderScene.reflectionProbeProjection().selected,
|
|
2716
|
+
cameras[0],
|
|
2717
|
+
);
|
|
1773
2718
|
latestCamera = cameras[0];
|
|
1774
2719
|
const temporalDemanded =
|
|
1775
2720
|
cameras[0]?.antialias === 'taa' || (cameras[0]?.motionBlur?.shutterAngle ?? 0) > 0;
|
|
@@ -1777,6 +2722,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1777
2722
|
persistentRenderScene.prepareTemporalFrame(renderables);
|
|
1778
2723
|
}
|
|
1779
2724
|
environmentLifecycle.bindScope(internals.deviceScope);
|
|
2725
|
+
if (fogFailure !== undefined) {
|
|
2726
|
+
environmentLifecycle.recordSelectionFailure(fogFailure);
|
|
2727
|
+
}
|
|
1780
2728
|
if (!environmentReady || environment === undefined) {
|
|
1781
2729
|
frameState.pendingTemporalCommit = { kind: 'none' };
|
|
1782
2730
|
// A world with no camera still has a valid clear-only render path.
|
|
@@ -1805,15 +2753,21 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1805
2753
|
}
|
|
1806
2754
|
if (lastMeshMaterialBindingFrame !== frame) {
|
|
1807
2755
|
lastMeshMaterialBindingFrame = frame;
|
|
1808
|
-
lastMeshMaterialBindings = renderables.map((renderable) =>
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
2756
|
+
lastMeshMaterialBindings = renderables.map((renderable) => {
|
|
2757
|
+
const materialWorld = preparedWorlds[renderable.worldId];
|
|
2758
|
+
return projectMeshMaterialBindingObservation({
|
|
2759
|
+
worldId: renderable.worldId,
|
|
2760
|
+
entityKey: renderable.entityKey,
|
|
2761
|
+
bindings: renderable.materials.map((material, slotIndex) => ({
|
|
2762
|
+
handle: material.materialHandle ?? 0,
|
|
2763
|
+
source: renderable.materialBindingSources[slotIndex] ?? 'engine-default',
|
|
2764
|
+
})),
|
|
2765
|
+
diagnostics: renderable.materialBindingDiagnostics ?? [],
|
|
2766
|
+
residency: renderable.materials.map((material) =>
|
|
2767
|
+
observeMaterialResidency(materialWorld, material, internals),
|
|
2768
|
+
),
|
|
2769
|
+
});
|
|
2770
|
+
});
|
|
1817
2771
|
}
|
|
1818
2772
|
|
|
1819
2773
|
bindGroupCounts.createBindGroup = 0;
|
|
@@ -1879,14 +2833,23 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1879
2833
|
createPreparedGraphicsResolver: preparedResolverFactory,
|
|
1880
2834
|
gpuWork: featureGpuWork,
|
|
1881
2835
|
});
|
|
1882
|
-
activeFeaturePostProcessEntries = featureFrame.fullscreenEffects;
|
|
1883
|
-
ensurePostProcessParamsResources(featureFrame.fullscreenEffects);
|
|
1884
2836
|
lastVisibilityStats.explicitlyHidden = featureFrame.hiddenEntityReports.length;
|
|
1885
2837
|
for (const featureError of featureFrame.errors) {
|
|
1886
2838
|
if (!isPendingRenderFeaturePreparation(featureError)) {
|
|
1887
2839
|
internals.errorRegistry.fire(featureError);
|
|
1888
2840
|
}
|
|
1889
2841
|
}
|
|
2842
|
+
const postProcessParamsCandidate = preparePostProcessParamsResources(
|
|
2843
|
+
featureFrame.fullscreenEffects,
|
|
2844
|
+
);
|
|
2845
|
+
if (!postProcessParamsCandidate.ok) {
|
|
2846
|
+
internals.errorRegistry.fire(postProcessParamsCandidate.error);
|
|
2847
|
+
for (const batch of featureFrame.preparedResourceBatches) {
|
|
2848
|
+
const released = batch.release();
|
|
2849
|
+
if (!released.ok) internals.errorRegistry.fire(released.error);
|
|
2850
|
+
}
|
|
2851
|
+
return [];
|
|
2852
|
+
}
|
|
1890
2853
|
featureGraphCandidate = {
|
|
1891
2854
|
plans: featureFrame.plans,
|
|
1892
2855
|
fullscreenEffects: featureFrame.fullscreenEffects,
|
|
@@ -1900,6 +2863,29 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1900
2863
|
internals.errorRegistry.fire(released.error);
|
|
1901
2864
|
}
|
|
1902
2865
|
}
|
|
2866
|
+
discardPostProcessParamsCandidate(postProcessParamsCandidate.value);
|
|
2867
|
+
},
|
|
2868
|
+
onAbandoned: () => {
|
|
2869
|
+
discardPostProcessParamsCandidate(postProcessParamsCandidate.value);
|
|
2870
|
+
},
|
|
2871
|
+
onAccepted: () => {
|
|
2872
|
+
for (const [id, previous] of activeFeaturePostProcessEntries) {
|
|
2873
|
+
const next = featureFrame.fullscreenEffects.get(id);
|
|
2874
|
+
if (next === undefined) {
|
|
2875
|
+
clearPostProcessPipelineCache(id);
|
|
2876
|
+
invalidatePostProcessModule(id);
|
|
2877
|
+
} else if (
|
|
2878
|
+
postProcessShaderEntrySignature(previous) !==
|
|
2879
|
+
postProcessShaderEntrySignature(next)
|
|
2880
|
+
) {
|
|
2881
|
+
clearPostProcessPipelineEntry(id, previous);
|
|
2882
|
+
if (previous.source !== next.source) {
|
|
2883
|
+
invalidatePostProcessModule(id);
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
acceptPostProcessParamsCandidate(postProcessParamsCandidate.value);
|
|
2888
|
+
activeFeaturePostProcessEntries = featureFrame.fullscreenEffects;
|
|
1903
2889
|
},
|
|
1904
2890
|
};
|
|
1905
2891
|
return featureFrame.preparedResourceBatches;
|
|
@@ -1976,14 +2962,20 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1976
2962
|
{
|
|
1977
2963
|
owner: gpuDrivenProduction,
|
|
1978
2964
|
scene: persistentRenderScene.compositionGpuDrivenState(),
|
|
1979
|
-
onCoverage: (ownsAll) => {
|
|
1980
|
-
const key = `${internals.gpuStore.meshResidencyEpoch}:${
|
|
2965
|
+
onCoverage: (ownsAll, clusteredLighting) => {
|
|
2966
|
+
const key = `${internals.gpuStore.meshResidencyEpoch}:${clusteredLighting ? 'clustered' : 'standard'}`;
|
|
1981
2967
|
persistentRenderScene.setCompositionGpuDrivenCoverage(key, ownsAll);
|
|
1982
2968
|
},
|
|
1983
2969
|
},
|
|
1984
2970
|
renderReadLeases,
|
|
1985
2971
|
featureGraphCandidate,
|
|
1986
2972
|
pointsLinesOwner,
|
|
2973
|
+
{
|
|
2974
|
+
snapshots: cubeCameras,
|
|
2975
|
+
scheduler: cubeCaptureScheduler,
|
|
2976
|
+
state: cubeCaptureState,
|
|
2977
|
+
reflectionProbes: reflectionProbeRecord,
|
|
2978
|
+
} satisfies CubeCaptureFrameInput,
|
|
1987
2979
|
environment?.environmentSignature ?? '',
|
|
1988
2980
|
environment?.fogSignature ?? '',
|
|
1989
2981
|
environmentReady && environment !== undefined,
|
|
@@ -1992,6 +2984,28 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1992
2984
|
timingCapture,
|
|
1993
2985
|
),
|
|
1994
2986
|
);
|
|
2987
|
+
const directionalQuality = lights.directionalShadowQuality;
|
|
2988
|
+
const directionalShadowReady =
|
|
2989
|
+
directionalQuality === undefined ||
|
|
2990
|
+
(frameState.currentDirectionalShadowView !== null &&
|
|
2991
|
+
(lights.lightViewProj?.length ?? 0) > 0 &&
|
|
2992
|
+
(internals.getPipelineState()?.perPassResources.shadowMapSize ?? 0) > 0);
|
|
2993
|
+
if (submitted && directionalShadowReady) {
|
|
2994
|
+
lastDirectionalShadowCandidate = 'accepted';
|
|
2995
|
+
} else if (directionalQuality !== undefined) {
|
|
2996
|
+
lastDirectionalShadowError = {
|
|
2997
|
+
code: 'directional-shadow-candidate-failed',
|
|
2998
|
+
expected:
|
|
2999
|
+
'the extracted Directional CSM candidate has a valid graph target and fitted matrices',
|
|
3000
|
+
hint: 'inspect the retained LKG and retry after the graph or resource owner recovers',
|
|
3001
|
+
detail: {
|
|
3002
|
+
submitted,
|
|
3003
|
+
hasShadowView: frameState.currentDirectionalShadowView !== null,
|
|
3004
|
+
matrixCount: lights.lightViewProj?.length ?? 0,
|
|
3005
|
+
shadowMapSize: internals.getPipelineState()?.perPassResources.shadowMapSize ?? 0,
|
|
3006
|
+
},
|
|
3007
|
+
};
|
|
3008
|
+
}
|
|
1995
3009
|
if (submitted) {
|
|
1996
3010
|
const temporalCommit = persistentRenderScene.commitTemporalFrame();
|
|
1997
3011
|
if (!temporalCommit.ok) {
|
|
@@ -2127,9 +3141,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2127
3141
|
profileConfig === undefined
|
|
2128
3142
|
? config
|
|
2129
3143
|
: {
|
|
2130
|
-
|
|
2131
|
-
? { clusterGrid: DEFAULT_CLUSTER_GRID }
|
|
2132
|
-
: {}),
|
|
3144
|
+
clusterGrid: DEFAULT_CLUSTER_GRID,
|
|
2133
3145
|
...(profileConfig.ssao ? { ssao: { enabled: true } } : {}),
|
|
2134
3146
|
...config,
|
|
2135
3147
|
};
|
|
@@ -2154,16 +3166,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2154
3166
|
// is a rare boot/swap event, never a per-frame cost.
|
|
2155
3167
|
installEpoch += 1;
|
|
2156
3168
|
frameState.installedPipelineHandle = installEpoch;
|
|
2157
|
-
// The installed profile may request clustered lighting, but the actual
|
|
2158
|
-
// graph lane is capability-gated. On WebGL2 (including WebKit's wgpu
|
|
2159
|
-
// fallback) the forward lane is selected when compute/storage buffers
|
|
2160
|
-
// are unavailable; keep the frame bindings on that same lane instead of
|
|
2161
|
-
// advertising HDRP's clustered bind-group layout to URP pipelines.
|
|
2162
|
-
frameState.isHdrpActive =
|
|
2163
|
-
resolveStandardLane(internals.standardProfile ?? DEFAULT_STANDARD_PROFILE, {
|
|
2164
|
-
compute: internals.device.caps.compute,
|
|
2165
|
-
storageBuffer: internals.device.caps.storageBuffer,
|
|
2166
|
-
}) === 'clustered';
|
|
2167
3169
|
frameState.installedPipelineConfig = resolvedConfig;
|
|
2168
3170
|
},
|
|
2169
3171
|
registerBuiltinPostProcess(id: string, entry: PostProcessShaderEntry): () => void {
|
|
@@ -2207,6 +3209,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2207
3209
|
}
|
|
2208
3210
|
builtinPostProcessEntries.set(id, entry);
|
|
2209
3211
|
if (paramsBuffer !== undefined) postProcessParamsBuffers.set(id, paramsBuffer);
|
|
3212
|
+
const moduleLabel = postProcessShaderModuleLabel(id, entry.source);
|
|
2210
3213
|
return () => {
|
|
2211
3214
|
if (builtinPostProcessEntries.get(id) === entry) {
|
|
2212
3215
|
builtinPostProcessEntries.delete(id);
|
|
@@ -2216,9 +3219,8 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2216
3219
|
internals.device.destroyBuffer(current);
|
|
2217
3220
|
postProcessParamsBuffers.delete(id);
|
|
2218
3221
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
}
|
|
3222
|
+
clearPostProcessPipelineCache(id);
|
|
3223
|
+
internals.invalidateShaderModule?.(moduleLabel);
|
|
2222
3224
|
};
|
|
2223
3225
|
} catch (cause) {
|
|
2224
3226
|
if (paramsBuffer !== undefined) internals.device.destroyBuffer(paramsBuffer);
|
|
@@ -2226,16 +3228,24 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2226
3228
|
}
|
|
2227
3229
|
},
|
|
2228
3230
|
disposeFrameState(): void {
|
|
3231
|
+
(internals as TimingSessionHost).gpuPassTimingSession?.dispose();
|
|
3232
|
+
(internals as TimingSessionHost).gpuPassTimingSession = undefined;
|
|
3233
|
+
(internals as TimingSessionHost).gpuPassTimingCapture = undefined;
|
|
3234
|
+
(internals as TimingSessionHost).gpuPassTimingSubmittedWork = undefined;
|
|
3235
|
+
(internals as TimingSessionHost).gpuPassTimingFrameIdentity = undefined;
|
|
3236
|
+
(internals as TimingSessionHost).gpuPassTimingBeginReason = undefined;
|
|
2229
3237
|
persistentRenderScene.dispose();
|
|
2230
3238
|
gpuDrivenProduction.dispose();
|
|
2231
3239
|
disposeFeatureGpuWork();
|
|
3240
|
+
reflectionProbeOwner.dispose();
|
|
2232
3241
|
// Bloom generations are renderer-owned rather than graph-owned. Drain
|
|
2233
3242
|
// candidate, active, and fence-retiring bundles before the rest of the
|
|
2234
3243
|
// frame state is torn down; the owner callback is idempotent so the
|
|
2235
3244
|
// outer Renderer.dispose cascade can safely call this once only.
|
|
2236
3245
|
internals.getPipelineState()?.perPassResources.drainBloomResources?.();
|
|
2237
3246
|
internals.clearPostProcessPipelineCache?.('forgeax.taa-resolve');
|
|
2238
|
-
|
|
3247
|
+
invalidatePostProcessModule('forgeax.taa-resolve');
|
|
3248
|
+
resetHdrpBuffers(internals);
|
|
2239
3249
|
// Retire graph-owned resources and dispose instance-buffer caches.
|
|
2240
3250
|
// Both calls are idempotent + tolerate per-handle errors silently;
|
|
2241
3251
|
// the Renderer.dispose() cascade owns the surrounding try/catch
|
|
@@ -2276,9 +3286,14 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2276
3286
|
frameState.volumetricFogHistoryGraph = null;
|
|
2277
3287
|
frameState.volumetricFogHistorySlot = null;
|
|
2278
3288
|
frameState.volumetricFogHistorySignature = null;
|
|
3289
|
+
frameState.hdrpClusterMembership = null;
|
|
2279
3290
|
const compiled = frameState.compiledFrameGraph;
|
|
2280
3291
|
frameState.compiledFrameGraph = null;
|
|
2281
3292
|
frameState.compiledFrameGraphTopologyKey = null;
|
|
3293
|
+
frameState.perFrameGraph = null;
|
|
3294
|
+
frameState.standardLightingGraphSignature = '';
|
|
3295
|
+
frameState.standardLightingInspection = undefined;
|
|
3296
|
+
frameState.pointShadowInspection = undefined;
|
|
2282
3297
|
if (compiled !== null) compiled.retire().catch(() => undefined);
|
|
2283
3298
|
for (const retired of frameState.retiredCompiledFrameGraphs) {
|
|
2284
3299
|
retired.retire().catch(() => undefined);
|
|
@@ -2313,6 +3328,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2313
3328
|
lastTransmissionKey = '';
|
|
2314
3329
|
transmissionCapabilityGeneration = -1;
|
|
2315
3330
|
transmissionCapability = undefined;
|
|
3331
|
+
// The replacement device cannot consume buffers minted by the lost
|
|
3332
|
+
// device. Drop the shared Standard Cluster bundle before the next
|
|
3333
|
+
// frame asks for a new generation; resetHdrpBuffers fences the bundle
|
|
3334
|
+
// using its own device/queue instead of the replacement runtime device.
|
|
3335
|
+
resetHdrpBuffers(internals);
|
|
2316
3336
|
// Keep the persistent material demand observable across device loss.
|
|
2317
3337
|
// The physical resource has already been fenced by markDeviceLost(); the
|
|
2318
3338
|
// detached inspection now exposes the rebuild-required lifecycle until a
|
|
@@ -2322,6 +3342,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2322
3342
|
gpuDrivenProduction.dispose();
|
|
2323
3343
|
gpuDrivenProduction = new GpuDrivenProduction(internals.device, gpuDrivenShaderFactory);
|
|
2324
3344
|
disposeFeatureGpuWork();
|
|
3345
|
+
reflectionProbeOwner.dispose();
|
|
2325
3346
|
// A recovered device cannot retain handles from the lost device. The
|
|
2326
3347
|
// Bloom owner abandons candidates and retires every committed generation
|
|
2327
3348
|
// before the new PipelineState is rebuilt.
|
|
@@ -2351,6 +3372,13 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2351
3372
|
frameState.volumetricFogParamsAcceptedSlot = null;
|
|
2352
3373
|
frameState.volumetricFogAcceptedParams = undefined;
|
|
2353
3374
|
frameState.volumetricFogPendingParams = undefined;
|
|
3375
|
+
// Do not expose graph-owned texture/descriptor accessors from the lost
|
|
3376
|
+
// device while recovery is between generations. Inspection and capture
|
|
3377
|
+
// must remain detached until the replacement graph is accepted.
|
|
3378
|
+
frameState.perFrameGraph = null;
|
|
3379
|
+
frameState.standardLightingGraphSignature = '';
|
|
3380
|
+
frameState.standardLightingInspection = undefined;
|
|
3381
|
+
frameState.pointShadowInspection = undefined;
|
|
2354
3382
|
frameState.directionalShadowCache = null;
|
|
2355
3383
|
frameState.directionalShadowCacheRecorded = false;
|
|
2356
3384
|
frameState.currentFrameObservationSource = undefined;
|
|
@@ -2369,6 +3397,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2369
3397
|
retireTemporalGpuState(retiring);
|
|
2370
3398
|
}
|
|
2371
3399
|
frameState.retiringTemporalGpuStates.clear();
|
|
3400
|
+
frameState.hdrpClusterMembership = null;
|
|
2372
3401
|
for (const retired of frameState.retiredCompiledFrameGraphs) {
|
|
2373
3402
|
retired.retire().catch(() => undefined);
|
|
2374
3403
|
}
|