@forgeax/engine-render 0.1.28 → 0.1.30
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 +856 -126
- package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
- package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/device-feature-admission.d.ts +4 -3
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-host.d.ts +27 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- 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 +14 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +31 -1
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-helpers.d.ts +24 -0
- package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -0
- 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/device-loss-fanout.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/recovery-attempt.d.ts +1 -6
- package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts +7 -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 +7 -1
- 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 +2 -2
- package/dist/capture/scene-projection.d.ts +41 -0
- package/dist/capture/scene-projection.d.ts.map +1 -0
- package/dist/{chunk-J52NMQKR.mjs → chunk-7MQ3XBSP.mjs} +1098 -844
- package/dist/chunk-7MQ3XBSP.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-VSL23LZE.mjs → chunk-C3DDSXYK.mjs} +1188 -463
- package/dist/chunk-C3DDSXYK.mjs.map +1 -0
- package/dist/{chunk-IQMKJLNU.mjs → chunk-FUUO4UND.mjs} +443 -30
- package/dist/chunk-FUUO4UND.mjs.map +1 -0
- package/dist/{chunk-J6KACNOH.mjs → chunk-JRBDO2KB.mjs} +617 -7
- package/dist/chunk-JRBDO2KB.mjs.map +1 -0
- package/dist/{chunk-GE3SDD72.mjs → chunk-MDQJPY3T.mjs} +20 -40
- package/dist/chunk-MDQJPY3T.mjs.map +1 -0
- package/dist/{chunk-MYJ5RPJO.mjs → chunk-NO4WZCAH.mjs} +3 -3
- package/dist/chunk-NO4WZCAH.mjs.map +1 -0
- package/dist/{chunk-RBDBZ5R7.mjs → chunk-PA5DRJNI.mjs} +2 -2
- package/dist/chunk-PA5DRJNI.mjs.map +1 -0
- package/dist/{chunk-MBU4ZVE7.mjs → chunk-PSEQ6QPH.mjs} +4655 -892
- package/dist/chunk-PSEQ6QPH.mjs.map +1 -0
- package/dist/{chunk-JAASCMXA.mjs → chunk-S5UE2T5P.mjs} +289 -41
- package/dist/chunk-S5UE2T5P.mjs.map +1 -0
- package/dist/components/camera.d.ts +62 -2
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/directional-light.d.ts +2 -2
- package/dist/components/dynamic-resolution.d.ts +15 -0
- package/dist/components/dynamic-resolution.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +6 -20
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/reflection-probe.d.ts +1 -0
- package/dist/components/reflection-probe.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/components/skylight.d.ts +4 -1
- package/dist/components/skylight.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +14809 -7570
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts +2 -0
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +3 -14
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/dynamic-geometry.d.ts.map +1 -1
- package/dist/environment/background.d.ts +7 -0
- package/dist/environment/background.d.ts.map +1 -0
- package/dist/environment/ibl.d.ts +11 -0
- package/dist/environment/ibl.d.ts.map +1 -0
- package/dist/environment/storage.d.ts +14 -0
- package/dist/environment/storage.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 +41 -4
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +7 -0
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +50 -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/material-artifacts.d.ts +24 -0
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -0
- package/dist/gpu-driven/material-bindings.d.ts +7 -0
- package/dist/gpu-driven/material-bindings.d.ts.map +1 -0
- 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 +2 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +169 -27
- 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-resource.d.ts +5 -2
- package/dist/gpu-resource.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 +9 -0
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +2 -0
- package/dist/ibl/IblPipelineCache.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 +3 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +87 -75
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +10 -8
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +180 -1
- 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 +117 -9
- 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 +15 -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 +23 -5
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/render-extent.d.ts +51 -0
- package/dist/pipeline/render-extent.d.ts.map +1 -0
- package/dist/pipeline/standard-build-context.d.ts +7 -0
- package/dist/pipeline/standard-build-context.d.ts.map +1 -0
- package/dist/pipeline/standard-forward-lane.d.ts +3 -2
- 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/standard-profile.d.ts +2 -1
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +5 -2
- 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/plugin.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/post-process-errors.d.ts +9 -2
- package/dist/post-process-errors.d.ts.map +1 -1
- package/dist/prepare/extended-lighting/resources.d.ts +3 -3
- 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 +109 -13
- 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 +41 -5
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +13 -9
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +43 -3
- 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 +14 -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 +1 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +101 -22
- 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 +39 -8
- 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/background.d.ts +14 -0
- package/dist/reflection/background.d.ts.map +1 -0
- package/dist/reflection/filter.d.ts.map +1 -1
- package/dist/reflection/projection.d.ts +1 -0
- package/dist/reflection/projection.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts +4 -4
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +211 -53
- 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 +81 -6
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +15 -6
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +128 -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 +27 -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 +31 -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/ssao-config.d.ts +8 -6
- package/dist/ssao-config.d.ts.map +1 -1
- 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 +4 -2
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/target-coverage-attachment.d.ts +15 -0
- package/dist/temporal/target-coverage-attachment.d.ts.map +1 -0
- package/dist/temporal/temporal-view.d.ts +6 -0
- package/dist/temporal/temporal-view.d.ts.map +1 -1
- package/dist/temporal/view.d.ts +6 -0
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +41 -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/component.d.ts +1 -0
- package/dist/volume/component.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__/capture-scene-projection.unit.test.ts +24 -0
- package/src/__tests__/detached-inspection.integration.test.ts +5 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +15 -24
- package/src/__tests__/device-loss-fanout.unit.test.ts +43 -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-batch.unit.test.ts +157 -0
- package/src/__tests__/dynamic-geometry.unit.test.ts +36 -0
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +81 -0
- package/src/__tests__/factory-contract.integration.test.ts +231 -9
- 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-observation.contract.test.ts +21 -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 +307 -7
- 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 +261 -0
- package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +346 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1422 -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 +1076 -106
- 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 +160 -52
- package/src/__tests__/gpu-pass-timing-browser-runner.ts +15 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +20 -2
- package/src/__tests__/gpu-resource-scope.unit.test.ts +71 -0
- 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 +613 -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 +57 -3
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +8 -0
- package/src/__tests__/morph-neutral-admission.unit.test.ts +19 -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 +42 -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__/post-process-error-policy-owner.unit.test.ts +6 -5
- 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 +251 -9
- 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 +171 -0
- package/src/__tests__/reflection-probe-authoring.unit.test.ts +12 -10
- package/src/__tests__/reflection-probe-box.dawn.test.ts +71 -0
- package/src/__tests__/reflection-probe-filter.unit.test.ts +9 -0
- package/src/__tests__/reflection-probe-orientation.dawn.test.ts +294 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +5 -14
- package/src/__tests__/reflection-probe-updates.unit.test.ts +129 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +7 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +27 -1
- package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
- package/src/__tests__/render-extent.unit.test.ts +63 -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 +915 -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 +233 -9
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
- package/src/__tests__/shader-manifest-fixture.ts +45 -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 +125 -14
- package/src/__tests__/specular-environment-scale.dawn.test.ts +158 -0
- package/src/__tests__/ssao-config.unit.test.ts +7 -0
- package/src/__tests__/ssao-surface.dawn.test.ts +252 -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-cube-silhouette.dawn.test.ts +270 -0
- package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +99 -0
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +116 -0
- package/src/__tests__/ssr-gpu-dispatch.ts +951 -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 +84 -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 +275 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
- package/src/__tests__/standard-profile-oracle.unit.test.ts +49 -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 +199 -0
- package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
- package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +782 -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 +342 -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__/temporal-view.unit.test.ts +17 -0
- package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
- package/src/__tests__/transmission-prewarm.unit.test.ts +57 -0
- 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/__tests__/volumetric-fog-stage-readback.dawn.test.ts +62 -13
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +7 -7
- package/src/assembly/atmosphere-shader-prewarm.ts +39 -0
- package/src/assembly/device-feature-admission.ts +9 -4
- package/src/assembly/dynamic-geometry-host.ts +284 -163
- 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 +19 -0
- package/src/assembly/material/assembly.ts +383 -1
- package/src/assembly/material/pipeline-helpers.ts +100 -0
- package/src/assembly/material-shader-policy.ts +110 -46
- package/src/assembly/recovery/device-loss-fanout.ts +5 -0
- package/src/assembly/recovery/failure-location.ts +62 -0
- package/src/assembly/recovery/recovery-attempt.ts +2 -29
- package/src/assembly/recovery/renderer-recover.ts +16 -17
- package/src/assembly/renderer-facade.ts +34 -1
- 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 +46 -1
- 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 +161 -131
- 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 +744 -730
- package/src/assembly/webgpu-vertex-layouts.ts +233 -87
- package/src/capture/scene-projection.ts +37 -0
- package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
- package/src/components/__tests__/dynamic-resolution-schema.unit.test.ts +65 -0
- package/src/components/__tests__/light-radius.integration.test.ts +59 -0
- package/src/components/camera.ts +287 -7
- package/src/components/directional-light.ts +3 -3
- package/src/components/dynamic-resolution.ts +67 -0
- package/src/components/index.ts +2 -0
- package/src/components/instances.ts +45 -18
- package/src/components/light-helpers.ts +6 -1
- package/src/components/reflection-probe.ts +1 -0
- package/src/components/scene-instance.ts +2 -1
- package/src/components/screen-space-reflection.ts +21 -0
- package/src/components/skylight.ts +4 -1
- package/src/components/sprite-animation.ts +2 -2
- package/src/components/sprite-instances.ts +1 -1
- package/src/device/device-scope.ts +13 -7
- package/src/device/gpu-residency.ts +45 -42
- package/src/dynamic-geometry.ts +13 -8
- package/src/environment/background.ts +281 -0
- package/src/environment/ibl.ts +211 -0
- package/src/environment/storage.ts +63 -0
- package/src/errors/gpu-driven.ts +49 -8
- package/src/errors/render.ts +91 -3
- package/src/extract/camera.ts +37 -1
- package/src/extract/contracts.ts +10 -0
- package/src/extract/gpu-driven.ts +240 -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 +452 -108
- package/src/gpu-driven/material-artifacts.ts +179 -0
- package/src/gpu-driven/material-bindings.ts +41 -0
- package/src/gpu-driven/pbr-program.ts +31 -0
- package/src/gpu-driven/prepared-draw.ts +42 -6
- package/src/gpu-driven/production-raster.ts +2806 -716
- package/src/gpu-driven/shadow-views.ts +539 -0
- package/src/gpu-driven/view-gpu.ts +188 -41
- package/src/gpu-resource.ts +21 -2
- package/src/gpu-scene-schema.ts +61 -11
- package/src/gpu-scene.ts +197 -99
- package/src/gpu-texture-usage.ts +1 -0
- package/src/hdrp-buffers.ts +141 -36
- package/src/ibl/IblPipelineCache.ts +5 -0
- package/src/ibl/frame-resources.ts +38 -0
- package/src/ibl/skylight-bind-group.ts +32 -21
- package/src/index.ts +194 -75
- package/src/inspection-types.ts +207 -1
- 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 +194 -0
- package/src/materials.ts +44 -8
- package/src/pbr-pipeline.ts +172 -19
- package/src/pipeline/render-extent.ts +111 -0
- package/src/pipeline/standard-build-context.ts +7 -0
- package/src/pipeline/standard-forward-lane.ts +74 -29
- 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 +232 -70
- package/src/pipeline/standard-post.ts +190 -27
- package/src/pipeline/standard-profile.ts +2 -1
- package/src/pipeline-builder.ts +28 -9
- package/src/pipeline-spec-types.ts +4 -0
- package/src/pipeline-spec.ts +37 -29
- package/src/plugin.ts +4 -0
- package/src/points-lines/standard-owner.ts +6 -14
- package/src/post-process-errors.ts +26 -18
- package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +2 -2
- package/src/prepare/extended-lighting/resources.ts +3 -3
- package/src/prepare/prepared-graphics-resolver.ts +9 -2
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +68 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +45 -0
- package/src/record/__tests__/material-slot-plan.unit.test.ts +57 -0
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +20 -1
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +41 -6
- package/src/record/__tests__/temporal-payload.unit.test.ts +2 -0
- 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 +15 -12
- package/src/record/frame-snapshot.ts +146 -34
- package/src/record/frame-targets.ts +9 -0
- package/src/record/frame.ts +838 -170
- package/src/record/main-pass-geometry.ts +209 -103
- package/src/record/main-pass-material.ts +157 -40
- package/src/record/main-pass-sprite-draws.ts +16 -107
- package/src/record/main-pass.ts +141 -12
- package/src/record/material-uniforms.ts +1 -1
- package/src/record/mesh-ssbo.ts +59 -15
- package/src/record/prepared-material-bindings.ts +238 -0
- package/src/record/recovery-pipeline.ts +69 -90
- package/src/record/render-context.ts +123 -24
- package/src/record/shadow-pass.ts +1389 -230
- package/src/record/skybox-post-pass.ts +14 -3
- package/src/record/typed-frame-graph.ts +1064 -47
- package/src/record/view-ubo.ts +12 -1
- package/src/recovery/render-system-candidate.ts +135 -68
- package/src/recovery/render-system-roots.ts +113 -0
- package/src/recovery/types.ts +77 -0
- package/src/reflection/background.ts +151 -0
- package/src/reflection/filter.ts +3 -0
- package/src/reflection/projection.ts +16 -6
- package/src/reflection/record-owner.ts +308 -140
- package/src/render-contract.ts +237 -49
- package/src/render-graph-primitives.ts +34 -5
- package/src/render-pipeline.ts +90 -2
- package/src/render-system-extract-tail.ts +431 -162
- package/src/render-system-extract.ts +478 -119
- package/src/render-system-projections.ts +241 -0
- package/src/render-system.ts +715 -828
- package/src/scene/render-scene-types.ts +13 -4
- package/src/scene/render-scene.ts +577 -355
- package/src/scene/shadow-visibility.ts +21 -0
- package/src/ssao-config.ts +25 -8
- 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 +797 -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 +79 -12
- package/src/temporal/history.ts +3 -1
- package/src/temporal/inspection.ts +2 -1
- package/src/temporal/standard-scene-data.ts +10 -2
- package/src/temporal/target-coverage-attachment.ts +72 -0
- package/src/temporal/temporal-view.ts +11 -2
- package/src/temporal/view.ts +12 -2
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +219 -139
- package/src/typed-render-graph-primitives.ts +304 -74
- package/src/typed-shadow-passes.ts +122 -12
- package/src/volume/component.ts +1 -0
- package/src/volume/passes.ts +3 -3
- package/src/volume/temporal.ts +10 -0
- package/dist/chunk-GE3SDD72.mjs.map +0 -1
- package/dist/chunk-GF523LHF.mjs.map +0 -1
- package/dist/chunk-IQMKJLNU.mjs.map +0 -1
- package/dist/chunk-J52NMQKR.mjs.map +0 -1
- package/dist/chunk-J6KACNOH.mjs.map +0 -1
- package/dist/chunk-JAASCMXA.mjs.map +0 -1
- package/dist/chunk-MBU4ZVE7.mjs.map +0 -1
- package/dist/chunk-MYJ5RPJO.mjs.map +0 -1
- package/dist/chunk-RBDBZ5R7.mjs.map +0 -1
- package/dist/chunk-VSL23LZE.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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { collectGpuDrivenMaterialArtifacts } from '../gpu-driven/material-artifacts';
|
|
2
|
+
import { materialBindingKey, projectMaterialBindingClasses } from '../gpu-driven/material-bindings';
|
|
1
3
|
import { uploadMaterialUniforms } from './material-uniforms';
|
|
2
4
|
// @forgeax/engine-runtime - RenderSystem record stage: frame.
|
|
3
5
|
// Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
|
|
@@ -5,7 +7,8 @@ import { uploadMaterialUniforms } from './material-uniforms';
|
|
|
5
7
|
import { HANDLE_NINESLICE_QUAD, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
6
8
|
import type { World } from '@forgeax/engine-ecs';
|
|
7
9
|
import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
8
|
-
import {
|
|
10
|
+
import { deriveVertexCount } from '@forgeax/engine-geometry';
|
|
11
|
+
import { frustum, mat4, vec3 } from '@forgeax/engine-math';
|
|
9
12
|
import {
|
|
10
13
|
type CurrentFrameObservationLease,
|
|
11
14
|
createCurrentFrameObservationLease,
|
|
@@ -21,7 +24,7 @@ import {
|
|
|
21
24
|
type TextureView,
|
|
22
25
|
} from '@forgeax/engine-rhi';
|
|
23
26
|
import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
|
|
24
|
-
import { handleSlot, toShared } from '@forgeax/engine-types';
|
|
27
|
+
import { BUILTIN_BASE, handleSlot, toShared } from '@forgeax/engine-types';
|
|
25
28
|
import { CUBE_CAMERA_FACE_ORDER } from '../components/cube-camera';
|
|
26
29
|
import type { DirectionalShadowQuality } from '../components/directional-shadow-filter';
|
|
27
30
|
import type { MeshGpuHandles } from '../device/gpu-residency';
|
|
@@ -30,7 +33,9 @@ import {
|
|
|
30
33
|
type ObservationUnavailableReason,
|
|
31
34
|
RendererOperationError,
|
|
32
35
|
} from '../errors/render';
|
|
36
|
+
import { gpuDrivenDrawKey, gpuDrivenSourceDrawItemIndex } from '../extract/gpu-driven';
|
|
33
37
|
import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
|
|
38
|
+
import type { ShadowViewIdentity } from '../gpu-driven/shadow-views';
|
|
34
39
|
import { GpuBuffer } from '../gpu-resource';
|
|
35
40
|
import { GPU_TEXTURE_USAGE_COPY_SRC } from '../gpu-texture-usage';
|
|
36
41
|
import {
|
|
@@ -40,6 +45,7 @@ import {
|
|
|
40
45
|
GPU_BUFFER_USAGE_VERTEX,
|
|
41
46
|
} from '../gpu-usage';
|
|
42
47
|
import { disposeTransientInstanceBuffers } from '../instance-buffer-cache';
|
|
48
|
+
import { deriveRenderExtent } from '../pipeline/render-extent';
|
|
43
49
|
import { inspectStandardLighting } from '../pipeline/standard-lighting/inspection';
|
|
44
50
|
import { standardLightingTopologySignature } from '../pipeline/standard-lighting/topology';
|
|
45
51
|
import { inspectPointShadow } from '../point-shadow-inspection';
|
|
@@ -56,12 +62,22 @@ import type {
|
|
|
56
62
|
ExtractedVolumetricFog,
|
|
57
63
|
MaterialSnapshot,
|
|
58
64
|
RenderableSnapshot,
|
|
65
|
+
ShadowCasterMembership,
|
|
59
66
|
SkyboxSnapshot,
|
|
60
67
|
SkylightSnapshot,
|
|
61
68
|
} from '../render-system-extract';
|
|
62
|
-
import type {
|
|
69
|
+
import type {
|
|
70
|
+
PersistentGpuDrivenState,
|
|
71
|
+
PersistentShadowCasterProjection,
|
|
72
|
+
} from '../scene/render-scene';
|
|
63
73
|
import type { OcclusionFrameProjection } from '../scene/visibility/occlusion-runtime';
|
|
64
74
|
import { SHADOW_ATLAS_DEFAULT_LAYERS } from '../shadow-atlas';
|
|
75
|
+
import {
|
|
76
|
+
admitSsrSpatial,
|
|
77
|
+
type SsrDependenciesInspection,
|
|
78
|
+
type SsrSpatialAdmission,
|
|
79
|
+
} from '../ssr/admission';
|
|
80
|
+
import { createSsrHistoryOwner } from '../ssr/history';
|
|
65
81
|
import {
|
|
66
82
|
getTransparentSortConfig,
|
|
67
83
|
TRANSPARENT_SORT_MODE_LAYER_Y,
|
|
@@ -123,6 +139,66 @@ export type FrameObservationReadback = (
|
|
|
123
139
|
lease: CurrentFrameObservationLease,
|
|
124
140
|
) => Promise<Result<Uint8Array, Error>>;
|
|
125
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Fold World identity into the renderer-owned validation token. The GPU
|
|
144
|
+
* production owner receives only this opaque key; ECS identity and ordering
|
|
145
|
+
* stay in the record owner where the World list is authoritative.
|
|
146
|
+
*/
|
|
147
|
+
function residencyValidationCacheKey(world: World, worlds: readonly World[]): string {
|
|
148
|
+
return [world.identity, ...worlds.map((candidate) => candidate.identity)].join('\u001f');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function renderableIdentity(snapshot: Pick<RenderableSnapshot, 'worldId' | 'entityKey'>): string {
|
|
152
|
+
return `${snapshot.worldId}:${snapshot.entityKey}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* A GPU-owned indirect draw has no CPU submit callback. Keep the same
|
|
157
|
+
* record-stage consumption proof for downstream lifecycle owners by checking
|
|
158
|
+
* the concrete draw-key projection that was actually encoded for this frame.
|
|
159
|
+
*/
|
|
160
|
+
function gpuDrivenRenderableConsumed(
|
|
161
|
+
entry: ValidatedRenderable,
|
|
162
|
+
drawKeys: ReadonlySet<string>,
|
|
163
|
+
): boolean {
|
|
164
|
+
const draws = entry.source.gpuDrivenDraws;
|
|
165
|
+
if (draws === undefined || draws.length === 0) return false;
|
|
166
|
+
const worldEntity = worldEntityKey(entry.source.worldId, entry.source.entityKey);
|
|
167
|
+
return draws.some((draw, compactIndex) => {
|
|
168
|
+
const material = entry.source.materials[draw.materialSlot] ?? entry.source.material;
|
|
169
|
+
return drawKeys.has(
|
|
170
|
+
gpuDrivenDrawKey(
|
|
171
|
+
worldEntity,
|
|
172
|
+
material.materialHandle ?? -1,
|
|
173
|
+
gpuDrivenSourceDrawItemIndex(draw, compactIndex),
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Project one shared validation result into a cull-specific renderable index
|
|
181
|
+
* domain. The retained ShadowCaster projection is the superset; the primary
|
|
182
|
+
* camera submission reuses its resolved mesh rows without a second residency
|
|
183
|
+
* walk or a second validation authority.
|
|
184
|
+
*/
|
|
185
|
+
function projectValidatedRows(
|
|
186
|
+
validated: readonly ValidatedRenderable[],
|
|
187
|
+
target: readonly RenderableSnapshot[],
|
|
188
|
+
): ValidatedRenderable[] {
|
|
189
|
+
const byIdentity = new Map<string, ValidatedRenderable>();
|
|
190
|
+
for (const row of validated) byIdentity.set(renderableIdentity(row.source), row);
|
|
191
|
+
const projected: ValidatedRenderable[] = [];
|
|
192
|
+
for (let index = 0; index < target.length; index += 1) {
|
|
193
|
+
const snapshot = target[index];
|
|
194
|
+
if (snapshot === undefined) continue;
|
|
195
|
+
const row = byIdentity.get(renderableIdentity(snapshot));
|
|
196
|
+
if (row === undefined) continue;
|
|
197
|
+
projected.push({ ...row, source: snapshot, renderableIndex: index });
|
|
198
|
+
}
|
|
199
|
+
return projected;
|
|
200
|
+
}
|
|
201
|
+
|
|
126
202
|
export interface FrameObservationOptions {
|
|
127
203
|
readonly semantic: 'linear-hdr';
|
|
128
204
|
readonly readback: FrameObservationReadback;
|
|
@@ -219,7 +295,8 @@ export async function observeCurrentFrame(
|
|
|
219
295
|
});
|
|
220
296
|
}
|
|
221
297
|
|
|
222
|
-
import {
|
|
298
|
+
import { acknowledgeRenderFeatureSubmission } from '../features/host';
|
|
299
|
+
import { createTemporalView, resolveTemporalReset, type TemporalView } from '../temporal/view';
|
|
223
300
|
import { inspectVolumetricFog } from '../volume/inspection';
|
|
224
301
|
import {
|
|
225
302
|
acquireSwapChainTarget,
|
|
@@ -244,6 +321,7 @@ import {
|
|
|
244
321
|
type CubeCaptureGraphState,
|
|
245
322
|
ensureCompiledFrameGraph,
|
|
246
323
|
executeCompiledFrameGraph,
|
|
324
|
+
getRenderFeatureGraphState,
|
|
247
325
|
type RenderFeatureGraphCandidate,
|
|
248
326
|
settleVolumetricFogGraphCandidate,
|
|
249
327
|
} from './typed-frame-graph';
|
|
@@ -256,35 +334,34 @@ import {
|
|
|
256
334
|
|
|
257
335
|
/**
|
|
258
336
|
* Resolve the GPU mesh view used by the optional production raster before its
|
|
259
|
-
* filtering pass runs.
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
337
|
+
* filtering pass runs. The result is keyed by the retained RenderScene slot,
|
|
338
|
+
* which preserves the World namespace already used by gpuStore. User meshes
|
|
339
|
+
* are pull-resident during record; relying only on the boot-time builtin map
|
|
340
|
+
* would make the first frame classify every imported mesh as a CPU fallback
|
|
341
|
+
* and would hide its LOD ranges from the GPU cull. Builtins are the only
|
|
342
|
+
* process-static entries and may be read from PipelineState.meshes.
|
|
264
343
|
*/
|
|
265
|
-
function gpuDrivenMeshesForFrame(
|
|
344
|
+
export function gpuDrivenMeshesForFrame(
|
|
266
345
|
internals: RenderSystemInternals,
|
|
267
346
|
pipelineState: PipelineState,
|
|
268
347
|
worlds: readonly World[],
|
|
269
348
|
scene: PersistentGpuDrivenState | undefined,
|
|
270
349
|
): ReadonlyMap<number, MeshGpuHandles> {
|
|
271
|
-
const meshes = new Map(
|
|
350
|
+
const meshes = new Map<number, MeshGpuHandles>();
|
|
272
351
|
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
352
|
for (const slot of scene.slots) {
|
|
282
|
-
const world = worlds[slot.snapshot.worldId];
|
|
283
|
-
if (world === undefined) continue;
|
|
284
353
|
const assetHandle = slot.snapshot.assetHandle;
|
|
285
354
|
const sharedHandle = toShared<'MeshAsset'>(assetHandle);
|
|
355
|
+
const assetSlot = handleSlot(sharedHandle);
|
|
356
|
+
if (assetSlot < BUILTIN_BASE) {
|
|
357
|
+
const gpu = pipelineState.meshes.get(assetSlot);
|
|
358
|
+
if (gpu !== undefined) meshes.set(slot.slot, gpu);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const world = worlds[slot.snapshot.worldId];
|
|
362
|
+
if (world === undefined) continue;
|
|
286
363
|
let gpu = internals.gpuStore.getMeshGpuHandles(sharedHandle, world);
|
|
287
|
-
if (gpu === undefined
|
|
364
|
+
if (gpu === undefined) {
|
|
288
365
|
const asset = resolveAssetHandle<MeshAsset>(world, sharedHandle);
|
|
289
366
|
if (asset.ok) {
|
|
290
367
|
const lodMeshes = slot.snapshot.lods?.map((lod) =>
|
|
@@ -303,26 +380,11 @@ function gpuDrivenMeshesForFrame(
|
|
|
303
380
|
if (resident.ok) gpu = resident.value;
|
|
304
381
|
}
|
|
305
382
|
}
|
|
306
|
-
if (gpu !== undefined) meshes.set(
|
|
383
|
+
if (gpu !== undefined) meshes.set(slot.slot, gpu);
|
|
307
384
|
}
|
|
308
|
-
|
|
309
|
-
gpuDrivenMeshCache.set(scene.scene, {
|
|
310
|
-
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
311
|
-
pipelineMeshes: pipelineState.meshes,
|
|
312
|
-
value,
|
|
313
|
-
});
|
|
314
|
-
return value;
|
|
385
|
+
return meshes;
|
|
315
386
|
}
|
|
316
387
|
|
|
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
388
|
/**
|
|
327
389
|
* A frame-local material table. Repeated snapshot objects share one UBO slot;
|
|
328
390
|
* each renderable keeps only the slot index used by each authored material.
|
|
@@ -342,18 +404,25 @@ export interface MaterialSlotPlan<T extends object> {
|
|
|
342
404
|
*/
|
|
343
405
|
export function buildMaterialSlotPlan<T extends object>(
|
|
344
406
|
materialGroups: readonly (readonly T[])[],
|
|
407
|
+
bindingClass?: (material: T, owner: number) => string,
|
|
345
408
|
): MaterialSlotPlan<T> {
|
|
346
|
-
const slotByMaterial = new Map<T, number
|
|
409
|
+
const slotByMaterial = new Map<T, Map<string, number>>();
|
|
347
410
|
const slots: T[] = [];
|
|
348
411
|
const slotOwners: number[] = [];
|
|
349
412
|
const slotIndices = materialGroups.map((materials, ownerIndex) =>
|
|
350
413
|
materials.map((material) => {
|
|
351
|
-
const
|
|
414
|
+
const resourceClass = bindingClass?.(material, ownerIndex) ?? '';
|
|
415
|
+
let materialClasses = slotByMaterial.get(material);
|
|
416
|
+
if (materialClasses === undefined) {
|
|
417
|
+
materialClasses = new Map();
|
|
418
|
+
slotByMaterial.set(material, materialClasses);
|
|
419
|
+
}
|
|
420
|
+
const cached = materialClasses.get(resourceClass);
|
|
352
421
|
if (cached !== undefined) return cached;
|
|
353
422
|
const slot = slots.length;
|
|
354
423
|
slots.push(material);
|
|
355
424
|
slotOwners.push(ownerIndex);
|
|
356
|
-
|
|
425
|
+
materialClasses.set(resourceClass, slot);
|
|
357
426
|
return slot;
|
|
358
427
|
}),
|
|
359
428
|
);
|
|
@@ -392,9 +461,38 @@ export function materialSlotCountForPrefix(
|
|
|
392
461
|
return maxSlot + 1;
|
|
393
462
|
}
|
|
394
463
|
|
|
464
|
+
type MeshSsboCapacityResult = ReturnType<typeof ensureMeshSsboCapacity>;
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Resolve the actual mesh-SSBO ceiling after the shared grow contract ran.
|
|
468
|
+
* A missing grow controller is the legacy/test surface with no renderer-owned
|
|
469
|
+
* ceiling; a failed grow explicitly exposes the pre-grow capacity and must
|
|
470
|
+
* constrain every row written by this frame, including retained shadow rows.
|
|
471
|
+
*/
|
|
472
|
+
function meshSsboSlotCapacityAfterEnsure(
|
|
473
|
+
internals: Pick<RenderSystemInternals, 'growMeshSsbo' | 'meshSsboState'>,
|
|
474
|
+
neededSlots: number,
|
|
475
|
+
result: MeshSsboCapacityResult,
|
|
476
|
+
): number {
|
|
477
|
+
if (internals.growMeshSsbo === undefined) return Number.POSITIVE_INFINITY;
|
|
478
|
+
if (!result.ok) return Math.max(0, result.degradedToSlotCount);
|
|
479
|
+
return internals.meshSsboState?.slotCount ?? neededSlots;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Keep a row sequence inside the capacity published by the mesh-SSBO owner.
|
|
484
|
+
* The caller has already reserved earlier rows (the primary view), so this
|
|
485
|
+
* helper only accepts the remaining slots for the retained shadow sequence.
|
|
486
|
+
*/
|
|
487
|
+
function limitRowsToMeshSsboCapacity<T>(rows: readonly T[], availableSlots: number): readonly T[] {
|
|
488
|
+
if (availableSlots === Number.POSITIVE_INFINITY) return rows;
|
|
489
|
+
return rows.slice(0, Math.max(0, Math.floor(availableSlots)));
|
|
490
|
+
}
|
|
491
|
+
|
|
395
492
|
export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T) => T;
|
|
396
493
|
|
|
397
494
|
export interface CubeCaptureFrameInput {
|
|
495
|
+
readonly captureDispatch?: readonly DispatchEntry[];
|
|
398
496
|
readonly snapshots: readonly CubeCameraSnapshot[];
|
|
399
497
|
readonly scheduler: import('../capture/scheduler').CubeCaptureScheduler;
|
|
400
498
|
readonly state: CubeCaptureGraphState;
|
|
@@ -456,6 +554,18 @@ function prepareCubeCapture(
|
|
|
456
554
|
}
|
|
457
555
|
|
|
458
556
|
const EMPTY_ENTITY_KEYS: ReadonlySet<number> = new Set<number>();
|
|
557
|
+
const EMPTY_DRAW_KEYS: ReadonlySet<string> = new Set<string>();
|
|
558
|
+
|
|
559
|
+
function cacheEntityKeysFromDrawKeys(drawKeys: ReadonlySet<string>): ReadonlySet<number> {
|
|
560
|
+
const entityKeys = new Set<number>();
|
|
561
|
+
for (const key of drawKeys) {
|
|
562
|
+
const separator = key.indexOf(':');
|
|
563
|
+
if (separator <= 0) continue;
|
|
564
|
+
const entityKey = Number(key.slice(0, separator));
|
|
565
|
+
if (Number.isSafeInteger(entityKey)) entityKeys.add(entityKey);
|
|
566
|
+
}
|
|
567
|
+
return entityKeys;
|
|
568
|
+
}
|
|
459
569
|
|
|
460
570
|
function runRecordProfilePhase<T>(
|
|
461
571
|
runner: RecordProfileRunner | undefined,
|
|
@@ -465,6 +575,14 @@ function runRecordProfilePhase<T>(
|
|
|
465
575
|
return runner === undefined ? action() : runner(phase, action);
|
|
466
576
|
}
|
|
467
577
|
|
|
578
|
+
function shadowPlanes(matrix: Float32Array): Float32Array {
|
|
579
|
+
return frustum.fromViewProjection(frustum.create(), matrix as never);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function shadowViewToken(matrix: Float32Array): string {
|
|
583
|
+
return Array.from(matrix).join(',');
|
|
584
|
+
}
|
|
585
|
+
|
|
468
586
|
function halfFloatToNumber(bits: number): number {
|
|
469
587
|
const sign = (bits & 0x8000) === 0 ? 1 : -1;
|
|
470
588
|
const exponent = (bits >>> 10) & 0x1f;
|
|
@@ -552,15 +670,19 @@ async function mapReflectionFallbackReadback(
|
|
|
552
670
|
for (let y = 0; y < request.height; y += 1) {
|
|
553
671
|
for (let x = 0; x < request.width; x += 1) {
|
|
554
672
|
const offset = y * request.bytesPerRow + x * 8;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
673
|
+
// Validation above already rejects non-finite half floats. Scan the
|
|
674
|
+
// raw RGB magnitudes (both signed zero encodings are empty) without
|
|
675
|
+
// allocating/decoding millions of background pixels each frame.
|
|
676
|
+
const red = view.getUint16(offset, true);
|
|
677
|
+
const green = view.getUint16(offset + 2, true);
|
|
678
|
+
const blue = view.getUint16(offset + 4, true);
|
|
679
|
+
if ((red & 0x7fff) !== 0 || (green & 0x7fff) !== 0 || (blue & 0x7fff) !== 0) {
|
|
680
|
+
linearHdr = [
|
|
681
|
+
halfFloatToNumber(red),
|
|
682
|
+
halfFloatToNumber(green),
|
|
683
|
+
halfFloatToNumber(blue),
|
|
684
|
+
halfFloatToNumber(view.getUint16(offset + 6, true)),
|
|
685
|
+
];
|
|
564
686
|
y = request.height;
|
|
565
687
|
break;
|
|
566
688
|
}
|
|
@@ -1048,6 +1170,157 @@ function stageAcceptedVolumetricFogFrame(
|
|
|
1048
1170
|
return { ...accepted, paramsBuffer };
|
|
1049
1171
|
}
|
|
1050
1172
|
|
|
1173
|
+
function ssrRuntimeError(
|
|
1174
|
+
cause: unknown,
|
|
1175
|
+
): RhiError | { code: string; message: string; name?: string } {
|
|
1176
|
+
if (cause instanceof RhiError) return cause;
|
|
1177
|
+
if (cause instanceof Error)
|
|
1178
|
+
return { code: 'ssr-history-failure', message: cause.message, name: cause.name };
|
|
1179
|
+
return { code: 'ssr-history-failure', message: String(cause) };
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function reportSsrHistoryFailure(
|
|
1183
|
+
internals: RenderSystemInternals,
|
|
1184
|
+
stage: 'build' | 'encode' | 'finish' | 'submit',
|
|
1185
|
+
cause: unknown,
|
|
1186
|
+
): void {
|
|
1187
|
+
internals.errorRegistry.fire(
|
|
1188
|
+
new RhiError({
|
|
1189
|
+
code: 'webgpu-runtime-error',
|
|
1190
|
+
expected: `SSR history ${stage} succeeds before queue submission`,
|
|
1191
|
+
hint: 'inspect the SSR history owner failure and retry the next frame',
|
|
1192
|
+
detail: { error: ssrRuntimeError(cause) },
|
|
1193
|
+
}),
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function mapSsrHistoryResetReason(
|
|
1198
|
+
reason: TemporalView['resetReason'],
|
|
1199
|
+
): import('../ssr/history').SsrHistoryResetReason | undefined {
|
|
1200
|
+
switch (reason) {
|
|
1201
|
+
case 'first-frame':
|
|
1202
|
+
return 'first-enable';
|
|
1203
|
+
case 'resize':
|
|
1204
|
+
return 'resize';
|
|
1205
|
+
case 'camera-cut':
|
|
1206
|
+
case 'view-switch':
|
|
1207
|
+
return 'camera-cut';
|
|
1208
|
+
case 'history-version':
|
|
1209
|
+
return 'history-version';
|
|
1210
|
+
case 'environment-change':
|
|
1211
|
+
case 'fog-change':
|
|
1212
|
+
return 'reflection-generation';
|
|
1213
|
+
case 'device-recover':
|
|
1214
|
+
return 'device-recovery';
|
|
1215
|
+
case 'submit-failure':
|
|
1216
|
+
case undefined:
|
|
1217
|
+
return undefined;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function stageSsrHistoryCandidate(
|
|
1222
|
+
internals: RenderSystemInternals,
|
|
1223
|
+
frameState: RenderFrameState,
|
|
1224
|
+
camera: CameraSnapshot,
|
|
1225
|
+
width: number,
|
|
1226
|
+
height: number,
|
|
1227
|
+
admission: SsrSpatialAdmission,
|
|
1228
|
+
temporalView: TemporalView,
|
|
1229
|
+
): boolean {
|
|
1230
|
+
const previousAdmission = frameState.ssrSpatialAdmission;
|
|
1231
|
+
frameState.ssrSpatialAdmission = admission;
|
|
1232
|
+
if (admission.status !== 'admitted') {
|
|
1233
|
+
if (frameState.ssrHistoryOwner !== undefined && previousAdmission?.status === 'admitted') {
|
|
1234
|
+
const owner = frameState.ssrHistoryOwner;
|
|
1235
|
+
owner.reset('disable');
|
|
1236
|
+
owner.retireAfterFence(internals.device.queue, (cause) => {
|
|
1237
|
+
reportSsrHistoryFailure(internals, 'submit', cause);
|
|
1238
|
+
});
|
|
1239
|
+
frameState.ssrHistoryOwner = undefined;
|
|
1240
|
+
}
|
|
1241
|
+
frameState.ssrHistoryCandidate = undefined;
|
|
1242
|
+
frameState.ssrLastCameraEntity = camera.entityKey;
|
|
1243
|
+
frameState.ssrLastHistoryVersion = camera.historyVersion;
|
|
1244
|
+
return true;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
let owner = frameState.ssrHistoryOwner;
|
|
1248
|
+
if (owner === undefined) {
|
|
1249
|
+
const created = createSsrHistoryOwner({
|
|
1250
|
+
device: internals.device,
|
|
1251
|
+
scope: internals.deviceScope,
|
|
1252
|
+
width,
|
|
1253
|
+
height,
|
|
1254
|
+
});
|
|
1255
|
+
if (!created.ok) {
|
|
1256
|
+
reportSsrHistoryFailure(internals, 'build', created.error);
|
|
1257
|
+
return false;
|
|
1258
|
+
}
|
|
1259
|
+
owner = created.value;
|
|
1260
|
+
frameState.ssrHistoryOwner = owner;
|
|
1261
|
+
} else if (owner.resources.width !== width || owner.resources.height !== height) {
|
|
1262
|
+
const resized = owner.resize(width, height);
|
|
1263
|
+
if (!resized.ok) {
|
|
1264
|
+
reportSsrHistoryFailure(internals, 'build', resized.error);
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
owner.retireAfterFence(internals.device.queue, (cause) => {
|
|
1268
|
+
reportSsrHistoryFailure(internals, 'submit', cause);
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
let resetReason: import('../ssr/history').SsrHistoryResetReason | undefined;
|
|
1273
|
+
if (
|
|
1274
|
+
frameState.ssrLastCameraEntity !== undefined &&
|
|
1275
|
+
frameState.ssrLastCameraEntity !== camera.entityKey
|
|
1276
|
+
) {
|
|
1277
|
+
resetReason = 'camera-cut';
|
|
1278
|
+
} else if (
|
|
1279
|
+
frameState.ssrLastHistoryVersion !== undefined &&
|
|
1280
|
+
frameState.ssrLastHistoryVersion !== camera.historyVersion
|
|
1281
|
+
) {
|
|
1282
|
+
resetReason = 'history-version';
|
|
1283
|
+
} else {
|
|
1284
|
+
resetReason = mapSsrHistoryResetReason(temporalView.resetReason);
|
|
1285
|
+
}
|
|
1286
|
+
if (resetReason !== undefined) owner.reset(resetReason);
|
|
1287
|
+
|
|
1288
|
+
const started = owner.beginFrame();
|
|
1289
|
+
if (!started.ok) {
|
|
1290
|
+
reportSsrHistoryFailure(internals, 'build', started.error);
|
|
1291
|
+
return false;
|
|
1292
|
+
}
|
|
1293
|
+
const candidate = started.value;
|
|
1294
|
+
const payload =
|
|
1295
|
+
frameState.ssrTemporalParamsPayload?.byteLength === 32
|
|
1296
|
+
? frameState.ssrTemporalParamsPayload
|
|
1297
|
+
: new Uint8Array(32);
|
|
1298
|
+
payload.fill(0);
|
|
1299
|
+
const values = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
1300
|
+
values.setUint32(0, candidate.historyValid ? 1 : 0, true);
|
|
1301
|
+
values.setFloat32(4, 0.9, true);
|
|
1302
|
+
values.setFloat32(8, admission.config.thickness, true);
|
|
1303
|
+
values.setFloat32(12, 0.5, true);
|
|
1304
|
+
// Reuse the shared successful-submit snapshot; SSR owns no jitter sequence
|
|
1305
|
+
// or independently advancing previous-camera state.
|
|
1306
|
+
const previousJitter = frameState.lastSuccessfulTemporalView?.currentJitterUv;
|
|
1307
|
+
values.setFloat32(16, temporalView.currentJitterUv?.[0] ?? 0, true);
|
|
1308
|
+
values.setFloat32(20, temporalView.currentJitterUv?.[1] ?? 0, true);
|
|
1309
|
+
values.setFloat32(24, previousJitter?.[0] ?? 0, true);
|
|
1310
|
+
values.setFloat32(28, previousJitter?.[1] ?? 0, true);
|
|
1311
|
+
const written = internals.device.queue.writeBuffer(owner.resources.paramsBuffer, 0, payload);
|
|
1312
|
+
if (!written.ok) {
|
|
1313
|
+
owner.abortFrame(candidate, 'encode');
|
|
1314
|
+
reportSsrHistoryFailure(internals, 'encode', written.error);
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
1317
|
+
frameState.ssrTemporalParamsPayload = payload;
|
|
1318
|
+
frameState.ssrHistoryCandidate = candidate;
|
|
1319
|
+
frameState.ssrLastCameraEntity = camera.entityKey;
|
|
1320
|
+
frameState.ssrLastHistoryVersion = camera.historyVersion;
|
|
1321
|
+
return true;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1051
1324
|
function updateVolumetricFogInspection(
|
|
1052
1325
|
frameState: RenderFrameState,
|
|
1053
1326
|
fog: ExtractedVolumetricFog | undefined,
|
|
@@ -1303,7 +1576,7 @@ export function recordFrame(
|
|
|
1303
1576
|
world: World,
|
|
1304
1577
|
cameras: CameraSnapshot[],
|
|
1305
1578
|
lights: ExtractedLights,
|
|
1306
|
-
renderables: RenderableSnapshot[],
|
|
1579
|
+
renderables: readonly RenderableSnapshot[],
|
|
1307
1580
|
transparentDispatch: readonly DispatchEntry[],
|
|
1308
1581
|
frameState: RenderFrameState,
|
|
1309
1582
|
dispatchCounts: DispatchCounts,
|
|
@@ -1339,10 +1612,18 @@ export function recordFrame(
|
|
|
1339
1612
|
fogSignature = '',
|
|
1340
1613
|
environmentReady = true,
|
|
1341
1614
|
transmissionDemand?: TransmissionDemand,
|
|
1615
|
+
shadowCasterEntityKeys: ReadonlySet<number> = EMPTY_ENTITY_KEYS,
|
|
1616
|
+
shadowCasterDrawKeys: ReadonlySet<string> | undefined = undefined,
|
|
1342
1617
|
volumetricFog?: ExtractedVolumetricFog,
|
|
1343
1618
|
timingCapture?: GpuTimingCapture,
|
|
1619
|
+
shadowCasterMembership?: readonly ShadowCasterMembership[],
|
|
1620
|
+
shadowCasterProjection?: PersistentShadowCasterProjection,
|
|
1621
|
+
shadowCasterWorldKeys?: readonly number[],
|
|
1622
|
+
preparedLighting?: ReturnType<typeof prepareFrameLighting>,
|
|
1623
|
+
ssrDependencies?: SsrDependenciesInspection,
|
|
1344
1624
|
onRenderableDraw?: (entry: ValidatedRenderable) => void,
|
|
1345
1625
|
): boolean {
|
|
1626
|
+
void shadowCasterEntityKeys;
|
|
1346
1627
|
frameState.reflectionFallbackObservationSource = undefined;
|
|
1347
1628
|
frameState.reflectionFallbackCompletion = undefined;
|
|
1348
1629
|
// The `try / finally` wrapper advances `frameState.frameNumber` exactly
|
|
@@ -1392,6 +1673,7 @@ export function recordFrame(
|
|
|
1392
1673
|
}
|
|
1393
1674
|
const camera = activeCameras[0];
|
|
1394
1675
|
if (!camera) return false;
|
|
1676
|
+
frameState.ssrRequested = camera.screenSpaceReflection !== undefined;
|
|
1395
1677
|
const pipelineState = internals.getPipelineState();
|
|
1396
1678
|
if (pipelineState === null) return false;
|
|
1397
1679
|
if (pipelineState.device !== internals.device) {
|
|
@@ -1433,8 +1715,12 @@ export function recordFrame(
|
|
|
1433
1715
|
// ensure + ExtractedLights three-arm destructure (directional fallback,
|
|
1434
1716
|
// point/spot/Rect arrays, totalLightCount). Extracted to prepareFrameLighting
|
|
1435
1717
|
// (M3/w18) so recordFrame stays a skeleton.
|
|
1436
|
-
const lighting = runRecordProfilePhase(
|
|
1437
|
-
|
|
1718
|
+
const lighting = runRecordProfilePhase(
|
|
1719
|
+
profilePhase,
|
|
1720
|
+
'record/scene-state/lighting-prep',
|
|
1721
|
+
() =>
|
|
1722
|
+
preparedLighting ??
|
|
1723
|
+
prepareFrameLighting(internals, frameState, lights, camera, pipelineState),
|
|
1438
1724
|
);
|
|
1439
1725
|
if (!lighting.ok) return lighting;
|
|
1440
1726
|
const { light, totalLightCount, standard } = lighting.value;
|
|
@@ -1519,6 +1805,10 @@ export function recordFrame(
|
|
|
1519
1805
|
const effectiveShadowMapSize = resolveShadowMapSize(internals, lights);
|
|
1520
1806
|
const effectiveSpotShadowMapSize = resolveSpotShadowMapSize(internals, lights);
|
|
1521
1807
|
const graphShadowMapSize = effectiveShadowMapSize ?? effectiveSpotShadowMapSize;
|
|
1808
|
+
const hasShadowViews =
|
|
1809
|
+
(effectiveShadowMapSize ?? 0) > 0 ||
|
|
1810
|
+
lights.pointShadow.length > 0 ||
|
|
1811
|
+
lights.spot.some((spot) => spot.castShadow);
|
|
1522
1812
|
const volumeCapability = hasVolumetricFogCapability(
|
|
1523
1813
|
internals.device.caps,
|
|
1524
1814
|
internals.volumetricFogShaders,
|
|
@@ -1579,89 +1869,287 @@ export function recordFrame(
|
|
|
1579
1869
|
(volumetricFog?.status === 'degraded' && acceptedFallback !== undefined);
|
|
1580
1870
|
|
|
1581
1871
|
let preparedGpuDriven: PreparedGpuDrivenFrame | undefined;
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1872
|
+
let materialBindingClasses: ReadonlyMap<string, string> | undefined;
|
|
1873
|
+
const abortPreparedGpuDriven = (): void => {
|
|
1874
|
+
preparedGpuDriven?._abortResourceReplacement?.();
|
|
1875
|
+
};
|
|
1876
|
+
const cubeCaptureWork = prepareCubeCapture(internals, cubeCapture, camera);
|
|
1877
|
+
const reflectionFallbackCandidate = cubeCapture?.reflectionProbes?.fallbackDemand === true;
|
|
1878
|
+
// Capture views have independent draw ownership; display-camera GPU work
|
|
1879
|
+
// remains active while their faces and SSR fallback attachments are recorded.
|
|
1880
|
+
// ProbeBlendRecord is an object-level fragment ABI. The scene-index GPU
|
|
1881
|
+
// lane has no binding for that record yet, so claiming a probe-bearing
|
|
1882
|
+
// row would silently fall back to Skylight and make local diffuse probes
|
|
1883
|
+
// visually ineffective. Keep the whole frame on the CPU producer until
|
|
1884
|
+
// the scene-index lane carries the same record contract.
|
|
1885
|
+
const probeBlendFrame = renderables.some((entry) => entry.probeBlendRecord !== undefined);
|
|
1886
|
+
const frameGpuDriven = probeBlendFrame ? undefined : gpuDriven;
|
|
1887
|
+
|
|
1888
|
+
// Validate first so user meshes are resident in the same frame. The GPU
|
|
1889
|
+
// owner consumes this producer-owned map together with the builtin mesh
|
|
1890
|
+
// entries; it never resolves an asset or creates a fallback residency.
|
|
1891
|
+
// Stable persistent-scene frames reuse this exact producer result. The
|
|
1892
|
+
// cache is fenced by source identities plus mesh/device/pipeline/catalog
|
|
1893
|
+
// generations, so recovery or any scene/resource replacement performs a
|
|
1894
|
+
// fresh owner validation before a capable submission.
|
|
1895
|
+
const shadowValidationActive = hasShadowViews && shadowCasterProjection !== undefined;
|
|
1896
|
+
const validationRenderables = shadowValidationActive
|
|
1897
|
+
? shadowCasterProjection.renderables
|
|
1898
|
+
: renderables;
|
|
1899
|
+
const validationDispatch = shadowValidationActive
|
|
1900
|
+
? shadowCasterProjection.dispatch
|
|
1901
|
+
: transparentDispatch;
|
|
1902
|
+
const validationInput = {
|
|
1903
|
+
cacheKey: residencyValidationCacheKey(world, worlds),
|
|
1904
|
+
renderables: validationRenderables,
|
|
1905
|
+
transparentDispatch: validationDispatch,
|
|
1906
|
+
pipelineState,
|
|
1907
|
+
pipelineHandle: frameState.installedPipelineHandle,
|
|
1908
|
+
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
1909
|
+
deviceGeneration: internals.deviceScope.generation,
|
|
1910
|
+
assetCatalogEpoch: internals.assets.catalogEpoch,
|
|
1911
|
+
} as const;
|
|
1912
|
+
const cachedValidation = frameGpuDriven?.owner.reuseResidencyValidation(validationInput);
|
|
1913
|
+
const validatedProjection =
|
|
1914
|
+
cachedValidation ??
|
|
1915
|
+
runRecordProfilePhase(profilePhase, 'record/validation', () =>
|
|
1916
|
+
validateRenderables(
|
|
1917
|
+
internals,
|
|
1918
|
+
world,
|
|
1919
|
+
worlds,
|
|
1920
|
+
pipelineState,
|
|
1921
|
+
frameState,
|
|
1922
|
+
validationRenderables,
|
|
1923
|
+
validationDispatch,
|
|
1924
|
+
EMPTY_ENTITY_KEYS,
|
|
1925
|
+
),
|
|
1585
1926
|
);
|
|
1586
|
-
|
|
1587
|
-
|
|
1927
|
+
if (cachedValidation === undefined) {
|
|
1928
|
+
frameGpuDriven?.owner.rememberResidencyValidation(validationInput, validatedProjection);
|
|
1929
|
+
}
|
|
1930
|
+
// Camera culling only changes the primary index domain. When shadows are
|
|
1931
|
+
// active, the retained projection is the validation superset; project the
|
|
1932
|
+
// same resolved rows into the camera-visible subset without another walk.
|
|
1933
|
+
const validatedShadowForResidency = shadowValidationActive ? validatedProjection : undefined;
|
|
1934
|
+
const validatedForResidency = shadowValidationActive
|
|
1935
|
+
? projectValidatedRows(validatedProjection, renderables)
|
|
1936
|
+
: validatedProjection;
|
|
1937
|
+
|
|
1938
|
+
// S_fallback is the second output of the built-in Standard PBR BRDF. A
|
|
1939
|
+
// graph may still carry the detached owner tickets for inspection, but
|
|
1940
|
+
// the MRT is admitted only when every CPU-rendered primary row in this
|
|
1941
|
+
// frame uses that exact output contract. Unsupported compositions remain
|
|
1942
|
+
// fail-closed for SSR instead of submitting a device-invalid command.
|
|
1943
|
+
const sceneInputs =
|
|
1944
|
+
validatedForResidency.length > 0 &&
|
|
1945
|
+
validatedForResidency.every(
|
|
1946
|
+
(entry) => entry.source.material.materialShaderId === 'forgeax::default-standard-pbr',
|
|
1947
|
+
);
|
|
1948
|
+
const ssrAdmission =
|
|
1949
|
+
ssrDependencies === undefined
|
|
1950
|
+
? undefined
|
|
1951
|
+
: admitSsrSpatial({
|
|
1952
|
+
camera,
|
|
1953
|
+
environment: {
|
|
1954
|
+
lane: standard.prepared.renderPath === 'deferred' ? 'deferred' : 'forward',
|
|
1955
|
+
m0: ssrDependencies.admission,
|
|
1956
|
+
sceneInputs,
|
|
1957
|
+
// Standard geometry produces current motion/coverage in this
|
|
1958
|
+
// graph, including its first frame. Prior history is optional.
|
|
1959
|
+
temporal: sceneInputs,
|
|
1960
|
+
reflectionFallback: ssrDependencies.reflectionFallback !== undefined,
|
|
1961
|
+
capabilities: {
|
|
1962
|
+
compute: internals.device.caps.compute,
|
|
1963
|
+
storageTexture: internals.device.caps.storageTexture,
|
|
1964
|
+
rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
|
|
1965
|
+
r32floatSampledStorage: ssrDependencies.format?.verdict === 'admitted',
|
|
1966
|
+
},
|
|
1967
|
+
recovery: true,
|
|
1968
|
+
},
|
|
1969
|
+
});
|
|
1970
|
+
frameState.reflectionFallbackDemand =
|
|
1971
|
+
(reflectionFallbackCandidate || ssrAdmission?.status === 'admitted') && sceneInputs;
|
|
1972
|
+
|
|
1973
|
+
if (frameGpuDriven !== undefined) {
|
|
1974
|
+
const gpuDrivenMeshes = new Map(
|
|
1975
|
+
gpuDrivenMeshesForFrame(internals, pipelineState, worlds, frameGpuDriven.scene),
|
|
1976
|
+
);
|
|
1977
|
+
const meshSlotByIdentity = new Map(
|
|
1978
|
+
(frameGpuDriven.scene?.slots ?? []).map((slot) => [
|
|
1979
|
+
worldEntityKey(slot.snapshot.worldId, slot.snapshot.entityKey),
|
|
1980
|
+
slot.slot,
|
|
1981
|
+
]),
|
|
1982
|
+
);
|
|
1983
|
+
for (const row of [...validatedForResidency, ...(validatedShadowForResidency ?? [])]) {
|
|
1984
|
+
const slot = meshSlotByIdentity.get(
|
|
1985
|
+
worldEntityKey(row.source.worldId, row.source.entityKey),
|
|
1986
|
+
);
|
|
1987
|
+
if (slot !== undefined) gpuDrivenMeshes.set(slot, row.mesh);
|
|
1988
|
+
}
|
|
1989
|
+
// Resolve one producer artifact for every prepared draw. COLOR_0 is a
|
|
1990
|
+
// per-geometry ABI axis, so a residency-wide `.some(color)` would pair
|
|
1991
|
+
// a no-color draw with the colored receipt (or vice versa) and collapse
|
|
1992
|
+
// two distinct pipeline/projection identities before indirect record.
|
|
1993
|
+
materialBindingClasses = projectMaterialBindingClasses(
|
|
1994
|
+
validatedForResidency,
|
|
1995
|
+
transparentDispatch,
|
|
1996
|
+
cubeCapture?.reflectionProbes,
|
|
1997
|
+
);
|
|
1998
|
+
const { materialArtifacts, shadowMaterialArtifacts, firstArtifact, firstSkinArtifact } =
|
|
1999
|
+
collectGpuDrivenMaterialArtifacts({
|
|
2000
|
+
rows: validatedForResidency,
|
|
2001
|
+
shadowRows: validatedShadowForResidency ?? [],
|
|
2002
|
+
dispatch: transparentDispatch,
|
|
2003
|
+
shadowDispatch: shadowCasterProjection?.dispatch ?? [],
|
|
2004
|
+
resolve: internals.getMaterialShaderArtifact,
|
|
2005
|
+
clustered:
|
|
2006
|
+
standard.kind === 'clustered' &&
|
|
2007
|
+
(standard.prepared.local.length > 0 ||
|
|
2008
|
+
frameState.installedPipelineConfig?.ssao?.enabled === true),
|
|
2009
|
+
reflectionFallback: frameState.reflectionFallbackDemand === true,
|
|
2010
|
+
});
|
|
2011
|
+
const paletteUploadBytes = validatedForResidency.reduce(
|
|
2012
|
+
(total, row) => total + (row.source.skin?.uploadBytes ?? 0),
|
|
2013
|
+
0,
|
|
2014
|
+
);
|
|
2015
|
+
const prepared = frameGpuDriven.owner.prepare({
|
|
2016
|
+
scene: frameGpuDriven.scene,
|
|
1588
2017
|
camera,
|
|
1589
|
-
|
|
2018
|
+
meshBySlot: gpuDrivenMeshes,
|
|
1590
2019
|
viewBindGroupLayout: pipelineState.viewBindGroupLayout,
|
|
1591
2020
|
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
// longer exists in the unified pipeline state, so it must not be
|
|
1595
|
-
// inferred from `standard.kind`.
|
|
1596
|
-
hdrp: false,
|
|
1597
|
-
...(gpuDriven.activeEntityKeys === undefined
|
|
2021
|
+
materialBindingClasses,
|
|
2022
|
+
...(frameGpuDriven.activeEntityKeys === undefined
|
|
1598
2023
|
? {}
|
|
1599
|
-
: { activeEntityKeys:
|
|
1600
|
-
...(
|
|
2024
|
+
: { activeEntityKeys: frameGpuDriven.activeEntityKeys }),
|
|
2025
|
+
...(frameGpuDriven.activeEntityRevision === undefined
|
|
1601
2026
|
? {}
|
|
1602
|
-
: { activeEntityRevision:
|
|
1603
|
-
...(
|
|
2027
|
+
: { activeEntityRevision: frameGpuDriven.activeEntityRevision }),
|
|
2028
|
+
...(frameGpuDriven.telemetryCandidateCount === undefined
|
|
1604
2029
|
? {}
|
|
1605
|
-
: { telemetryCandidateCount:
|
|
1606
|
-
...(
|
|
1607
|
-
...(
|
|
1608
|
-
|
|
2030
|
+
: { telemetryCandidateCount: frameGpuDriven.telemetryCandidateCount }),
|
|
2031
|
+
...(frameGpuDriven.occlusion === undefined ? {} : { occlusion: frameGpuDriven.occlusion }),
|
|
2032
|
+
...(frameGpuDriven.scene?.structureMetrics === undefined
|
|
2033
|
+
? {}
|
|
2034
|
+
: {
|
|
2035
|
+
structureMetrics: {
|
|
2036
|
+
...frameGpuDriven.scene.structureMetrics,
|
|
2037
|
+
paletteUploadBytes,
|
|
2038
|
+
},
|
|
2039
|
+
}),
|
|
2040
|
+
...(firstArtifact === undefined ? {} : { materialArtifact: firstArtifact }),
|
|
2041
|
+
...(firstSkinArtifact === undefined ? {} : { materialSkinArtifact: firstSkinArtifact }),
|
|
2042
|
+
...(materialArtifacts.size === 0 ? {} : { materialArtifacts }),
|
|
2043
|
+
...(shadowMaterialArtifacts.size === 0 ? {} : { shadowMaterialArtifacts }),
|
|
2044
|
+
materialPipelineState: pipelineState,
|
|
2045
|
+
...(internals.getMaterialShaderPipelineEntry === undefined
|
|
2046
|
+
? {}
|
|
2047
|
+
: { materialPipelineFactory: internals.getMaterialShaderPipelineEntry }),
|
|
2048
|
+
...(shadowCasterDrawKeys === undefined ? {} : { shadowCasterDrawKeys }),
|
|
2049
|
+
...(shadowCasterMembership === undefined ? {} : { shadowCasterMembership }),
|
|
1609
2050
|
});
|
|
1610
|
-
if (prepared.ok)
|
|
1611
|
-
|
|
2051
|
+
if (!prepared.ok) {
|
|
2052
|
+
// A capable GPU-owned candidate with missing producer resources is a
|
|
2053
|
+
// structured stop, not permission to enumerate the same draw through
|
|
2054
|
+
// the CPU semantic path. Preserve the last-known-good graph and retry
|
|
2055
|
+
// after the producer publishes a validated artifact.
|
|
1612
2056
|
internals.errorRegistry.fire(prepared.error);
|
|
2057
|
+
abortPreparedGpuDriven();
|
|
2058
|
+
return false;
|
|
2059
|
+
} else if (prepared.value !== undefined) {
|
|
2060
|
+
preparedGpuDriven = prepared.value;
|
|
2061
|
+
const shadowViews: Array<{
|
|
2062
|
+
readonly identity: ShadowViewIdentity;
|
|
2063
|
+
readonly planes: Float32Array;
|
|
2064
|
+
readonly cacheToken: string;
|
|
2065
|
+
}> = [];
|
|
2066
|
+
for (const [index, matrix] of (lights.lightViewProj ?? []).entries()) {
|
|
2067
|
+
shadowViews.push({
|
|
2068
|
+
identity: { kind: 'directional', index },
|
|
2069
|
+
planes: shadowPlanes(matrix),
|
|
2070
|
+
cacheToken: `${shadowViewToken(matrix)}|target:${graphShadowMapSize}:${frameState.graphGeneration}`,
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
for (const [index, point] of lights.pointShadow.entries()) {
|
|
2074
|
+
for (let face = 0; face < 6; face += 1) {
|
|
2075
|
+
const matrix = point.shadowMatrices.subarray(face * 16, face * 16 + 16);
|
|
2076
|
+
shadowViews.push({
|
|
2077
|
+
identity: { kind: 'point', index, face },
|
|
2078
|
+
planes: shadowPlanes(matrix),
|
|
2079
|
+
cacheToken: `${shadowViewToken(matrix)}|target:${graphShadowMapSize}:${frameState.graphGeneration}`,
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
let shadowSpotIndex = 0;
|
|
2084
|
+
for (const spot of lights.spot) {
|
|
2085
|
+
if (spot.lightViewProj === undefined) continue;
|
|
2086
|
+
shadowViews.push({
|
|
2087
|
+
identity: { kind: 'spot', index: shadowSpotIndex },
|
|
2088
|
+
planes: shadowPlanes(spot.lightViewProj),
|
|
2089
|
+
cacheToken: `${shadowViewToken(spot.lightViewProj)}|target:${graphShadowMapSize}:${frameState.graphGeneration}`,
|
|
2090
|
+
});
|
|
2091
|
+
shadowSpotIndex += 1;
|
|
2092
|
+
}
|
|
2093
|
+
if (prepared.value.updateShadowViews !== undefined) {
|
|
2094
|
+
const shadowUpdate = prepared.value.updateShadowViews(shadowViews);
|
|
2095
|
+
if (!shadowUpdate.ok) {
|
|
2096
|
+
internals.errorRegistry.fire(shadowUpdate.error);
|
|
2097
|
+
abortPreparedGpuDriven();
|
|
2098
|
+
return false;
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
1613
2102
|
}
|
|
1614
2103
|
|
|
2104
|
+
const gpuOwnedDrawKeys = preparedGpuDriven?.drawKeys ?? EMPTY_DRAW_KEYS;
|
|
1615
2105
|
// The GPU lane owns only the primary opaque raster today. Directional,
|
|
1616
|
-
// point, and spot shadow views
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
const
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
frameState,
|
|
1642
|
-
renderables,
|
|
1643
|
-
transparentDispatch,
|
|
1644
|
-
cpuShadowCastersActive || cubeCaptureWork.length > 0
|
|
1645
|
-
? EMPTY_ENTITY_KEYS
|
|
1646
|
-
: gpuOwnedEntityKeys,
|
|
1647
|
-
gpuDrivenWorldKeys,
|
|
1648
|
-
),
|
|
2106
|
+
// point, and spot shadow views consume the same CPU-validated rows; they do
|
|
2107
|
+
// not require a second residency walk because the first validation already
|
|
2108
|
+
// resolved every renderable used by both lanes.
|
|
2109
|
+
// The GPU lane owns the actual geometry, but the main-pass material
|
|
2110
|
+
// producer still needs the validated snapshot rows to build the
|
|
2111
|
+
// frame-global bind-group table consumed by indirect draws. The record
|
|
2112
|
+
// loop skips claimed submeshes through `gpuDrivenDrawKeys`, so retaining
|
|
2113
|
+
// these rows does not issue duplicate CPU geometry. Reusing this one
|
|
2114
|
+
// validation result also keeps active shadow views from repeating the
|
|
2115
|
+
// residency walk.
|
|
2116
|
+
const validated = validatedForResidency;
|
|
2117
|
+
frameGpuDriven?.owner.recordCpuValidation(
|
|
2118
|
+
validated,
|
|
2119
|
+
gpuOwnedDrawKeys,
|
|
2120
|
+
validationInput.cacheKey,
|
|
2121
|
+
);
|
|
2122
|
+
const cacheValidated =
|
|
2123
|
+
validatedShadowForResidency === undefined
|
|
2124
|
+
? validated
|
|
2125
|
+
: [...validated, ...validatedShadowForResidency];
|
|
2126
|
+
cleanPerFrameCaches(
|
|
2127
|
+
internals,
|
|
2128
|
+
frameState,
|
|
2129
|
+
cacheValidated,
|
|
2130
|
+
cacheEntityKeysFromDrawKeys(gpuOwnedDrawKeys),
|
|
1649
2131
|
);
|
|
1650
|
-
gpuDriven?.owner.recordCpuValidation(validated, gpuOwnedEntityKeys, gpuDrivenWorldKeys);
|
|
1651
|
-
cleanPerFrameCaches(internals, frameState, validated, gpuOwnedEntityKeys);
|
|
1652
|
-
|
|
1653
|
-
// S_fallback is the second output of the built-in Standard PBR BRDF. A
|
|
1654
|
-
// graph may still carry the detached owner tickets for inspection, but
|
|
1655
|
-
// the MRT is admitted only when every CPU-rendered primary row in this
|
|
1656
|
-
// frame uses that exact output contract. Unsupported compositions remain
|
|
1657
|
-
// fail-closed for SSR instead of submitting a device-invalid command.
|
|
1658
|
-
frameState.reflectionFallbackDemand =
|
|
1659
|
-
reflectionFallbackCandidate &&
|
|
1660
|
-
validated.length > 0 &&
|
|
1661
|
-
validated.every(
|
|
1662
|
-
(entry) => entry.source.material.materialShaderId === 'forgeax::default-standard-pbr',
|
|
1663
|
-
);
|
|
1664
2132
|
|
|
2133
|
+
// Compile the single frame graph only after GPU preparation has projected
|
|
2134
|
+
// the current frame. This keeps the graph topology and its execution
|
|
2135
|
+
// closure on the same prepared GPU-owned draw set.
|
|
2136
|
+
|
|
2137
|
+
// RenderExtent is an authored DynamicResolution path. The native TAA path
|
|
2138
|
+
// intentionally has no extent object, so every Standard consumer keeps the
|
|
2139
|
+
// exact surface domain even when a surface is not 8-pixel aligned. An
|
|
2140
|
+
// adaptive range remains unavailable until a renderer-owned timing/
|
|
2141
|
+
// controller chain exists; do not turn maxScale into a fake performance
|
|
2142
|
+
// decision. Fixed minScale=maxScale is the only extent currently admitted.
|
|
2143
|
+
const renderExtent =
|
|
2144
|
+
camera.dynamicResolution === undefined ||
|
|
2145
|
+
camera.dynamicResolution.minScale !== camera.dynamicResolution.maxScale
|
|
2146
|
+
? undefined
|
|
2147
|
+
: deriveRenderExtent({
|
|
2148
|
+
outputWidth: Math.max(1, internals.canvas.width),
|
|
2149
|
+
outputHeight: Math.max(1, internals.canvas.height),
|
|
2150
|
+
requestedScale: camera.dynamicResolution.minScale,
|
|
2151
|
+
generation: internals.deviceScope?.generation ?? 0,
|
|
2152
|
+
});
|
|
1665
2153
|
const preflightGraph = runRecordProfilePhase(profilePhase, 'record/render-graph', () =>
|
|
1666
2154
|
ensureCompiledFrameGraph(
|
|
1667
2155
|
internals,
|
|
@@ -1677,13 +2165,19 @@ export function recordFrame(
|
|
|
1677
2165
|
cubeCapture?.state,
|
|
1678
2166
|
cameras.length === 0,
|
|
1679
2167
|
transmissionDemand,
|
|
1680
|
-
|
|
2168
|
+
frameGpuDriven?.occlusion,
|
|
1681
2169
|
graphVolumetricFog,
|
|
1682
2170
|
volumeTopologyCandidate,
|
|
1683
2171
|
standard,
|
|
2172
|
+
undefined,
|
|
2173
|
+
ssrAdmission,
|
|
2174
|
+
renderExtent,
|
|
1684
2175
|
),
|
|
1685
2176
|
);
|
|
1686
|
-
if (preflightGraph === null)
|
|
2177
|
+
if (preflightGraph === null) {
|
|
2178
|
+
abortPreparedGpuDriven();
|
|
2179
|
+
return false;
|
|
2180
|
+
}
|
|
1687
2181
|
|
|
1688
2182
|
// The compiled graph is the acceptance boundary for the prepared
|
|
1689
2183
|
// Standard lighting declaration. Upload the same declaration only after
|
|
@@ -1704,6 +2198,7 @@ export function recordFrame(
|
|
|
1704
2198
|
},
|
|
1705
2199
|
}),
|
|
1706
2200
|
);
|
|
2201
|
+
abortPreparedGpuDriven();
|
|
1707
2202
|
return false;
|
|
1708
2203
|
}
|
|
1709
2204
|
if (standard.kind === 'clustered') {
|
|
@@ -1732,6 +2227,7 @@ export function recordFrame(
|
|
|
1732
2227
|
);
|
|
1733
2228
|
if (!clusterWrite.ok) {
|
|
1734
2229
|
internals.errorRegistry.fire(clusterWrite.error);
|
|
2230
|
+
abortPreparedGpuDriven();
|
|
1735
2231
|
return false;
|
|
1736
2232
|
}
|
|
1737
2233
|
}
|
|
@@ -1744,7 +2240,10 @@ export function recordFrame(
|
|
|
1744
2240
|
const swapTarget = runRecordProfilePhase(profilePhase, 'record/swapchain', () =>
|
|
1745
2241
|
acquireSwapChainTarget(internals, pipelineState),
|
|
1746
2242
|
);
|
|
1747
|
-
if (swapTarget === null)
|
|
2243
|
+
if (swapTarget === null) {
|
|
2244
|
+
abortPreparedGpuDriven();
|
|
2245
|
+
return false;
|
|
2246
|
+
}
|
|
1748
2247
|
const currentTexture = swapTarget.currentTexture;
|
|
1749
2248
|
const view = swapTarget.view;
|
|
1750
2249
|
const targetW = swapTarget.targetW;
|
|
@@ -1770,6 +2269,12 @@ export function recordFrame(
|
|
|
1770
2269
|
environmentSignature,
|
|
1771
2270
|
fogSignature,
|
|
1772
2271
|
deviceGeneration: internals.deviceScope?.generation ?? 0,
|
|
2272
|
+
...(renderExtent === undefined
|
|
2273
|
+
? {}
|
|
2274
|
+
: {
|
|
2275
|
+
internalWidth: renderExtent.internalWidth,
|
|
2276
|
+
internalHeight: renderExtent.internalHeight,
|
|
2277
|
+
}),
|
|
1773
2278
|
...(camera.historyVersion === undefined ? {} : { historyVersion: camera.historyVersion }),
|
|
1774
2279
|
historyValid: false,
|
|
1775
2280
|
currentUnjitteredViewProjection,
|
|
@@ -1802,6 +2307,23 @@ export function recordFrame(
|
|
|
1802
2307
|
: { kind: 'off', view: temporalView };
|
|
1803
2308
|
const recordCamera: CameraSnapshot =
|
|
1804
2309
|
temporalView.mode === 'taa' ? { ...camera, temporal: temporalView } : camera;
|
|
2310
|
+
if (ssrAdmission !== undefined) {
|
|
2311
|
+
const historyWidth = Math.max(1, internals.canvas.width);
|
|
2312
|
+
const historyHeight = Math.max(1, internals.canvas.height);
|
|
2313
|
+
if (
|
|
2314
|
+
!stageSsrHistoryCandidate(
|
|
2315
|
+
internals,
|
|
2316
|
+
frameState,
|
|
2317
|
+
camera,
|
|
2318
|
+
historyWidth,
|
|
2319
|
+
historyHeight,
|
|
2320
|
+
ssrAdmission,
|
|
2321
|
+
temporalView,
|
|
2322
|
+
)
|
|
2323
|
+
) {
|
|
2324
|
+
return false;
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
1805
2327
|
let activeVolumetricFogContext: VolumetricFogFrameContext | undefined;
|
|
1806
2328
|
let activeVolumetricFogSignature: VolumeTemporalSignature | undefined;
|
|
1807
2329
|
if (graphVolumetricFog?.status === 'available' && baseVolumetricFogContext !== undefined) {
|
|
@@ -1828,6 +2350,7 @@ export function recordFrame(
|
|
|
1828
2350
|
);
|
|
1829
2351
|
if (!historyFlagWritten.ok) {
|
|
1830
2352
|
internals.errorRegistry.fire(historyFlagWritten.error);
|
|
2353
|
+
abortPreparedGpuDriven();
|
|
1831
2354
|
return false;
|
|
1832
2355
|
}
|
|
1833
2356
|
}
|
|
@@ -1903,9 +2426,19 @@ export function recordFrame(
|
|
|
1903
2426
|
// order, run the mesh-SSBO capacity gate (graceful truncation), and build
|
|
1904
2427
|
// the fold dispatch plan. Extracted to buildDispatchPlan (M3/w18).
|
|
1905
2428
|
const dispatchPlan = runRecordProfilePhase(profilePhase, 'record/dispatch-plan', () =>
|
|
1906
|
-
buildDispatchPlan(
|
|
2429
|
+
buildDispatchPlan(
|
|
2430
|
+
internals,
|
|
2431
|
+
validated,
|
|
2432
|
+
transparentDispatch,
|
|
2433
|
+
foldBuckets,
|
|
2434
|
+
validatedShadowForResidency ?? [],
|
|
2435
|
+
materialBindingClasses,
|
|
2436
|
+
),
|
|
1907
2437
|
);
|
|
1908
2438
|
const validatedOrdered = dispatchPlan.validatedOrdered;
|
|
2439
|
+
const shadowValidatedOrdered = dispatchPlan.shadowValidatedOrdered;
|
|
2440
|
+
const shadowMeshSsboBase = validatedOrdered.length;
|
|
2441
|
+
const shadowDispatch = shadowCasterProjection?.dispatch;
|
|
1909
2442
|
const foldDispatchPlan = dispatchPlan.foldDispatchPlan;
|
|
1910
2443
|
const materialSlotIndices = dispatchPlan.materialSlotIndices;
|
|
1911
2444
|
const materialSlots = dispatchPlan.materialSlots;
|
|
@@ -1937,12 +2470,13 @@ export function recordFrame(
|
|
|
1937
2470
|
const unormViewRes = internals.device.createTextureView(currentTexture, {});
|
|
1938
2471
|
if (!unormViewRes.ok) {
|
|
1939
2472
|
internals.errorRegistry.fire(unormViewRes.error);
|
|
2473
|
+
abortPreparedGpuDriven();
|
|
1940
2474
|
return false;
|
|
1941
2475
|
}
|
|
1942
2476
|
ldrSpritePassView = unormViewRes.value;
|
|
1943
2477
|
}
|
|
1944
2478
|
|
|
1945
|
-
// View / mesh uniform uploads are
|
|
2479
|
+
// View / mesh uniform uploads are needed when geometry or a feature consumes View,
|
|
1946
2480
|
// OR when a skybox is active (skybox pass reads inverseViewProj from
|
|
1947
2481
|
// the View UBO). Skip the writeBuffer round-trips on the Case E
|
|
1948
2482
|
// (clear-pass-only) path only when neither condition is met.
|
|
@@ -1957,7 +2491,10 @@ export function recordFrame(
|
|
|
1957
2491
|
);
|
|
1958
2492
|
if (
|
|
1959
2493
|
validatedOrdered.length > 0 ||
|
|
2494
|
+
shadowValidatedOrdered.length > 0 ||
|
|
2495
|
+
getRenderFeatureGraphState(internals).plans.some(({ plan }) => plan.passes.length > 0) ||
|
|
1960
2496
|
skyboxActive ||
|
|
2497
|
+
frameState.environmentFrame?.source.kind === 'atmosphere' ||
|
|
1961
2498
|
activeVolumetricFogContext !== undefined ||
|
|
1962
2499
|
preparedVolumetricFog !== undefined
|
|
1963
2500
|
) {
|
|
@@ -2000,6 +2537,15 @@ export function recordFrame(
|
|
|
2000
2537
|
validatedOrdered,
|
|
2001
2538
|
foldDispatchPlan,
|
|
2002
2539
|
);
|
|
2540
|
+
if (shadowValidatedOrdered.length > 0) {
|
|
2541
|
+
uploadMeshSsboBatch(
|
|
2542
|
+
internals.device.queue,
|
|
2543
|
+
pipelineState.meshStorageBuffer,
|
|
2544
|
+
shadowValidatedOrdered,
|
|
2545
|
+
null,
|
|
2546
|
+
shadowMeshSsboBase,
|
|
2547
|
+
);
|
|
2548
|
+
}
|
|
2003
2549
|
});
|
|
2004
2550
|
}
|
|
2005
2551
|
|
|
@@ -2022,6 +2568,7 @@ export function recordFrame(
|
|
|
2022
2568
|
const encoderResult = internals.device.createCommandEncoder({ label: 'render-system-frame' });
|
|
2023
2569
|
if (!encoderResult.ok) {
|
|
2024
2570
|
internals.errorRegistry.fire(encoderResult.error);
|
|
2571
|
+
abortPreparedGpuDriven();
|
|
2025
2572
|
return false;
|
|
2026
2573
|
}
|
|
2027
2574
|
const encoder: RhiCommandEncoder = encoderResult.value;
|
|
@@ -2100,8 +2647,6 @@ export function recordFrame(
|
|
|
2100
2647
|
const passCtx: _InternalRenderPipelineContext = {
|
|
2101
2648
|
assets: internals.assets,
|
|
2102
2649
|
world,
|
|
2103
|
-
gpuDrivenEntityKeys: gpuOwnedEntityKeys,
|
|
2104
|
-
...(gpuDrivenWorldKeys === undefined ? {} : { gpuDrivenWorldKeys }),
|
|
2105
2650
|
store: internals.gpuStore,
|
|
2106
2651
|
pipelineState,
|
|
2107
2652
|
bloomResources: pipelineState.perPassResources.getBloomResources?.() ?? null,
|
|
@@ -2111,6 +2656,7 @@ export function recordFrame(
|
|
|
2111
2656
|
clear,
|
|
2112
2657
|
targetW,
|
|
2113
2658
|
targetH,
|
|
2659
|
+
...(renderExtent === undefined ? {} : { extent: renderExtent }),
|
|
2114
2660
|
currentTexture,
|
|
2115
2661
|
camera: recordCamera,
|
|
2116
2662
|
tonemapActive,
|
|
@@ -2137,6 +2683,10 @@ export function recordFrame(
|
|
|
2137
2683
|
...(internals.volumetricFogShaders === undefined
|
|
2138
2684
|
? {}
|
|
2139
2685
|
: { volumetricFogShaders: internals.volumetricFogShaders }),
|
|
2686
|
+
...(internals.ssrShaders === undefined ? {} : { ssrShaders: internals.ssrShaders }),
|
|
2687
|
+
...(internals.atmosphereShaders === undefined
|
|
2688
|
+
? {}
|
|
2689
|
+
: { atmosphereShaders: internals.atmosphereShaders }),
|
|
2140
2690
|
...(activeVolumetricFogContext === undefined
|
|
2141
2691
|
? {}
|
|
2142
2692
|
: { volumetricFog: activeVolumetricFogContext }),
|
|
@@ -2146,24 +2696,49 @@ export function recordFrame(
|
|
|
2146
2696
|
dispatch: transparentDispatch,
|
|
2147
2697
|
hdrpClusterBindGroup,
|
|
2148
2698
|
hdrpClusterMembershipBindGroup,
|
|
2149
|
-
...(
|
|
2699
|
+
...(frameGpuDriven?.occlusion === undefined ? {} : { occlusion: frameGpuDriven.occlusion }),
|
|
2150
2700
|
standardLighting: standard,
|
|
2151
2701
|
foldDispatchPlan,
|
|
2152
2702
|
materialSlotIndices,
|
|
2153
2703
|
materialSlots,
|
|
2154
2704
|
materialSlotOwners,
|
|
2155
2705
|
materialSlotCount,
|
|
2706
|
+
...(preparedGpuDriven === undefined
|
|
2707
|
+
? {}
|
|
2708
|
+
: {
|
|
2709
|
+
gpuDrivenStandardPbrFrameResources: preparedGpuDriven.standardPbrFrameResources,
|
|
2710
|
+
gpuDrivenDrawKeys: preparedGpuDriven.drawKeys,
|
|
2711
|
+
gpuDrivenShadowViews: preparedGpuDriven.shadowViewPool,
|
|
2712
|
+
gpuDrivenShadowBatchProjections: preparedGpuDriven.shadowBatchProjections,
|
|
2713
|
+
gpuDrivenShadowDrawKeys: preparedGpuDriven.shadowDrawKeys,
|
|
2714
|
+
gpuDrivenShadowDrawKeysByView: preparedGpuDriven.shadowDrawKeysByView,
|
|
2715
|
+
}),
|
|
2716
|
+
...(frameGpuDriven?.scene === undefined
|
|
2717
|
+
? {}
|
|
2718
|
+
: { gpuDrivenSceneMaterialBuffer: frameGpuDriven.scene.scene.materialBuffer }),
|
|
2156
2719
|
...(cubeCapture?.reflectionProbes === undefined
|
|
2157
2720
|
? {}
|
|
2158
2721
|
: { reflectionProbes: cubeCapture.reflectionProbes }),
|
|
2722
|
+
...(cubeCapture?.captureDispatch === undefined
|
|
2723
|
+
? {}
|
|
2724
|
+
: { captureDispatch: cubeCapture.captureDispatch }),
|
|
2159
2725
|
pointsLines: pointsLinesOwner,
|
|
2160
2726
|
materialBgAssemblyCache: frameState.materialBgAssemblyCache,
|
|
2161
2727
|
...(onRenderableDraw === undefined ? {} : { onRenderableDraw }),
|
|
2162
2728
|
directionalShadowCacheReuse: directionalShadowCache.reuse,
|
|
2729
|
+
...(shadowCasterMembership === undefined ? {} : { shadowCasterMembership }),
|
|
2730
|
+
...(shadowValidatedOrdered.length === 0
|
|
2731
|
+
? {}
|
|
2732
|
+
: { shadowValidatedOrdered, shadowMeshSsboBase }),
|
|
2733
|
+
...(shadowDispatch === undefined ? {} : { shadowDispatch }),
|
|
2734
|
+
...(shadowCasterWorldKeys === undefined ? {} : { shadowCasterWorldKeys }),
|
|
2163
2735
|
...(profilePhase !== undefined ? { profilePhase } : {}),
|
|
2164
2736
|
};
|
|
2165
2737
|
uploadMaterialUniforms(passCtx);
|
|
2166
|
-
const fallbackReadbackRequest =
|
|
2738
|
+
const fallbackReadbackRequest =
|
|
2739
|
+
internals.captureReflectionFallbackReadback === true
|
|
2740
|
+
? createReflectionFallbackReadbackRequest(internals, frameState)
|
|
2741
|
+
: undefined;
|
|
2167
2742
|
if (fallbackReadbackRequest !== undefined) {
|
|
2168
2743
|
frameState.reflectionFallbackReadback = fallbackReadbackRequest;
|
|
2169
2744
|
}
|
|
@@ -2190,6 +2765,37 @@ export function recordFrame(
|
|
|
2190
2765
|
frameState.temporalFrameInput = stagedTemporalFrame;
|
|
2191
2766
|
frameState.temporalFrameTransaction.stage(stagedTemporalFrame);
|
|
2192
2767
|
volumeSubmissionAttempted = volumeTopologyCandidate || activeVolumetricFogContext !== undefined;
|
|
2768
|
+
const ssrOwner = frameState.ssrHistoryOwner;
|
|
2769
|
+
const ssrCandidate = frameState.ssrHistoryCandidate;
|
|
2770
|
+
const frameHooks =
|
|
2771
|
+
preparedGpuDriven === undefined &&
|
|
2772
|
+
frameGpuDriven?.occlusion === undefined &&
|
|
2773
|
+
ssrCandidate === undefined
|
|
2774
|
+
? undefined
|
|
2775
|
+
: {
|
|
2776
|
+
afterGraphExecute: () =>
|
|
2777
|
+
frameGpuDriven?.occlusion?.recordResolve(encoder) ?? ok(undefined),
|
|
2778
|
+
onSubmitted: () => {
|
|
2779
|
+
if (ssrOwner !== undefined && ssrCandidate !== undefined) {
|
|
2780
|
+
const committed = ssrOwner.commitFrame(ssrCandidate);
|
|
2781
|
+
if (!committed.ok) {
|
|
2782
|
+
ssrOwner.abortFrame(ssrCandidate, 'submit');
|
|
2783
|
+
reportSsrHistoryFailure(internals, 'submit', committed.error);
|
|
2784
|
+
}
|
|
2785
|
+
frameState.ssrHistoryCandidate = undefined;
|
|
2786
|
+
}
|
|
2787
|
+
preparedGpuDriven?._commitResourceReplacement();
|
|
2788
|
+
frameGpuDriven?.occlusion?.commit(true, frameState.frameNumber, profilePhase);
|
|
2789
|
+
},
|
|
2790
|
+
onAborted: () => {
|
|
2791
|
+
if (ssrOwner !== undefined && ssrCandidate !== undefined) {
|
|
2792
|
+
ssrOwner.abortFrame(ssrCandidate, 'submit');
|
|
2793
|
+
frameState.ssrHistoryCandidate = undefined;
|
|
2794
|
+
}
|
|
2795
|
+
preparedGpuDriven?._abortResourceReplacement?.();
|
|
2796
|
+
frameGpuDriven?.occlusion?.commit(false, frameState.frameNumber);
|
|
2797
|
+
},
|
|
2798
|
+
};
|
|
2193
2799
|
submitted = runRecordProfilePhase(profilePhase, 'record/graph-execute', () =>
|
|
2194
2800
|
executeCompiledFrameGraph(
|
|
2195
2801
|
internals,
|
|
@@ -2199,17 +2805,7 @@ export function recordFrame(
|
|
|
2199
2805
|
profilePhase === undefined
|
|
2200
2806
|
? undefined
|
|
2201
2807
|
: (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
|
|
2202
|
-
|
|
2203
|
-
? undefined
|
|
2204
|
-
: {
|
|
2205
|
-
afterGraphExecute: () => gpuDriven.occlusion?.recordResolve(encoder) ?? ok(undefined),
|
|
2206
|
-
onSubmitted: () => {
|
|
2207
|
-
gpuDriven.occlusion?.commit(true, frameState.frameNumber, profilePhase);
|
|
2208
|
-
},
|
|
2209
|
-
onAborted: () => {
|
|
2210
|
-
gpuDriven.occlusion?.commit(false, frameState.frameNumber);
|
|
2211
|
-
},
|
|
2212
|
-
},
|
|
2808
|
+
frameHooks,
|
|
2213
2809
|
cubeCaptureWork.length === 0 ? undefined : cubeCaptureWork.map((work) => work.faceIndex),
|
|
2214
2810
|
(done) => {
|
|
2215
2811
|
completion = done;
|
|
@@ -2231,6 +2827,20 @@ export function recordFrame(
|
|
|
2231
2827
|
timingCapture,
|
|
2232
2828
|
),
|
|
2233
2829
|
);
|
|
2830
|
+
if (submitted && preparedGpuDriven?.drawKeys !== undefined && onRenderableDraw !== undefined) {
|
|
2831
|
+
for (const entry of validated) {
|
|
2832
|
+
if (gpuDrivenRenderableConsumed(entry, preparedGpuDriven.drawKeys)) {
|
|
2833
|
+
onRenderableDraw(entry);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
if (submitted) {
|
|
2838
|
+
// Feature plans are submission-sensitive: VFX keeps queued fixed ticks
|
|
2839
|
+
// until the graph that consumed them is actually submitted. Acknowledge
|
|
2840
|
+
// the exact accepted graph here so successful frames do not replay every
|
|
2841
|
+
// prior tick into the next topology.
|
|
2842
|
+
acknowledgeRenderFeatureSubmission(getRenderFeatureGraphState(internals).plans);
|
|
2843
|
+
}
|
|
2234
2844
|
if (cubeCapture !== undefined && cubeCaptureWork.length > 0) {
|
|
2235
2845
|
const completed = cubeCapture.scheduler.completeSubmission(submitted, completion);
|
|
2236
2846
|
if (!completed.ok) internals.errorRegistry.fire(completed.error);
|
|
@@ -2238,21 +2848,20 @@ export function recordFrame(
|
|
|
2238
2848
|
if (!submitted && fallbackReadbackRequest !== undefined) {
|
|
2239
2849
|
internals.device.destroyBuffer(fallbackReadbackRequest.buffer);
|
|
2240
2850
|
}
|
|
2851
|
+
const fallbackSource =
|
|
2852
|
+
frameState.reflectionFallbackObservationSource as RenderFrameState['reflectionFallbackObservationSource'];
|
|
2241
2853
|
const reflectionFallbackCompletion = cubeCapture?.reflectionProbes?.completeSubmission(
|
|
2242
2854
|
submitted,
|
|
2243
2855
|
completion,
|
|
2244
|
-
|
|
2856
|
+
fallbackSource === undefined
|
|
2245
2857
|
? undefined
|
|
2246
2858
|
: {
|
|
2247
|
-
format:
|
|
2248
|
-
size:
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
graphGeneration: fallbackReadbackRequest.expected.graphGeneration,
|
|
2254
|
-
textureIdentity: fallbackReadbackRequest.expected.textureIdentity,
|
|
2255
|
-
readback: fallbackReadback,
|
|
2859
|
+
format: fallbackSource.descriptor.format,
|
|
2860
|
+
size: fallbackSource.descriptor.size,
|
|
2861
|
+
frameId: fallbackSource.frameId,
|
|
2862
|
+
graphGeneration: frameState.graphGeneration,
|
|
2863
|
+
textureIdentity: getTextureIdentity(fallbackSource.texture),
|
|
2864
|
+
...(fallbackReadback === undefined ? {} : { readback: fallbackReadback }),
|
|
2256
2865
|
},
|
|
2257
2866
|
frameState.reflectionFallbackDemand,
|
|
2258
2867
|
);
|
|
@@ -2312,6 +2921,10 @@ export function recordFrame(
|
|
|
2312
2921
|
}
|
|
2313
2922
|
return submitted;
|
|
2314
2923
|
} finally {
|
|
2924
|
+
if (!submitted && frameState.ssrHistoryCandidate !== undefined) {
|
|
2925
|
+
frameState.ssrHistoryOwner?.abortFrame(frameState.ssrHistoryCandidate, 'submit');
|
|
2926
|
+
frameState.ssrHistoryCandidate = undefined;
|
|
2927
|
+
}
|
|
2315
2928
|
// Volume topology replacements are provisional until the queue submission
|
|
2316
2929
|
// succeeds. This runs for every early-return path as well, so a swapchain,
|
|
2317
2930
|
// encoder, or graph failure cannot leave a non-submitted candidate as the
|
|
@@ -2422,6 +3035,15 @@ function prepareDirectionalShadowCache(
|
|
|
2422
3035
|
): DirectionalShadowCacheDecision {
|
|
2423
3036
|
const cascadeCount = lights.cascadeCount;
|
|
2424
3037
|
const lightViewProj = lights.lightViewProj;
|
|
3038
|
+
// GPU feature buffers can change without an ECS mesh mutation. Until a
|
|
3039
|
+
// producer provides a stable content revision, never reuse their shadow atlas.
|
|
3040
|
+
if (
|
|
3041
|
+
getRenderFeatureGraphState(internals).plans.some(({ plan }) =>
|
|
3042
|
+
plan.passes.some((pass) => pass.kind === 'shadow-caster'),
|
|
3043
|
+
)
|
|
3044
|
+
) {
|
|
3045
|
+
return { reuse: false, next: null };
|
|
3046
|
+
}
|
|
2425
3047
|
if (
|
|
2426
3048
|
frameState.compiledFrameGraph === null ||
|
|
2427
3049
|
frameState.compiledFrameGraphTopologyKey === null ||
|
|
@@ -2662,6 +3284,9 @@ export function validateRenderables(
|
|
|
2662
3284
|
return validated;
|
|
2663
3285
|
}
|
|
2664
3286
|
|
|
3287
|
+
// Keep the primary record-stage name explicit at its call site while sharing
|
|
3288
|
+
// the same owner implementation for the complete retained ShadowCaster view.
|
|
3289
|
+
|
|
2665
3290
|
function prepareMorphMesh(
|
|
2666
3291
|
internals: RenderSystemInternals,
|
|
2667
3292
|
frameState: RenderFrameState,
|
|
@@ -2673,9 +3298,23 @@ function prepareMorphMesh(
|
|
|
2673
3298
|
if (morph === undefined || frameState.morphBuffers === undefined) return undefined;
|
|
2674
3299
|
const targets = mesh.morphTargets;
|
|
2675
3300
|
if (targets === undefined || targets.length !== morph.targetCount) return undefined;
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
3301
|
+
const position = base.layoutProjection.attributes.find(
|
|
3302
|
+
(attribute) => attribute.key === 'position',
|
|
3303
|
+
);
|
|
3304
|
+
if (
|
|
3305
|
+
position === undefined ||
|
|
3306
|
+
position.offset !== 0 ||
|
|
3307
|
+
position.shaderLocation !== 0 ||
|
|
3308
|
+
position.format !== 'float32x3' ||
|
|
3309
|
+
position.byteLength !== 12
|
|
3310
|
+
) {
|
|
3311
|
+
return undefined;
|
|
3312
|
+
}
|
|
3313
|
+
const vertexCount = deriveVertexCount(mesh.vertices, base.layoutProjection);
|
|
3314
|
+
if (vertexCount === undefined || vertexCount !== base.vertexCount || vertexCount <= 0) {
|
|
3315
|
+
return undefined;
|
|
3316
|
+
}
|
|
3317
|
+
const stride = base.layoutProjection.arrayStride / Float32Array.BYTES_PER_ELEMENT;
|
|
2679
3318
|
|
|
2680
3319
|
let active = false;
|
|
2681
3320
|
for (const weight of morph.weights) {
|
|
@@ -2753,17 +3392,20 @@ function prepareMorphMesh(
|
|
|
2753
3392
|
*
|
|
2754
3393
|
* @internal
|
|
2755
3394
|
*/
|
|
2756
|
-
function buildDispatchPlan(
|
|
3395
|
+
export function buildDispatchPlan(
|
|
2757
3396
|
internals: RenderSystemInternals,
|
|
2758
3397
|
validated: readonly ValidatedRenderable[],
|
|
2759
3398
|
transparentDispatch: readonly DispatchEntry[],
|
|
2760
3399
|
foldBuckets: readonly FoldBucket[],
|
|
3400
|
+
shadowValidated: readonly ValidatedRenderable[] = [],
|
|
3401
|
+
materialBindingClasses?: ReadonlyMap<string, string>,
|
|
2761
3402
|
): {
|
|
2762
3403
|
validatedOrdered: readonly ValidatedRenderable[];
|
|
3404
|
+
shadowValidatedOrdered: readonly ValidatedRenderable[];
|
|
2763
3405
|
foldDispatchPlan: FoldDispatchPlan | null;
|
|
2764
3406
|
materialSlotIndices: readonly (readonly number[])[];
|
|
2765
3407
|
materialSlots: readonly MaterialSnapshot[];
|
|
2766
|
-
materialSlotOwners: readonly
|
|
3408
|
+
materialSlotOwners: readonly ValidatedRenderable[];
|
|
2767
3409
|
materialSlotCount: number;
|
|
2768
3410
|
} {
|
|
2769
3411
|
// A production GPU-owned frame has no CPU validated rows by design. The
|
|
@@ -2771,9 +3413,16 @@ function buildDispatchPlan(
|
|
|
2771
3413
|
// CPU plan, material table, or fold bucket is consumed in that lane. Return
|
|
2772
3414
|
// the empty plan without walking that list so a steady 100k scene remains
|
|
2773
3415
|
// bounded by the persistent GPU batches rather than per-entity record work.
|
|
2774
|
-
if (validated.length === 0) {
|
|
3416
|
+
if (validated.length === 0 && shadowValidated.length === 0) {
|
|
3417
|
+
const meshSsboCapResult = ensureMeshSsboCapacity(internals, shadowValidated.length);
|
|
3418
|
+
const meshSsboSlotCapacity = meshSsboSlotCapacityAfterEnsure(
|
|
3419
|
+
internals,
|
|
3420
|
+
shadowValidated.length,
|
|
3421
|
+
meshSsboCapResult,
|
|
3422
|
+
);
|
|
2775
3423
|
return {
|
|
2776
3424
|
validatedOrdered: [],
|
|
3425
|
+
shadowValidatedOrdered: limitRowsToMeshSsboCapacity(shadowValidated, meshSsboSlotCapacity),
|
|
2777
3426
|
foldDispatchPlan: null,
|
|
2778
3427
|
materialSlotIndices: [],
|
|
2779
3428
|
materialSlots: [],
|
|
@@ -2821,18 +3470,33 @@ function buildDispatchPlan(
|
|
|
2821
3470
|
validatedOrdered = ordered;
|
|
2822
3471
|
}
|
|
2823
3472
|
|
|
2824
|
-
|
|
3473
|
+
// Shadow-only/off-camera CPU residuals consume the same material UBO as
|
|
3474
|
+
// visible draws. Their preparation must not depend on main-view visibility.
|
|
3475
|
+
const materialRows = [...validatedOrdered, ...shadowValidated];
|
|
3476
|
+
const materialGroups = materialRows.map((entry): readonly MaterialSnapshot[] => {
|
|
2825
3477
|
const shaderId = entry.source.material.materialShaderId;
|
|
2826
3478
|
if (shaderId === 'forgeax::sprite' || shaderId === 'forgeax::sprite-lit') {
|
|
2827
3479
|
return [entry.source.material];
|
|
2828
3480
|
}
|
|
2829
3481
|
return entry.source.materials.length > 0 ? entry.source.materials : [entry.source.material];
|
|
2830
3482
|
});
|
|
2831
|
-
const materialSlotPlan = buildMaterialSlotPlan(materialGroups)
|
|
3483
|
+
const materialSlotPlan = buildMaterialSlotPlan(materialGroups, (material, owner) => {
|
|
3484
|
+
const row = materialRows[owner];
|
|
3485
|
+
return row === undefined
|
|
3486
|
+
? ''
|
|
3487
|
+
: (materialBindingClasses?.get(
|
|
3488
|
+
materialBindingKey(row.source, material.materialHandle ?? -1),
|
|
3489
|
+
) ?? '');
|
|
3490
|
+
});
|
|
2832
3491
|
let materialSlotIndices = materialSlotPlan.slotIndices;
|
|
2833
3492
|
let materialSlots = materialSlotPlan.slots;
|
|
2834
|
-
let materialSlotOwners = materialSlotPlan.slotOwners
|
|
3493
|
+
let materialSlotOwners = materialSlotPlan.slotOwners.map((index) => {
|
|
3494
|
+
const owner = materialRows[index];
|
|
3495
|
+
if (owner === undefined) throw new Error('material slot has no validated owner');
|
|
3496
|
+
return owner;
|
|
3497
|
+
});
|
|
2835
3498
|
let materialSlotCount = materialSlots.length;
|
|
3499
|
+
let materialRenderableCount = materialRows.length;
|
|
2836
3500
|
|
|
2837
3501
|
// feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M3 / T-M3-04:
|
|
2838
3502
|
// ensure the mesh-SSBO + material-UBO buffer pair is large enough to hold
|
|
@@ -2857,8 +3521,13 @@ function buildDispatchPlan(
|
|
|
2857
3521
|
//
|
|
2858
3522
|
// feat-20260624 M1' / t7: sprite-lit treated identically to sprite
|
|
2859
3523
|
// for material-slot accounting (paramSchema mirror, t4).
|
|
2860
|
-
const neededSlots = Math.max(validatedOrdered.length, materialSlotCount);
|
|
3524
|
+
const neededSlots = Math.max(validatedOrdered.length + shadowValidated.length, materialSlotCount);
|
|
2861
3525
|
const meshSsboCapResult = ensureMeshSsboCapacity(internals, neededSlots);
|
|
3526
|
+
const meshSsboSlotCapacity = meshSsboSlotCapacityAfterEnsure(
|
|
3527
|
+
internals,
|
|
3528
|
+
neededSlots,
|
|
3529
|
+
meshSsboCapResult,
|
|
3530
|
+
);
|
|
2862
3531
|
if (!meshSsboCapResult.ok) {
|
|
2863
3532
|
// Graceful degradation: the controller reports slots, but this stage
|
|
2864
3533
|
// consumes entities. Find a complete prefix instead of slicing at the
|
|
@@ -2867,12 +3536,17 @@ function buildDispatchPlan(
|
|
|
2867
3536
|
materialSlotIndices,
|
|
2868
3537
|
meshSsboCapResult.degradedToSlotCount,
|
|
2869
3538
|
);
|
|
3539
|
+
materialRenderableCount = degradedRenderableCount;
|
|
2870
3540
|
materialSlotCount = materialSlotCountForPrefix(materialSlotIndices, degradedRenderableCount);
|
|
2871
3541
|
materialSlotIndices = materialSlotIndices.slice(0, degradedRenderableCount);
|
|
2872
3542
|
materialSlots = materialSlots.slice(0, materialSlotCount);
|
|
2873
3543
|
materialSlotOwners = materialSlotOwners.slice(0, materialSlotCount);
|
|
2874
3544
|
validatedOrdered = validatedOrdered.slice(0, degradedRenderableCount);
|
|
2875
3545
|
}
|
|
3546
|
+
const shadowValidatedOrdered = limitRowsToMeshSsboCapacity(
|
|
3547
|
+
shadowValidated,
|
|
3548
|
+
Math.min(meshSsboSlotCapacity, materialRenderableCount) - validatedOrdered.length,
|
|
3549
|
+
);
|
|
2876
3550
|
|
|
2877
3551
|
// feat-20260622-chunk-gpu-instancing-sprite-tilemap M1 / w4-record-swap
|
|
2878
3552
|
// (D-1): build the fold dispatch plan once `validatedOrdered` is final
|
|
@@ -2965,6 +3639,7 @@ function buildDispatchPlan(
|
|
|
2965
3639
|
|
|
2966
3640
|
return {
|
|
2967
3641
|
validatedOrdered,
|
|
3642
|
+
shadowValidatedOrdered,
|
|
2968
3643
|
foldDispatchPlan,
|
|
2969
3644
|
materialSlotIndices,
|
|
2970
3645
|
materialSlots,
|
|
@@ -3042,13 +3717,6 @@ function cleanPerFrameCaches(
|
|
|
3042
3717
|
}
|
|
3043
3718
|
}
|
|
3044
3719
|
|
|
3045
|
-
if (frameState.instanceResidency !== undefined) {
|
|
3046
|
-
const liveCollections = new Set(validated.map((entry) => entry.source.instances?.collectionId));
|
|
3047
|
-
for (const id of frameState.instanceResidency.keys()) {
|
|
3048
|
-
if (!liveCollections.has(id)) frameState.instanceResidency.delete(id);
|
|
3049
|
-
}
|
|
3050
|
-
}
|
|
3051
|
-
|
|
3052
3720
|
// Chunk keys are `${worldEntityKey}:${chunkStart}`. Keep the key opaque to
|
|
3053
3721
|
// the authoring API, but retain the numeric owner prefix for eviction.
|
|
3054
3722
|
if (frameState.instanceBufferChunks !== undefined) {
|