@forgeax/engine-render 0.1.27 → 0.1.29
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 +842 -124
- package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
- package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-host.d.ts +52 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-runtime.d.ts +20 -0
- package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +3 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/gpu-driven-owner.d.ts +10 -0
- package/dist/assembly/gpu-driven-owner.d.ts.map +1 -0
- package/dist/assembly/host-contract.d.ts +19 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +7 -0
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material-shader-policy.d.ts +3 -1
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/recovery/failure-location.d.ts +12 -0
- package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts +6 -4
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts +5 -0
- package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -0
- package/dist/assembly/renderer-frame-transaction.d.ts +19 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-helpers.d.ts +3 -0
- package/dist/assembly/renderer-helpers.d.ts.map +1 -1
- package/dist/assembly/renderer-inspection.d.ts +4 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +6 -0
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
- package/dist/assembly/skin-palette-owner.d.ts +8 -0
- package/dist/assembly/skin-palette-owner.d.ts.map +1 -0
- package/dist/assembly/ssr-shader-prewarm.d.ts +6 -0
- package/dist/assembly/ssr-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-device-loss.d.ts +15 -0
- package/dist/assembly/webgpu-device-loss.d.ts.map +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts +38 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-mipmap.d.ts +11 -0
- package/dist/assembly/webgpu-ready-mipmap.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +7 -3
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts +10 -0
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-contract.d.ts +62 -0
- package/dist/assembly/webgpu-renderer-contract.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-guards.d.ts +9 -0
- package/dist/assembly/webgpu-renderer-guards.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-observation.d.ts +6 -0
- package/dist/assembly/webgpu-renderer-observation.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-recovery-failure.d.ts +12 -0
- package/dist/assembly/webgpu-renderer-recovery-failure.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +7 -141
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/assembly/webgpu-vertex-layouts.d.ts +28 -84
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
- package/dist/authoring.mjs +3 -3
- package/dist/{chunk-ADHHVYLW.mjs → chunk-33XZFTQU.mjs} +245 -39
- package/dist/chunk-33XZFTQU.mjs.map +1 -0
- package/dist/{chunk-GF523LHF.mjs → chunk-BLXGONR2.mjs} +30 -12
- package/dist/chunk-BLXGONR2.mjs.map +1 -0
- package/dist/{chunk-KM2NOX2I.mjs → chunk-BQVQOQJH.mjs} +958 -254
- package/dist/chunk-BQVQOQJH.mjs.map +1 -0
- package/dist/{chunk-HKXTW355.mjs → chunk-BTWH7BP4.mjs} +386 -17
- package/dist/chunk-BTWH7BP4.mjs.map +1 -0
- package/dist/{chunk-TMIASV2N.mjs → chunk-FUODTTIA.mjs} +3934 -676
- package/dist/chunk-FUODTTIA.mjs.map +1 -0
- package/dist/{chunk-FJ6P52EE.mjs → chunk-ILQ5MEZF.mjs} +3 -17
- package/dist/chunk-ILQ5MEZF.mjs.map +1 -0
- package/dist/{chunk-E34VL5VI.mjs → chunk-JQTSEI3Z.mjs} +3 -3
- package/dist/{chunk-E34VL5VI.mjs.map → chunk-JQTSEI3Z.mjs.map} +1 -1
- package/dist/{chunk-GE3SDD72.mjs → chunk-NICXSHAT.mjs} +16 -38
- package/dist/chunk-NICXSHAT.mjs.map +1 -0
- package/dist/{chunk-ZZ4YQ474.mjs → chunk-NRNXBLIJ.mjs} +577 -7
- package/dist/chunk-NRNXBLIJ.mjs.map +1 -0
- package/dist/{chunk-JDWARUOI.mjs → chunk-PA5DRJNI.mjs} +17 -3
- package/dist/chunk-PA5DRJNI.mjs.map +1 -0
- package/dist/{chunk-XDP6XE5V.mjs → chunk-S4DNKQYQ.mjs} +1118 -446
- package/dist/chunk-S4DNKQYQ.mjs.map +1 -0
- package/dist/components/camera.d.ts +62 -2
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +6 -20
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/scene-instance.d.ts +2 -1
- package/dist/components/scene-instance.d.ts.map +1 -1
- package/dist/components/screen-space-reflection.d.ts +14 -0
- package/dist/components/screen-space-reflection.d.ts.map +1 -0
- package/dist/construct-renderer.mjs +13805 -7186
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +11 -17
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/device/mesh-residency-lifetime.d.ts +21 -0
- package/dist/device/mesh-residency-lifetime.d.ts.map +1 -0
- package/dist/dynamic-geometry.d.ts +125 -0
- package/dist/dynamic-geometry.d.ts.map +1 -0
- package/dist/environment/background.d.ts +6 -0
- package/dist/environment/background.d.ts.map +1 -0
- package/dist/errors/gpu-driven.d.ts +6 -3
- package/dist/errors/gpu-driven.d.ts.map +1 -1
- package/dist/errors/render.d.ts +7 -4
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +33 -3
- package/dist/extract/gpu-driven.d.ts.map +1 -1
- package/dist/features/host.d.ts +23 -1
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/noise-texture.d.ts +17 -0
- package/dist/features/noise-texture.d.ts.map +1 -0
- package/dist/features/plan.d.ts +83 -2
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +37 -4
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts.map +1 -1
- package/dist/features/prepared-graphics.d.ts +5 -0
- package/dist/features/prepared-graphics.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts +5 -3
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +11 -3
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +19 -3
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/types.d.ts +11 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +2 -0
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +34 -1
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/pbr-program.d.ts +10 -0
- package/dist/gpu-driven/pbr-program.d.ts.map +1 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +144 -23
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-views.d.ts +126 -0
- package/dist/gpu-driven/shadow-views.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +18 -2
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts +2 -8
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +14 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-texture-usage.d.ts +1 -0
- package/dist/gpu-texture-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +6 -0
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/frame-resources.d.ts +8 -0
- package/dist/ibl/frame-resources.d.ts.map +1 -0
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +21 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +8 -7
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +164 -0
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances-derived-bounds.d.ts +11 -0
- package/dist/instances-derived-bounds.d.ts.map +1 -1
- package/dist/instances.d.ts +89 -44
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +118 -10
- package/dist/internal.mjs.map +1 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/material-row.d.ts +5 -0
- package/dist/material-row.d.ts.map +1 -0
- package/dist/materials.d.ts +15 -0
- package/dist/materials.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +21 -5
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-output/auto-exposure/capability.d.ts +12 -0
- package/dist/pipeline/standard-output/auto-exposure/capability.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts +70 -0
- package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/graph.d.ts +75 -0
- package/dist/pipeline/standard-output/auto-exposure/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts +111 -0
- package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts +16 -0
- package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/preset.d.ts +14 -0
- package/dist/pipeline/standard-output/auto-exposure/preset.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/state.d.ts +46 -0
- package/dist/pipeline/standard-output/auto-exposure/state.d.ts.map +1 -0
- package/dist/pipeline/standard-output/color-transform.d.ts +27 -0
- package/dist/pipeline/standard-output/color-transform.d.ts.map +1 -0
- package/dist/pipeline/standard-output/graph.d.ts +15 -0
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-admission.d.ts +29 -0
- package/dist/pipeline/standard-output/lut-admission.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-gpu.d.ts +48 -0
- package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-state.d.ts +68 -0
- package/dist/pipeline/standard-output/lut-state.d.ts.map +1 -0
- package/dist/pipeline/standard-output/resources.d.ts +10 -0
- package/dist/pipeline/standard-output/resources.d.ts.map +1 -0
- package/dist/pipeline/standard-output/types.d.ts +39 -0
- package/dist/pipeline/standard-output/types.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +2 -2
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +2 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec-types.d.ts +4 -0
- package/dist/pipeline-spec-types.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +1 -9
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/points-lines/standard-owner.d.ts +1 -1
- package/dist/points-lines/standard-owner.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +6 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/dynamic-geometry-consumption.d.ts +15 -0
- package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +3 -3
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +106 -5
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +39 -4
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +7 -8
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +38 -1
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +2 -10
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +12 -2
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +15 -6
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/prepared-material-bindings.d.ts +19 -0
- package/dist/record/prepared-material-bindings.d.ts.map +1 -0
- package/dist/record/recovery-pipeline.d.ts +2 -9
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +76 -26
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +62 -7
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +35 -7
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +1 -0
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +10 -2
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/recovery/render-system-roots.d.ts +11 -0
- package/dist/recovery/render-system-roots.d.ts.map +1 -0
- package/dist/recovery/types.d.ts +76 -0
- package/dist/recovery/types.d.ts.map +1 -0
- package/dist/reflection/record-owner.d.ts +0 -2
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +185 -30
- 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 +64 -5
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +7 -5
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +101 -41
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-projections.d.ts +22 -0
- package/dist/render-system-projections.d.ts.map +1 -0
- package/dist/render-system.d.ts +34 -77
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +14 -7
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +29 -11
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/shadow-visibility.d.ts +5 -0
- package/dist/scene/shadow-visibility.d.ts.map +1 -0
- package/dist/ssr/admission.d.ts +70 -1
- package/dist/ssr/admission.d.ts.map +1 -1
- package/dist/ssr/compose.d.ts +13 -0
- package/dist/ssr/compose.d.ts.map +1 -0
- package/dist/ssr/errors.d.ts +35 -0
- package/dist/ssr/errors.d.ts.map +1 -1
- package/dist/ssr/graph.d.ts +60 -0
- package/dist/ssr/graph.d.ts.map +1 -0
- package/dist/ssr/history.d.ts +97 -0
- package/dist/ssr/history.d.ts.map +1 -0
- package/dist/ssr/hiz.d.ts +37 -0
- package/dist/ssr/hiz.d.ts.map +1 -0
- package/dist/ssr/inspection.d.ts +42 -0
- package/dist/ssr/inspection.d.ts.map +1 -0
- package/dist/ssr/resources.d.ts +58 -0
- package/dist/ssr/resources.d.ts.map +1 -0
- package/dist/ssr/temporal.d.ts +38 -0
- package/dist/ssr/temporal.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts +32 -10
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +15 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -1
- package/dist/systems/transparent-dispatch.d.ts +9 -0
- package/dist/systems/transparent-dispatch.d.ts.map +1 -0
- package/dist/targets/material-source.d.ts +7 -0
- package/dist/targets/material-source.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts +7 -0
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts +8 -0
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/history.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -6
- package/dist/temporal/inspection.d.ts.map +1 -1
- package/dist/temporal/standard-scene-data.d.ts +2 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/temporal-view.d.ts +2 -0
- package/dist/temporal/temporal-view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +35 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +3 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +6 -0
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +21 -20
- package/src/__tests__/auto-exposure-gpu-evidence.ts +291 -0
- package/src/__tests__/auto-exposure-inspection.unit.test.ts +80 -0
- package/src/__tests__/auto-exposure-transaction.integration.test.ts +99 -0
- package/src/__tests__/auto-exposure.browser.test.ts +33 -0
- package/src/__tests__/auto-exposure.dawn.test.ts +33 -0
- package/src/__tests__/detached-inspection.integration.test.ts +5 -0
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +18 -0
- package/src/__tests__/device-scope-stale-matrix.unit.test.ts +18 -0
- package/src/__tests__/docs-gate-green.unit.test.ts +3 -3
- package/src/__tests__/dynamic-geometry-host.unit.test.ts +1123 -0
- package/src/__tests__/dynamic-geometry.unit.test.ts +233 -0
- package/src/__tests__/factory-contract.integration.test.ts +73 -22
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +19 -0
- package/src/__tests__/feature-depth-input.dawn.test.ts +121 -0
- package/src/__tests__/feature-noise.unit.test.ts +38 -0
- package/src/__tests__/fixtures/taa-resolve-pinned-baseline.wgsl +301 -0
- package/src/__tests__/frame-camera-selection.unit.test.ts +131 -0
- package/src/__tests__/frame-plan-contract.unit.test.ts +24 -0
- package/src/__tests__/frame-targets.unit.test.ts +24 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +3 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +79 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +287 -6
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +122 -142
- package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +10 -1
- package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +264 -0
- package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +349 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1417 -0
- package/src/__tests__/gpu-driven-pbr.integration.test.ts +21 -0
- package/src/__tests__/gpu-driven-production-fixture.ts +96 -0
- package/src/__tests__/gpu-driven-production.integration.test.ts +1075 -93
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +1 -3
- package/src/__tests__/gpu-driven-shadow-views.browser.test.ts +63 -0
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +294 -0
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +97 -0
- package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +168 -0
- package/src/__tests__/gpu-driven-skin-contract.test-d.ts +11 -0
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +72 -20
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +30 -9
- package/src/__tests__/gpu-driven-view.unit.test.ts +159 -52
- package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -1
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +42 -0
- package/src/__tests__/gpu-scene-render-graph-gpu.ts +19 -5
- package/src/__tests__/gpu-scene.dawn.test.ts +40 -13
- package/src/__tests__/gpu-scene.unit.test.ts +559 -33
- package/src/__tests__/instances-culling.integration.test.ts +36 -10
- package/src/__tests__/instances-derived-bounds.unit.test.ts +54 -0
- package/src/__tests__/instances-store.unit.test.ts +242 -39
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +6 -0
- package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +13 -16
- package/src/__tests__/lut-lkg-transaction.integration.test.ts +122 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +5 -6
- package/src/__tests__/material-dispatch-shader-id.unit.test.ts +51 -0
- package/src/__tests__/material-lane-selection.unit.test.ts +60 -0
- package/src/__tests__/material-numeric-upload.unit.test.ts +53 -0
- package/src/__tests__/material-texture-handles.unit.test.ts +18 -0
- package/src/__tests__/material-texture-source.unit.test.ts +87 -0
- package/src/__tests__/materials-authoring-validation.unit.test.ts +41 -0
- package/src/__tests__/materials-standard-contract.unit.test.ts +52 -2
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +53 -0
- package/src/__tests__/normal-fallback.dawn.test.ts +122 -0
- package/src/__tests__/particle-material-input-layout.unit.test.ts +104 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +37 -3
- package/src/__tests__/pipeline-texture-specialization.unit.test.ts +138 -0
- package/src/__tests__/point-shadow-inspection.unit.test.ts +2 -0
- package/src/__tests__/prepared-gpu-driven-pbr-contract.test-d.ts +31 -0
- package/src/__tests__/prepared-gpu-driven-pbr-contract.unit.test.ts +39 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +35 -4
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +21 -0
- package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +6 -2
- package/src/__tests__/profiler-phase-catalog.test.ts +5 -0
- package/src/__tests__/public-surface.test-d.ts +3 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +30 -12
- package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +104 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +2 -1
- package/src/__tests__/render-error-code-owner.test-d.ts +4 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +22 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
- package/src/__tests__/render-feature-gpu-work.integration.test.ts +285 -2
- package/src/__tests__/render-feature-plan.unit.test.ts +245 -0
- package/src/__tests__/render-feature-shadow-order.unit.test.ts +228 -0
- package/src/__tests__/render-feature-zero-work.unit.test.ts +35 -1
- package/src/__tests__/render-graph-fallback.integration.test.ts +2 -2
- package/src/__tests__/render-owner-cohesion.unit.test.ts +55 -0
- package/src/__tests__/render-scene-mixed-updates.integration.test.ts +426 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +914 -23
- package/src/__tests__/render-scene-resource-updates.integration.test.ts +214 -0
- package/src/__tests__/render-scene-skin-lifecycle.integration.test.ts +173 -0
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +133 -0
- package/src/__tests__/render-target-material-source.test-d.ts +27 -3
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +61 -1
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
- package/src/__tests__/shader-manifest-fixture.ts +39 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +0 -33
- package/src/__tests__/skin-palette-persistent.integration.test.ts +28 -0
- package/src/__tests__/skin-palette-persistent.unit.test.ts +247 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +118 -14
- package/src/__tests__/specular-environment-scale.dawn.test.ts +156 -0
- package/src/__tests__/ssr-admission-matrix.integration.test.ts +7 -1
- package/src/__tests__/ssr-confidence-compose.unit.test.ts +84 -0
- package/src/__tests__/ssr-coordinate-oracle.unit.test.ts +28 -0
- package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +91 -0
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +112 -0
- package/src/__tests__/ssr-gpu-dispatch.ts +946 -0
- package/src/__tests__/ssr-graph-topology.unit.test.ts +104 -0
- package/src/__tests__/ssr-history-filter.dawn.test.ts +198 -0
- package/src/__tests__/ssr-hit-color.dawn.test.ts +273 -0
- package/src/__tests__/ssr-hiz-odd-reduction.dawn.test.ts +569 -0
- package/src/__tests__/ssr-hiz-rescue.dawn.test.ts +279 -0
- package/src/__tests__/ssr-hiz.browser.test.ts +150 -0
- package/src/__tests__/ssr-hiz.dawn.test.ts +150 -0
- package/src/__tests__/ssr-hiz.unit.test.ts +56 -0
- package/src/__tests__/ssr-inspection.unit.test.ts +105 -0
- package/src/__tests__/ssr-memory-budget.unit.test.ts +45 -0
- package/src/__tests__/ssr-performance-sequence-aggregation.unit.test.ts +15 -0
- package/src/__tests__/ssr-public-surface.test-d.ts +37 -0
- package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +387 -0
- package/src/__tests__/ssr-reflection-mip-reducer.dawn.test.ts +309 -0
- package/src/__tests__/ssr-reprojection-probe.ts +90 -0
- package/src/__tests__/ssr-roughness-mip.dawn.test.ts +163 -0
- package/src/__tests__/ssr-shader-prewarm.unit.test.ts +59 -0
- package/src/__tests__/ssr-spatial.integration.test.ts +141 -0
- package/src/__tests__/ssr-stable-lattice.dawn.test.ts +475 -0
- package/src/__tests__/ssr-temporal-history.unit.test.ts +202 -0
- package/src/__tests__/ssr-texel-crossing.dawn.test.ts +448 -0
- package/src/__tests__/ssr-zero-work.integration.test.ts +87 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +44 -12
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +29 -0
- package/src/__tests__/standard-output-chain-contract.unit.test.ts +99 -0
- package/src/__tests__/standard-output-chain.integration.test.ts +73 -0
- package/src/__tests__/standard-output-gpu-evidence.ts +205 -0
- package/src/__tests__/standard-output.browser.test.ts +15 -0
- package/src/__tests__/standard-output.dawn.test.ts +15 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +38 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
- package/src/__tests__/standard-profile-oracle.unit.test.ts +22 -0
- package/src/__tests__/standard-surface-lighting.unit.test.ts +41 -0
- package/src/__tests__/standard-texture-specialization.dawn.test.ts +141 -0
- package/src/__tests__/surface-adjacent-regressions.integration.test.ts +6 -3
- package/src/__tests__/taa-coverage-edge.dawn.test.ts +193 -0
- package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
- package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +778 -0
- package/src/__tests__/taa-stability-feedback.browser.test.ts +23 -0
- package/src/__tests__/taa-stability-feedback.dawn.test.ts +68 -0
- package/src/__tests__/taa-stability-feedback.ts +341 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +49 -7
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +9 -0
- package/src/__tests__/temporal-transaction.integration.test.ts +28 -0
- package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +109 -4
- package/src/__tests__/typed-scene-pass-material-cache.integration.test.ts +126 -0
- package/src/__tests__/vertex-layouts-ssot.unit.test.ts +47 -0
- package/src/__tests__/visibility-extract.perf.test.ts +20 -8
- package/src/__tests__/visibility-instances.integration.test.ts +19 -7
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +20 -9
- package/src/assembly/atmosphere-shader-prewarm.ts +38 -0
- package/src/assembly/dynamic-geometry-host.ts +887 -0
- package/src/assembly/dynamic-geometry-runtime.ts +71 -0
- package/src/assembly/factory.ts +4 -0
- package/src/assembly/gpu-driven-owner.ts +29 -0
- package/src/assembly/host-contract.ts +36 -0
- package/src/assembly/material/assembly.ts +38 -0
- package/src/assembly/material-shader-policy.ts +71 -28
- package/src/assembly/recovery/failure-location.ts +62 -0
- package/src/assembly/recovery/renderer-recover.ts +19 -17
- package/src/assembly/renderer-facade.ts +29 -0
- package/src/assembly/renderer-feature-inspection.ts +13 -0
- package/src/assembly/renderer-frame-transaction.ts +63 -0
- package/src/assembly/renderer-helpers.ts +26 -0
- package/src/assembly/renderer-inspection.ts +6 -0
- package/src/assembly/shader-prewarm-policy.ts +32 -0
- package/src/assembly/skin-palette-owner.ts +21 -0
- package/src/assembly/ssr-shader-prewarm.ts +41 -0
- package/src/assembly/webgpu-device-loss.ts +75 -0
- package/src/assembly/webgpu-pbr-ready.ts +232 -0
- package/src/assembly/webgpu-ready-mipmap.ts +26 -0
- package/src/assembly/webgpu-ready.ts +142 -121
- package/src/assembly/webgpu-renderer-bootstrap.ts +85 -0
- package/src/assembly/webgpu-renderer-contract.ts +69 -0
- package/src/assembly/webgpu-renderer-guards.ts +32 -0
- package/src/assembly/webgpu-renderer-observation.ts +43 -0
- package/src/assembly/webgpu-renderer-recovery-failure.ts +62 -0
- package/src/assembly/webgpu-renderer.ts +675 -614
- package/src/assembly/webgpu-vertex-layouts.ts +233 -87
- package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
- package/src/components/camera.ts +287 -7
- package/src/components/index.ts +1 -0
- package/src/components/instances.ts +45 -18
- package/src/components/scene-instance.ts +2 -1
- package/src/components/screen-space-reflection.ts +21 -0
- package/src/components/sprite-animation.ts +3 -3
- package/src/components/sprite-instances.ts +1 -1
- package/src/device/gpu-residency.ts +96 -93
- package/src/device/mesh-residency-lifetime.ts +73 -0
- package/src/dynamic-geometry.ts +736 -0
- package/src/environment/background.ts +263 -0
- package/src/errors/gpu-driven.ts +39 -5
- package/src/errors/render.ts +27 -3
- package/src/extract/camera.ts +26 -1
- package/src/extract/gpu-driven.ts +185 -10
- package/src/features/host.ts +237 -19
- package/src/features/noise-texture.ts +91 -0
- package/src/features/plan.ts +553 -24
- package/src/features/prepared-gpu-work.ts +631 -49
- package/src/features/prepared-graphics-store.ts +31 -0
- package/src/features/prepared-graphics.ts +5 -0
- package/src/features/render-graph-compute.ts +41 -8
- package/src/features/render-graph-contribution.ts +141 -3
- package/src/features/render-graph-raster.ts +143 -30
- package/src/features/types.ts +11 -0
- package/src/fullscreen-post-process-pass.ts +3 -0
- package/src/gpu-driven/batch-topology.ts +424 -102
- package/src/gpu-driven/pbr-program.ts +31 -0
- package/src/gpu-driven/prepared-draw.ts +29 -4
- package/src/gpu-driven/production-raster.ts +2267 -694
- package/src/gpu-driven/shadow-views.ts +539 -0
- package/src/gpu-driven/view-gpu.ts +188 -41
- package/src/gpu-scene-schema.ts +52 -11
- package/src/gpu-scene.ts +186 -99
- package/src/gpu-texture-usage.ts +1 -0
- package/src/hdrp-buffers.ts +72 -36
- package/src/ibl/frame-resources.ts +38 -0
- package/src/ibl/skylight-bind-group.ts +5 -4
- package/src/index.ts +125 -1
- package/src/inspection-types.ts +191 -0
- package/src/instances-derived-bounds.ts +258 -34
- package/src/instances.ts +459 -150
- package/src/internal.ts +22 -0
- package/src/lifecycle.ts +2 -0
- package/src/material-row.ts +145 -0
- package/src/materials.ts +44 -8
- package/src/pbr-pipeline.ts +159 -17
- package/src/pipeline/standard-forward-lane.ts +52 -24
- package/src/pipeline/standard-output/__tests__/lut-admission.unit.test.ts +103 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/capability.unit.test.ts +40 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/gpu.contract.unit.test.ts +125 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/graph.contract.unit.test.ts +94 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/oracle.test.ts +108 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/preset.test.ts +26 -0
- package/src/pipeline/standard-output/auto-exposure/capability.ts +38 -0
- package/src/pipeline/standard-output/auto-exposure/gpu.ts +567 -0
- package/src/pipeline/standard-output/auto-exposure/graph.ts +221 -0
- package/src/pipeline/standard-output/auto-exposure/inspection.ts +231 -0
- package/src/pipeline/standard-output/auto-exposure/oracle.ts +291 -0
- package/src/pipeline/standard-output/auto-exposure/preset.ts +28 -0
- package/src/pipeline/standard-output/auto-exposure/state.ts +156 -0
- package/src/pipeline/standard-output/color-transform.ts +124 -0
- package/src/pipeline/standard-output/graph.ts +139 -0
- package/src/pipeline/standard-output/lut-admission.ts +121 -0
- package/src/pipeline/standard-output/lut-gpu.ts +399 -0
- package/src/pipeline/standard-output/lut-state.ts +176 -0
- package/src/pipeline/standard-output/resources.ts +121 -0
- package/src/pipeline/standard-output/types.ts +58 -0
- package/src/pipeline/standard-pipeline.ts +192 -59
- package/src/pipeline/standard-post.ts +186 -27
- package/src/pipeline-builder.ts +16 -6
- package/src/pipeline-spec-types.ts +4 -0
- package/src/pipeline-spec.ts +37 -29
- package/src/points-lines/standard-owner.ts +6 -14
- package/src/prepare/prepared-graphics-resolver.ts +9 -2
- package/src/record/__tests__/material-slot-plan.unit.test.ts +48 -0
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +2 -29
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +36 -5
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +45 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +5 -0
- package/src/record/dynamic-geometry-consumption.ts +59 -0
- package/src/record/frame-lighting.ts +12 -11
- package/src/record/frame-snapshot.ts +133 -23
- package/src/record/frame-targets.ts +9 -0
- package/src/record/frame.ts +727 -133
- package/src/record/main-pass-geometry.ts +162 -98
- package/src/record/main-pass-material.ts +115 -26
- package/src/record/main-pass-sprite-draws.ts +18 -107
- package/src/record/main-pass.ts +78 -8
- package/src/record/material-uniforms.ts +1 -1
- package/src/record/mesh-ssbo.ts +59 -15
- package/src/record/prepared-material-bindings.ts +234 -0
- package/src/record/recovery-pipeline.ts +59 -136
- package/src/record/render-context.ts +92 -22
- package/src/record/shadow-pass.ts +1231 -209
- package/src/record/skybox-post-pass.ts +14 -3
- package/src/record/typed-frame-graph.ts +1006 -53
- package/src/record/view-ubo.ts +11 -0
- package/src/recovery/render-system-candidate.ts +90 -66
- package/src/recovery/render-system-roots.ts +113 -0
- package/src/recovery/types.ts +77 -0
- package/src/reflection/record-owner.ts +26 -54
- package/src/render-contract.ts +219 -25
- package/src/render-graph-primitives.ts +6 -2
- package/src/render-pipeline.ts +70 -1
- package/src/render-system-extract-tail.ts +409 -160
- package/src/render-system-extract.ts +376 -102
- package/src/render-system-projections.ts +241 -0
- package/src/render-system.ts +692 -791
- package/src/scene/render-scene-types.ts +13 -4
- package/src/scene/render-scene.ts +569 -355
- package/src/scene/shadow-visibility.ts +21 -0
- package/src/ssr/admission.ts +380 -14
- package/src/ssr/compose.ts +144 -0
- package/src/ssr/errors.ts +48 -0
- package/src/ssr/graph.ts +777 -0
- package/src/ssr/history.ts +436 -0
- package/src/ssr/hiz.ts +124 -0
- package/src/ssr/inspection.ts +124 -0
- package/src/ssr/resources.ts +202 -0
- package/src/ssr/temporal.ts +164 -0
- package/src/systems/skin-palette-allocator.ts +495 -148
- package/src/systems/skin-palette-types.ts +17 -0
- package/src/systems/transparent-dispatch.ts +90 -0
- package/src/targets/material-source.ts +34 -0
- package/src/temporal/coverage.ts +1 -1
- package/src/temporal/frame-coordinator.ts +23 -2
- package/src/temporal/gpu.ts +74 -12
- package/src/temporal/history.ts +3 -1
- package/src/temporal/inspection.ts +2 -1
- package/src/temporal/standard-scene-data.ts +3 -1
- package/src/temporal/temporal-view.ts +3 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +204 -139
- package/src/typed-render-graph-primitives.ts +266 -74
- package/src/typed-shadow-passes.ts +122 -12
- package/src/volume/temporal.ts +10 -0
- package/dist/chunk-ADHHVYLW.mjs.map +0 -1
- package/dist/chunk-FJ6P52EE.mjs.map +0 -1
- package/dist/chunk-GE3SDD72.mjs.map +0 -1
- package/dist/chunk-GF523LHF.mjs.map +0 -1
- package/dist/chunk-HKXTW355.mjs.map +0 -1
- package/dist/chunk-JDWARUOI.mjs.map +0 -1
- package/dist/chunk-KM2NOX2I.mjs.map +0 -1
- package/dist/chunk-TMIASV2N.mjs.map +0 -1
- package/dist/chunk-XDP6XE5V.mjs.map +0 -1
- package/dist/chunk-ZZ4YQ474.mjs.map +0 -1
- package/src/__tests__/instances-world-ownership.integration.test.ts +0 -62
- package/src/scene/__tests__/changed-block-summary.candidate.test.ts +0 -36
package/src/record/frame.ts
CHANGED
|
@@ -5,7 +5,8 @@ import { uploadMaterialUniforms } from './material-uniforms';
|
|
|
5
5
|
import { HANDLE_NINESLICE_QUAD, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
6
6
|
import type { World } from '@forgeax/engine-ecs';
|
|
7
7
|
import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
8
|
-
import {
|
|
8
|
+
import { deriveVertexCount } from '@forgeax/engine-geometry';
|
|
9
|
+
import { frustum, mat4, vec3 } from '@forgeax/engine-math';
|
|
9
10
|
import {
|
|
10
11
|
type CurrentFrameObservationLease,
|
|
11
12
|
createCurrentFrameObservationLease,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
type TextureView,
|
|
22
23
|
} from '@forgeax/engine-rhi';
|
|
23
24
|
import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
|
|
24
|
-
import { handleSlot, toShared } from '@forgeax/engine-types';
|
|
25
|
+
import { BUILTIN_BASE, handleSlot, toShared } from '@forgeax/engine-types';
|
|
25
26
|
import { CUBE_CAMERA_FACE_ORDER } from '../components/cube-camera';
|
|
26
27
|
import type { DirectionalShadowQuality } from '../components/directional-shadow-filter';
|
|
27
28
|
import type { MeshGpuHandles } from '../device/gpu-residency';
|
|
@@ -30,7 +31,9 @@ import {
|
|
|
30
31
|
type ObservationUnavailableReason,
|
|
31
32
|
RendererOperationError,
|
|
32
33
|
} from '../errors/render';
|
|
34
|
+
import { gpuDrivenDrawKey, gpuDrivenSourceDrawItemIndex } from '../extract/gpu-driven';
|
|
33
35
|
import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
|
|
36
|
+
import type { ShadowViewIdentity } from '../gpu-driven/shadow-views';
|
|
34
37
|
import { GpuBuffer } from '../gpu-resource';
|
|
35
38
|
import { GPU_TEXTURE_USAGE_COPY_SRC } from '../gpu-texture-usage';
|
|
36
39
|
import {
|
|
@@ -56,12 +59,22 @@ import type {
|
|
|
56
59
|
ExtractedVolumetricFog,
|
|
57
60
|
MaterialSnapshot,
|
|
58
61
|
RenderableSnapshot,
|
|
62
|
+
ShadowCasterMembership,
|
|
59
63
|
SkyboxSnapshot,
|
|
60
64
|
SkylightSnapshot,
|
|
61
65
|
} from '../render-system-extract';
|
|
62
|
-
import type {
|
|
66
|
+
import type {
|
|
67
|
+
PersistentGpuDrivenState,
|
|
68
|
+
PersistentShadowCasterProjection,
|
|
69
|
+
} from '../scene/render-scene';
|
|
63
70
|
import type { OcclusionFrameProjection } from '../scene/visibility/occlusion-runtime';
|
|
64
71
|
import { SHADOW_ATLAS_DEFAULT_LAYERS } from '../shadow-atlas';
|
|
72
|
+
import {
|
|
73
|
+
admitSsrSpatial,
|
|
74
|
+
type SsrDependenciesInspection,
|
|
75
|
+
type SsrSpatialAdmission,
|
|
76
|
+
} from '../ssr/admission';
|
|
77
|
+
import { createSsrHistoryOwner } from '../ssr/history';
|
|
65
78
|
import {
|
|
66
79
|
getTransparentSortConfig,
|
|
67
80
|
TRANSPARENT_SORT_MODE_LAYER_Y,
|
|
@@ -123,6 +136,66 @@ export type FrameObservationReadback = (
|
|
|
123
136
|
lease: CurrentFrameObservationLease,
|
|
124
137
|
) => Promise<Result<Uint8Array, Error>>;
|
|
125
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Fold World identity into the renderer-owned validation token. The GPU
|
|
141
|
+
* production owner receives only this opaque key; ECS identity and ordering
|
|
142
|
+
* stay in the record owner where the World list is authoritative.
|
|
143
|
+
*/
|
|
144
|
+
function residencyValidationCacheKey(world: World, worlds: readonly World[]): string {
|
|
145
|
+
return [world.identity, ...worlds.map((candidate) => candidate.identity)].join('\u001f');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function renderableIdentity(snapshot: Pick<RenderableSnapshot, 'worldId' | 'entityKey'>): string {
|
|
149
|
+
return `${snapshot.worldId}:${snapshot.entityKey}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* A GPU-owned indirect draw has no CPU submit callback. Keep the same
|
|
154
|
+
* record-stage consumption proof for downstream lifecycle owners by checking
|
|
155
|
+
* the concrete draw-key projection that was actually encoded for this frame.
|
|
156
|
+
*/
|
|
157
|
+
function gpuDrivenRenderableConsumed(
|
|
158
|
+
entry: ValidatedRenderable,
|
|
159
|
+
drawKeys: ReadonlySet<string>,
|
|
160
|
+
): boolean {
|
|
161
|
+
const draws = entry.source.gpuDrivenDraws;
|
|
162
|
+
if (draws === undefined || draws.length === 0) return false;
|
|
163
|
+
const worldEntity = worldEntityKey(entry.source.worldId, entry.source.entityKey);
|
|
164
|
+
return draws.some((draw, compactIndex) => {
|
|
165
|
+
const material = entry.source.materials[draw.materialSlot] ?? entry.source.material;
|
|
166
|
+
return drawKeys.has(
|
|
167
|
+
gpuDrivenDrawKey(
|
|
168
|
+
worldEntity,
|
|
169
|
+
material.materialHandle ?? -1,
|
|
170
|
+
gpuDrivenSourceDrawItemIndex(draw, compactIndex),
|
|
171
|
+
),
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Project one shared validation result into a cull-specific renderable index
|
|
178
|
+
* domain. The retained ShadowCaster projection is the superset; the primary
|
|
179
|
+
* camera submission reuses its resolved mesh rows without a second residency
|
|
180
|
+
* walk or a second validation authority.
|
|
181
|
+
*/
|
|
182
|
+
function projectValidatedRows(
|
|
183
|
+
validated: readonly ValidatedRenderable[],
|
|
184
|
+
target: readonly RenderableSnapshot[],
|
|
185
|
+
): ValidatedRenderable[] {
|
|
186
|
+
const byIdentity = new Map<string, ValidatedRenderable>();
|
|
187
|
+
for (const row of validated) byIdentity.set(renderableIdentity(row.source), row);
|
|
188
|
+
const projected: ValidatedRenderable[] = [];
|
|
189
|
+
for (let index = 0; index < target.length; index += 1) {
|
|
190
|
+
const snapshot = target[index];
|
|
191
|
+
if (snapshot === undefined) continue;
|
|
192
|
+
const row = byIdentity.get(renderableIdentity(snapshot));
|
|
193
|
+
if (row === undefined) continue;
|
|
194
|
+
projected.push({ ...row, source: snapshot, renderableIndex: index });
|
|
195
|
+
}
|
|
196
|
+
return projected;
|
|
197
|
+
}
|
|
198
|
+
|
|
126
199
|
export interface FrameObservationOptions {
|
|
127
200
|
readonly semantic: 'linear-hdr';
|
|
128
201
|
readonly readback: FrameObservationReadback;
|
|
@@ -219,7 +292,8 @@ export async function observeCurrentFrame(
|
|
|
219
292
|
});
|
|
220
293
|
}
|
|
221
294
|
|
|
222
|
-
import {
|
|
295
|
+
import { acknowledgeRenderFeatureSubmission } from '../features/host';
|
|
296
|
+
import { createTemporalView, resolveTemporalReset, type TemporalView } from '../temporal/view';
|
|
223
297
|
import { inspectVolumetricFog } from '../volume/inspection';
|
|
224
298
|
import {
|
|
225
299
|
acquireSwapChainTarget,
|
|
@@ -244,6 +318,7 @@ import {
|
|
|
244
318
|
type CubeCaptureGraphState,
|
|
245
319
|
ensureCompiledFrameGraph,
|
|
246
320
|
executeCompiledFrameGraph,
|
|
321
|
+
getRenderFeatureGraphState,
|
|
247
322
|
type RenderFeatureGraphCandidate,
|
|
248
323
|
settleVolumetricFogGraphCandidate,
|
|
249
324
|
} from './typed-frame-graph';
|
|
@@ -256,35 +331,34 @@ import {
|
|
|
256
331
|
|
|
257
332
|
/**
|
|
258
333
|
* Resolve the GPU mesh view used by the optional production raster before its
|
|
259
|
-
* filtering pass runs.
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
334
|
+
* filtering pass runs. The result is keyed by the retained RenderScene slot,
|
|
335
|
+
* which preserves the World namespace already used by gpuStore. User meshes
|
|
336
|
+
* are pull-resident during record; relying only on the boot-time builtin map
|
|
337
|
+
* would make the first frame classify every imported mesh as a CPU fallback
|
|
338
|
+
* and would hide its LOD ranges from the GPU cull. Builtins are the only
|
|
339
|
+
* process-static entries and may be read from PipelineState.meshes.
|
|
264
340
|
*/
|
|
265
|
-
function gpuDrivenMeshesForFrame(
|
|
341
|
+
export function gpuDrivenMeshesForFrame(
|
|
266
342
|
internals: RenderSystemInternals,
|
|
267
343
|
pipelineState: PipelineState,
|
|
268
344
|
worlds: readonly World[],
|
|
269
345
|
scene: PersistentGpuDrivenState | undefined,
|
|
270
346
|
): ReadonlyMap<number, MeshGpuHandles> {
|
|
271
|
-
const meshes = new Map(
|
|
347
|
+
const meshes = new Map<number, MeshGpuHandles>();
|
|
272
348
|
if (scene === undefined) return meshes;
|
|
273
|
-
const cached = gpuDrivenMeshCache.get(scene.scene);
|
|
274
|
-
if (
|
|
275
|
-
cached !== undefined &&
|
|
276
|
-
cached.meshResidencyEpoch === internals.gpuStore.meshResidencyEpoch &&
|
|
277
|
-
cached.pipelineMeshes === pipelineState.meshes
|
|
278
|
-
) {
|
|
279
|
-
return cached.value;
|
|
280
|
-
}
|
|
281
349
|
for (const slot of scene.slots) {
|
|
282
|
-
const world = worlds[slot.snapshot.worldId];
|
|
283
|
-
if (world === undefined) continue;
|
|
284
350
|
const assetHandle = slot.snapshot.assetHandle;
|
|
285
351
|
const sharedHandle = toShared<'MeshAsset'>(assetHandle);
|
|
352
|
+
const assetSlot = handleSlot(sharedHandle);
|
|
353
|
+
if (assetSlot < BUILTIN_BASE) {
|
|
354
|
+
const gpu = pipelineState.meshes.get(assetSlot);
|
|
355
|
+
if (gpu !== undefined) meshes.set(slot.slot, gpu);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
const world = worlds[slot.snapshot.worldId];
|
|
359
|
+
if (world === undefined) continue;
|
|
286
360
|
let gpu = internals.gpuStore.getMeshGpuHandles(sharedHandle, world);
|
|
287
|
-
if (gpu === undefined
|
|
361
|
+
if (gpu === undefined) {
|
|
288
362
|
const asset = resolveAssetHandle<MeshAsset>(world, sharedHandle);
|
|
289
363
|
if (asset.ok) {
|
|
290
364
|
const lodMeshes = slot.snapshot.lods?.map((lod) =>
|
|
@@ -303,26 +377,11 @@ function gpuDrivenMeshesForFrame(
|
|
|
303
377
|
if (resident.ok) gpu = resident.value;
|
|
304
378
|
}
|
|
305
379
|
}
|
|
306
|
-
if (gpu !== undefined) meshes.set(
|
|
380
|
+
if (gpu !== undefined) meshes.set(slot.slot, gpu);
|
|
307
381
|
}
|
|
308
|
-
|
|
309
|
-
gpuDrivenMeshCache.set(scene.scene, {
|
|
310
|
-
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
311
|
-
pipelineMeshes: pipelineState.meshes,
|
|
312
|
-
value,
|
|
313
|
-
});
|
|
314
|
-
return value;
|
|
382
|
+
return meshes;
|
|
315
383
|
}
|
|
316
384
|
|
|
317
|
-
const gpuDrivenMeshCache = new WeakMap<
|
|
318
|
-
object,
|
|
319
|
-
{
|
|
320
|
-
readonly meshResidencyEpoch: number;
|
|
321
|
-
readonly pipelineMeshes: ReadonlyMap<number, MeshGpuHandles>;
|
|
322
|
-
readonly value: ReadonlyMap<number, MeshGpuHandles>;
|
|
323
|
-
}
|
|
324
|
-
>();
|
|
325
|
-
|
|
326
385
|
/**
|
|
327
386
|
* A frame-local material table. Repeated snapshot objects share one UBO slot;
|
|
328
387
|
* each renderable keeps only the slot index used by each authored material.
|
|
@@ -392,6 +451,34 @@ export function materialSlotCountForPrefix(
|
|
|
392
451
|
return maxSlot + 1;
|
|
393
452
|
}
|
|
394
453
|
|
|
454
|
+
type MeshSsboCapacityResult = ReturnType<typeof ensureMeshSsboCapacity>;
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Resolve the actual mesh-SSBO ceiling after the shared grow contract ran.
|
|
458
|
+
* A missing grow controller is the legacy/test surface with no renderer-owned
|
|
459
|
+
* ceiling; a failed grow explicitly exposes the pre-grow capacity and must
|
|
460
|
+
* constrain every row written by this frame, including retained shadow rows.
|
|
461
|
+
*/
|
|
462
|
+
function meshSsboSlotCapacityAfterEnsure(
|
|
463
|
+
internals: Pick<RenderSystemInternals, 'growMeshSsbo' | 'meshSsboState'>,
|
|
464
|
+
neededSlots: number,
|
|
465
|
+
result: MeshSsboCapacityResult,
|
|
466
|
+
): number {
|
|
467
|
+
if (internals.growMeshSsbo === undefined) return Number.POSITIVE_INFINITY;
|
|
468
|
+
if (!result.ok) return Math.max(0, result.degradedToSlotCount);
|
|
469
|
+
return internals.meshSsboState?.slotCount ?? neededSlots;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Keep a row sequence inside the capacity published by the mesh-SSBO owner.
|
|
474
|
+
* The caller has already reserved earlier rows (the primary view), so this
|
|
475
|
+
* helper only accepts the remaining slots for the retained shadow sequence.
|
|
476
|
+
*/
|
|
477
|
+
function limitRowsToMeshSsboCapacity<T>(rows: readonly T[], availableSlots: number): readonly T[] {
|
|
478
|
+
if (availableSlots === Number.POSITIVE_INFINITY) return rows;
|
|
479
|
+
return rows.slice(0, Math.max(0, Math.floor(availableSlots)));
|
|
480
|
+
}
|
|
481
|
+
|
|
395
482
|
export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T) => T;
|
|
396
483
|
|
|
397
484
|
export interface CubeCaptureFrameInput {
|
|
@@ -456,6 +543,18 @@ function prepareCubeCapture(
|
|
|
456
543
|
}
|
|
457
544
|
|
|
458
545
|
const EMPTY_ENTITY_KEYS: ReadonlySet<number> = new Set<number>();
|
|
546
|
+
const EMPTY_DRAW_KEYS: ReadonlySet<string> = new Set<string>();
|
|
547
|
+
|
|
548
|
+
function cacheEntityKeysFromDrawKeys(drawKeys: ReadonlySet<string>): ReadonlySet<number> {
|
|
549
|
+
const entityKeys = new Set<number>();
|
|
550
|
+
for (const key of drawKeys) {
|
|
551
|
+
const separator = key.indexOf(':');
|
|
552
|
+
if (separator <= 0) continue;
|
|
553
|
+
const entityKey = Number(key.slice(0, separator));
|
|
554
|
+
if (Number.isSafeInteger(entityKey)) entityKeys.add(entityKey);
|
|
555
|
+
}
|
|
556
|
+
return entityKeys;
|
|
557
|
+
}
|
|
459
558
|
|
|
460
559
|
function runRecordProfilePhase<T>(
|
|
461
560
|
runner: RecordProfileRunner | undefined,
|
|
@@ -465,6 +564,14 @@ function runRecordProfilePhase<T>(
|
|
|
465
564
|
return runner === undefined ? action() : runner(phase, action);
|
|
466
565
|
}
|
|
467
566
|
|
|
567
|
+
function shadowPlanes(matrix: Float32Array): Float32Array {
|
|
568
|
+
return frustum.fromViewProjection(frustum.create(), matrix as never);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function shadowViewToken(matrix: Float32Array): string {
|
|
572
|
+
return Array.from(matrix).join(',');
|
|
573
|
+
}
|
|
574
|
+
|
|
468
575
|
function halfFloatToNumber(bits: number): number {
|
|
469
576
|
const sign = (bits & 0x8000) === 0 ? 1 : -1;
|
|
470
577
|
const exponent = (bits >>> 10) & 0x1f;
|
|
@@ -1048,6 +1155,157 @@ function stageAcceptedVolumetricFogFrame(
|
|
|
1048
1155
|
return { ...accepted, paramsBuffer };
|
|
1049
1156
|
}
|
|
1050
1157
|
|
|
1158
|
+
function ssrRuntimeError(
|
|
1159
|
+
cause: unknown,
|
|
1160
|
+
): RhiError | { code: string; message: string; name?: string } {
|
|
1161
|
+
if (cause instanceof RhiError) return cause;
|
|
1162
|
+
if (cause instanceof Error)
|
|
1163
|
+
return { code: 'ssr-history-failure', message: cause.message, name: cause.name };
|
|
1164
|
+
return { code: 'ssr-history-failure', message: String(cause) };
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
function reportSsrHistoryFailure(
|
|
1168
|
+
internals: RenderSystemInternals,
|
|
1169
|
+
stage: 'build' | 'encode' | 'finish' | 'submit',
|
|
1170
|
+
cause: unknown,
|
|
1171
|
+
): void {
|
|
1172
|
+
internals.errorRegistry.fire(
|
|
1173
|
+
new RhiError({
|
|
1174
|
+
code: 'webgpu-runtime-error',
|
|
1175
|
+
expected: `SSR history ${stage} succeeds before queue submission`,
|
|
1176
|
+
hint: 'inspect the SSR history owner failure and retry the next frame',
|
|
1177
|
+
detail: { error: ssrRuntimeError(cause) },
|
|
1178
|
+
}),
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function mapSsrHistoryResetReason(
|
|
1183
|
+
reason: TemporalView['resetReason'],
|
|
1184
|
+
): import('../ssr/history').SsrHistoryResetReason | undefined {
|
|
1185
|
+
switch (reason) {
|
|
1186
|
+
case 'first-frame':
|
|
1187
|
+
return 'first-enable';
|
|
1188
|
+
case 'resize':
|
|
1189
|
+
return 'resize';
|
|
1190
|
+
case 'camera-cut':
|
|
1191
|
+
case 'view-switch':
|
|
1192
|
+
return 'camera-cut';
|
|
1193
|
+
case 'history-version':
|
|
1194
|
+
return 'history-version';
|
|
1195
|
+
case 'environment-change':
|
|
1196
|
+
case 'fog-change':
|
|
1197
|
+
return 'reflection-generation';
|
|
1198
|
+
case 'device-recover':
|
|
1199
|
+
return 'device-recovery';
|
|
1200
|
+
case 'submit-failure':
|
|
1201
|
+
case undefined:
|
|
1202
|
+
return undefined;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function stageSsrHistoryCandidate(
|
|
1207
|
+
internals: RenderSystemInternals,
|
|
1208
|
+
frameState: RenderFrameState,
|
|
1209
|
+
camera: CameraSnapshot,
|
|
1210
|
+
width: number,
|
|
1211
|
+
height: number,
|
|
1212
|
+
admission: SsrSpatialAdmission,
|
|
1213
|
+
temporalView: TemporalView,
|
|
1214
|
+
): boolean {
|
|
1215
|
+
const previousAdmission = frameState.ssrSpatialAdmission;
|
|
1216
|
+
frameState.ssrSpatialAdmission = admission;
|
|
1217
|
+
if (admission.status !== 'admitted') {
|
|
1218
|
+
if (frameState.ssrHistoryOwner !== undefined && previousAdmission?.status === 'admitted') {
|
|
1219
|
+
const owner = frameState.ssrHistoryOwner;
|
|
1220
|
+
owner.reset('disable');
|
|
1221
|
+
owner.retireAfterFence(internals.device.queue, (cause) => {
|
|
1222
|
+
reportSsrHistoryFailure(internals, 'submit', cause);
|
|
1223
|
+
});
|
|
1224
|
+
frameState.ssrHistoryOwner = undefined;
|
|
1225
|
+
}
|
|
1226
|
+
frameState.ssrHistoryCandidate = undefined;
|
|
1227
|
+
frameState.ssrLastCameraEntity = camera.entityKey;
|
|
1228
|
+
frameState.ssrLastHistoryVersion = camera.historyVersion;
|
|
1229
|
+
return true;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
let owner = frameState.ssrHistoryOwner;
|
|
1233
|
+
if (owner === undefined) {
|
|
1234
|
+
const created = createSsrHistoryOwner({
|
|
1235
|
+
device: internals.device,
|
|
1236
|
+
scope: internals.deviceScope,
|
|
1237
|
+
width,
|
|
1238
|
+
height,
|
|
1239
|
+
});
|
|
1240
|
+
if (!created.ok) {
|
|
1241
|
+
reportSsrHistoryFailure(internals, 'build', created.error);
|
|
1242
|
+
return false;
|
|
1243
|
+
}
|
|
1244
|
+
owner = created.value;
|
|
1245
|
+
frameState.ssrHistoryOwner = owner;
|
|
1246
|
+
} else if (owner.resources.width !== width || owner.resources.height !== height) {
|
|
1247
|
+
const resized = owner.resize(width, height);
|
|
1248
|
+
if (!resized.ok) {
|
|
1249
|
+
reportSsrHistoryFailure(internals, 'build', resized.error);
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
owner.retireAfterFence(internals.device.queue, (cause) => {
|
|
1253
|
+
reportSsrHistoryFailure(internals, 'submit', cause);
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
let resetReason: import('../ssr/history').SsrHistoryResetReason | undefined;
|
|
1258
|
+
if (
|
|
1259
|
+
frameState.ssrLastCameraEntity !== undefined &&
|
|
1260
|
+
frameState.ssrLastCameraEntity !== camera.entityKey
|
|
1261
|
+
) {
|
|
1262
|
+
resetReason = 'camera-cut';
|
|
1263
|
+
} else if (
|
|
1264
|
+
frameState.ssrLastHistoryVersion !== undefined &&
|
|
1265
|
+
frameState.ssrLastHistoryVersion !== camera.historyVersion
|
|
1266
|
+
) {
|
|
1267
|
+
resetReason = 'history-version';
|
|
1268
|
+
} else {
|
|
1269
|
+
resetReason = mapSsrHistoryResetReason(temporalView.resetReason);
|
|
1270
|
+
}
|
|
1271
|
+
if (resetReason !== undefined) owner.reset(resetReason);
|
|
1272
|
+
|
|
1273
|
+
const started = owner.beginFrame();
|
|
1274
|
+
if (!started.ok) {
|
|
1275
|
+
reportSsrHistoryFailure(internals, 'build', started.error);
|
|
1276
|
+
return false;
|
|
1277
|
+
}
|
|
1278
|
+
const candidate = started.value;
|
|
1279
|
+
const payload =
|
|
1280
|
+
frameState.ssrTemporalParamsPayload?.byteLength === 32
|
|
1281
|
+
? frameState.ssrTemporalParamsPayload
|
|
1282
|
+
: new Uint8Array(32);
|
|
1283
|
+
payload.fill(0);
|
|
1284
|
+
const values = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
1285
|
+
values.setUint32(0, candidate.historyValid ? 1 : 0, true);
|
|
1286
|
+
values.setFloat32(4, 0.9, true);
|
|
1287
|
+
values.setFloat32(8, admission.config.thickness, true);
|
|
1288
|
+
values.setFloat32(12, 0.5, true);
|
|
1289
|
+
// Reuse the shared successful-submit snapshot; SSR owns no jitter sequence
|
|
1290
|
+
// or independently advancing previous-camera state.
|
|
1291
|
+
const previousJitter = frameState.lastSuccessfulTemporalView?.currentJitterUv;
|
|
1292
|
+
values.setFloat32(16, temporalView.currentJitterUv?.[0] ?? 0, true);
|
|
1293
|
+
values.setFloat32(20, temporalView.currentJitterUv?.[1] ?? 0, true);
|
|
1294
|
+
values.setFloat32(24, previousJitter?.[0] ?? 0, true);
|
|
1295
|
+
values.setFloat32(28, previousJitter?.[1] ?? 0, true);
|
|
1296
|
+
const written = internals.device.queue.writeBuffer(owner.resources.paramsBuffer, 0, payload);
|
|
1297
|
+
if (!written.ok) {
|
|
1298
|
+
owner.abortFrame(candidate, 'encode');
|
|
1299
|
+
reportSsrHistoryFailure(internals, 'encode', written.error);
|
|
1300
|
+
return false;
|
|
1301
|
+
}
|
|
1302
|
+
frameState.ssrTemporalParamsPayload = payload;
|
|
1303
|
+
frameState.ssrHistoryCandidate = candidate;
|
|
1304
|
+
frameState.ssrLastCameraEntity = camera.entityKey;
|
|
1305
|
+
frameState.ssrLastHistoryVersion = camera.historyVersion;
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1051
1309
|
function updateVolumetricFogInspection(
|
|
1052
1310
|
frameState: RenderFrameState,
|
|
1053
1311
|
fog: ExtractedVolumetricFog | undefined,
|
|
@@ -1303,7 +1561,7 @@ export function recordFrame(
|
|
|
1303
1561
|
world: World,
|
|
1304
1562
|
cameras: CameraSnapshot[],
|
|
1305
1563
|
lights: ExtractedLights,
|
|
1306
|
-
renderables: RenderableSnapshot[],
|
|
1564
|
+
renderables: readonly RenderableSnapshot[],
|
|
1307
1565
|
transparentDispatch: readonly DispatchEntry[],
|
|
1308
1566
|
frameState: RenderFrameState,
|
|
1309
1567
|
dispatchCounts: DispatchCounts,
|
|
@@ -1339,9 +1597,18 @@ export function recordFrame(
|
|
|
1339
1597
|
fogSignature = '',
|
|
1340
1598
|
environmentReady = true,
|
|
1341
1599
|
transmissionDemand?: TransmissionDemand,
|
|
1600
|
+
shadowCasterEntityKeys: ReadonlySet<number> = EMPTY_ENTITY_KEYS,
|
|
1601
|
+
shadowCasterDrawKeys: ReadonlySet<string> | undefined = undefined,
|
|
1342
1602
|
volumetricFog?: ExtractedVolumetricFog,
|
|
1343
1603
|
timingCapture?: GpuTimingCapture,
|
|
1604
|
+
shadowCasterMembership?: readonly ShadowCasterMembership[],
|
|
1605
|
+
shadowCasterProjection?: PersistentShadowCasterProjection,
|
|
1606
|
+
shadowCasterWorldKeys?: readonly number[],
|
|
1607
|
+
preparedLighting?: ReturnType<typeof prepareFrameLighting>,
|
|
1608
|
+
ssrDependencies?: SsrDependenciesInspection,
|
|
1609
|
+
onRenderableDraw?: (entry: ValidatedRenderable) => void,
|
|
1344
1610
|
): boolean {
|
|
1611
|
+
void shadowCasterEntityKeys;
|
|
1345
1612
|
frameState.reflectionFallbackObservationSource = undefined;
|
|
1346
1613
|
frameState.reflectionFallbackCompletion = undefined;
|
|
1347
1614
|
// The `try / finally` wrapper advances `frameState.frameNumber` exactly
|
|
@@ -1391,6 +1658,7 @@ export function recordFrame(
|
|
|
1391
1658
|
}
|
|
1392
1659
|
const camera = activeCameras[0];
|
|
1393
1660
|
if (!camera) return false;
|
|
1661
|
+
frameState.ssrRequested = camera.screenSpaceReflection !== undefined;
|
|
1394
1662
|
const pipelineState = internals.getPipelineState();
|
|
1395
1663
|
if (pipelineState === null) return false;
|
|
1396
1664
|
if (pipelineState.device !== internals.device) {
|
|
@@ -1432,8 +1700,12 @@ export function recordFrame(
|
|
|
1432
1700
|
// ensure + ExtractedLights three-arm destructure (directional fallback,
|
|
1433
1701
|
// point/spot/Rect arrays, totalLightCount). Extracted to prepareFrameLighting
|
|
1434
1702
|
// (M3/w18) so recordFrame stays a skeleton.
|
|
1435
|
-
const lighting = runRecordProfilePhase(
|
|
1436
|
-
|
|
1703
|
+
const lighting = runRecordProfilePhase(
|
|
1704
|
+
profilePhase,
|
|
1705
|
+
'record/scene-state/lighting-prep',
|
|
1706
|
+
() =>
|
|
1707
|
+
preparedLighting ??
|
|
1708
|
+
prepareFrameLighting(internals, frameState, lights, camera, pipelineState),
|
|
1437
1709
|
);
|
|
1438
1710
|
if (!lighting.ok) return lighting;
|
|
1439
1711
|
const { light, totalLightCount, standard } = lighting.value;
|
|
@@ -1518,6 +1790,10 @@ export function recordFrame(
|
|
|
1518
1790
|
const effectiveShadowMapSize = resolveShadowMapSize(internals, lights);
|
|
1519
1791
|
const effectiveSpotShadowMapSize = resolveSpotShadowMapSize(internals, lights);
|
|
1520
1792
|
const graphShadowMapSize = effectiveShadowMapSize ?? effectiveSpotShadowMapSize;
|
|
1793
|
+
const hasShadowViews =
|
|
1794
|
+
(effectiveShadowMapSize ?? 0) > 0 ||
|
|
1795
|
+
lights.pointShadow.length > 0 ||
|
|
1796
|
+
lights.spot.some((spot) => spot.castShadow);
|
|
1521
1797
|
const volumeCapability = hasVolumetricFogCapability(
|
|
1522
1798
|
internals.device.caps,
|
|
1523
1799
|
internals.volumetricFogShaders,
|
|
@@ -1578,89 +1854,248 @@ export function recordFrame(
|
|
|
1578
1854
|
(volumetricFog?.status === 'degraded' && acceptedFallback !== undefined);
|
|
1579
1855
|
|
|
1580
1856
|
let preparedGpuDriven: PreparedGpuDrivenFrame | undefined;
|
|
1581
|
-
|
|
1857
|
+
const abortPreparedGpuDriven = (): void => {
|
|
1858
|
+
preparedGpuDriven?._abortResourceReplacement?.();
|
|
1859
|
+
};
|
|
1860
|
+
const cubeCaptureWork = prepareCubeCapture(internals, cubeCapture, camera);
|
|
1861
|
+
const reflectionFallbackCandidate = cubeCapture?.reflectionProbes?.fallbackDemand === true;
|
|
1862
|
+
// The Standard PBR GPU raster owns one color target. Reflection fallback
|
|
1863
|
+
// adds an MRT attachment, so keep this frame on the ordinary CPU producer
|
|
1864
|
+
// instead of claiming rows that the one-target GPU pipeline cannot draw.
|
|
1865
|
+
const frameGpuDriven = reflectionFallbackCandidate ? undefined : gpuDriven;
|
|
1866
|
+
|
|
1867
|
+
// Validate first so user meshes are resident in the same frame. The GPU
|
|
1868
|
+
// owner consumes this producer-owned map together with the builtin mesh
|
|
1869
|
+
// entries; it never resolves an asset or creates a fallback residency.
|
|
1870
|
+
// Stable persistent-scene frames reuse this exact producer result. The
|
|
1871
|
+
// cache is fenced by source identities plus mesh/device/pipeline/catalog
|
|
1872
|
+
// generations, so recovery or any scene/resource replacement performs a
|
|
1873
|
+
// fresh owner validation before a capable submission.
|
|
1874
|
+
const shadowValidationActive = hasShadowViews && shadowCasterProjection !== undefined;
|
|
1875
|
+
const validationRenderables = shadowValidationActive
|
|
1876
|
+
? shadowCasterProjection.renderables
|
|
1877
|
+
: renderables;
|
|
1878
|
+
const validationDispatch = shadowValidationActive
|
|
1879
|
+
? shadowCasterProjection.dispatch
|
|
1880
|
+
: transparentDispatch;
|
|
1881
|
+
const validationInput = {
|
|
1882
|
+
cacheKey: residencyValidationCacheKey(world, worlds),
|
|
1883
|
+
renderables: validationRenderables,
|
|
1884
|
+
transparentDispatch: validationDispatch,
|
|
1885
|
+
pipelineState,
|
|
1886
|
+
pipelineHandle: frameState.installedPipelineHandle,
|
|
1887
|
+
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
1888
|
+
deviceGeneration: internals.deviceScope.generation,
|
|
1889
|
+
assetCatalogEpoch: internals.assets.catalogEpoch,
|
|
1890
|
+
} as const;
|
|
1891
|
+
const cachedValidation = frameGpuDriven?.owner.reuseResidencyValidation(validationInput);
|
|
1892
|
+
const validatedProjection =
|
|
1893
|
+
cachedValidation ??
|
|
1894
|
+
runRecordProfilePhase(profilePhase, 'record/validation', () =>
|
|
1895
|
+
validateRenderables(
|
|
1896
|
+
internals,
|
|
1897
|
+
world,
|
|
1898
|
+
worlds,
|
|
1899
|
+
pipelineState,
|
|
1900
|
+
frameState,
|
|
1901
|
+
validationRenderables,
|
|
1902
|
+
validationDispatch,
|
|
1903
|
+
EMPTY_ENTITY_KEYS,
|
|
1904
|
+
),
|
|
1905
|
+
);
|
|
1906
|
+
if (cachedValidation === undefined) {
|
|
1907
|
+
frameGpuDriven?.owner.rememberResidencyValidation(validationInput, validatedProjection);
|
|
1908
|
+
}
|
|
1909
|
+
// Camera culling only changes the primary index domain. When shadows are
|
|
1910
|
+
// active, the retained projection is the validation superset; project the
|
|
1911
|
+
// same resolved rows into the camera-visible subset without another walk.
|
|
1912
|
+
const validatedShadowForResidency = shadowValidationActive ? validatedProjection : undefined;
|
|
1913
|
+
const validatedForResidency = shadowValidationActive
|
|
1914
|
+
? projectValidatedRows(validatedProjection, renderables)
|
|
1915
|
+
: validatedProjection;
|
|
1916
|
+
|
|
1917
|
+
if (frameGpuDriven !== undefined) {
|
|
1918
|
+
const gpuDrivenMeshes = new Map(
|
|
1919
|
+
gpuDrivenMeshesForFrame(internals, pipelineState, worlds, frameGpuDriven.scene),
|
|
1920
|
+
);
|
|
1921
|
+
const meshSlotByIdentity = new Map(
|
|
1922
|
+
(frameGpuDriven.scene?.slots ?? []).map((slot) => [
|
|
1923
|
+
worldEntityKey(slot.snapshot.worldId, slot.snapshot.entityKey),
|
|
1924
|
+
slot.slot,
|
|
1925
|
+
]),
|
|
1926
|
+
);
|
|
1927
|
+
for (const row of [...validatedForResidency, ...(validatedShadowForResidency ?? [])]) {
|
|
1928
|
+
const slot = meshSlotByIdentity.get(
|
|
1929
|
+
worldEntityKey(row.source.worldId, row.source.entityKey),
|
|
1930
|
+
);
|
|
1931
|
+
if (slot !== undefined) gpuDrivenMeshes.set(slot, row.mesh);
|
|
1932
|
+
}
|
|
1582
1933
|
const standardPbrArtifact = internals.getMaterialShaderArtifact?.(
|
|
1583
1934
|
'forgeax::default-standard-pbr',
|
|
1584
1935
|
);
|
|
1585
|
-
const
|
|
1586
|
-
|
|
1936
|
+
const standardPbrSkinArtifact = internals.getMaterialShaderArtifact?.('forgeax::pbr-skin');
|
|
1937
|
+
const paletteUploadBytes = validatedForResidency.reduce(
|
|
1938
|
+
(total, row) => total + (row.source.skin?.uploadBytes ?? 0),
|
|
1939
|
+
0,
|
|
1940
|
+
);
|
|
1941
|
+
const prepared = frameGpuDriven.owner.prepare({
|
|
1942
|
+
scene: frameGpuDriven.scene,
|
|
1587
1943
|
camera,
|
|
1588
|
-
|
|
1944
|
+
meshBySlot: gpuDrivenMeshes,
|
|
1589
1945
|
viewBindGroupLayout: pipelineState.viewBindGroupLayout,
|
|
1590
1946
|
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
1591
|
-
//
|
|
1592
|
-
//
|
|
1593
|
-
//
|
|
1594
|
-
//
|
|
1595
|
-
hdrp:
|
|
1596
|
-
|
|
1947
|
+
// GPU-driven Standard PBR currently owns the non-clustered path only.
|
|
1948
|
+
// Cluster bindings also carry screen-space ambient occlusion. Admit
|
|
1949
|
+
// the scene-index variant only when none of those surface inputs are
|
|
1950
|
+
// required; a zero local-light count alone does not establish that.
|
|
1951
|
+
hdrp:
|
|
1952
|
+
standard.kind === 'clustered' &&
|
|
1953
|
+
(standard.prepared.local.length > 0 ||
|
|
1954
|
+
frameState.installedPipelineConfig?.ssao?.enabled === true),
|
|
1955
|
+
...(frameGpuDriven.activeEntityKeys === undefined
|
|
1956
|
+
? {}
|
|
1957
|
+
: { activeEntityKeys: frameGpuDriven.activeEntityKeys }),
|
|
1958
|
+
...(frameGpuDriven.activeEntityRevision === undefined
|
|
1597
1959
|
? {}
|
|
1598
|
-
: {
|
|
1599
|
-
...(
|
|
1960
|
+
: { activeEntityRevision: frameGpuDriven.activeEntityRevision }),
|
|
1961
|
+
...(frameGpuDriven.telemetryCandidateCount === undefined
|
|
1600
1962
|
? {}
|
|
1601
|
-
: {
|
|
1602
|
-
...(
|
|
1963
|
+
: { telemetryCandidateCount: frameGpuDriven.telemetryCandidateCount }),
|
|
1964
|
+
...(frameGpuDriven.occlusion === undefined ? {} : { occlusion: frameGpuDriven.occlusion }),
|
|
1965
|
+
...(frameGpuDriven.scene?.structureMetrics === undefined
|
|
1603
1966
|
? {}
|
|
1604
|
-
: {
|
|
1605
|
-
|
|
1967
|
+
: {
|
|
1968
|
+
structureMetrics: {
|
|
1969
|
+
...frameGpuDriven.scene.structureMetrics,
|
|
1970
|
+
paletteUploadBytes,
|
|
1971
|
+
},
|
|
1972
|
+
}),
|
|
1606
1973
|
...(standardPbrArtifact === undefined ? {} : { standardPbrArtifact }),
|
|
1974
|
+
...(standardPbrSkinArtifact === undefined ? {} : { standardPbrSkinArtifact }),
|
|
1607
1975
|
standardPbrPipelineState: pipelineState,
|
|
1976
|
+
...(shadowCasterDrawKeys === undefined ? {} : { shadowCasterDrawKeys }),
|
|
1977
|
+
...(shadowCasterMembership === undefined ? {} : { shadowCasterMembership }),
|
|
1608
1978
|
});
|
|
1609
|
-
if (prepared.ok)
|
|
1610
|
-
|
|
1979
|
+
if (!prepared.ok) {
|
|
1980
|
+
// A capable GPU-owned candidate with missing producer resources is a
|
|
1981
|
+
// structured stop, not permission to enumerate the same draw through
|
|
1982
|
+
// the CPU semantic path. Preserve the last-known-good graph and retry
|
|
1983
|
+
// after the producer publishes a validated artifact.
|
|
1611
1984
|
internals.errorRegistry.fire(prepared.error);
|
|
1985
|
+
abortPreparedGpuDriven();
|
|
1986
|
+
return false;
|
|
1987
|
+
} else if (prepared.value !== undefined) {
|
|
1988
|
+
preparedGpuDriven = prepared.value;
|
|
1989
|
+
const shadowViews: Array<{
|
|
1990
|
+
readonly identity: ShadowViewIdentity;
|
|
1991
|
+
readonly planes: Float32Array;
|
|
1992
|
+
readonly cacheToken: string;
|
|
1993
|
+
}> = [];
|
|
1994
|
+
for (const [index, matrix] of (lights.lightViewProj ?? []).entries()) {
|
|
1995
|
+
shadowViews.push({
|
|
1996
|
+
identity: { kind: 'directional', index },
|
|
1997
|
+
planes: shadowPlanes(matrix),
|
|
1998
|
+
cacheToken: `${shadowViewToken(matrix)}|target:${graphShadowMapSize}:${frameState.graphGeneration}`,
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
for (const [index, point] of lights.pointShadow.entries()) {
|
|
2002
|
+
for (let face = 0; face < 6; face += 1) {
|
|
2003
|
+
const matrix = point.shadowMatrices.subarray(face * 16, face * 16 + 16);
|
|
2004
|
+
shadowViews.push({
|
|
2005
|
+
identity: { kind: 'point', index, face },
|
|
2006
|
+
planes: shadowPlanes(matrix),
|
|
2007
|
+
cacheToken: `${shadowViewToken(matrix)}|target:${graphShadowMapSize}:${frameState.graphGeneration}`,
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
let shadowSpotIndex = 0;
|
|
2012
|
+
for (const spot of lights.spot) {
|
|
2013
|
+
if (spot.lightViewProj === undefined) continue;
|
|
2014
|
+
shadowViews.push({
|
|
2015
|
+
identity: { kind: 'spot', index: shadowSpotIndex },
|
|
2016
|
+
planes: shadowPlanes(spot.lightViewProj),
|
|
2017
|
+
cacheToken: `${shadowViewToken(spot.lightViewProj)}|target:${graphShadowMapSize}:${frameState.graphGeneration}`,
|
|
2018
|
+
});
|
|
2019
|
+
shadowSpotIndex += 1;
|
|
2020
|
+
}
|
|
2021
|
+
if (prepared.value.updateShadowViews !== undefined) {
|
|
2022
|
+
const shadowUpdate = prepared.value.updateShadowViews(shadowViews);
|
|
2023
|
+
if (!shadowUpdate.ok) {
|
|
2024
|
+
internals.errorRegistry.fire(shadowUpdate.error);
|
|
2025
|
+
abortPreparedGpuDriven();
|
|
2026
|
+
return false;
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
1612
2030
|
}
|
|
1613
2031
|
|
|
2032
|
+
const gpuOwnedDrawKeys = preparedGpuDriven?.drawKeys ?? EMPTY_DRAW_KEYS;
|
|
1614
2033
|
// The GPU lane owns only the primary opaque raster today. Directional,
|
|
1615
|
-
// point, and spot shadow views
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
const gpuOwnedEntityKeys = preparedGpuDriven?.entityKeys ?? EMPTY_ENTITY_KEYS;
|
|
1631
|
-
const gpuDrivenWorldKeys = gpuDriven?.scene?.worldKeys;
|
|
1632
|
-
const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
|
|
1633
|
-
gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive && cubeCaptureWork.length === 0
|
|
1634
|
-
? []
|
|
1635
|
-
: validateRenderables(
|
|
1636
|
-
internals,
|
|
1637
|
-
world,
|
|
1638
|
-
worlds,
|
|
1639
|
-
pipelineState,
|
|
1640
|
-
frameState,
|
|
1641
|
-
renderables,
|
|
1642
|
-
transparentDispatch,
|
|
1643
|
-
cpuShadowCastersActive || cubeCaptureWork.length > 0
|
|
1644
|
-
? EMPTY_ENTITY_KEYS
|
|
1645
|
-
: gpuOwnedEntityKeys,
|
|
1646
|
-
gpuDrivenWorldKeys,
|
|
1647
|
-
),
|
|
2034
|
+
// point, and spot shadow views consume the same CPU-validated rows; they do
|
|
2035
|
+
// not require a second residency walk because the first validation already
|
|
2036
|
+
// resolved every renderable used by both lanes.
|
|
2037
|
+
// The GPU lane owns the actual geometry, but the main-pass material
|
|
2038
|
+
// producer still needs the validated snapshot rows to build the
|
|
2039
|
+
// frame-global bind-group table consumed by indirect draws. The record
|
|
2040
|
+
// loop skips claimed submeshes through `gpuDrivenDrawKeys`, so retaining
|
|
2041
|
+
// these rows does not issue duplicate CPU geometry. Reusing this one
|
|
2042
|
+
// validation result also keeps active shadow views from repeating the
|
|
2043
|
+
// residency walk.
|
|
2044
|
+
const validated = validatedForResidency;
|
|
2045
|
+
frameGpuDriven?.owner.recordCpuValidation(
|
|
2046
|
+
validated,
|
|
2047
|
+
gpuOwnedDrawKeys,
|
|
2048
|
+
validationInput.cacheKey,
|
|
1648
2049
|
);
|
|
1649
|
-
|
|
1650
|
-
|
|
2050
|
+
const cacheValidated =
|
|
2051
|
+
validatedShadowForResidency === undefined
|
|
2052
|
+
? validated
|
|
2053
|
+
: [...validated, ...validatedShadowForResidency];
|
|
2054
|
+
cleanPerFrameCaches(
|
|
2055
|
+
internals,
|
|
2056
|
+
frameState,
|
|
2057
|
+
cacheValidated,
|
|
2058
|
+
cacheEntityKeysFromDrawKeys(gpuOwnedDrawKeys),
|
|
2059
|
+
);
|
|
2060
|
+
|
|
2061
|
+
// Compile the single frame graph only after GPU preparation has projected
|
|
2062
|
+
// the current frame. This keeps the graph topology and its execution
|
|
2063
|
+
// closure on the same prepared GPU-owned draw set.
|
|
1651
2064
|
|
|
1652
2065
|
// S_fallback is the second output of the built-in Standard PBR BRDF. A
|
|
1653
2066
|
// graph may still carry the detached owner tickets for inspection, but
|
|
1654
2067
|
// the MRT is admitted only when every CPU-rendered primary row in this
|
|
1655
2068
|
// frame uses that exact output contract. Unsupported compositions remain
|
|
1656
2069
|
// fail-closed for SSR instead of submitting a device-invalid command.
|
|
1657
|
-
|
|
1658
|
-
reflectionFallbackCandidate &&
|
|
2070
|
+
const sceneInputs =
|
|
1659
2071
|
validated.length > 0 &&
|
|
1660
2072
|
validated.every(
|
|
1661
2073
|
(entry) => entry.source.material.materialShaderId === 'forgeax::default-standard-pbr',
|
|
1662
2074
|
);
|
|
1663
|
-
|
|
2075
|
+
const ssrAdmission =
|
|
2076
|
+
ssrDependencies === undefined
|
|
2077
|
+
? undefined
|
|
2078
|
+
: admitSsrSpatial({
|
|
2079
|
+
camera,
|
|
2080
|
+
environment: {
|
|
2081
|
+
lane: standard.prepared.renderPath === 'deferred' ? 'deferred' : 'forward',
|
|
2082
|
+
m0: ssrDependencies.admission,
|
|
2083
|
+
sceneInputs,
|
|
2084
|
+
// Standard geometry produces current motion/coverage in this
|
|
2085
|
+
// graph, including its first frame. Prior history is optional.
|
|
2086
|
+
temporal: sceneInputs,
|
|
2087
|
+
reflectionFallback: ssrDependencies.reflectionFallback !== undefined,
|
|
2088
|
+
capabilities: {
|
|
2089
|
+
compute: internals.device.caps.compute,
|
|
2090
|
+
storageTexture: internals.device.caps.storageTexture,
|
|
2091
|
+
rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
|
|
2092
|
+
r32floatSampledStorage: ssrDependencies.format?.verdict === 'admitted',
|
|
2093
|
+
},
|
|
2094
|
+
recovery: true,
|
|
2095
|
+
},
|
|
2096
|
+
});
|
|
2097
|
+
frameState.reflectionFallbackDemand =
|
|
2098
|
+
(reflectionFallbackCandidate || ssrAdmission?.status === 'admitted') && sceneInputs;
|
|
1664
2099
|
const preflightGraph = runRecordProfilePhase(profilePhase, 'record/render-graph', () =>
|
|
1665
2100
|
ensureCompiledFrameGraph(
|
|
1666
2101
|
internals,
|
|
@@ -1676,13 +2111,18 @@ export function recordFrame(
|
|
|
1676
2111
|
cubeCapture?.state,
|
|
1677
2112
|
cameras.length === 0,
|
|
1678
2113
|
transmissionDemand,
|
|
1679
|
-
|
|
2114
|
+
frameGpuDriven?.occlusion,
|
|
1680
2115
|
graphVolumetricFog,
|
|
1681
2116
|
volumeTopologyCandidate,
|
|
1682
2117
|
standard,
|
|
2118
|
+
undefined,
|
|
2119
|
+
ssrAdmission,
|
|
1683
2120
|
),
|
|
1684
2121
|
);
|
|
1685
|
-
if (preflightGraph === null)
|
|
2122
|
+
if (preflightGraph === null) {
|
|
2123
|
+
abortPreparedGpuDriven();
|
|
2124
|
+
return false;
|
|
2125
|
+
}
|
|
1686
2126
|
|
|
1687
2127
|
// The compiled graph is the acceptance boundary for the prepared
|
|
1688
2128
|
// Standard lighting declaration. Upload the same declaration only after
|
|
@@ -1703,6 +2143,7 @@ export function recordFrame(
|
|
|
1703
2143
|
},
|
|
1704
2144
|
}),
|
|
1705
2145
|
);
|
|
2146
|
+
abortPreparedGpuDriven();
|
|
1706
2147
|
return false;
|
|
1707
2148
|
}
|
|
1708
2149
|
if (standard.kind === 'clustered') {
|
|
@@ -1731,6 +2172,7 @@ export function recordFrame(
|
|
|
1731
2172
|
);
|
|
1732
2173
|
if (!clusterWrite.ok) {
|
|
1733
2174
|
internals.errorRegistry.fire(clusterWrite.error);
|
|
2175
|
+
abortPreparedGpuDriven();
|
|
1734
2176
|
return false;
|
|
1735
2177
|
}
|
|
1736
2178
|
}
|
|
@@ -1743,7 +2185,10 @@ export function recordFrame(
|
|
|
1743
2185
|
const swapTarget = runRecordProfilePhase(profilePhase, 'record/swapchain', () =>
|
|
1744
2186
|
acquireSwapChainTarget(internals, pipelineState),
|
|
1745
2187
|
);
|
|
1746
|
-
if (swapTarget === null)
|
|
2188
|
+
if (swapTarget === null) {
|
|
2189
|
+
abortPreparedGpuDriven();
|
|
2190
|
+
return false;
|
|
2191
|
+
}
|
|
1747
2192
|
const currentTexture = swapTarget.currentTexture;
|
|
1748
2193
|
const view = swapTarget.view;
|
|
1749
2194
|
const targetW = swapTarget.targetW;
|
|
@@ -1801,6 +2246,23 @@ export function recordFrame(
|
|
|
1801
2246
|
: { kind: 'off', view: temporalView };
|
|
1802
2247
|
const recordCamera: CameraSnapshot =
|
|
1803
2248
|
temporalView.mode === 'taa' ? { ...camera, temporal: temporalView } : camera;
|
|
2249
|
+
if (ssrAdmission !== undefined) {
|
|
2250
|
+
const historyWidth = Math.max(1, internals.canvas.width);
|
|
2251
|
+
const historyHeight = Math.max(1, internals.canvas.height);
|
|
2252
|
+
if (
|
|
2253
|
+
!stageSsrHistoryCandidate(
|
|
2254
|
+
internals,
|
|
2255
|
+
frameState,
|
|
2256
|
+
camera,
|
|
2257
|
+
historyWidth,
|
|
2258
|
+
historyHeight,
|
|
2259
|
+
ssrAdmission,
|
|
2260
|
+
temporalView,
|
|
2261
|
+
)
|
|
2262
|
+
) {
|
|
2263
|
+
return false;
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
1804
2266
|
let activeVolumetricFogContext: VolumetricFogFrameContext | undefined;
|
|
1805
2267
|
let activeVolumetricFogSignature: VolumeTemporalSignature | undefined;
|
|
1806
2268
|
if (graphVolumetricFog?.status === 'available' && baseVolumetricFogContext !== undefined) {
|
|
@@ -1827,6 +2289,7 @@ export function recordFrame(
|
|
|
1827
2289
|
);
|
|
1828
2290
|
if (!historyFlagWritten.ok) {
|
|
1829
2291
|
internals.errorRegistry.fire(historyFlagWritten.error);
|
|
2292
|
+
abortPreparedGpuDriven();
|
|
1830
2293
|
return false;
|
|
1831
2294
|
}
|
|
1832
2295
|
}
|
|
@@ -1902,9 +2365,18 @@ export function recordFrame(
|
|
|
1902
2365
|
// order, run the mesh-SSBO capacity gate (graceful truncation), and build
|
|
1903
2366
|
// the fold dispatch plan. Extracted to buildDispatchPlan (M3/w18).
|
|
1904
2367
|
const dispatchPlan = runRecordProfilePhase(profilePhase, 'record/dispatch-plan', () =>
|
|
1905
|
-
buildDispatchPlan(
|
|
2368
|
+
buildDispatchPlan(
|
|
2369
|
+
internals,
|
|
2370
|
+
validated,
|
|
2371
|
+
transparentDispatch,
|
|
2372
|
+
foldBuckets,
|
|
2373
|
+
validatedShadowForResidency ?? [],
|
|
2374
|
+
),
|
|
1906
2375
|
);
|
|
1907
2376
|
const validatedOrdered = dispatchPlan.validatedOrdered;
|
|
2377
|
+
const shadowValidatedOrdered = dispatchPlan.shadowValidatedOrdered;
|
|
2378
|
+
const shadowMeshSsboBase = validatedOrdered.length;
|
|
2379
|
+
const shadowDispatch = shadowCasterProjection?.dispatch;
|
|
1908
2380
|
const foldDispatchPlan = dispatchPlan.foldDispatchPlan;
|
|
1909
2381
|
const materialSlotIndices = dispatchPlan.materialSlotIndices;
|
|
1910
2382
|
const materialSlots = dispatchPlan.materialSlots;
|
|
@@ -1936,12 +2408,13 @@ export function recordFrame(
|
|
|
1936
2408
|
const unormViewRes = internals.device.createTextureView(currentTexture, {});
|
|
1937
2409
|
if (!unormViewRes.ok) {
|
|
1938
2410
|
internals.errorRegistry.fire(unormViewRes.error);
|
|
2411
|
+
abortPreparedGpuDriven();
|
|
1939
2412
|
return false;
|
|
1940
2413
|
}
|
|
1941
2414
|
ldrSpritePassView = unormViewRes.value;
|
|
1942
2415
|
}
|
|
1943
2416
|
|
|
1944
|
-
// View / mesh uniform uploads are
|
|
2417
|
+
// View / mesh uniform uploads are needed when geometry or a feature consumes View,
|
|
1945
2418
|
// OR when a skybox is active (skybox pass reads inverseViewProj from
|
|
1946
2419
|
// the View UBO). Skip the writeBuffer round-trips on the Case E
|
|
1947
2420
|
// (clear-pass-only) path only when neither condition is met.
|
|
@@ -1956,7 +2429,10 @@ export function recordFrame(
|
|
|
1956
2429
|
);
|
|
1957
2430
|
if (
|
|
1958
2431
|
validatedOrdered.length > 0 ||
|
|
2432
|
+
shadowValidatedOrdered.length > 0 ||
|
|
2433
|
+
getRenderFeatureGraphState(internals).plans.some(({ plan }) => plan.passes.length > 0) ||
|
|
1959
2434
|
skyboxActive ||
|
|
2435
|
+
frameState.environmentFrame?.source.kind === 'atmosphere' ||
|
|
1960
2436
|
activeVolumetricFogContext !== undefined ||
|
|
1961
2437
|
preparedVolumetricFog !== undefined
|
|
1962
2438
|
) {
|
|
@@ -1999,6 +2475,15 @@ export function recordFrame(
|
|
|
1999
2475
|
validatedOrdered,
|
|
2000
2476
|
foldDispatchPlan,
|
|
2001
2477
|
);
|
|
2478
|
+
if (shadowValidatedOrdered.length > 0) {
|
|
2479
|
+
uploadMeshSsboBatch(
|
|
2480
|
+
internals.device.queue,
|
|
2481
|
+
pipelineState.meshStorageBuffer,
|
|
2482
|
+
shadowValidatedOrdered,
|
|
2483
|
+
null,
|
|
2484
|
+
shadowMeshSsboBase,
|
|
2485
|
+
);
|
|
2486
|
+
}
|
|
2002
2487
|
});
|
|
2003
2488
|
}
|
|
2004
2489
|
|
|
@@ -2021,6 +2506,7 @@ export function recordFrame(
|
|
|
2021
2506
|
const encoderResult = internals.device.createCommandEncoder({ label: 'render-system-frame' });
|
|
2022
2507
|
if (!encoderResult.ok) {
|
|
2023
2508
|
internals.errorRegistry.fire(encoderResult.error);
|
|
2509
|
+
abortPreparedGpuDriven();
|
|
2024
2510
|
return false;
|
|
2025
2511
|
}
|
|
2026
2512
|
const encoder: RhiCommandEncoder = encoderResult.value;
|
|
@@ -2099,8 +2585,6 @@ export function recordFrame(
|
|
|
2099
2585
|
const passCtx: _InternalRenderPipelineContext = {
|
|
2100
2586
|
assets: internals.assets,
|
|
2101
2587
|
world,
|
|
2102
|
-
gpuDrivenEntityKeys: gpuOwnedEntityKeys,
|
|
2103
|
-
...(gpuDrivenWorldKeys === undefined ? {} : { gpuDrivenWorldKeys }),
|
|
2104
2588
|
store: internals.gpuStore,
|
|
2105
2589
|
pipelineState,
|
|
2106
2590
|
bloomResources: pipelineState.perPassResources.getBloomResources?.() ?? null,
|
|
@@ -2136,6 +2620,10 @@ export function recordFrame(
|
|
|
2136
2620
|
...(internals.volumetricFogShaders === undefined
|
|
2137
2621
|
? {}
|
|
2138
2622
|
: { volumetricFogShaders: internals.volumetricFogShaders }),
|
|
2623
|
+
...(internals.ssrShaders === undefined ? {} : { ssrShaders: internals.ssrShaders }),
|
|
2624
|
+
...(internals.atmosphereShaders === undefined
|
|
2625
|
+
? {}
|
|
2626
|
+
: { atmosphereShaders: internals.atmosphereShaders }),
|
|
2139
2627
|
...(activeVolumetricFogContext === undefined
|
|
2140
2628
|
? {}
|
|
2141
2629
|
: { volumetricFog: activeVolumetricFogContext }),
|
|
@@ -2145,19 +2633,39 @@ export function recordFrame(
|
|
|
2145
2633
|
dispatch: transparentDispatch,
|
|
2146
2634
|
hdrpClusterBindGroup,
|
|
2147
2635
|
hdrpClusterMembershipBindGroup,
|
|
2148
|
-
...(
|
|
2636
|
+
...(frameGpuDriven?.occlusion === undefined ? {} : { occlusion: frameGpuDriven.occlusion }),
|
|
2149
2637
|
standardLighting: standard,
|
|
2150
2638
|
foldDispatchPlan,
|
|
2151
2639
|
materialSlotIndices,
|
|
2152
2640
|
materialSlots,
|
|
2153
2641
|
materialSlotOwners,
|
|
2154
2642
|
materialSlotCount,
|
|
2643
|
+
...(preparedGpuDriven === undefined
|
|
2644
|
+
? {}
|
|
2645
|
+
: {
|
|
2646
|
+
gpuDrivenStandardPbrFrameResources: preparedGpuDriven.standardPbrFrameResources,
|
|
2647
|
+
gpuDrivenDrawKeys: preparedGpuDriven.drawKeys,
|
|
2648
|
+
gpuDrivenShadowViews: preparedGpuDriven.shadowViewPool,
|
|
2649
|
+
gpuDrivenShadowBatchProjections: preparedGpuDriven.shadowBatchProjections,
|
|
2650
|
+
gpuDrivenShadowDrawKeys: preparedGpuDriven.shadowDrawKeys,
|
|
2651
|
+
gpuDrivenShadowDrawKeysByView: preparedGpuDriven.shadowDrawKeysByView,
|
|
2652
|
+
}),
|
|
2653
|
+
...(frameGpuDriven?.scene === undefined
|
|
2654
|
+
? {}
|
|
2655
|
+
: { gpuDrivenSceneMaterialBuffer: frameGpuDriven.scene.scene.materialBuffer }),
|
|
2155
2656
|
...(cubeCapture?.reflectionProbes === undefined
|
|
2156
2657
|
? {}
|
|
2157
2658
|
: { reflectionProbes: cubeCapture.reflectionProbes }),
|
|
2158
2659
|
pointsLines: pointsLinesOwner,
|
|
2159
2660
|
materialBgAssemblyCache: frameState.materialBgAssemblyCache,
|
|
2661
|
+
...(onRenderableDraw === undefined ? {} : { onRenderableDraw }),
|
|
2160
2662
|
directionalShadowCacheReuse: directionalShadowCache.reuse,
|
|
2663
|
+
...(shadowCasterMembership === undefined ? {} : { shadowCasterMembership }),
|
|
2664
|
+
...(shadowValidatedOrdered.length === 0
|
|
2665
|
+
? {}
|
|
2666
|
+
: { shadowValidatedOrdered, shadowMeshSsboBase }),
|
|
2667
|
+
...(shadowDispatch === undefined ? {} : { shadowDispatch }),
|
|
2668
|
+
...(shadowCasterWorldKeys === undefined ? {} : { shadowCasterWorldKeys }),
|
|
2161
2669
|
...(profilePhase !== undefined ? { profilePhase } : {}),
|
|
2162
2670
|
};
|
|
2163
2671
|
uploadMaterialUniforms(passCtx);
|
|
@@ -2188,6 +2696,37 @@ export function recordFrame(
|
|
|
2188
2696
|
frameState.temporalFrameInput = stagedTemporalFrame;
|
|
2189
2697
|
frameState.temporalFrameTransaction.stage(stagedTemporalFrame);
|
|
2190
2698
|
volumeSubmissionAttempted = volumeTopologyCandidate || activeVolumetricFogContext !== undefined;
|
|
2699
|
+
const ssrOwner = frameState.ssrHistoryOwner;
|
|
2700
|
+
const ssrCandidate = frameState.ssrHistoryCandidate;
|
|
2701
|
+
const frameHooks =
|
|
2702
|
+
preparedGpuDriven === undefined &&
|
|
2703
|
+
frameGpuDriven?.occlusion === undefined &&
|
|
2704
|
+
ssrCandidate === undefined
|
|
2705
|
+
? undefined
|
|
2706
|
+
: {
|
|
2707
|
+
afterGraphExecute: () =>
|
|
2708
|
+
frameGpuDriven?.occlusion?.recordResolve(encoder) ?? ok(undefined),
|
|
2709
|
+
onSubmitted: () => {
|
|
2710
|
+
if (ssrOwner !== undefined && ssrCandidate !== undefined) {
|
|
2711
|
+
const committed = ssrOwner.commitFrame(ssrCandidate);
|
|
2712
|
+
if (!committed.ok) {
|
|
2713
|
+
ssrOwner.abortFrame(ssrCandidate, 'submit');
|
|
2714
|
+
reportSsrHistoryFailure(internals, 'submit', committed.error);
|
|
2715
|
+
}
|
|
2716
|
+
frameState.ssrHistoryCandidate = undefined;
|
|
2717
|
+
}
|
|
2718
|
+
preparedGpuDriven?._commitResourceReplacement();
|
|
2719
|
+
frameGpuDriven?.occlusion?.commit(true, frameState.frameNumber, profilePhase);
|
|
2720
|
+
},
|
|
2721
|
+
onAborted: () => {
|
|
2722
|
+
if (ssrOwner !== undefined && ssrCandidate !== undefined) {
|
|
2723
|
+
ssrOwner.abortFrame(ssrCandidate, 'submit');
|
|
2724
|
+
frameState.ssrHistoryCandidate = undefined;
|
|
2725
|
+
}
|
|
2726
|
+
preparedGpuDriven?._abortResourceReplacement?.();
|
|
2727
|
+
frameGpuDriven?.occlusion?.commit(false, frameState.frameNumber);
|
|
2728
|
+
},
|
|
2729
|
+
};
|
|
2191
2730
|
submitted = runRecordProfilePhase(profilePhase, 'record/graph-execute', () =>
|
|
2192
2731
|
executeCompiledFrameGraph(
|
|
2193
2732
|
internals,
|
|
@@ -2197,17 +2736,7 @@ export function recordFrame(
|
|
|
2197
2736
|
profilePhase === undefined
|
|
2198
2737
|
? undefined
|
|
2199
2738
|
: (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
|
|
2200
|
-
|
|
2201
|
-
? undefined
|
|
2202
|
-
: {
|
|
2203
|
-
afterGraphExecute: () => gpuDriven.occlusion?.recordResolve(encoder) ?? ok(undefined),
|
|
2204
|
-
onSubmitted: () => {
|
|
2205
|
-
gpuDriven.occlusion?.commit(true, frameState.frameNumber, profilePhase);
|
|
2206
|
-
},
|
|
2207
|
-
onAborted: () => {
|
|
2208
|
-
gpuDriven.occlusion?.commit(false, frameState.frameNumber);
|
|
2209
|
-
},
|
|
2210
|
-
},
|
|
2739
|
+
frameHooks,
|
|
2211
2740
|
cubeCaptureWork.length === 0 ? undefined : cubeCaptureWork.map((work) => work.faceIndex),
|
|
2212
2741
|
(done) => {
|
|
2213
2742
|
completion = done;
|
|
@@ -2229,6 +2758,20 @@ export function recordFrame(
|
|
|
2229
2758
|
timingCapture,
|
|
2230
2759
|
),
|
|
2231
2760
|
);
|
|
2761
|
+
if (submitted && preparedGpuDriven?.drawKeys !== undefined && onRenderableDraw !== undefined) {
|
|
2762
|
+
for (const entry of validated) {
|
|
2763
|
+
if (gpuDrivenRenderableConsumed(entry, preparedGpuDriven.drawKeys)) {
|
|
2764
|
+
onRenderableDraw(entry);
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
if (submitted) {
|
|
2769
|
+
// Feature plans are submission-sensitive: VFX keeps queued fixed ticks
|
|
2770
|
+
// until the graph that consumed them is actually submitted. Acknowledge
|
|
2771
|
+
// the exact accepted graph here so successful frames do not replay every
|
|
2772
|
+
// prior tick into the next topology.
|
|
2773
|
+
acknowledgeRenderFeatureSubmission(getRenderFeatureGraphState(internals).plans);
|
|
2774
|
+
}
|
|
2232
2775
|
if (cubeCapture !== undefined && cubeCaptureWork.length > 0) {
|
|
2233
2776
|
const completed = cubeCapture.scheduler.completeSubmission(submitted, completion);
|
|
2234
2777
|
if (!completed.ok) internals.errorRegistry.fire(completed.error);
|
|
@@ -2310,6 +2853,10 @@ export function recordFrame(
|
|
|
2310
2853
|
}
|
|
2311
2854
|
return submitted;
|
|
2312
2855
|
} finally {
|
|
2856
|
+
if (!submitted && frameState.ssrHistoryCandidate !== undefined) {
|
|
2857
|
+
frameState.ssrHistoryOwner?.abortFrame(frameState.ssrHistoryCandidate, 'submit');
|
|
2858
|
+
frameState.ssrHistoryCandidate = undefined;
|
|
2859
|
+
}
|
|
2313
2860
|
// Volume topology replacements are provisional until the queue submission
|
|
2314
2861
|
// succeeds. This runs for every early-return path as well, so a swapchain,
|
|
2315
2862
|
// encoder, or graph failure cannot leave a non-submitted candidate as the
|
|
@@ -2420,6 +2967,15 @@ function prepareDirectionalShadowCache(
|
|
|
2420
2967
|
): DirectionalShadowCacheDecision {
|
|
2421
2968
|
const cascadeCount = lights.cascadeCount;
|
|
2422
2969
|
const lightViewProj = lights.lightViewProj;
|
|
2970
|
+
// GPU feature buffers can change without an ECS mesh mutation. Until a
|
|
2971
|
+
// producer provides a stable content revision, never reuse their shadow atlas.
|
|
2972
|
+
if (
|
|
2973
|
+
getRenderFeatureGraphState(internals).plans.some(({ plan }) =>
|
|
2974
|
+
plan.passes.some((pass) => pass.kind === 'shadow-caster'),
|
|
2975
|
+
)
|
|
2976
|
+
) {
|
|
2977
|
+
return { reuse: false, next: null };
|
|
2978
|
+
}
|
|
2423
2979
|
if (
|
|
2424
2980
|
frameState.compiledFrameGraph === null ||
|
|
2425
2981
|
frameState.compiledFrameGraphTopologyKey === null ||
|
|
@@ -2660,6 +3216,9 @@ export function validateRenderables(
|
|
|
2660
3216
|
return validated;
|
|
2661
3217
|
}
|
|
2662
3218
|
|
|
3219
|
+
// Keep the primary record-stage name explicit at its call site while sharing
|
|
3220
|
+
// the same owner implementation for the complete retained ShadowCaster view.
|
|
3221
|
+
|
|
2663
3222
|
function prepareMorphMesh(
|
|
2664
3223
|
internals: RenderSystemInternals,
|
|
2665
3224
|
frameState: RenderFrameState,
|
|
@@ -2671,9 +3230,23 @@ function prepareMorphMesh(
|
|
|
2671
3230
|
if (morph === undefined || frameState.morphBuffers === undefined) return undefined;
|
|
2672
3231
|
const targets = mesh.morphTargets;
|
|
2673
3232
|
if (targets === undefined || targets.length !== morph.targetCount) return undefined;
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
3233
|
+
const position = base.layoutProjection.attributes.find(
|
|
3234
|
+
(attribute) => attribute.key === 'position',
|
|
3235
|
+
);
|
|
3236
|
+
if (
|
|
3237
|
+
position === undefined ||
|
|
3238
|
+
position.offset !== 0 ||
|
|
3239
|
+
position.shaderLocation !== 0 ||
|
|
3240
|
+
position.format !== 'float32x3' ||
|
|
3241
|
+
position.byteLength !== 12
|
|
3242
|
+
) {
|
|
3243
|
+
return undefined;
|
|
3244
|
+
}
|
|
3245
|
+
const vertexCount = deriveVertexCount(mesh.vertices, base.layoutProjection);
|
|
3246
|
+
if (vertexCount === undefined || vertexCount !== base.vertexCount || vertexCount <= 0) {
|
|
3247
|
+
return undefined;
|
|
3248
|
+
}
|
|
3249
|
+
const stride = base.layoutProjection.arrayStride / Float32Array.BYTES_PER_ELEMENT;
|
|
2677
3250
|
|
|
2678
3251
|
let active = false;
|
|
2679
3252
|
for (const weight of morph.weights) {
|
|
@@ -2751,17 +3324,19 @@ function prepareMorphMesh(
|
|
|
2751
3324
|
*
|
|
2752
3325
|
* @internal
|
|
2753
3326
|
*/
|
|
2754
|
-
function buildDispatchPlan(
|
|
3327
|
+
export function buildDispatchPlan(
|
|
2755
3328
|
internals: RenderSystemInternals,
|
|
2756
3329
|
validated: readonly ValidatedRenderable[],
|
|
2757
3330
|
transparentDispatch: readonly DispatchEntry[],
|
|
2758
3331
|
foldBuckets: readonly FoldBucket[],
|
|
3332
|
+
shadowValidated: readonly ValidatedRenderable[] = [],
|
|
2759
3333
|
): {
|
|
2760
3334
|
validatedOrdered: readonly ValidatedRenderable[];
|
|
3335
|
+
shadowValidatedOrdered: readonly ValidatedRenderable[];
|
|
2761
3336
|
foldDispatchPlan: FoldDispatchPlan | null;
|
|
2762
3337
|
materialSlotIndices: readonly (readonly number[])[];
|
|
2763
3338
|
materialSlots: readonly MaterialSnapshot[];
|
|
2764
|
-
materialSlotOwners: readonly
|
|
3339
|
+
materialSlotOwners: readonly ValidatedRenderable[];
|
|
2765
3340
|
materialSlotCount: number;
|
|
2766
3341
|
} {
|
|
2767
3342
|
// A production GPU-owned frame has no CPU validated rows by design. The
|
|
@@ -2769,9 +3344,16 @@ function buildDispatchPlan(
|
|
|
2769
3344
|
// CPU plan, material table, or fold bucket is consumed in that lane. Return
|
|
2770
3345
|
// the empty plan without walking that list so a steady 100k scene remains
|
|
2771
3346
|
// bounded by the persistent GPU batches rather than per-entity record work.
|
|
2772
|
-
if (validated.length === 0) {
|
|
3347
|
+
if (validated.length === 0 && shadowValidated.length === 0) {
|
|
3348
|
+
const meshSsboCapResult = ensureMeshSsboCapacity(internals, shadowValidated.length);
|
|
3349
|
+
const meshSsboSlotCapacity = meshSsboSlotCapacityAfterEnsure(
|
|
3350
|
+
internals,
|
|
3351
|
+
shadowValidated.length,
|
|
3352
|
+
meshSsboCapResult,
|
|
3353
|
+
);
|
|
2773
3354
|
return {
|
|
2774
3355
|
validatedOrdered: [],
|
|
3356
|
+
shadowValidatedOrdered: limitRowsToMeshSsboCapacity(shadowValidated, meshSsboSlotCapacity),
|
|
2775
3357
|
foldDispatchPlan: null,
|
|
2776
3358
|
materialSlotIndices: [],
|
|
2777
3359
|
materialSlots: [],
|
|
@@ -2819,7 +3401,10 @@ function buildDispatchPlan(
|
|
|
2819
3401
|
validatedOrdered = ordered;
|
|
2820
3402
|
}
|
|
2821
3403
|
|
|
2822
|
-
|
|
3404
|
+
// Shadow-only/off-camera CPU residuals consume the same material UBO as
|
|
3405
|
+
// visible draws. Their preparation must not depend on main-view visibility.
|
|
3406
|
+
const materialRows = [...validatedOrdered, ...shadowValidated];
|
|
3407
|
+
const materialGroups = materialRows.map((entry): readonly MaterialSnapshot[] => {
|
|
2823
3408
|
const shaderId = entry.source.material.materialShaderId;
|
|
2824
3409
|
if (shaderId === 'forgeax::sprite' || shaderId === 'forgeax::sprite-lit') {
|
|
2825
3410
|
return [entry.source.material];
|
|
@@ -2829,8 +3414,13 @@ function buildDispatchPlan(
|
|
|
2829
3414
|
const materialSlotPlan = buildMaterialSlotPlan(materialGroups);
|
|
2830
3415
|
let materialSlotIndices = materialSlotPlan.slotIndices;
|
|
2831
3416
|
let materialSlots = materialSlotPlan.slots;
|
|
2832
|
-
let materialSlotOwners = materialSlotPlan.slotOwners
|
|
3417
|
+
let materialSlotOwners = materialSlotPlan.slotOwners.map((index) => {
|
|
3418
|
+
const owner = materialRows[index];
|
|
3419
|
+
if (owner === undefined) throw new Error('material slot has no validated owner');
|
|
3420
|
+
return owner;
|
|
3421
|
+
});
|
|
2833
3422
|
let materialSlotCount = materialSlots.length;
|
|
3423
|
+
let materialRenderableCount = materialRows.length;
|
|
2834
3424
|
|
|
2835
3425
|
// feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M3 / T-M3-04:
|
|
2836
3426
|
// ensure the mesh-SSBO + material-UBO buffer pair is large enough to hold
|
|
@@ -2855,8 +3445,13 @@ function buildDispatchPlan(
|
|
|
2855
3445
|
//
|
|
2856
3446
|
// feat-20260624 M1' / t7: sprite-lit treated identically to sprite
|
|
2857
3447
|
// for material-slot accounting (paramSchema mirror, t4).
|
|
2858
|
-
const neededSlots = Math.max(validatedOrdered.length, materialSlotCount);
|
|
3448
|
+
const neededSlots = Math.max(validatedOrdered.length + shadowValidated.length, materialSlotCount);
|
|
2859
3449
|
const meshSsboCapResult = ensureMeshSsboCapacity(internals, neededSlots);
|
|
3450
|
+
const meshSsboSlotCapacity = meshSsboSlotCapacityAfterEnsure(
|
|
3451
|
+
internals,
|
|
3452
|
+
neededSlots,
|
|
3453
|
+
meshSsboCapResult,
|
|
3454
|
+
);
|
|
2860
3455
|
if (!meshSsboCapResult.ok) {
|
|
2861
3456
|
// Graceful degradation: the controller reports slots, but this stage
|
|
2862
3457
|
// consumes entities. Find a complete prefix instead of slicing at the
|
|
@@ -2865,12 +3460,17 @@ function buildDispatchPlan(
|
|
|
2865
3460
|
materialSlotIndices,
|
|
2866
3461
|
meshSsboCapResult.degradedToSlotCount,
|
|
2867
3462
|
);
|
|
3463
|
+
materialRenderableCount = degradedRenderableCount;
|
|
2868
3464
|
materialSlotCount = materialSlotCountForPrefix(materialSlotIndices, degradedRenderableCount);
|
|
2869
3465
|
materialSlotIndices = materialSlotIndices.slice(0, degradedRenderableCount);
|
|
2870
3466
|
materialSlots = materialSlots.slice(0, materialSlotCount);
|
|
2871
3467
|
materialSlotOwners = materialSlotOwners.slice(0, materialSlotCount);
|
|
2872
3468
|
validatedOrdered = validatedOrdered.slice(0, degradedRenderableCount);
|
|
2873
3469
|
}
|
|
3470
|
+
const shadowValidatedOrdered = limitRowsToMeshSsboCapacity(
|
|
3471
|
+
shadowValidated,
|
|
3472
|
+
Math.min(meshSsboSlotCapacity, materialRenderableCount) - validatedOrdered.length,
|
|
3473
|
+
);
|
|
2874
3474
|
|
|
2875
3475
|
// feat-20260622-chunk-gpu-instancing-sprite-tilemap M1 / w4-record-swap
|
|
2876
3476
|
// (D-1): build the fold dispatch plan once `validatedOrdered` is final
|
|
@@ -2963,6 +3563,7 @@ function buildDispatchPlan(
|
|
|
2963
3563
|
|
|
2964
3564
|
return {
|
|
2965
3565
|
validatedOrdered,
|
|
3566
|
+
shadowValidatedOrdered,
|
|
2966
3567
|
foldDispatchPlan,
|
|
2967
3568
|
materialSlotIndices,
|
|
2968
3569
|
materialSlots,
|
|
@@ -3040,13 +3641,6 @@ function cleanPerFrameCaches(
|
|
|
3040
3641
|
}
|
|
3041
3642
|
}
|
|
3042
3643
|
|
|
3043
|
-
if (frameState.instanceResidency !== undefined) {
|
|
3044
|
-
const liveCollections = new Set(validated.map((entry) => entry.source.instances?.collectionId));
|
|
3045
|
-
for (const id of frameState.instanceResidency.keys()) {
|
|
3046
|
-
if (!liveCollections.has(id)) frameState.instanceResidency.delete(id);
|
|
3047
|
-
}
|
|
3048
|
-
}
|
|
3049
|
-
|
|
3050
3644
|
// Chunk keys are `${worldEntityKey}:${chunkStart}`. Keep the key opaque to
|
|
3051
3645
|
// the authoring API, but retain the numeric owner prefix for eviction.
|
|
3052
3646
|
if (frameState.instanceBufferChunks !== undefined) {
|