@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
|
@@ -3,41 +3,40 @@ import { componentId } from '@forgeax/engine-ecs/internal';
|
|
|
3
3
|
import type { RenderReadLease, RenderReadVersion } from '@forgeax/engine-ecs/projection';
|
|
4
4
|
import { box3, frustum, mat4 } from '@forgeax/engine-math';
|
|
5
5
|
import { type RhiDevice, RhiError } from '@forgeax/engine-rhi';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ChildOf,
|
|
8
|
+
collectSubtree,
|
|
9
|
+
GlobalTransform,
|
|
10
|
+
MorphWeights,
|
|
11
|
+
Transform,
|
|
12
|
+
} from '@forgeax/engine-scene';
|
|
7
13
|
import { Skin } from '@forgeax/engine-skinning';
|
|
8
14
|
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
9
15
|
import {
|
|
10
|
-
Camera,
|
|
11
|
-
DirectionalLight,
|
|
12
16
|
Instances,
|
|
13
17
|
Layer,
|
|
14
|
-
LightProbe,
|
|
15
18
|
Lines,
|
|
16
19
|
MeshFilter,
|
|
17
20
|
MeshRenderer,
|
|
18
|
-
MotionBlur,
|
|
19
|
-
PointLight,
|
|
20
|
-
PointLightShadow,
|
|
21
21
|
Points,
|
|
22
|
-
PostProcessParams,
|
|
23
|
-
ReflectionProbe,
|
|
24
|
-
SkyboxBackground,
|
|
25
|
-
Skylight,
|
|
26
22
|
SortKey,
|
|
27
|
-
SpotLight,
|
|
28
23
|
SpriteInstances,
|
|
29
24
|
SpriteRegionOverride,
|
|
30
25
|
Visibility,
|
|
31
26
|
} from '../components';
|
|
32
27
|
import type { DeviceScope, LifecycleResourceSpec } from '../device/device-scope';
|
|
28
|
+
import { gpuDrivenShadowDrawKey } from '../extract/gpu-driven';
|
|
33
29
|
import {
|
|
34
30
|
BatchTopology,
|
|
35
31
|
type BatchTopologyInspection,
|
|
36
32
|
type SubmissionPlan,
|
|
37
33
|
} from '../gpu-driven/batch-topology';
|
|
38
34
|
import { GpuScene } from '../gpu-scene';
|
|
39
|
-
import type {
|
|
40
|
-
|
|
35
|
+
import type {
|
|
36
|
+
GpuDrivenStructureMetrics,
|
|
37
|
+
PersistentRenderSceneInspection,
|
|
38
|
+
} from '../inspection-types';
|
|
39
|
+
import type { InstanceCollectionStore } from '../instances';
|
|
41
40
|
import { fingerprintNumericArray, InstanceBoundsCache } from '../instances-derived-bounds';
|
|
42
41
|
import type { PointsLinesInspection } from '../points-lines/inspection';
|
|
43
42
|
import type { PointsLinesRetainedSnapshot } from '../points-lines/snapshot';
|
|
@@ -57,13 +56,11 @@ import type {
|
|
|
57
56
|
RenderableSnapshot,
|
|
58
57
|
RenderableTemporalSnapshot,
|
|
59
58
|
} from '../render-system-extract';
|
|
60
|
-
import type { SkinPaletteAllocator } from '../systems/skin-palette-allocator';
|
|
61
59
|
import {
|
|
62
60
|
PersistentTransmissionDemandProjection,
|
|
63
61
|
type TransmissionDemand,
|
|
64
62
|
type TransmissionDemandOperation,
|
|
65
63
|
} from '../transmission/projection';
|
|
66
|
-
import { VolumetricFog } from '../volume/component';
|
|
67
64
|
import { ProbeBlendSceneProjection, type ProbeSceneObjectInput } from './probe-blend';
|
|
68
65
|
import type {
|
|
69
66
|
RenderSceneApplyResult,
|
|
@@ -74,6 +71,7 @@ import type {
|
|
|
74
71
|
RenderSceneResyncReason,
|
|
75
72
|
RenderSceneSlot,
|
|
76
73
|
} from './render-scene-types';
|
|
74
|
+
import { buildShadowFrusta } from './shadow-visibility';
|
|
77
75
|
import { createVisibilityBudget, type VisibilityBudget } from './visibility/budget';
|
|
78
76
|
import {
|
|
79
77
|
primitiveKey,
|
|
@@ -103,6 +101,10 @@ interface PendingIdentity {
|
|
|
103
101
|
current: RenderableSnapshot | undefined;
|
|
104
102
|
removed: boolean;
|
|
105
103
|
contentChanged: boolean;
|
|
104
|
+
/** An Instances row changed during this apply batch. */
|
|
105
|
+
instanceChanged: boolean;
|
|
106
|
+
/** A retained world transform changed during this apply batch. */
|
|
107
|
+
transformChanged: boolean;
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
interface WorldTransformSpan {
|
|
@@ -137,6 +139,16 @@ export interface RenderSceneSubmissionDelta {
|
|
|
137
139
|
readonly slots: readonly number[];
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Complete ShadowCaster producer projection retained beside the camera-cull
|
|
144
|
+
* submission frame. The persistent scene owns this sequence; record consumes
|
|
145
|
+
* it for the CPU residual lane without rebuilding a second scene projection.
|
|
146
|
+
*/
|
|
147
|
+
export interface PersistentShadowCasterProjection {
|
|
148
|
+
readonly renderables: readonly RenderableSnapshot[];
|
|
149
|
+
readonly dispatch: readonly DispatchEntry[];
|
|
150
|
+
}
|
|
151
|
+
|
|
140
152
|
function identityKey(worldId: number, entityKey: number): string {
|
|
141
153
|
return `${worldId}:${entityKey}`;
|
|
142
154
|
}
|
|
@@ -222,13 +234,6 @@ function ownPointsLinesSnapshot(
|
|
|
222
234
|
};
|
|
223
235
|
}
|
|
224
236
|
|
|
225
|
-
function withWorld(snapshot: RenderableSnapshot, world: Float32Array): RenderableSnapshot {
|
|
226
|
-
return {
|
|
227
|
-
...snapshot,
|
|
228
|
-
transform: { ...snapshot.transform, world: new Float32Array(world) },
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
237
|
function intersects(left: RenderSceneBounds, right: RenderSceneBounds): boolean {
|
|
233
238
|
return (
|
|
234
239
|
left.min[0] <= right.max[0] &&
|
|
@@ -322,8 +327,8 @@ export class RenderScene {
|
|
|
322
327
|
private readonly freeSlots: number[] = [];
|
|
323
328
|
private readonly slotsByWorld = new Map<number, Map<number, number>>();
|
|
324
329
|
private readonly slotsByMaterial = new Map<number, Set<number>>();
|
|
330
|
+
private readonly slotsByAsset = new Map<number, Set<number>>();
|
|
325
331
|
private readonly dirtySinceSubmitted = new Set<number>();
|
|
326
|
-
private readonly transformUpdatedSlots: RenderSceneSlot[] = [];
|
|
327
332
|
private temporalTracking = false;
|
|
328
333
|
private lastSubmittedVisible = new Set<string>();
|
|
329
334
|
private submittedEpoch = 0;
|
|
@@ -348,13 +353,26 @@ export class RenderScene {
|
|
|
348
353
|
return this.orderedSlots.length;
|
|
349
354
|
}
|
|
350
355
|
|
|
351
|
-
apply(
|
|
352
|
-
|
|
356
|
+
apply(
|
|
357
|
+
operations: readonly RenderSceneOperation[],
|
|
358
|
+
spans: readonly WorldTransformSpan[] = [],
|
|
359
|
+
): RenderSceneApplyResult {
|
|
360
|
+
if (operations.length === 0 && spans.length === 0) {
|
|
353
361
|
this.noChangeFrames += 1;
|
|
354
362
|
this.renderableScans = 0;
|
|
355
363
|
return this.emptyResult();
|
|
356
364
|
}
|
|
357
365
|
|
|
366
|
+
for (const operation of operations) {
|
|
367
|
+
if (
|
|
368
|
+
operation.kind === 'update' &&
|
|
369
|
+
operation.snapshot !== undefined &&
|
|
370
|
+
(operation.worldId !== operation.snapshot.worldId ||
|
|
371
|
+
operation.entityKey !== operation.snapshot.entityKey)
|
|
372
|
+
) {
|
|
373
|
+
throw new RangeError('RenderScene update identity must match its source snapshot');
|
|
374
|
+
}
|
|
375
|
+
}
|
|
358
376
|
this.deltaFrames += 1;
|
|
359
377
|
this.renderableScans = operations.length;
|
|
360
378
|
const pendingByWorld = new Map<number, Map<number, PendingIdentity>>();
|
|
@@ -377,31 +395,75 @@ export class RenderScene {
|
|
|
377
395
|
current: initial?.snapshot,
|
|
378
396
|
removed: false,
|
|
379
397
|
contentChanged: false,
|
|
398
|
+
instanceChanged: false,
|
|
399
|
+
transformChanged: false,
|
|
380
400
|
};
|
|
381
401
|
entities.set(entityKey, pending);
|
|
382
402
|
pendingOrder.push(pending);
|
|
383
403
|
return pending;
|
|
384
404
|
};
|
|
385
405
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
406
|
+
const updateWorld = (pending: PendingIdentity, source: Float32Array, offset: number): void => {
|
|
407
|
+
const target = pending.current?.transform.world;
|
|
408
|
+
if (target === undefined) return;
|
|
409
|
+
for (let lane = 0; lane < 16; lane += 1) {
|
|
410
|
+
const value = source[offset + lane] ?? 0;
|
|
411
|
+
if (target[lane] !== value) {
|
|
412
|
+
target[lane] = value;
|
|
413
|
+
pending.transformChanged = true;
|
|
414
|
+
}
|
|
393
415
|
}
|
|
416
|
+
};
|
|
417
|
+
for (const operation of operations) {
|
|
394
418
|
const pending = pendingFor(operation.worldId, operation.entityKey);
|
|
395
419
|
if (operation.kind === 'remove') {
|
|
396
420
|
pending.current = undefined;
|
|
397
421
|
pending.removed = true;
|
|
398
422
|
continue;
|
|
399
423
|
}
|
|
424
|
+
if (operation.snapshot !== undefined) {
|
|
425
|
+
const previousMaterials = pending.current?.materials;
|
|
426
|
+
const next = ownSnapshot(operation.snapshot);
|
|
427
|
+
pending.current =
|
|
428
|
+
previousMaterials !== undefined &&
|
|
429
|
+
previousMaterials.length === next.materials.length &&
|
|
430
|
+
previousMaterials.every((material, index) => material === next.materials[index])
|
|
431
|
+
? { ...next, materials: previousMaterials }
|
|
432
|
+
: next;
|
|
433
|
+
pending.contentChanged = true;
|
|
434
|
+
}
|
|
400
435
|
if (pending.current === undefined) {
|
|
401
436
|
ignoredLateUpdates += 1;
|
|
402
437
|
continue;
|
|
403
438
|
}
|
|
404
|
-
|
|
439
|
+
if (operation.instances !== undefined) {
|
|
440
|
+
pending.current = {
|
|
441
|
+
...pending.current,
|
|
442
|
+
instances: {
|
|
443
|
+
...operation.instances,
|
|
444
|
+
transforms: new Float32Array(operation.instances.transforms),
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
pending.instanceChanged = true;
|
|
448
|
+
}
|
|
449
|
+
if (operation.world !== undefined) {
|
|
450
|
+
updateWorld(pending, operation.world, 0);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// The same pending identity merges column updates with all other source
|
|
454
|
+
// edits. There is one publication and one consumer delta per entity.
|
|
455
|
+
for (const span of spans) {
|
|
456
|
+
this.renderableScans += span.entities.length;
|
|
457
|
+
for (let row = 0; row < span.entities.length; row += 1) {
|
|
458
|
+
const entity = span.entities[row];
|
|
459
|
+
if (entity === undefined) continue;
|
|
460
|
+
const pending = pendingFor(span.worldId, entity);
|
|
461
|
+
if (pending.current === undefined) {
|
|
462
|
+
ignoredLateUpdates += 1;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
updateWorld(pending, span.worlds, row * 16);
|
|
466
|
+
}
|
|
405
467
|
}
|
|
406
468
|
|
|
407
469
|
let created = 0;
|
|
@@ -411,6 +473,7 @@ export class RenderScene {
|
|
|
411
473
|
const createdSlots: RenderSceneSlot[] = [];
|
|
412
474
|
const updatedSlots: RenderSceneSlot[] = [];
|
|
413
475
|
const contentUpdatedSlots: RenderSceneSlot[] = [];
|
|
476
|
+
const instanceUpdatedSlots: RenderSceneSlot[] = [];
|
|
414
477
|
const removedSlots: RenderSceneRecord[] = [];
|
|
415
478
|
const recreatedSlots: RenderSceneSlot[] = [];
|
|
416
479
|
for (const pending of pendingOrder) {
|
|
@@ -438,24 +501,38 @@ export class RenderScene {
|
|
|
438
501
|
recreated += 1;
|
|
439
502
|
continue;
|
|
440
503
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
504
|
+
if (!pending.contentChanged && !pending.instanceChanged && !pending.transformChanged)
|
|
505
|
+
continue;
|
|
506
|
+
const updatedSlot: RenderSceneSlot =
|
|
507
|
+
pending.current === pending.initial.snapshot
|
|
508
|
+
? pending.initial
|
|
509
|
+
: { ...pending.initial, snapshot: pending.current };
|
|
510
|
+
if (pending.contentChanged) this.unindexMaterials(pending.initial);
|
|
446
511
|
this.slots[pending.initial.slot] = updatedSlot;
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
512
|
+
if (pending.current.instances === undefined && !pending.contentChanged) {
|
|
513
|
+
const cached = this.worldBoundsBySlot[updatedSlot.slot];
|
|
514
|
+
this.worldBoundsBySlot[updatedSlot.slot] =
|
|
515
|
+
worldBounds(pending.current, cached ?? undefined) ?? null;
|
|
516
|
+
} else {
|
|
517
|
+
this.worldBoundsBySlot[updatedSlot.slot] = undefined;
|
|
518
|
+
}
|
|
519
|
+
if (pending.contentChanged) this.indexMaterials(updatedSlot);
|
|
520
|
+
if (this.temporalTracking) this.dirtySinceSubmitted.add(updatedSlot.slot);
|
|
450
521
|
updatedSlots.push(updatedSlot);
|
|
451
522
|
if (pending.contentChanged) contentUpdatedSlots.push(updatedSlot);
|
|
523
|
+
else if (pending.instanceChanged) instanceUpdatedSlots.push(updatedSlot);
|
|
452
524
|
updated += 1;
|
|
453
525
|
}
|
|
454
526
|
|
|
455
527
|
const changed = created + updated + removed + recreated > 0;
|
|
456
528
|
if (changed) {
|
|
457
529
|
this.revision += 1;
|
|
458
|
-
|
|
530
|
+
if (
|
|
531
|
+
created + removed + recreated > 0 ||
|
|
532
|
+
pendingOrder.some((pending) => pending.current !== pending.initial?.snapshot)
|
|
533
|
+
) {
|
|
534
|
+
this.invalidateSnapshots();
|
|
535
|
+
}
|
|
459
536
|
}
|
|
460
537
|
return {
|
|
461
538
|
created,
|
|
@@ -466,61 +543,31 @@ export class RenderScene {
|
|
|
466
543
|
createdSlots,
|
|
467
544
|
updatedSlots,
|
|
468
545
|
contentUpdatedSlots,
|
|
546
|
+
instanceUpdatedSlots,
|
|
469
547
|
removedSlots,
|
|
470
548
|
recreatedSlots,
|
|
471
549
|
resynced: 0,
|
|
472
550
|
};
|
|
473
551
|
}
|
|
474
552
|
|
|
475
|
-
/**
|
|
476
|
-
|
|
477
|
-
this.
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
if (slot === undefined) {
|
|
490
|
-
ignoredLateUpdates += 1;
|
|
491
|
-
continue;
|
|
492
|
-
}
|
|
493
|
-
const target = slot.snapshot.transform.world;
|
|
494
|
-
const sourceStart = row * 16;
|
|
495
|
-
for (let index = 0; index < 16; index += 1) {
|
|
496
|
-
target[index] = span.worlds[sourceStart + index] ?? target[index] ?? 0;
|
|
497
|
-
}
|
|
498
|
-
if (slot.snapshot.instances === undefined) {
|
|
499
|
-
const cached = this.worldBoundsBySlot[slot.slot];
|
|
500
|
-
const bounds = worldBounds(slot.snapshot, cached ?? undefined);
|
|
501
|
-
this.worldBoundsBySlot[slot.slot] = bounds ?? null;
|
|
502
|
-
} else {
|
|
503
|
-
this.worldBoundsBySlot[slot.slot] = undefined;
|
|
504
|
-
}
|
|
505
|
-
if (this.temporalTracking) this.dirtySinceSubmitted.add(slot.slot);
|
|
506
|
-
updatedSlots.push(slot);
|
|
507
|
-
updated += 1;
|
|
553
|
+
/** Rebind positional World indices while retaining stable slot and submission history. */
|
|
554
|
+
remapWorlds(indices: readonly number[]): void {
|
|
555
|
+
this.slotsByWorld.clear();
|
|
556
|
+
for (const slot of this.slots) {
|
|
557
|
+
if (slot === undefined) continue;
|
|
558
|
+
this.instanceBoundsCache.invalidate(slot.entityKey, slot.worldId);
|
|
559
|
+
const mapped = indices[slot.worldId];
|
|
560
|
+
const worldId = mapped === undefined || mapped < 0 ? -1 - slot.worldId : mapped;
|
|
561
|
+
const remapped = { ...slot, worldId, snapshot: { ...slot.snapshot, worldId } };
|
|
562
|
+
this.slots[slot.slot] = remapped;
|
|
563
|
+
let entities = this.slotsByWorld.get(worldId);
|
|
564
|
+
if (entities === undefined) {
|
|
565
|
+
entities = new Map<number, number>();
|
|
566
|
+
this.slotsByWorld.set(worldId, entities);
|
|
508
567
|
}
|
|
568
|
+
entities.set(slot.entityKey, slot.slot);
|
|
509
569
|
}
|
|
510
|
-
this.
|
|
511
|
-
if (updated > 0) this.revision += 1;
|
|
512
|
-
return {
|
|
513
|
-
created: 0,
|
|
514
|
-
updated,
|
|
515
|
-
removed: 0,
|
|
516
|
-
recreated: 0,
|
|
517
|
-
ignoredLateUpdates,
|
|
518
|
-
createdSlots: [],
|
|
519
|
-
updatedSlots,
|
|
520
|
-
removedSlots: [],
|
|
521
|
-
recreatedSlots: [],
|
|
522
|
-
resynced: 0,
|
|
523
|
-
};
|
|
570
|
+
this.invalidateSnapshots();
|
|
524
571
|
}
|
|
525
572
|
|
|
526
573
|
/** Reconcile the current identity set while retaining slots for survivors. */
|
|
@@ -545,7 +592,7 @@ export class RenderScene {
|
|
|
545
592
|
for (const snapshot of desired.values()) {
|
|
546
593
|
const existing = this.lookup(snapshot.worldId, snapshot.entityKey);
|
|
547
594
|
if (existing === undefined) {
|
|
548
|
-
const created = this.allocate(snapshot);
|
|
595
|
+
const created = this.allocate(ownSnapshot(snapshot));
|
|
549
596
|
this.dirtySinceSubmitted.add(created.slot);
|
|
550
597
|
continue;
|
|
551
598
|
}
|
|
@@ -647,7 +694,6 @@ export class RenderScene {
|
|
|
647
694
|
}
|
|
648
695
|
this.lastSubmittedVisible = new Set(capture.visible);
|
|
649
696
|
this.submittedEpoch += 1;
|
|
650
|
-
this.invalidateSnapshots();
|
|
651
697
|
return {
|
|
652
698
|
epoch: this.submittedEpoch,
|
|
653
699
|
slots: capture.slots.map((update) => update.slot),
|
|
@@ -712,6 +758,10 @@ export class RenderScene {
|
|
|
712
758
|
return records;
|
|
713
759
|
}
|
|
714
760
|
|
|
761
|
+
get contentRevision(): number {
|
|
762
|
+
return this.revision;
|
|
763
|
+
}
|
|
764
|
+
|
|
715
765
|
inspect(): RenderSceneInspection {
|
|
716
766
|
return {
|
|
717
767
|
records: this.slotsSnapshot().map(({ slot, generation, worldId, entityKey }) => ({
|
|
@@ -729,6 +779,7 @@ export class RenderScene {
|
|
|
729
779
|
fullRebuilds: this.fullRebuilds,
|
|
730
780
|
resyncs: this.resyncs,
|
|
731
781
|
lastResyncReason: this.lastResyncReason,
|
|
782
|
+
instanceBoundsCache: this.instanceBoundsCache.inspect(),
|
|
732
783
|
};
|
|
733
784
|
}
|
|
734
785
|
|
|
@@ -741,6 +792,8 @@ export class RenderScene {
|
|
|
741
792
|
ignoredLateUpdates: 0,
|
|
742
793
|
createdSlots: [],
|
|
743
794
|
updatedSlots: [],
|
|
795
|
+
contentUpdatedSlots: [],
|
|
796
|
+
instanceUpdatedSlots: [],
|
|
744
797
|
removedSlots: [],
|
|
745
798
|
recreatedSlots: [],
|
|
746
799
|
resynced: 0,
|
|
@@ -761,7 +814,7 @@ export class RenderScene {
|
|
|
761
814
|
generation,
|
|
762
815
|
worldId: snapshot.worldId,
|
|
763
816
|
entityKey: snapshot.entityKey,
|
|
764
|
-
snapshot
|
|
817
|
+
snapshot,
|
|
765
818
|
};
|
|
766
819
|
let previousWorld = this.previousWorldBySlot[slot];
|
|
767
820
|
if (previousWorld === undefined) {
|
|
@@ -796,7 +849,23 @@ export class RenderScene {
|
|
|
796
849
|
this.freeSlots.push(record.slot);
|
|
797
850
|
}
|
|
798
851
|
|
|
852
|
+
addSharedRefConsumers(worldId: number, handle: number, target: Set<number>): void {
|
|
853
|
+
for (const index of [this.slotsByAsset, this.slotsByMaterial]) {
|
|
854
|
+
for (const slot of index.get(handle) ?? []) {
|
|
855
|
+
const record = this.slots[slot];
|
|
856
|
+
if (record?.worldId === worldId) target.add(record.entityKey);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
799
861
|
private indexMaterials(record: RenderSceneSlot): void {
|
|
862
|
+
const asset = record.snapshot.assetHandle;
|
|
863
|
+
let assetSlots = this.slotsByAsset.get(asset);
|
|
864
|
+
if (assetSlots === undefined) {
|
|
865
|
+
assetSlots = new Set<number>();
|
|
866
|
+
this.slotsByAsset.set(asset, assetSlots);
|
|
867
|
+
}
|
|
868
|
+
assetSlots.add(record.slot);
|
|
800
869
|
for (const material of record.snapshot.materials) {
|
|
801
870
|
const handle = material.materialHandle ?? 0;
|
|
802
871
|
let slots = this.slotsByMaterial.get(handle);
|
|
@@ -809,6 +878,10 @@ export class RenderScene {
|
|
|
809
878
|
}
|
|
810
879
|
|
|
811
880
|
private unindexMaterials(record: RenderSceneSlot): void {
|
|
881
|
+
const asset = record.snapshot.assetHandle;
|
|
882
|
+
const assetSlots = this.slotsByAsset.get(asset);
|
|
883
|
+
assetSlots?.delete(record.slot);
|
|
884
|
+
if (assetSlots?.size === 0) this.slotsByAsset.delete(asset);
|
|
812
885
|
for (const material of record.snapshot.materials) {
|
|
813
886
|
const handle = material.materialHandle ?? 0;
|
|
814
887
|
const slots = this.slotsByMaterial.get(handle);
|
|
@@ -914,9 +987,8 @@ export interface PersistentRenderSceneOptions {
|
|
|
914
987
|
readonly onGpuError?: ((error: RhiError) => void) | undefined;
|
|
915
988
|
readonly onSharedRefMutation?: ((worldId: number, handle: number) => void) | undefined;
|
|
916
989
|
readonly configuredQueryBudget?: number | undefined;
|
|
917
|
-
/**
|
|
918
|
-
readonly instanceCollections?:
|
|
919
|
-
readonly getSkinPaletteAllocator?: (() => SkinPaletteAllocator | null) | undefined;
|
|
990
|
+
/** Producer-owned instance collections consumed by the same source update flow. */
|
|
991
|
+
readonly instanceCollections?: InstanceCollectionStore | undefined;
|
|
920
992
|
}
|
|
921
993
|
|
|
922
994
|
export interface PersistentGpuDrivenState {
|
|
@@ -925,6 +997,7 @@ export interface PersistentGpuDrivenState {
|
|
|
925
997
|
readonly slots: readonly RenderSceneSlot[];
|
|
926
998
|
/** Stable renderer-local World keys aligned with RenderSceneSlot.worldId. */
|
|
927
999
|
readonly worldKeys?: readonly number[];
|
|
1000
|
+
readonly structureMetrics?: GpuDrivenStructureMetrics;
|
|
928
1001
|
}
|
|
929
1002
|
|
|
930
1003
|
export interface CpuDirectViewInput {
|
|
@@ -993,19 +1066,10 @@ type PersistentGpuSceneInspection = PersistentRenderSceneInspection['gpu'];
|
|
|
993
1066
|
interface PersistentCompositionEntry {
|
|
994
1067
|
readonly token: object;
|
|
995
1068
|
readonly worlds: readonly World[];
|
|
996
|
-
readonly cameraOwner: number;
|
|
997
|
-
readonly resourceOwner: number;
|
|
998
1069
|
readonly projection: RenderScene;
|
|
999
1070
|
readonly topology: BatchTopology;
|
|
1000
1071
|
readonly transmissionDemand: PersistentTransmissionDemandProjection;
|
|
1001
|
-
|
|
1002
|
-
ExtractedFrame,
|
|
1003
|
-
| 'dispatch'
|
|
1004
|
-
| 'visibilityStats'
|
|
1005
|
-
| 'visibilitySnapshots'
|
|
1006
|
-
| 'featureVisibilitySnapshots'
|
|
1007
|
-
| 'hiddenEntityReports'
|
|
1008
|
-
>;
|
|
1072
|
+
hiddenEntityReports: ExtractedFrame['hiddenEntityReports'];
|
|
1009
1073
|
/** Stable renderer-local World keys; unlike RenderableSnapshot.worldId they survive reorder. */
|
|
1010
1074
|
readonly worldKeys: readonly number[];
|
|
1011
1075
|
readonly readVersions: RenderReadVersion[];
|
|
@@ -1014,53 +1078,22 @@ interface PersistentCompositionEntry {
|
|
|
1014
1078
|
readonly renderChangeQueries: readonly ReadonlyMap<number, Query>[];
|
|
1015
1079
|
readonly dispatchBySlot: Map<number, readonly DispatchEntry[]>;
|
|
1016
1080
|
dispatchRevision: number;
|
|
1017
|
-
dispatchCache:
|
|
1018
|
-
| { readonly revision: number; readonly value: readonly DispatchEntry[] }
|
|
1019
|
-
| undefined;
|
|
1081
|
+
dispatchCache: { readonly revision: number; readonly value: DispatchEntry[] } | undefined;
|
|
1020
1082
|
readonly skinConsumersByJoint: Map<string, Set<number>>;
|
|
1021
1083
|
/** Renderable identities whose partial producer still needs a successful publish. */
|
|
1022
1084
|
readonly pendingRenderableEntitiesByWorld: Set<number>[];
|
|
1023
1085
|
/** Slots retained for recovery but excluded from direct and indirect submission. */
|
|
1024
1086
|
readonly unavailableSlots: Set<number>;
|
|
1025
1087
|
catalogEpoch: number;
|
|
1088
|
+
instanceEpoch: number;
|
|
1026
1089
|
readonly probeProjection: ProbeBlendSceneProjection;
|
|
1027
1090
|
temporalDemanded: boolean;
|
|
1028
1091
|
gpuDrivenSceneRequired: boolean;
|
|
1029
1092
|
}
|
|
1030
1093
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
componentId(MeshFilter),
|
|
1035
|
-
componentId(MeshRenderer),
|
|
1036
|
-
componentId(Instances),
|
|
1037
|
-
componentId(SpriteInstances),
|
|
1038
|
-
componentId(SpriteRegionOverride),
|
|
1039
|
-
componentId(Skin),
|
|
1040
|
-
componentId(Layer),
|
|
1041
|
-
componentId(Visibility),
|
|
1042
|
-
componentId(MorphWeights),
|
|
1043
|
-
componentId(Points),
|
|
1044
|
-
componentId(Lines),
|
|
1045
|
-
componentId(SortKey),
|
|
1046
|
-
componentId(Camera),
|
|
1047
|
-
componentId(MotionBlur),
|
|
1048
|
-
componentId(DirectionalLight),
|
|
1049
|
-
componentId(LightProbe),
|
|
1050
|
-
componentId(PointLight),
|
|
1051
|
-
componentId(PointLightShadow),
|
|
1052
|
-
componentId(SpotLight),
|
|
1053
|
-
componentId(Skylight),
|
|
1054
|
-
componentId(SkyboxBackground),
|
|
1055
|
-
componentId(PostProcessParams),
|
|
1056
|
-
componentId(ReflectionProbe),
|
|
1057
|
-
componentId(VolumetricFog),
|
|
1058
|
-
]);
|
|
1059
|
-
|
|
1060
|
-
// These components can change the retained renderable source. Other relevant
|
|
1061
|
-
// components affect frame resources and still require a composition rebuild;
|
|
1062
|
-
// they must not cause a renderable-only partial extraction to silently retain
|
|
1063
|
-
// stale lights, cameras, or environment facts.
|
|
1094
|
+
// Retained geometry consumes these component changes. Camera, lighting and
|
|
1095
|
+
// environment facts are freshly extracted by deriveFramePlan on every draw;
|
|
1096
|
+
// their edits must not recreate unrelated geometry or its submission history.
|
|
1064
1097
|
const RENDERABLE_SOURCE_COMPONENTS = [
|
|
1065
1098
|
GlobalTransform,
|
|
1066
1099
|
ChildOf,
|
|
@@ -1105,6 +1138,7 @@ function createGlobalTransformChangeQuery(world: World): GlobalTransformChangeQu
|
|
|
1105
1138
|
function createRenderChangeQueries(world: World): ReadonlyMap<number, Query> {
|
|
1106
1139
|
const queries = new Map<number, Query>();
|
|
1107
1140
|
for (const component of RENDERABLE_SOURCE_COMPONENTS) {
|
|
1141
|
+
if (component === GlobalTransform) continue;
|
|
1108
1142
|
const result = world.query({ read: [component], changed: [component] });
|
|
1109
1143
|
if (!result.ok) throw result.error;
|
|
1110
1144
|
const query = result.value as Query;
|
|
@@ -1190,7 +1224,7 @@ function dispatchForProjection(
|
|
|
1190
1224
|
dispatchBySlot: ReadonlyMap<number, readonly DispatchEntry[]>,
|
|
1191
1225
|
projection: RenderScene,
|
|
1192
1226
|
unavailableSlots: ReadonlySet<number>,
|
|
1193
|
-
):
|
|
1227
|
+
): DispatchEntry[] {
|
|
1194
1228
|
const dispatch: DispatchEntry[] = [];
|
|
1195
1229
|
const renderables = projection.materialize();
|
|
1196
1230
|
for (let renderableIndex = 0; renderableIndex < renderables.length; renderableIndex += 1) {
|
|
@@ -1200,7 +1234,9 @@ function dispatchForProjection(
|
|
|
1200
1234
|
if (slot === undefined) continue;
|
|
1201
1235
|
if (unavailableSlots.has(slot.slot)) continue;
|
|
1202
1236
|
for (const entry of dispatchBySlot.get(slot.slot) ?? []) {
|
|
1203
|
-
dispatch.push(
|
|
1237
|
+
dispatch.push(
|
|
1238
|
+
renderableIndex === entry.renderableIndex ? entry : { ...entry, renderableIndex },
|
|
1239
|
+
);
|
|
1204
1240
|
}
|
|
1205
1241
|
}
|
|
1206
1242
|
return dispatch;
|
|
@@ -1212,39 +1248,29 @@ function topologyDeltaForAvailable(
|
|
|
1212
1248
|
newlyUnavailable: readonly RenderSceneSlot[],
|
|
1213
1249
|
): RenderSceneApplyResult {
|
|
1214
1250
|
const updatedSlots = delta.updatedSlots.filter((slot) => !unavailableSlots.has(slot.slot));
|
|
1251
|
+
const unavailableUpdates = newlyUnavailable.map((slot) => ({
|
|
1252
|
+
...slot,
|
|
1253
|
+
snapshot: { ...slot.snapshot, gpuDrivenDraws: [] },
|
|
1254
|
+
}));
|
|
1215
1255
|
const contentUpdatedSlots = delta.contentUpdatedSlots?.filter(
|
|
1216
1256
|
(slot) => !unavailableSlots.has(slot.slot),
|
|
1217
1257
|
);
|
|
1258
|
+
const instanceUpdatedSlots = delta.instanceUpdatedSlots?.filter(
|
|
1259
|
+
(slot) => !unavailableSlots.has(slot.slot),
|
|
1260
|
+
);
|
|
1261
|
+
const contentBySlot =
|
|
1262
|
+
contentUpdatedSlots === undefined
|
|
1263
|
+
? undefined
|
|
1264
|
+
: new Map(contentUpdatedSlots.map((slot) => [slot.slot, slot]));
|
|
1265
|
+
for (const slot of unavailableUpdates) contentBySlot?.set(slot.slot, slot);
|
|
1218
1266
|
return {
|
|
1219
1267
|
...delta,
|
|
1220
|
-
updatedSlots: [
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
...slot,
|
|
1224
|
-
snapshot: { ...slot.snapshot, gpuDrivenDraws: [] },
|
|
1225
|
-
})),
|
|
1226
|
-
],
|
|
1227
|
-
...(contentUpdatedSlots === undefined ? {} : { contentUpdatedSlots }),
|
|
1268
|
+
updatedSlots: [...updatedSlots, ...unavailableUpdates],
|
|
1269
|
+
...(contentBySlot === undefined ? {} : { contentUpdatedSlots: [...contentBySlot.values()] }),
|
|
1270
|
+
...(instanceUpdatedSlots === undefined ? {} : { instanceUpdatedSlots }),
|
|
1228
1271
|
};
|
|
1229
1272
|
}
|
|
1230
1273
|
|
|
1231
|
-
function addSharedRefConsumers(
|
|
1232
|
-
projection: RenderScene,
|
|
1233
|
-
worldId: number,
|
|
1234
|
-
handle: number,
|
|
1235
|
-
target: Set<number>,
|
|
1236
|
-
): void {
|
|
1237
|
-
for (const slot of projection.slotsSnapshot()) {
|
|
1238
|
-
if (
|
|
1239
|
-
slot.worldId === worldId &&
|
|
1240
|
-
(slot.snapshot.assetHandle === handle ||
|
|
1241
|
-
slot.snapshot.materials.some((material) => material.materialHandle === handle))
|
|
1242
|
-
) {
|
|
1243
|
-
target.add(slot.entityKey);
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
1274
|
function frameDemandsTemporal(frame: ExtractedFrame): boolean {
|
|
1249
1275
|
const camera = frame.cameras[0];
|
|
1250
1276
|
return camera?.antialias === 'taa' || (camera?.motionBlur?.shutterAngle ?? 0) > 0;
|
|
@@ -1295,6 +1321,18 @@ function cullPersistentFrame(
|
|
|
1295
1321
|
) => worldBounds(snapshot),
|
|
1296
1322
|
): ExtractedFrame {
|
|
1297
1323
|
const planes = cameraFrusta(frame.cameras);
|
|
1324
|
+
// Shadow casters are not constrained by the display camera. Keep the
|
|
1325
|
+
// primary camera cull intact, but admit a caster that is outside every
|
|
1326
|
+
// camera frustum when its bounds intersect one of the active light-space
|
|
1327
|
+
// frusta. This is especially important for point-light cube faces: the
|
|
1328
|
+
// caster can be behind the camera while still occluding the receiver.
|
|
1329
|
+
const casterIndices = new Set(
|
|
1330
|
+
frame.dispatch
|
|
1331
|
+
.filter((entry) => entry.tags.LightMode === 'ShadowCaster')
|
|
1332
|
+
.map((entry) => entry.renderableIndex),
|
|
1333
|
+
);
|
|
1334
|
+
const shadowFrusta = casterIndices.size === 0 ? [] : buildShadowFrusta(frame.lights);
|
|
1335
|
+
const shadowOnly = new Set<number>();
|
|
1298
1336
|
const visible: RenderableSnapshot[] = [];
|
|
1299
1337
|
if (cullProjectedIndexScratch.length < candidates.length) {
|
|
1300
1338
|
cullProjectedIndexScratch = new Int32Array(candidates.length);
|
|
@@ -1313,6 +1351,7 @@ function cullPersistentFrame(
|
|
|
1313
1351
|
continue;
|
|
1314
1352
|
}
|
|
1315
1353
|
let isVisible = true;
|
|
1354
|
+
let isShadowVisible = false;
|
|
1316
1355
|
const candidateBounds = boundsOf(candidate, index);
|
|
1317
1356
|
if (candidateBounds !== undefined) {
|
|
1318
1357
|
total += 1;
|
|
@@ -1332,10 +1371,16 @@ function cullPersistentFrame(
|
|
|
1332
1371
|
break;
|
|
1333
1372
|
}
|
|
1334
1373
|
}
|
|
1374
|
+
if (!isVisible && casterIndices.has(index)) {
|
|
1375
|
+
isShadowVisible = shadowFrusta.some((lightPlanes) =>
|
|
1376
|
+
frustum.intersectsBox(lightPlanes, worldAabb as box3.Box3Like),
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1335
1379
|
}
|
|
1336
1380
|
if (!isVisible) {
|
|
1337
1381
|
culled += 1;
|
|
1338
|
-
continue;
|
|
1382
|
+
if (!isShadowVisible) continue;
|
|
1383
|
+
shadowOnly.add(index);
|
|
1339
1384
|
}
|
|
1340
1385
|
projectedIndex[index] = visible.length;
|
|
1341
1386
|
// The culling candidate is the persistent projection snapshot used for
|
|
@@ -1349,9 +1394,15 @@ function cullPersistentFrame(
|
|
|
1349
1394
|
const dispatch: DispatchEntry[] = [];
|
|
1350
1395
|
for (const entry of frame.dispatch) {
|
|
1351
1396
|
const candidateIndex = entry.renderableIndex;
|
|
1397
|
+
// Shadow-only candidates must never enter the main forward/transparent
|
|
1398
|
+
// dispatch. Their ShadowCaster entries remain available to the depth
|
|
1399
|
+
// passes, which consume the same validated ordered list.
|
|
1400
|
+
if (shadowOnly.has(candidateIndex) && entry.tags.LightMode !== 'ShadowCaster') continue;
|
|
1352
1401
|
const renderableIndex = projectedIndex[candidateIndex];
|
|
1353
1402
|
if (renderableIndex === undefined || renderableIndex < 0) continue;
|
|
1354
|
-
dispatch.push(
|
|
1403
|
+
dispatch.push(
|
|
1404
|
+
renderableIndex === entry.renderableIndex ? entry : { ...entry, renderableIndex },
|
|
1405
|
+
);
|
|
1355
1406
|
}
|
|
1356
1407
|
return {
|
|
1357
1408
|
...frame,
|
|
@@ -1391,12 +1442,15 @@ export class PersistentRenderScene {
|
|
|
1391
1442
|
private readonly materialSnapshotCaches: MaterialSnapshotCachesByWorld = new WeakMap();
|
|
1392
1443
|
private fullRebuilds = 0;
|
|
1393
1444
|
private worldEntitiesScanned = 0;
|
|
1445
|
+
private sceneTableUploadBytes = 0;
|
|
1394
1446
|
private noChangeFrames = 0;
|
|
1395
1447
|
private deltaFrames = 0;
|
|
1396
1448
|
private transformUpdates = 0;
|
|
1397
1449
|
private lastResyncReason: RenderSceneResyncReason | undefined;
|
|
1398
1450
|
private composition: PersistentCompositionEntry | undefined;
|
|
1399
1451
|
private gpuScene: GpuScene | undefined;
|
|
1452
|
+
private readonly retiredGpuScenes = new Set<GpuScene>();
|
|
1453
|
+
private shadowProjection: PersistentShadowCasterProjection | undefined;
|
|
1400
1454
|
private gpuOwner: object | undefined;
|
|
1401
1455
|
private gpuDevice: RhiDevice | undefined;
|
|
1402
1456
|
private gpuStatus: 'inactive' | 'unsupported' | 'resident' | 'rebuild-pending' | 'error' =
|
|
@@ -1424,6 +1478,7 @@ export class PersistentRenderScene {
|
|
|
1424
1478
|
}
|
|
1425
1479
|
| undefined;
|
|
1426
1480
|
private visibilityProjectionRevision = 0;
|
|
1481
|
+
private projectedIndexScratch = new Int32Array(0);
|
|
1427
1482
|
private lastActiveEntityKeys: ReadonlySet<number> | undefined;
|
|
1428
1483
|
/** Stable renderer-world/entity lookup; rebuilt only with composition topology. */
|
|
1429
1484
|
private readonly stableSlotByEntity = new Map<number, RenderSceneSlot>();
|
|
@@ -1473,6 +1528,10 @@ export class PersistentRenderScene {
|
|
|
1473
1528
|
return this.composition?.projection.slotsSnapshot() ?? [];
|
|
1474
1529
|
}
|
|
1475
1530
|
|
|
1531
|
+
shadowCasterProjection(): PersistentShadowCasterProjection | undefined {
|
|
1532
|
+
return this.shadowProjection;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1476
1535
|
/** Stable renderer-world/entity lookup shared by query and final projection paths. */
|
|
1477
1536
|
compositionSlotByStableEntity(): ReadonlyMap<number, RenderSceneSlot> {
|
|
1478
1537
|
return this.stableSlotByEntity;
|
|
@@ -1517,7 +1576,7 @@ export class PersistentRenderScene {
|
|
|
1517
1576
|
renderable.lods.length === 0 ||
|
|
1518
1577
|
bounds === undefined ||
|
|
1519
1578
|
bounds.length < 6 ||
|
|
1520
|
-
|
|
1579
|
+
!bounds.every(Number.isFinite)
|
|
1521
1580
|
) {
|
|
1522
1581
|
continue;
|
|
1523
1582
|
}
|
|
@@ -1594,15 +1653,19 @@ export class PersistentRenderScene {
|
|
|
1594
1653
|
) {
|
|
1595
1654
|
return cached.projection;
|
|
1596
1655
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1656
|
+
if (this.projectedIndexScratch.length < renderables.length) {
|
|
1657
|
+
this.projectedIndexScratch = new Int32Array(renderables.length);
|
|
1658
|
+
}
|
|
1659
|
+
const projectedIndex = this.projectedIndexScratch;
|
|
1660
|
+
projectedIndex.fill(-1, 0, renderables.length);
|
|
1599
1661
|
const projected: RenderableSnapshot[] = [];
|
|
1600
1662
|
const activeEntityKeys = new Set<number>();
|
|
1601
1663
|
let suppressed = 0;
|
|
1602
1664
|
for (let index = 0; index < renderables.length; index += 1) {
|
|
1603
1665
|
const renderable = renderables[index];
|
|
1604
1666
|
if (renderable === undefined) continue;
|
|
1605
|
-
const
|
|
1667
|
+
const stableEntity = this.stableEntityKey(worlds, renderable);
|
|
1668
|
+
const slot = this.stableSlotByEntity.get(stableEntity);
|
|
1606
1669
|
let draw = true;
|
|
1607
1670
|
if (slot !== undefined && (renderable.lods?.length ?? 0) > 0) {
|
|
1608
1671
|
const primitive = primitiveKey({
|
|
@@ -1613,9 +1676,7 @@ export class PersistentRenderScene {
|
|
|
1613
1676
|
});
|
|
1614
1677
|
const bounds = renderable.localAabb;
|
|
1615
1678
|
const validBounds =
|
|
1616
|
-
bounds !== undefined &&
|
|
1617
|
-
bounds.length >= 6 &&
|
|
1618
|
-
[...bounds].every((value) => Number.isFinite(value));
|
|
1679
|
+
bounds !== undefined && bounds.length >= 6 && bounds.every(Number.isFinite);
|
|
1619
1680
|
draw = decideVisibility({
|
|
1620
1681
|
authorVisible: renderable.authorVisible !== false,
|
|
1621
1682
|
validBounds,
|
|
@@ -1631,13 +1692,15 @@ export class PersistentRenderScene {
|
|
|
1631
1692
|
}
|
|
1632
1693
|
projectedIndex[index] = projected.length;
|
|
1633
1694
|
projected.push(renderable);
|
|
1634
|
-
activeEntityKeys.add(
|
|
1695
|
+
activeEntityKeys.add(stableEntity);
|
|
1635
1696
|
}
|
|
1636
1697
|
const projectedDispatch: DispatchEntry[] = [];
|
|
1637
1698
|
for (const entry of dispatch) {
|
|
1638
1699
|
const renderableIndex = projectedIndex[entry.renderableIndex];
|
|
1639
1700
|
if (renderableIndex === undefined || renderableIndex < 0) continue;
|
|
1640
|
-
projectedDispatch.push(
|
|
1701
|
+
projectedDispatch.push(
|
|
1702
|
+
renderableIndex === entry.renderableIndex ? entry : { ...entry, renderableIndex },
|
|
1703
|
+
);
|
|
1641
1704
|
}
|
|
1642
1705
|
const projection = {
|
|
1643
1706
|
renderables: Object.freeze(projected),
|
|
@@ -1725,6 +1788,7 @@ export class PersistentRenderScene {
|
|
|
1725
1788
|
leases?: readonly RenderReadLease[],
|
|
1726
1789
|
): ExtractedFrame {
|
|
1727
1790
|
this.worldEntitiesScanned = 0;
|
|
1791
|
+
this.sceneTableUploadBytes = 0;
|
|
1728
1792
|
const entry = this.composition;
|
|
1729
1793
|
const sameWorldSet =
|
|
1730
1794
|
entry !== undefined &&
|
|
@@ -1749,15 +1813,12 @@ export class PersistentRenderScene {
|
|
|
1749
1813
|
leaseSetPreserved;
|
|
1750
1814
|
if (
|
|
1751
1815
|
entry === undefined ||
|
|
1752
|
-
entry.catalogEpoch !== catalogEpoch ||
|
|
1753
|
-
entry.cameraOwner !== owner.cameraOwner ||
|
|
1754
|
-
entry.resourceOwner !== owner.resourceOwner ||
|
|
1755
1816
|
entry.worlds.length !== worlds.length ||
|
|
1756
1817
|
entry.worlds.some((world, index) => world !== worlds[index]) ||
|
|
1757
1818
|
leases === undefined ||
|
|
1758
1819
|
leases.length !== worlds.length
|
|
1759
1820
|
) {
|
|
1760
|
-
return this.
|
|
1821
|
+
return this.reconcileComposition(
|
|
1761
1822
|
worlds,
|
|
1762
1823
|
owner,
|
|
1763
1824
|
catalogEpoch,
|
|
@@ -1766,14 +1827,33 @@ export class PersistentRenderScene {
|
|
|
1766
1827
|
preserveVisibilityOnReorder,
|
|
1767
1828
|
);
|
|
1768
1829
|
}
|
|
1769
|
-
let
|
|
1830
|
+
let requiresSourceRefresh = false;
|
|
1770
1831
|
const changedEntitiesByWorld = worlds.map(
|
|
1771
1832
|
(_, worldId) => new Set(entry.pendingRenderableEntitiesByWorld[worldId] ?? []),
|
|
1772
1833
|
);
|
|
1834
|
+
const contentChangedByWorld = worlds.map(
|
|
1835
|
+
(_, worldId) => new Set(entry.pendingRenderableEntitiesByWorld[worldId] ?? []),
|
|
1836
|
+
);
|
|
1837
|
+
if (entry.catalogEpoch !== catalogEpoch) {
|
|
1838
|
+
for (const slot of entry.projection.slotsSnapshot()) {
|
|
1839
|
+
changedEntitiesByWorld[slot.worldId]?.add(slot.entityKey);
|
|
1840
|
+
contentChangedByWorld[slot.worldId]?.add(slot.entityKey);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
const instanceEpoch = this.options.instanceCollections?.epoch ?? 0;
|
|
1844
|
+
if (entry.instanceEpoch !== instanceEpoch) {
|
|
1845
|
+
for (const slot of entry.projection.slotsSnapshot()) {
|
|
1846
|
+
const id = slot.snapshot.instances?.collectionId;
|
|
1847
|
+
if (id === undefined) continue;
|
|
1848
|
+
const current = this.options.instanceCollections?.inspect(id);
|
|
1849
|
+
if (!current?.ok || current.value.revision !== slot.snapshot.instances?.revision) {
|
|
1850
|
+
changedEntitiesByWorld[slot.worldId]?.add(slot.entityKey);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1773
1854
|
const nextReadVersions = entry.readVersions.slice();
|
|
1774
1855
|
const structureChangedWorlds = new Set<number>();
|
|
1775
1856
|
const structuralComponentIdsByWorld = worlds.map(() => new Set<number>());
|
|
1776
|
-
let refreshStructureFacts = false;
|
|
1777
1857
|
let currentRenderableEntitiesByWorld: readonly (ReadonlySet<number> | undefined)[] | undefined;
|
|
1778
1858
|
const removedSlots: RenderSceneSlot[] = [];
|
|
1779
1859
|
for (let worldId = 0; worldId < worlds.length; worldId += 1) {
|
|
@@ -1781,24 +1861,23 @@ export class PersistentRenderScene {
|
|
|
1781
1861
|
if (world === undefined) continue;
|
|
1782
1862
|
const lease = leases?.[worldId];
|
|
1783
1863
|
if (lease === undefined || entry.leaseIdentities[worldId] !== lease.worldIdentity) {
|
|
1784
|
-
|
|
1864
|
+
requiresSourceRefresh = true;
|
|
1785
1865
|
break;
|
|
1786
1866
|
}
|
|
1787
1867
|
const version = entry.readVersions[worldId];
|
|
1788
1868
|
if (version === undefined) {
|
|
1789
|
-
|
|
1869
|
+
requiresSourceRefresh = true;
|
|
1790
1870
|
break;
|
|
1791
1871
|
}
|
|
1792
1872
|
let read: ReturnType<RenderReadLease['readChanges']>;
|
|
1793
1873
|
try {
|
|
1794
1874
|
read = lease.readChanges(version);
|
|
1795
1875
|
} catch {
|
|
1796
|
-
|
|
1876
|
+
requiresSourceRefresh = true;
|
|
1797
1877
|
break;
|
|
1798
1878
|
}
|
|
1799
1879
|
if (read.status === 'rebuild') {
|
|
1800
1880
|
structureChangedWorlds.add(worldId);
|
|
1801
|
-
refreshStructureFacts = true;
|
|
1802
1881
|
}
|
|
1803
1882
|
const changes = read.world;
|
|
1804
1883
|
const shared = read.sharedRefs;
|
|
@@ -1806,56 +1885,80 @@ export class PersistentRenderScene {
|
|
|
1806
1885
|
for (const record of shared.records) {
|
|
1807
1886
|
this.options.onSharedRefMutation?.(worldId, record.handle);
|
|
1808
1887
|
const changed = changedEntitiesByWorld[worldId];
|
|
1809
|
-
|
|
1810
|
-
|
|
1888
|
+
const content = contentChangedByWorld[worldId];
|
|
1889
|
+
if (changed !== undefined && content !== undefined) {
|
|
1890
|
+
entry.projection.addSharedRefConsumers(worldId, record.handle, content);
|
|
1891
|
+
for (const entity of content) changed.add(entity);
|
|
1892
|
+
}
|
|
1811
1893
|
}
|
|
1812
1894
|
}
|
|
1813
1895
|
for (const changedComponentId of changes.changedComponentIds) {
|
|
1814
1896
|
if (read.status === 'rebuild') {
|
|
1815
1897
|
structuralComponentIdsByWorld[worldId]?.add(changedComponentId);
|
|
1816
1898
|
}
|
|
1817
|
-
if (!
|
|
1818
|
-
if (!RENDERABLE_SOURCE_COMPONENT_IDS.has(changedComponentId)) {
|
|
1819
|
-
requiresRebuild = true;
|
|
1820
|
-
break;
|
|
1821
|
-
}
|
|
1899
|
+
if (!RENDERABLE_SOURCE_COMPONENT_IDS.has(changedComponentId)) continue;
|
|
1822
1900
|
const changed = changedEntitiesByWorld[worldId];
|
|
1823
1901
|
if (changed === undefined) continue;
|
|
1824
|
-
|
|
1825
|
-
refreshStructureFacts = true;
|
|
1826
|
-
// Visibility and parent changes affect the effective state of a
|
|
1827
|
-
// subtree. The normal update path evaluates the current rows; the
|
|
1828
|
-
// set is deliberately allowed to be broad, while empty work still
|
|
1829
|
-
// exits naturally without a candidate loop.
|
|
1830
|
-
for (const entity of collectRenderableEntities(world)) changed.add(entity);
|
|
1831
|
-
continue;
|
|
1832
|
-
}
|
|
1833
|
-
if (changedComponentId === componentId(GlobalTransform)) {
|
|
1834
|
-
const query = entry.renderChangeQueries[worldId]?.get(changedComponentId);
|
|
1835
|
-
for (const row of query ?? []) {
|
|
1836
|
-
const consumers = entry.skinConsumersByJoint.get(
|
|
1837
|
-
skinJointKey(worldId, row.entity as number),
|
|
1838
|
-
);
|
|
1839
|
-
for (const consumer of consumers ?? []) changed.add(consumer);
|
|
1840
|
-
}
|
|
1841
|
-
continue;
|
|
1842
|
-
}
|
|
1902
|
+
const otherChanged = contentChangedByWorld[worldId];
|
|
1843
1903
|
const query = entry.renderChangeQueries[worldId]?.get(changedComponentId);
|
|
1844
1904
|
if (query !== undefined) {
|
|
1845
|
-
for (const row of query)
|
|
1905
|
+
for (const row of query) {
|
|
1906
|
+
const entity = row.entity as number;
|
|
1907
|
+
if (VISIBILITY_DEPENDENCY_COMPONENT_IDS.has(changedComponentId)) {
|
|
1908
|
+
const affected = collectSubtree(world, row.entity);
|
|
1909
|
+
for (const dependent of affected) {
|
|
1910
|
+
if (
|
|
1911
|
+
!entry.projection.has(worldId, dependent) &&
|
|
1912
|
+
!world.hasComponent(dependent as EntityHandle, MeshRenderer)
|
|
1913
|
+
)
|
|
1914
|
+
continue;
|
|
1915
|
+
changed.add(dependent);
|
|
1916
|
+
otherChanged?.add(dependent);
|
|
1917
|
+
}
|
|
1918
|
+
} else {
|
|
1919
|
+
changed.add(entity);
|
|
1920
|
+
if (changedComponentId !== componentId(Instances)) otherChanged?.add(entity);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1846
1923
|
}
|
|
1847
1924
|
}
|
|
1848
|
-
if (
|
|
1925
|
+
if (requiresSourceRefresh) break;
|
|
1849
1926
|
nextReadVersions[worldId] = read.version;
|
|
1850
1927
|
}
|
|
1851
|
-
if (
|
|
1852
|
-
return this.
|
|
1928
|
+
if (requiresSourceRefresh) {
|
|
1929
|
+
return this.reconcileComposition(worlds, owner, catalogEpoch, buildCandidateFrame, leases);
|
|
1853
1930
|
}
|
|
1854
1931
|
|
|
1932
|
+
const transformSpans: WorldTransformSpan[] = [];
|
|
1933
|
+
for (let worldId = 0; worldId < entry.transformQueries.length; worldId += 1) {
|
|
1934
|
+
const query = entry.transformQueries[worldId];
|
|
1935
|
+
if (query === undefined) continue;
|
|
1936
|
+
for (const span of query.spans().unwrap()) {
|
|
1937
|
+
const changedWorlds = span.get(GlobalTransform).world;
|
|
1938
|
+
if (!(changedWorlds instanceof Float32Array)) {
|
|
1939
|
+
requiresSourceRefresh = true;
|
|
1940
|
+
break;
|
|
1941
|
+
}
|
|
1942
|
+
transformSpans.push({ worldId, entities: span.entities, worlds: changedWorlds });
|
|
1943
|
+
for (const entity of span.entities) {
|
|
1944
|
+
for (const consumer of entry.skinConsumersByJoint.get(skinJointKey(worldId, entity)) ??
|
|
1945
|
+
[]) {
|
|
1946
|
+
changedEntitiesByWorld[worldId]?.add(consumer);
|
|
1947
|
+
contentChangedByWorld[worldId]?.add(consumer);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
if (requiresSourceRefresh) break;
|
|
1952
|
+
}
|
|
1953
|
+
if (requiresSourceRefresh) {
|
|
1954
|
+
return this.reconcileComposition(worlds, owner, catalogEpoch, buildCandidateFrame, leases);
|
|
1955
|
+
}
|
|
1855
1956
|
if (structureChangedWorlds.size > 0) {
|
|
1856
1957
|
this.visibilityFacets.clear();
|
|
1857
1958
|
this.invalidateActiveEntityRevision();
|
|
1858
|
-
const currentByWorld = worlds.map((world) =>
|
|
1959
|
+
const currentByWorld = worlds.map((world, worldId) =>
|
|
1960
|
+
structureChangedWorlds.has(worldId) ? collectRenderableEntities(world) : undefined,
|
|
1961
|
+
);
|
|
1859
1962
|
currentRenderableEntitiesByWorld = currentByWorld;
|
|
1860
1963
|
for (let worldId = 0; worldId < currentByWorld.length; worldId += 1) {
|
|
1861
1964
|
const world = worlds[worldId];
|
|
@@ -1873,7 +1976,10 @@ export class PersistentRenderScene {
|
|
|
1873
1976
|
if (indexedWorldId !== worldId) continue;
|
|
1874
1977
|
const jointEntity = Number(jointKey.slice(separator + 1));
|
|
1875
1978
|
if (world.hasComponent(jointEntity as EntityHandle, GlobalTransform)) continue;
|
|
1876
|
-
for (const consumer of consumers)
|
|
1979
|
+
for (const consumer of consumers) {
|
|
1980
|
+
changed.add(consumer);
|
|
1981
|
+
contentChangedByWorld[worldId]?.add(consumer);
|
|
1982
|
+
}
|
|
1877
1983
|
}
|
|
1878
1984
|
for (const entity of current) {
|
|
1879
1985
|
if (entry.projection.slot(worldId, entity) === undefined) changed.add(entity);
|
|
@@ -1889,26 +1995,14 @@ export class PersistentRenderScene {
|
|
|
1889
1995
|
const membershipChanged = sourceMembershipChanged(world, slot);
|
|
1890
1996
|
if (scanAllMembership || membershipChanged) {
|
|
1891
1997
|
changed.add(slot.entityKey);
|
|
1892
|
-
|
|
1893
|
-
slot.snapshot.instances !== undefined &&
|
|
1894
|
-
!world.hasComponent(slot.entityKey as EntityHandle, Instances)
|
|
1895
|
-
) {
|
|
1896
|
-
this.options.instanceCollections?.release(world, slot.entityKey);
|
|
1897
|
-
}
|
|
1898
|
-
if (
|
|
1899
|
-
slot.snapshot.skin !== undefined &&
|
|
1900
|
-
!world.hasComponent(slot.entityKey as EntityHandle, Skin)
|
|
1901
|
-
) {
|
|
1902
|
-
this.options
|
|
1903
|
-
.getSkinPaletteAllocator?.()
|
|
1904
|
-
?.releaseSliceFor(`${world.identity}:${slot.entityKey}`);
|
|
1905
|
-
}
|
|
1998
|
+
contentChangedByWorld[worldId]?.add(slot.entityKey);
|
|
1906
1999
|
}
|
|
1907
2000
|
}
|
|
1908
2001
|
}
|
|
1909
2002
|
for (const slot of entry.projection.slotsSnapshot()) {
|
|
1910
2003
|
if (slot.worldId !== worldId || current.has(slot.entityKey)) continue;
|
|
1911
2004
|
removedSlots.push(slot);
|
|
2005
|
+
changedEntitiesByWorld[worldId]?.add(slot.entityKey);
|
|
1912
2006
|
}
|
|
1913
2007
|
this.worldEntitiesScanned += current.size;
|
|
1914
2008
|
}
|
|
@@ -1925,7 +2019,7 @@ export class PersistentRenderScene {
|
|
|
1925
2019
|
|
|
1926
2020
|
let changedDelta: RenderSceneApplyResult | undefined;
|
|
1927
2021
|
const hasChangedEntities = changedEntitiesByWorld.some((entities) => entities.size > 0);
|
|
1928
|
-
if (hasChangedEntities || removedSlots.length > 0) {
|
|
2022
|
+
if (hasChangedEntities || removedSlots.length > 0 || transformSpans.length > 0) {
|
|
1929
2023
|
const operations: RenderSceneOperation[] = [];
|
|
1930
2024
|
const removedDemand: TransmissionDemandOperation[] = [];
|
|
1931
2025
|
const replacedSkinSnapshots: RenderableSnapshot[] = [];
|
|
@@ -1940,11 +2034,6 @@ export class PersistentRenderScene {
|
|
|
1940
2034
|
if (slot.snapshot.skinJointEntities !== undefined) {
|
|
1941
2035
|
removeSkinConsumer(entry.skinConsumersByJoint, slot.snapshot);
|
|
1942
2036
|
}
|
|
1943
|
-
const world = worlds[slot.worldId];
|
|
1944
|
-
if (world !== undefined) this.options.instanceCollections?.release(world, slot.entityKey);
|
|
1945
|
-
this.options
|
|
1946
|
-
.getSkinPaletteAllocator?.()
|
|
1947
|
-
?.releaseSliceFor(`${world?.identity ?? slot.worldId}:${slot.entityKey}`);
|
|
1948
2037
|
for (
|
|
1949
2038
|
let materialIndex = 0;
|
|
1950
2039
|
materialIndex < slot.snapshot.materials.length;
|
|
@@ -1969,15 +2058,39 @@ export class PersistentRenderScene {
|
|
|
1969
2058
|
}
|
|
1970
2059
|
}
|
|
1971
2060
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2061
|
+
let partial: ExtractedFrame | undefined;
|
|
2062
|
+
try {
|
|
2063
|
+
partial = hasChangedEntities
|
|
2064
|
+
? buildCandidateFrame({ kind: 'partial', entitiesByWorld: changedEntitiesByWorld })
|
|
2065
|
+
: undefined;
|
|
2066
|
+
} catch (cause) {
|
|
2067
|
+
// Change queries have been observed, but this publication never ran.
|
|
2068
|
+
// Retain every consumed transform identity for the producer retry.
|
|
2069
|
+
for (const span of transformSpans) {
|
|
2070
|
+
const pending = entry.pendingRenderableEntitiesByWorld[span.worldId];
|
|
2071
|
+
for (const entity of span.entities) {
|
|
2072
|
+
if (entry.projection.has(span.worldId, entity)) pending?.add(entity);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
throw cause;
|
|
2076
|
+
}
|
|
1975
2077
|
const publishedEntitiesByWorld = worlds.map(() => new Set<number>());
|
|
1976
2078
|
for (const snapshot of partial?.renderables ?? []) {
|
|
1977
|
-
|
|
2079
|
+
const existing = entry.projection.slot(snapshot.worldId, snapshot.entityKey);
|
|
2080
|
+
const contentChanged =
|
|
2081
|
+
existing === undefined ||
|
|
2082
|
+
contentChangedByWorld[snapshot.worldId]?.has(snapshot.entityKey);
|
|
2083
|
+
operations.push({
|
|
2084
|
+
kind: 'update',
|
|
2085
|
+
worldId: snapshot.worldId,
|
|
2086
|
+
entityKey: snapshot.entityKey,
|
|
2087
|
+
...(contentChanged || snapshot.instances === undefined
|
|
2088
|
+
? { snapshot }
|
|
2089
|
+
: { instances: snapshot.instances }),
|
|
2090
|
+
});
|
|
1978
2091
|
publishedEntitiesByWorld[snapshot.worldId]?.add(snapshot.entityKey);
|
|
1979
2092
|
}
|
|
1980
|
-
changedDelta = entry.projection.apply(operations);
|
|
2093
|
+
changedDelta = entry.projection.apply(operations, transformSpans);
|
|
1981
2094
|
const publishedSkinKeys = new Set<string>();
|
|
1982
2095
|
for (const snapshot of partial?.renderables ?? []) {
|
|
1983
2096
|
publishedSkinKeys.add(`${snapshot.worldId}:${snapshot.entityKey}`);
|
|
@@ -2005,17 +2118,21 @@ export class PersistentRenderScene {
|
|
|
2005
2118
|
for (const removed of changedDelta.removedSlots) entry.dispatchBySlot.delete(removed.slot);
|
|
2006
2119
|
for (const recreated of changedDelta.recreatedSlots)
|
|
2007
2120
|
entry.dispatchBySlot.delete(recreated.slot);
|
|
2121
|
+
if (partial !== undefined || removedSlots.length > 0) {
|
|
2122
|
+
const worldIndices = new Map(worlds.map((world, index) => [world, index]));
|
|
2123
|
+
entry.hiddenEntityReports = [
|
|
2124
|
+
...entry.hiddenEntityReports.filter((report) => {
|
|
2125
|
+
const worldId = worldIndices.get(report.world);
|
|
2126
|
+
return (
|
|
2127
|
+
worldId !== undefined &&
|
|
2128
|
+
!changedEntitiesByWorld[worldId]?.has(report.entity) &&
|
|
2129
|
+
currentRenderableEntitiesByWorld?.[worldId]?.has(report.entity) !== false
|
|
2130
|
+
);
|
|
2131
|
+
}),
|
|
2132
|
+
...(partial?.hiddenEntityReports ?? []),
|
|
2133
|
+
];
|
|
2134
|
+
}
|
|
2008
2135
|
if (partial !== undefined) {
|
|
2009
|
-
if (refreshStructureFacts) {
|
|
2010
|
-
entry.structure = {
|
|
2011
|
-
...entry.structure,
|
|
2012
|
-
dispatch: partial.dispatch,
|
|
2013
|
-
visibilityStats: partial.visibilityStats,
|
|
2014
|
-
visibilitySnapshots: partial.visibilitySnapshots,
|
|
2015
|
-
featureVisibilitySnapshots: partial.featureVisibilitySnapshots,
|
|
2016
|
-
hiddenEntityReports: partial.hiddenEntityReports,
|
|
2017
|
-
};
|
|
2018
|
-
}
|
|
2019
2136
|
const partialDispatch = dispatchEntriesBySlot(partial, entry.projection);
|
|
2020
2137
|
for (const [slot, dispatch] of partialDispatch) entry.dispatchBySlot.set(slot, dispatch);
|
|
2021
2138
|
// A changed renderable which now fails producer validation still owns
|
|
@@ -2067,8 +2184,10 @@ export class PersistentRenderScene {
|
|
|
2067
2184
|
for (const removed of changedDelta.removedSlots) entry.unavailableSlots.delete(removed.slot);
|
|
2068
2185
|
for (const recreated of changedDelta.recreatedSlots)
|
|
2069
2186
|
entry.unavailableSlots.delete(recreated.slot);
|
|
2070
|
-
|
|
2071
|
-
|
|
2187
|
+
if (partial !== undefined || removedSlots.length > 0) {
|
|
2188
|
+
entry.dispatchRevision += 1;
|
|
2189
|
+
entry.dispatchCache = undefined;
|
|
2190
|
+
}
|
|
2072
2191
|
entry.topology.apply(
|
|
2073
2192
|
topologyDeltaForAvailable(changedDelta, entry.unavailableSlots, newlyUnavailable),
|
|
2074
2193
|
);
|
|
@@ -2084,41 +2203,13 @@ export class PersistentRenderScene {
|
|
|
2084
2203
|
this.transformUpdates += changedDelta.updated;
|
|
2085
2204
|
}
|
|
2086
2205
|
|
|
2087
|
-
|
|
2088
|
-
for (let worldId = 0; worldId < entry.transformQueries.length; worldId += 1) {
|
|
2089
|
-
const query = entry.transformQueries[worldId];
|
|
2090
|
-
if (query === undefined) continue;
|
|
2091
|
-
for (const span of query.spans().unwrap()) {
|
|
2092
|
-
const changedWorlds = span.get(GlobalTransform).world;
|
|
2093
|
-
if (!(changedWorlds instanceof Float32Array)) {
|
|
2094
|
-
requiresRebuild = true;
|
|
2095
|
-
break;
|
|
2096
|
-
}
|
|
2097
|
-
transformSpans.push({ worldId, entities: span.entities, worlds: changedWorlds });
|
|
2098
|
-
}
|
|
2099
|
-
if (requiresRebuild) break;
|
|
2100
|
-
}
|
|
2101
|
-
if (requiresRebuild) {
|
|
2102
|
-
return this.rebuildComposition(worlds, owner, catalogEpoch, buildCandidateFrame, leases);
|
|
2103
|
-
}
|
|
2104
|
-
if (transformSpans.length === 0 && changedDelta === undefined) {
|
|
2206
|
+
if (changedDelta === undefined) {
|
|
2105
2207
|
this.noChangeFrames += 1;
|
|
2106
2208
|
this.syncGpuOwner(entry.token, entry.projection);
|
|
2107
|
-
const result = this.deriveFramePlan(entry, buildCandidateFrame);
|
|
2108
|
-
for (let worldId = 0; worldId < nextReadVersions.length; worldId += 1) {
|
|
2109
|
-
const version = nextReadVersions[worldId];
|
|
2110
|
-
if (version !== undefined) entry.readVersions[worldId] = version;
|
|
2111
|
-
}
|
|
2112
|
-
return result;
|
|
2113
|
-
}
|
|
2114
|
-
if (transformSpans.length > 0) {
|
|
2115
|
-
const delta = entry.projection.applyTransformSpans(transformSpans);
|
|
2116
|
-
entry.topology.apply(topologyDeltaForAvailable(delta, entry.unavailableSlots, []));
|
|
2117
|
-
this.syncGpuOwner(entry.token, entry.projection, delta);
|
|
2118
|
-
this.deltaFrames += 1;
|
|
2119
|
-
this.transformUpdates += delta.updated;
|
|
2120
2209
|
}
|
|
2121
2210
|
const result = this.deriveFramePlan(entry, buildCandidateFrame);
|
|
2211
|
+
entry.catalogEpoch = catalogEpoch;
|
|
2212
|
+
entry.instanceEpoch = instanceEpoch;
|
|
2122
2213
|
for (let worldId = 0; worldId < nextReadVersions.length; worldId += 1) {
|
|
2123
2214
|
const version = nextReadVersions[worldId];
|
|
2124
2215
|
if (version !== undefined) entry.readVersions[worldId] = version;
|
|
@@ -2128,6 +2219,7 @@ export class PersistentRenderScene {
|
|
|
2128
2219
|
|
|
2129
2220
|
invalidate(): void {
|
|
2130
2221
|
this.composition = undefined;
|
|
2222
|
+
this.shadowProjection = undefined;
|
|
2131
2223
|
this.temporalCapture = undefined;
|
|
2132
2224
|
this.visibilityFacets.clear();
|
|
2133
2225
|
this.invalidateActiveEntityRevision();
|
|
@@ -2150,16 +2242,9 @@ export class PersistentRenderScene {
|
|
|
2150
2242
|
? this.composition
|
|
2151
2243
|
: undefined;
|
|
2152
2244
|
if (detachedComposition !== undefined) {
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
this.options.instanceCollections?.release(world, slot.entityKey);
|
|
2156
|
-
if (slot.snapshot.skin === undefined) continue;
|
|
2157
|
-
this.options
|
|
2158
|
-
.getSkinPaletteAllocator?.()
|
|
2159
|
-
?.releaseSliceFor(`${world.identity}:${slot.entityKey}`);
|
|
2160
|
-
}
|
|
2245
|
+
this.composition = undefined;
|
|
2246
|
+
this.shadowProjection = undefined;
|
|
2161
2247
|
}
|
|
2162
|
-
if (detachedComposition !== undefined) this.composition = undefined;
|
|
2163
2248
|
if (detachedComposition !== undefined) this.temporalCapture = undefined;
|
|
2164
2249
|
if (detachedComposition !== undefined) {
|
|
2165
2250
|
this.visibilityFacets.clear();
|
|
@@ -2171,7 +2256,7 @@ export class PersistentRenderScene {
|
|
|
2171
2256
|
this.stableSlotByEntity.clear();
|
|
2172
2257
|
}
|
|
2173
2258
|
if (this.gpuOwner !== world && this.gpuOwner !== detachedComposition?.token) return;
|
|
2174
|
-
this.gpuScene
|
|
2259
|
+
this.retireGpuScene(this.gpuScene);
|
|
2175
2260
|
this.gpuScene = undefined;
|
|
2176
2261
|
this.gpuOwner = undefined;
|
|
2177
2262
|
this.gpuDevice = undefined;
|
|
@@ -2181,6 +2266,7 @@ export class PersistentRenderScene {
|
|
|
2181
2266
|
/** Drop only device-owned tables; the CPU projection remains the recovery authority. */
|
|
2182
2267
|
resetGpuForRecover(): void {
|
|
2183
2268
|
if (this.composition !== undefined) this.composition.gpuDrivenSceneRequired = true;
|
|
2269
|
+
this.retireGpuScene(this.gpuScene);
|
|
2184
2270
|
this.gpuScene = undefined;
|
|
2185
2271
|
this.gpuOwner = undefined;
|
|
2186
2272
|
this.gpuDevice = undefined;
|
|
@@ -2256,6 +2342,7 @@ export class PersistentRenderScene {
|
|
|
2256
2342
|
publish: () => {
|
|
2257
2343
|
if (published || released) return;
|
|
2258
2344
|
published = true;
|
|
2345
|
+
this.retireGpuScene(this.gpuScene);
|
|
2259
2346
|
this.gpuScene = scene;
|
|
2260
2347
|
this.gpuOwner = composition.token;
|
|
2261
2348
|
this.gpuDevice = device;
|
|
@@ -2301,12 +2388,13 @@ export class PersistentRenderScene {
|
|
|
2301
2388
|
}
|
|
2302
2389
|
|
|
2303
2390
|
dispose(): void {
|
|
2304
|
-
this.gpuScene
|
|
2391
|
+
this.retireGpuScene(this.gpuScene);
|
|
2305
2392
|
this.gpuScene = undefined;
|
|
2306
2393
|
this.gpuOwner = undefined;
|
|
2307
2394
|
this.gpuDevice = undefined;
|
|
2308
2395
|
this.gpuStatus = 'inactive';
|
|
2309
2396
|
this.composition = undefined;
|
|
2397
|
+
this.shadowProjection = undefined;
|
|
2310
2398
|
this.temporalCapture = undefined;
|
|
2311
2399
|
this.visibilityFacets.clear();
|
|
2312
2400
|
this.invalidateActiveEntityRevision();
|
|
@@ -2316,6 +2404,59 @@ export class PersistentRenderScene {
|
|
|
2316
2404
|
this.pointsLinesInspections = [];
|
|
2317
2405
|
}
|
|
2318
2406
|
|
|
2407
|
+
private retireGpuScene(scene: GpuScene | undefined, device = this.gpuDevice): void {
|
|
2408
|
+
if (scene === undefined) return;
|
|
2409
|
+
if (device === undefined) {
|
|
2410
|
+
scene.dispose();
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
this.retiredGpuScenes.add(scene);
|
|
2414
|
+
const release = (): void => {
|
|
2415
|
+
if (!this.retiredGpuScenes.delete(scene)) return;
|
|
2416
|
+
scene.dispose();
|
|
2417
|
+
};
|
|
2418
|
+
// Retirement can happen while the current frame is still being encoded
|
|
2419
|
+
// (for example when a new composition token is published). Calling
|
|
2420
|
+
// onSubmittedWorkDone() synchronously at that point observes only work
|
|
2421
|
+
// already queued and may resolve before the frame that still references
|
|
2422
|
+
// this scene is submitted. Defer fence acquisition to the next
|
|
2423
|
+
// microtask, after the synchronous record/submit transaction has had a
|
|
2424
|
+
// chance to enqueue its command buffer.
|
|
2425
|
+
void Promise.resolve()
|
|
2426
|
+
.then(() => {
|
|
2427
|
+
try {
|
|
2428
|
+
return device.queue.onSubmittedWorkDone();
|
|
2429
|
+
} catch {
|
|
2430
|
+
return undefined;
|
|
2431
|
+
}
|
|
2432
|
+
})
|
|
2433
|
+
.then(release, release);
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
/** Read existing culling bounds; never expose cached mutable arrays. */
|
|
2437
|
+
bounds(world: World, entity: number): RenderSceneBounds | undefined {
|
|
2438
|
+
const entry = this.composition;
|
|
2439
|
+
const worldId = entry?.worlds.indexOf(world) ?? -1;
|
|
2440
|
+
if (entry === undefined || worldId < 0) return undefined;
|
|
2441
|
+
const slot = entry.projection.slot(worldId, entity);
|
|
2442
|
+
if (slot === undefined) return undefined;
|
|
2443
|
+
const bounds = entry.projection.cullingWorldBoundsAt(slot);
|
|
2444
|
+
if (bounds === undefined) return undefined;
|
|
2445
|
+
for (const axis of [0, 1, 2] as const) {
|
|
2446
|
+
if (
|
|
2447
|
+
!Number.isFinite(bounds.min[axis]) ||
|
|
2448
|
+
!Number.isFinite(bounds.max[axis]) ||
|
|
2449
|
+
bounds.min[axis] > bounds.max[axis]
|
|
2450
|
+
)
|
|
2451
|
+
return undefined;
|
|
2452
|
+
}
|
|
2453
|
+
return { min: [...bounds.min], max: [...bounds.max] };
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
reflectionCaptureRevision(): string {
|
|
2457
|
+
return `${this.fullRebuilds}:${this.composition?.projection.contentRevision ?? 0}`;
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2319
2460
|
inspect(): PersistentRenderSceneInspection {
|
|
2320
2461
|
const entry = this.composition;
|
|
2321
2462
|
const pointsLines = this.pointsLinesInspections;
|
|
@@ -2327,6 +2468,9 @@ export class PersistentRenderScene {
|
|
|
2327
2468
|
transformUpdates: this.transformUpdates,
|
|
2328
2469
|
lastResyncReason: this.lastResyncReason,
|
|
2329
2470
|
projectionRecords: entry?.projection.inspect().records.length ?? 0,
|
|
2471
|
+
...(entry === undefined
|
|
2472
|
+
? {}
|
|
2473
|
+
: { instanceBoundsCache: entry.projection.inspect().instanceBoundsCache }),
|
|
2330
2474
|
...(entry === undefined ? {} : { probeBlend: entry.probeProjection.inspect() }),
|
|
2331
2475
|
topology:
|
|
2332
2476
|
entry?.topology.inspect() ??
|
|
@@ -2353,6 +2497,11 @@ export class PersistentRenderScene {
|
|
|
2353
2497
|
plan: entry.topology.plan(),
|
|
2354
2498
|
slots: entry.projection.slotsSnapshot(),
|
|
2355
2499
|
worldKeys: entry.worldKeys,
|
|
2500
|
+
structureMetrics: {
|
|
2501
|
+
worldEntitiesScanned: this.worldEntitiesScanned,
|
|
2502
|
+
sceneTableUploadBytes: this.sceneTableUploadBytes,
|
|
2503
|
+
paletteUploadBytes: 0,
|
|
2504
|
+
},
|
|
2356
2505
|
};
|
|
2357
2506
|
}
|
|
2358
2507
|
|
|
@@ -2408,9 +2557,9 @@ export class PersistentRenderScene {
|
|
|
2408
2557
|
return { status: 'inactive' };
|
|
2409
2558
|
}
|
|
2410
2559
|
|
|
2411
|
-
private
|
|
2560
|
+
private reconcileComposition(
|
|
2412
2561
|
worlds: readonly World[],
|
|
2413
|
-
|
|
2562
|
+
_owner: { readonly cameraOwner: number; readonly resourceOwner: number },
|
|
2414
2563
|
catalogEpoch: number,
|
|
2415
2564
|
buildCandidateFrame: (request: PersistentRenderCandidateRequest) => ExtractedFrame,
|
|
2416
2565
|
leases?: readonly RenderReadLease[],
|
|
@@ -2471,8 +2620,28 @@ export class PersistentRenderScene {
|
|
|
2471
2620
|
renderables: sortedRenderables,
|
|
2472
2621
|
dispatch: sortedDispatch,
|
|
2473
2622
|
};
|
|
2474
|
-
const projection = new RenderScene();
|
|
2475
|
-
|
|
2623
|
+
const projection = previousEntry?.projection ?? new RenderScene();
|
|
2624
|
+
if (previousEntry !== undefined) {
|
|
2625
|
+
projection.remapWorlds(previousEntry.worlds.map((world) => worlds.indexOf(world)));
|
|
2626
|
+
}
|
|
2627
|
+
const desired = new Set(
|
|
2628
|
+
sortedRenderables.map((snapshot) => identityKey(snapshot.worldId, snapshot.entityKey)),
|
|
2629
|
+
);
|
|
2630
|
+
const operations: RenderSceneOperation[] = [];
|
|
2631
|
+
for (const slot of projection.slotsSnapshot()) {
|
|
2632
|
+
if (!desired.has(identityKey(slot.worldId, slot.entityKey))) {
|
|
2633
|
+
operations.push({ kind: 'remove', worldId: slot.worldId, entityKey: slot.entityKey });
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
for (const snapshot of sortedRenderables) {
|
|
2637
|
+
operations.push({
|
|
2638
|
+
kind: 'update',
|
|
2639
|
+
worldId: snapshot.worldId,
|
|
2640
|
+
entityKey: snapshot.entityKey,
|
|
2641
|
+
snapshot,
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
const delta = projection.apply(operations);
|
|
2476
2645
|
this.stableSlotByEntity.clear();
|
|
2477
2646
|
for (const slot of projection.slotsSnapshot()) {
|
|
2478
2647
|
const stableWorld = worldKeys[slot.worldId] ?? slot.worldId;
|
|
@@ -2480,28 +2649,20 @@ export class PersistentRenderScene {
|
|
|
2480
2649
|
}
|
|
2481
2650
|
const transmissionDemand = new PersistentTransmissionDemandProjection();
|
|
2482
2651
|
transmissionDemand.apply(transmissionDemandOperations(candidateFrame.renderables));
|
|
2483
|
-
const topology = new BatchTopology();
|
|
2484
|
-
topology.
|
|
2652
|
+
const topology = previousEntry?.topology ?? new BatchTopology();
|
|
2653
|
+
topology.apply(delta);
|
|
2485
2654
|
const skinConsumersByJoint = new Map<string, Set<number>>();
|
|
2486
2655
|
for (const snapshot of stableCandidateFrame.renderables) {
|
|
2487
2656
|
if (snapshot.skinJointEntities !== undefined) addSkinConsumer(skinConsumersByJoint, snapshot);
|
|
2488
2657
|
}
|
|
2489
|
-
const token = {};
|
|
2658
|
+
const token = previousEntry?.token ?? {};
|
|
2490
2659
|
const entry: PersistentCompositionEntry = {
|
|
2491
2660
|
token,
|
|
2492
2661
|
worlds: [...worlds],
|
|
2493
|
-
cameraOwner: owner.cameraOwner,
|
|
2494
|
-
resourceOwner: owner.resourceOwner,
|
|
2495
2662
|
projection,
|
|
2496
2663
|
topology,
|
|
2497
2664
|
transmissionDemand,
|
|
2498
|
-
|
|
2499
|
-
dispatch: stableCandidateFrame.dispatch,
|
|
2500
|
-
visibilityStats: stableCandidateFrame.visibilityStats,
|
|
2501
|
-
visibilitySnapshots: stableCandidateFrame.visibilitySnapshots,
|
|
2502
|
-
featureVisibilitySnapshots: stableCandidateFrame.featureVisibilitySnapshots,
|
|
2503
|
-
hiddenEntityReports: stableCandidateFrame.hiddenEntityReports,
|
|
2504
|
-
},
|
|
2665
|
+
hiddenEntityReports: stableCandidateFrame.hiddenEntityReports,
|
|
2505
2666
|
worldKeys,
|
|
2506
2667
|
readVersions:
|
|
2507
2668
|
leases === undefined || leases.length !== worlds.length
|
|
@@ -2526,6 +2687,7 @@ export class PersistentRenderScene {
|
|
|
2526
2687
|
pendingRenderableEntitiesByWorld: worlds.map(() => new Set<number>()),
|
|
2527
2688
|
unavailableSlots: new Set<number>(),
|
|
2528
2689
|
catalogEpoch,
|
|
2690
|
+
instanceEpoch: this.options.instanceCollections?.epoch ?? 0,
|
|
2529
2691
|
probeProjection: new ProbeBlendSceneProjection(),
|
|
2530
2692
|
temporalDemanded: frameDemandsTemporal(candidateFrame),
|
|
2531
2693
|
gpuDrivenSceneRequired: true,
|
|
@@ -2539,8 +2701,8 @@ export class PersistentRenderScene {
|
|
|
2539
2701
|
// later rebuild.
|
|
2540
2702
|
this.projectReflectionProbes(candidateFrame.reflectionProbes ?? []);
|
|
2541
2703
|
this.projectProbes(entry, candidateFrame);
|
|
2542
|
-
this.syncGpuOwner(token, projection,
|
|
2543
|
-
this.fullRebuilds += 1;
|
|
2704
|
+
this.syncGpuOwner(token, projection, delta);
|
|
2705
|
+
if (previousEntry === undefined) this.fullRebuilds += 1;
|
|
2544
2706
|
this.lastResyncReason = 'attach';
|
|
2545
2707
|
const materialized = projection.materialize();
|
|
2546
2708
|
const candidates = entry.temporalDemanded
|
|
@@ -2553,16 +2715,14 @@ export class PersistentRenderScene {
|
|
|
2553
2715
|
entry.probeProjection,
|
|
2554
2716
|
);
|
|
2555
2717
|
const slots = projection.slotsSnapshot();
|
|
2556
|
-
const
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
},
|
|
2565
|
-
);
|
|
2718
|
+
const ownedFrame = this.projectShadowOwnership(frameWithProbes);
|
|
2719
|
+
this.shadowProjection = { renderables: ownedFrame.renderables, dispatch: ownedFrame.dispatch };
|
|
2720
|
+
const result = cullPersistentFrame(ownedFrame, ownedFrame.renderables, (snapshot, index) => {
|
|
2721
|
+
const slot = slots[index];
|
|
2722
|
+
return slot === undefined
|
|
2723
|
+
? projection.cullingWorldBounds(snapshot)
|
|
2724
|
+
: projection.cullingWorldBoundsAt(slot);
|
|
2725
|
+
});
|
|
2566
2726
|
this.frameEpoch += 1;
|
|
2567
2727
|
this.visibilityFacetFrame = undefined;
|
|
2568
2728
|
this.visibilityProjectionCache = undefined;
|
|
@@ -2589,17 +2749,17 @@ export class PersistentRenderScene {
|
|
|
2589
2749
|
): ExtractedFrame {
|
|
2590
2750
|
const resourceFrame = buildCandidateFrame('none');
|
|
2591
2751
|
const cachedDispatch = entry.dispatchCache;
|
|
2592
|
-
const dispatch
|
|
2593
|
-
|
|
2752
|
+
const dispatch =
|
|
2753
|
+
cachedDispatch?.revision === entry.dispatchRevision
|
|
2594
2754
|
? cachedDispatch.value
|
|
2595
|
-
: dispatchForProjection(entry.dispatchBySlot, entry.projection, entry.unavailableSlots)
|
|
2596
|
-
];
|
|
2755
|
+
: dispatchForProjection(entry.dispatchBySlot, entry.projection, entry.unavailableSlots);
|
|
2597
2756
|
if (cachedDispatch?.revision !== entry.dispatchRevision) {
|
|
2598
2757
|
entry.dispatchCache = { revision: entry.dispatchRevision, value: dispatch };
|
|
2599
2758
|
}
|
|
2600
2759
|
const candidateFrame: ExtractedFrame = {
|
|
2601
2760
|
...resourceFrame,
|
|
2602
|
-
|
|
2761
|
+
hiddenEntityReports: entry.hiddenEntityReports,
|
|
2762
|
+
visibilityStats: { explicitlyHidden: entry.hiddenEntityReports.length },
|
|
2603
2763
|
dispatch,
|
|
2604
2764
|
};
|
|
2605
2765
|
entry.temporalDemanded = frameDemandsTemporal(candidateFrame);
|
|
@@ -2626,22 +2786,55 @@ export class PersistentRenderScene {
|
|
|
2626
2786
|
entry.probeProjection,
|
|
2627
2787
|
);
|
|
2628
2788
|
const slots = entry.projection.slotsSnapshot();
|
|
2629
|
-
const
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
},
|
|
2638
|
-
);
|
|
2789
|
+
const ownedFrame = this.projectShadowOwnership(frameWithProbes);
|
|
2790
|
+
this.shadowProjection = { renderables: ownedFrame.renderables, dispatch: ownedFrame.dispatch };
|
|
2791
|
+
const result = cullPersistentFrame(ownedFrame, ownedFrame.renderables, (snapshot, index) => {
|
|
2792
|
+
const slot = slots[index];
|
|
2793
|
+
return slot === undefined
|
|
2794
|
+
? entry.projection.cullingWorldBounds(snapshot)
|
|
2795
|
+
: entry.projection.cullingWorldBoundsAt(slot);
|
|
2796
|
+
});
|
|
2639
2797
|
this.frameEpoch += 1;
|
|
2640
2798
|
this.visibilityFacetFrame = undefined;
|
|
2641
2799
|
this.visibilityProjectionCache = undefined;
|
|
2642
2800
|
return result;
|
|
2643
2801
|
}
|
|
2644
2802
|
|
|
2803
|
+
private projectShadowOwnership(frame: ExtractedFrame): ExtractedFrame {
|
|
2804
|
+
const shadowCasterEntityKeys = new Set<number>();
|
|
2805
|
+
const shadowCasterDrawKeys = new Set<string>();
|
|
2806
|
+
const shadowCasterMembership: NonNullable<ExtractedFrame['shadowCasterMembership']>[number][] =
|
|
2807
|
+
[];
|
|
2808
|
+
for (
|
|
2809
|
+
let renderableIndex = 0;
|
|
2810
|
+
renderableIndex < frame.renderables.length;
|
|
2811
|
+
renderableIndex += 1
|
|
2812
|
+
) {
|
|
2813
|
+
const snapshot = frame.renderables[renderableIndex];
|
|
2814
|
+
if (snapshot === undefined || snapshot.authorVisible === false) continue;
|
|
2815
|
+
const worldEntity = worldEntityKey(snapshot.worldId, snapshot.entityKey);
|
|
2816
|
+
for (const pass of snapshot.shadowCasterPasses ?? []) {
|
|
2817
|
+
shadowCasterMembership.push({ ...pass, worldEntity, renderableIndex });
|
|
2818
|
+
shadowCasterEntityKeys.add(worldEntity);
|
|
2819
|
+
shadowCasterDrawKeys.add(
|
|
2820
|
+
gpuDrivenShadowDrawKey(
|
|
2821
|
+
worldEntity,
|
|
2822
|
+
pass.materialHandle,
|
|
2823
|
+
pass.drawItemIndex,
|
|
2824
|
+
pass.passIndex,
|
|
2825
|
+
),
|
|
2826
|
+
);
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
for (const draw of frame.dispatch) {
|
|
2830
|
+
if (draw.tags.LightMode !== 'ShadowCaster') continue;
|
|
2831
|
+
const snapshot = frame.renderables[draw.renderableIndex];
|
|
2832
|
+
if (snapshot !== undefined)
|
|
2833
|
+
shadowCasterEntityKeys.add(worldEntityKey(snapshot.worldId, snapshot.entityKey));
|
|
2834
|
+
}
|
|
2835
|
+
return { ...frame, shadowCasterEntityKeys, shadowCasterDrawKeys, shadowCasterMembership };
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2645
2838
|
private projectProbes(entry: PersistentCompositionEntry, frame: ExtractedFrame): void {
|
|
2646
2839
|
const sky =
|
|
2647
2840
|
frame.skylight === undefined
|
|
@@ -2692,14 +2885,19 @@ export class PersistentRenderScene {
|
|
|
2692
2885
|
owner: object,
|
|
2693
2886
|
projection: RenderScene,
|
|
2694
2887
|
delta?: RenderSceneApplyResult,
|
|
2695
|
-
full = false,
|
|
2696
2888
|
): void {
|
|
2697
2889
|
if (this.composition?.token === owner && !this.composition.gpuDrivenSceneRequired) return;
|
|
2698
2890
|
const acquired = this.acquireGpuScene(owner, projection);
|
|
2699
|
-
if (acquired === undefined
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2891
|
+
if (acquired === undefined) {
|
|
2892
|
+
this.sceneTableUploadBytes = 0;
|
|
2893
|
+
return;
|
|
2894
|
+
}
|
|
2895
|
+
if (acquired.rebuilt) {
|
|
2896
|
+
this.sceneTableUploadBytes = acquired.uploadBytes;
|
|
2897
|
+
return;
|
|
2898
|
+
}
|
|
2899
|
+
const result =
|
|
2900
|
+
delta === undefined
|
|
2703
2901
|
? acquired.scene.sync({
|
|
2704
2902
|
created: 0,
|
|
2705
2903
|
updated: 0,
|
|
@@ -2708,22 +2906,32 @@ export class PersistentRenderScene {
|
|
|
2708
2906
|
ignoredLateUpdates: 0,
|
|
2709
2907
|
createdSlots: [],
|
|
2710
2908
|
updatedSlots: [],
|
|
2909
|
+
contentUpdatedSlots: [],
|
|
2910
|
+
instanceUpdatedSlots: [],
|
|
2711
2911
|
removedSlots: [],
|
|
2712
2912
|
recreatedSlots: [],
|
|
2713
2913
|
resynced: 0,
|
|
2714
2914
|
})
|
|
2715
2915
|
: acquired.scene.sync(delta);
|
|
2716
|
-
if (!result.ok)
|
|
2916
|
+
if (!result.ok) {
|
|
2917
|
+
this.sceneTableUploadBytes = 0;
|
|
2918
|
+
this.failGpu(result.error);
|
|
2919
|
+
return;
|
|
2920
|
+
}
|
|
2921
|
+
this.sceneTableUploadBytes = result.value.bytes;
|
|
2717
2922
|
}
|
|
2718
2923
|
|
|
2719
2924
|
private acquireGpuScene(
|
|
2720
2925
|
owner: object,
|
|
2721
2926
|
projection: RenderScene,
|
|
2722
|
-
):
|
|
2927
|
+
):
|
|
2928
|
+
| { readonly scene: GpuScene; readonly rebuilt: boolean; readonly uploadBytes: number }
|
|
2929
|
+
| undefined {
|
|
2723
2930
|
const getDevice = this.options.getDevice;
|
|
2724
2931
|
if (getDevice === undefined) return undefined;
|
|
2725
2932
|
const device = getDevice();
|
|
2726
2933
|
if (this.gpuDevice !== undefined && this.gpuDevice !== device) {
|
|
2934
|
+
this.retireGpuScene(this.gpuScene, this.gpuDevice);
|
|
2727
2935
|
this.gpuScene = undefined;
|
|
2728
2936
|
this.gpuOwner = undefined;
|
|
2729
2937
|
this.gpuStatus = 'rebuild-pending';
|
|
@@ -2731,9 +2939,23 @@ export class PersistentRenderScene {
|
|
|
2731
2939
|
if (this.gpuStatus === 'error' && this.gpuDevice === device) return undefined;
|
|
2732
2940
|
if (this.gpuStatus === 'unsupported' && this.gpuDevice === device) return undefined;
|
|
2733
2941
|
if (this.gpuScene !== undefined && this.gpuOwner === owner) {
|
|
2734
|
-
return { scene: this.gpuScene, rebuilt: false };
|
|
2942
|
+
return { scene: this.gpuScene, rebuilt: false, uploadBytes: 0 };
|
|
2943
|
+
}
|
|
2944
|
+
if (this.gpuScene !== undefined && this.gpuDevice === device) {
|
|
2945
|
+
// Composition rebuilds replace the CPU projection token, but the GPU
|
|
2946
|
+
// table resource can be rebuilt in place. Keeping one scene identity
|
|
2947
|
+
// avoids destroying buffers that cached frame graphs still reference
|
|
2948
|
+
// and lets queue ordering preserve writes across the next submission.
|
|
2949
|
+
const rebuilt = this.gpuScene.rebuild(projection.slotsSnapshot());
|
|
2950
|
+
if (!rebuilt.ok) {
|
|
2951
|
+
this.failGpu(rebuilt.error);
|
|
2952
|
+
return undefined;
|
|
2953
|
+
}
|
|
2954
|
+
this.gpuOwner = owner;
|
|
2955
|
+
this.gpuStatus = 'resident';
|
|
2956
|
+
return { scene: this.gpuScene, rebuilt: true, uploadBytes: rebuilt.value.bytes };
|
|
2735
2957
|
}
|
|
2736
|
-
|
|
2958
|
+
this.retireGpuScene(this.gpuScene);
|
|
2737
2959
|
this.gpuScene = undefined;
|
|
2738
2960
|
this.gpuOwner = undefined;
|
|
2739
2961
|
this.gpuDevice = device;
|
|
@@ -2756,11 +2978,11 @@ export class PersistentRenderScene {
|
|
|
2756
2978
|
this.gpuScene = scene;
|
|
2757
2979
|
this.gpuOwner = owner;
|
|
2758
2980
|
this.gpuStatus = 'resident';
|
|
2759
|
-
return { scene, rebuilt: true };
|
|
2981
|
+
return { scene, rebuilt: true, uploadBytes: rebuilt.value.bytes };
|
|
2760
2982
|
}
|
|
2761
2983
|
|
|
2762
2984
|
private failGpu(error: RhiError): void {
|
|
2763
|
-
this.gpuScene
|
|
2985
|
+
this.retireGpuScene(this.gpuScene);
|
|
2764
2986
|
this.gpuScene = undefined;
|
|
2765
2987
|
this.gpuOwner = undefined;
|
|
2766
2988
|
this.gpuStatus = 'error';
|