@forgeax/engine-render 0.1.3 → 0.1.6
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 +130 -641
- package/dist/.tsbuildinfo +1 -1
- package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts +2 -0
- package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts.map +1 -0
- package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts +2 -0
- package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts.map +1 -0
- package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +1 -1
- package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts +6 -0
- package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts.map +1 -1
- package/dist/{gpu-driven/view-reference.d.ts → __tests__/gpu-driven-view-reference.d.ts} +4 -4
- package/dist/__tests__/gpu-driven-view-reference.d.ts.map +1 -0
- package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts.map +1 -1
- package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts +2 -0
- package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts.map +1 -0
- package/dist/__tests__/morph-feature-plan.unit.test.d.ts +2 -0
- package/dist/__tests__/morph-feature-plan.unit.test.d.ts.map +1 -0
- package/dist/__tests__/render-feature-plan.test-d.d.ts +2 -0
- package/dist/__tests__/render-feature-plan.test-d.d.ts.map +1 -0
- package/dist/__tests__/render-feature-plan.unit.test.d.ts +2 -0
- package/dist/__tests__/render-feature-plan.unit.test.d.ts.map +1 -0
- package/dist/__tests__/ssao-config.unit.test.d.ts +2 -0
- package/dist/__tests__/ssao-config.unit.test.d.ts.map +1 -0
- package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts +2 -0
- package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts.map +1 -0
- package/dist/__tests__/standard-descriptors.test-d.d.ts +2 -0
- package/dist/__tests__/standard-descriptors.test-d.d.ts.map +1 -0
- package/dist/__tests__/standard-pipeline.dawn.test.d.ts +2 -0
- package/dist/__tests__/standard-pipeline.dawn.test.d.ts.map +1 -0
- package/dist/__tests__/standard-pipeline.integration.test.d.ts +2 -0
- package/dist/__tests__/standard-pipeline.integration.test.d.ts.map +1 -0
- package/dist/__tests__/standard-profile-oracle.unit.test.d.ts +2 -0
- package/dist/__tests__/standard-profile-oracle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts +2 -0
- package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts.map +1 -0
- package/dist/{renderer/backend-selection.d.ts → assembly/backend-contract.d.ts} +15 -4
- package/dist/assembly/backend-contract.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +207 -0
- package/dist/assembly/factory.d.ts.map +1 -0
- package/dist/assembly/host-contract.d.ts +84 -0
- package/dist/assembly/host-contract.d.ts.map +1 -0
- package/dist/{renderer → assembly}/material/assembly.d.ts +1 -2
- package/dist/{renderer → assembly}/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-projection.d.ts +30 -0
- package/dist/assembly/material/pipeline-projection.d.ts.map +1 -0
- package/dist/authoring.d.ts +10 -11
- package/dist/authoring.d.ts.map +1 -1
- package/dist/authoring.mjs +25 -3
- package/dist/authoring.mjs.map +1 -1
- package/dist/chunk-CQXZC5XI.mjs +182 -0
- package/dist/chunk-CQXZC5XI.mjs.map +1 -0
- package/dist/chunk-QC4VW3XJ.mjs +79 -0
- package/dist/chunk-QC4VW3XJ.mjs.map +1 -0
- package/dist/chunk-SCRSAK5B.mjs +480 -0
- package/dist/chunk-SCRSAK5B.mjs.map +1 -0
- package/dist/components/__tests__/schema-contract.unit.test.d.ts +2 -0
- package/dist/components/__tests__/schema-contract.unit.test.d.ts.map +1 -0
- package/dist/components/glyph-text.d.ts +1 -1
- package/dist/components/glyph-text.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +1 -1
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/mesh-filter.d.ts +7 -7
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/scene-instance.d.ts +11 -11
- package/dist/components/skybox-background.d.ts +56 -0
- package/dist/components/skybox-background.d.ts.map +1 -0
- package/dist/components/skylight.d.ts +55 -0
- package/dist/components/skylight.d.ts.map +1 -0
- package/dist/components/sprite-animation.d.ts +2 -3
- package/dist/components/sprite-animation.d.ts.map +1 -1
- package/dist/components/sprite-instances.d.ts +1 -1
- package/dist/components/sprite-playback-mode.d.ts +6 -1
- package/dist/components/sprite-playback-mode.d.ts.map +1 -1
- package/dist/components/sprite-region-override.d.ts +2 -3
- package/dist/components/sprite-region-override.d.ts.map +1 -1
- package/dist/components/tile-layer.d.ts +5 -0
- package/dist/components/tile-layer.d.ts.map +1 -1
- package/dist/construct-renderer.d.ts +18 -1
- package/dist/construct-renderer.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +27008 -4
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/debug-draw-glue.d.ts +2 -53
- package/dist/debug-draw-glue.d.ts.map +1 -1
- package/dist/device/device-scope.d.ts +83 -0
- package/dist/device/device-scope.d.ts.map +1 -0
- package/dist/{gpu-resource-store.d.ts → device/gpu-residency.d.ts} +91 -48
- package/dist/device/gpu-residency.d.ts.map +1 -0
- package/dist/device/resource-types.d.ts +8 -0
- package/dist/device/resource-types.d.ts.map +1 -0
- package/dist/engine-metrics.d.ts +2 -2
- package/dist/engine-metrics.d.ts.map +1 -1
- package/dist/errors/recover.d.ts +28 -1
- package/dist/errors/recover.d.ts.map +1 -1
- package/dist/errors/render.d.ts +249 -258
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/features/fullscreen.d.ts +19 -0
- package/dist/features/fullscreen.d.ts.map +1 -0
- package/dist/features/host.d.ts +32 -26
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/morph/morph-feature.d.ts +39 -5
- package/dist/features/morph/morph-feature.d.ts.map +1 -1
- package/dist/features/plan.d.ts +182 -0
- package/dist/features/plan.d.ts.map +1 -0
- package/dist/features/prepared-gpu-work.d.ts +21 -17
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts +2 -1
- package/dist/features/prepared-graphics-store.d.ts.map +1 -1
- package/dist/features/prepared-graphics.d.ts +11 -21
- package/dist/features/prepared-graphics.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts +2 -2
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +4 -7
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +2 -2
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/render-graph-resources.d.ts +6 -6
- package/dist/features/render-graph-resources.d.ts.map +1 -1
- package/dist/features/targets.d.ts +5 -5
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +11 -190
- package/dist/features/types.d.ts.map +1 -1
- package/dist/features/vocabulary.d.ts +14 -0
- package/dist/features/vocabulary.d.ts.map +1 -0
- package/dist/fullscreen-post-process-pass.d.ts +4 -25
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/glyph-text-layout-system.d.ts +3 -2
- package/dist/glyph-text-layout-system.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +6 -75
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +13 -112
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +10 -66
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-resource.d.ts +3 -3
- package/dist/gpu-scene-schema.d.ts +1 -94
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +7 -33
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +3 -53
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +74 -180
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +1 -1
- package/dist/index.d.ts +13 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +165 -3
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +69 -0
- package/dist/inspection-types.d.ts.map +1 -0
- package/dist/instance-buffer-cache.d.ts +1 -4
- package/dist/instance-buffer-cache.d.ts.map +1 -1
- package/dist/internal.d.ts +1 -73
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +1 -43
- package/dist/internal.mjs.map +1 -1
- package/dist/lifecycle.d.ts +36 -2
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/materials.d.ts +0 -3
- package/dist/materials.d.ts.map +1 -1
- package/dist/mesh-material-bindings.d.ts +0 -1
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +75 -29
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts +5 -0
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts +31 -0
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -0
- package/dist/pipeline/standard-profile.d.ts +40 -0
- package/dist/pipeline/standard-profile.d.ts.map +1 -0
- package/dist/pipeline-builder.d.ts +3 -3
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec-types.d.ts +24 -0
- package/dist/pipeline-spec-types.d.ts.map +1 -0
- package/dist/pipeline-spec.d.ts +16 -155
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts +1 -14
- package/dist/plugin.d.ts.map +1 -1
- package/dist/post-process-errors.d.ts +18 -18
- package/dist/post-process-errors.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +2 -5
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts +2 -0
- package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +7 -73
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +44 -44
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts +5 -38
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +64 -52
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +3 -2
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +5 -4
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +90 -59
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +28 -3
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +276 -3
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +235 -0
- package/dist/record/render-context.d.ts.map +1 -0
- package/dist/record/shadow-pass.d.ts +15 -88
- 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 +29 -7
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +3 -3
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/render-contract.d.ts +416 -0
- package/dist/render-contract.d.ts.map +1 -0
- package/dist/render-data.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +14 -362
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +25 -64
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +107 -338
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +21 -713
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +58 -0
- package/dist/scene/render-scene-types.d.ts.map +1 -0
- package/dist/scene/render-scene.d.ts +105 -0
- package/dist/scene/render-scene.d.ts.map +1 -0
- package/dist/scene-instances/post-spawn-resolve-joints.d.ts +2 -5
- package/dist/scene-instances/post-spawn-resolve-joints.d.ts.map +1 -1
- package/dist/ssao-buffers.d.ts +22 -3
- package/dist/ssao-buffers.d.ts.map +1 -1
- package/dist/systems/skin-palette-allocator.d.ts +2 -4
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +14 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -0
- package/dist/systems/transparent-sort-config.d.ts +23 -2
- package/dist/systems/transparent-sort-config.d.ts.map +1 -1
- package/dist/tilemap-chunk-extract-system.d.ts +4 -3
- package/dist/tilemap-chunk-extract-system.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +2 -4
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +3 -3
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/package.json +23 -23
- package/src/__tests__/backend-selection-rhi-capture.test-d.ts +1 -1
- package/src/__tests__/callback-inference.test-d.ts +6 -3
- package/src/__tests__/cluster-grid-default-surface.unit.test.ts +7 -4
- package/src/__tests__/construct-renderer-identity.unit.test.ts +15 -2
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +151 -0
- package/src/__tests__/device-scope-stale-matrix.unit.test.ts +42 -0
- package/src/__tests__/direct-extract-consumer-contract.test-d.ts +8 -19
- package/src/__tests__/direct-light-snapshot.unit.test.ts +3 -13
- package/src/__tests__/factory-contract.integration.test.ts +158 -209
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +1 -4
- package/src/__tests__/frame-observation.contract.test.ts +17 -8
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +32 -0
- package/src/__tests__/glyph-text-layout-recovery.unit.test.ts +6 -11
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +9 -16
- package/src/__tests__/gpu-driven-production.integration.test.ts +32 -205
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +4 -7
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +87 -8
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +170 -8
- package/src/{gpu-driven/view-reference.ts → __tests__/gpu-driven-view-reference.ts} +4 -3
- package/src/__tests__/gpu-driven-view.browser.test.ts +12 -1
- package/src/__tests__/gpu-driven-view.dawn.test.ts +12 -1
- package/src/__tests__/gpu-driven-view.unit.test.ts +11 -267
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +3 -3
- package/src/__tests__/gpu-scene-render-graph-gpu.ts +2 -3
- package/src/__tests__/gpu-scene.dawn.test.ts +2 -6
- package/src/__tests__/gpu-scene.unit.test.ts +10 -24
- package/src/__tests__/ibl/face-uniforms.unit.test.ts +46 -0
- package/src/__tests__/infinite-grid-no-vertex-probe.unit.test.ts +34 -66
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +14 -5
- package/src/__tests__/light-buffer-layout.unit.test.ts +4 -49
- package/src/__tests__/material-cooked-projection.unit.test.ts +2 -51
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +10 -15
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +4 -23
- package/src/__tests__/material-stride-surface.unit.test.ts +2 -2
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +7 -8
- package/src/__tests__/morph-culling-reentry.dawn.test.ts +29 -38
- package/src/__tests__/morph-feature-gpu-work.integration.test.ts +45 -43
- package/src/__tests__/morph-feature-plan.unit.test.ts +53 -0
- package/src/__tests__/morph-visual-evidence.integration.test.ts +47 -33
- package/src/__tests__/nineslice-handle-surface.unit.test.ts +11 -6
- package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -13
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +6 -81
- package/src/__tests__/prepared-graphics-generation.unit.test.ts +3 -17
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +6 -74
- package/src/__tests__/prepared-graphics-resolver.unit.test.ts +1 -3
- package/src/__tests__/prepared-graphics-snapshot.unit.test.ts +2 -2
- package/src/__tests__/profiler-default-off.test.ts +1 -1
- package/src/__tests__/profiler-phase-catalog.test.ts +2 -2
- package/src/__tests__/profiler-phase-integration.test.ts +5 -13
- package/src/__tests__/profiler-phase-token.test.ts +3 -8
- package/src/__tests__/public-surface.test-d.ts +65 -3
- package/src/__tests__/recover-error-policy-owner.unit.test.ts +1 -21
- package/src/__tests__/render-error-code-owner.test-d.ts +18 -23
- package/src/__tests__/render-error-detail-ssot.test-d.ts +0 -95
- package/src/__tests__/render-error-exhaustive.test-d.ts +8 -1
- package/src/__tests__/render-feature-assembly.test-d.ts +8 -16
- package/src/__tests__/render-feature-context.test-d.ts +16 -40
- package/src/__tests__/render-feature-contract.test-d.ts +9 -18
- package/src/__tests__/render-feature-diagnostics.unit.test.ts +6 -8
- package/src/__tests__/render-feature-docs.unit.test.ts +8 -7
- package/src/__tests__/render-feature-gpu-work.integration.test.ts +14 -75
- package/src/__tests__/render-feature-plan.test-d.ts +161 -0
- package/src/__tests__/render-feature-plan.unit.test.ts +170 -0
- package/src/__tests__/render-feature-prepared-diagnostics.unit.test.ts +8 -19
- package/src/__tests__/render-feature-prepared-errors.test-d.ts +3 -6
- package/src/__tests__/render-feature-prepared-graphics-docs.unit.test.ts +10 -71
- package/src/__tests__/render-feature-prepared-graphics-isolation.unit.test.ts +11 -61
- package/src/__tests__/render-feature-prepared-graphics-negative.test-d.ts +20 -43
- package/src/__tests__/render-feature-prepared-graphics.test-d.ts +56 -78
- package/src/__tests__/render-feature-public-surface.test-d.ts +6 -24
- package/src/__tests__/render-feature-retry.unit.test.ts +7 -48
- package/src/__tests__/render-feature-scheduling.unit.test.ts +15 -27
- package/src/__tests__/render-feature-stage-failed-policy-owner.unit.test.ts +0 -9
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +19 -109
- package/src/__tests__/render-feature-visibility-context.test-d.ts +1 -6
- package/src/__tests__/render-feature-visibility-context.unit.test.ts +1 -2
- package/src/__tests__/render-feature-zero-work.unit.test.ts +14 -20
- package/src/__tests__/render-scene-projection.unit.test.ts +73 -111
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +46 -50
- package/src/__tests__/renderer-lifecycle.integration.test.ts +49 -1
- package/src/__tests__/shader-stage-surface.unit.test.ts +14 -9
- package/src/__tests__/ssao-config.unit.test.ts +36 -0
- package/src/__tests__/ssao-kernel-surface.unit.test.ts +4 -5
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +28 -0
- package/src/__tests__/standard-descriptors.test-d.ts +22 -0
- package/src/__tests__/standard-pipeline.dawn.test.ts +61 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +124 -0
- package/src/__tests__/standard-profile-oracle.unit.test.ts +62 -0
- package/src/__tests__/surface-configuration.unit.test.ts +1 -2
- package/src/__tests__/texture-usage-surface.unit.test.ts +3 -6
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +40 -206
- package/src/__tests__/urp-pipeline-targets.unit.test.ts +1 -11
- package/src/__tests__/urp-pipeline.unit.test.ts +2 -2
- package/src/__tests__/vertex-color-pipeline.unit.test.ts +10 -2
- package/src/__tests__/vertex-layout-consumer.unit.test.ts +1 -1
- package/src/__tests__/visibility-diagnostics.integration.test.ts +7 -11
- package/src/__tests__/visibility-extract.integration.test.ts +20 -11
- package/src/__tests__/visibility-extract.perf.test.ts +66 -32
- package/src/__tests__/visibility-feature-dedup.integration.test.ts +2 -4
- package/src/__tests__/visibility-hidden-report.unit.test.ts +1 -2
- package/src/__tests__/visibility-instances.integration.test.ts +7 -11
- package/src/__tests__/visibility-multi-world.integration.test.ts +38 -6
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +18 -41
- package/src/__tests__/visibility-public-surface.test-d.ts +10 -9
- package/src/__tests__/visibility-resolver.unit.test.ts +2 -1
- package/src/__tests__/visibility-resource-short-circuit.unit.test.ts +80 -0
- package/src/__tests__/visibility-snapshot-contract.unit.test.ts +2 -1
- package/src/__tests__/vocabulary.unit.test.ts +1 -1
- package/src/assembly/backend-contract.ts +57 -0
- package/src/{renderer/renderer-factory.ts → assembly/factory.ts} +1709 -2693
- package/src/assembly/host-contract.ts +101 -0
- package/src/{renderer → assembly}/material/assembly.ts +1 -3
- package/src/assembly/material/pipeline-projection.ts +63 -0
- package/src/authoring.ts +14 -22
- package/src/components/__tests__/schema-contract.unit.test.ts +10 -0
- package/src/components/glyph-text.ts +3 -2
- package/src/components/index.ts +7 -9
- package/src/components/instances.ts +4 -5
- package/src/components/light-helpers.ts +1 -1
- package/src/components/mesh-filter.ts +11 -10
- package/src/components/post-process-params.ts +3 -3
- package/src/components/scene-instance.ts +12 -12
- package/src/components/skybox-background.ts +103 -0
- package/src/components/{environment.ts → skylight.ts} +2 -111
- package/src/components/sprite-animation.ts +3 -4
- package/src/components/sprite-instances.ts +2 -2
- package/src/components/sprite-playback-mode.ts +7 -1
- package/src/components/sprite-region-override.ts +2 -3
- package/src/components/tile-layer.ts +8 -2
- package/src/construct-renderer.ts +67 -4
- package/src/debug-draw-glue.ts +8 -129
- package/src/device/device-scope.ts +306 -0
- package/src/{gpu-resource-store.ts → device/gpu-residency.ts} +540 -639
- package/src/device/resource-types.ts +20 -0
- package/src/engine-metrics.ts +12 -19
- package/src/errors/recover.ts +45 -3
- package/src/errors/render.ts +357 -460
- package/src/features/fullscreen.ts +46 -0
- package/src/features/host.ts +575 -309
- package/src/features/morph/morph-feature.ts +195 -105
- package/src/features/plan.ts +447 -0
- package/src/features/prepared-gpu-work.ts +72 -15
- package/src/features/prepared-graphics-store.ts +20 -6
- package/src/features/prepared-graphics.ts +12 -71
- package/src/features/render-graph-compute.ts +14 -5
- package/src/features/render-graph-contribution.ts +43 -63
- package/src/features/render-graph-raster.ts +11 -6
- package/src/features/render-graph-resources.ts +25 -23
- package/src/features/targets.ts +8 -21
- package/src/features/types.ts +39 -227
- package/src/features/vocabulary.ts +24 -0
- package/src/fullscreen-post-process-pass.ts +14 -57
- package/src/glyph-text-layout-system.ts +70 -96
- package/src/gpu-driven/batch-topology.ts +24 -202
- package/src/gpu-driven/production-raster.ts +93 -1108
- package/src/gpu-driven/view-gpu.ts +46 -192
- package/src/gpu-resource.ts +4 -4
- package/src/gpu-scene-schema.ts +11 -99
- package/src/gpu-scene.ts +231 -364
- package/src/hdrp-buffers.ts +12 -221
- package/src/ibl/IblPipelineCache.ts +471 -711
- package/src/ibl/skylight-bind-group.ts +4 -4
- package/src/index.ts +58 -217
- package/src/inspection-types.ts +76 -0
- package/src/instance-buffer-cache.ts +1 -29
- package/src/internal.ts +6 -85
- package/src/lifecycle.ts +64 -19
- package/src/materials.ts +7 -29
- package/src/mesh-material-bindings.ts +0 -1
- package/src/pbr-pipeline.ts +434 -53
- package/src/{urp-pipeline.ts → pipeline/standard-forward-lane.ts} +38 -149
- package/src/{hdrp-pipeline.ts → pipeline/standard-pipeline.ts} +221 -191
- package/src/pipeline/standard-profile.ts +65 -0
- package/src/pipeline-builder.ts +60 -64
- package/src/pipeline-spec-types.ts +28 -0
- package/src/pipeline-spec.ts +72 -609
- package/src/plugin.ts +3 -75
- package/src/post-process-errors.ts +31 -33
- package/src/prepare/prepared-graphics-resolver.ts +5 -4
- package/src/record/__tests__/material-slot-plan.unit.test.ts +1 -33
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +41 -0
- package/src/record/__tests__/video-texture-view.unit.test.ts +1 -1
- package/src/record/frame-lighting.ts +74 -195
- package/src/record/frame-snapshot.ts +45 -60
- package/src/record/frame-targets.ts +8 -243
- package/src/record/frame.ts +509 -796
- package/src/record/helpers.ts +6 -11
- package/src/record/main-pass-geometry.ts +106 -147
- package/src/record/main-pass-material.ts +517 -190
- package/src/record/main-pass-sprite-draws.ts +198 -117
- package/src/record/main-pass.ts +162 -143
- package/src/record/mesh-ssbo.ts +517 -38
- package/src/record/render-context.ts +364 -0
- package/src/record/shadow-pass.ts +138 -1838
- package/src/record/skybox-post-pass.ts +12 -14
- package/src/record/typed-frame-graph.ts +215 -254
- package/src/record/view-ubo.ts +6 -37
- package/src/render-contract.ts +571 -0
- package/src/render-data.ts +3 -3
- package/src/render-graph-primitives.ts +76 -851
- package/src/render-pipeline.ts +104 -188
- package/src/render-system-extract.ts +1048 -1729
- package/src/render-system.ts +330 -1957
- package/src/scene/render-scene-types.ts +64 -0
- package/src/scene/render-scene.ts +1032 -0
- package/src/scene-instances/post-spawn-resolve-joints.ts +4 -7
- package/src/ssao-buffers.ts +167 -8
- package/src/systems/skin-palette-allocator.ts +21 -64
- package/src/systems/skin-palette-types.ts +14 -0
- package/src/systems/transparent-sort-config.ts +25 -3
- package/src/tilemap-chunk-extract-system.ts +53 -70
- package/src/typed-render-graph-primitives.ts +35 -24
- package/src/typed-shadow-passes.ts +26 -56
- package/dist/__tests__/analytic-sky-contract.test.d.ts +0 -2
- package/dist/__tests__/analytic-sky-contract.test.d.ts.map +0 -1
- package/dist/__tests__/analytic-sky-falsify.test.d.ts +0 -2
- package/dist/__tests__/analytic-sky-falsify.test.d.ts.map +0 -1
- package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts +0 -2
- package/dist/__tests__/assets-runtime-handoff.integration.test.d.ts.map +0 -1
- package/dist/__tests__/backend-selection.integration.test.d.ts +0 -2
- package/dist/__tests__/backend-selection.integration.test.d.ts.map +0 -1
- package/dist/__tests__/camera-temporal.test-d.d.ts +0 -2
- package/dist/__tests__/camera-temporal.test-d.d.ts.map +0 -1
- package/dist/__tests__/camera-temporal.test.d.ts +0 -2
- package/dist/__tests__/camera-temporal.test.d.ts.map +0 -1
- package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts +0 -2
- package/dist/__tests__/directional-shadow-probe-atlas-layout.unit.test.d.ts.map +0 -1
- package/dist/__tests__/environment-inspection.test.d.ts +0 -2
- package/dist/__tests__/environment-inspection.test.d.ts.map +0 -1
- package/dist/__tests__/fog-ai-user.fixture.d.ts +0 -2
- package/dist/__tests__/fog-ai-user.fixture.d.ts.map +0 -1
- package/dist/__tests__/fog-custom-render-feature.fixture.d.ts +0 -16
- package/dist/__tests__/fog-custom-render-feature.fixture.d.ts.map +0 -1
- package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-custom-render-feature.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-inspection.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-inspection.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-owner-state.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-owner-state.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-pipeline-topology.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-public-surface.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-public-surface.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-ray-contract.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-ray-contract.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-source-absence.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-source-absence.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fog-temporal.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-temporal.integration.test.d.ts.map +0 -1
- package/dist/__tests__/frame-chain.unit.test.d.ts +0 -2
- package/dist/__tests__/frame-chain.unit.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts +0 -2
- package/dist/__tests__/gpu-scene-field-type-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/hdrp-pipeline.unit.test.d.ts +0 -2
- package/dist/__tests__/hdrp-pipeline.unit.test.d.ts.map +0 -1
- package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl/ibl-fallback.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-error-stop-draw.integration.test.d.ts.map +0 -1
- package/dist/__tests__/material-derived-payload.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-payload.integration.test.d.ts.map +0 -1
- package/dist/__tests__/materials.unit.test.d.ts +0 -2
- package/dist/__tests__/materials.unit.test.d.ts.map +0 -1
- package/dist/__tests__/param-schema-projection-census.unit.test.d.ts +0 -2
- package/dist/__tests__/param-schema-projection-census.unit.test.d.ts.map +0 -1
- package/dist/__tests__/persistent-render-scene.unit.test.d.ts +0 -2
- package/dist/__tests__/persistent-render-scene.unit.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts +0 -2
- package/dist/__tests__/pipeline-error-policy-owner.unit.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-pass-trace.test.d.ts +0 -2
- package/dist/__tests__/pipeline-pass-trace.test.d.ts.map +0 -1
- package/dist/__tests__/pipeline-stage-owner.test-d.d.ts +0 -2
- package/dist/__tests__/pipeline-stage-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/plugin.test.d.ts +0 -2
- package/dist/__tests__/plugin.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-backend-matrix.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-resolver-recording.unit.test.d.ts.map +0 -1
- package/dist/__tests__/public-pipeline-id.test-d.d.ts +0 -2
- package/dist/__tests__/public-pipeline-id.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-dispatch-membership.unit.test.d.ts +0 -2
- package/dist/__tests__/render-dispatch-membership.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-construction.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-construction.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-error-code-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-gpu-buffer-usage-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-graph.integration.test.d.ts +0 -2
- package/dist/__tests__/render-feature-graph.integration.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-graphics-staging.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-order.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-order.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-graphics-recording.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-staging.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-staging.unit.test.d.ts.map +0 -1
- package/dist/__tests__/renderer-render-scene-public.test-d.d.ts +0 -2
- package/dist/__tests__/renderer-render-scene-public.test-d.d.ts.map +0 -1
- package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts +0 -2
- package/dist/__tests__/scene-temporal-coverage.unit.test.d.ts.map +0 -1
- package/dist/__tests__/sun-snapshot-consumers.test.d.ts +0 -2
- package/dist/__tests__/sun-snapshot-consumers.test.d.ts.map +0 -1
- package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts +0 -2
- package/dist/__tests__/surface-backend-kind-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/taa-history-store.unit.test.d.ts +0 -2
- package/dist/__tests__/taa-history-store.unit.test.d.ts.map +0 -1
- package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts +0 -2
- package/dist/__tests__/temporal-frame-transaction.integration.test.d.ts.map +0 -1
- package/dist/__tests__/temporal-inspection.unit.test.d.ts +0 -2
- package/dist/__tests__/temporal-inspection.unit.test.d.ts.map +0 -1
- package/dist/assets/__tests__/asset-decoders.unit.test.d.ts +0 -2
- package/dist/assets/__tests__/asset-decoders.unit.test.d.ts.map +0 -1
- package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts +0 -2
- package/dist/assets/__tests__/mipmap-generator.unit.test.d.ts.map +0 -1
- package/dist/assets/__tests__/render-asset-port.unit.test.d.ts +0 -2
- package/dist/assets/__tests__/render-asset-port.unit.test.d.ts.map +0 -1
- package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts +0 -2
- package/dist/assets/__tests__/resolve-render-asset.unit.test.d.ts.map +0 -1
- package/dist/chunk-33UF5SAO.mjs +0 -7397
- package/dist/chunk-33UF5SAO.mjs.map +0 -1
- package/dist/chunk-A5QCJSNL.mjs +0 -29913
- package/dist/chunk-A5QCJSNL.mjs.map +0 -1
- package/dist/chunk-JMSB7AR4.mjs +0 -3
- package/dist/chunk-JMSB7AR4.mjs.map +0 -1
- package/dist/chunk-LXNGO6AO.mjs +0 -569
- package/dist/chunk-LXNGO6AO.mjs.map +0 -1
- package/dist/chunk-QRXPSX4X.mjs +0 -659
- package/dist/chunk-QRXPSX4X.mjs.map +0 -1
- package/dist/chunk-YAKEQPWO.mjs +0 -1439
- package/dist/chunk-YAKEQPWO.mjs.map +0 -1
- package/dist/components/__tests__/atmosphere-red.test.d.ts +0 -2
- package/dist/components/__tests__/atmosphere-red.test.d.ts.map +0 -1
- package/dist/components/__tests__/atmosphere.test.d.ts +0 -2
- package/dist/components/__tests__/atmosphere.test.d.ts.map +0 -1
- package/dist/components/__tests__/fog.test-d.d.ts +0 -2
- package/dist/components/__tests__/fog.test-d.d.ts.map +0 -1
- package/dist/components/__tests__/fog.test.d.ts +0 -2
- package/dist/components/__tests__/fog.test.d.ts.map +0 -1
- package/dist/components/atmosphere.d.ts +0 -18
- package/dist/components/atmosphere.d.ts.map +0 -1
- package/dist/components/environment.d.ts +0 -116
- package/dist/components/environment.d.ts.map +0 -1
- package/dist/deferred-lighting.d.ts +0 -13
- package/dist/deferred-lighting.d.ts.map +0 -1
- package/dist/dynamic-texture-store.d.ts +0 -42
- package/dist/dynamic-texture-store.d.ts.map +0 -1
- package/dist/environment/__tests__/failure-injection.test.d.ts +0 -2
- package/dist/environment/__tests__/failure-injection.test.d.ts.map +0 -1
- package/dist/environment/__tests__/generation-red.test.d.ts +0 -2
- package/dist/environment/__tests__/generation-red.test.d.ts.map +0 -1
- package/dist/environment/__tests__/generation.test.d.ts +0 -2
- package/dist/environment/__tests__/generation.test.d.ts.map +0 -1
- package/dist/environment/__tests__/graph-transaction.test.d.ts +0 -2
- package/dist/environment/__tests__/graph-transaction.test.d.ts.map +0 -1
- package/dist/environment/__tests__/inspection-red.test.d.ts +0 -2
- package/dist/environment/__tests__/inspection-red.test.d.ts.map +0 -1
- package/dist/environment/__tests__/lifecycle-evidence.test.d.ts +0 -2
- package/dist/environment/__tests__/lifecycle-evidence.test.d.ts.map +0 -1
- package/dist/environment/__tests__/pipeline-topology.test.d.ts +0 -2
- package/dist/environment/__tests__/pipeline-topology.test.d.ts.map +0 -1
- package/dist/environment/__tests__/readiness.test.d.ts +0 -2
- package/dist/environment/__tests__/readiness.test.d.ts.map +0 -1
- package/dist/environment/__tests__/recovery.test.d.ts +0 -2
- package/dist/environment/__tests__/recovery.test.d.ts.map +0 -1
- package/dist/environment/__tests__/resize-dispose.test.d.ts +0 -2
- package/dist/environment/__tests__/resize-dispose.test.d.ts.map +0 -1
- package/dist/environment/__tests__/signature-red.test.d.ts +0 -2
- package/dist/environment/__tests__/signature-red.test.d.ts.map +0 -1
- package/dist/environment/__tests__/signature.test.d.ts +0 -2
- package/dist/environment/__tests__/signature.test.d.ts.map +0 -1
- package/dist/environment/descriptors.d.ts +0 -2
- package/dist/environment/descriptors.d.ts.map +0 -1
- package/dist/environment/extracted-environment.d.ts +0 -53
- package/dist/environment/extracted-environment.d.ts.map +0 -1
- package/dist/environment/generation.d.ts +0 -62
- package/dist/environment/generation.d.ts.map +0 -1
- package/dist/environment/gpu-bundle.d.ts +0 -23
- package/dist/environment/gpu-bundle.d.ts.map +0 -1
- package/dist/environment/graph.d.ts +0 -108
- package/dist/environment/graph.d.ts.map +0 -1
- package/dist/environment/inspection.d.ts +0 -48
- package/dist/environment/inspection.d.ts.map +0 -1
- package/dist/environment/lifecycle.d.ts +0 -22
- package/dist/environment/lifecycle.d.ts.map +0 -1
- package/dist/environment/readiness.d.ts +0 -29
- package/dist/environment/readiness.d.ts.map +0 -1
- package/dist/environment/signature.d.ts +0 -70
- package/dist/environment/signature.d.ts.map +0 -1
- package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts +0 -2
- package/dist/extract/__tests__/directional-csm-domain.unit.test.d.ts.map +0 -1
- package/dist/extract/__tests__/environment-source.test.d.ts +0 -2
- package/dist/extract/__tests__/environment-source.test.d.ts.map +0 -1
- package/dist/extract/__tests__/schema-readers.unit.test.d.ts +0 -2
- package/dist/extract/__tests__/schema-readers.unit.test.d.ts.map +0 -1
- package/dist/extract/directional-csm.d.ts +0 -129
- package/dist/extract/directional-csm.d.ts.map +0 -1
- package/dist/extract/index.d.ts +0 -5
- package/dist/extract/index.d.ts.map +0 -1
- package/dist/extract/material-interface.d.ts +0 -56
- package/dist/extract/material-interface.d.ts.map +0 -1
- package/dist/extract/schema-readers.d.ts +0 -20
- package/dist/extract/schema-readers.d.ts.map +0 -1
- package/dist/features/graph-contribution.d.ts +0 -64
- package/dist/features/graph-contribution.d.ts.map +0 -1
- package/dist/features/index.d.ts +0 -21
- package/dist/features/index.d.ts.map +0 -1
- package/dist/features/morph/index.d.ts +0 -3
- package/dist/features/morph/index.d.ts.map +0 -1
- package/dist/features/morph/morph-bounds.d.ts +0 -42
- package/dist/features/morph/morph-bounds.d.ts.map +0 -1
- package/dist/features/scene-temporal.d.ts +0 -45
- package/dist/features/scene-temporal.d.ts.map +0 -1
- package/dist/features/taa-resolve.d.ts +0 -21
- package/dist/features/taa-resolve.d.ts.map +0 -1
- package/dist/frame-chain.d.ts +0 -14
- package/dist/frame-chain.d.ts.map +0 -1
- package/dist/gpu-driven/view-reference.d.ts.map +0 -1
- package/dist/gpu-resource-store.d.ts.map +0 -1
- package/dist/hdrp-cluster-membership.d.ts +0 -2
- package/dist/hdrp-cluster-membership.d.ts.map +0 -1
- package/dist/hdrp-pipeline.d.ts +0 -36
- package/dist/hdrp-pipeline.d.ts.map +0 -1
- package/dist/ibl/cubemap-projection.d.ts +0 -23
- package/dist/ibl/cubemap-projection.d.ts.map +0 -1
- package/dist/ibl/face-uniforms.d.ts +0 -65
- package/dist/ibl/face-uniforms.d.ts.map +0 -1
- package/dist/mesh-update-errors.d.ts +0 -71
- package/dist/mesh-update-errors.d.ts.map +0 -1
- package/dist/mipmap-generator.d.ts +0 -39
- package/dist/mipmap-generator.d.ts.map +0 -1
- package/dist/param-schema-projection-census.d.ts +0 -18
- package/dist/param-schema-projection-census.d.ts.map +0 -1
- package/dist/persistent-render-scene.d.ts +0 -158
- package/dist/persistent-render-scene.d.ts.map +0 -1
- package/dist/pipeline/index.d.ts +0 -11
- package/dist/pipeline/index.d.ts.map +0 -1
- package/dist/pipeline-errors.d.ts +0 -110
- package/dist/pipeline-errors.d.ts.map +0 -1
- package/dist/prepare/__tests__/schema-readers.unit.test.d.ts +0 -2
- package/dist/prepare/__tests__/schema-readers.unit.test.d.ts.map +0 -1
- package/dist/prepare/index.d.ts +0 -4
- package/dist/prepare/index.d.ts.map +0 -1
- package/dist/prepare/schema-readers.d.ts +0 -11
- package/dist/prepare/schema-readers.d.ts.map +0 -1
- package/dist/record/__tests__/environment-record.test.d.ts +0 -2
- package/dist/record/__tests__/environment-record.test.d.ts.map +0 -1
- package/dist/record/__tests__/frame-targets.unit.test.d.ts +0 -2
- package/dist/record/__tests__/frame-targets.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/membership-timing.unit.test.d.ts +0 -2
- package/dist/record/__tests__/membership-timing.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/render-submission.integration.test.d.ts +0 -2
- package/dist/record/__tests__/render-submission.integration.test.d.ts.map +0 -1
- package/dist/record/__tests__/render-submission.perf.test.d.ts +0 -2
- package/dist/record/__tests__/render-submission.perf.test.d.ts.map +0 -1
- package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts +0 -2
- package/dist/record/__tests__/shadow-submit-mode.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts +0 -2
- package/dist/record/__tests__/view-ubo-layout.unit.test.d.ts.map +0 -1
- package/dist/record/frame-observation.d.ts +0 -38
- package/dist/record/frame-observation.d.ts.map +0 -1
- package/dist/record/index.d.ts +0 -15
- package/dist/record/index.d.ts.map +0 -1
- package/dist/record/main-pass-skin.d.ts +0 -46
- package/dist/record/main-pass-skin.d.ts.map +0 -1
- package/dist/record/main-pass-sprite.d.ts +0 -69
- package/dist/record/main-pass-sprite.d.ts.map +0 -1
- package/dist/record/material-payload.d.ts +0 -43
- package/dist/record/material-payload.d.ts.map +0 -1
- package/dist/record/material-slot-plan.d.ts +0 -26
- package/dist/record/material-slot-plan.d.ts.map +0 -1
- package/dist/record/membership-timing.d.ts +0 -232
- package/dist/record/membership-timing.d.ts.map +0 -1
- package/dist/record/shadow-caster-uniform.d.ts +0 -8
- package/dist/record/shadow-caster-uniform.d.ts.map +0 -1
- package/dist/record/shadow-submit-mode.d.ts +0 -66
- package/dist/record/shadow-submit-mode.d.ts.map +0 -1
- package/dist/render-dispatch-membership.d.ts +0 -47
- package/dist/render-dispatch-membership.d.ts.map +0 -1
- package/dist/render-pipeline-context.d.ts +0 -289
- package/dist/render-pipeline-context.d.ts.map +0 -1
- package/dist/render-pipeline-target.d.ts +0 -16
- package/dist/render-pipeline-target.d.ts.map +0 -1
- package/dist/render-scene-projection.d.ts +0 -174
- package/dist/render-scene-projection.d.ts.map +0 -1
- package/dist/render-system-fold.d.ts +0 -266
- package/dist/render-system-fold.d.ts.map +0 -1
- package/dist/renderer/asset-service.d.ts +0 -10
- package/dist/renderer/asset-service.d.ts.map +0 -1
- package/dist/renderer/backend-selection.d.ts.map +0 -1
- package/dist/renderer/environment-classify.d.ts +0 -6
- package/dist/renderer/environment-classify.d.ts.map +0 -1
- package/dist/renderer/environment-error.d.ts +0 -13
- package/dist/renderer/environment-error.d.ts.map +0 -1
- package/dist/renderer/material/pipeline-projection.d.ts +0 -91
- package/dist/renderer/material/pipeline-projection.d.ts.map +0 -1
- package/dist/renderer/renderer-factory.d.ts +0 -283
- package/dist/renderer/renderer-factory.d.ts.map +0 -1
- package/dist/renderer.d.ts +0 -771
- package/dist/renderer.d.ts.map +0 -1
- package/dist/scene-utils/collect-subtree.d.ts +0 -11
- package/dist/scene-utils/collect-subtree.d.ts.map +0 -1
- package/dist/ssao-data.d.ts +0 -34
- package/dist/ssao-data.d.ts.map +0 -1
- package/dist/surface-format.d.ts +0 -10
- package/dist/surface-format.d.ts.map +0 -1
- package/dist/taa-history-store.d.ts +0 -88
- package/dist/taa-history-store.d.ts.map +0 -1
- package/dist/temporal-coverage.d.ts +0 -35
- package/dist/temporal-coverage.d.ts.map +0 -1
- package/dist/temporal-frame-transaction.d.ts +0 -92
- package/dist/temporal-frame-transaction.d.ts.map +0 -1
- package/dist/temporal-inspection.d.ts +0 -57
- package/dist/temporal-inspection.d.ts.map +0 -1
- package/dist/temporal-view.d.ts +0 -43
- package/dist/temporal-view.d.ts.map +0 -1
- package/dist/tilemap/resolve-tileset-runtime.d.ts +0 -23
- package/dist/tilemap/resolve-tileset-runtime.d.ts.map +0 -1
- package/dist/urp-pipeline.d.ts +0 -4
- package/dist/urp-pipeline.d.ts.map +0 -1
- package/src/__tests__/analytic-sky-contract.test.ts +0 -27
- package/src/__tests__/analytic-sky-falsify-carriers.mjs +0 -43
- package/src/__tests__/analytic-sky-falsify.test.ts +0 -94
- package/src/__tests__/assets-runtime-handoff.integration.test.ts +0 -36
- package/src/__tests__/backend-selection.integration.test.ts +0 -11
- package/src/__tests__/camera-temporal.test-d.ts +0 -8
- package/src/__tests__/camera-temporal.test.ts +0 -188
- package/src/__tests__/directional-shadow-probe-atlas-layout.unit.test.ts +0 -18
- package/src/__tests__/environment-inspection.test.ts +0 -45
- package/src/__tests__/fog-ai-user.fixture.ts +0 -350
- package/src/__tests__/fog-custom-render-feature.fixture.ts +0 -33
- package/src/__tests__/fog-custom-render-feature.integration.test.ts +0 -76
- package/src/__tests__/fog-inspection.integration.test.ts +0 -127
- package/src/__tests__/fog-owner-state.integration.test.ts +0 -112
- package/src/__tests__/fog-pipeline-topology.integration.test.ts +0 -93
- package/src/__tests__/fog-public-surface.integration.test.ts +0 -60
- package/src/__tests__/fog-ray-contract.integration.test.ts +0 -73
- package/src/__tests__/fog-source-absence.integration.test.ts +0 -51
- package/src/__tests__/fog-temporal.integration.test.ts +0 -115
- package/src/__tests__/frame-chain.unit.test.ts +0 -26
- package/src/__tests__/gpu-scene-field-type-owner.test-d.ts +0 -27
- package/src/__tests__/hdrp-pipeline.unit.test.ts +0 -100
- package/src/__tests__/ibl/ibl-fallback.unit.test.ts +0 -23
- package/src/__tests__/material-derived-error-stop-draw.integration.test.ts +0 -69
- package/src/__tests__/material-derived-payload.integration.test.ts +0 -164
- package/src/__tests__/materials.unit.test.ts +0 -25
- package/src/__tests__/param-schema-projection-census.unit.test.ts +0 -40
- package/src/__tests__/persistent-render-scene.unit.test.ts +0 -1311
- package/src/__tests__/pipeline-error-policy-owner.unit.test.ts +0 -167
- package/src/__tests__/pipeline-pass-trace.test.ts +0 -92
- package/src/__tests__/pipeline-stage-owner.test-d.ts +0 -13
- package/src/__tests__/plugin.test.ts +0 -25
- package/src/__tests__/prepared-graphics-backend-matrix.unit.test.ts +0 -129
- package/src/__tests__/prepared-graphics-resolver-recording.unit.test.ts +0 -180
- package/src/__tests__/public-pipeline-id.test-d.ts +0 -6
- package/src/__tests__/render-dispatch-membership.unit.test.ts +0 -117
- package/src/__tests__/render-feature-construction.unit.test.ts +0 -124
- package/src/__tests__/render-feature-error-code-owner.test-d.ts +0 -27
- package/src/__tests__/render-feature-gpu-buffer-usage-owner.test-d.ts +0 -46
- package/src/__tests__/render-feature-graph.integration.test.ts +0 -205
- package/src/__tests__/render-feature-graphics-staging.unit.test.ts +0 -200
- package/src/__tests__/render-feature-order.unit.test.ts +0 -66
- package/src/__tests__/render-feature-prepared-graphics-recording.unit.test.ts +0 -107
- package/src/__tests__/render-feature-staging.unit.test.ts +0 -94
- package/src/__tests__/renderer-render-scene-public.test-d.ts +0 -27
- package/src/__tests__/scene-temporal-coverage.unit.test.ts +0 -259
- package/src/__tests__/sun-snapshot-consumers.test.ts +0 -35
- package/src/__tests__/surface-backend-kind-owner.test-d.ts +0 -42
- package/src/__tests__/taa-history-store.unit.test.ts +0 -196
- package/src/__tests__/temporal-frame-transaction.integration.test.ts +0 -537
- package/src/__tests__/temporal-inspection.unit.test.ts +0 -138
- package/src/assets/__tests__/asset-decoders.unit.test.ts +0 -92
- package/src/assets/__tests__/mipmap-generator.unit.test.ts +0 -52
- package/src/assets/__tests__/render-asset-port.unit.test.ts +0 -54
- package/src/assets/__tests__/resolve-render-asset.unit.test.ts +0 -47
- package/src/components/__tests__/atmosphere-red.test.ts +0 -51
- package/src/components/__tests__/atmosphere.test.ts +0 -79
- package/src/components/__tests__/fog.test-d.ts +0 -49
- package/src/components/__tests__/fog.test.ts +0 -60
- package/src/components/atmosphere.ts +0 -64
- package/src/deferred-lighting.ts +0 -92
- package/src/dynamic-texture-store.ts +0 -111
- package/src/environment/README.md +0 -76
- package/src/environment/__tests__/failure-injection.test.ts +0 -50
- package/src/environment/__tests__/generation-red.test.ts +0 -64
- package/src/environment/__tests__/generation.test.ts +0 -37
- package/src/environment/__tests__/graph-transaction.test.ts +0 -85
- package/src/environment/__tests__/inspection-red.test.ts +0 -53
- package/src/environment/__tests__/lifecycle-evidence.test.ts +0 -40
- package/src/environment/__tests__/pipeline-topology.test.ts +0 -33
- package/src/environment/__tests__/readiness.test.ts +0 -42
- package/src/environment/__tests__/recovery.test.ts +0 -62
- package/src/environment/__tests__/resize-dispose.test.ts +0 -30
- package/src/environment/__tests__/signature-red.test.ts +0 -45
- package/src/environment/__tests__/signature.test.ts +0 -60
- package/src/environment/descriptors.ts +0 -7
- package/src/environment/extracted-environment.ts +0 -239
- package/src/environment/generation.ts +0 -158
- package/src/environment/gpu-bundle.ts +0 -144
- package/src/environment/graph.ts +0 -588
- package/src/environment/inspection.ts +0 -107
- package/src/environment/lifecycle.ts +0 -124
- package/src/environment/readiness.ts +0 -76
- package/src/environment/signature.ts +0 -185
- package/src/extract/__tests__/directional-csm-domain.unit.test.ts +0 -391
- package/src/extract/__tests__/environment-source.test.ts +0 -105
- package/src/extract/__tests__/schema-readers.unit.test.ts +0 -16
- package/src/extract/directional-csm.ts +0 -959
- package/src/extract/index.ts +0 -10
- package/src/extract/material-interface.ts +0 -604
- package/src/extract/schema-readers.ts +0 -27
- package/src/features/graph-contribution.ts +0 -577
- package/src/features/index.ts +0 -100
- package/src/features/morph/index.ts +0 -24
- package/src/features/morph/morph-bounds.ts +0 -123
- package/src/features/scene-temporal.ts +0 -274
- package/src/features/taa-resolve.ts +0 -244
- package/src/frame-chain.ts +0 -21
- package/src/hdrp-cluster-membership.ts +0 -70
- package/src/ibl/cubemap-projection.ts +0 -60
- package/src/ibl/face-uniforms.ts +0 -150
- package/src/mesh-update-errors.ts +0 -113
- package/src/mipmap-generator.ts +0 -244
- package/src/param-schema-projection-census.ts +0 -73
- package/src/persistent-render-scene.ts +0 -1752
- package/src/pipeline/index.ts +0 -40
- package/src/pipeline-errors.ts +0 -172
- package/src/prepare/__tests__/schema-readers.unit.test.ts +0 -16
- package/src/prepare/index.ts +0 -8
- package/src/prepare/schema-readers.ts +0 -20
- package/src/record/__tests__/environment-record.test.ts +0 -35
- package/src/record/__tests__/frame-targets.unit.test.ts +0 -54
- package/src/record/__tests__/membership-timing.unit.test.ts +0 -524
- package/src/record/__tests__/render-submission.integration.test.ts +0 -76
- package/src/record/__tests__/render-submission.perf.test.ts +0 -141
- package/src/record/__tests__/shadow-submit-mode.unit.test.ts +0 -88
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +0 -101
- package/src/record/frame-observation.ts +0 -122
- package/src/record/index.ts +0 -15
- package/src/record/main-pass-skin.ts +0 -51
- package/src/record/main-pass-sprite.ts +0 -166
- package/src/record/material-payload.ts +0 -292
- package/src/record/material-slot-plan.ts +0 -68
- package/src/record/membership-timing.ts +0 -1012
- package/src/record/shadow-caster-uniform.ts +0 -37
- package/src/record/shadow-submit-mode.ts +0 -277
- package/src/render-dispatch-membership.ts +0 -155
- package/src/render-pipeline-context.ts +0 -332
- package/src/render-pipeline-target.ts +0 -96
- package/src/render-scene-projection.ts +0 -1738
- package/src/render-system-fold.ts +0 -531
- package/src/renderer/asset-service.ts +0 -32
- package/src/renderer/backend-selection.ts +0 -121
- package/src/renderer/environment-classify.ts +0 -30
- package/src/renderer/environment-error.ts +0 -19
- package/src/renderer/material/pipeline-projection.ts +0 -140
- package/src/renderer.ts +0 -940
- package/src/scene-utils/collect-subtree.ts +0 -40
- package/src/ssao-data.ts +0 -106
- package/src/surface-format.ts +0 -37
- package/src/taa-history-store.ts +0 -429
- package/src/temporal-coverage.ts +0 -106
- package/src/temporal-frame-transaction.ts +0 -301
- package/src/temporal-inspection.ts +0 -194
- package/src/temporal-view.ts +0 -174
- package/src/tilemap/resolve-tileset-runtime.ts +0 -72
package/src/record/frame.ts
CHANGED
|
@@ -1,74 +1,47 @@
|
|
|
1
1
|
// @forgeax/engine-runtime - RenderSystem record stage: frame.
|
|
2
2
|
// Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
|
|
3
3
|
|
|
4
|
+
import { HANDLE_NINESLICE_QUAD, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
4
5
|
import type { World } from '@forgeax/engine-ecs';
|
|
5
|
-
import {
|
|
6
|
-
import { type RhiCommandEncoder, RhiError, type TextureView } from '@forgeax/engine-rhi';
|
|
7
|
-
import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
|
|
8
|
-
import { toShared } from '@forgeax/engine-types';
|
|
9
|
-
import { resolveRenderAsset } from '../assets/resolve-render-asset';
|
|
10
|
-
import type { EngineMetrics } from '../engine-metrics';
|
|
11
|
-
import type { ExtractedEnvironment } from '../environment/extracted-environment';
|
|
12
|
-
import {
|
|
13
|
-
beginEnvironmentGeneration,
|
|
14
|
-
environmentConsumerSnapshot,
|
|
15
|
-
publishEnvironmentGeneration,
|
|
16
|
-
} from '../environment/generation';
|
|
17
|
-
import { createEnvironmentGpuBundle } from '../environment/gpu-bundle';
|
|
18
|
-
import type { EnvironmentTransactionAttempt } from '../environment/graph';
|
|
6
|
+
import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
19
7
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from '../environment/lifecycle';
|
|
8
|
+
type CurrentFrameObservationLease,
|
|
9
|
+
createCurrentFrameObservationLease,
|
|
10
|
+
} from '@forgeax/engine-render-graph';
|
|
24
11
|
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
12
|
+
err,
|
|
13
|
+
ok,
|
|
14
|
+
type Result,
|
|
15
|
+
type RhiCommandEncoder,
|
|
16
|
+
RhiError,
|
|
17
|
+
type TextureFormat,
|
|
18
|
+
type TextureView,
|
|
19
|
+
} from '@forgeax/engine-rhi';
|
|
20
|
+
import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
|
|
21
|
+
import { handleSlot, toShared } from '@forgeax/engine-types';
|
|
22
|
+
import type { MeshGpuHandles } from '../device/gpu-residency';
|
|
23
|
+
import { ObservationUnavailableError, type ObservationUnavailableReason } from '../errors/render';
|
|
30
24
|
import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
|
|
31
|
-
import
|
|
25
|
+
import { GpuBuffer } from '../gpu-resource';
|
|
26
|
+
import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_VERTEX } from '../gpu-usage';
|
|
32
27
|
import { disposeTransientInstanceBuffers } from '../instance-buffer-cache';
|
|
33
|
-
import type {
|
|
34
|
-
import type { _InternalRenderPipelineContext } from '../render-pipeline-context';
|
|
35
|
-
import type { RenderSceneProjection, RenderSceneSubmissionDelta } from '../render-scene-projection';
|
|
36
|
-
import type { MeshGpuHandles, RenderSystemInternals } from '../render-system';
|
|
28
|
+
import type { CameraSnapshot, RenderRecordPhase } from '../render-contract';
|
|
37
29
|
import type {
|
|
38
|
-
CameraSnapshot,
|
|
39
30
|
DispatchEntry,
|
|
40
31
|
ExtractedLights,
|
|
41
|
-
FogSnapshot,
|
|
42
32
|
MaterialSnapshot,
|
|
43
33
|
RenderableSnapshot,
|
|
34
|
+
SkyboxSnapshot,
|
|
35
|
+
SkylightSnapshot,
|
|
44
36
|
} from '../render-system-extract';
|
|
45
|
-
|
|
46
|
-
// fold operator). Pure linear-scan helper that groups transparent-dispatch
|
|
47
|
-
// entries with equal (Layer.value, sortKey, materialHandle) into FoldBucket
|
|
48
|
-
// descriptors. The drawIndexed swap (1 instanced draw per bucket vs N
|
|
49
|
-
// per-entity draws) hooks into the sprite-pass dispatch loop below
|
|
50
|
-
// (w4-record-swap), using {@link buildFoldDispatchPlan} to translate
|
|
51
|
-
// renderableIndex-keyed buckets into validatedOrdered-index-keyed
|
|
52
|
-
// head/skip maps.
|
|
53
|
-
import {
|
|
54
|
-
buildFoldDispatchPlan,
|
|
55
|
-
evaluateFoldBucketUniformCap,
|
|
56
|
-
type FoldBucket,
|
|
57
|
-
type FoldDispatchPlan,
|
|
58
|
-
foldDispatchBuckets,
|
|
59
|
-
incrementFoldedDrawsMetric,
|
|
60
|
-
} from '../render-system-fold';
|
|
61
|
-
import type { RenderRecordPhase } from '../renderer';
|
|
37
|
+
import type { PersistentGpuDrivenState } from '../scene/render-scene';
|
|
62
38
|
import {
|
|
63
39
|
getTransparentSortConfig,
|
|
64
40
|
TRANSPARENT_SORT_MODE_LAYER_Y,
|
|
65
41
|
TRANSPARENT_SORT_MODE_LAYER_Z,
|
|
66
42
|
} from '../systems/transparent-sort-config';
|
|
67
|
-
import type { TemporalCoverageSummary } from '../temporal-coverage';
|
|
68
|
-
import type { TemporalFrameAttempt, TemporalFrameTransaction } from '../temporal-frame-transaction';
|
|
69
43
|
import {
|
|
70
44
|
buildPerFrameBindGroups,
|
|
71
|
-
createEnvironmentConsumerSnapshot,
|
|
72
45
|
prepareFrameLighting,
|
|
73
46
|
resolveSkyboxActive,
|
|
74
47
|
warnZeroLightStandard,
|
|
@@ -78,16 +51,139 @@ import {
|
|
|
78
51
|
} from './frame-lighting';
|
|
79
52
|
import {
|
|
80
53
|
type BindGroupCounts,
|
|
81
|
-
clampPcfKernelSize,
|
|
82
54
|
type DirectionalShadowCache,
|
|
83
55
|
type DirectionalShadowWorldState,
|
|
84
56
|
type DispatchCounts,
|
|
57
|
+
type FrameObservationSource,
|
|
85
58
|
makeZeroCameraFallbackSnapshot,
|
|
86
|
-
type RasterItemKey,
|
|
87
59
|
type RenderFrameState,
|
|
88
60
|
type ValidatedRenderable,
|
|
89
61
|
worldEntityKey,
|
|
90
62
|
} from './frame-snapshot';
|
|
63
|
+
// feat-20260622-chunk-gpu-instancing-sprite-tilemap M1 / w4 (D-1 record-stage
|
|
64
|
+
// fold operator). Pure linear-scan helper that groups transparent-dispatch
|
|
65
|
+
// entries with equal (Layer.value, sortKey, materialHandle) into FoldBucket
|
|
66
|
+
// descriptors. The drawIndexed swap (1 instanced draw per bucket vs N
|
|
67
|
+
// per-entity draws) hooks into the sprite-pass dispatch loop below
|
|
68
|
+
// (w4-record-swap), using {@link buildFoldDispatchPlan} to translate
|
|
69
|
+
// renderableIndex-keyed buckets into validatedOrdered-index-keyed
|
|
70
|
+
// head/skip maps.
|
|
71
|
+
import {
|
|
72
|
+
buildFoldDispatchPlan,
|
|
73
|
+
evaluateFoldBucketUniformCap,
|
|
74
|
+
type FoldBucket,
|
|
75
|
+
type FoldDispatchPlan,
|
|
76
|
+
foldDispatchBuckets,
|
|
77
|
+
incrementFoldedDrawsMetric,
|
|
78
|
+
} from './mesh-ssbo';
|
|
79
|
+
import type {
|
|
80
|
+
_InternalRenderPipelineContext,
|
|
81
|
+
PipelineState,
|
|
82
|
+
RenderSystemInternals,
|
|
83
|
+
} from './render-context';
|
|
84
|
+
|
|
85
|
+
export type { FrameObservationSource } from './frame-snapshot';
|
|
86
|
+
|
|
87
|
+
export type FrameObservationReadback = (
|
|
88
|
+
lease: CurrentFrameObservationLease,
|
|
89
|
+
) => Promise<Result<Uint8Array, Error>>;
|
|
90
|
+
|
|
91
|
+
export interface FrameObservationOptions {
|
|
92
|
+
readonly semantic: 'linear-hdr';
|
|
93
|
+
readonly readback: FrameObservationReadback;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface FrameObservationMetadata {
|
|
97
|
+
readonly format: TextureFormat;
|
|
98
|
+
readonly size: { readonly width: number; readonly height: number };
|
|
99
|
+
readonly usage: number;
|
|
100
|
+
readonly sample: number;
|
|
101
|
+
readonly frameId: number;
|
|
102
|
+
readonly lifetime: { readonly frameId: number; readonly state: 'active' | 'retired' };
|
|
103
|
+
readonly pipelineId: 'forgeax::standard';
|
|
104
|
+
readonly backendId: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface FrameObservation {
|
|
108
|
+
readonly bytes: Uint8Array;
|
|
109
|
+
readonly metadata: FrameObservationMetadata;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function unavailable(
|
|
113
|
+
reason: ObservationUnavailableReason,
|
|
114
|
+
hint: string,
|
|
115
|
+
): Result<never, ObservationUnavailableError> {
|
|
116
|
+
return err(new ObservationUnavailableError(reason, hint));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function mapLeaseFailure(code: string, hint: string): Result<never, ObservationUnavailableError> {
|
|
120
|
+
let reason: ObservationUnavailableReason = 'resource';
|
|
121
|
+
if (code === 'observation-stale') reason = 'stale';
|
|
122
|
+
else if (code === 'observation-invalid-format') reason = 'format';
|
|
123
|
+
else if (code === 'observation-missing-copy-src') reason = 'copy-src';
|
|
124
|
+
return unavailable(reason, hint);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function observeCurrentFrame(
|
|
128
|
+
options: FrameObservationOptions,
|
|
129
|
+
source: FrameObservationSource | undefined,
|
|
130
|
+
currentFrameId: number,
|
|
131
|
+
): Promise<Result<FrameObservation, ObservationUnavailableError>> {
|
|
132
|
+
if (options.semantic !== 'linear-hdr') {
|
|
133
|
+
return unavailable('identity', 'request the producer-owned linear-hdr semantic');
|
|
134
|
+
}
|
|
135
|
+
if (source === undefined) {
|
|
136
|
+
return unavailable(
|
|
137
|
+
'no-frame',
|
|
138
|
+
'draw a frame through the Standard producer before requesting observation',
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
if (source.pipelineId !== 'forgeax::standard' || source.backendId.length === 0) {
|
|
142
|
+
return unavailable(
|
|
143
|
+
'identity',
|
|
144
|
+
'observe a producer with explicit pipeline and backend identity',
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const leaseResult = createCurrentFrameObservationLease(
|
|
149
|
+
{ ...source.descriptor, texture: source.texture, frameId: source.frameId },
|
|
150
|
+
currentFrameId,
|
|
151
|
+
);
|
|
152
|
+
if (!leaseResult.ok) {
|
|
153
|
+
return mapLeaseFailure(leaseResult.error.code, leaseResult.error.hint);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let bytesResult: Result<Uint8Array, Error>;
|
|
157
|
+
try {
|
|
158
|
+
bytesResult = await options.readback(leaseResult.value);
|
|
159
|
+
} catch (cause) {
|
|
160
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
161
|
+
return unavailable('readback-failed', `retry the current-frame readback after: ${message}`);
|
|
162
|
+
}
|
|
163
|
+
if (!bytesResult.ok) {
|
|
164
|
+
return unavailable('readback-failed', bytesResult.error.message);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const activeResult = leaseResult.value.beginReadback();
|
|
168
|
+
if (!activeResult.ok) {
|
|
169
|
+
return mapLeaseFailure(activeResult.error.code, activeResult.error.hint);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return ok({
|
|
173
|
+
bytes: bytesResult.value,
|
|
174
|
+
metadata: {
|
|
175
|
+
format: leaseResult.value.descriptor.format,
|
|
176
|
+
size: leaseResult.value.descriptor.size,
|
|
177
|
+
usage: leaseResult.value.descriptor.usage,
|
|
178
|
+
sample: source.descriptor.sample,
|
|
179
|
+
frameId: source.frameId,
|
|
180
|
+
lifetime: leaseResult.value.lifetime,
|
|
181
|
+
pipelineId: source.pipelineId,
|
|
182
|
+
backendId: source.backendId,
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
91
187
|
import {
|
|
92
188
|
acquireSwapChainTarget,
|
|
93
189
|
graphExecutionPhase,
|
|
@@ -102,62 +198,89 @@ import {
|
|
|
102
198
|
warnMultiSkybox,
|
|
103
199
|
warnMultiSkylight,
|
|
104
200
|
} from './helpers';
|
|
105
|
-
import { computeSplitLdrSprite } from './main-pass-sprite';
|
|
106
|
-
import {
|
|
107
|
-
buildMaterialSlotPlan,
|
|
108
|
-
findRenderablePrefixForSlotCapacity,
|
|
109
|
-
materialSlotCountForPrefix,
|
|
110
|
-
} from './material-slot-plan';
|
|
201
|
+
import { computeSplitLdrSprite } from './main-pass-sprite-draws';
|
|
111
202
|
import { cleanPerEntityCache, ensureMeshSsboCapacity, uploadMeshSsboBatch } from './mesh-ssbo';
|
|
112
|
-
import { writeShadowCasterUniforms } from './shadow-
|
|
113
|
-
import {
|
|
203
|
+
import { writeShadowCasterUniforms } from './shadow-pass';
|
|
204
|
+
import {
|
|
205
|
+
ensureCompiledFrameGraph,
|
|
206
|
+
executeCompiledFrameGraph,
|
|
207
|
+
type RenderFeatureGraphCandidate,
|
|
208
|
+
} from './typed-frame-graph';
|
|
114
209
|
import { writeViewUbo } from './view-ubo';
|
|
115
210
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
211
|
+
/**
|
|
212
|
+
* A frame-local material table. Repeated snapshot objects share one UBO slot;
|
|
213
|
+
* each renderable keeps only the slot index used by each authored material.
|
|
214
|
+
*/
|
|
215
|
+
export interface MaterialSlotPlan<T extends object> {
|
|
216
|
+
readonly slotIndices: readonly (readonly number[])[];
|
|
217
|
+
readonly slots: readonly T[];
|
|
218
|
+
/** Index of the first renderable that referenced each slot. */
|
|
219
|
+
readonly slotOwners: readonly number[];
|
|
220
|
+
}
|
|
121
221
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Intern identical material snapshots into one frame-local slot table.
|
|
224
|
+
* Snapshot identity is the extract layer's invalidation token, so this keeps
|
|
225
|
+
* the same correctness boundary while avoiding duplicate payload assembly and
|
|
226
|
+
* upload for repeated scene instances.
|
|
227
|
+
*/
|
|
228
|
+
export function buildMaterialSlotPlan<T extends object>(
|
|
229
|
+
materialGroups: readonly (readonly T[])[],
|
|
230
|
+
): MaterialSlotPlan<T> {
|
|
231
|
+
const slotByMaterial = new Map<T, number>();
|
|
232
|
+
const slots: T[] = [];
|
|
233
|
+
const slotOwners: number[] = [];
|
|
234
|
+
const slotIndices = materialGroups.map((materials, ownerIndex) =>
|
|
235
|
+
materials.map((material) => {
|
|
236
|
+
const cached = slotByMaterial.get(material);
|
|
237
|
+
if (cached !== undefined) return cached;
|
|
238
|
+
const slot = slots.length;
|
|
239
|
+
slots.push(material);
|
|
240
|
+
slotOwners.push(ownerIndex);
|
|
241
|
+
slotByMaterial.set(material, slot);
|
|
242
|
+
return slot;
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
245
|
+
return { slotIndices, slots, slotOwners };
|
|
130
246
|
}
|
|
131
247
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Return the largest complete renderable prefix supported by a shared
|
|
250
|
+
* mesh/material capacity. Mesh rows consume one slot per renderable; material
|
|
251
|
+
* rows use the deduplicated slot indices assigned in first-seen order.
|
|
252
|
+
*/
|
|
253
|
+
export function findRenderablePrefixForSlotCapacity(
|
|
254
|
+
materialSlotIndices: readonly (readonly number[])[],
|
|
255
|
+
slotCapacity: number,
|
|
256
|
+
): number {
|
|
257
|
+
const capacity = Math.max(0, Math.floor(slotCapacity));
|
|
258
|
+
const maxRenderableCount = Math.min(materialSlotIndices.length, capacity);
|
|
259
|
+
let renderableCount = 0;
|
|
260
|
+
while (renderableCount < maxRenderableCount) {
|
|
261
|
+
const slots = materialSlotIndices[renderableCount] ?? [];
|
|
262
|
+
if (slots.some((slot) => slot >= capacity)) break;
|
|
263
|
+
renderableCount += 1;
|
|
139
264
|
}
|
|
140
|
-
|
|
141
|
-
return sorted;
|
|
265
|
+
return renderableCount;
|
|
142
266
|
}
|
|
143
267
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
):
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return new Set(
|
|
155
|
-
view.compactVisible.flatMap((candidate) =>
|
|
156
|
-
candidate.candidateIndex === undefined ? [] : [candidate.candidateIndex],
|
|
157
|
-
),
|
|
158
|
-
);
|
|
268
|
+
/** Count material slots reachable from a prefix of a first-seen slot plan. */
|
|
269
|
+
export function materialSlotCountForPrefix(
|
|
270
|
+
materialSlotIndices: readonly (readonly number[])[],
|
|
271
|
+
renderableCount: number,
|
|
272
|
+
): number {
|
|
273
|
+
let maxSlot = -1;
|
|
274
|
+
for (let index = 0; index < renderableCount; index += 1) {
|
|
275
|
+
for (const slot of materialSlotIndices[index] ?? []) maxSlot = Math.max(maxSlot, slot);
|
|
276
|
+
}
|
|
277
|
+
return maxSlot + 1;
|
|
159
278
|
}
|
|
160
279
|
|
|
280
|
+
export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T) => T;
|
|
281
|
+
|
|
282
|
+
const EMPTY_ENTITY_KEYS: ReadonlySet<number> = new Set<number>();
|
|
283
|
+
|
|
161
284
|
function runRecordProfilePhase<T>(
|
|
162
285
|
runner: RecordProfileRunner | undefined,
|
|
163
286
|
phase: RenderRecordPhase,
|
|
@@ -166,119 +289,6 @@ function runRecordProfilePhase<T>(
|
|
|
166
289
|
return runner === undefined ? action() : runner(phase, action);
|
|
167
290
|
}
|
|
168
291
|
|
|
169
|
-
const ANALYTIC_SKY_QUALITY = Object.freeze({ skySize: 128, irradianceSize: 32, prefilterMips: 5 });
|
|
170
|
-
const ANALYTIC_SKY_ALGORITHM = 'preetham-perez-1999-r1';
|
|
171
|
-
|
|
172
|
-
function beginProductionEnvironmentGeneration(
|
|
173
|
-
internals: RenderSystemInternals,
|
|
174
|
-
frameState: RenderFrameState,
|
|
175
|
-
environment: ExtractedEnvironment,
|
|
176
|
-
): EnvironmentTransactionAttempt | undefined {
|
|
177
|
-
if (environment.kind !== 'atmosphere') {
|
|
178
|
-
const state = frameState.environmentGeneration;
|
|
179
|
-
const oldHandles = [state.active?.handle, state.candidate?.handle].filter(
|
|
180
|
-
(handle): handle is NonNullable<typeof handle> => handle !== undefined,
|
|
181
|
-
);
|
|
182
|
-
if (oldHandles.length > 0) {
|
|
183
|
-
resizeEnvironmentGeneration(state);
|
|
184
|
-
void internals.device.queue.onSubmittedWorkDone().then(() => {
|
|
185
|
-
for (const handle of oldHandles) {
|
|
186
|
-
if (typeof handle === 'object' && handle !== null && 'destroy' in handle) {
|
|
187
|
-
(handle as { readonly destroy: (device: typeof internals.device) => unknown }).destroy(
|
|
188
|
-
internals.device,
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
return undefined;
|
|
195
|
-
}
|
|
196
|
-
const quality = ANALYTIC_SKY_QUALITY;
|
|
197
|
-
const signature = environmentSignature({
|
|
198
|
-
atmosphere: {
|
|
199
|
-
turbidity: environment.atmosphere.turbidity,
|
|
200
|
-
rayleigh: environment.atmosphere.rayleigh,
|
|
201
|
-
mieCoefficient: environment.atmosphere.mieCoefficient,
|
|
202
|
-
mieDirectionalG: environment.atmosphere.mieDirectionalG,
|
|
203
|
-
sunAngularRadius: environment.atmosphere.sunAngularRadius,
|
|
204
|
-
},
|
|
205
|
-
sun: {
|
|
206
|
-
direction: environment.atmosphere.sun.direction,
|
|
207
|
-
color: environment.atmosphere.sun.color,
|
|
208
|
-
illuminance: environment.atmosphere.sun.illuminance,
|
|
209
|
-
},
|
|
210
|
-
quality,
|
|
211
|
-
algorithmRevision: ANALYTIC_SKY_ALGORITHM,
|
|
212
|
-
worldId: environment.worldId,
|
|
213
|
-
entity: environment.atmosphere.entity,
|
|
214
|
-
deviceGeneration:
|
|
215
|
-
typeof frameState.environmentGeneration.deviceGeneration === 'number'
|
|
216
|
-
? frameState.environmentGeneration.deviceGeneration
|
|
217
|
-
: 0,
|
|
218
|
-
outputFormat: 'rgba16float',
|
|
219
|
-
});
|
|
220
|
-
const state = frameState.environmentGeneration;
|
|
221
|
-
if (state.active?.signature === signature) {
|
|
222
|
-
return undefined;
|
|
223
|
-
}
|
|
224
|
-
if (state.candidate !== undefined) return undefined;
|
|
225
|
-
const descriptors = deriveEnvironmentBundleDescriptors(quality, 'rgba16float');
|
|
226
|
-
const attempt = beginEnvironmentGeneration(state, {
|
|
227
|
-
signature,
|
|
228
|
-
bytes: environmentBundleBytes(descriptors),
|
|
229
|
-
});
|
|
230
|
-
const bundle = createEnvironmentGpuBundle(internals.device, descriptors);
|
|
231
|
-
if (!bundle.ok) {
|
|
232
|
-
attempt.fail('readiness');
|
|
233
|
-
internals.errorRegistry.fire(bundle.error);
|
|
234
|
-
return undefined;
|
|
235
|
-
}
|
|
236
|
-
const candidateSnapshot = createEnvironmentConsumerSnapshot({
|
|
237
|
-
generation: attempt.generation,
|
|
238
|
-
signature: String(attempt.desired.signature),
|
|
239
|
-
direction: [
|
|
240
|
-
environment.atmosphere.sun.direction[0] ?? 0,
|
|
241
|
-
environment.atmosphere.sun.direction[1] ?? 0,
|
|
242
|
-
environment.atmosphere.sun.direction[2] ?? 0,
|
|
243
|
-
],
|
|
244
|
-
color: [
|
|
245
|
-
environment.atmosphere.sun.color[0] ?? 0,
|
|
246
|
-
environment.atmosphere.sun.color[1] ?? 0,
|
|
247
|
-
environment.atmosphere.sun.color[2] ?? 0,
|
|
248
|
-
],
|
|
249
|
-
illuminance: environment.atmosphere.sun.illuminance,
|
|
250
|
-
atmosphere: {
|
|
251
|
-
turbidity: environment.atmosphere.turbidity,
|
|
252
|
-
rayleigh: environment.atmosphere.rayleigh,
|
|
253
|
-
mieCoefficient: environment.atmosphere.mieCoefficient,
|
|
254
|
-
mieDirectionalG: environment.atmosphere.mieDirectionalG,
|
|
255
|
-
sunAngularRadius: environment.atmosphere.sunAngularRadius,
|
|
256
|
-
},
|
|
257
|
-
source: { worldId: environment.worldId, entity: environment.atmosphere.entity },
|
|
258
|
-
});
|
|
259
|
-
const candidate = attempt.complete(bundle.value, candidateSnapshot);
|
|
260
|
-
const queueCompletion = `analytic-sky:${candidate.generation}:${frameState.frameNumber}`;
|
|
261
|
-
let settled = false;
|
|
262
|
-
const abandonCandidate = (stage: 'execute' | 'finish' | 'submit'): void => {
|
|
263
|
-
if (settled) return;
|
|
264
|
-
settled = true;
|
|
265
|
-
attempt.fail(stage);
|
|
266
|
-
bundle.value.destroy(internals.device);
|
|
267
|
-
};
|
|
268
|
-
return {
|
|
269
|
-
abort: abandonCandidate,
|
|
270
|
-
publish: () => {
|
|
271
|
-
if (settled) return;
|
|
272
|
-
settled = true;
|
|
273
|
-
publishEnvironmentGeneration(state, candidate, queueCompletion);
|
|
274
|
-
void internals.device.queue.onSubmittedWorkDone().then(() => {
|
|
275
|
-
completeEnvironmentQueue(state, queueCompletion);
|
|
276
|
-
destroyCompletedEnvironmentGenerations(state, internals.device, queueCompletion);
|
|
277
|
-
});
|
|
278
|
-
},
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
292
|
export function recordFrame(
|
|
283
293
|
internals: RenderSystemInternals,
|
|
284
294
|
world: World,
|
|
@@ -289,14 +299,18 @@ export function recordFrame(
|
|
|
289
299
|
frameState: RenderFrameState,
|
|
290
300
|
dispatchCounts: DispatchCounts,
|
|
291
301
|
bindGroupCounts: BindGroupCounts,
|
|
292
|
-
|
|
302
|
+
skylight: SkylightSnapshot | undefined,
|
|
303
|
+
skylightCount: number,
|
|
304
|
+
skybox: SkyboxSnapshot | undefined,
|
|
305
|
+
skyboxCount: number,
|
|
293
306
|
postProcessParams: ReadonlyMap<string, Uint8Array>,
|
|
294
307
|
// feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): the
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
//
|
|
308
|
+
// full worlds[] list passed to `renderer.draw`, indexed by
|
|
309
|
+
// RenderableSnapshot.worldId so the record stage resolves each renderable's
|
|
310
|
+
// mesh + material textures against the world it was extracted from (mirroring
|
|
311
|
+
// the per-world extract stage). Optional + defaulting to `[world]` keeps the
|
|
312
|
+
// pre-split single-world identity path (worldId always 0) byte-for-byte and
|
|
313
|
+
// the existing unit-test callers (which pass a single mock world) valid.
|
|
300
314
|
worlds: readonly World[] = [world],
|
|
301
315
|
profilePhase?: RecordProfileRunner,
|
|
302
316
|
gpuDriven?: {
|
|
@@ -304,21 +318,9 @@ export function recordFrame(
|
|
|
304
318
|
readonly scene: PersistentGpuDrivenState | undefined;
|
|
305
319
|
readonly onCoverage: (ownsAll: boolean) => void;
|
|
306
320
|
},
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
readonly transaction: TemporalFrameTransaction;
|
|
310
|
-
readonly projection: RenderSceneProjection;
|
|
311
|
-
readonly coverage: TemporalCoverageSummary;
|
|
312
|
-
readonly reactiveRevision?: number | undefined;
|
|
313
|
-
readonly onSceneSubmitted?: ((delta: RenderSceneSubmissionDelta) => void) | undefined;
|
|
314
|
-
},
|
|
315
|
-
fog?: FogSnapshot | undefined,
|
|
321
|
+
renderReadLeases?: readonly RenderReadLease[],
|
|
322
|
+
featureGraphCandidate?: RenderFeatureGraphCandidate,
|
|
316
323
|
): boolean {
|
|
317
|
-
const skylight =
|
|
318
|
-
environment.kind === 'none' || environment.kind === 'image' ? environment.skylight : undefined;
|
|
319
|
-
const skylightCount = environment.skylightCount;
|
|
320
|
-
const skybox = environment.kind === 'image' ? environment.skybox : undefined;
|
|
321
|
-
const skyboxCount = environment.skyboxCount;
|
|
322
324
|
// The `try / finally` wrapper advances `frameState.frameNumber` exactly
|
|
323
325
|
// once per `recordFrame` invocation regardless of which early-return
|
|
324
326
|
// branch is taken (camera missing, cap exceeded, pipeline pending,
|
|
@@ -338,9 +340,6 @@ export function recordFrame(
|
|
|
338
340
|
// (no Camera = no scene), so geometry submission is a no-op and only
|
|
339
341
|
// the swap-chain clear lands.
|
|
340
342
|
let activeCameras = cameras;
|
|
341
|
-
let temporalAttempt: TemporalFrameAttempt | undefined;
|
|
342
|
-
let environmentAttempt: EnvironmentTransactionAttempt | undefined;
|
|
343
|
-
let frameSubmitted = false;
|
|
344
343
|
if (activeCameras.length === 0) {
|
|
345
344
|
internals.errorRegistry.fire(
|
|
346
345
|
new RhiError({
|
|
@@ -369,11 +368,6 @@ export function recordFrame(
|
|
|
369
368
|
frameState.currentFrameObservationSource = undefined;
|
|
370
369
|
frameState.currentDirectionalShadowView = null;
|
|
371
370
|
frameState.currentSpotShadowView = null;
|
|
372
|
-
environmentAttempt = beginProductionEnvironmentGeneration(internals, frameState, environment);
|
|
373
|
-
// `beginProductionEnvironmentGeneration` owns the producer snapshot and
|
|
374
|
-
// updates the active snapshot only after a successful submit. This keeps
|
|
375
|
-
// direct light/CSM consumers on the previous LKG while a replacement is
|
|
376
|
-
// still a candidate.
|
|
377
371
|
|
|
378
372
|
// Record-stage fold operator linear scan (groups transparent-sort entries
|
|
379
373
|
// into fold buckets + records the fold-eligible count metric). Extracted to
|
|
@@ -401,7 +395,7 @@ export function recordFrame(
|
|
|
401
395
|
// no Skylight (skylight === undefined)
|
|
402
396
|
// AND 0 direct light (totalLightCount === 0)
|
|
403
397
|
// AND StandardMaterial (renderables.some materialShaderId !== 'forgeax::default-unlit')
|
|
404
|
-
// All three true -> black + warn.
|
|
398
|
+
// All three true -> black + warn. A single false -> no warn.
|
|
405
399
|
//
|
|
406
400
|
// Multi-Skylight warn (F-4 nit + feat-20260630 M3 / w19): >1 Skylight
|
|
407
401
|
// entity -> warn ONCE per RenderSystem lifetime (not per frame), naming the
|
|
@@ -454,8 +448,7 @@ export function recordFrame(
|
|
|
454
448
|
spotLights,
|
|
455
449
|
profilePhase,
|
|
456
450
|
pipelineState.hdrpClusterMembershipPipeline !== null &&
|
|
457
|
-
internals.device.caps?.compute === true
|
|
458
|
-
internals.membershipTiming?.usesCpuControl() !== true,
|
|
451
|
+
internals.device.caps?.compute === true,
|
|
459
452
|
pipelineState.hdrpClusterMembershipBindGroupLayout,
|
|
460
453
|
);
|
|
461
454
|
});
|
|
@@ -467,8 +460,6 @@ export function recordFrame(
|
|
|
467
460
|
return { foldBuckets, light };
|
|
468
461
|
});
|
|
469
462
|
|
|
470
|
-
void activeCameras; // referenced below
|
|
471
|
-
|
|
472
463
|
// Case E (this commit): 0 renderables = legitimate scene (LO §1.1
|
|
473
464
|
// hello-window minimum semantic). Mirrors the Case C softening for
|
|
474
465
|
// 0 DirectionalLight (line 134 above; D-Q7). recordFrame() falls
|
|
@@ -493,40 +484,13 @@ export function recordFrame(
|
|
|
493
484
|
const effectiveSpotShadowMapSize = resolveSpotShadowMapSize(internals, lights);
|
|
494
485
|
const graphShadowMapSize = effectiveShadowMapSize ?? effectiveSpotShadowMapSize;
|
|
495
486
|
|
|
496
|
-
const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
|
|
497
|
-
validateRenderables(
|
|
498
|
-
internals,
|
|
499
|
-
world,
|
|
500
|
-
worlds,
|
|
501
|
-
renderables,
|
|
502
|
-
transparentDispatch,
|
|
503
|
-
worldForRenderId,
|
|
504
|
-
),
|
|
505
|
-
);
|
|
506
|
-
const residentMeshes = new Map<number, MeshGpuHandles>();
|
|
507
|
-
const meshWorlds = new Map<number, number>();
|
|
508
|
-
const ambiguousMeshHandles = new Set<number>();
|
|
509
|
-
for (const entry of validated) {
|
|
510
|
-
const handle = entry.source.assetHandle;
|
|
511
|
-
if (ambiguousMeshHandles.has(handle)) continue;
|
|
512
|
-
const priorWorld = meshWorlds.get(handle);
|
|
513
|
-
if (priorWorld !== undefined && priorWorld !== entry.source.worldId) {
|
|
514
|
-
residentMeshes.delete(handle);
|
|
515
|
-
ambiguousMeshHandles.add(handle);
|
|
516
|
-
continue;
|
|
517
|
-
}
|
|
518
|
-
meshWorlds.set(handle, entry.source.worldId);
|
|
519
|
-
residentMeshes.set(handle, entry.mesh);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
487
|
let preparedGpuDriven: PreparedGpuDrivenFrame | undefined;
|
|
523
488
|
if (gpuDriven !== undefined) {
|
|
524
489
|
const prepared = gpuDriven.owner.prepare({
|
|
525
490
|
scene: gpuDriven.scene,
|
|
526
491
|
camera,
|
|
527
|
-
meshes:
|
|
492
|
+
meshes: pipelineState.meshes,
|
|
528
493
|
viewBindGroupLayout: pipelineState.viewBindGroupLayout,
|
|
529
|
-
materialBindGroupLayout: pipelineState.materialBindGroupLayout,
|
|
530
494
|
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
531
495
|
hdrp: frameState.isHdrpActive,
|
|
532
496
|
});
|
|
@@ -534,22 +498,16 @@ export function recordFrame(
|
|
|
534
498
|
else if (prepared.error.code !== 'rhi-not-available')
|
|
535
499
|
internals.errorRegistry.fire(prepared.error);
|
|
536
500
|
}
|
|
537
|
-
const cpuVisibility = gpuDriven?.owner.inspect().cpuVisibility;
|
|
538
|
-
const multiViewSubmissions = gpuDriven?.owner.multiViewSubmissions() ?? [];
|
|
539
501
|
|
|
540
|
-
// The GPU lane owns only the primary opaque raster today.
|
|
541
|
-
//
|
|
542
|
-
// shadow encoder consumes them through a one-way direct-RHI adapter.
|
|
502
|
+
// The GPU lane owns only the primary opaque raster today. Directional,
|
|
503
|
+
// point, and spot shadow views still consume the CPU-validated draw plan.
|
|
543
504
|
const cpuShadowCastersActive =
|
|
544
505
|
(effectiveShadowMapSize ?? 0) > 0 ||
|
|
545
506
|
lights.pointShadow.length > 0 ||
|
|
546
507
|
lights.spot.some((spot) => spot.castShadow);
|
|
547
|
-
const gpuOwnsAllPrimaryRaster = preparedGpuDriven?.
|
|
508
|
+
const gpuOwnsAllPrimaryRaster = preparedGpuDriven?.ownsAllRenderables === true;
|
|
548
509
|
gpuDriven?.onCoverage(gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive);
|
|
549
510
|
|
|
550
|
-
const activeEnvironmentSignature =
|
|
551
|
-
frameState.environmentGeneration.candidate?.signature ??
|
|
552
|
-
frameState.environmentGeneration.active?.signature;
|
|
553
511
|
const preflightGraph = runRecordProfilePhase(profilePhase, 'record/render-graph', () =>
|
|
554
512
|
ensureCompiledFrameGraph(
|
|
555
513
|
internals,
|
|
@@ -561,26 +519,10 @@ export function recordFrame(
|
|
|
561
519
|
Math.max(1, internals.canvas.height),
|
|
562
520
|
graphShadowMapSize,
|
|
563
521
|
preparedGpuDriven,
|
|
564
|
-
|
|
522
|
+
featureGraphCandidate,
|
|
565
523
|
),
|
|
566
524
|
);
|
|
567
525
|
if (preflightGraph === null) return false;
|
|
568
|
-
temporalAttempt = temporal?.transaction.begin({
|
|
569
|
-
camera,
|
|
570
|
-
surfaceWidth: Math.max(1, internals.canvas.width),
|
|
571
|
-
surfaceHeight: Math.max(1, internals.canvas.height),
|
|
572
|
-
projection: temporal.projection,
|
|
573
|
-
visible: renderables,
|
|
574
|
-
coverage: temporal.coverage,
|
|
575
|
-
...(Object.hasOwn(temporal, 'reactiveRevision')
|
|
576
|
-
? { reactiveRevision: temporal.reactiveRevision }
|
|
577
|
-
: {}),
|
|
578
|
-
environmentSignature:
|
|
579
|
-
typeof activeEnvironmentSignature === 'string' ? activeEnvironmentSignature : undefined,
|
|
580
|
-
...(temporal.onSceneSubmitted === undefined
|
|
581
|
-
? {}
|
|
582
|
-
: { onSceneSubmitted: temporal.onSceneSubmitted }),
|
|
583
|
-
});
|
|
584
526
|
|
|
585
527
|
// Acquire the swap-chain texture + colour view + target dimensions (with
|
|
586
528
|
// one reconfigure-and-retry on surface-outdated). Extracted to
|
|
@@ -611,29 +553,6 @@ export function recordFrame(
|
|
|
611
553
|
|
|
612
554
|
pipelineState.perPassResources.shadowMapSize = graphShadowMapSize ?? 0;
|
|
613
555
|
pipelineState.perPassResources.shadowCascadeCount = lights.cascadeCount ?? 0;
|
|
614
|
-
pipelineState.perPassResources.shadowDepthBias = lights.depthBias ?? 0.005;
|
|
615
|
-
pipelineState.perPassResources.shadowNormalBias = lights.normalBias ?? 0.05;
|
|
616
|
-
pipelineState.perPassResources.shadowPcfKernelSize = clampPcfKernelSize(lights.pcfKernelSize);
|
|
617
|
-
const environmentSunDirection = environmentConsumerSnapshot(frameState.environmentGeneration)
|
|
618
|
-
?.csm.direction;
|
|
619
|
-
const environmentShadowDirection =
|
|
620
|
-
environmentSunDirection === undefined
|
|
621
|
-
? undefined
|
|
622
|
-
: [
|
|
623
|
-
-(environmentSunDirection[0] ?? 0),
|
|
624
|
-
-(environmentSunDirection[1] ?? 0),
|
|
625
|
-
-(environmentSunDirection[2] ?? 0),
|
|
626
|
-
];
|
|
627
|
-
pipelineState.perPassResources.shadowLightDirection =
|
|
628
|
-
environmentShadowDirection !== undefined
|
|
629
|
-
? new Float32Array(environmentShadowDirection)
|
|
630
|
-
: lights.directional === undefined
|
|
631
|
-
? null
|
|
632
|
-
: new Float32Array([
|
|
633
|
-
lights.directional.direction[0] ?? 0,
|
|
634
|
-
lights.directional.direction[1] ?? 0,
|
|
635
|
-
lights.directional.direction[2] ?? 0,
|
|
636
|
-
]);
|
|
637
556
|
const firstDirectionalShadowMatrix = lights.lightViewProj?.[0];
|
|
638
557
|
pipelineState.perPassResources.shadowLightSpaceMatrix =
|
|
639
558
|
firstDirectionalShadowMatrix === undefined
|
|
@@ -655,7 +574,6 @@ export function recordFrame(
|
|
|
655
574
|
: {
|
|
656
575
|
viewMatrix: new Float32Array(computeViewMatrix(camera)),
|
|
657
576
|
splitPlanes: new Float32Array(lights.splitPlanes),
|
|
658
|
-
cascadeBlend: lights.cascadeBlend ?? 0,
|
|
659
577
|
};
|
|
660
578
|
}
|
|
661
579
|
|
|
@@ -667,9 +585,6 @@ export function recordFrame(
|
|
|
667
585
|
camera,
|
|
668
586
|
skybox,
|
|
669
587
|
);
|
|
670
|
-
const atmosphereActive =
|
|
671
|
-
frameState.environmentGeneration.active !== undefined ||
|
|
672
|
-
frameState.environmentGeneration.candidate !== undefined;
|
|
673
588
|
|
|
674
589
|
// feat-20260604-learn-render-4.10-anti-aliasing-msaa M2 / w9 (D-6, C-9):
|
|
675
590
|
// MSAA is a per-Camera switch derived from `camera.antialias`, never
|
|
@@ -703,13 +618,27 @@ export function recordFrame(
|
|
|
703
618
|
// build a renderableIndex -> renderState map from dispatch entries
|
|
704
619
|
// so each ValidatedRenderable carries its per-material renderState
|
|
705
620
|
// override without an O(n^2) back-scan in the draw loop.
|
|
706
|
-
const
|
|
707
|
-
|
|
621
|
+
const gpuOwnedEntityKeys = preparedGpuDriven?.entityKeys ?? EMPTY_ENTITY_KEYS;
|
|
622
|
+
const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
|
|
623
|
+
gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive
|
|
624
|
+
? []
|
|
625
|
+
: validateRenderables(
|
|
626
|
+
internals,
|
|
627
|
+
world,
|
|
628
|
+
worlds,
|
|
629
|
+
pipelineState,
|
|
630
|
+
frameState,
|
|
631
|
+
renderables,
|
|
632
|
+
transparentDispatch,
|
|
633
|
+
cpuShadowCastersActive ? EMPTY_ENTITY_KEYS : gpuOwnedEntityKeys,
|
|
634
|
+
),
|
|
635
|
+
);
|
|
636
|
+
gpuDriven?.owner.recordCpuValidation(validated, gpuOwnedEntityKeys);
|
|
708
637
|
|
|
709
638
|
// Per-frame cache clean-up: drop per-entity BG cache entries + instance
|
|
710
639
|
// buffers whose entityKey is absent from the current validated set (despawn
|
|
711
640
|
// eviction). Extracted to cleanPerFrameCaches (M3/w18).
|
|
712
|
-
cleanPerFrameCaches(internals, frameState, validated);
|
|
641
|
+
cleanPerFrameCaches(internals, frameState, validated, gpuOwnedEntityKeys);
|
|
713
642
|
|
|
714
643
|
// Dispatch-ordered render plan: reorder validated renderables to dispatch
|
|
715
644
|
// order, run the mesh-SSBO capacity gate (graceful truncation), and build
|
|
@@ -718,44 +647,6 @@ export function recordFrame(
|
|
|
718
647
|
buildDispatchPlan(internals, validated, transparentDispatch, foldBuckets),
|
|
719
648
|
);
|
|
720
649
|
const validatedOrdered = dispatchPlan.validatedOrdered;
|
|
721
|
-
// Build the candidate-index owner once at the frame boundary. Directional
|
|
722
|
-
// CPU fallback consumes the same multi-view membership contract as the
|
|
723
|
-
// indirect path and must not rediscover selected casters by scanning the
|
|
724
|
-
// complete dispatch for every cascade.
|
|
725
|
-
const validatedByCandidateIndex = new Map<number, ValidatedRenderable>();
|
|
726
|
-
const validatedIndexByCandidateIndex = new Map<number, number>();
|
|
727
|
-
for (let validatedIndex = 0; validatedIndex < validatedOrdered.length; validatedIndex += 1) {
|
|
728
|
-
const entry = validatedOrdered[validatedIndex];
|
|
729
|
-
if (entry === undefined) continue;
|
|
730
|
-
const candidateIndex = entry.source.candidateIndex ?? entry.renderableIndex;
|
|
731
|
-
if (!validatedByCandidateIndex.has(candidateIndex)) {
|
|
732
|
-
validatedByCandidateIndex.set(candidateIndex, entry);
|
|
733
|
-
validatedIndexByCandidateIndex.set(candidateIndex, validatedIndex);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
// Build the compatibility shadow geometry owner once. Directional CSM
|
|
737
|
-
// uses its compact per-cascade submission; point/spot fallback passes
|
|
738
|
-
// need the main-view domain and the original validated slot for their
|
|
739
|
-
// dynamic mesh offset. Keeping both facts here removes a full
|
|
740
|
-
// validatedOrdered walk from every point face and spot tile while
|
|
741
|
-
// preserving the existing overflow-conservative behavior.
|
|
742
|
-
const mainSubmission = multiViewSubmissions.find((submission) => submission.kind === 'main');
|
|
743
|
-
const mainShadowCandidateIndices =
|
|
744
|
-
mainSubmission === undefined || mainSubmission.overflow
|
|
745
|
-
? undefined
|
|
746
|
-
: mainSubmission.candidateIndices;
|
|
747
|
-
const validatedShadowEntries = Object.freeze(
|
|
748
|
-
validatedOrdered.flatMap((entry, validatedIndex) => {
|
|
749
|
-
const candidateIndex = entry.source.candidateIndex ?? entry.renderableIndex;
|
|
750
|
-
if (
|
|
751
|
-
mainShadowCandidateIndices !== undefined &&
|
|
752
|
-
!mainShadowCandidateIndices.has(candidateIndex)
|
|
753
|
-
) {
|
|
754
|
-
return [];
|
|
755
|
-
}
|
|
756
|
-
return [{ entry, validatedIndex }];
|
|
757
|
-
}),
|
|
758
|
-
);
|
|
759
650
|
const foldDispatchPlan = dispatchPlan.foldDispatchPlan;
|
|
760
651
|
const materialSlotIndices = dispatchPlan.materialSlotIndices;
|
|
761
652
|
const materialSlots = dispatchPlan.materialSlots;
|
|
@@ -794,12 +685,8 @@ export function recordFrame(
|
|
|
794
685
|
// (clear-pass-only) path only when neither condition is met.
|
|
795
686
|
// feat-20260531-skybox-env-background M2 / w6: gate relaxed from
|
|
796
687
|
// `validatedOrdered.length > 0` to include skybox-only frames
|
|
797
|
-
// (plan-strategy D-3, R-3).
|
|
798
|
-
|
|
799
|
-
// scene validation list is empty; keep that payload authoritative before
|
|
800
|
-
// the graph executes those passes.
|
|
801
|
-
const renderFeaturesActive = (internals.featureHost?.size ?? 0) > 0;
|
|
802
|
-
if (validatedOrdered.length > 0 || skyboxActive || atmosphereActive || renderFeaturesActive) {
|
|
688
|
+
// (plan-strategy D-3, R-3).
|
|
689
|
+
if (validatedOrdered.length > 0 || skyboxActive) {
|
|
803
690
|
runRecordProfilePhase(profilePhase, 'record/uploads', () => {
|
|
804
691
|
// View UBO + CSM/spot-shadow matrix pack: assembled + uploaded in one
|
|
805
692
|
// queue.writeBuffer round-trip. Extracted to view-ubo.ts (M3/w18) so
|
|
@@ -811,8 +698,6 @@ export function recordFrame(
|
|
|
811
698
|
light,
|
|
812
699
|
lights,
|
|
813
700
|
frameState.spotShadowSnapshots,
|
|
814
|
-
temporalAttempt?.view,
|
|
815
|
-
fog,
|
|
816
701
|
);
|
|
817
702
|
writeShadowCasterUniforms(
|
|
818
703
|
internals.device.queue,
|
|
@@ -834,12 +719,11 @@ export function recordFrame(
|
|
|
834
719
|
pipelineState.meshStorageBuffer,
|
|
835
720
|
validatedOrdered,
|
|
836
721
|
foldDispatchPlan,
|
|
837
|
-
temporalAttempt?.reactiveRevisionChanged ?? false,
|
|
838
722
|
);
|
|
839
723
|
});
|
|
840
724
|
}
|
|
841
725
|
|
|
842
|
-
const
|
|
726
|
+
const directionalShadowCache = runRecordProfilePhase(
|
|
843
727
|
profilePhase,
|
|
844
728
|
'record/scene-state/directional-shadow-cache',
|
|
845
729
|
() =>
|
|
@@ -847,21 +731,12 @@ export function recordFrame(
|
|
|
847
731
|
internals,
|
|
848
732
|
frameState,
|
|
849
733
|
worlds,
|
|
734
|
+
renderReadLeases,
|
|
850
735
|
lights,
|
|
851
736
|
effectiveShadowMapSize,
|
|
852
737
|
validatedOrdered,
|
|
853
|
-
cpuVisibility,
|
|
854
|
-
gpuDriven?.scene,
|
|
855
738
|
),
|
|
856
739
|
);
|
|
857
|
-
const directionalShadowTileClearPipeline =
|
|
858
|
-
internals.getDirectionalShadowTileClearPipeline?.() ?? null;
|
|
859
|
-
const directionalShadowCache =
|
|
860
|
-
!preparedDirectionalShadowCache.reuse &&
|
|
861
|
-
preparedDirectionalShadowCache.hitMask !== 0 &&
|
|
862
|
-
directionalShadowTileClearPipeline === null
|
|
863
|
-
? { ...preparedDirectionalShadowCache, hitMask: 0 }
|
|
864
|
-
: preparedDirectionalShadowCache;
|
|
865
740
|
frameState.directionalShadowCacheRecorded = false;
|
|
866
741
|
|
|
867
742
|
const encoderResult = internals.device.createCommandEncoder({ label: 'render-system-frame' });
|
|
@@ -898,29 +773,13 @@ export function recordFrame(
|
|
|
898
773
|
// errorRegistry + shader-cache lookups) surfaces (`internals` itself satisfies
|
|
899
774
|
// `RenderSystemRuntime` so `runtime: internals` is a zero-cost reference). The
|
|
900
775
|
// 0-consumed `skyboxCount` residual is dropped.
|
|
901
|
-
const csmPlan = lights.directionalCsm;
|
|
902
|
-
const mainMembership = visibilityCandidateIndices(cpuVisibility, 'main');
|
|
903
|
-
const mainDispatch =
|
|
904
|
-
mainMembership === undefined
|
|
905
|
-
? transparentDispatch
|
|
906
|
-
: transparentDispatch.filter((entry) => {
|
|
907
|
-
const source = renderables[entry.renderableIndex];
|
|
908
|
-
const candidateIndex = source?.candidateIndex ?? entry.renderableIndex;
|
|
909
|
-
return mainMembership.has(candidateIndex);
|
|
910
|
-
});
|
|
911
|
-
|
|
912
776
|
const passCtx: _InternalRenderPipelineContext = {
|
|
913
777
|
assets: internals.assets,
|
|
914
778
|
world,
|
|
915
|
-
|
|
916
|
-
gpuDrivenOwnsAllForwardItems: gpuOwnsAllPrimaryRaster,
|
|
917
|
-
validatedByCandidateIndex,
|
|
918
|
-
validatedIndexByCandidateIndex,
|
|
919
|
-
validatedShadowEntries,
|
|
779
|
+
gpuDrivenEntityKeys: gpuOwnedEntityKeys,
|
|
920
780
|
store: internals.gpuStore,
|
|
921
781
|
pipelineState,
|
|
922
782
|
runtime: internals,
|
|
923
|
-
temporalFrame: temporalAttempt,
|
|
924
783
|
encoder,
|
|
925
784
|
view,
|
|
926
785
|
clear,
|
|
@@ -949,10 +808,7 @@ export function recordFrame(
|
|
|
949
808
|
geometryColorResolveView,
|
|
950
809
|
ldrSpriteColorView,
|
|
951
810
|
postProcessParams,
|
|
952
|
-
dispatch:
|
|
953
|
-
shadowDispatch: transparentDispatch,
|
|
954
|
-
directionalCsmPlan: csmPlan,
|
|
955
|
-
shadowDebugObservation: internals.shadowDebugObservation,
|
|
811
|
+
dispatch: transparentDispatch,
|
|
956
812
|
hdrpClusterBindGroup,
|
|
957
813
|
hdrpClusterMembershipBindGroup,
|
|
958
814
|
foldDispatchPlan,
|
|
@@ -962,12 +818,9 @@ export function recordFrame(
|
|
|
962
818
|
materialSlotCount,
|
|
963
819
|
materialBgAssemblyCache: frameState.materialBgAssemblyCache,
|
|
964
820
|
directionalShadowCacheReuse: directionalShadowCache.reuse,
|
|
965
|
-
directionalShadowCacheTokens: directionalShadowCache.cascadeTokens,
|
|
966
|
-
directionalShadowCacheHitMask: directionalShadowCache.hitMask,
|
|
967
|
-
multiViewSubmissions,
|
|
968
821
|
...(profilePhase !== undefined ? { profilePhase } : {}),
|
|
969
822
|
};
|
|
970
|
-
|
|
823
|
+
const submitted = runRecordProfilePhase(profilePhase, 'record/graph-execute', () =>
|
|
971
824
|
executeCompiledFrameGraph(
|
|
972
825
|
internals,
|
|
973
826
|
frameState,
|
|
@@ -976,19 +829,15 @@ export function recordFrame(
|
|
|
976
829
|
profilePhase === undefined
|
|
977
830
|
? undefined
|
|
978
831
|
: (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
|
|
979
|
-
temporalAttempt,
|
|
980
|
-
environmentAttempt,
|
|
981
832
|
),
|
|
982
833
|
);
|
|
983
|
-
if (
|
|
834
|
+
if (submitted && !directionalShadowCache.reuse) {
|
|
984
835
|
frameState.directionalShadowCache = frameState.directionalShadowCacheRecorded
|
|
985
836
|
? directionalShadowCache.next
|
|
986
837
|
: null;
|
|
987
838
|
}
|
|
988
|
-
return
|
|
839
|
+
return submitted;
|
|
989
840
|
} finally {
|
|
990
|
-
temporalAttempt?.abort();
|
|
991
|
-
if (!frameSubmitted) environmentAttempt?.abort('execute');
|
|
992
841
|
frameState.frameNumber += 1;
|
|
993
842
|
// feat-20260608-cluster-lighting M5 / w22: clear HDRP once-per-frame fired
|
|
994
843
|
// set so the next frame re-fires if the condition persists.
|
|
@@ -999,294 +848,63 @@ export function recordFrame(
|
|
|
999
848
|
interface DirectionalShadowCacheDecision {
|
|
1000
849
|
readonly reuse: boolean;
|
|
1001
850
|
readonly next: DirectionalShadowCache | null;
|
|
1002
|
-
readonly cascadeTokens: readonly string[];
|
|
1003
|
-
readonly hitMask: number;
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
export type DirectionalShadowCacheMissReason =
|
|
1007
|
-
| 'no-entry'
|
|
1008
|
-
| 'shadow-map-size'
|
|
1009
|
-
| 'cascade-count'
|
|
1010
|
-
| 'pipeline'
|
|
1011
|
-
| 'graph-topology'
|
|
1012
|
-
| 'scene-mode'
|
|
1013
|
-
| 'asset-catalog'
|
|
1014
|
-
| 'mesh-residency'
|
|
1015
|
-
| 'world-state'
|
|
1016
|
-
| 'cascade-token';
|
|
1017
|
-
|
|
1018
|
-
function observeDirectionalShadowTokenBuild(
|
|
1019
|
-
metrics: EngineMetrics,
|
|
1020
|
-
enabled: boolean,
|
|
1021
|
-
built: { readonly tokens: readonly string[]; readonly productionSceneToken: boolean },
|
|
1022
|
-
): void {
|
|
1023
|
-
if (!enabled || built.tokens.length === 0) return;
|
|
1024
|
-
metrics.increment('shadow.csm.token-build');
|
|
1025
|
-
metrics.increment(
|
|
1026
|
-
built.productionSceneToken
|
|
1027
|
-
? 'shadow.csm.token-production-scene'
|
|
1028
|
-
: 'shadow.csm.token-validated-fallback',
|
|
1029
|
-
);
|
|
1030
|
-
for (const _token of built.tokens) metrics.increment('shadow.csm.token-cascade');
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
function observeDirectionalShadowCacheDecision(
|
|
1034
|
-
metrics: EngineMetrics,
|
|
1035
|
-
enabled: boolean,
|
|
1036
|
-
decision: Pick<DirectionalShadowCacheDecision, 'reuse' | 'cascadeTokens' | 'hitMask'>,
|
|
1037
|
-
missReason: DirectionalShadowCacheMissReason | undefined,
|
|
1038
|
-
): void {
|
|
1039
|
-
if (!enabled || decision.cascadeTokens.length === 0) return;
|
|
1040
|
-
metrics.increment(decision.reuse ? 'shadow.csm.cache.reuse' : 'shadow.csm.cache.refresh');
|
|
1041
|
-
if (decision.hitMask !== 0 && !decision.reuse) {
|
|
1042
|
-
metrics.increment('shadow.csm.cache.partial-hit');
|
|
1043
|
-
}
|
|
1044
|
-
for (let index = 0; index < decision.cascadeTokens.length; index += 1) {
|
|
1045
|
-
metrics.increment(
|
|
1046
|
-
(decision.hitMask & (1 << index)) !== 0
|
|
1047
|
-
? 'shadow.csm.cache.cascade-hit'
|
|
1048
|
-
: 'shadow.csm.cache.cascade-miss',
|
|
1049
|
-
);
|
|
1050
|
-
}
|
|
1051
|
-
if (missReason !== undefined) metrics.increment(`shadow.csm.cache.miss.${missReason}`);
|
|
1052
851
|
}
|
|
1053
852
|
|
|
1054
|
-
function
|
|
1055
|
-
readonly cached: DirectionalShadowCache | null;
|
|
1056
|
-
readonly built: { readonly productionSceneToken: boolean };
|
|
1057
|
-
readonly shadowMapSize: number | undefined;
|
|
1058
|
-
readonly cascadeCount: number | undefined;
|
|
1059
|
-
readonly pipelineHandle: number;
|
|
1060
|
-
readonly graphTopologyKey: string | null;
|
|
1061
|
-
readonly assetCatalogEpoch: number;
|
|
1062
|
-
readonly meshResidencyEpoch: number;
|
|
1063
|
-
readonly worldStateMatches: boolean;
|
|
1064
|
-
}): DirectionalShadowCacheMissReason {
|
|
1065
|
-
const cached = input.cached;
|
|
1066
|
-
if (cached === null) return 'no-entry';
|
|
1067
|
-
if (cached.shadowMapSize !== input.shadowMapSize) return 'shadow-map-size';
|
|
1068
|
-
if (cached.cascadeCount !== input.cascadeCount) return 'cascade-count';
|
|
1069
|
-
if (cached.pipelineHandle !== input.pipelineHandle) return 'pipeline';
|
|
1070
|
-
if (cached.graphTopologyKey !== input.graphTopologyKey) return 'graph-topology';
|
|
1071
|
-
if (cached.productionSceneToken !== input.built.productionSceneToken) return 'scene-mode';
|
|
1072
|
-
if (cached.assetCatalogEpoch !== input.assetCatalogEpoch) return 'asset-catalog';
|
|
1073
|
-
if (cached.meshResidencyEpoch !== input.meshResidencyEpoch) return 'mesh-residency';
|
|
1074
|
-
if (!input.built.productionSceneToken && !input.worldStateMatches) return 'world-state';
|
|
1075
|
-
return 'cascade-token';
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
/**
|
|
1079
|
-
* @internal Pure cache decision owner used by recordFrame and its semantic
|
|
1080
|
-
* invalidation tests. Keeping the comparison separate makes the dangerous
|
|
1081
|
-
* path explicit: a hit is only reusable when every cascade token matches and
|
|
1082
|
-
* all frame/resource owners agree; a forced redraw is represented by
|
|
1083
|
-
* `reuse:false` even when one cascade happens to match.
|
|
1084
|
-
*/
|
|
1085
|
-
export function evaluateDirectionalShadowCache(input: {
|
|
1086
|
-
readonly cached: DirectionalShadowCache | null;
|
|
1087
|
-
readonly cascadeTokens: readonly string[];
|
|
1088
|
-
readonly productionSceneToken: boolean;
|
|
1089
|
-
readonly shadowMapSize: number | undefined;
|
|
1090
|
-
readonly cascadeCount: number | undefined;
|
|
1091
|
-
readonly pipelineHandle: number;
|
|
1092
|
-
readonly graphTopologyKey: string | null;
|
|
1093
|
-
readonly assetCatalogEpoch: number;
|
|
1094
|
-
readonly meshResidencyEpoch: number;
|
|
1095
|
-
readonly worldStateMatches: boolean;
|
|
1096
|
-
}): {
|
|
1097
|
-
readonly reuse: boolean;
|
|
1098
|
-
readonly hitMask: number;
|
|
1099
|
-
readonly missReason: DirectionalShadowCacheMissReason | undefined;
|
|
1100
|
-
} {
|
|
1101
|
-
const cached = input.cached;
|
|
1102
|
-
const commonHit =
|
|
1103
|
-
cached !== null &&
|
|
1104
|
-
cached.shadowMapSize === input.shadowMapSize &&
|
|
1105
|
-
cached.cascadeCount === input.cascadeCount &&
|
|
1106
|
-
cached.pipelineHandle === input.pipelineHandle &&
|
|
1107
|
-
cached.graphTopologyKey === input.graphTopologyKey &&
|
|
1108
|
-
cached.productionSceneToken === input.productionSceneToken &&
|
|
1109
|
-
cached.assetCatalogEpoch === input.assetCatalogEpoch &&
|
|
1110
|
-
cached.meshResidencyEpoch === input.meshResidencyEpoch &&
|
|
1111
|
-
(input.productionSceneToken || input.worldStateMatches);
|
|
1112
|
-
let hitMask = 0;
|
|
1113
|
-
if (commonHit && cached !== null) {
|
|
1114
|
-
for (let index = 0; index < input.cascadeTokens.length; index += 1) {
|
|
1115
|
-
if (cached.cascadeTokens[index] === input.cascadeTokens[index]) hitMask |= 1 << index;
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
const reuse = input.cascadeTokens.length > 0 && hitMask === (1 << input.cascadeTokens.length) - 1;
|
|
853
|
+
function readWorldState(lease: RenderReadLease): DirectionalShadowWorldState {
|
|
1119
854
|
return {
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
missReason: reuse
|
|
1123
|
-
? undefined
|
|
1124
|
-
: classifyDirectionalShadowCacheMiss({
|
|
1125
|
-
cached,
|
|
1126
|
-
built: { productionSceneToken: input.productionSceneToken },
|
|
1127
|
-
shadowMapSize: input.shadowMapSize,
|
|
1128
|
-
cascadeCount: input.cascadeCount,
|
|
1129
|
-
pipelineHandle: input.pipelineHandle,
|
|
1130
|
-
graphTopologyKey: input.graphTopologyKey,
|
|
1131
|
-
assetCatalogEpoch: input.assetCatalogEpoch,
|
|
1132
|
-
meshResidencyEpoch: input.meshResidencyEpoch,
|
|
1133
|
-
worldStateMatches: input.worldStateMatches,
|
|
1134
|
-
}),
|
|
855
|
+
worldIdentity: lease.worldIdentity,
|
|
856
|
+
changeCursor: lease.inspectCursor(),
|
|
1135
857
|
};
|
|
1136
858
|
}
|
|
1137
859
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
let next = hash;
|
|
1147
|
-
for (let index = 0; index < 8; index += 1) {
|
|
1148
|
-
next = hashShadowTokenByte(next, SHADOW_TOKEN_NUMBER.getUint8(index));
|
|
1149
|
-
}
|
|
1150
|
-
return next;
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
function hashShadowTokenString(hash: number, value: string): number {
|
|
1154
|
-
let next = hash;
|
|
1155
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
1156
|
-
const code = value.charCodeAt(index);
|
|
1157
|
-
next = hashShadowTokenByte(next, code & 0xff);
|
|
1158
|
-
next = hashShadowTokenByte(next, code >>> 8);
|
|
1159
|
-
}
|
|
1160
|
-
return next;
|
|
1161
|
-
}
|
|
1162
|
-
|
|
1163
|
-
function hashShadowTokenNumbers(hash: number, values: ArrayLike<number>): number {
|
|
1164
|
-
let next = hash;
|
|
1165
|
-
for (let index = 0; index < values.length; index += 1) {
|
|
1166
|
-
next = hashShadowTokenNumber(next, values[index]);
|
|
1167
|
-
}
|
|
1168
|
-
return next;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
/** @internal Deterministic cascade-local invalidation token producer. */
|
|
1172
|
-
export function buildCascadeLocalCacheTokens(input: {
|
|
1173
|
-
readonly lights: ExtractedLights;
|
|
1174
|
-
readonly visibility: CpuMultiViewVisibilityResult | undefined;
|
|
1175
|
-
readonly scene: PersistentGpuDrivenState | undefined;
|
|
1176
|
-
readonly validated: readonly ValidatedRenderable[];
|
|
1177
|
-
}): { readonly tokens: readonly string[]; readonly productionSceneToken: boolean } {
|
|
1178
|
-
const plan = input.lights.directionalCsm;
|
|
1179
|
-
if (plan === undefined) return { tokens: [], productionSceneToken: false };
|
|
1180
|
-
const productionSceneToken = input.visibility !== undefined && input.scene !== undefined;
|
|
1181
|
-
// PersistentRenderScene already owns O(1) slot lookup. The fallback map is
|
|
1182
|
-
// retained only for older test/custom state doubles that do not expose the
|
|
1183
|
-
// projection owner; it is intentionally not identity-cached because those
|
|
1184
|
-
// doubles may mutate their slots array in place.
|
|
1185
|
-
const slotByStableId =
|
|
1186
|
-
input.scene === undefined || input.scene.projection !== undefined
|
|
1187
|
-
? undefined
|
|
1188
|
-
: shadowSlotIndex(input.scene.slots);
|
|
1189
|
-
const submissionMembership =
|
|
1190
|
-
input.scene === undefined ? undefined : buildSubmissionCandidateMembership(input.scene.plan);
|
|
1191
|
-
const cascadeCount = Math.min(input.lights.cascadeCount ?? 0, plan.cascades.length);
|
|
1192
|
-
const tokens: string[] = [];
|
|
1193
|
-
for (let cascadeIndex = 0; cascadeIndex < cascadeCount; cascadeIndex += 1) {
|
|
1194
|
-
const cascade = plan.cascades[cascadeIndex];
|
|
1195
|
-
if (cascade === undefined) continue;
|
|
1196
|
-
let hash = 2_166_136_261;
|
|
1197
|
-
hash = hashShadowTokenString(hash, 'csm-cache-v2');
|
|
1198
|
-
hash = hashShadowTokenNumber(hash, cascadeIndex);
|
|
1199
|
-
hash = hashShadowTokenNumber(hash, cascade.revision);
|
|
1200
|
-
hash = hashShadowTokenNumbers(hash, cascade.lightViewProj);
|
|
1201
|
-
hash = hashShadowTokenNumbers(hash, cascade.clipLightBounds);
|
|
1202
|
-
hash = hashShadowTokenNumbers(hash, cascade.casterBroadphaseWorldAabb);
|
|
1203
|
-
hash = hashShadowTokenNumber(hash, cascade.rasterViewport.x);
|
|
1204
|
-
hash = hashShadowTokenNumber(hash, cascade.rasterViewport.y);
|
|
1205
|
-
hash = hashShadowTokenNumber(hash, cascade.rasterViewport.w);
|
|
1206
|
-
hash = hashShadowTokenNumber(hash, cascade.rasterViewport.h);
|
|
1207
|
-
|
|
1208
|
-
const visibilityView = input.visibility?.views.find(
|
|
1209
|
-
(view) => view.kind === 'cascade' && view.cascadeIndex === cascadeIndex,
|
|
1210
|
-
);
|
|
1211
|
-
if (
|
|
1212
|
-
productionSceneToken &&
|
|
1213
|
-
visibilityView !== undefined &&
|
|
1214
|
-
input.scene !== undefined &&
|
|
1215
|
-
(slotByStableId !== undefined || input.scene.projection !== undefined)
|
|
1216
|
-
) {
|
|
1217
|
-
const visibleStableIds = sortedUniqueStableIds(visibilityView.stableIds);
|
|
1218
|
-
for (const stableId of visibleStableIds) {
|
|
1219
|
-
const slot = input.scene.projection?.slotByIndex(stableId) ?? slotByStableId?.get(stableId);
|
|
1220
|
-
if (slot === undefined) continue;
|
|
1221
|
-
const revisions = slot.fact.revisions;
|
|
1222
|
-
hash = hashShadowTokenNumber(hash, stableId);
|
|
1223
|
-
hash = hashShadowTokenNumber(hash, slot.generation);
|
|
1224
|
-
hash = hashShadowTokenNumber(hash, revisions.transform);
|
|
1225
|
-
hash = hashShadowTokenNumber(hash, revisions.geometry);
|
|
1226
|
-
hash = hashShadowTokenNumber(hash, revisions.material);
|
|
1227
|
-
hash = hashShadowTokenNumber(hash, revisions.eligibility);
|
|
1228
|
-
hash = hashShadowTokenNumber(hash, revisions.space);
|
|
1229
|
-
hash = hashShadowTokenNumber(hash, revisions.batch);
|
|
1230
|
-
hash = hashShadowTokenNumber(hash, revisions.resource);
|
|
1231
|
-
}
|
|
1232
|
-
const visibleBatchIds = new Set<number>();
|
|
1233
|
-
if (submissionMembership !== undefined) {
|
|
1234
|
-
for (const stableId of visibleStableIds) {
|
|
1235
|
-
for (const entry of submissionMembership.byPrimitiveIndex.get(stableId) ?? []) {
|
|
1236
|
-
visibleBatchIds.add(entry.batchId);
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
// SubmissionPlan owns a stable numeric batch order. Normalize the
|
|
1240
|
-
// token input explicitly instead of inheriting Set insertion order
|
|
1241
|
-
// from primitive/cascade traversal; a reorder must be a conservative
|
|
1242
|
-
// miss at most, never a semantic token change by accident.
|
|
1243
|
-
for (const batchId of [...visibleBatchIds].sort((left, right) => left - right)) {
|
|
1244
|
-
const batch = submissionMembership.byBatchId.get(batchId);
|
|
1245
|
-
if (batch === undefined) continue;
|
|
1246
|
-
hash = hashShadowTokenNumber(hash, batch.batchId);
|
|
1247
|
-
hash = hashShadowTokenNumber(hash, batch.generation);
|
|
1248
|
-
hash = hashShadowTokenNumber(hash, batch.key.resourceRevision);
|
|
1249
|
-
hash = hashShadowTokenNumber(hash, batch.key.materialRevision);
|
|
1250
|
-
hash = hashShadowTokenString(hash, batch.key.pipelineClass);
|
|
1251
|
-
hash = hashShadowTokenString(hash, batch.key.materialClass);
|
|
1252
|
-
hash = hashShadowTokenString(hash, batch.key.materialResourceClass);
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
} else {
|
|
1256
|
-
hash = hashShadowTokenNumbers(hash, cascade.casterRenderableIndices);
|
|
1257
|
-
const casterMembership = new Set(cascade.casterRenderableIndices);
|
|
1258
|
-
for (const entry of input.validated) {
|
|
1259
|
-
const candidateIndex = entry.source.candidateIndex ?? entry.renderableIndex;
|
|
1260
|
-
if (!casterMembership.has(candidateIndex)) continue;
|
|
1261
|
-
const revisions = entry.source.sceneFact?.revisions;
|
|
1262
|
-
hash = hashShadowTokenNumber(hash, candidateIndex);
|
|
1263
|
-
hash = hashShadowTokenNumber(hash, revisions?.transform);
|
|
1264
|
-
hash = hashShadowTokenNumber(hash, revisions?.geometry);
|
|
1265
|
-
hash = hashShadowTokenNumber(hash, revisions?.material);
|
|
1266
|
-
hash = hashShadowTokenNumber(hash, revisions?.eligibility);
|
|
1267
|
-
hash = hashShadowTokenNumber(hash, revisions?.space);
|
|
1268
|
-
hash = hashShadowTokenNumber(hash, revisions?.batch);
|
|
1269
|
-
hash = hashShadowTokenNumber(hash, revisions?.resource);
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
tokens.push(`csm-${cascadeIndex}-${hash.toString(16).padStart(8, '0')}`);
|
|
860
|
+
function sameWorldState(
|
|
861
|
+
cached: DirectionalShadowCache,
|
|
862
|
+
worlds: readonly World[],
|
|
863
|
+
leases: readonly RenderReadLease[] | undefined,
|
|
864
|
+
): boolean {
|
|
865
|
+
if (leases === undefined || leases.length !== worlds.length) return false;
|
|
866
|
+
if (cached.worlds.length !== worlds.length || cached.worldStateTokens.length !== leases.length) {
|
|
867
|
+
return false;
|
|
1273
868
|
}
|
|
1274
|
-
return { tokens: Object.freeze(tokens), productionSceneToken };
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
function snapshotWorldState(world: World): DirectionalShadowWorldState {
|
|
1278
|
-
return { projection: createWorldProjection(world) };
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
function sameWorldState(cached: DirectionalShadowCache, worlds: readonly World[]): boolean {
|
|
1282
|
-
if (cached.worlds.length !== worlds.length) return false;
|
|
1283
869
|
for (let index = 0; index < worlds.length; index += 1) {
|
|
1284
870
|
const world = worlds[index];
|
|
1285
|
-
|
|
871
|
+
const lease = leases[index];
|
|
872
|
+
if (world === undefined || lease === undefined || cached.worlds[index] !== world) return false;
|
|
1286
873
|
const cachedState = cached.worldStateTokens[index];
|
|
1287
874
|
if (cachedState === undefined) return false;
|
|
1288
|
-
|
|
1289
|
-
|
|
875
|
+
if (cachedState.worldIdentity !== lease.worldIdentity) return false;
|
|
876
|
+
let changes: ReturnType<RenderReadLease['readChanges']>;
|
|
877
|
+
try {
|
|
878
|
+
changes = lease.readChanges(cachedState.changeCursor);
|
|
879
|
+
} catch {
|
|
880
|
+
// A lease may have evicted an old cursor after another owner consumed
|
|
881
|
+
// its bounded cursor window. Treat that as stale render evidence and
|
|
882
|
+
// rebuild the atlas from the current lease rather than reaching into
|
|
883
|
+
// World for a second mutation clock.
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
if (changes.status !== 'ok') return false;
|
|
887
|
+
if (changes.world.records.length !== 0 || changes.sharedRefs.records.length !== 0) {
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
function sameLightViewProj(
|
|
895
|
+
cached: DirectionalShadowCache,
|
|
896
|
+
current: readonly Float32Array[],
|
|
897
|
+
): boolean {
|
|
898
|
+
if (cached.lightViewProj.length !== current.length) return false;
|
|
899
|
+
for (let matrixIndex = 0; matrixIndex < current.length; matrixIndex += 1) {
|
|
900
|
+
const previous = cached.lightViewProj[matrixIndex];
|
|
901
|
+
const next = current[matrixIndex];
|
|
902
|
+
if (previous === undefined || next === undefined || previous.length !== next.length) {
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
for (let valueIndex = 0; valueIndex < next.length; valueIndex += 1) {
|
|
906
|
+
if (previous[valueIndex] !== next[valueIndex]) return false;
|
|
907
|
+
}
|
|
1290
908
|
}
|
|
1291
909
|
return true;
|
|
1292
910
|
}
|
|
@@ -1295,21 +913,13 @@ function prepareDirectionalShadowCache(
|
|
|
1295
913
|
internals: RenderSystemInternals,
|
|
1296
914
|
frameState: RenderFrameState,
|
|
1297
915
|
worlds: readonly World[],
|
|
916
|
+
leases: readonly RenderReadLease[] | undefined,
|
|
1298
917
|
lights: ExtractedLights,
|
|
1299
918
|
shadowMapSize: number | undefined,
|
|
1300
919
|
validatedOrdered: readonly ValidatedRenderable[],
|
|
1301
|
-
visibility: CpuMultiViewVisibilityResult | undefined,
|
|
1302
|
-
scene: PersistentGpuDrivenState | undefined,
|
|
1303
920
|
): DirectionalShadowCacheDecision {
|
|
1304
|
-
const observeShadowCensus = internals.shadowDebugObservation === true;
|
|
1305
921
|
const cascadeCount = lights.cascadeCount;
|
|
1306
|
-
const
|
|
1307
|
-
lights,
|
|
1308
|
-
visibility,
|
|
1309
|
-
scene,
|
|
1310
|
-
validated: validatedOrdered,
|
|
1311
|
-
});
|
|
1312
|
-
observeDirectionalShadowTokenBuild(internals.metrics, observeShadowCensus, built);
|
|
922
|
+
const lightViewProj = lights.lightViewProj;
|
|
1313
923
|
if (
|
|
1314
924
|
frameState.compiledFrameGraph === null ||
|
|
1315
925
|
frameState.compiledFrameGraphTopologyKey === null ||
|
|
@@ -1317,87 +927,49 @@ function prepareDirectionalShadowCache(
|
|
|
1317
927
|
shadowMapSize <= 0 ||
|
|
1318
928
|
cascadeCount === undefined ||
|
|
1319
929
|
cascadeCount <= 0 ||
|
|
1320
|
-
|
|
930
|
+
lightViewProj === undefined ||
|
|
1321
931
|
validatedOrdered.length === 0
|
|
1322
932
|
) {
|
|
1323
|
-
|
|
1324
|
-
internals.metrics.increment('shadow.csm.cache.precondition');
|
|
1325
|
-
}
|
|
1326
|
-
return { reuse: false, next: null, cascadeTokens: built.tokens, hitMask: 0 };
|
|
933
|
+
return { reuse: false, next: null };
|
|
1327
934
|
}
|
|
1328
935
|
|
|
1329
936
|
const cached = frameState.directionalShadowCache;
|
|
1330
|
-
const assetCatalogEpoch = internals.assets.
|
|
937
|
+
const assetCatalogEpoch = internals.assets.catalogEpoch;
|
|
1331
938
|
const meshResidencyEpoch = internals.gpuStore.meshResidencyEpoch;
|
|
1332
|
-
|
|
1333
|
-
cached
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
});
|
|
1345
|
-
if (evaluation.reuse) {
|
|
1346
|
-
const decision = {
|
|
1347
|
-
reuse: true,
|
|
1348
|
-
next: null,
|
|
1349
|
-
cascadeTokens: built.tokens,
|
|
1350
|
-
hitMask: evaluation.hitMask,
|
|
1351
|
-
};
|
|
1352
|
-
observeDirectionalShadowCacheDecision(
|
|
1353
|
-
internals.metrics,
|
|
1354
|
-
observeShadowCensus,
|
|
1355
|
-
decision,
|
|
1356
|
-
undefined,
|
|
1357
|
-
);
|
|
1358
|
-
return decision;
|
|
939
|
+
if (
|
|
940
|
+
cached !== null &&
|
|
941
|
+
cached.shadowMapSize === shadowMapSize &&
|
|
942
|
+
cached.cascadeCount === cascadeCount &&
|
|
943
|
+
cached.pipelineHandle === frameState.installedPipelineHandle &&
|
|
944
|
+
cached.graphTopologyKey === frameState.compiledFrameGraphTopologyKey &&
|
|
945
|
+
cached.assetCatalogEpoch === assetCatalogEpoch &&
|
|
946
|
+
cached.meshResidencyEpoch === meshResidencyEpoch &&
|
|
947
|
+
sameWorldState(cached, worlds, leases) &&
|
|
948
|
+
sameLightViewProj(cached, lightViewProj)
|
|
949
|
+
) {
|
|
950
|
+
return { reuse: true, next: null };
|
|
1359
951
|
}
|
|
1360
952
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
};
|
|
1367
|
-
observeDirectionalShadowCacheDecision(
|
|
1368
|
-
internals.metrics,
|
|
1369
|
-
observeShadowCensus,
|
|
1370
|
-
decision,
|
|
1371
|
-
observeShadowCensus ? evaluation.missReason : undefined,
|
|
1372
|
-
);
|
|
1373
|
-
|
|
1374
|
-
const worldStateTokens = worlds.map((world) => snapshotWorldState(world));
|
|
1375
|
-
if (worldStateTokens.some((state) => state === null)) {
|
|
1376
|
-
// A world that does not expose the ECS mutation tokens cannot safely
|
|
1377
|
-
// participate in a static-shadow reuse decision. Keep the conservative
|
|
1378
|
-
// path for custom/test worlds instead of collapsing token indexes with a
|
|
1379
|
-
// filter and risking a false-positive match.
|
|
1380
|
-
if (observeShadowCensus) {
|
|
1381
|
-
internals.metrics.increment('shadow.csm.cache.world-state-unavailable');
|
|
1382
|
-
}
|
|
1383
|
-
return decision;
|
|
953
|
+
if (leases === undefined || leases.length !== worlds.length) {
|
|
954
|
+
// Direct record-stage test harnesses may omit the host-owned leases. They
|
|
955
|
+
// still exercise the real shadow encoder, but without the ECS read owner
|
|
956
|
+
// they must conservatively rebuild rather than inspect World internals.
|
|
957
|
+
return { reuse: false, next: null };
|
|
1384
958
|
}
|
|
959
|
+
const worldStateTokens = leases.map(readWorldState);
|
|
1385
960
|
|
|
1386
961
|
return {
|
|
1387
962
|
reuse: false,
|
|
1388
|
-
cascadeTokens: built.tokens,
|
|
1389
|
-
hitMask: evaluation.hitMask,
|
|
1390
963
|
next: {
|
|
1391
964
|
worlds: worlds.slice(),
|
|
1392
|
-
worldStateTokens,
|
|
965
|
+
worldStateTokens: worldStateTokens as DirectionalShadowWorldState[],
|
|
1393
966
|
assetCatalogEpoch,
|
|
1394
967
|
pipelineHandle: frameState.installedPipelineHandle,
|
|
1395
968
|
graphTopologyKey: frameState.compiledFrameGraphTopologyKey,
|
|
1396
969
|
shadowMapSize,
|
|
1397
970
|
cascadeCount,
|
|
971
|
+
lightViewProj: lightViewProj.map((matrix) => new Float32Array(matrix)),
|
|
1398
972
|
meshResidencyEpoch,
|
|
1399
|
-
cascadeTokens: built.tokens,
|
|
1400
|
-
productionSceneToken: built.productionSceneToken,
|
|
1401
973
|
},
|
|
1402
974
|
};
|
|
1403
975
|
}
|
|
@@ -1417,15 +989,17 @@ function validateRenderables(
|
|
|
1417
989
|
internals: RenderSystemInternals,
|
|
1418
990
|
world: World,
|
|
1419
991
|
// feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): the full
|
|
1420
|
-
// Each renderable's mesh handle is a slot in its OWN
|
|
1421
|
-
// sharedRefs; resolving against a foreign world either misses
|
|
1422
|
-
// the wrong slot payload.
|
|
1423
|
-
//
|
|
1424
|
-
//
|
|
992
|
+
// worlds[] list. Each renderable's mesh handle is a user-tier slot in its OWN
|
|
993
|
+
// world's sharedRefs; resolving against a foreign world either misses
|
|
994
|
+
// (asset-not-registered) or resolves the wrong slot payload. `world` (the
|
|
995
|
+
// resource-owner) is retained as the fallback for renderables whose worldId
|
|
996
|
+
// is out of range (defensive; extractFrames always stamps a valid index).
|
|
1425
997
|
worlds: readonly World[],
|
|
998
|
+
pipelineState: PipelineState,
|
|
999
|
+
frameState: RenderFrameState,
|
|
1426
1000
|
renderables: readonly RenderableSnapshot[],
|
|
1427
1001
|
transparentDispatch: readonly DispatchEntry[],
|
|
1428
|
-
|
|
1002
|
+
excludedEntityKeys: ReadonlySet<number> = new Set<number>(),
|
|
1429
1003
|
): ValidatedRenderable[] {
|
|
1430
1004
|
// bug-20260527-renderstate-pipeline-dispatch-gap D-4:
|
|
1431
1005
|
// build a renderableIndex -> renderState map from dispatch entries
|
|
@@ -1451,23 +1025,17 @@ function validateRenderables(
|
|
|
1451
1025
|
for (let rIdx = 0; rIdx < renderables.length; rIdx++) {
|
|
1452
1026
|
const r = renderables[rIdx];
|
|
1453
1027
|
if (r === undefined) continue;
|
|
1028
|
+
if (excludedEntityKeys.has(worldEntityKey(r.worldId, r.entityKey))) continue;
|
|
1454
1029
|
// feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): resolve
|
|
1455
|
-
// this renderable's mesh against the world it was EXTRACTED from
|
|
1456
|
-
// single resource-owner `world`.
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
hint: 'keep every World passed to renderer.draw attached until its frame retires',
|
|
1465
|
-
detail: { assetHandle: r.assetHandle },
|
|
1466
|
-
}),
|
|
1467
|
-
);
|
|
1468
|
-
continue;
|
|
1469
|
-
}
|
|
1470
|
-
const assetRes = resolveRenderAsset<MeshAsset>(
|
|
1030
|
+
// this renderable's mesh against the world it was EXTRACTED from
|
|
1031
|
+
// (worlds[r.worldId]) — NOT the single resource-owner `world`. Builtin mesh
|
|
1032
|
+
// slots (< BUILTIN_BASE) resolve process-statically regardless of world, so
|
|
1033
|
+
// the per-world pick only matters for user-tier handles, but selecting it
|
|
1034
|
+
// unconditionally keeps a single code path. Falls back to the resource-owner
|
|
1035
|
+
// world if worldId is out of range (defensive; extractFrames always stamps
|
|
1036
|
+
// a valid index into worlds[]).
|
|
1037
|
+
const renderableWorld = worlds[r.worldId] ?? world;
|
|
1038
|
+
const assetRes = resolveAssetHandle<MeshAsset>(
|
|
1471
1039
|
renderableWorld,
|
|
1472
1040
|
toShared<'MeshAsset'>(r.assetHandle),
|
|
1473
1041
|
);
|
|
@@ -1475,20 +1043,28 @@ function validateRenderables(
|
|
|
1475
1043
|
internals.errorRegistry.fire(
|
|
1476
1044
|
new RhiError({
|
|
1477
1045
|
code: 'asset-not-registered',
|
|
1478
|
-
expected: 'MeshFilter.assetHandle in
|
|
1479
|
-
hint: '
|
|
1046
|
+
expected: 'MeshFilter.assetHandle in AssetRegistry',
|
|
1047
|
+
hint: 'use HANDLE_CUBE / HANDLE_TRIANGLE imports; custom mesh register path: feat-future-asset-system',
|
|
1480
1048
|
detail: { assetHandle: r.assetHandle },
|
|
1481
1049
|
}),
|
|
1482
1050
|
);
|
|
1483
1051
|
continue;
|
|
1484
1052
|
}
|
|
1053
|
+
// feat-20260601-device/gpu-residency-extraction M1 (D-1): builtin meshes
|
|
1054
|
+
// (slots through HANDLE_NINESLICE_QUAD) keep the createRenderer step-3
|
|
1055
|
+
// direct-upload + `pipelineState.meshes` path -- they are NOT routed
|
|
1056
|
+
// through `ensureResident`. User-registered meshes pull through the store
|
|
1057
|
+
// on first access (the register->upload push was severed in this M1);
|
|
1058
|
+
// the POD fetched above (assetRes.value) is passed in, store holds no
|
|
1059
|
+
// registry ref (D-2). A first-access miss builds the GPU buffers; later
|
|
1060
|
+
// frames hit the O(1) cache.
|
|
1485
1061
|
const meshAssetHandle = toShared<'MeshAsset'>(r.assetHandle);
|
|
1486
|
-
let meshHandles = internals.gpuStore.getMeshGpuHandles(meshAssetHandle,
|
|
1487
|
-
if (meshHandles === undefined) {
|
|
1062
|
+
let meshHandles = internals.gpuStore.getMeshGpuHandles(meshAssetHandle, renderableWorld);
|
|
1063
|
+
if (meshHandles === undefined && r.assetHandle > handleSlot(HANDLE_NINESLICE_QUAD)) {
|
|
1488
1064
|
const residentRes = internals.gpuStore.ensureResident(
|
|
1489
1065
|
meshAssetHandle,
|
|
1490
1066
|
assetRes.value,
|
|
1491
|
-
|
|
1067
|
+
renderableWorld,
|
|
1492
1068
|
);
|
|
1493
1069
|
if (residentRes.ok) {
|
|
1494
1070
|
meshHandles = residentRes.value;
|
|
@@ -1496,20 +1072,71 @@ function validateRenderables(
|
|
|
1496
1072
|
internals.errorRegistry.fire(residentRes.error);
|
|
1497
1073
|
}
|
|
1498
1074
|
}
|
|
1075
|
+
meshHandles = meshHandles ?? pipelineState.meshes.get(r.assetHandle);
|
|
1499
1076
|
if (meshHandles === undefined) {
|
|
1500
1077
|
internals.errorRegistry.fire(
|
|
1501
1078
|
new RhiError({
|
|
1502
1079
|
code: 'asset-not-registered',
|
|
1503
1080
|
expected: 'GPU mesh buffers uploaded for assetHandle',
|
|
1504
|
-
hint: 'await renderer.
|
|
1081
|
+
hint: 'await renderer.initialization before draw([world], { cameraOwner: 0, resourceOwner: 0 }); ensure AssetRegistry.configureGpuDevice ran so user meshes are uploaded',
|
|
1505
1082
|
detail: { assetHandle: r.assetHandle },
|
|
1506
1083
|
}),
|
|
1507
1084
|
);
|
|
1508
1085
|
continue;
|
|
1509
1086
|
}
|
|
1087
|
+
// feat-20260527-sprite-nineslice M2 / w11 (plan-strategy section D-2):
|
|
1088
|
+
// sprite branch with non-zero `slicesAndMode` (post-w12 paramSnapshot
|
|
1089
|
+
// entry name) overrides the user-supplied mesh handle (typically
|
|
1090
|
+
// HANDLE_QUAD = 3) with the 16-vertex / 54-index HANDLE_NINESLICE_QUAD
|
|
1091
|
+
// (id=5) topology so the vertex shader sees the 4x4 grid required for
|
|
1092
|
+
// 9-region anchor mapping. Default slicesAndMode ([0, 0, 0, 0]) keeps
|
|
1093
|
+
// the legacy HANDLE_QUAD path; a flip from zero to non-zero on the
|
|
1094
|
+
// same entity routes here per-frame so AI users can toggle 9-slice
|
|
1095
|
+
// on the fly without re-spawning the entity (charter F1 minimum
|
|
1096
|
+
// surface). The HANDLE_NINESLICE_QUAD GPU buffers are seeded by
|
|
1097
|
+
// createRenderer step-3.
|
|
1098
|
+
//
|
|
1099
|
+
// feat-20260625-refactor-sprite-as-transparent-mesh M3 / w13: judgement
|
|
1100
|
+
// key migrated from `shadingModel === 'sprite'` to
|
|
1101
|
+
// `materialShaderId === 'forgeax::sprite'` (plan-strategy D-10); slices
|
|
1102
|
+
// sourced from `paramSnapshot.slicesAndMode` (post-w12 UBO-aligned
|
|
1103
|
+
// overlay path).
|
|
1104
|
+
//
|
|
1105
|
+
// feat-20260624-sprite-lit-shading-model-pure-2d-lighting M1' / t7:
|
|
1106
|
+
// sprite-lit shares the sprite paramSchema (5 fields, t4 mirror) so
|
|
1107
|
+
// the 9-slices mesh swap applies identically.
|
|
1108
|
+
let effectiveMeshHandles = meshHandles;
|
|
1109
|
+
if (
|
|
1110
|
+
r.material.materialShaderId === 'forgeax::sprite' ||
|
|
1111
|
+
r.material.materialShaderId === 'forgeax::sprite-lit'
|
|
1112
|
+
) {
|
|
1113
|
+
const slicesArr = r.material.paramSnapshot?.slicesAndMode as readonly number[] | undefined;
|
|
1114
|
+
if (
|
|
1115
|
+
slicesArr !== undefined &&
|
|
1116
|
+
slicesArr.length >= 4 &&
|
|
1117
|
+
(slicesArr[0] !== 0 || slicesArr[1] !== 0 || slicesArr[2] !== 0 || slicesArr[3] !== 0)
|
|
1118
|
+
) {
|
|
1119
|
+
const nineSliceHandles = pipelineState.meshes.get(handleSlot(HANDLE_NINESLICE_QUAD));
|
|
1120
|
+
if (nineSliceHandles !== undefined) {
|
|
1121
|
+
effectiveMeshHandles = nineSliceHandles;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (r.morph !== undefined) {
|
|
1126
|
+
const morphed = prepareMorphMesh(
|
|
1127
|
+
internals,
|
|
1128
|
+
frameState,
|
|
1129
|
+
worldEntityKey(r.worldId, r.entityKey),
|
|
1130
|
+
effectiveMeshHandles,
|
|
1131
|
+
assetRes.value,
|
|
1132
|
+
r.morph,
|
|
1133
|
+
);
|
|
1134
|
+
if (morphed !== undefined) effectiveMeshHandles = morphed;
|
|
1135
|
+
}
|
|
1510
1136
|
validated.push({
|
|
1511
1137
|
source: r,
|
|
1512
|
-
|
|
1138
|
+
world: renderableWorld,
|
|
1139
|
+
mesh: effectiveMeshHandles,
|
|
1513
1140
|
renderableIndex: rIdx,
|
|
1514
1141
|
renderState: renderStateByRenderableIdx.get(rIdx),
|
|
1515
1142
|
variantSet: variantSetByRenderableIdx.get(rIdx),
|
|
@@ -1519,6 +1146,81 @@ function validateRenderables(
|
|
|
1519
1146
|
return validated;
|
|
1520
1147
|
}
|
|
1521
1148
|
|
|
1149
|
+
function prepareMorphMesh(
|
|
1150
|
+
internals: RenderSystemInternals,
|
|
1151
|
+
frameState: RenderFrameState,
|
|
1152
|
+
cacheKey: number,
|
|
1153
|
+
base: MeshGpuHandles,
|
|
1154
|
+
mesh: MeshAsset,
|
|
1155
|
+
morph: RenderableSnapshot['morph'],
|
|
1156
|
+
): MeshGpuHandles | undefined {
|
|
1157
|
+
if (morph === undefined || frameState.morphBuffers === undefined) return undefined;
|
|
1158
|
+
const targets = mesh.morphTargets;
|
|
1159
|
+
if (targets === undefined || targets.length !== morph.targetCount) return undefined;
|
|
1160
|
+
if (base.layoutProjection.arrayStride !== 48 || base.vertexCount <= 0) return undefined;
|
|
1161
|
+
const stride = mesh.vertices.length / base.vertexCount;
|
|
1162
|
+
if (!Number.isInteger(stride) || stride < 3) return undefined;
|
|
1163
|
+
|
|
1164
|
+
let active = false;
|
|
1165
|
+
for (const weight of morph.weights) {
|
|
1166
|
+
if (weight !== 0) {
|
|
1167
|
+
active = true;
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
if (!active) {
|
|
1172
|
+
frameState.morphBuffers.get(cacheKey)?.buffer.destroy();
|
|
1173
|
+
frameState.morphBuffers.delete(cacheKey);
|
|
1174
|
+
return undefined;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
const vertices = new Float32Array(mesh.vertices);
|
|
1178
|
+
for (let targetIndex = 0; targetIndex < targets.length; targetIndex += 1) {
|
|
1179
|
+
const weight = morph.weights[targetIndex] ?? 0;
|
|
1180
|
+
const positions = targets[targetIndex]?.position;
|
|
1181
|
+
if (positions === undefined || positions.length !== base.vertexCount * 3) return undefined;
|
|
1182
|
+
for (let vertex = 0; vertex < base.vertexCount; vertex += 1) {
|
|
1183
|
+
const vertexBase = vertex * stride;
|
|
1184
|
+
const positionBase = vertex * 3;
|
|
1185
|
+
vertices[vertexBase] = (vertices[vertexBase] ?? 0) + (positions[positionBase] ?? 0) * weight;
|
|
1186
|
+
vertices[vertexBase + 1] =
|
|
1187
|
+
(vertices[vertexBase + 1] ?? 0) + (positions[positionBase + 1] ?? 0) * weight;
|
|
1188
|
+
vertices[vertexBase + 2] =
|
|
1189
|
+
(vertices[vertexBase + 2] ?? 0) + (positions[positionBase + 2] ?? 0) * weight;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
let cached = frameState.morphBuffers.get(cacheKey);
|
|
1194
|
+
if (cached === undefined || cached.byteLength !== vertices.byteLength) {
|
|
1195
|
+
if (cached !== undefined && !cached.buffer.isDestroyed) {
|
|
1196
|
+
const destroyed = cached.buffer.destroy();
|
|
1197
|
+
if (!destroyed.ok) internals.errorRegistry.fire(destroyed.error);
|
|
1198
|
+
}
|
|
1199
|
+
const created = internals.device.createBuffer({
|
|
1200
|
+
label: `morph-${cacheKey}-vbo`,
|
|
1201
|
+
size: vertices.byteLength,
|
|
1202
|
+
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1203
|
+
mappedAtCreation: false,
|
|
1204
|
+
});
|
|
1205
|
+
if (!created.ok) {
|
|
1206
|
+
internals.errorRegistry.fire(created.error);
|
|
1207
|
+
frameState.morphBuffers.delete(cacheKey);
|
|
1208
|
+
return undefined;
|
|
1209
|
+
}
|
|
1210
|
+
cached = {
|
|
1211
|
+
buffer: new GpuBuffer(internals.device, created.value),
|
|
1212
|
+
byteLength: vertices.byteLength,
|
|
1213
|
+
};
|
|
1214
|
+
frameState.morphBuffers.set(cacheKey, cached);
|
|
1215
|
+
}
|
|
1216
|
+
const written = internals.device.queue.writeBuffer(cached.buffer.handle, 0, vertices);
|
|
1217
|
+
if (!written.ok) {
|
|
1218
|
+
internals.errorRegistry.fire(written.error);
|
|
1219
|
+
return undefined;
|
|
1220
|
+
}
|
|
1221
|
+
return { ...base, vertexBuffer: cached.buffer };
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1522
1224
|
/**
|
|
1523
1225
|
* feat-20260704 M3/w18: build the dispatch-ordered render plan, extracted
|
|
1524
1226
|
* verbatim from `recordFrame`. (1) M3/w26 dispatch-ordered reorder: reorder
|
|
@@ -1575,7 +1277,7 @@ function buildDispatchPlan(
|
|
|
1575
1277
|
ordered.push(v);
|
|
1576
1278
|
}
|
|
1577
1279
|
}
|
|
1578
|
-
// Append
|
|
1280
|
+
// Append renderables not in the dispatch list (e.g. default-material entities)
|
|
1579
1281
|
for (const v of validated) {
|
|
1580
1282
|
if (!seen.has(v.renderableIndex)) {
|
|
1581
1283
|
ordered.push(v);
|
|
@@ -1610,7 +1312,7 @@ function buildDispatchPlan(
|
|
|
1610
1312
|
//
|
|
1611
1313
|
// bug-20260609: feat-20260608 M5 amend made the material UBO indexed by
|
|
1612
1314
|
// cumulative *material-slot* count (one slot per submesh material),
|
|
1613
|
-
// which is >= entity count once
|
|
1315
|
+
// which is >= entity count once an entity carries `materials.length>1`.
|
|
1614
1316
|
// The mesh + material buffer pair share `slotCount` (single allocator),
|
|
1615
1317
|
// so we size against the larger of the two requirements: entity count
|
|
1616
1318
|
// (mesh-SSBO consumer) vs cumulative material-slot count (material-UBO
|
|
@@ -1721,7 +1423,7 @@ function buildDispatchPlan(
|
|
|
1721
1423
|
// Singleton buckets (mode-bypass under D-5, or non-foldable under
|
|
1722
1424
|
// mode 0) carry `bucketSize === 1` and never enter `headBuckets`, so
|
|
1723
1425
|
// the per-entity drawIndexed path correctly does not count.
|
|
1724
|
-
// SSOT helper lives in
|
|
1426
|
+
// SSOT helper lives in this record owner — engine code never
|
|
1725
1427
|
// hardcodes the metric key string.
|
|
1726
1428
|
incrementFoldedDrawsMetric(foldDispatchPlan, internals.metrics);
|
|
1727
1429
|
}
|
|
@@ -1798,6 +1500,17 @@ function cleanPerFrameCaches(
|
|
|
1798
1500
|
frameState.instanceBuffers.delete(key);
|
|
1799
1501
|
}
|
|
1800
1502
|
}
|
|
1503
|
+
|
|
1504
|
+
if (frameState.morphBuffers !== undefined) {
|
|
1505
|
+
for (const [key, entry] of frameState.morphBuffers.entries()) {
|
|
1506
|
+
if (validatedEntityKeys.has(key)) continue;
|
|
1507
|
+
if (!entry.buffer.isDestroyed) {
|
|
1508
|
+
const result = entry.buffer.destroy();
|
|
1509
|
+
if (!result.ok) internals.errorRegistry.fire(result.error);
|
|
1510
|
+
}
|
|
1511
|
+
frameState.morphBuffers.delete(key);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1801
1514
|
}
|
|
1802
1515
|
|
|
1803
1516
|
/**
|