@forgeax/engine-render 0.1.4 → 0.1.7
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 +268 -640
- 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 +214 -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 +0 -1
- 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 +11 -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-HCOGPHKS.mjs +93 -0
- package/dist/chunk-HCOGPHKS.mjs.map +1 -0
- package/dist/chunk-YEJIUW7D.mjs +1152 -0
- package/dist/chunk-YEJIUW7D.mjs.map +1 -0
- package/dist/components/__tests__/points-lines-admission.unit.test.d.ts +2 -0
- package/dist/components/__tests__/points-lines-admission.unit.test.d.ts.map +1 -0
- package/dist/components/__tests__/points-lines-schema.unit.test.d.ts +2 -0
- package/dist/components/__tests__/points-lines-schema.unit.test.d.ts.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 +4 -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/lines.d.ts +5 -0
- package/dist/components/lines.d.ts.map +1 -0
- package/dist/components/mesh-filter.d.ts +7 -7
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/points.d.ts +15 -0
- package/dist/components/points.d.ts.map +1 -0
- 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 +27463 -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 +320 -248
- 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 +7 -3
- 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 +17 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +167 -3
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +72 -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 +77 -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/points-lines/__tests__/bounds.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/bounds.unit.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts +2 -0
- package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/inspection.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/inspection.unit.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts +2 -0
- package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/points-lines.bench.d.ts +2 -0
- package/dist/points-lines/__tests__/points-lines.bench.d.ts.map +1 -0
- package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts +2 -0
- package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/snapshot.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/snapshot.unit.test.d.ts.map +1 -0
- package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts +2 -0
- package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts.map +1 -0
- package/dist/points-lines/admission.d.ts +36 -0
- package/dist/points-lines/admission.d.ts.map +1 -0
- package/dist/points-lines/bounds.d.ts +8 -0
- package/dist/points-lines/bounds.d.ts.map +1 -0
- package/dist/points-lines/expansion-cache.d.ts +28 -0
- package/dist/points-lines/expansion-cache.d.ts.map +1 -0
- package/dist/points-lines/inspection.d.ts +53 -0
- package/dist/points-lines/inspection.d.ts.map +1 -0
- package/dist/points-lines/prepare.d.ts +67 -0
- package/dist/points-lines/prepare.d.ts.map +1 -0
- package/dist/points-lines/record.d.ts +60 -0
- package/dist/points-lines/record.d.ts.map +1 -0
- package/dist/points-lines/snapshot.d.ts +49 -0
- package/dist/points-lines/snapshot.d.ts.map +1 -0
- 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 +12 -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 +252 -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 +9 -3
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/render-contract.d.ts +424 -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 +110 -338
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +24 -713
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +61 -0
- package/dist/scene/render-scene-types.d.ts.map +1 -0
- package/dist/scene/render-scene.d.ts +112 -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__/authoring-path.unit.test.ts +9 -0
- 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 +21 -18
- 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 +31 -58
- package/src/__tests__/material-snapshot-mutation.integration.test.ts +10 -15
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +31 -50
- 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 +24 -23
- package/src/__tests__/render-error-detail-ssot.test-d.ts +0 -95
- package/src/__tests__/render-error-exhaustive.test-d.ts +27 -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 +82 -95
- 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} +1741 -2699
- package/src/assembly/host-contract.ts +101 -0
- package/src/{renderer → assembly}/material/assembly.ts +2 -7
- package/src/assembly/material/pipeline-projection.ts +63 -0
- package/src/authoring.ts +15 -22
- package/src/components/__tests__/points-lines-admission.unit.test.ts +219 -0
- package/src/components/__tests__/points-lines-schema.unit.test.ts +56 -0
- package/src/components/__tests__/schema-contract.unit.test.ts +10 -0
- package/src/components/glyph-text.ts +3 -2
- package/src/components/index.ts +14 -9
- package/src/components/instances.ts +4 -5
- package/src/components/light-helpers.ts +1 -1
- package/src/components/lines.ts +18 -0
- package/src/components/mesh-filter.ts +11 -10
- package/src/components/points.ts +40 -0
- 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 -110
- 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 +469 -429
- 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 +32 -8
- 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 +99 -1109
- 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 +75 -217
- package/src/inspection-types.ts +80 -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 +441 -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 +7 -75
- package/src/points-lines/__tests__/bounds.unit.test.ts +21 -0
- package/src/points-lines/__tests__/cache-recovery.unit.test.ts +157 -0
- package/src/points-lines/__tests__/inspection-lanes.integration.test.ts +146 -0
- package/src/points-lines/__tests__/inspection.unit.test.ts +173 -0
- package/src/points-lines/__tests__/lane-structural.integration.test.ts +122 -0
- package/src/points-lines/__tests__/points-lines.bench.ts +400 -0
- package/src/points-lines/__tests__/raster-lane.integration.test.ts +44 -0
- package/src/points-lines/__tests__/snapshot.unit.test.ts +101 -0
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +73 -0
- package/src/points-lines/admission.ts +238 -0
- package/src/points-lines/bounds.ts +23 -0
- package/src/points-lines/expansion-cache.ts +215 -0
- package/src/points-lines/inspection.ts +97 -0
- package/src/points-lines/prepare.ts +177 -0
- package/src/points-lines/record.ts +125 -0
- package/src/points-lines/snapshot.ts +130 -0
- 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 +88 -197
- package/src/record/frame-snapshot.ts +45 -60
- package/src/record/frame-targets.ts +8 -243
- package/src/record/frame.ts +521 -797
- package/src/record/helpers.ts +6 -11
- package/src/record/main-pass-geometry.ts +198 -147
- package/src/record/main-pass-material.ts +517 -190
- package/src/record/main-pass-sprite-draws.ts +199 -118
- package/src/record/main-pass.ts +166 -144
- package/src/record/mesh-ssbo.ts +517 -38
- package/src/record/render-context.ts +384 -0
- package/src/record/shadow-pass.ts +154 -1843
- package/src/record/skybox-post-pass.ts +14 -15
- package/src/record/typed-frame-graph.ts +224 -262
- package/src/record/view-ubo.ts +53 -37
- package/src/render-contract.ts +582 -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 +1114 -1732
- package/src/render-system.ts +614 -1939
- package/src/scene/render-scene-types.ts +67 -0
- package/src/scene/render-scene.ts +1087 -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-DJIMFYX6.mjs +0 -1439
- package/dist/chunk-DJIMFYX6.mjs.map +0 -1
- package/dist/chunk-F2SIOCRM.mjs +0 -7423
- package/dist/chunk-F2SIOCRM.mjs.map +0 -1
- package/dist/chunk-JMSB7AR4.mjs +0 -3
- package/dist/chunk-JMSB7AR4.mjs.map +0 -1
- package/dist/chunk-KHI6K4C6.mjs +0 -659
- package/dist/chunk-KHI6K4C6.mjs.map +0 -1
- package/dist/chunk-PORZOL2V.mjs +0 -569
- package/dist/chunk-PORZOL2V.mjs.map +0 -1
- package/dist/chunk-UOJGMDGP.mjs +0 -29913
- package/dist/chunk-UOJGMDGP.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 -115
- 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 -289
- 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 {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
203
|
+
import { writeShadowCasterUniforms } from './shadow-pass';
|
|
204
|
+
import {
|
|
205
|
+
ensureCompiledFrameGraph,
|
|
206
|
+
executeCompiledFrameGraph,
|
|
207
|
+
type RenderFeatureGraphCandidate,
|
|
208
|
+
} from './typed-frame-graph';
|
|
209
|
+
import { writePointsLinesViewUbo, writeViewUbo } from './view-ubo';
|
|
119
210
|
|
|
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,10 @@ export function recordFrame(
|
|
|
304
318
|
readonly scene: PersistentGpuDrivenState | undefined;
|
|
305
319
|
readonly onCoverage: (ownsAll: boolean) => void;
|
|
306
320
|
},
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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,
|
|
323
|
+
pointsLinesOwner?: import('./render-context').PointsLinesRecordOwner,
|
|
316
324
|
): 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
325
|
// The `try / finally` wrapper advances `frameState.frameNumber` exactly
|
|
323
326
|
// once per `recordFrame` invocation regardless of which early-return
|
|
324
327
|
// branch is taken (camera missing, cap exceeded, pipeline pending,
|
|
@@ -338,9 +341,6 @@ export function recordFrame(
|
|
|
338
341
|
// (no Camera = no scene), so geometry submission is a no-op and only
|
|
339
342
|
// the swap-chain clear lands.
|
|
340
343
|
let activeCameras = cameras;
|
|
341
|
-
let temporalAttempt: TemporalFrameAttempt | undefined;
|
|
342
|
-
let environmentAttempt: EnvironmentTransactionAttempt | undefined;
|
|
343
|
-
let frameSubmitted = false;
|
|
344
344
|
if (activeCameras.length === 0) {
|
|
345
345
|
internals.errorRegistry.fire(
|
|
346
346
|
new RhiError({
|
|
@@ -369,11 +369,6 @@ export function recordFrame(
|
|
|
369
369
|
frameState.currentFrameObservationSource = undefined;
|
|
370
370
|
frameState.currentDirectionalShadowView = null;
|
|
371
371
|
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
372
|
|
|
378
373
|
// Record-stage fold operator linear scan (groups transparent-sort entries
|
|
379
374
|
// into fold buckets + records the fold-eligible count metric). Extracted to
|
|
@@ -401,7 +396,7 @@ export function recordFrame(
|
|
|
401
396
|
// no Skylight (skylight === undefined)
|
|
402
397
|
// AND 0 direct light (totalLightCount === 0)
|
|
403
398
|
// AND StandardMaterial (renderables.some materialShaderId !== 'forgeax::default-unlit')
|
|
404
|
-
// All three true -> black + warn.
|
|
399
|
+
// All three true -> black + warn. A single false -> no warn.
|
|
405
400
|
//
|
|
406
401
|
// Multi-Skylight warn (F-4 nit + feat-20260630 M3 / w19): >1 Skylight
|
|
407
402
|
// entity -> warn ONCE per RenderSystem lifetime (not per frame), naming the
|
|
@@ -454,8 +449,7 @@ export function recordFrame(
|
|
|
454
449
|
spotLights,
|
|
455
450
|
profilePhase,
|
|
456
451
|
pipelineState.hdrpClusterMembershipPipeline !== null &&
|
|
457
|
-
internals.device.caps?.compute === true
|
|
458
|
-
internals.membershipTiming?.usesCpuControl() !== true,
|
|
452
|
+
internals.device.caps?.compute === true,
|
|
459
453
|
pipelineState.hdrpClusterMembershipBindGroupLayout,
|
|
460
454
|
);
|
|
461
455
|
});
|
|
@@ -467,8 +461,6 @@ export function recordFrame(
|
|
|
467
461
|
return { foldBuckets, light };
|
|
468
462
|
});
|
|
469
463
|
|
|
470
|
-
void activeCameras; // referenced below
|
|
471
|
-
|
|
472
464
|
// Case E (this commit): 0 renderables = legitimate scene (LO §1.1
|
|
473
465
|
// hello-window minimum semantic). Mirrors the Case C softening for
|
|
474
466
|
// 0 DirectionalLight (line 134 above; D-Q7). recordFrame() falls
|
|
@@ -493,40 +485,13 @@ export function recordFrame(
|
|
|
493
485
|
const effectiveSpotShadowMapSize = resolveSpotShadowMapSize(internals, lights);
|
|
494
486
|
const graphShadowMapSize = effectiveShadowMapSize ?? effectiveSpotShadowMapSize;
|
|
495
487
|
|
|
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
488
|
let preparedGpuDriven: PreparedGpuDrivenFrame | undefined;
|
|
523
489
|
if (gpuDriven !== undefined) {
|
|
524
490
|
const prepared = gpuDriven.owner.prepare({
|
|
525
491
|
scene: gpuDriven.scene,
|
|
526
492
|
camera,
|
|
527
|
-
meshes:
|
|
493
|
+
meshes: pipelineState.meshes,
|
|
528
494
|
viewBindGroupLayout: pipelineState.viewBindGroupLayout,
|
|
529
|
-
materialBindGroupLayout: pipelineState.materialBindGroupLayout,
|
|
530
495
|
meshResidencyEpoch: internals.gpuStore.meshResidencyEpoch,
|
|
531
496
|
hdrp: frameState.isHdrpActive,
|
|
532
497
|
});
|
|
@@ -534,22 +499,16 @@ export function recordFrame(
|
|
|
534
499
|
else if (prepared.error.code !== 'rhi-not-available')
|
|
535
500
|
internals.errorRegistry.fire(prepared.error);
|
|
536
501
|
}
|
|
537
|
-
const cpuVisibility = gpuDriven?.owner.inspect().cpuVisibility;
|
|
538
|
-
const multiViewSubmissions = gpuDriven?.owner.multiViewSubmissions() ?? [];
|
|
539
502
|
|
|
540
|
-
// The GPU lane owns only the primary opaque raster today.
|
|
541
|
-
//
|
|
542
|
-
// shadow encoder consumes them through a one-way direct-RHI adapter.
|
|
503
|
+
// The GPU lane owns only the primary opaque raster today. Directional,
|
|
504
|
+
// point, and spot shadow views still consume the CPU-validated draw plan.
|
|
543
505
|
const cpuShadowCastersActive =
|
|
544
506
|
(effectiveShadowMapSize ?? 0) > 0 ||
|
|
545
507
|
lights.pointShadow.length > 0 ||
|
|
546
508
|
lights.spot.some((spot) => spot.castShadow);
|
|
547
|
-
const gpuOwnsAllPrimaryRaster = preparedGpuDriven?.
|
|
509
|
+
const gpuOwnsAllPrimaryRaster = preparedGpuDriven?.ownsAllRenderables === true;
|
|
548
510
|
gpuDriven?.onCoverage(gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive);
|
|
549
511
|
|
|
550
|
-
const activeEnvironmentSignature =
|
|
551
|
-
frameState.environmentGeneration.candidate?.signature ??
|
|
552
|
-
frameState.environmentGeneration.active?.signature;
|
|
553
512
|
const preflightGraph = runRecordProfilePhase(profilePhase, 'record/render-graph', () =>
|
|
554
513
|
ensureCompiledFrameGraph(
|
|
555
514
|
internals,
|
|
@@ -561,26 +520,10 @@ export function recordFrame(
|
|
|
561
520
|
Math.max(1, internals.canvas.height),
|
|
562
521
|
graphShadowMapSize,
|
|
563
522
|
preparedGpuDriven,
|
|
564
|
-
|
|
523
|
+
featureGraphCandidate,
|
|
565
524
|
),
|
|
566
525
|
);
|
|
567
526
|
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
527
|
|
|
585
528
|
// Acquire the swap-chain texture + colour view + target dimensions (with
|
|
586
529
|
// one reconfigure-and-retry on surface-outdated). Extracted to
|
|
@@ -611,29 +554,6 @@ export function recordFrame(
|
|
|
611
554
|
|
|
612
555
|
pipelineState.perPassResources.shadowMapSize = graphShadowMapSize ?? 0;
|
|
613
556
|
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
557
|
const firstDirectionalShadowMatrix = lights.lightViewProj?.[0];
|
|
638
558
|
pipelineState.perPassResources.shadowLightSpaceMatrix =
|
|
639
559
|
firstDirectionalShadowMatrix === undefined
|
|
@@ -655,7 +575,6 @@ export function recordFrame(
|
|
|
655
575
|
: {
|
|
656
576
|
viewMatrix: new Float32Array(computeViewMatrix(camera)),
|
|
657
577
|
splitPlanes: new Float32Array(lights.splitPlanes),
|
|
658
|
-
cascadeBlend: lights.cascadeBlend ?? 0,
|
|
659
578
|
};
|
|
660
579
|
}
|
|
661
580
|
|
|
@@ -667,9 +586,6 @@ export function recordFrame(
|
|
|
667
586
|
camera,
|
|
668
587
|
skybox,
|
|
669
588
|
);
|
|
670
|
-
const atmosphereActive =
|
|
671
|
-
frameState.environmentGeneration.active !== undefined ||
|
|
672
|
-
frameState.environmentGeneration.candidate !== undefined;
|
|
673
589
|
|
|
674
590
|
// feat-20260604-learn-render-4.10-anti-aliasing-msaa M2 / w9 (D-6, C-9):
|
|
675
591
|
// MSAA is a per-Camera switch derived from `camera.antialias`, never
|
|
@@ -703,13 +619,27 @@ export function recordFrame(
|
|
|
703
619
|
// build a renderableIndex -> renderState map from dispatch entries
|
|
704
620
|
// so each ValidatedRenderable carries its per-material renderState
|
|
705
621
|
// override without an O(n^2) back-scan in the draw loop.
|
|
706
|
-
const
|
|
707
|
-
|
|
622
|
+
const gpuOwnedEntityKeys = preparedGpuDriven?.entityKeys ?? EMPTY_ENTITY_KEYS;
|
|
623
|
+
const validated = runRecordProfilePhase(profilePhase, 'record/validation', () =>
|
|
624
|
+
gpuOwnsAllPrimaryRaster && !cpuShadowCastersActive
|
|
625
|
+
? []
|
|
626
|
+
: validateRenderables(
|
|
627
|
+
internals,
|
|
628
|
+
world,
|
|
629
|
+
worlds,
|
|
630
|
+
pipelineState,
|
|
631
|
+
frameState,
|
|
632
|
+
renderables,
|
|
633
|
+
transparentDispatch,
|
|
634
|
+
cpuShadowCastersActive ? EMPTY_ENTITY_KEYS : gpuOwnedEntityKeys,
|
|
635
|
+
),
|
|
636
|
+
);
|
|
637
|
+
gpuDriven?.owner.recordCpuValidation(validated, gpuOwnedEntityKeys);
|
|
708
638
|
|
|
709
639
|
// Per-frame cache clean-up: drop per-entity BG cache entries + instance
|
|
710
640
|
// buffers whose entityKey is absent from the current validated set (despawn
|
|
711
641
|
// eviction). Extracted to cleanPerFrameCaches (M3/w18).
|
|
712
|
-
cleanPerFrameCaches(internals, frameState, validated);
|
|
642
|
+
cleanPerFrameCaches(internals, frameState, validated, gpuOwnedEntityKeys);
|
|
713
643
|
|
|
714
644
|
// Dispatch-ordered render plan: reorder validated renderables to dispatch
|
|
715
645
|
// order, run the mesh-SSBO capacity gate (graceful truncation), and build
|
|
@@ -718,44 +648,6 @@ export function recordFrame(
|
|
|
718
648
|
buildDispatchPlan(internals, validated, transparentDispatch, foldBuckets),
|
|
719
649
|
);
|
|
720
650
|
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
651
|
const foldDispatchPlan = dispatchPlan.foldDispatchPlan;
|
|
760
652
|
const materialSlotIndices = dispatchPlan.materialSlotIndices;
|
|
761
653
|
const materialSlots = dispatchPlan.materialSlots;
|
|
@@ -794,12 +686,8 @@ export function recordFrame(
|
|
|
794
686
|
// (clear-pass-only) path only when neither condition is met.
|
|
795
687
|
// feat-20260531-skybox-env-background M2 / w6: gate relaxed from
|
|
796
688
|
// `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) {
|
|
689
|
+
// (plan-strategy D-3, R-3).
|
|
690
|
+
if (validatedOrdered.length > 0 || skyboxActive) {
|
|
803
691
|
runRecordProfilePhase(profilePhase, 'record/uploads', () => {
|
|
804
692
|
// View UBO + CSM/spot-shadow matrix pack: assembled + uploaded in one
|
|
805
693
|
// queue.writeBuffer round-trip. Extracted to view-ubo.ts (M3/w18) so
|
|
@@ -811,9 +699,16 @@ export function recordFrame(
|
|
|
811
699
|
light,
|
|
812
700
|
lights,
|
|
813
701
|
frameState.spotShadowSnapshots,
|
|
814
|
-
temporalAttempt?.view,
|
|
815
|
-
fog,
|
|
816
702
|
);
|
|
703
|
+
if (pipelineState.pointsLinesViewBuffer !== undefined) {
|
|
704
|
+
writePointsLinesViewUbo(
|
|
705
|
+
internals.device.queue,
|
|
706
|
+
pipelineState.pointsLinesViewBuffer,
|
|
707
|
+
camera,
|
|
708
|
+
targetW,
|
|
709
|
+
targetH,
|
|
710
|
+
);
|
|
711
|
+
}
|
|
817
712
|
writeShadowCasterUniforms(
|
|
818
713
|
internals.device.queue,
|
|
819
714
|
pipelineState.shadowCasterCascadeBuffer,
|
|
@@ -834,12 +729,11 @@ export function recordFrame(
|
|
|
834
729
|
pipelineState.meshStorageBuffer,
|
|
835
730
|
validatedOrdered,
|
|
836
731
|
foldDispatchPlan,
|
|
837
|
-
temporalAttempt?.reactiveRevisionChanged ?? false,
|
|
838
732
|
);
|
|
839
733
|
});
|
|
840
734
|
}
|
|
841
735
|
|
|
842
|
-
const
|
|
736
|
+
const directionalShadowCache = runRecordProfilePhase(
|
|
843
737
|
profilePhase,
|
|
844
738
|
'record/scene-state/directional-shadow-cache',
|
|
845
739
|
() =>
|
|
@@ -847,21 +741,12 @@ export function recordFrame(
|
|
|
847
741
|
internals,
|
|
848
742
|
frameState,
|
|
849
743
|
worlds,
|
|
744
|
+
renderReadLeases,
|
|
850
745
|
lights,
|
|
851
746
|
effectiveShadowMapSize,
|
|
852
747
|
validatedOrdered,
|
|
853
|
-
cpuVisibility,
|
|
854
|
-
gpuDriven?.scene,
|
|
855
748
|
),
|
|
856
749
|
);
|
|
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
750
|
frameState.directionalShadowCacheRecorded = false;
|
|
866
751
|
|
|
867
752
|
const encoderResult = internals.device.createCommandEncoder({ label: 'render-system-frame' });
|
|
@@ -898,29 +783,13 @@ export function recordFrame(
|
|
|
898
783
|
// errorRegistry + shader-cache lookups) surfaces (`internals` itself satisfies
|
|
899
784
|
// `RenderSystemRuntime` so `runtime: internals` is a zero-cost reference). The
|
|
900
785
|
// 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
786
|
const passCtx: _InternalRenderPipelineContext = {
|
|
913
787
|
assets: internals.assets,
|
|
914
788
|
world,
|
|
915
|
-
|
|
916
|
-
gpuDrivenOwnsAllForwardItems: gpuOwnsAllPrimaryRaster,
|
|
917
|
-
validatedByCandidateIndex,
|
|
918
|
-
validatedIndexByCandidateIndex,
|
|
919
|
-
validatedShadowEntries,
|
|
789
|
+
gpuDrivenEntityKeys: gpuOwnedEntityKeys,
|
|
920
790
|
store: internals.gpuStore,
|
|
921
791
|
pipelineState,
|
|
922
792
|
runtime: internals,
|
|
923
|
-
temporalFrame: temporalAttempt,
|
|
924
793
|
encoder,
|
|
925
794
|
view,
|
|
926
795
|
clear,
|
|
@@ -949,10 +818,7 @@ export function recordFrame(
|
|
|
949
818
|
geometryColorResolveView,
|
|
950
819
|
ldrSpriteColorView,
|
|
951
820
|
postProcessParams,
|
|
952
|
-
dispatch:
|
|
953
|
-
shadowDispatch: transparentDispatch,
|
|
954
|
-
directionalCsmPlan: csmPlan,
|
|
955
|
-
shadowDebugObservation: internals.shadowDebugObservation,
|
|
821
|
+
dispatch: transparentDispatch,
|
|
956
822
|
hdrpClusterBindGroup,
|
|
957
823
|
hdrpClusterMembershipBindGroup,
|
|
958
824
|
foldDispatchPlan,
|
|
@@ -960,14 +826,12 @@ export function recordFrame(
|
|
|
960
826
|
materialSlots,
|
|
961
827
|
materialSlotOwners,
|
|
962
828
|
materialSlotCount,
|
|
829
|
+
pointsLines: pointsLinesOwner,
|
|
963
830
|
materialBgAssemblyCache: frameState.materialBgAssemblyCache,
|
|
964
831
|
directionalShadowCacheReuse: directionalShadowCache.reuse,
|
|
965
|
-
directionalShadowCacheTokens: directionalShadowCache.cascadeTokens,
|
|
966
|
-
directionalShadowCacheHitMask: directionalShadowCache.hitMask,
|
|
967
|
-
multiViewSubmissions,
|
|
968
832
|
...(profilePhase !== undefined ? { profilePhase } : {}),
|
|
969
833
|
};
|
|
970
|
-
|
|
834
|
+
const submitted = runRecordProfilePhase(profilePhase, 'record/graph-execute', () =>
|
|
971
835
|
executeCompiledFrameGraph(
|
|
972
836
|
internals,
|
|
973
837
|
frameState,
|
|
@@ -976,19 +840,15 @@ export function recordFrame(
|
|
|
976
840
|
profilePhase === undefined
|
|
977
841
|
? undefined
|
|
978
842
|
: (pass, encode) => profilePhase(graphExecutionPhase(pass.name), encode),
|
|
979
|
-
temporalAttempt,
|
|
980
|
-
environmentAttempt,
|
|
981
843
|
),
|
|
982
844
|
);
|
|
983
|
-
if (
|
|
845
|
+
if (submitted && !directionalShadowCache.reuse) {
|
|
984
846
|
frameState.directionalShadowCache = frameState.directionalShadowCacheRecorded
|
|
985
847
|
? directionalShadowCache.next
|
|
986
848
|
: null;
|
|
987
849
|
}
|
|
988
|
-
return
|
|
850
|
+
return submitted;
|
|
989
851
|
} finally {
|
|
990
|
-
temporalAttempt?.abort();
|
|
991
|
-
if (!frameSubmitted) environmentAttempt?.abort('execute');
|
|
992
852
|
frameState.frameNumber += 1;
|
|
993
853
|
// feat-20260608-cluster-lighting M5 / w22: clear HDRP once-per-frame fired
|
|
994
854
|
// set so the next frame re-fires if the condition persists.
|
|
@@ -999,294 +859,63 @@ export function recordFrame(
|
|
|
999
859
|
interface DirectionalShadowCacheDecision {
|
|
1000
860
|
readonly reuse: boolean;
|
|
1001
861
|
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
862
|
}
|
|
1053
863
|
|
|
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;
|
|
864
|
+
function readWorldState(lease: RenderReadLease): DirectionalShadowWorldState {
|
|
1119
865
|
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
|
-
}),
|
|
866
|
+
worldIdentity: lease.worldIdentity,
|
|
867
|
+
changeCursor: lease.inspectCursor(),
|
|
1135
868
|
};
|
|
1136
869
|
}
|
|
1137
870
|
|
|
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')}`);
|
|
871
|
+
function sameWorldState(
|
|
872
|
+
cached: DirectionalShadowCache,
|
|
873
|
+
worlds: readonly World[],
|
|
874
|
+
leases: readonly RenderReadLease[] | undefined,
|
|
875
|
+
): boolean {
|
|
876
|
+
if (leases === undefined || leases.length !== worlds.length) return false;
|
|
877
|
+
if (cached.worlds.length !== worlds.length || cached.worldStateTokens.length !== leases.length) {
|
|
878
|
+
return false;
|
|
1273
879
|
}
|
|
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
880
|
for (let index = 0; index < worlds.length; index += 1) {
|
|
1284
881
|
const world = worlds[index];
|
|
1285
|
-
|
|
882
|
+
const lease = leases[index];
|
|
883
|
+
if (world === undefined || lease === undefined || cached.worlds[index] !== world) return false;
|
|
1286
884
|
const cachedState = cached.worldStateTokens[index];
|
|
1287
885
|
if (cachedState === undefined) return false;
|
|
1288
|
-
|
|
1289
|
-
|
|
886
|
+
if (cachedState.worldIdentity !== lease.worldIdentity) return false;
|
|
887
|
+
let changes: ReturnType<RenderReadLease['readChanges']>;
|
|
888
|
+
try {
|
|
889
|
+
changes = lease.readChanges(cachedState.changeCursor);
|
|
890
|
+
} catch {
|
|
891
|
+
// A lease may have evicted an old cursor after another owner consumed
|
|
892
|
+
// its bounded cursor window. Treat that as stale render evidence and
|
|
893
|
+
// rebuild the atlas from the current lease rather than reaching into
|
|
894
|
+
// World for a second mutation clock.
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
if (changes.status !== 'ok') return false;
|
|
898
|
+
if (changes.world.records.length !== 0 || changes.sharedRefs.records.length !== 0) {
|
|
899
|
+
return false;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function sameLightViewProj(
|
|
906
|
+
cached: DirectionalShadowCache,
|
|
907
|
+
current: readonly Float32Array[],
|
|
908
|
+
): boolean {
|
|
909
|
+
if (cached.lightViewProj.length !== current.length) return false;
|
|
910
|
+
for (let matrixIndex = 0; matrixIndex < current.length; matrixIndex += 1) {
|
|
911
|
+
const previous = cached.lightViewProj[matrixIndex];
|
|
912
|
+
const next = current[matrixIndex];
|
|
913
|
+
if (previous === undefined || next === undefined || previous.length !== next.length) {
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
for (let valueIndex = 0; valueIndex < next.length; valueIndex += 1) {
|
|
917
|
+
if (previous[valueIndex] !== next[valueIndex]) return false;
|
|
918
|
+
}
|
|
1290
919
|
}
|
|
1291
920
|
return true;
|
|
1292
921
|
}
|
|
@@ -1295,21 +924,13 @@ function prepareDirectionalShadowCache(
|
|
|
1295
924
|
internals: RenderSystemInternals,
|
|
1296
925
|
frameState: RenderFrameState,
|
|
1297
926
|
worlds: readonly World[],
|
|
927
|
+
leases: readonly RenderReadLease[] | undefined,
|
|
1298
928
|
lights: ExtractedLights,
|
|
1299
929
|
shadowMapSize: number | undefined,
|
|
1300
930
|
validatedOrdered: readonly ValidatedRenderable[],
|
|
1301
|
-
visibility: CpuMultiViewVisibilityResult | undefined,
|
|
1302
|
-
scene: PersistentGpuDrivenState | undefined,
|
|
1303
931
|
): DirectionalShadowCacheDecision {
|
|
1304
|
-
const observeShadowCensus = internals.shadowDebugObservation === true;
|
|
1305
932
|
const cascadeCount = lights.cascadeCount;
|
|
1306
|
-
const
|
|
1307
|
-
lights,
|
|
1308
|
-
visibility,
|
|
1309
|
-
scene,
|
|
1310
|
-
validated: validatedOrdered,
|
|
1311
|
-
});
|
|
1312
|
-
observeDirectionalShadowTokenBuild(internals.metrics, observeShadowCensus, built);
|
|
933
|
+
const lightViewProj = lights.lightViewProj;
|
|
1313
934
|
if (
|
|
1314
935
|
frameState.compiledFrameGraph === null ||
|
|
1315
936
|
frameState.compiledFrameGraphTopologyKey === null ||
|
|
@@ -1317,87 +938,49 @@ function prepareDirectionalShadowCache(
|
|
|
1317
938
|
shadowMapSize <= 0 ||
|
|
1318
939
|
cascadeCount === undefined ||
|
|
1319
940
|
cascadeCount <= 0 ||
|
|
1320
|
-
|
|
941
|
+
lightViewProj === undefined ||
|
|
1321
942
|
validatedOrdered.length === 0
|
|
1322
943
|
) {
|
|
1323
|
-
|
|
1324
|
-
internals.metrics.increment('shadow.csm.cache.precondition');
|
|
1325
|
-
}
|
|
1326
|
-
return { reuse: false, next: null, cascadeTokens: built.tokens, hitMask: 0 };
|
|
944
|
+
return { reuse: false, next: null };
|
|
1327
945
|
}
|
|
1328
946
|
|
|
1329
947
|
const cached = frameState.directionalShadowCache;
|
|
1330
|
-
const assetCatalogEpoch = internals.assets.
|
|
948
|
+
const assetCatalogEpoch = internals.assets.catalogEpoch;
|
|
1331
949
|
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;
|
|
950
|
+
if (
|
|
951
|
+
cached !== null &&
|
|
952
|
+
cached.shadowMapSize === shadowMapSize &&
|
|
953
|
+
cached.cascadeCount === cascadeCount &&
|
|
954
|
+
cached.pipelineHandle === frameState.installedPipelineHandle &&
|
|
955
|
+
cached.graphTopologyKey === frameState.compiledFrameGraphTopologyKey &&
|
|
956
|
+
cached.assetCatalogEpoch === assetCatalogEpoch &&
|
|
957
|
+
cached.meshResidencyEpoch === meshResidencyEpoch &&
|
|
958
|
+
sameWorldState(cached, worlds, leases) &&
|
|
959
|
+
sameLightViewProj(cached, lightViewProj)
|
|
960
|
+
) {
|
|
961
|
+
return { reuse: true, next: null };
|
|
1359
962
|
}
|
|
1360
963
|
|
|
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;
|
|
964
|
+
if (leases === undefined || leases.length !== worlds.length) {
|
|
965
|
+
// Direct record-stage test harnesses may omit the host-owned leases. They
|
|
966
|
+
// still exercise the real shadow encoder, but without the ECS read owner
|
|
967
|
+
// they must conservatively rebuild rather than inspect World internals.
|
|
968
|
+
return { reuse: false, next: null };
|
|
1384
969
|
}
|
|
970
|
+
const worldStateTokens = leases.map(readWorldState);
|
|
1385
971
|
|
|
1386
972
|
return {
|
|
1387
973
|
reuse: false,
|
|
1388
|
-
cascadeTokens: built.tokens,
|
|
1389
|
-
hitMask: evaluation.hitMask,
|
|
1390
974
|
next: {
|
|
1391
975
|
worlds: worlds.slice(),
|
|
1392
|
-
worldStateTokens,
|
|
976
|
+
worldStateTokens: worldStateTokens as DirectionalShadowWorldState[],
|
|
1393
977
|
assetCatalogEpoch,
|
|
1394
978
|
pipelineHandle: frameState.installedPipelineHandle,
|
|
1395
979
|
graphTopologyKey: frameState.compiledFrameGraphTopologyKey,
|
|
1396
980
|
shadowMapSize,
|
|
1397
981
|
cascadeCount,
|
|
982
|
+
lightViewProj: lightViewProj.map((matrix) => new Float32Array(matrix)),
|
|
1398
983
|
meshResidencyEpoch,
|
|
1399
|
-
cascadeTokens: built.tokens,
|
|
1400
|
-
productionSceneToken: built.productionSceneToken,
|
|
1401
984
|
},
|
|
1402
985
|
};
|
|
1403
986
|
}
|
|
@@ -1417,15 +1000,17 @@ function validateRenderables(
|
|
|
1417
1000
|
internals: RenderSystemInternals,
|
|
1418
1001
|
world: World,
|
|
1419
1002
|
// 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
|
-
//
|
|
1003
|
+
// worlds[] list. Each renderable's mesh handle is a user-tier slot in its OWN
|
|
1004
|
+
// world's sharedRefs; resolving against a foreign world either misses
|
|
1005
|
+
// (asset-not-registered) or resolves the wrong slot payload. `world` (the
|
|
1006
|
+
// resource-owner) is retained as the fallback for renderables whose worldId
|
|
1007
|
+
// is out of range (defensive; extractFrames always stamps a valid index).
|
|
1425
1008
|
worlds: readonly World[],
|
|
1009
|
+
pipelineState: PipelineState,
|
|
1010
|
+
frameState: RenderFrameState,
|
|
1426
1011
|
renderables: readonly RenderableSnapshot[],
|
|
1427
1012
|
transparentDispatch: readonly DispatchEntry[],
|
|
1428
|
-
|
|
1013
|
+
excludedEntityKeys: ReadonlySet<number> = new Set<number>(),
|
|
1429
1014
|
): ValidatedRenderable[] {
|
|
1430
1015
|
// bug-20260527-renderstate-pipeline-dispatch-gap D-4:
|
|
1431
1016
|
// build a renderableIndex -> renderState map from dispatch entries
|
|
@@ -1451,23 +1036,17 @@ function validateRenderables(
|
|
|
1451
1036
|
for (let rIdx = 0; rIdx < renderables.length; rIdx++) {
|
|
1452
1037
|
const r = renderables[rIdx];
|
|
1453
1038
|
if (r === undefined) continue;
|
|
1039
|
+
if (excludedEntityKeys.has(worldEntityKey(r.worldId, r.entityKey))) continue;
|
|
1454
1040
|
// 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>(
|
|
1041
|
+
// this renderable's mesh against the world it was EXTRACTED from
|
|
1042
|
+
// (worlds[r.worldId]) — NOT the single resource-owner `world`. Builtin mesh
|
|
1043
|
+
// slots (< BUILTIN_BASE) resolve process-statically regardless of world, so
|
|
1044
|
+
// the per-world pick only matters for user-tier handles, but selecting it
|
|
1045
|
+
// unconditionally keeps a single code path. Falls back to the resource-owner
|
|
1046
|
+
// world if worldId is out of range (defensive; extractFrames always stamps
|
|
1047
|
+
// a valid index into worlds[]).
|
|
1048
|
+
const renderableWorld = worlds[r.worldId] ?? world;
|
|
1049
|
+
const assetRes = resolveAssetHandle<MeshAsset>(
|
|
1471
1050
|
renderableWorld,
|
|
1472
1051
|
toShared<'MeshAsset'>(r.assetHandle),
|
|
1473
1052
|
);
|
|
@@ -1475,20 +1054,28 @@ function validateRenderables(
|
|
|
1475
1054
|
internals.errorRegistry.fire(
|
|
1476
1055
|
new RhiError({
|
|
1477
1056
|
code: 'asset-not-registered',
|
|
1478
|
-
expected: 'MeshFilter.assetHandle in
|
|
1479
|
-
hint: '
|
|
1057
|
+
expected: 'MeshFilter.assetHandle in AssetRegistry',
|
|
1058
|
+
hint: 'use HANDLE_CUBE / HANDLE_TRIANGLE imports; custom mesh register path: feat-future-asset-system',
|
|
1480
1059
|
detail: { assetHandle: r.assetHandle },
|
|
1481
1060
|
}),
|
|
1482
1061
|
);
|
|
1483
1062
|
continue;
|
|
1484
1063
|
}
|
|
1064
|
+
// feat-20260601-device/gpu-residency-extraction M1 (D-1): builtin meshes
|
|
1065
|
+
// (slots through HANDLE_NINESLICE_QUAD) keep the createRenderer step-3
|
|
1066
|
+
// direct-upload + `pipelineState.meshes` path -- they are NOT routed
|
|
1067
|
+
// through `ensureResident`. User-registered meshes pull through the store
|
|
1068
|
+
// on first access (the register->upload push was severed in this M1);
|
|
1069
|
+
// the POD fetched above (assetRes.value) is passed in, store holds no
|
|
1070
|
+
// registry ref (D-2). A first-access miss builds the GPU buffers; later
|
|
1071
|
+
// frames hit the O(1) cache.
|
|
1485
1072
|
const meshAssetHandle = toShared<'MeshAsset'>(r.assetHandle);
|
|
1486
|
-
let meshHandles = internals.gpuStore.getMeshGpuHandles(meshAssetHandle,
|
|
1487
|
-
if (meshHandles === undefined) {
|
|
1073
|
+
let meshHandles = internals.gpuStore.getMeshGpuHandles(meshAssetHandle, renderableWorld);
|
|
1074
|
+
if (meshHandles === undefined && r.assetHandle > handleSlot(HANDLE_NINESLICE_QUAD)) {
|
|
1488
1075
|
const residentRes = internals.gpuStore.ensureResident(
|
|
1489
1076
|
meshAssetHandle,
|
|
1490
1077
|
assetRes.value,
|
|
1491
|
-
|
|
1078
|
+
renderableWorld,
|
|
1492
1079
|
);
|
|
1493
1080
|
if (residentRes.ok) {
|
|
1494
1081
|
meshHandles = residentRes.value;
|
|
@@ -1496,20 +1083,71 @@ function validateRenderables(
|
|
|
1496
1083
|
internals.errorRegistry.fire(residentRes.error);
|
|
1497
1084
|
}
|
|
1498
1085
|
}
|
|
1086
|
+
meshHandles = meshHandles ?? pipelineState.meshes.get(r.assetHandle);
|
|
1499
1087
|
if (meshHandles === undefined) {
|
|
1500
1088
|
internals.errorRegistry.fire(
|
|
1501
1089
|
new RhiError({
|
|
1502
1090
|
code: 'asset-not-registered',
|
|
1503
1091
|
expected: 'GPU mesh buffers uploaded for assetHandle',
|
|
1504
|
-
hint: 'await renderer.
|
|
1092
|
+
hint: 'await renderer.initialization before draw([world], { cameraOwner: 0, resourceOwner: 0 }); ensure AssetRegistry.configureGpuDevice ran so user meshes are uploaded',
|
|
1505
1093
|
detail: { assetHandle: r.assetHandle },
|
|
1506
1094
|
}),
|
|
1507
1095
|
);
|
|
1508
1096
|
continue;
|
|
1509
1097
|
}
|
|
1098
|
+
// feat-20260527-sprite-nineslice M2 / w11 (plan-strategy section D-2):
|
|
1099
|
+
// sprite branch with non-zero `slicesAndMode` (post-w12 paramSnapshot
|
|
1100
|
+
// entry name) overrides the user-supplied mesh handle (typically
|
|
1101
|
+
// HANDLE_QUAD = 3) with the 16-vertex / 54-index HANDLE_NINESLICE_QUAD
|
|
1102
|
+
// (id=5) topology so the vertex shader sees the 4x4 grid required for
|
|
1103
|
+
// 9-region anchor mapping. Default slicesAndMode ([0, 0, 0, 0]) keeps
|
|
1104
|
+
// the legacy HANDLE_QUAD path; a flip from zero to non-zero on the
|
|
1105
|
+
// same entity routes here per-frame so AI users can toggle 9-slice
|
|
1106
|
+
// on the fly without re-spawning the entity (charter F1 minimum
|
|
1107
|
+
// surface). The HANDLE_NINESLICE_QUAD GPU buffers are seeded by
|
|
1108
|
+
// createRenderer step-3.
|
|
1109
|
+
//
|
|
1110
|
+
// feat-20260625-refactor-sprite-as-transparent-mesh M3 / w13: judgement
|
|
1111
|
+
// key migrated from `shadingModel === 'sprite'` to
|
|
1112
|
+
// `materialShaderId === 'forgeax::sprite'` (plan-strategy D-10); slices
|
|
1113
|
+
// sourced from `paramSnapshot.slicesAndMode` (post-w12 UBO-aligned
|
|
1114
|
+
// overlay path).
|
|
1115
|
+
//
|
|
1116
|
+
// feat-20260624-sprite-lit-shading-model-pure-2d-lighting M1' / t7:
|
|
1117
|
+
// sprite-lit shares the sprite paramSchema (5 fields, t4 mirror) so
|
|
1118
|
+
// the 9-slices mesh swap applies identically.
|
|
1119
|
+
let effectiveMeshHandles = meshHandles;
|
|
1120
|
+
if (
|
|
1121
|
+
r.material.materialShaderId === 'forgeax::sprite' ||
|
|
1122
|
+
r.material.materialShaderId === 'forgeax::sprite-lit'
|
|
1123
|
+
) {
|
|
1124
|
+
const slicesArr = r.material.paramSnapshot?.slicesAndMode as readonly number[] | undefined;
|
|
1125
|
+
if (
|
|
1126
|
+
slicesArr !== undefined &&
|
|
1127
|
+
slicesArr.length >= 4 &&
|
|
1128
|
+
(slicesArr[0] !== 0 || slicesArr[1] !== 0 || slicesArr[2] !== 0 || slicesArr[3] !== 0)
|
|
1129
|
+
) {
|
|
1130
|
+
const nineSliceHandles = pipelineState.meshes.get(handleSlot(HANDLE_NINESLICE_QUAD));
|
|
1131
|
+
if (nineSliceHandles !== undefined) {
|
|
1132
|
+
effectiveMeshHandles = nineSliceHandles;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
if (r.morph !== undefined) {
|
|
1137
|
+
const morphed = prepareMorphMesh(
|
|
1138
|
+
internals,
|
|
1139
|
+
frameState,
|
|
1140
|
+
worldEntityKey(r.worldId, r.entityKey),
|
|
1141
|
+
effectiveMeshHandles,
|
|
1142
|
+
assetRes.value,
|
|
1143
|
+
r.morph,
|
|
1144
|
+
);
|
|
1145
|
+
if (morphed !== undefined) effectiveMeshHandles = morphed;
|
|
1146
|
+
}
|
|
1510
1147
|
validated.push({
|
|
1511
1148
|
source: r,
|
|
1512
|
-
|
|
1149
|
+
world: renderableWorld,
|
|
1150
|
+
mesh: effectiveMeshHandles,
|
|
1513
1151
|
renderableIndex: rIdx,
|
|
1514
1152
|
renderState: renderStateByRenderableIdx.get(rIdx),
|
|
1515
1153
|
variantSet: variantSetByRenderableIdx.get(rIdx),
|
|
@@ -1519,6 +1157,81 @@ function validateRenderables(
|
|
|
1519
1157
|
return validated;
|
|
1520
1158
|
}
|
|
1521
1159
|
|
|
1160
|
+
function prepareMorphMesh(
|
|
1161
|
+
internals: RenderSystemInternals,
|
|
1162
|
+
frameState: RenderFrameState,
|
|
1163
|
+
cacheKey: number,
|
|
1164
|
+
base: MeshGpuHandles,
|
|
1165
|
+
mesh: MeshAsset,
|
|
1166
|
+
morph: RenderableSnapshot['morph'],
|
|
1167
|
+
): MeshGpuHandles | undefined {
|
|
1168
|
+
if (morph === undefined || frameState.morphBuffers === undefined) return undefined;
|
|
1169
|
+
const targets = mesh.morphTargets;
|
|
1170
|
+
if (targets === undefined || targets.length !== morph.targetCount) return undefined;
|
|
1171
|
+
if (base.layoutProjection.arrayStride !== 48 || base.vertexCount <= 0) return undefined;
|
|
1172
|
+
const stride = mesh.vertices.length / base.vertexCount;
|
|
1173
|
+
if (!Number.isInteger(stride) || stride < 3) return undefined;
|
|
1174
|
+
|
|
1175
|
+
let active = false;
|
|
1176
|
+
for (const weight of morph.weights) {
|
|
1177
|
+
if (weight !== 0) {
|
|
1178
|
+
active = true;
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
if (!active) {
|
|
1183
|
+
frameState.morphBuffers.get(cacheKey)?.buffer.destroy();
|
|
1184
|
+
frameState.morphBuffers.delete(cacheKey);
|
|
1185
|
+
return undefined;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
const vertices = new Float32Array(mesh.vertices);
|
|
1189
|
+
for (let targetIndex = 0; targetIndex < targets.length; targetIndex += 1) {
|
|
1190
|
+
const weight = morph.weights[targetIndex] ?? 0;
|
|
1191
|
+
const positions = targets[targetIndex]?.position;
|
|
1192
|
+
if (positions === undefined || positions.length !== base.vertexCount * 3) return undefined;
|
|
1193
|
+
for (let vertex = 0; vertex < base.vertexCount; vertex += 1) {
|
|
1194
|
+
const vertexBase = vertex * stride;
|
|
1195
|
+
const positionBase = vertex * 3;
|
|
1196
|
+
vertices[vertexBase] = (vertices[vertexBase] ?? 0) + (positions[positionBase] ?? 0) * weight;
|
|
1197
|
+
vertices[vertexBase + 1] =
|
|
1198
|
+
(vertices[vertexBase + 1] ?? 0) + (positions[positionBase + 1] ?? 0) * weight;
|
|
1199
|
+
vertices[vertexBase + 2] =
|
|
1200
|
+
(vertices[vertexBase + 2] ?? 0) + (positions[positionBase + 2] ?? 0) * weight;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
let cached = frameState.morphBuffers.get(cacheKey);
|
|
1205
|
+
if (cached === undefined || cached.byteLength !== vertices.byteLength) {
|
|
1206
|
+
if (cached !== undefined && !cached.buffer.isDestroyed) {
|
|
1207
|
+
const destroyed = cached.buffer.destroy();
|
|
1208
|
+
if (!destroyed.ok) internals.errorRegistry.fire(destroyed.error);
|
|
1209
|
+
}
|
|
1210
|
+
const created = internals.device.createBuffer({
|
|
1211
|
+
label: `morph-${cacheKey}-vbo`,
|
|
1212
|
+
size: vertices.byteLength,
|
|
1213
|
+
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
1214
|
+
mappedAtCreation: false,
|
|
1215
|
+
});
|
|
1216
|
+
if (!created.ok) {
|
|
1217
|
+
internals.errorRegistry.fire(created.error);
|
|
1218
|
+
frameState.morphBuffers.delete(cacheKey);
|
|
1219
|
+
return undefined;
|
|
1220
|
+
}
|
|
1221
|
+
cached = {
|
|
1222
|
+
buffer: new GpuBuffer(internals.device, created.value),
|
|
1223
|
+
byteLength: vertices.byteLength,
|
|
1224
|
+
};
|
|
1225
|
+
frameState.morphBuffers.set(cacheKey, cached);
|
|
1226
|
+
}
|
|
1227
|
+
const written = internals.device.queue.writeBuffer(cached.buffer.handle, 0, vertices);
|
|
1228
|
+
if (!written.ok) {
|
|
1229
|
+
internals.errorRegistry.fire(written.error);
|
|
1230
|
+
return undefined;
|
|
1231
|
+
}
|
|
1232
|
+
return { ...base, vertexBuffer: cached.buffer };
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1522
1235
|
/**
|
|
1523
1236
|
* feat-20260704 M3/w18: build the dispatch-ordered render plan, extracted
|
|
1524
1237
|
* verbatim from `recordFrame`. (1) M3/w26 dispatch-ordered reorder: reorder
|
|
@@ -1575,7 +1288,7 @@ function buildDispatchPlan(
|
|
|
1575
1288
|
ordered.push(v);
|
|
1576
1289
|
}
|
|
1577
1290
|
}
|
|
1578
|
-
// Append
|
|
1291
|
+
// Append renderables not in the dispatch list (e.g. default-material entities)
|
|
1579
1292
|
for (const v of validated) {
|
|
1580
1293
|
if (!seen.has(v.renderableIndex)) {
|
|
1581
1294
|
ordered.push(v);
|
|
@@ -1610,7 +1323,7 @@ function buildDispatchPlan(
|
|
|
1610
1323
|
//
|
|
1611
1324
|
// bug-20260609: feat-20260608 M5 amend made the material UBO indexed by
|
|
1612
1325
|
// cumulative *material-slot* count (one slot per submesh material),
|
|
1613
|
-
// which is >= entity count once
|
|
1326
|
+
// which is >= entity count once an entity carries `materials.length>1`.
|
|
1614
1327
|
// The mesh + material buffer pair share `slotCount` (single allocator),
|
|
1615
1328
|
// so we size against the larger of the two requirements: entity count
|
|
1616
1329
|
// (mesh-SSBO consumer) vs cumulative material-slot count (material-UBO
|
|
@@ -1721,7 +1434,7 @@ function buildDispatchPlan(
|
|
|
1721
1434
|
// Singleton buckets (mode-bypass under D-5, or non-foldable under
|
|
1722
1435
|
// mode 0) carry `bucketSize === 1` and never enter `headBuckets`, so
|
|
1723
1436
|
// the per-entity drawIndexed path correctly does not count.
|
|
1724
|
-
// SSOT helper lives in
|
|
1437
|
+
// SSOT helper lives in this record owner — engine code never
|
|
1725
1438
|
// hardcodes the metric key string.
|
|
1726
1439
|
incrementFoldedDrawsMetric(foldDispatchPlan, internals.metrics);
|
|
1727
1440
|
}
|
|
@@ -1798,6 +1511,17 @@ function cleanPerFrameCaches(
|
|
|
1798
1511
|
frameState.instanceBuffers.delete(key);
|
|
1799
1512
|
}
|
|
1800
1513
|
}
|
|
1514
|
+
|
|
1515
|
+
if (frameState.morphBuffers !== undefined) {
|
|
1516
|
+
for (const [key, entry] of frameState.morphBuffers.entries()) {
|
|
1517
|
+
if (validatedEntityKeys.has(key)) continue;
|
|
1518
|
+
if (!entry.buffer.isDestroyed) {
|
|
1519
|
+
const result = entry.buffer.destroy();
|
|
1520
|
+
if (!result.ok) internals.errorRegistry.fire(result.error);
|
|
1521
|
+
}
|
|
1522
|
+
frameState.morphBuffers.delete(key);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1801
1525
|
}
|
|
1802
1526
|
|
|
1803
1527
|
/**
|