@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/render-system.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Engine-internal phase: NOT registered to World schedule (AC-09);
|
|
5
5
|
// `Renderer.draw([world], { cameraOwner: 0, resourceOwner: 0 })` invokes once per frame. See `Renderer` JSDoc in
|
|
6
|
-
// `./
|
|
6
|
+
// `./render-contract.ts` for the public error/frame contract and AGENTS.md
|
|
7
7
|
// "ECS render bridge" section for the AI-user-facing contract.
|
|
8
8
|
//
|
|
9
9
|
// Stage carve-out (review round 1 finding #3 - 505 line cap fallback):
|
|
@@ -16,175 +16,108 @@
|
|
|
16
16
|
// .invert` (charter proposition 5: no math reinvention; render-system.test.ts
|
|
17
17
|
// asserts `/@forgeax\/engine-math/` shows up in render-system.ts source).
|
|
18
18
|
|
|
19
|
-
import
|
|
20
|
-
import type {
|
|
21
|
-
import type {
|
|
22
|
-
import type {
|
|
23
|
-
|
|
24
|
-
BindGroupLayout,
|
|
25
|
-
Buffer,
|
|
26
|
-
ComputePipeline,
|
|
27
|
-
PipelineLayout,
|
|
28
|
-
RenderPipeline,
|
|
29
|
-
RhiCanvasContext,
|
|
30
|
-
RhiDevice,
|
|
31
|
-
Sampler,
|
|
32
|
-
TextureView,
|
|
33
|
-
} from '@forgeax/engine-rhi';
|
|
19
|
+
import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
20
|
+
import type { World } from '@forgeax/engine-ecs';
|
|
21
|
+
import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
22
|
+
import type { RecorderSession } from '@forgeax/engine-profiler';
|
|
23
|
+
import type { BindGroupEntry, BindGroupLayout, Buffer, RenderPipeline } from '@forgeax/engine-rhi';
|
|
34
24
|
import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
|
|
35
|
-
import type { MaterialRuntimeArtifact } from '@forgeax/engine-shader';
|
|
36
25
|
import {
|
|
26
|
+
derive,
|
|
37
27
|
type Handle,
|
|
38
|
-
type ImmutableParamSchemaProjection,
|
|
39
28
|
type MaterialAsset,
|
|
40
|
-
type MaterialRenderState,
|
|
41
29
|
type MaterialTextureValue,
|
|
42
|
-
type
|
|
43
|
-
type PassKind,
|
|
44
|
-
type PrimitiveTopology,
|
|
30
|
+
type MeshAsset,
|
|
45
31
|
type RenderPipelineAsset,
|
|
46
32
|
RenderQueue,
|
|
47
33
|
type SamplerAsset,
|
|
34
|
+
toShared,
|
|
48
35
|
} from '@forgeax/engine-types';
|
|
49
|
-
import type { RenderAssetPort } from './assets/render-asset-port';
|
|
50
|
-
import { resolveRenderAsset } from './assets/resolve-render-asset';
|
|
51
36
|
import { createClusterBinScratch } from './cluster-binner';
|
|
52
|
-
import type { DynamicTextureStore } from './dynamic-texture-store';
|
|
53
|
-
import type { EngineMetrics } from './engine-metrics';
|
|
54
37
|
import {
|
|
55
|
-
createEnvironmentGenerationState,
|
|
56
|
-
environmentConsumerSnapshot,
|
|
57
|
-
} from './environment/generation';
|
|
58
|
-
import { isEnvironmentGpuBundle } from './environment/gpu-bundle';
|
|
59
|
-
import { type EnvironmentInspection, inspectEnvironmentGeneration } from './environment/inspection';
|
|
60
|
-
import { abandonEnvironmentDevice, disposeEnvironmentGeneration } from './environment/lifecycle';
|
|
61
|
-
import {
|
|
62
|
-
HdrpCapsInsufficientError,
|
|
63
38
|
type ObservationUnavailableError,
|
|
39
|
+
PointsLinesMaterialUnsupportedError,
|
|
64
40
|
type RenderError,
|
|
65
41
|
} from './errors/render';
|
|
66
|
-
import { resolveMaterialFromPort } from './extract/material-interface';
|
|
67
|
-
import {
|
|
68
|
-
createRenderFeatureContributionStaging,
|
|
69
|
-
mergeRenderFeatureContributions,
|
|
70
|
-
type RenderFeatureGraphComposition,
|
|
71
|
-
type RenderFeatureGraphContribution,
|
|
72
|
-
} from './features/graph-contribution';
|
|
73
42
|
import {
|
|
74
|
-
type RenderFeatureHost,
|
|
75
43
|
type RenderFeaturePreparedGraphicsResolverInput,
|
|
76
44
|
runRenderFeatureFrame,
|
|
77
45
|
settlePreparedGraphicsCompletion,
|
|
78
46
|
} from './features/host';
|
|
79
47
|
import {
|
|
80
|
-
|
|
81
|
-
type
|
|
48
|
+
createRenderFeatureGpuWorkOwner,
|
|
49
|
+
type RenderFeatureGpuWorkOwner,
|
|
82
50
|
} from './features/prepared-gpu-work';
|
|
83
|
-
import { sceneTemporalCoverage } from './features/scene-temporal';
|
|
84
51
|
import { resolveStandardRenderFeatureTargets } from './features/targets';
|
|
85
|
-
import type { RenderFeaturePassContext } from './features/types';
|
|
86
52
|
import {
|
|
87
53
|
buildFullscreenPostProcessPass,
|
|
88
54
|
DEPTH_MIN_PARAMS_BYTE_SIZE,
|
|
89
55
|
entryHasDepthRead,
|
|
90
56
|
type PostProcessShaderEntry,
|
|
91
57
|
} from './fullscreen-post-process-pass';
|
|
58
|
+
import { GpuDrivenProduction } from './gpu-driven/production-raster';
|
|
59
|
+
import type { RenderSceneInspection } from './inspection-types';
|
|
60
|
+
import { admitPointsLines } from './points-lines/admission';
|
|
61
|
+
import { PointsLinesExpansionCache } from './points-lines/expansion-cache';
|
|
62
|
+
import {
|
|
63
|
+
inspectPointsLines,
|
|
64
|
+
type PointsLinesInspection,
|
|
65
|
+
type PointsLinesSourceError,
|
|
66
|
+
} from './points-lines/inspection';
|
|
92
67
|
import {
|
|
93
|
-
|
|
94
|
-
type
|
|
95
|
-
} from './
|
|
96
|
-
import type
|
|
97
|
-
import type {
|
|
68
|
+
createPointsLinesLanePreparationAdapter,
|
|
69
|
+
type PointsLinesLanePreparationAdapter,
|
|
70
|
+
} from './points-lines/prepare';
|
|
71
|
+
import { createPointsLinesLaneAdapter, type PointsLinesBackend } from './points-lines/record';
|
|
72
|
+
import type { PointsLinesRetainedSnapshot } from './points-lines/snapshot';
|
|
73
|
+
import type { PointsLinesRecordOwner, PointsLinesRecordSubmission } from './record/render-context';
|
|
74
|
+
|
|
75
|
+
export type { RenderSceneInspection } from './inspection-types';
|
|
76
|
+
|
|
77
|
+
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
98
78
|
import {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
import { validateClusterGrid } from './hdrp-pipeline';
|
|
79
|
+
GPU_BUFFER_USAGE_COPY_DST,
|
|
80
|
+
GPU_BUFFER_USAGE_INDEX,
|
|
81
|
+
GPU_BUFFER_USAGE_UNIFORM,
|
|
82
|
+
GPU_BUFFER_USAGE_VERTEX,
|
|
83
|
+
} from './gpu-usage';
|
|
105
84
|
import { assembleMaterialWithSkylightEntries } from './ibl/skylight-bind-group';
|
|
106
85
|
import { disposeInstanceBuffers, disposeTransientInstanceBuffers } from './instance-buffer-cache';
|
|
107
|
-
import type { RhiErrorListenerRegistry } from './lifecycle';
|
|
108
86
|
import type { MeshMaterialBindingObservation } from './mesh-material-bindings';
|
|
109
|
-
import {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
} from './param-schema-projection-census';
|
|
113
|
-
import {
|
|
114
|
-
PersistentRenderScene,
|
|
115
|
-
type PersistentRenderSceneInspection,
|
|
116
|
-
} from './persistent-render-scene';
|
|
117
|
-
import { PipelineError } from './pipeline-errors';
|
|
118
|
-
import {
|
|
119
|
-
PostProcessError,
|
|
120
|
-
type PostProcessError as PostProcessErrorType,
|
|
121
|
-
} from './post-process-errors';
|
|
87
|
+
import { validateClusterGrid } from './pipeline/standard-pipeline';
|
|
88
|
+
import { DEFAULT_CLUSTER_GRID } from './pipeline/standard-profile';
|
|
89
|
+
import { PostProcessError } from './post-process-errors';
|
|
122
90
|
import {
|
|
123
91
|
createPreparedGraphicsResolver,
|
|
124
92
|
type PreparedGraphicsResolver,
|
|
125
93
|
} from './prepare/prepared-graphics-resolver';
|
|
126
|
-
import { type RecordProfileRunner, type RenderFrameState, recordFrame } from './record';
|
|
127
|
-
import { buildPerFrameBindGroups } from './record/frame-lighting';
|
|
128
94
|
import {
|
|
129
95
|
type FrameObservation,
|
|
130
96
|
type FrameObservationOptions,
|
|
131
97
|
observeCurrentFrame,
|
|
132
|
-
|
|
98
|
+
type RecordProfileRunner,
|
|
99
|
+
recordFrame,
|
|
100
|
+
} from './record/frame';
|
|
101
|
+
import { buildPerFrameBindGroups } from './record/frame-lighting';
|
|
102
|
+
import type { RenderFrameState } from './record/frame-snapshot';
|
|
133
103
|
import { applyParamSnapshotToUbo, residentTextureView } from './record/main-pass-material';
|
|
134
|
-
import type {
|
|
135
|
-
import {
|
|
136
|
-
applyShadowSubmitMode,
|
|
137
|
-
createShadowSubmitModeState,
|
|
138
|
-
restoreShadowSubmitMode,
|
|
139
|
-
type ShadowSubmitMode,
|
|
140
|
-
type ShadowSubmitModeState,
|
|
141
|
-
type ShadowSubmitResult,
|
|
142
|
-
} from './record/shadow-submit-mode';
|
|
143
|
-
import { retireCompiledFrameGraph } from './record/typed-frame-graph';
|
|
144
|
-
// The forgeax-concept RenderPipeline (registrable / installable unit) - aliased to avoid
|
|
145
|
-
// the name collision with the RHI opaque `RenderPipeline` handle imported above. The RHI
|
|
146
|
-
// handle stays internal (requirements line 155); this concept type is the public surface.
|
|
147
|
-
import type { RenderPipeline as RenderPipelineDef } from './render-pipeline';
|
|
148
|
-
import type { RenderPipelineContext } from './render-pipeline-context';
|
|
149
|
-
import type {
|
|
150
|
-
CameraSnapshot,
|
|
151
|
-
DispatchEntry,
|
|
152
|
-
ExtractedFrame,
|
|
153
|
-
MaterialSnapshotCachesByWorld,
|
|
154
|
-
RenderableSnapshot,
|
|
155
|
-
} from './render-system-extract';
|
|
156
|
-
import { TaaHistoryStore } from './taa-history-store';
|
|
157
|
-
import { TemporalFrameTransaction } from './temporal-frame-transaction';
|
|
158
|
-
import { projectTemporalInspection, type TemporalInspection } from './temporal-inspection';
|
|
159
|
-
|
|
160
|
-
export type RenderSceneInspection = PersistentRenderSceneInspection & {
|
|
161
|
-
readonly gpuDriven: GpuDrivenProductionInspection;
|
|
162
|
-
readonly temporal: TemporalInspection;
|
|
163
|
-
readonly environment: EnvironmentInspection;
|
|
164
|
-
};
|
|
165
|
-
|
|
104
|
+
import type { RenderSystemInternals } from './record/render-context';
|
|
166
105
|
import {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
prepareExtractContext,
|
|
171
|
-
refreshExtractedFrameCameras,
|
|
172
|
-
refreshExtractedFrameLights,
|
|
173
|
-
refreshExtractedFramePostProcessParams,
|
|
174
|
-
refreshRenderableMaterialSnapshot,
|
|
175
|
-
selectFog,
|
|
176
|
-
} from './render-system-extract';
|
|
106
|
+
type RenderFeatureGraphCandidate,
|
|
107
|
+
resetRenderFeatureGraphState,
|
|
108
|
+
} from './record/typed-frame-graph';
|
|
177
109
|
import {
|
|
110
|
+
type CameraSnapshot,
|
|
178
111
|
type DrawOwnerOptions,
|
|
179
112
|
RENDER_PHASE_CATALOG,
|
|
180
113
|
type RenderPhase,
|
|
181
114
|
type RenderPhaseSkipReason,
|
|
182
115
|
type RenderRecordPhase,
|
|
183
|
-
} from './
|
|
184
|
-
import type {
|
|
185
|
-
import
|
|
186
|
-
import {
|
|
187
|
-
import
|
|
116
|
+
} from './render-contract';
|
|
117
|
+
import type { DispatchEntry, ExtractedFrame, RenderableSnapshot } from './render-system-extract';
|
|
118
|
+
import { extractFrames } from './render-system-extract';
|
|
119
|
+
import { PersistentRenderScene } from './scene/render-scene';
|
|
120
|
+
import { resolveSsaoParameters } from './ssao-config';
|
|
188
121
|
import {
|
|
189
122
|
getTransparentSortConfig,
|
|
190
123
|
TRANSPARENT_SORT_MODE_DISTANCE,
|
|
@@ -192,7 +125,24 @@ import {
|
|
|
192
125
|
TRANSPARENT_SORT_MODE_LAYER_YZ,
|
|
193
126
|
TRANSPARENT_SORT_MODE_LAYER_Z,
|
|
194
127
|
} from './systems/transparent-sort-config';
|
|
195
|
-
|
|
128
|
+
|
|
129
|
+
export type {
|
|
130
|
+
_InternalRenderPipelineContext,
|
|
131
|
+
_StandardForwardSceneView,
|
|
132
|
+
PerPassResources,
|
|
133
|
+
PipelineState,
|
|
134
|
+
RenderSystemInternals,
|
|
135
|
+
RenderSystemRuntime,
|
|
136
|
+
SurfaceBackendKind,
|
|
137
|
+
SwapChainFormatPair,
|
|
138
|
+
} from './record/render-context';
|
|
139
|
+
export {
|
|
140
|
+
configureSurface,
|
|
141
|
+
MATERIAL_PER_ENTITY_STRIDE,
|
|
142
|
+
resolveSurfaceFormatPair,
|
|
143
|
+
STANDARD_PBR_UBO_SIZE,
|
|
144
|
+
selectSwapChainFormat,
|
|
145
|
+
} from './record/render-context';
|
|
196
146
|
|
|
197
147
|
/**
|
|
198
148
|
* Unified transparent-queue sub-sort covering all four
|
|
@@ -284,7 +234,7 @@ function sortTransparentDispatch(
|
|
|
284
234
|
const footY = posY - pivotY * sizeY;
|
|
285
235
|
if (mode === TRANSPARENT_SORT_MODE_LAYER_Y) return -footY;
|
|
286
236
|
if (mode === TRANSPARENT_SORT_MODE_LAYER_YZ) return footY + cfg.yzAlpha * posZ;
|
|
287
|
-
// Defensive fallback for
|
|
237
|
+
// Defensive fallback for an unknown mode that slips past setTransparentSortConfig.
|
|
288
238
|
return posZ;
|
|
289
239
|
};
|
|
290
240
|
|
|
@@ -317,9 +267,6 @@ function sortTransparentDispatch(
|
|
|
317
267
|
return result;
|
|
318
268
|
}
|
|
319
269
|
|
|
320
|
-
/** Dynamic-offset stride for one derived material UBO slot. */
|
|
321
|
-
export const MATERIAL_PER_ENTITY_STRIDE = 512;
|
|
322
|
-
|
|
323
270
|
/**
|
|
324
271
|
* Engine-internal Extract / Prepare / Record driver; constructed by createRenderer.
|
|
325
272
|
*
|
|
@@ -338,18 +285,13 @@ export const MATERIAL_PER_ENTITY_STRIDE = 512;
|
|
|
338
285
|
*/
|
|
339
286
|
export interface RenderSystem {
|
|
340
287
|
/** Returns true only when this invocation reached queue submission. */
|
|
341
|
-
draw(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
): ShadowSubmitResult<ShadowSubmitModeState>;
|
|
347
|
-
/** Restore the renderer-owned mode to the independent baseline. */
|
|
348
|
-
restoreShadowSubmitMode(rendererGeneration: string): ShadowSubmitResult<ShadowSubmitModeState>;
|
|
349
|
-
/** Read the generation-bound mode and exact lifecycle counters. */
|
|
350
|
-
getShadowSubmitMode(): ShadowSubmitModeState;
|
|
288
|
+
draw(
|
|
289
|
+
worlds: readonly World[],
|
|
290
|
+
opts: DrawOwnerOptions,
|
|
291
|
+
renderReadLeases?: readonly RenderReadLease[],
|
|
292
|
+
): boolean;
|
|
351
293
|
/** Release renderer-owned persistent state for one detached World. */
|
|
352
|
-
|
|
294
|
+
detachScene(world: World): void;
|
|
353
295
|
/** Release the profiler catalog contribution owned by this RenderSystem. */
|
|
354
296
|
releaseProfilerCatalog(): void;
|
|
355
297
|
observeCurrentFrame(
|
|
@@ -367,6 +309,8 @@ export interface RenderSystem {
|
|
|
367
309
|
readonly visibilityStats: { explicitlyHidden: number };
|
|
368
310
|
/** Persistent scene maintenance evidence from the ordinary single-World path. */
|
|
369
311
|
readonly renderScene: RenderSceneInspection;
|
|
312
|
+
/** Retained Points/Lines authoring facts from the single scene projection. */
|
|
313
|
+
readonly pointsLinesSnapshots: readonly PointsLinesRetainedSnapshot[];
|
|
370
314
|
/** Current mesh-slot provenance and active diagnostics from the last frame. */
|
|
371
315
|
readonly meshMaterialBindings: readonly MeshMaterialBindingObservation[];
|
|
372
316
|
/**
|
|
@@ -397,46 +341,10 @@ export interface RenderSystem {
|
|
|
397
341
|
readonly createBindGroup: number;
|
|
398
342
|
readonly keys: readonly string[];
|
|
399
343
|
};
|
|
400
|
-
/**
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
* declare the `shadowDepth` color target. Read-only debug seam:
|
|
405
|
-
* the engine itself reads the same view inline in `recordFrame`;
|
|
406
|
-
* this getter exists so debug readback paths
|
|
407
|
-
* (`Renderer.debugSampleShadowFactor`) read the same SSOT view as
|
|
408
|
-
* the engine's record stage (D-2: graph owns the lifecycle; no
|
|
409
|
-
* mirror slot on `pipelineState.perPassResources`).
|
|
410
|
-
*/
|
|
411
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
412
|
-
getCurrentShadowView(): any | null;
|
|
413
|
-
/**
|
|
414
|
-
* feat-20260601-customizable-render-pipeline-seam M1 / w7: register a render-pipeline
|
|
415
|
-
* logic under `id`. Same-id re-register THROWS a PipelineError
|
|
416
|
-
* (`'pipeline-already-registered'`) - programmer-error fail-fast, mirroring
|
|
417
|
-
* ShaderRegistry.installMaterialArtifact. Engine builtins use the `forgeax::` prefix.
|
|
418
|
-
*/
|
|
419
|
-
registerPipeline(id: string, impl: RenderPipelineDef): void;
|
|
420
|
-
/**
|
|
421
|
-
* feat-20260601-customizable-render-pipeline-seam M1 / w7 (D-19): install the pipeline
|
|
422
|
-
* described by a `RenderPipelineAsset` POD. Looks up `pipelineId` in the registry; on an
|
|
423
|
-
* unregistered id returns `Result.err(PipelineError{code:'pipeline-not-found'})`. On
|
|
424
|
-
* success the next `draw` detects the install-epoch bump and rebuilds the memoized
|
|
425
|
-
* per-frame graph (hot-swap). Takes the payload directly because installation happens at
|
|
426
|
-
* boot/swap time before any World exists -- there is no handle to resolve.
|
|
427
|
-
*/
|
|
428
|
-
installPipeline(asset: RenderPipelineAsset): Result<() => void, PipelineError>;
|
|
429
|
-
/**
|
|
430
|
-
* feat-20260604-resource-owning-render-graph-and-fullscreen-postpr M2 / w13:
|
|
431
|
-
* fullscreen post-process shader registry, parallel to ShaderRegistry.installMaterialArtifact
|
|
432
|
-
* (D-4: material shader handles 4-BGL / 12-float-vertex / depth / triangle-list, while
|
|
433
|
-
* fullscreen post-process uses 0-vertex-buffer / no-depth / input-texture-BGL).
|
|
434
|
-
* Same-id re-register THROWS PostProcessError ('post-process-already-registered').
|
|
435
|
-
* Engine builtins use the `forgeax::` prefix.
|
|
436
|
-
*/
|
|
437
|
-
readonly postProcess: {
|
|
438
|
-
register(id: string, entry: PostProcessShaderEntry): () => void;
|
|
439
|
-
};
|
|
344
|
+
/** Configure the sole Standard graph owner before the first frame. */
|
|
345
|
+
configureStandard(config: RenderPipelineAsset['config']): void;
|
|
346
|
+
/** Register one engine-owned post-process shader used by the Standard lane. */
|
|
347
|
+
registerBuiltinPostProcess(id: string, entry: PostProcessShaderEntry): () => void;
|
|
440
348
|
/**
|
|
441
349
|
* feat-20260612-rhi-destroy-renderer-dispose-gpu-lifecycle / M5 / w21:
|
|
442
350
|
* release the per-RenderSystem frame-state GPU bookkeeping during
|
|
@@ -461,313 +369,11 @@ export interface RenderSystem {
|
|
|
461
369
|
* Idempotent and graph-optional (no-op when nothing has compiled yet).
|
|
462
370
|
*/
|
|
463
371
|
resetForRecover(): void;
|
|
464
|
-
/** Recreate GPU-driven production state after internals.device is replaced. */
|
|
465
|
-
restoreGpuDrivenForRecover(): void;
|
|
466
372
|
/** Recreate post-process GPU parameter resources after a device rebuild. */
|
|
467
373
|
restorePostProcessResources(): void;
|
|
468
374
|
}
|
|
469
375
|
|
|
470
|
-
|
|
471
|
-
* feat-20260601-customizable-render-pipeline-seam M2 / w10: the NARROW runtime-services
|
|
472
|
-
* surface a `RenderPipeline` pass closure consumes (UE `FRDGBuilder` runtime half). Bundles
|
|
473
|
-
* exactly the four `RenderSystemInternals` members the 9 urp closures reach
|
|
474
|
-
* directly: the GPU `device`, the structured-error sink `errorRegistry`, and the two
|
|
475
|
-
* per-MaterialShader pipeline-cache / param-schema lookups. `RenderPipelineContext.runtime`
|
|
476
|
-
* is typed as this interface, NOT the full `RenderSystemInternals` - a pipeline author
|
|
477
|
-
* cannot reach `context` / `getPipelineState` / `canvas` (charter P4: a
|
|
478
|
-
* consistent narrow abstraction over the runtime, not the kitchen-sink). `internals`
|
|
479
|
-
* itself is unreachable through the public ctx (the AC-08 oracle: `ctx.internals` is a
|
|
480
|
-
* compile error).
|
|
481
|
-
*/
|
|
482
|
-
export interface RenderSystemRuntime {
|
|
483
|
-
readonly device: RhiDevice;
|
|
484
|
-
readonly errorRegistry: RhiErrorListenerRegistry;
|
|
485
|
-
/** @internal Emit directional shadow causal markers only for debug capture. */
|
|
486
|
-
readonly shadowDebugObservation?: boolean | undefined;
|
|
487
|
-
/** @internal Debug-only ParamSchema compatibility census; absent in production. */
|
|
488
|
-
readonly paramSchemaProjectionCensus?: ParamSchemaProjectionCensus | undefined;
|
|
489
|
-
/** @internal Depth-only fullscreen draw used to clear one invalid cascade tile. */
|
|
490
|
-
readonly getDirectionalShadowTileClearPipeline?: () => RenderPipeline | null;
|
|
491
|
-
/**
|
|
492
|
-
* feat-20260622-s5-device-surface-self-heal-recover M2 / w8: health registry
|
|
493
|
-
* threaded into the record stage so `recordFrame` can fire `internal-fault`
|
|
494
|
-
* when surface reconfigure+retry both fail (A-IN-2).
|
|
495
|
-
*/
|
|
496
|
-
readonly healthRegistry: import('./lifecycle').HealthListenerRegistry;
|
|
497
|
-
/**
|
|
498
|
-
* Cooked material consumption boundary. Runtime record code may obtain a
|
|
499
|
-
* projection and content-addressed artifact here; it must not resolve an
|
|
500
|
-
* authored parent chain or compile a missing specialization.
|
|
501
|
-
*/
|
|
502
|
-
readonly getMaterialProjection?: (materialGuid: string) => MaterialRenderProjection | undefined;
|
|
503
|
-
readonly getMaterialArtifact?: (specializationKey: string) => MaterialRuntimeArtifact | undefined;
|
|
504
|
-
/** Production MaterialReady/Error lookup used by the record draw gate. */
|
|
505
|
-
readonly getMaterialReadiness?: (materialGuid: string) => MaterialReadiness | undefined;
|
|
506
|
-
// feat-20260523-shader-template-instance-split M9-T03 (D-PipelineBuilder):
|
|
507
|
-
// per-MaterialShader pipeline cache lookup. Returns the cached pipeline
|
|
508
|
-
// for `materialShaderId`, lazily building on first miss via
|
|
509
|
-
// ShaderRegistry.findMaterialArtifact -> buildPipelineForMaterialShader.
|
|
510
|
-
// Returns `null` when:
|
|
511
|
-
// - the id is not registered in ShaderRegistry (authored materials skip
|
|
512
|
-
// the affected draw; feature-only callers may use the engine path)
|
|
513
|
-
// - the underlying async shader-module build is still pending (caller
|
|
514
|
-
// falls back for one frame and retries on the next; mirrors the
|
|
515
|
-
// `makeShaderDeviceAdapter` 1-frame-warmup idiom)
|
|
516
|
-
// - the pipeline build itself fails (caller fires a structured
|
|
517
|
-
// RhiError via errorRegistry; charter P3 explicit failure)
|
|
518
|
-
readonly getMaterialShaderPipeline?: (
|
|
519
|
-
materialShaderId: string,
|
|
520
|
-
isHdr: boolean,
|
|
521
|
-
renderState?: MaterialRenderState,
|
|
522
|
-
// feat-20260604 M3 / w9: per-mesh topology selects a per-topology PSO
|
|
523
|
-
// (WebGPU bakes topology into the immutable pipeline). Omitted resolves to
|
|
524
|
-
// 'triangle-list' (AC-03 zero-regression).
|
|
525
|
-
topology?: PrimitiveTopology,
|
|
526
|
-
// feat-20260604 M5 / w15: strip topologies bake the mesh's index width into
|
|
527
|
-
// primitive.stripIndexIndex. The record stage threads entry.mesh.indexFormat
|
|
528
|
-
// here so a uint16-indexed strip gets a uint16 PSO (not a hardcoded uint32).
|
|
529
|
-
// Ignored for list topologies (WebGPU spec: stripIndexFormat strip-only).
|
|
530
|
-
indexFormat?: 'uint16' | 'uint32',
|
|
531
|
-
// feat-20260609 M4 / w33: variantSet selects the per-variant WGSL from the
|
|
532
|
-
// shader manifest. When non-empty, the pipeline builder resolves the matching
|
|
533
|
-
// variant's composedWgsl instead of the boot-time default; the BGL is
|
|
534
|
-
// derived from paramSchema via derive() in pbr-pipeline (M3 / w12-w13).
|
|
535
|
-
// URP callers pass 'STORAGE_BUFFER_AVAILABLE=true'; HDRP callers pass
|
|
536
|
-
// 'CLUSTER_FORWARD_AVAILABLE=true+STORAGE_BUFFER_AVAILABLE=true'. Omitted
|
|
537
|
-
// (undefined / '') preserves backward-compatible behaviour (existing PSOs).
|
|
538
|
-
variantSet?: string,
|
|
539
|
-
// feat-20260609 M0 / T-002: passKind distinguishes forward (colour+DS)
|
|
540
|
-
// from shadow-caster passes in the PSO cache key. Defaults to
|
|
541
|
-
// 'forward' for backward compatibility (AC-06).
|
|
542
|
-
passKind?: PassKind,
|
|
543
|
-
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w16 (D-4):
|
|
544
|
-
// pbr-skin layoutKind reads its 6-attribute / 72-byte vertex buffer
|
|
545
|
-
// layout via `deriveVertexBufferLayout` (vertex-attribute-layout.ts SSOT).
|
|
546
|
-
// Callers with a resolved `MeshAsset.attributes` pass it through so the
|
|
547
|
-
// built PSO matches the mesh's real attribute set. Undefined falls
|
|
548
|
-
// through to the synthesized 6-key sentinel inside buildPipelineContext;
|
|
549
|
-
// non-skin layoutKinds ignore this parameter entirely.
|
|
550
|
-
meshAttributes?: import('@forgeax/engine-types').VertexAttributeMap,
|
|
551
|
-
// bug-20260615 M2 / m2-1: sampleCount drives the multisample descriptor
|
|
552
|
-
// field in the pipeline builder — it is a CAMERA fact (per-frame
|
|
553
|
-
// antialias setting). Default 1 preserves byte-identity of every
|
|
554
|
-
// pre-M2 cache slot + descriptor.
|
|
555
|
-
sampleCount?: number,
|
|
556
|
-
// feat-20260625-refactor-sprite-as-transparent-mesh R2 fix-up:
|
|
557
|
-
// LDR-color override for sub-passes whose attachment view is not the
|
|
558
|
-
// sRGB swap-chain view (LDR sprite split sub-pass writes through the
|
|
559
|
-
// storage / non-sRGB view of the same texture so premultiplied-alpha
|
|
560
|
-
// blends correctly). Threaded into the cache key + PSO descriptor.
|
|
561
|
-
// Undefined preserves the default sRGB view used by the geometry +
|
|
562
|
-
// tonemap + skybox callers. Ignored when `isHdr=true` or
|
|
563
|
-
// `passKind='shadow-caster'`.
|
|
564
|
-
colorFormatOverride?: GPUTextureFormat,
|
|
565
|
-
// Shader-declared UV count used by material pipeline cache aliases.
|
|
566
|
-
shaderUvSetCount?: number,
|
|
567
|
-
// `null` explicitly requests a color-only prepared-graphics pipeline;
|
|
568
|
-
// omitted keeps the normal forward material depth attachment.
|
|
569
|
-
depthFormatOverride?: GPUTextureFormat | null,
|
|
570
|
-
// Prepared graphics may select one of the host-owned canonical vertex
|
|
571
|
-
// layouts. Ordinary material callers leave this undefined.
|
|
572
|
-
vertexLayout?: string,
|
|
573
|
-
// Authored material GUID used to distinguish the publication-backed draw
|
|
574
|
-
// path from feature-only pipeline callers.
|
|
575
|
-
materialGuid?: string,
|
|
576
|
-
// Fragment entry point override for shaders with distinct color-domain
|
|
577
|
-
// entry points. Omitted keeps the shader's default `fs_main` entry.
|
|
578
|
-
fragmentEntry?: string,
|
|
579
|
-
/** Geometry-owned immutable projection carried to the renderer PSO seam. */
|
|
580
|
-
vertexLayoutProjection?: VertexLayoutProjection,
|
|
581
|
-
) => RenderPipeline | null;
|
|
582
|
-
readonly getMaterialShaderBindingContract?: (
|
|
583
|
-
materialShaderId: string,
|
|
584
|
-
) => 'group-0' | 'group-0-resource' | 'view-only' | 'view-with-resource' | 'render-material';
|
|
585
|
-
/**
|
|
586
|
-
* feat-20260527 M2 / w7: schema lookup for material param overlay.
|
|
587
|
-
* Returns the paramSchema for a registered material shader, or
|
|
588
|
-
* `undefined` when the shader is not in the ShaderRegistry.
|
|
589
|
-
* Record stage uses this instead of reading `MaterialSnapshot.paramSchema`.
|
|
590
|
-
*/
|
|
591
|
-
readonly getParamSchema?: (materialShaderId: string) => readonly ParamSchemaEntry[] | undefined;
|
|
592
|
-
/**
|
|
593
|
-
* Runtime ParamSchema projection owner lookup. Registered shader consumers
|
|
594
|
-
* must prefer this immutable projection over deriving the same schema again.
|
|
595
|
-
* The optional seam keeps feature-only and legacy test runtimes compatible.
|
|
596
|
-
*/
|
|
597
|
-
readonly getParamSchemaProjection?: (
|
|
598
|
-
materialShaderId: string,
|
|
599
|
-
) => ImmutableParamSchemaProjection | undefined;
|
|
600
|
-
/**
|
|
601
|
-
* feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1): the per-shader
|
|
602
|
-
* material BindGroupLayout for the given material shader. A custom shader
|
|
603
|
-
* declaring >3 user-region textures (e.g. parallax `heightTexture`) owns a
|
|
604
|
-
* material BGL whose entry count + injection start differ from the built-in
|
|
605
|
-
* 18-entry layout; the record stage must create the material bind group
|
|
606
|
-
* against THIS layout so the entry count matches. Returns `undefined` when
|
|
607
|
-
* the shader is unregistered or resolves to the shared built-in layout
|
|
608
|
-
* (3-texture shaders), in which case the caller falls back to
|
|
609
|
-
* `pipelineState.materialBindGroupLayout`.
|
|
610
|
-
*/
|
|
611
|
-
readonly getMaterialBindGroupLayout?: (materialShaderId: string) => BindGroupLayout | undefined;
|
|
612
|
-
/**
|
|
613
|
-
* feat-20260527-sprite-nineslice M4 / w16 (D-5): per-Renderer metrics counter
|
|
614
|
-
* surfaced through `renderer.metrics`. Record-stage soft-warns (e.g.
|
|
615
|
-
* `nineslice.scale-too-small` when Transform.scale falls below the four
|
|
616
|
-
* 9-slice corner anchors) call `runtime.metrics.increment(name)` instead of
|
|
617
|
-
* a per-entity `console.warn` so AI users observe machine-readable counters
|
|
618
|
-
* (charter P3 over P-flooded text logs). Each Renderer owns its own
|
|
619
|
-
* EngineMetrics instance (D-5 candidate 1: multi-Renderer isolation).
|
|
620
|
-
*/
|
|
621
|
-
readonly metrics: EngineMetrics;
|
|
622
|
-
/** Optional Render-owned membership timing capability. */
|
|
623
|
-
readonly membershipTiming?: MembershipTimingController | undefined;
|
|
624
|
-
/**
|
|
625
|
-
* feat-20260604-resource-owning-render-graph-and-fullscreen-postpr M3 / F-2 fix-up:
|
|
626
|
-
* post-process registry lookup. Returns the `PostProcessShaderEntry` registered
|
|
627
|
-
* via `renderer.postProcess.register(id, entry)`, or `undefined` when `id`
|
|
628
|
-
* resolves to no entry. `addFullscreenPass`'s execute closure dispatches on this
|
|
629
|
-
* to route to either the FXAA built-in path (id === 'fxaa') or the generic
|
|
630
|
-
* `buildFullscreenPostProcessPass + createFullscreenBindGroup` path; an
|
|
631
|
-
* unregistered id surfaces as a thrown `PostProcessError({code:'post-process-not-found'})`
|
|
632
|
-
* fail-fast (charter P3 / pipeline-errors throw template).
|
|
633
|
-
*
|
|
634
|
-
* Optional so a custom RenderSystemRuntime fixture (test doubles) need not
|
|
635
|
-
* supply it; the engine's own createRenderSystem populates it unconditionally.
|
|
636
|
-
*/
|
|
637
|
-
readonly lookupPostProcess?: (id: string) => PostProcessShaderEntry | undefined;
|
|
638
|
-
/**
|
|
639
|
-
* feat-20260621-fullscreen-post-process-per-frame-uniform-params-l M-A2 / w8:
|
|
640
|
-
* per-shader params UBO accessor. Returns the eager-created (at register time,
|
|
641
|
-
* M-A1 / w5) GPU buffer for `id`, or `undefined` when the id has no params
|
|
642
|
-
* (param-less consumer) / is unregistered. dispatchFullscreenPass reads this to
|
|
643
|
-
* writeBuffer the per-frame snapshot bytes and bind the buffer at group(1)
|
|
644
|
-
* binding(2). Optional so test fixtures need not supply it; createRenderSystem
|
|
645
|
-
* populates it unconditionally (mirrors lookupPostProcess / getPostProcessPipeline).
|
|
646
|
-
*/
|
|
647
|
-
readonly getPostProcessParamsBuffer?: (id: string) => Buffer | undefined;
|
|
648
|
-
/**
|
|
649
|
-
* feat-20260609-learn-render-4-5-framebuffers-demo-offscreen-rt-an M4 / T-10-a:
|
|
650
|
-
* post-process render-pipeline lookup with eager-on-first-call build. Returns a
|
|
651
|
-
* cached `RenderPipeline` (RHI handle) for `id`, lazily building on first call
|
|
652
|
-
* via the closure passed through `RenderSystemInternals.buildPostProcessPipeline`.
|
|
653
|
-
* Returns `null` when:
|
|
654
|
-
* - the id is not registered in the post-process registry (caller skips the
|
|
655
|
-
* pass — paired with the `'post-process-not-found'` throw site, this branch
|
|
656
|
-
* is unreachable on the happy path)
|
|
657
|
-
* - the underlying async shader-module compile is still pending (caller falls
|
|
658
|
-
* back for one frame and retries on the next; mirrors the
|
|
659
|
-
* `getMaterialShaderPipeline` 1-frame-warmup idiom)
|
|
660
|
-
* - the pipeline build itself fails (caller skips the pass; the build path
|
|
661
|
-
* fires a structured RhiError via errorRegistry; charter P3 explicit failure)
|
|
662
|
-
*
|
|
663
|
-
* Solves M1 CONCERN-1: the dispatcher in `dispatchFullscreenPass` previously
|
|
664
|
-
* passed `pipeline=null` to `built.createHandle` because the per-frame execute
|
|
665
|
-
* closure cannot await `createShaderModule` (async). With this lookup the
|
|
666
|
-
* dispatcher reads the pipeline synchronously; warm-up costs one frame.
|
|
667
|
-
*
|
|
668
|
-
* `bgl` is the fullscreen input-texture BGL the dispatcher already built via
|
|
669
|
-
* `buildFullscreenPostProcessPass`. The engine assumes a single static BGL
|
|
670
|
-
* shape (FULLSCREEN_BGL_DESCRIPTOR) so the pipeline cache keys on `id` only;
|
|
671
|
-
* subsequent calls reuse the cached pipeline regardless of which BGL identity
|
|
672
|
-
* is threaded.
|
|
673
|
-
*
|
|
674
|
-
* `colorFormat` is the swap-chain / target color format the post-process
|
|
675
|
-
* pipeline writes (`bgra8unorm-srgb` for swap-chain, the offscreen RT format
|
|
676
|
-
* otherwise). Cache key is `id|colorFormat`.
|
|
677
|
-
*
|
|
678
|
-
* Optional so a custom RenderSystemRuntime fixture (test doubles) need not
|
|
679
|
-
* supply it; the engine's own createRenderSystem populates it when
|
|
680
|
-
* `RenderSystemInternals.buildPostProcessPipeline` is provided.
|
|
681
|
-
*/
|
|
682
|
-
readonly getPostProcessPipeline?: (
|
|
683
|
-
id: string,
|
|
684
|
-
bgl: BindGroupLayout,
|
|
685
|
-
colorFormat: GPUTextureFormat,
|
|
686
|
-
) => RenderPipeline | null;
|
|
687
|
-
/**
|
|
688
|
-
* feat-20260623-world-space-video-asset M4 / w16 (D-3): transient per-frame
|
|
689
|
-
* texture store for VideoAsset sources. Independent of {@link gpuStore} — the
|
|
690
|
-
* record stage routes a `MaterialSnapshot.videoTextureFields` field through
|
|
691
|
-
* this store (per-frame copyExternalImageToTexture upload + current-frame view)
|
|
692
|
-
* instead of `gpuStore.ensureResident` (whose static cache video must never
|
|
693
|
-
* enter, AC-08). Optional so test fixtures that drive recordFrame manually
|
|
694
|
-
* need not supply one — a video field then degrades to the default view
|
|
695
|
-
* (charter P3). The production createRenderer always wires it.
|
|
696
|
-
*/
|
|
697
|
-
readonly dynamicTextureStore?: DynamicTextureStore | undefined;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
export interface RenderSystemInternals extends RenderSystemRuntime {
|
|
701
|
-
readonly canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
702
|
-
/** Optional feature host supplied by the renderer assembly layer. */
|
|
703
|
-
readonly featureHost?: RenderFeatureHost | undefined;
|
|
704
|
-
readonly shaderModuleFactory?: import('./pipeline-builder').PipelineBuilderShaderModuleFactory;
|
|
705
|
-
/** Optional host profiler capability for the existing Render path. */
|
|
706
|
-
readonly profiler?: Profiler | undefined;
|
|
707
|
-
// M6 / w41 (feat-20260510-rhi-resource-creation): the canvas context is the
|
|
708
|
-
// forgeax `RhiCanvasContext` brand; render-system-record.ts uses
|
|
709
|
-
// `getCurrentTexture()` -> `device.createTextureView(...)` (K-4 two-step
|
|
710
|
-
// explicit form, charter proposition 5 consistent abstraction red line).
|
|
711
|
-
readonly context: RhiCanvasContext | null;
|
|
712
|
-
// feat-20260608-create-app-param-surface-trim / M1 / AC-02: the legacy
|
|
713
|
-
// `clearColor` field is removed. The record stage reads clear color
|
|
714
|
-
// straight from the Camera SoA (`camera.clearColor`, array<f32,4> as of
|
|
715
|
-
// feat-20260709 M3); zero-Camera fallback uses
|
|
716
|
-
// `ZERO_CAMERA_CLEAR_FALLBACK = [0, 0, 0, 1]`.
|
|
717
|
-
readonly getPipelineState: () => PipelineState | null;
|
|
718
|
-
readonly assets: RenderAssetPort;
|
|
719
|
-
// feat-20260601-gpu-resource-store-extraction M1: GPU residency layer
|
|
720
|
-
// extracted out of AssetRegistry. Record stage reads GPU texture / cubemap /
|
|
721
|
-
// mesh handles through the store; CPU POD reads stay on `assets`.
|
|
722
|
-
readonly gpuStore: GpuResourceStore;
|
|
723
|
-
// feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M3 / T-M3-04:
|
|
724
|
-
// mesh-SSBO grow hook + read-only state surface threaded into the record
|
|
725
|
-
// stage so `ensureMeshSsboCapacity(internals, validatedOrdered.length)` can
|
|
726
|
-
// bridge the recordFrame → growController seam without leaking the full
|
|
727
|
-
// controller object into RenderSystemRuntime (the narrow ctx). Both fields
|
|
728
|
-
// are optional for test fixtures that wire createRenderSystem manually
|
|
729
|
-
// without a controller (legacy path returns ok:true and short-circuits);
|
|
730
|
-
// the production createRenderer always sets both. The `| undefined`
|
|
731
|
-
// suffix keeps both fields assignable from the WebGPU-internals carrier
|
|
732
|
-
// whose own field is `?:` -- exactOptionalPropertyTypes ABI compat.
|
|
733
|
-
readonly growMeshSsbo?:
|
|
734
|
-
| ((neededSlots: number) =>
|
|
735
|
-
| { readonly ok: true }
|
|
736
|
-
| {
|
|
737
|
-
readonly ok: false;
|
|
738
|
-
readonly code: 'mesh-ssbo-ceiling-reached' | 'mesh-ssbo-capacity-exceeded';
|
|
739
|
-
readonly degradedToSlotCount: number;
|
|
740
|
-
})
|
|
741
|
-
| undefined;
|
|
742
|
-
readonly meshSsboState?: { readonly slotCount: number } | undefined;
|
|
743
|
-
/**
|
|
744
|
-
* feat-20260609 M4 / T-10-a: factory backing `getPostProcessPipeline`. Provided
|
|
745
|
-
* by `createRenderer.ts` (it owns the shared `getShaderModuleAdapter` and the
|
|
746
|
-
* fullscreen pipeline-layout builder); receives a registered
|
|
747
|
-
* `PostProcessShaderEntry` + the fullscreen BGL the dispatcher already
|
|
748
|
-
* composed + the target color format, returns a built `RenderPipeline`
|
|
749
|
-
* (RHI handle) or `null` (shader still compiling / build failed). The first
|
|
750
|
-
* call per id triggers the async shader compile; subsequent calls hit the
|
|
751
|
-
* RenderSystem-level cache and never reach this factory.
|
|
752
|
-
*
|
|
753
|
-
* Optional so test fixtures (createRenderSystem with a stub
|
|
754
|
-
* RenderSystemInternals) need not provide a real factory; without it
|
|
755
|
-
* `getPostProcessPipeline` resolves to `undefined` on the runtime surface
|
|
756
|
-
* and the dispatcher falls through to its existing `null`-skip branch.
|
|
757
|
-
*/
|
|
758
|
-
readonly buildPostProcessPipeline?:
|
|
759
|
-
| ((
|
|
760
|
-
entry: PostProcessShaderEntry,
|
|
761
|
-
bgl: BindGroupLayout,
|
|
762
|
-
colorFormat: GPUTextureFormat,
|
|
763
|
-
label: string,
|
|
764
|
-
) => RenderPipeline | null)
|
|
765
|
-
| undefined;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
function isStructuredRendererError(
|
|
769
|
-
error: unknown,
|
|
770
|
-
): error is RhiError | RenderError | PostProcessErrorType {
|
|
376
|
+
function isStructuredRendererError(error: unknown): error is RhiError | RenderError {
|
|
771
377
|
if (!(error instanceof Error)) return false;
|
|
772
378
|
const structured = error as {
|
|
773
379
|
readonly code?: unknown;
|
|
@@ -805,55 +411,6 @@ function reportPreparedGraphicsCompletionError(
|
|
|
805
411
|
);
|
|
806
412
|
}
|
|
807
413
|
|
|
808
|
-
export interface RenderFeatureGraphRuntimeState {
|
|
809
|
-
contributions: readonly RenderFeatureGraphContribution<RenderFeaturePassContext>[];
|
|
810
|
-
topologySignature: string | undefined;
|
|
811
|
-
composition:
|
|
812
|
-
| RenderFeatureGraphComposition<RenderPipelineContext, RenderFeaturePassContext>
|
|
813
|
-
| undefined;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
const renderFeatureGraphStates = new WeakMap<
|
|
817
|
-
RenderSystemInternals,
|
|
818
|
-
RenderFeatureGraphRuntimeState
|
|
819
|
-
>();
|
|
820
|
-
|
|
821
|
-
export function getRenderFeatureGraphState(
|
|
822
|
-
internals: RenderSystemInternals,
|
|
823
|
-
): RenderFeatureGraphRuntimeState {
|
|
824
|
-
const existing = renderFeatureGraphStates.get(internals);
|
|
825
|
-
if (existing !== undefined) return existing;
|
|
826
|
-
const created: RenderFeatureGraphRuntimeState = {
|
|
827
|
-
contributions: [],
|
|
828
|
-
topologySignature: undefined,
|
|
829
|
-
composition: undefined,
|
|
830
|
-
};
|
|
831
|
-
renderFeatureGraphStates.set(internals, created);
|
|
832
|
-
return created;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
function resetRenderFeatureGraphState(internals: RenderSystemInternals): void {
|
|
836
|
-
const state = getRenderFeatureGraphState(internals);
|
|
837
|
-
state.contributions = [];
|
|
838
|
-
state.topologySignature = undefined;
|
|
839
|
-
state.composition = undefined;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
export function reportRenderFeatureGraphError(
|
|
843
|
-
internals: RenderSystemInternals,
|
|
844
|
-
error: RenderError,
|
|
845
|
-
): void {
|
|
846
|
-
if (internals.featureHost !== undefined && 'detail' in error) {
|
|
847
|
-
const detail = error.detail;
|
|
848
|
-
if (detail !== undefined && 'featureIdentity' in detail && 'order' in detail) {
|
|
849
|
-
const owned = internals.featureHost.recordError(detail.featureIdentity, error);
|
|
850
|
-
internals.errorRegistry.fire(owned);
|
|
851
|
-
return;
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
internals.errorRegistry.fire(error);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
414
|
function isPendingRenderFeaturePreparation(error: RenderError): boolean {
|
|
858
415
|
return (
|
|
859
416
|
error.code === 'render-feature-preparation-failed' &&
|
|
@@ -869,785 +426,272 @@ function makePreparedPipelinePendingError(): RhiError {
|
|
|
869
426
|
});
|
|
870
427
|
}
|
|
871
428
|
|
|
872
|
-
|
|
873
|
-
readonly
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
// both format and colorAttachmentFormat from storageBufferCapable
|
|
878
|
-
// (Channel 2 UA-preferred bgra8unorm / Channel 3 GLES fallback rgba8unorm);
|
|
879
|
-
// bug-20260612-webgpu-canvas-format-prefer-bgra supersedes the previous
|
|
880
|
-
// module-level SWAP_CHAIN_VIEW_FORMAT constant (bug-20260519 / bug-20260610).
|
|
881
|
-
// Equal to `format` for offscreen render targets.
|
|
882
|
-
readonly colorAttachmentFormat: string;
|
|
883
|
-
|
|
884
|
-
// Per-frame uniform / storage buffers + bind group layouts used to compose
|
|
885
|
-
// the 3 BindGroups (view / material / mesh-array) the pbr.wgsl pipeline
|
|
886
|
-
// expects (D-S2 + plan-strategy 1 architecture). The view + material UBOs
|
|
887
|
-
// and the mesh SSBO are reused across frames; only the contents are
|
|
888
|
-
// queue.writeBuffer-updated each draw([world], { cameraOwner: 0, resourceOwner: 0 }) invocation.
|
|
889
|
-
readonly viewBindGroupLayout: BindGroupLayout;
|
|
890
|
-
readonly materialBindGroupLayout: BindGroupLayout;
|
|
891
|
-
readonly meshBindGroupLayout: BindGroupLayout;
|
|
892
|
-
readonly viewUniformBuffer: Buffer;
|
|
893
|
-
// feat-20260613-csm-cascaded-shadow-maps M5 / w28: per-pass shadow_caster
|
|
894
|
-
// cascade-index UBO. 16 B (1 u32 + 12 B pad to satisfy the 16 B uniform
|
|
895
|
-
// buffer alignment). The host writes `index = i` (0..3) before each
|
|
896
|
-
// cascade's recordShadowPass submit; shadow_caster.wgsl reads it via
|
|
897
|
-
// `@group(0) @binding(5) shadowCasterCascade.index` to pick the matching
|
|
898
|
-
// `view.lightViewProj_X`. Forward shaders declare the binding (so the
|
|
899
|
-
// shared view BGL accommodates both pipelines) but never reference it.
|
|
900
|
-
readonly shadowCasterCascadeBuffer: Buffer;
|
|
901
|
-
// feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M2 / T-M2-06:
|
|
902
|
-
// materialUniformBuffer + meshStorageBuffer fields hold wrapper objects
|
|
903
|
-
// (`{ buffer, sizeInBytes }`) instead of bare `Buffer` handles. The outer
|
|
904
|
-
// wrapper-object identity is stable across grow events (research §F8 R1)
|
|
905
|
-
// so PipelineState references survive grow without re-bind cycles; the
|
|
906
|
-
// inner `.buffer` is replaced by `growMeshSsbo` (createRenderer.ts /
|
|
907
|
-
// T-M2-05). Consumers must read `.buffer` to reach the underlying
|
|
908
|
-
// `Buffer` handle (M3-04: `pipelineState.meshStorageBuffer.buffer` /
|
|
909
|
-
// `.materialUniformBuffer.buffer`); using the wrapper directly as a
|
|
910
|
-
// WeakMap chain key would bind the cache to the wrapper object identity
|
|
911
|
-
// (which never changes) instead of the inner buffer identity (which DOES
|
|
912
|
-
// change on grow), defeating the cache miss -> re-bind path (D-3
|
|
913
|
-
// grep-gate guards this).
|
|
914
|
-
readonly materialUniformBuffer: { readonly buffer: Buffer; readonly sizeInBytes: number };
|
|
915
|
-
readonly meshStorageBuffer: { readonly buffer: Buffer; readonly sizeInBytes: number };
|
|
916
|
-
// feat-20260519-light-casters-point-spot-pbr M3 / w20 (D-S1 + D-S2):
|
|
917
|
-
// PointLight + SpotLight std430 storage buffers occupy two new
|
|
918
|
-
// viewBindGroupLayout entries (binding 1 + 2) alongside the existing
|
|
919
|
-
// viewUniformBuffer (binding 0). Each buffer carries a 16 B std430 header
|
|
920
|
-
// (count u32 + 12 B pad) followed by a fixed 4-slot first-slice cap
|
|
921
|
-
// (32 B per PointLight / 48 B per SpotLight; total 144 B / 208 B). The
|
|
922
|
-
// record stage rewrites both buffers per frame via `queue.writeBuffer`
|
|
923
|
-
// (D-S6 per-frame full rewrite); the bind-group layout entries exist
|
|
924
|
-
// even when shaders do not yet reference them so the M4 pbr.wgsl
|
|
925
|
-
// multi-light helper landing requires no further createRenderer
|
|
926
|
-
// changes (charter P5 consistent abstraction; AC-04 c byte-frozen
|
|
927
|
-
// layout). Cap-gate at createRenderer time
|
|
928
|
-
// (`assertStorageBufferCap >= 4`) keeps the layout reachable before any
|
|
929
|
-
// frame records (R-4 + plan-strategy 4 risk table).
|
|
930
|
-
readonly pointLightsBuffer: Buffer;
|
|
931
|
-
readonly spotLightsBuffer: Buffer;
|
|
932
|
-
// feat-20260513-instanced-mesh M3 (T-M3-2 / T-M3-3): SSBO single-branch
|
|
933
|
-
// record-stage. `meshStorageBuffer` carries one `entity_world` mat4 per
|
|
934
|
-
// renderable (256-byte stride for dynamic-offset alignment); the
|
|
935
|
-
// `instancesBindGroupLayout` + `identityInstanceBuffer` form the second
|
|
936
|
-
// tier of the chained transform: shader composes
|
|
937
|
-
// `world = entity_world * instances_local[instance_index]`. When a
|
|
938
|
-
// renderable does not carry an `Instances` component the record stage
|
|
939
|
-
// binds the shared 1-element identity-mat4 fallback (`drawIndexed
|
|
940
|
-
// instanceCount=1`); when it does, the per-entity GPU storage buffer
|
|
941
|
-
// owned by the record stage's `frameState.instanceBuffers` cache is
|
|
942
|
-
// bound (`drawIndexed instanceCount=Instances.transforms.count / 16`).
|
|
943
|
-
// feat-20260514 M3 / w15: the legacy
|
|
944
|
-
// `AssetRegistry.createInstancedBuffer` triplet is gone — Instances data
|
|
945
|
-
// flows entirely through the ECS-managed `array<f32>` field; the record
|
|
946
|
-
// stage owns GPU storage buffer allocation + `LimitExceededDetail`
|
|
947
|
-
// emit + `caps.storageBuffer` cap-gate.
|
|
948
|
-
readonly instancesBindGroupLayout: BindGroupLayout;
|
|
949
|
-
readonly identityInstanceBuffer: Buffer;
|
|
950
|
-
|
|
951
|
-
// feat-20260515 M3 / T-M3-05 (research F-6 fix): default sampler +
|
|
952
|
-
// fallback 1x1 white textureView seed the materialBindGroup binding=1 /
|
|
953
|
-
// binding=2 entries when MaterialAsset.baseColorTexture is undefined.
|
|
954
|
-
// The defaults follow research F-5 SSOT (linear min/mag/mipmap +
|
|
955
|
-
// repeat) so AI users that don't supply a texture still get a sensible
|
|
956
|
-
// sampling configuration without having to register a SamplerAsset
|
|
957
|
-
// (charter F2 minimal surface).
|
|
958
|
-
readonly defaultSampler: Sampler;
|
|
959
|
-
// Nearest-neighbour (mag=nearest, min=nearest, mip=nearest, clamp-to-edge)
|
|
960
|
-
// sampler used for sprite / tilemap materials so pixel-art tiles render sharp.
|
|
961
|
-
readonly nearestSampler: Sampler;
|
|
962
|
-
readonly fallbackTextureView: TextureView;
|
|
963
|
-
|
|
964
|
-
// bug-20260519: BUILTIN_CUBE / BUILTIN_TRIANGLE migrated to 12-floats
|
|
965
|
-
// (position + normal + uv + tangent) so a single (`unlit-procedural` /
|
|
966
|
-
// `standard`) pipeline pair handles all renderables. The legacy
|
|
967
|
-
// `unlitBuiltinPipeline` + zero-stride `unlitBuiltinDummyAttrBuffer`
|
|
968
|
-
// (which hard-coded uv = (0,0) for BUILTIN cubes) are deleted.
|
|
969
|
-
//
|
|
970
|
-
// - `unlitPipeline` : unlit module + 12F vertex stride.
|
|
971
|
-
// Routed to every entity whose
|
|
972
|
-
// shader identity is `forgeax::default-unlit`.
|
|
973
|
-
// - `standardPipeline` : pbr module + 12F vertex stride.
|
|
974
|
-
// Routed to every entity carrying a
|
|
975
|
-
// standard / GGX-PBR material.
|
|
976
|
-
//
|
|
977
|
-
// Both pipelines share the same 4-BindGroupLayout chain (view + material +
|
|
978
|
-
// mesh-array + instances). The record stage selects between them on
|
|
979
|
-
// `mat.materialShaderId` only (D-2 BUILTIN-vs-procedural distinction retired).
|
|
980
|
-
// bug-20260519 D-3: nullable when manifest carries zero entries (Camera-
|
|
981
|
-
// only / clear-pass-only path; D-1 + D-2 + plan-strategy section 1
|
|
982
|
-
// mermaid). The render-time access point in `render-system-record.ts`
|
|
983
|
-
// narrows on `=== null` and fires a structured `RhiError
|
|
984
|
-
// shader-compile-failed` (charter P3 explicit failure; AC-03).
|
|
985
|
-
readonly unlitPipeline: RenderPipeline | null;
|
|
986
|
-
readonly standardPipeline: RenderPipeline | null;
|
|
987
|
-
/**
|
|
988
|
-
* feat-20260604-learn-render-4.10-anti-aliasing-msaa M2 / w8: count=4
|
|
989
|
-
* multisample variants of the 7 geometry pipelines that write the screen
|
|
990
|
-
* or HDR colour target. A pipeline's `multisample.count` must match the
|
|
991
|
-
* sampleCount of the colour attachment it writes (WebGPU validation),
|
|
992
|
-
* so a count=4 pass cannot reuse a count=1 pipeline -- the record stage
|
|
993
|
-
* picks the `*Msaa` variant when the active camera carries
|
|
994
|
-
* `antialias === 'msaa'`. Post-process + shadow pipelines stay single-
|
|
995
|
-
* sample (AC-06) and have no MSAA variant. `null` when the base pipeline
|
|
996
|
-
* is null (empty-manifest path).
|
|
997
|
-
*/
|
|
998
|
-
readonly unlitPipelineMsaa: RenderPipeline | null;
|
|
999
|
-
readonly standardPipelineMsaa: RenderPipeline | null;
|
|
1000
|
-
// feat-20260625-refactor-sprite-as-transparent-mesh M3 / w14 (D-7 / AC-12):
|
|
1001
|
-
// the four sprite-dedicated PSO fields are gone — sprite PSO now flows
|
|
1002
|
-
// through `runtime.getMaterialShaderPipeline('forgeax::sprite', ...)`
|
|
1003
|
-
// keyed on the premultiplied-alpha renderState the record stage
|
|
1004
|
-
// assembles for any transparent material (plan-strategy D-7). Concept
|
|
1005
|
-
// count -4 fields on PipelineState; sprite's "is a transparent mesh"
|
|
1006
|
-
// story is told entirely by the generic cache + `material.transparent`
|
|
1007
|
-
// (derived by the extract stage from `passes[0].renderState.blend !==
|
|
1008
|
-
// undefined`, the post-feat-20260626-collapse SSOT).
|
|
1009
|
-
readonly unlitPipelineHdrMsaa: RenderPipeline | null;
|
|
1010
|
-
readonly standardPipelineHdrMsaa: RenderPipeline | null;
|
|
1011
|
-
/**
|
|
1012
|
-
* feat-20260523-shader-template-instance-split M9-T03 (D-PipelineBuilder):
|
|
1013
|
-
* the shared 4-BindGroupLayout chain pipeline layout (`[view, material,
|
|
1014
|
-
* mesh-array, instances]`) -- same handle the standard / unlit / sprite
|
|
1015
|
-
* pipelines share. M9 exposes it on PipelineState so the per-MaterialShader
|
|
1016
|
-
* pipeline cache (`getMaterialShaderPipeline`) can reuse it at lazy build
|
|
1017
|
-
* time without re-running pbrLayouts construction (charter P4 consistent
|
|
1018
|
-
* abstraction). `null` when the manifest is empty (Camera-only / clear-
|
|
1019
|
-
* pass-only path; bug-20260519 D-3 nullable).
|
|
1020
|
-
*/
|
|
1021
|
-
readonly pbrPipelineLayout: PipelineLayout | null;
|
|
1022
|
-
/**
|
|
1023
|
-
* feat-20260609-hdrp-cluster-fragment-ggx M4.5 / w36 (D-10 option A):
|
|
1024
|
-
* per-variant PipelineLayout for HDRP. Mirrors `pbrPipelineLayout` but
|
|
1025
|
-
* substitutes the HDRP unified 7-slot group(2) BGL
|
|
1026
|
-
* (`createHdrpBindGroupLayoutDescriptor`) for the 1-slot pbr-mesh-array
|
|
1027
|
-
* BGL. Built once at boot and reused for any HDRP variant PSO. `null`
|
|
1028
|
-
* when the manifest is empty (parallel to `pbrPipelineLayout`) or when
|
|
1029
|
-
* the boot-time createBindGroupLayout / createPipelineLayout fails (in
|
|
1030
|
-
* which case `selectPipelineLayoutForVariant` falls back to the URP
|
|
1031
|
-
* `pbrPipelineLayout` instead of hard-disabling the build path).
|
|
1032
|
-
*/
|
|
1033
|
-
readonly hdrpPbrPipelineLayout: PipelineLayout | null;
|
|
1034
|
-
/** WebGPU storage-path producer for ordered HDRP cluster memberships. */
|
|
1035
|
-
readonly hdrpClusterMembershipPipeline: ComputePipeline | null;
|
|
1036
|
-
/** Compute bind-group layout paired with the membership producer pipeline. */
|
|
1037
|
-
readonly hdrpClusterMembershipBindGroupLayout: BindGroupLayout | null;
|
|
1038
|
-
/**
|
|
1039
|
-
* bug-20260611-skin-pipeline-layout: dedicated PipelineLayout for the
|
|
1040
|
-
* `forgeax::pbr-skin` material shader. Mirrors `pbrPipelineLayout` but
|
|
1041
|
-
* substitutes a 3-entry mesh-array BGL (binding 0 = meshes, bindings 1/2 =
|
|
1042
|
-
* current/previous palettes) for standard PBR's 1-entry mesh-array BGL. Built once at boot
|
|
1043
|
-
* by `buildPbrSkinLayouts` and selected by
|
|
1044
|
-
* `selectPipelineLayoutForVariant` when the caller passes
|
|
1045
|
-
* `LayoutKind = 'pbr-skin'`. `null` when manifest is empty or the skin
|
|
1046
|
-
* shader is not registered (storage-buffer cap gate / register failure);
|
|
1047
|
-
* `selectPipelineLayoutForVariant` returns `null` rather than silently
|
|
1048
|
-
* falling back to URP layout (charter P3 explicit failure, mirroring the
|
|
1049
|
-
* `hdrp-active-must-not-fallback-to-urp-pipeline` guard).
|
|
1050
|
-
*/
|
|
1051
|
-
readonly pbrSkinPipelineLayout: PipelineLayout | null;
|
|
1052
|
-
/**
|
|
1053
|
-
* feat-20260611 R2 / M8 / w28 (IS-14): record-stage handle to the
|
|
1054
|
-
* 3-binding `pbr-skin-mesh-array-bgl` returned by `buildPbrSkinLayouts`
|
|
1055
|
-
* (binding 0 mesh-array UBO, bindings 1/2 current/previous palette UBOs;
|
|
1056
|
-
* all `hasDynamicOffset: true`). The record stage builds a BG against this
|
|
1057
|
-
* BGL for skin entries (`entry.source.skin !== undefined`) and binds it
|
|
1058
|
-
* at group(2) when the entry's pipeline layout is `pbr-skin-pl`. The
|
|
1059
|
-
* URP path keeps using `meshBindGroupLayout` (1-binding) at group(2),
|
|
1060
|
-
* so `pbr-mesh-array-bgl` is unchanged. Stays `null` when
|
|
1061
|
-
* `pbrSkinPipelineLayout` itself failed to build (charter P3 explicit
|
|
1062
|
-
* failure -- skin path is hard-disabled, mirroring the pipeline-layout
|
|
1063
|
-
* fallback in `selectPipelineLayoutForVariant`).
|
|
1064
|
-
*/
|
|
1065
|
-
readonly pbrSkinMeshBindGroupLayout: BindGroupLayout | null;
|
|
1066
|
-
/**
|
|
1067
|
-
* feat-20260612-skin-palette-per-frame-upload M1 / m1-2: animator-ready
|
|
1068
|
-
* skin-palette storage allocator. Replaces the prior identity-buffer
|
|
1069
|
-
* stub (PR #353 / feat-20260611 R2 / M8 / w28 IS-14) -- a single 16320 B
|
|
1070
|
-
* identity-seeded UBO shared by every skin entry. The allocator owns the
|
|
1071
|
-
* lifecycle of the per-frame palette buffer (`createSkinPaletteAllocator`
|
|
1072
|
-
* from `./systems/skin-palette-allocator`); the record stage reads the
|
|
1073
|
-
* GPU buffer through `skinPaletteAllocator.buffer` and the extract stage
|
|
1074
|
-
* (M2) calls `allocateSlice` + `writeJointPalette` to land animated
|
|
1075
|
-
* palette data per skinned entity. Per plan-strategy D-1 candidate (b) the
|
|
1076
|
-
* allocator is the single authoritative carrier -- no parallel fallback
|
|
1077
|
-
* stub. `null` when `pbrSkinPipelineLayout` itself failed (skin path
|
|
1078
|
-
* hard-disabled, mirroring the prior stub field's gating).
|
|
1079
|
-
*/
|
|
1080
|
-
readonly skinPaletteAllocator: SkinPaletteAllocator | null;
|
|
1081
|
-
// 1x1 white texture view (feat-20260518 M3 / w13 + AC-06): seeds the
|
|
1082
|
-
// baseColorTexture (binding 2) and metallicRoughnessTexture (binding 4)
|
|
1083
|
-
// entries when the schema-driven MaterialAsset values omit the optional textures.
|
|
1084
|
-
// Reuses the same sampling-friendly default as `fallbackTextureView`
|
|
1085
|
-
// (above); the alias keeps the new MVP code path declarative without
|
|
1086
|
-
// renaming the existing field. Note: normalTexture (binding 6) does
|
|
1087
|
-
// NOT use this view -- it has its own RG-encoded fallback below.
|
|
1088
|
-
readonly defaultWhiteTextureView: TextureView;
|
|
1089
|
-
// 1x1 RGBA8 (128,128,255,255) view dedicated to the normalTexture slot
|
|
1090
|
-
// (binding 6). pbr.wgsl decodes sample.rg * 2 - 1 + z = sqrt(1-x^2-y^2),
|
|
1091
|
-
// so RG=(128,128)=0.5 maps to tangent (0,0,1) -- zero perturbation when
|
|
1092
|
-
// normalTexture is absent. White (255,255) would yield sqrt(1-2)=NaN
|
|
1093
|
-
// under saturate (clamps to 0) -> tangent.z=0 -> N rotated into the
|
|
1094
|
-
// tangent plane (severely wrong). Separate view because baseColor /
|
|
1095
|
-
// metallicRoughness require white-on-missing, normal requires (0,0,1).
|
|
1096
|
-
readonly defaultNormalTextureView: TextureView;
|
|
1097
|
-
|
|
1098
|
-
// ── feat-20260519-tonemap-reinhard-mvp / M2 / T-M2.5 ──────────────────────
|
|
1099
|
-
//
|
|
1100
|
-
// HDR (rgba16float) variants of `unlitPipeline` / `standardPipeline`. AI
|
|
1101
|
-
// users opt in via `Camera.tonemap === 'reinhard-extended'`; the record
|
|
1102
|
-
// stage picks `unlitPipelineHdr` / `standardPipelineHdr` when the active
|
|
1103
|
-
// camera's tonemap field is non-'none', and writes the geometry pass into
|
|
1104
|
-
// `hdrColorView` instead of the swap-chain view. The post-process tonemap
|
|
1105
|
-
// pass then reads `hdrColorView` and writes the final LDR pixels into the
|
|
1106
|
-
// swap-chain view (charter P3 explicit failure: a wrong format combination
|
|
1107
|
-
// would otherwise silently produce mis-tonemapped pixels).
|
|
1108
|
-
//
|
|
1109
|
-
// Both HDR pipelines share the same 4-BindGroupLayout chain as their
|
|
1110
|
-
// sRGB siblings — only the colour-attachment format differs (D-2 +
|
|
1111
|
-
// plan-strategy D-3).
|
|
1112
|
-
//
|
|
1113
|
-
// bug-20260519 D-3 nullable extension: when the renderer starts with a
|
|
1114
|
-
// zero-entry shader manifest the entire HDR + tonemap-resource block in
|
|
1115
|
-
// `createRenderer.ts` is skipped (Camera-only / clear-pass-only path),
|
|
1116
|
-
// so these fields stay `null`. Record-stage HDR pipeline dispatch +
|
|
1117
|
-
// fullscreen tonemap pass narrow on `=== null` and fire structured
|
|
1118
|
-
// `shader-compile-failed` (charter P3 explicit failure; AC-03).
|
|
1119
|
-
readonly unlitPipelineHdr: RenderPipeline | null;
|
|
1120
|
-
readonly standardPipelineHdr: RenderPipeline | null;
|
|
1121
|
-
|
|
1122
|
-
/**
|
|
1123
|
-
* feat-20260520-directional-light-shadow-mapping M2 / w14 (D-1):
|
|
1124
|
-
* 1x1 depth32float fallback bound at viewBindGroup binding(3) when no
|
|
1125
|
-
* shadow RT exists (castShadow:false or allocation failed).
|
|
1126
|
-
* Cleared to 1.0 (far plane) so comparison-sampler always returns fully lit.
|
|
1127
|
-
*/
|
|
1128
|
-
readonly shadowFallbackTextureView: TextureView;
|
|
1129
|
-
|
|
1130
|
-
/**
|
|
1131
|
-
* feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1: 1x1x6
|
|
1132
|
-
* depth32float `texture_depth_cube_array` (layers=1) fallback bound at
|
|
1133
|
-
* viewBindGroup binding(5) when no PointLightShadow snapshots are active
|
|
1134
|
-
* (zero-shadow scene = AC-09 zero allocation -- the real cube_array atlas
|
|
1135
|
-
* stays in `ShadowAtlas` and is only allocated when the extract stage
|
|
1136
|
-
* sees its first PointLightShadow). Cleared to 1.0 so the
|
|
1137
|
-
* comparison-sampler returns fully lit on the no-shadow path. The view
|
|
1138
|
-
* dimension is `cube-array` to satisfy the BGL `viewDimension:
|
|
1139
|
-
* 'cube-array'`; the underlying texture has 6 layers (one cube).
|
|
1140
|
-
*/
|
|
1141
|
-
readonly shadowAtlasFallbackTextureView: TextureView;
|
|
1142
|
-
|
|
1143
|
-
/**
|
|
1144
|
-
* feat-20260612-point-light-shadows-urp-hdrp Round-2 F-1: 64 B uniform
|
|
1145
|
-
* buffer carrying `array<vec4<f32>, 4>` -- one lane per shadow-casting
|
|
1146
|
-
* point light slot. Lane N stores `(near, far, 1/(far-near), 0)` for the
|
|
1147
|
-
* point light with `shadowAtlasLayer === N`. Lanes for non-shadow-casting
|
|
1148
|
-
* slots stay zeroed; the WGSL sample path is gated by
|
|
1149
|
-
* `PointLight.shadowAtlasLayer >= 0` so zeroed lanes are never read.
|
|
1150
|
-
* Bound at viewBindGroup binding(6); written per frame from the
|
|
1151
|
-
* `pointShadowSnapshots` array.
|
|
1152
|
-
*/
|
|
1153
|
-
readonly shadowParamsBuffer: Buffer;
|
|
1154
|
-
|
|
1155
|
-
// feat-20260625-spot-light-shadow-mapping w25 (scope-amend webkit-fallback):
|
|
1156
|
-
// the per-spot fragment-read perspective lightViewProj matrices no longer
|
|
1157
|
-
// occupy a dedicated `spotLightViewProjBuffer` / view-BG binding 9 — they fold
|
|
1158
|
-
// into the View UBO tail (`view.spotLightViewProj`, bytes 528..784) written by
|
|
1159
|
-
// render-system-record's viewPayload. The standalone uniform buffer pushed the
|
|
1160
|
-
// WebGL2 fallback fragment uniform-buffer count to 12, over GLES 3.0's
|
|
1161
|
-
// `max_uniform_buffers_per_shader_stage = 11`. Caster vertex channel (binding
|
|
1162
|
-
// 7) is untouched: same-frame write contention only applies to casters (D-1).
|
|
1163
|
-
|
|
1164
|
-
// ── feat-20260520-directional-light-shadow-mapping M2 / w15 (AC-12) ─────
|
|
1165
|
-
//
|
|
1166
|
-
// GPU shadow-factor probe pipeline. `debugSampleShadowFactor` packs N world
|
|
1167
|
-
// positions into `shadowProbeInputBuf` (`array<vec4<f32>, PROBE_MAX_COUNT>`),
|
|
1168
|
-
// writes the active lightSpaceMatrix into `shadowProbeLsmUbo`, builds a
|
|
1169
|
-
// transient BindGroup binding the active shadow texture + comparison
|
|
1170
|
-
// sampler, and renders 1 pixel per probe into `shadowProbeOutputTex`
|
|
1171
|
-
// (1xN rgba32float). The fragment stage runs the same atlas-depth PCF path
|
|
1172
|
-
// as `pbr.wgsl::evalDirectional()` and returns factor, center depth,
|
|
1173
|
-
// cascade index, and receiver depth in the four channels. The 1-row staging
|
|
1174
|
-
// buffer (1024 B = PROBE_MAX_COUNT * 16 B; already 256B-aligned) is mapped
|
|
1175
|
-
// and the leading N vec4 records returned. Compiled once at pipeline build
|
|
1176
|
-
// time; null when the shader manifest is empty (no probe possible).
|
|
1177
|
-
readonly shadowProbePipeline: RenderPipeline | null;
|
|
1178
|
-
readonly shadowProbeBindGroupLayout: BindGroupLayout | null;
|
|
1179
|
-
readonly shadowProbeLsmUbo: Buffer | null;
|
|
1180
|
-
readonly shadowProbeInputBuf: Buffer | null;
|
|
1181
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle (matches sibling fields)
|
|
1182
|
-
readonly shadowProbeOutputTex: any | null;
|
|
1183
|
-
readonly shadowProbeOutputView: TextureView | null;
|
|
1184
|
-
readonly shadowProbeStagingBuf: Buffer | null;
|
|
1185
|
-
|
|
1186
|
-
/**
|
|
1187
|
-
* Fallback Skylight resource bundle (feat-20260520-skylight-ibl-cubemap
|
|
1188
|
-
* M2 round-2 / t40, plan-strategy D-5). 1x1 all-zero rgba16float
|
|
1189
|
-
* texture_cube * 2 (irradiance + prefilter) + 1x1 approximate rg16float
|
|
1190
|
-
* brdfLut + intensity=0 16 B uniform buffer + 7-entry BindGroup compose
|
|
1191
|
-
* the @group(4) skylight binding for the "no Skylight ECS entity"
|
|
1192
|
-
* branch. The M4 round-2 recordFrame branch binds `bindGroup` when
|
|
1193
|
-
* `skylightCount === 0` so `standardPipeline` / `standardPipelineHdr`
|
|
1194
|
-
* (which keep a 4-slot pipeline layout per D-5 round-4) dispatch with
|
|
1195
|
-
* ambient=0 -- physical convergence with D-4 (charter F1: AI users
|
|
1196
|
-
* writing demos do not need a "is there a skylight?" branch).
|
|
1197
|
-
*
|
|
1198
|
-
* D-5 round-4: the fallback bundle no longer carries a stand-alone
|
|
1199
|
-
* BindGroup. The PBR material BindGroupLayout now holds 14 entries
|
|
1200
|
-
* (material 0..6 + Skylight 7..13) and the per-frame material
|
|
1201
|
-
* BindGroup assembly site (render-system-record) merges Skylight
|
|
1202
|
-
* resources at binding 7..13 -- active when a Skylight component
|
|
1203
|
-
* exists, fallback identity when `skylightCount === 0`.
|
|
1204
|
-
*
|
|
1205
|
-
* bug-20260519 D-3 nullable extension: the empty-manifest path
|
|
1206
|
-
* (Camera-only / clear-pass-only) skips the entire PBR pipeline build
|
|
1207
|
-
* block; we still allocate the fallback bundle in the normal path,
|
|
1208
|
-
* but tests / OOS paths that mock the manifest empty land here with
|
|
1209
|
-
* `null` (charter P3 explicit failure: M4 record-stage gates on
|
|
1210
|
-
* `skylightFallback !== null` before binding fallback resources at
|
|
1211
|
-
* material BG entries 7..13).
|
|
1212
|
-
*/
|
|
1213
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque SkylightFallback shape
|
|
1214
|
-
readonly skylightFallback: any | null;
|
|
1215
|
-
|
|
1216
|
-
/**
|
|
1217
|
-
* feat-20260529-rendergraph-pass-abstraction M3 / w11 (D-2 + Finding 3):
|
|
1218
|
-
* per-pass mutable texture cache slots extracted from the global
|
|
1219
|
-
* PipelineState. These fields are the lazily-allocated texture attachments
|
|
1220
|
-
* + pass-specific pipelines and bindgroups that belong to a single render
|
|
1221
|
-
* pass (depth / shadow / tonemap / FXAA). M4 render-graph resource
|
|
1222
|
-
* declarations will read these descriptors to derive transient/persistent
|
|
1223
|
-
* resources; shadow probe (OOS-4) stays on PipelineState directly.
|
|
1224
|
-
*/
|
|
1225
|
-
readonly perPassResources: PerPassResources;
|
|
1226
|
-
}
|
|
429
|
+
type PointsLinesGpuResources = {
|
|
430
|
+
readonly vertexBuffer: Buffer;
|
|
431
|
+
readonly indexBuffer: Buffer;
|
|
432
|
+
};
|
|
433
|
+
type PointsLinesPreparationAdapter = PointsLinesLanePreparationAdapter<PointsLinesGpuResources>;
|
|
1227
434
|
|
|
1228
435
|
/**
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1232
|
-
*
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1236
|
-
* lazy first-frame path (ensureContextConfigured in createRenderer.ts) and the
|
|
1237
|
-
* F2 surface-outdated reconfigure-and-retry branch (render-system-record.ts) so
|
|
1238
|
-
* the two cannot drift (architecture-principles #1 SSOT). Returns the configure
|
|
1239
|
-
* Result; callers own the `state.perPassResources.configured` flag + the
|
|
1240
|
-
* `__forgeaxSwapChainFormat` probe write (those differ per call site).
|
|
436
|
+
* The Standard renderer's single Points/Lines prepare owner.
|
|
437
|
+
*
|
|
438
|
+
* The retained snapshot is the identity boundary; this owner only resolves
|
|
439
|
+
* the source assets, runs admission, and publishes the existing preparation
|
|
440
|
+
* and record contracts to the main geometry loop. The expansion cache is
|
|
441
|
+
* shared across lanes and this owner keeps the one prepared vertex/index
|
|
442
|
+
* resource pair for each retained identity.
|
|
1241
443
|
*/
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
backendKind,
|
|
1252
|
-
format as GPUTextureFormat,
|
|
1253
|
-
colorAttachmentFormat as GPUTextureFormat,
|
|
1254
|
-
);
|
|
1255
|
-
const supportsTextureBinding = device.caps?.storageBuffer ?? true;
|
|
1256
|
-
return context.configure({
|
|
1257
|
-
device,
|
|
1258
|
-
format: surfaceFormats.storage,
|
|
1259
|
-
alphaMode: isWebGl2 ? 'opaque' : 'premultiplied',
|
|
1260
|
-
usage:
|
|
1261
|
-
GPU_TEXTURE_USAGE_RENDER_ATTACHMENT |
|
|
1262
|
-
(supportsTextureBinding ? GPU_TEXTURE_USAGE_TEXTURE_BINDING : 0) |
|
|
1263
|
-
(isWebGl2 ? 0 : GPU_TEXTURE_USAGE_COPY_SRC),
|
|
1264
|
-
...(!isWebGl2 ? { viewFormats: [surfaceFormats.view] } : {}),
|
|
444
|
+
class StandardPointsLinesOwner implements PointsLinesRecordOwner {
|
|
445
|
+
private readonly cache = new PointsLinesExpansionCache();
|
|
446
|
+
private readonly preparations = new Map<string, PointsLinesPreparationAdapter>();
|
|
447
|
+
private readonly active = new Map<string, PointsLinesInspection>();
|
|
448
|
+
private readonly layoutProjection = deriveVertexLayoutProjection({
|
|
449
|
+
position: new Float32Array(0),
|
|
450
|
+
normal: new Float32Array(0),
|
|
451
|
+
uv: new Float32Array(0),
|
|
452
|
+
tangent: new Float32Array(0),
|
|
1265
453
|
});
|
|
1266
|
-
}
|
|
1267
454
|
|
|
1268
|
-
|
|
1269
|
-
* feat-20260529-rendergraph-pass-abstraction M3 / w11 (D-2):
|
|
1270
|
-
* per-pass mutable resource slots extracted from PipelineState per
|
|
1271
|
-
* research Finding 3 fact-based grouping (depth / shadow / tonemap / fxaa).
|
|
1272
|
-
* These fields are the lazily-allocated texture caches + pass-specific
|
|
1273
|
-
* pipeline/bindgroup handles that belong to a single render pass.
|
|
1274
|
-
* Mutable (non-readonly) because the record stage writes back after
|
|
1275
|
-
* lazy-alloc on size drift.
|
|
1276
|
-
*/
|
|
1277
|
-
export interface PerPassResources {
|
|
1278
|
-
/**
|
|
1279
|
-
* Per-frame depth attachment. Lazily allocated by
|
|
1280
|
-
* `ensureContextConfigured` and recreated whenever the canvas resizes
|
|
1281
|
-
* (see `ensureDepthTexture` in createRenderer.ts). Bound at the
|
|
1282
|
-
* `beginRenderPass.depthStencilAttachment` slot with `depthLoadOp:'clear'`
|
|
1283
|
-
* + `clearValue:1` so each frame starts with a far-plane depth buffer
|
|
1284
|
-
* and the back-face / inside-of-cube fragments are correctly occluded
|
|
1285
|
-
* (bug-20260519).
|
|
1286
|
-
*
|
|
1287
|
-
* `depthTextureView` is `null` until the canvas dimensions are known
|
|
1288
|
-
* (first draw); the record stage skips the frame and fires
|
|
1289
|
-
* `webgpu-runtime-error` if it stays null past configure.
|
|
1290
|
-
*/
|
|
1291
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle (matches sibling fields)
|
|
1292
|
-
depthTexture: any | null;
|
|
1293
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1294
|
-
depthTextureView: any | null;
|
|
1295
|
-
depthTextureWidth: number;
|
|
1296
|
-
depthTextureHeight: number;
|
|
1297
|
-
configured: boolean;
|
|
1298
|
-
|
|
1299
|
-
// ── feat-20260519-tonemap-reinhard-mvp / M2 / T-M2.5 ──────────────────────
|
|
1300
|
-
// feat-20260621 M-A3 (D-5): the dedicated tonemap pipeline / BGL / sampler /
|
|
1301
|
-
// params-UBO fields are deleted — the built-in tonemap now flows through the
|
|
1302
|
-
// unified fullscreen post-process channel (`postProcess.register(
|
|
1303
|
-
// 'forgeax::tonemap', { source, params })` + the per-frame PostProcessParams
|
|
1304
|
-
// channel). dispatchFullscreenPass owns the pipeline (getPostProcessPipeline
|
|
1305
|
-
// cache), the BGL + sampler (buildFullscreenPostProcessPass), and the params
|
|
1306
|
-
// UBO (eager-created at register). Only the HDR colour/depth attachments below
|
|
1307
|
-
// remain here — they belong to the geometry pass, not the tonemap pass.
|
|
1308
|
-
|
|
1309
|
-
/**
|
|
1310
|
-
* Lazy HDR colour + depth attachments for the opt-in tonemap path. Both
|
|
1311
|
-
* are `null` until the first frame whose active camera carries
|
|
1312
|
-
* `tonemap !== 'none'`; the record stage allocates them at the swap-chain
|
|
1313
|
-
* size and re-creates on resize (mirrors `depthTexture` / `depthTextureView`
|
|
1314
|
-
* idiom; AC-12).
|
|
1315
|
-
*
|
|
1316
|
-
* `hdrColor`'s format is `rgba16float` (AC-03(d)); the depth attachment is
|
|
1317
|
-
* the same `depth24plus-stencil8` format the geometry pipelines already declare
|
|
1318
|
-
* (`DEPTH_TEXTURE_FORMAT` SSOT in `createRenderer.ts`). When a camera
|
|
1319
|
-
* downgrades to `tonemap === 'none'` between frames the textures stay
|
|
1320
|
-
* allocated for cheap re-opt-in; full dispose happens via
|
|
1321
|
-
* `Renderer.dispose()` -> browser GC (matches the `depthTexture` fix-f6
|
|
1322
|
-
* placeholder).
|
|
1323
|
-
*/
|
|
1324
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle (matches sibling fields)
|
|
1325
|
-
hdrColorTexture: any | null;
|
|
1326
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1327
|
-
hdrColorView: any | null;
|
|
1328
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1329
|
-
hdrDepthTexture: any | null;
|
|
1330
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1331
|
-
hdrDepthView: any | null;
|
|
1332
|
-
hdrTextureWidth: number;
|
|
1333
|
-
hdrTextureHeight: number;
|
|
1334
|
-
/**
|
|
1335
|
-
* feat-20260604 M2 / w10: sampleCount the `hdrDepth` attachment was last
|
|
1336
|
-
* allocated at (1 or 4). The HDR colour target is always sampled
|
|
1337
|
-
* single-sample (`hdrColorView`); the depth pairs with the geometry colour
|
|
1338
|
-
* target's sampleCount, which flips with `antialias === 'msaa'`. Tracking it
|
|
1339
|
-
* lets the record stage reallocate depth when MSAA toggles between frames
|
|
1340
|
-
* (a stale count=4 depth under a count=1 pipeline fails WebGPU validation).
|
|
1341
|
-
*/
|
|
1342
|
-
hdrDepthSampleCount: number;
|
|
455
|
+
constructor(private readonly internals: RenderSystemInternals) {}
|
|
1343
456
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
// pipeline shape: compiled at buildReadyWebGPU step 2 alongside the tonemap
|
|
1348
|
-
// pipeline and stored on PipelineState for per-frame consumption in the
|
|
1349
|
-
// record stage.
|
|
1350
|
-
//
|
|
1351
|
-
// D-2: 2-entry BGL (texture + sampler), no UBO. D-3: intermediate texture
|
|
1352
|
-
// format = bgra8unorm (swap-chain storage format). D-7: antialias=0
|
|
1353
|
-
// zero-overhead -- all FXAA fields stay null/0 until the first frame with
|
|
1354
|
-
// antialias='fxaa'.
|
|
1355
|
-
readonly fxaaPipeline: RenderPipeline | null;
|
|
1356
|
-
readonly fxaaBindGroupLayout: BindGroupLayout | null;
|
|
1357
|
-
readonly fxaaSampler: Sampler | null;
|
|
1358
|
-
|
|
1359
|
-
/** Fixed two-attachment linear TAA resolve resources, owned by this renderer. */
|
|
1360
|
-
readonly taaResolvePipeline: RenderPipeline | null;
|
|
1361
|
-
readonly taaResolveBindGroupLayout: BindGroupLayout | null;
|
|
1362
|
-
readonly taaResolveSampler: Sampler | null;
|
|
1363
|
-
taaResolveParamsBuffer: Buffer | null;
|
|
1364
|
-
|
|
1365
|
-
// ── feat-20260604-learn-render-4.10-anti-aliasing-msaa M2 / w7 ──────────
|
|
1366
|
-
//
|
|
1367
|
-
// MSAA (4x multisample) attachment slots. All `null`/0 until the first
|
|
1368
|
-
// frame whose active camera carries `antialias === 'msaa'`; the record
|
|
1369
|
-
// stage allocates them at the swap-chain size and re-creates on resize
|
|
1370
|
-
// (mirrors the hdrColor size-drift idiom; D-1). When
|
|
1371
|
-
// antialias is none/fxaa these stay null with zero allocation (C-9: MSAA
|
|
1372
|
-
// is a per-Camera switch derived from `camera.antialias === 'msaa'`, D-6).
|
|
1373
|
-
//
|
|
1374
|
-
// LDR swap-chain path (tonemapActive=false):
|
|
1375
|
-
// ONE count=4 multisample texture backs both the geometry and sprite
|
|
1376
|
-
// sub-passes. Two views of that same texture are taken: an srgb view
|
|
1377
|
-
// (msaaColorView, geometry pass) and an unorm view (msaaSpriteColorView,
|
|
1378
|
-
// sprite-split sub-pass; F-1). The single texture's storage format is
|
|
1379
|
-
// bgra8unorm, so the srgb and unorm views are both valid view formats of
|
|
1380
|
-
// it (same trick as the non-MSAA split path). The srgb view resolves to
|
|
1381
|
-
// the swap-chain srgb view; the unorm view resolves to the swap-chain
|
|
1382
|
-
// unorm view. No second multisample texture is allocated.
|
|
1383
|
-
// msaaDepth (depth24plus-stencil8, count=4) -> paired, never resolved (D-3).
|
|
1384
|
-
//
|
|
1385
|
-
// HDR path (tonemapActive=true): hdrColor/hdrDepth are themselves
|
|
1386
|
-
// allocated count=4 (record stage); hdrColorResolve is the single-sample
|
|
1387
|
-
// resolve output the tonemap/bloom passes sample (AC-03).
|
|
1388
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle (matches sibling fields)
|
|
1389
|
-
msaaColorTexture: any | null;
|
|
1390
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1391
|
-
msaaColorView: any | null;
|
|
1392
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1393
|
-
msaaSpriteColorTexture: any | null;
|
|
1394
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1395
|
-
msaaSpriteColorView: any | null;
|
|
1396
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1397
|
-
msaaDepthTexture: any | null;
|
|
1398
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1399
|
-
msaaDepthView: any | null;
|
|
1400
|
-
msaaTextureWidth: number;
|
|
1401
|
-
msaaTextureHeight: number;
|
|
1402
|
-
// HDR path MSAA: the count=4 multisample rgba16float geometry target. The
|
|
1403
|
-
// geometry + skybox passes write it; it resolves to the single-sample
|
|
1404
|
-
// `hdrColorView` (which the tonemap / bloom passes keep sampling unchanged).
|
|
1405
|
-
// Allocated alongside hdrColor when antialias='msaa' && tonemap!='none'.
|
|
1406
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle (HDR multisample target)
|
|
1407
|
-
hdrColorMsaaTexture: any | null;
|
|
1408
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1409
|
-
hdrColorMsaaView: any | null;
|
|
1410
|
-
|
|
1411
|
-
// ── feat-20260531-skybox-env-background M3 / w15 ─────────────────────────
|
|
1412
|
-
//
|
|
1413
|
-
// Skybox fullscreen cubemap pipeline resources. Mirrors tonemap/fxaa
|
|
1414
|
-
// construction: 4-entry BGL (texture_cube + sampler + View UBO + rotation UBO), fullscreen
|
|
1415
|
-
// triangle vertex stage, cubemap-sample fragment stage. Skybox writes HDR to
|
|
1416
|
-
// hdrColor rgba16float (targeted by the hdrColor render target); the tonemap
|
|
1417
|
-
// pass later reads hdrColor and maps to the LDR swap-chain. Fields are null
|
|
1418
|
-
// when the manifest has no skybox entry (D-7 optional, legacy manifests
|
|
1419
|
-
// continue to boot).
|
|
1420
|
-
readonly skyboxPipeline: RenderPipeline | null;
|
|
1421
|
-
// feat-20260604 M2 / w8: count=4 multisample variant of the skybox pipeline.
|
|
1422
|
-
// Used when the active camera carries antialias='msaa' (the skybox writes the
|
|
1423
|
-
// count=4 hdrColorMsaa target alongside the main geometry pass). null when
|
|
1424
|
-
// the base skybox pipeline is null (legacy manifest) or its MSAA build failed.
|
|
1425
|
-
readonly skyboxPipelineMsaa: RenderPipeline | null;
|
|
1426
|
-
readonly skyboxBindGroupLayout: BindGroupLayout | null;
|
|
1427
|
-
readonly skyboxSampler: Sampler | null;
|
|
1428
|
-
/** Stable 16 B quaternion UBO updated by the skybox record pass. */
|
|
1429
|
-
readonly skyboxRotationBuffer: Buffer | null;
|
|
1430
|
-
|
|
1431
|
-
/** Executable analytic-atmosphere producer and background resources. */
|
|
1432
|
-
readonly atmosphereCubemapPipeline?: RenderPipeline | null;
|
|
1433
|
-
readonly atmosphereCubemapBindGroupLayout?: BindGroupLayout | null;
|
|
1434
|
-
readonly atmosphereIrradiancePipeline?: RenderPipeline | null;
|
|
1435
|
-
readonly atmospherePrefilterPipeline?: RenderPipeline | null;
|
|
1436
|
-
readonly atmosphereIblParamsBindGroupLayout?: BindGroupLayout | null;
|
|
1437
|
-
readonly atmosphereIblSkyBindGroupLayout?: BindGroupLayout | null;
|
|
1438
|
-
readonly atmosphereBackgroundPipeline?: RenderPipeline | null;
|
|
1439
|
-
readonly atmosphereBackgroundBindGroupLayout?: BindGroupLayout | null;
|
|
1440
|
-
readonly atmosphereParamsBuffer?: Buffer | null;
|
|
1441
|
-
readonly atmosphereCubeVertexBuffer?: Buffer | null;
|
|
1442
|
-
|
|
1443
|
-
// ── feat-20260520-directional-light-shadow-mapping M1c / w8 ────────────
|
|
1444
|
-
//
|
|
1445
|
-
// Shadow RT (depth32float) lazy-allocated per mapSize; recreated on mapSize
|
|
1446
|
-
// drift. Used by the shadow depth pass (colorAttachments: []) and sampled
|
|
1447
|
-
// by the main pass as @group(0) binding(3) in M2/M3. debugReadback copies
|
|
1448
|
-
// the depth texture to a staging buffer for Inspector readback (D-2/D-5).
|
|
1449
|
-
//
|
|
1450
|
-
// feat-20260609 M4 / T-009: shadowCasterPipeline / shadowCasterPipelineLayout
|
|
1451
|
-
// removed — shadow PSO now obtained via frameState.pipelineCache lookup
|
|
1452
|
-
// (runtime.getMaterialShaderPipeline, passKind: 'shadow-caster'), same
|
|
1453
|
-
// path as forward passes (charter P4 consistent abstraction).
|
|
1454
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1455
|
-
shadowTexture: any | null;
|
|
1456
|
-
shadowMapSize: number;
|
|
1457
|
-
shadowCascadeCount: number;
|
|
1458
|
-
/** Exact dimensions of the compiled directional cascade atlas. */
|
|
1459
|
-
shadowAtlasWidth: number;
|
|
1460
|
-
shadowAtlasHeight: number;
|
|
1461
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1462
|
-
shadowSampler: any | null;
|
|
1463
|
-
/**
|
|
1464
|
-
* feat-20260520-directional-light-shadow-mapping M1c / w11 +
|
|
1465
|
-
* feat-20260613-csm-cascaded-shadow-maps M5 / w28:
|
|
1466
|
-
* latest cascade-0 lightViewProj mat4 (16 f32, col-major). Kept under
|
|
1467
|
-
* the legacy field name as an Inspector backward-compat surface
|
|
1468
|
-
* (`runtime.lights.directionalShadow.lightSpaceMatrix` JSON-RPC), per
|
|
1469
|
-
* AGENTS.md §Change stance exception for external-visible wire
|
|
1470
|
-
* protocols with known downstream consumers. Populated each frame
|
|
1471
|
-
* from `lights.lightViewProj[0]`. Null before the first extract with
|
|
1472
|
-
* an active shadow-casting DirectionalLight.
|
|
1473
|
-
*/
|
|
1474
|
-
shadowLightSpaceMatrix: Float32Array | null;
|
|
1475
|
-
/**
|
|
1476
|
-
* feat-20260613-csm-cascaded-shadow-maps M5 / w28: full 4-cascade
|
|
1477
|
-
* lightViewProj concatenation (4 × 16 = 64 f32, col-major) consumed by
|
|
1478
|
-
* `debugSampleShadowFactor` so the probe can project against the selected
|
|
1479
|
-
* cascade. Null before the first extract
|
|
1480
|
-
* with an active shadow-casting DirectionalLight.
|
|
1481
|
-
*/
|
|
1482
|
-
shadowCsmLightViewProj: Float32Array | null;
|
|
1483
|
-
/**
|
|
1484
|
-
* Producer-owned CSM receiver-selection facts. The debug probe must use the
|
|
1485
|
-
* same view-space depth + split-plane selector as the main receiver path;
|
|
1486
|
-
* keeping the two inputs together prevents a selector mirror from silently
|
|
1487
|
-
* drifting at cascade boundaries.
|
|
1488
|
-
*/
|
|
1489
|
-
shadowCsmSelection: {
|
|
1490
|
-
readonly viewMatrix: Float32Array;
|
|
1491
|
-
readonly splitPlanes: Float32Array;
|
|
1492
|
-
readonly cascadeBlend: number;
|
|
1493
|
-
} | null;
|
|
1494
|
-
/** Producer-owned directional shadow comparison parameters for GPU probes. */
|
|
1495
|
-
shadowLightDirection: Float32Array | null;
|
|
1496
|
-
shadowDepthBias: number;
|
|
1497
|
-
shadowNormalBias: number;
|
|
1498
|
-
shadowPcfKernelSize: number;
|
|
1499
|
-
|
|
1500
|
-
// ── feat-20260531-bloom-first-declarative-render-graph-pass w13/w16 ────
|
|
1501
|
-
//
|
|
1502
|
-
// Bloom post-processing chain: 4 passes (bright / blur-H / blur-V / composite)
|
|
1503
|
-
// operating in HDR rgba16float domain. Pipeline handles assembled at
|
|
1504
|
-
// buildReadyWebGPU (optional — legacy manifests without bloom.wgsl leave
|
|
1505
|
-
// these null). Intermediate textures are graph-owned transient targets
|
|
1506
|
-
// resolved per-frame; the bloom bind groups are identity-cached on those
|
|
1507
|
-
// views (frameState.postProcessBgCache) and rebuild automatically when a
|
|
1508
|
-
// resize retires the old textures.
|
|
1509
|
-
//
|
|
1510
|
-
// D-1: blur H/V share the same module, per-axis texelSize baked at UBO write.
|
|
1511
|
-
// D-4: 3 distinct BGL layouts (bright/blur: 1-tex+UBO, composite: 2-tex+UBO).
|
|
1512
|
-
// D-6: all intermediate textures use rgba16float format.
|
|
1513
|
-
readonly bloomBrightPipeline: RenderPipeline | null;
|
|
1514
|
-
readonly bloomBlurHPipeline: RenderPipeline | null;
|
|
1515
|
-
readonly bloomBlurVPipeline: RenderPipeline | null;
|
|
1516
|
-
readonly bloomCompositePipeline: RenderPipeline | null;
|
|
1517
|
-
readonly bloomBrightBindGroupLayout: BindGroupLayout | null;
|
|
1518
|
-
readonly bloomBlurBindGroupLayout: BindGroupLayout | null;
|
|
1519
|
-
readonly bloomCompositeBindGroupLayout: BindGroupLayout | null;
|
|
1520
|
-
readonly bloomSampler: Sampler | null;
|
|
1521
|
-
readonly bloomBrightParamsBuffer: Buffer | null;
|
|
1522
|
-
// bug-20260625: H and V blur passes need SEPARATE params UBOs. They run in
|
|
1523
|
-
// the same frame encoder; with one shared buffer the later writeBuffer (V's
|
|
1524
|
-
// texelSize=(0,1/h)) clobbers H's (texelSize=(1/w,0)) before the GPU runs the
|
|
1525
|
-
// H pass, so both passes blurred vertically -> only vertical bloom, no
|
|
1526
|
-
// horizontal spread. One buffer per axis keeps each pass's params intact.
|
|
1527
|
-
readonly bloomBlurHParamsBuffer: Buffer | null;
|
|
1528
|
-
readonly bloomBlurVParamsBuffer: Buffer | null;
|
|
1529
|
-
readonly bloomCompositeParamsBuffer: Buffer | null;
|
|
1530
|
-
|
|
1531
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1532
|
-
bloomBrightTexture: any | null;
|
|
1533
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1534
|
-
bloomBrightView: any | null;
|
|
1535
|
-
bloomBrightWidth: number;
|
|
1536
|
-
bloomBrightHeight: number;
|
|
457
|
+
beginFrame(): void {
|
|
458
|
+
this.active.clear();
|
|
459
|
+
}
|
|
1537
460
|
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
461
|
+
prepare(
|
|
462
|
+
entry: import('./record/frame-snapshot').ValidatedRenderable,
|
|
463
|
+
clustered: boolean,
|
|
464
|
+
): PointsLinesRecordSubmission | undefined {
|
|
465
|
+
const snapshot = entry.source.pointsLines;
|
|
466
|
+
if (snapshot === undefined || snapshot.component === undefined || entry.world === undefined) {
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
const key = `${snapshot.worldId}:${snapshot.entityKey}`;
|
|
470
|
+
const topology = snapshot.component === 'Points' ? 'point-list' : 'line-list';
|
|
471
|
+
const backend = this.backend();
|
|
472
|
+
const lane = backend === 'wgpu-webgl2' ? 'cpu-webgl2' : clustered ? 'clustered' : 'direct';
|
|
473
|
+
const meshResult = resolveAssetHandle<MeshAsset>(
|
|
474
|
+
entry.world,
|
|
475
|
+
toShared<'MeshAsset'>(entry.source.assetHandle),
|
|
476
|
+
);
|
|
477
|
+
const materialResult = resolveAssetHandle<MaterialAsset>(
|
|
478
|
+
entry.world,
|
|
479
|
+
toShared<'MaterialAsset'>(snapshot.materialHandle),
|
|
480
|
+
);
|
|
481
|
+
if (!meshResult.ok || !materialResult.ok) {
|
|
482
|
+
this.publishRefusal(
|
|
483
|
+
snapshot,
|
|
484
|
+
new PointsLinesMaterialUnsupportedError({
|
|
485
|
+
entity: snapshot.entityKey,
|
|
486
|
+
material: 'unresolved',
|
|
487
|
+
pass: 'forward',
|
|
488
|
+
module: 'asset-resolution',
|
|
489
|
+
reason: 'source MeshAsset or MaterialAsset could not be resolved',
|
|
490
|
+
}),
|
|
491
|
+
);
|
|
492
|
+
return undefined;
|
|
493
|
+
}
|
|
494
|
+
const admission = admitPointsLines({
|
|
495
|
+
entity: snapshot.entityKey,
|
|
496
|
+
mesh: meshResult.value,
|
|
497
|
+
material: materialResult.value,
|
|
498
|
+
...(snapshot.style?.kind === 'points'
|
|
499
|
+
? {
|
|
500
|
+
points: {
|
|
501
|
+
sizePx: snapshot.style.sizePx,
|
|
502
|
+
shape: snapshot.style.shape === 'circle' ? 1 : 0,
|
|
503
|
+
},
|
|
504
|
+
}
|
|
505
|
+
: { lines: { widthPx: snapshot.style?.widthPx ?? 1 } }),
|
|
506
|
+
});
|
|
507
|
+
if (!admission.ok) {
|
|
508
|
+
this.publishRefusal(snapshot, admission.error);
|
|
509
|
+
return undefined;
|
|
510
|
+
}
|
|
511
|
+
const geometryKey = this.cache.getOrCreate(snapshot, meshResult.value).key;
|
|
512
|
+
const prepKey = `${geometryKey}:${lane}:${backend}`;
|
|
513
|
+
let preparation = this.preparations.get(prepKey);
|
|
514
|
+
if (preparation === undefined) {
|
|
515
|
+
preparation = createPointsLinesLanePreparationAdapter(lane, backend, {
|
|
516
|
+
cache: this.cache,
|
|
517
|
+
adapter: {
|
|
518
|
+
create: (geometry) => {
|
|
519
|
+
const vertex = this.internals.device.createBuffer({
|
|
520
|
+
label: `points-lines-vertices:${geometryKey}`,
|
|
521
|
+
size: Math.max(4, geometry.vertices.byteLength),
|
|
522
|
+
usage: GPU_BUFFER_USAGE_VERTEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
523
|
+
mappedAtCreation: false,
|
|
524
|
+
});
|
|
525
|
+
if (!vertex.ok) return err(vertex.error);
|
|
526
|
+
const index = this.internals.device.createBuffer({
|
|
527
|
+
label: `points-lines-indices:${geometryKey}`,
|
|
528
|
+
size: Math.max(4, geometry.indices.byteLength),
|
|
529
|
+
usage: GPU_BUFFER_USAGE_INDEX | GPU_BUFFER_USAGE_COPY_DST,
|
|
530
|
+
mappedAtCreation: false,
|
|
531
|
+
});
|
|
532
|
+
if (!index.ok) {
|
|
533
|
+
this.internals.device.destroyBuffer(vertex.value);
|
|
534
|
+
return err(index.error);
|
|
535
|
+
}
|
|
536
|
+
return ok({ vertexBuffer: vertex.value, indexBuffer: index.value });
|
|
537
|
+
},
|
|
538
|
+
upload: (resource, geometry) => {
|
|
539
|
+
if (geometry.vertices.byteLength > 0) {
|
|
540
|
+
const vertexWrite = this.internals.device.queue.writeBuffer(
|
|
541
|
+
resource.vertexBuffer,
|
|
542
|
+
0,
|
|
543
|
+
geometry.vertices,
|
|
544
|
+
);
|
|
545
|
+
if (!vertexWrite.ok) return err(vertexWrite.error);
|
|
546
|
+
}
|
|
547
|
+
if (geometry.indices.byteLength > 0) {
|
|
548
|
+
const indexWrite = this.internals.device.queue.writeBuffer(
|
|
549
|
+
resource.indexBuffer,
|
|
550
|
+
0,
|
|
551
|
+
geometry.indices,
|
|
552
|
+
);
|
|
553
|
+
if (!indexWrite.ok) return err(indexWrite.error);
|
|
554
|
+
}
|
|
555
|
+
return ok(geometry.derivedBytes);
|
|
556
|
+
},
|
|
557
|
+
validate: (_resource, geometry) =>
|
|
558
|
+
geometry.expandedVertexCount > 0 && geometry.expandedIndexCount > 0
|
|
559
|
+
? ok(undefined)
|
|
560
|
+
: err(new Error('Points/Lines expansion contains no drawable triangles')),
|
|
561
|
+
destroy: (resource) => {
|
|
562
|
+
this.internals.device.destroyBuffer(resource.vertexBuffer);
|
|
563
|
+
this.internals.device.destroyBuffer(resource.indexBuffer);
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
});
|
|
567
|
+
this.preparations.set(prepKey, preparation);
|
|
568
|
+
}
|
|
569
|
+
const prepared = preparation.prepare(snapshot, meshResult.value);
|
|
570
|
+
if (!prepared.ok) {
|
|
571
|
+
const lkg = preparation.lastKnownGood();
|
|
572
|
+
if (lkg !== undefined) {
|
|
573
|
+
const lkgPlan = createPointsLinesLaneAdapter(lane, backend).createRecordPlan(
|
|
574
|
+
lkg.snapshot,
|
|
575
|
+
lkg.geometry,
|
|
576
|
+
);
|
|
577
|
+
const state = preparation.inspect();
|
|
578
|
+
this.active.set(
|
|
579
|
+
key,
|
|
580
|
+
inspectPointsLines({
|
|
581
|
+
snapshot,
|
|
582
|
+
topology,
|
|
583
|
+
lane,
|
|
584
|
+
pointCount: lkg.geometry.pointCount,
|
|
585
|
+
segmentCount: lkg.geometry.segmentCount,
|
|
586
|
+
sourceBytes: lkg.geometry.sourceBytes,
|
|
587
|
+
derivedBytes: lkg.geometry.derivedBytes,
|
|
588
|
+
cache: { hit: true, rebuilds: state.rebuilds, evictions: 0 },
|
|
589
|
+
drawCount: lkgPlan.drawCount,
|
|
590
|
+
uploadBytes: 0,
|
|
591
|
+
lastKnownGood: true,
|
|
592
|
+
refusal: {
|
|
593
|
+
code: prepared.error.code,
|
|
594
|
+
expected: prepared.error.expected,
|
|
595
|
+
hint: prepared.error.hint,
|
|
596
|
+
detail: prepared.error.detail,
|
|
597
|
+
generation: prepared.error.detail.generation,
|
|
598
|
+
lastKnownGood: true,
|
|
599
|
+
},
|
|
600
|
+
}),
|
|
601
|
+
);
|
|
602
|
+
return {
|
|
603
|
+
plan: lkgPlan,
|
|
604
|
+
vertexBuffer: lkg.resource.vertexBuffer,
|
|
605
|
+
indexBuffer: lkg.resource.indexBuffer,
|
|
606
|
+
layoutProjection: this.layoutProjection,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
this.publishRefusal(snapshot, prepared.error);
|
|
610
|
+
return undefined;
|
|
611
|
+
}
|
|
612
|
+
const plan = createPointsLinesLaneAdapter(lane, backend).createRecordPlan(
|
|
613
|
+
snapshot,
|
|
614
|
+
prepared.value.geometry,
|
|
615
|
+
);
|
|
616
|
+
const state = preparation.inspect();
|
|
617
|
+
this.active.set(
|
|
618
|
+
key,
|
|
619
|
+
inspectPointsLines({
|
|
620
|
+
snapshot,
|
|
621
|
+
topology,
|
|
622
|
+
lane,
|
|
623
|
+
pointCount: prepared.value.geometry.pointCount,
|
|
624
|
+
segmentCount: prepared.value.geometry.segmentCount,
|
|
625
|
+
sourceBytes: prepared.value.geometry.sourceBytes,
|
|
626
|
+
derivedBytes: prepared.value.geometry.derivedBytes,
|
|
627
|
+
cache: {
|
|
628
|
+
hit: prepared.value.uploadedBytes === 0,
|
|
629
|
+
rebuilds: state.rebuilds,
|
|
630
|
+
evictions: 0,
|
|
631
|
+
},
|
|
632
|
+
drawCount: plan.drawCount,
|
|
633
|
+
uploadBytes: prepared.value.uploadedBytes,
|
|
634
|
+
lastKnownGood: prepared.value.lastKnownGood,
|
|
635
|
+
}),
|
|
636
|
+
);
|
|
637
|
+
return {
|
|
638
|
+
plan,
|
|
639
|
+
vertexBuffer: prepared.value.resource.vertexBuffer,
|
|
640
|
+
indexBuffer: prepared.value.resource.indexBuffer,
|
|
641
|
+
layoutProjection: this.layoutProjection,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
1544
644
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
bloomBlurVWidth: number;
|
|
1550
|
-
bloomBlurVHeight: number;
|
|
645
|
+
resetForDeviceLoss(): void {
|
|
646
|
+
for (const preparation of this.preparations.values()) preparation.resetForDeviceLoss();
|
|
647
|
+
this.active.clear();
|
|
648
|
+
}
|
|
1551
649
|
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
// half-resolution R8 single-channel textures. RenderPipeline handles
|
|
1556
|
-
// assembled at buildReadyWebGPU (optional — manifests without
|
|
1557
|
-
// hdrp-ssao.wgsl leave these null). Dedicated BGL (9 entries matching
|
|
1558
|
-
// current WGSL @group(0) bindings 0-8) shared by both pipelines: calc
|
|
1559
|
-
// and blur both bind all 9 entries; the blur uses ssaoRaw (binding 7) +
|
|
1560
|
-
// ssaoSampler (binding 8) while the calc binds a 1x1 fallback view at
|
|
1561
|
-
// 7 (the calc shader never samples ssaoRaw).
|
|
1562
|
-
// plan-strategy D-A; D-D; D-E.
|
|
1563
|
-
//
|
|
1564
|
-
// Samplers (filtering + non-filtering depth) and the 1x1 ssaoRaw fallback
|
|
1565
|
-
// view used by the calc pass are lazy-allocated inside the record closure
|
|
1566
|
-
// on first frame and cached on the mutable slots below — keeps createRenderer
|
|
1567
|
-
// free of additional state and matches bloom's lazy bind-group pattern.
|
|
1568
|
-
readonly ssaoCalcPipeline: RenderPipeline | null;
|
|
1569
|
-
readonly ssaoBlurPipeline: RenderPipeline | null;
|
|
1570
|
-
readonly ssaoBgl: BindGroupLayout | null;
|
|
1571
|
-
/** Filtering sampler shared by ssao_noise_sampler (binding 3) + ssaoSampler (binding 8). */
|
|
1572
|
-
ssaoFilteringSampler: Sampler | null;
|
|
1573
|
-
/** Non-filtering sampler dedicated to hdr_depth (binding 6); WebGPU validation. */
|
|
1574
|
-
ssaoDepthSampler: Sampler | null;
|
|
1575
|
-
/** 1x1 fallback view bound at ssaoRaw (binding 7) in the calc pass. */
|
|
1576
|
-
// biome-ignore lint/suspicious/noExplicitAny: opaque RHI handle
|
|
1577
|
-
ssaoFallbackRawView: any | null;
|
|
1578
|
-
}
|
|
650
|
+
inspections(): readonly PointsLinesInspection[] {
|
|
651
|
+
return [...this.active.values()];
|
|
652
|
+
}
|
|
1579
653
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
* `MeshAsset.indices`). When `null` the record stage takes the non-indexed
|
|
1591
|
-
* `pass.draw(vertexCount)` path and never calls `setIndexBuffer`. Gated
|
|
1592
|
-
* on `indexed` below.
|
|
1593
|
-
*/
|
|
1594
|
-
readonly indexBuffer: GpuBuffer | null;
|
|
1595
|
-
/** Allocation byte size of the vertex buffer (mirrors GPUBuffer.size). */
|
|
1596
|
-
readonly vboBytes: number;
|
|
1597
|
-
/** Allocation byte size of the index buffer, or 0 when `indexBuffer` is null. */
|
|
1598
|
-
readonly iboBytes: number;
|
|
1599
|
-
readonly indexCount: number;
|
|
1600
|
-
/**
|
|
1601
|
-
* Index buffer format inferred from the source `MeshAsset.indices` typed
|
|
1602
|
-
* array (`Uint16Array` -> `'uint16'`, `Uint32Array` -> `'uint32'`). The
|
|
1603
|
-
* record stage threads this into `pass.setIndexBuffer(..., format)` so
|
|
1604
|
-
* the GPU reads the correct stride per index. bug-20260519: the prior
|
|
1605
|
-
* hard-coded `'uint16'` corrupted procedural meshes whose factories
|
|
1606
|
-
* (`createBoxGeometry` etc) emit Uint32Array indices.
|
|
1607
|
-
*/
|
|
1608
|
-
readonly indexFormat: 'uint16' | 'uint32';
|
|
1609
|
-
/** Geometry-owned immutable projection carried through every GPU consumer. */
|
|
1610
|
-
readonly layoutProjection: VertexLayoutProjection;
|
|
1611
|
-
/**
|
|
1612
|
-
* Vertex count is derived from the projection stride at upload time. The
|
|
1613
|
-
* non-indexed draw path passes this to `pass.draw(vertexCount)`.
|
|
1614
|
-
*/
|
|
1615
|
-
readonly vertexCount: number;
|
|
1616
|
-
/**
|
|
1617
|
-
* True when the mesh carries an index buffer (`MeshAsset.indices` present).
|
|
1618
|
-
* `false` for vertex-only meshes. The record stage branches on this to pick
|
|
1619
|
-
* `drawIndexed` vs `draw` and to gate `setIndexBuffer`.
|
|
1620
|
-
*/
|
|
1621
|
-
readonly indexed: boolean;
|
|
1622
|
-
/**
|
|
1623
|
-
* Primitive topology of this mesh (first submesh's topology, default
|
|
1624
|
-
* 'triangle-list'). WebGPU bakes topology into the immutable PSO, so the
|
|
1625
|
-
* record stage threads this into `getMaterialShaderPipeline` to select a
|
|
1626
|
-
* per-topology PSO (feat-20260604 M3 / w9).
|
|
1627
|
-
*
|
|
1628
|
-
* feat-20260608 M4 / w16: topology is per-submesh; this field reflects
|
|
1629
|
-
* `submeshes[0].topology` for backward compat. Use `submeshes` field for
|
|
1630
|
-
* per-submesh draw iteration.
|
|
1631
|
-
*/
|
|
1632
|
-
readonly topology: PrimitiveTopology;
|
|
1633
|
-
/**
|
|
1634
|
-
* Submeshes from MeshAsset.submeshes, carried through from the GPU store
|
|
1635
|
-
* so the record stage can iterate per-submesh drawIndexed (feat-20260608 M4 / w16).
|
|
1636
|
-
*/
|
|
1637
|
-
readonly submeshes: readonly import('@forgeax/engine-types').Submesh[];
|
|
1638
|
-
}
|
|
654
|
+
private backend(): PointsLinesBackend {
|
|
655
|
+
switch (this.internals.device.caps.backendKind) {
|
|
656
|
+
case 'wgpu-webgl2':
|
|
657
|
+
return 'wgpu-webgl2';
|
|
658
|
+
case 'null':
|
|
659
|
+
return 'null';
|
|
660
|
+
default:
|
|
661
|
+
return 'webgpu';
|
|
662
|
+
}
|
|
663
|
+
}
|
|
1639
664
|
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
665
|
+
private publishRefusal(
|
|
666
|
+
snapshot: PointsLinesRetainedSnapshot,
|
|
667
|
+
error: PointsLinesSourceError,
|
|
668
|
+
): void {
|
|
669
|
+
this.active.set(
|
|
670
|
+
`${snapshot.worldId}:${snapshot.entityKey}`,
|
|
671
|
+
inspectPointsLines({
|
|
672
|
+
snapshot,
|
|
673
|
+
topology: snapshot.component === 'Points' ? 'point-list' : 'line-list',
|
|
674
|
+
lane: 'refused',
|
|
675
|
+
pointCount: 0,
|
|
676
|
+
segmentCount: 0,
|
|
677
|
+
sourceBytes: 0,
|
|
678
|
+
derivedBytes: 0,
|
|
679
|
+
cache: { hit: false, rebuilds: 0, evictions: 0 },
|
|
680
|
+
drawCount: 0,
|
|
681
|
+
uploadBytes: 0,
|
|
682
|
+
lastKnownGood: false,
|
|
683
|
+
refusal: {
|
|
684
|
+
code: error.code,
|
|
685
|
+
expected: error.expected,
|
|
686
|
+
hint: error.hint,
|
|
687
|
+
detail: error.detail,
|
|
688
|
+
generation: snapshot.meshGeneration,
|
|
689
|
+
lastKnownGood: false,
|
|
690
|
+
},
|
|
691
|
+
}),
|
|
692
|
+
);
|
|
693
|
+
}
|
|
1649
694
|
}
|
|
1650
|
-
`;
|
|
1651
695
|
|
|
1652
696
|
export function createRenderSystem(internals: RenderSystemInternals): RenderSystem {
|
|
1653
697
|
const phaseCatalogRegistration = internals.profiler?.registerPhaseCatalog(
|
|
@@ -1656,17 +700,15 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1656
700
|
);
|
|
1657
701
|
let releaseProfilerCatalog =
|
|
1658
702
|
phaseCatalogRegistration?.ok === true ? phaseCatalogRegistration.value : undefined;
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
// WeakMap also lets a dropped Play world release its cache with the world.
|
|
1662
|
-
const materialSnapshotCachesByWorld: MaterialSnapshotCachesByWorld = new WeakMap();
|
|
703
|
+
let preparedWorlds: readonly World[] = [];
|
|
704
|
+
const pointsLinesOwner = new StandardPointsLinesOwner(internals);
|
|
1663
705
|
const persistentRenderScene = new PersistentRenderScene({
|
|
1664
706
|
getDevice: () => internals.device,
|
|
1665
707
|
onGpuError: (error) => internals.errorRegistry.fire(error),
|
|
1666
|
-
|
|
708
|
+
onSharedRefMutation: (worldId, handle) => {
|
|
709
|
+
internals.gpuStore.invalidateMesh(handle, preparedWorlds[worldId] ?? worldId);
|
|
710
|
+
},
|
|
1667
711
|
});
|
|
1668
|
-
const taaHistoryStore = new TaaHistoryStore(internals.device);
|
|
1669
|
-
const temporalFrameTransaction = new TemporalFrameTransaction(taaHistoryStore);
|
|
1670
712
|
const gpuDrivenShaderFactory =
|
|
1671
713
|
internals.shaderModuleFactory ??
|
|
1672
714
|
({
|
|
@@ -1679,89 +721,25 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1679
721
|
}),
|
|
1680
722
|
),
|
|
1681
723
|
} satisfies import('./pipeline-builder').PipelineBuilderShaderModuleFactory);
|
|
1682
|
-
let directionalShadowTileClear:
|
|
1683
|
-
| { readonly device: RhiDevice; readonly pipeline: RenderPipeline }
|
|
1684
|
-
| undefined;
|
|
1685
|
-
const getDirectionalShadowTileClearPipeline = (): RenderPipeline | null => {
|
|
1686
|
-
const device = internals.device;
|
|
1687
|
-
if (directionalShadowTileClear?.device === device) {
|
|
1688
|
-
return directionalShadowTileClear.pipeline;
|
|
1689
|
-
}
|
|
1690
|
-
const module = gpuDrivenShaderFactory.createShaderModule({
|
|
1691
|
-
label: 'directional-shadow-tile-clear',
|
|
1692
|
-
code: DIRECTIONAL_SHADOW_TILE_CLEAR_WGSL,
|
|
1693
|
-
});
|
|
1694
|
-
if (!module.ok) {
|
|
1695
|
-
if (module.error.code !== 'rhi-not-available') internals.errorRegistry.fire(module.error);
|
|
1696
|
-
return null;
|
|
1697
|
-
}
|
|
1698
|
-
const layout = device.createPipelineLayout({
|
|
1699
|
-
label: 'directional-shadow-tile-clear-layout',
|
|
1700
|
-
bindGroupLayouts: [],
|
|
1701
|
-
});
|
|
1702
|
-
if (!layout.ok) {
|
|
1703
|
-
internals.errorRegistry.fire(layout.error);
|
|
1704
|
-
return null;
|
|
1705
|
-
}
|
|
1706
|
-
const pipeline = device.createRenderPipeline({
|
|
1707
|
-
label: 'directional-shadow-tile-clear',
|
|
1708
|
-
layout: layout.value as unknown as GPUPipelineLayout,
|
|
1709
|
-
vertex: {
|
|
1710
|
-
module: module.value as unknown as GPUShaderModule,
|
|
1711
|
-
entryPoint: 'vs_main',
|
|
1712
|
-
buffers: [],
|
|
1713
|
-
},
|
|
1714
|
-
fragment: undefined,
|
|
1715
|
-
primitive: { topology: 'triangle-list', cullMode: 'none' },
|
|
1716
|
-
depthStencil: {
|
|
1717
|
-
format: 'depth32float',
|
|
1718
|
-
depthWriteEnabled: true,
|
|
1719
|
-
depthCompare: 'always',
|
|
1720
|
-
},
|
|
1721
|
-
});
|
|
1722
|
-
if (!pipeline.ok) {
|
|
1723
|
-
internals.errorRegistry.fire(pipeline.error);
|
|
1724
|
-
return null;
|
|
1725
|
-
}
|
|
1726
|
-
directionalShadowTileClear = { device, pipeline: pipeline.value };
|
|
1727
|
-
return pipeline.value;
|
|
1728
|
-
};
|
|
1729
|
-
(
|
|
1730
|
-
internals as unknown as {
|
|
1731
|
-
getDirectionalShadowTileClearPipeline: typeof getDirectionalShadowTileClearPipeline;
|
|
1732
|
-
}
|
|
1733
|
-
).getDirectionalShadowTileClearPipeline = getDirectionalShadowTileClearPipeline;
|
|
1734
724
|
let gpuDrivenProduction = new GpuDrivenProduction(internals.device, gpuDrivenShaderFactory);
|
|
1735
|
-
const featureGpuWork =
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
}),
|
|
1751
|
-
),
|
|
1752
|
-
} satisfies import('./pipeline-builder').PipelineBuilderShaderModuleFactory),
|
|
1753
|
-
generation: internals.featureHost?.preparedGeneration ?? 0,
|
|
1754
|
-
featureIdentity,
|
|
1755
|
-
});
|
|
1756
|
-
featureGpuWork.set(featureIdentity, created);
|
|
1757
|
-
return created;
|
|
1758
|
-
};
|
|
725
|
+
const featureGpuWork: RenderFeatureGpuWorkOwner = createRenderFeatureGpuWorkOwner({
|
|
726
|
+
getDevice: () => internals.device,
|
|
727
|
+
getShaderModuleFactory: () =>
|
|
728
|
+
internals.shaderModuleFactory ??
|
|
729
|
+
({
|
|
730
|
+
createShaderModule: () =>
|
|
731
|
+
err(
|
|
732
|
+
new RhiError({
|
|
733
|
+
code: 'rhi-not-available',
|
|
734
|
+
expected: 'the renderer backend exposes shader module creation',
|
|
735
|
+
hint: 'construct the renderer through the backend pack before activating GPU features',
|
|
736
|
+
}),
|
|
737
|
+
),
|
|
738
|
+
} satisfies import('./pipeline-builder').PipelineBuilderShaderModuleFactory),
|
|
739
|
+
});
|
|
1759
740
|
const disposeFeatureGpuWork = (): void => {
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
if (!disposed.ok) internals.errorRegistry.fire(disposed.error);
|
|
1763
|
-
}
|
|
1764
|
-
featureGpuWork.clear();
|
|
741
|
+
const disposed = featureGpuWork.dispose();
|
|
742
|
+
if (!disposed.ok) internals.errorRegistry.fire(disposed.error);
|
|
1765
743
|
};
|
|
1766
744
|
// Per-RenderSystem frame state: closure-internal frameNumber + the
|
|
1767
745
|
// per-entity instance GPU buffer cache (feat-20260514 M3 / w15: the
|
|
@@ -1778,9 +756,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1778
756
|
// raw (material-handle-based, cross-world collision semantically correct).
|
|
1779
757
|
const frameState: RenderFrameState = {
|
|
1780
758
|
frameNumber: 0,
|
|
1781
|
-
environmentGeneration: createEnvironmentGenerationState(0),
|
|
1782
|
-
shadowSubmitModeState: createShadowSubmitModeState(),
|
|
1783
|
-
pendingShadowCommandBuffers: [],
|
|
1784
759
|
directionalShadowCache: null,
|
|
1785
760
|
directionalShadowCacheRecorded: false,
|
|
1786
761
|
compiledFrameGraph: null,
|
|
@@ -1790,6 +765,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1790
765
|
currentDirectionalShadowView: null,
|
|
1791
766
|
currentSpotShadowView: null,
|
|
1792
767
|
instanceBuffers: new Map(),
|
|
768
|
+
morphBuffers: new Map(),
|
|
1793
769
|
hdrpClusterBinScratch: createClusterBinScratch(),
|
|
1794
770
|
hdrpClusterGridScratch: null,
|
|
1795
771
|
hdrpLightIndexListScratch: null,
|
|
@@ -1810,7 +786,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1810
786
|
// cleared (charter F1 minimal surface — the per-RenderSystem lifetime
|
|
1811
787
|
// is the natural upper bound).
|
|
1812
788
|
warnedMissingBaseColorTextureHandles: new Set<number>(),
|
|
1813
|
-
reportedMaterialDiagnosticKeys: new Set<string>(),
|
|
1814
789
|
// feat-20260527-sprite-nineslice M2 / w11 + M4 / w16 (AC-16): once-per-
|
|
1815
790
|
// renderable guard for the runtime `nineslice.scale-too-small` metric
|
|
1816
791
|
// counter (`runtime.metrics.increment(...)` in render-system-record.ts).
|
|
@@ -1842,14 +817,13 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1842
817
|
// WeakMap chain so resize-retired transient targets rebuild automatically.
|
|
1843
818
|
postProcessBgCache: new WeakMap(),
|
|
1844
819
|
// feat-20260601-customizable-render-pipeline-seam M1 / w7: installed-pipeline state.
|
|
1845
|
-
// 0 = nothing installed yet (createRenderer dogfood installs the default before
|
|
820
|
+
// 0 = nothing installed yet (createRenderer dogfood installs the default before a
|
|
1846
821
|
// draw). activePipeline defaults to the built-in forward pipeline.
|
|
1847
822
|
installedPipelineHandle: 0,
|
|
1848
|
-
|
|
1849
|
-
activePipeline: urpPipeline,
|
|
823
|
+
activePipeline: internals.standardPipeline,
|
|
1850
824
|
// feat-20260601 verify round 2: the standard forward pipeline installs with no config
|
|
1851
|
-
// (its topology is frame-invariant).
|
|
1852
|
-
// asset
|
|
825
|
+
// (its topology is frame-invariant). Standard configuration overwrites this with the
|
|
826
|
+
// resolved asset config on every swap so the active graph reads its current config.
|
|
1853
827
|
installedPipelineConfig: undefined,
|
|
1854
828
|
// feat-20260608-cluster-lighting M2 / w10 + M5 / w20: HDRP active flag + once-per-frame
|
|
1855
829
|
// warn dedup set for hdrp-light-budget-exceeded / hdrp-index-list-overflow.
|
|
@@ -1859,7 +833,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1859
833
|
// cube_array shadow atlas + per-frame snapshot list. Atlas is null until the
|
|
1860
834
|
// first frame whose extracted lights.pointShadow is non-empty (zero-shadow
|
|
1861
835
|
// scenes never allocate; AC-09); the snapshot list defaults to an empty
|
|
1862
|
-
// tuple so the
|
|
836
|
+
// tuple so the typed point-shadow graph sees zero shadow lights as the
|
|
1863
837
|
// initial steady state.
|
|
1864
838
|
pointShadowAtlas: null,
|
|
1865
839
|
pointShadowSnapshots: [],
|
|
@@ -1917,52 +891,61 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1917
891
|
if (opened) endProfilePhase(session);
|
|
1918
892
|
}
|
|
1919
893
|
}
|
|
1920
|
-
// Pipeline registry and current installation live in the RenderSystem closure
|
|
1921
|
-
// (plan-strategy D-D: all real logic on the RenderSystem layer; the Renderer facade
|
|
1922
|
-
// only forwards). The registry dedups same-id register (Map.has -> throw), mirroring
|
|
1923
|
-
// ShaderRegistry.installMaterialArtifact.
|
|
1924
|
-
const pipelineRegistry = new Map<string, RenderPipelineDef>();
|
|
1925
|
-
const pipelineInstalls: Array<{
|
|
1926
|
-
readonly token: object;
|
|
1927
|
-
readonly id: string;
|
|
1928
|
-
readonly pipeline: RenderPipelineDef;
|
|
1929
|
-
readonly config: RenderPipelineAsset['config'];
|
|
1930
|
-
readonly isHdrp: boolean;
|
|
1931
|
-
}> = [];
|
|
1932
894
|
let lastBuiltPipelineHandle = 0;
|
|
1933
|
-
// Monotonic
|
|
895
|
+
// Monotonic configuration epoch: bumped on every Standard configuration change to brand the
|
|
1934
896
|
// installed pipeline so `draw` can detect a swap and rebuild the per-frame
|
|
1935
|
-
// graph. Replaces the prior raw-handle brand (D-19:
|
|
897
|
+
// graph. Replaces the prior raw-handle brand (D-19: the pipeline configuration takes a
|
|
1936
898
|
// POD, no handle).
|
|
1937
899
|
let installEpoch = 0;
|
|
1938
|
-
//
|
|
1939
|
-
//
|
|
1940
|
-
// Dedups same-id register (Map.has -> throw), mirroring ShaderRegistry.installMaterialArtifact.
|
|
1941
|
-
const postProcessRegistry = new Map<string, PostProcessShaderEntry>();
|
|
1942
|
-
// CPU post-process declarations are the logical registry and remain live
|
|
1943
|
-
// across recovery. Only device-bound buffers and pipelines are rebuilt.
|
|
900
|
+
// CPU post-process declarations are owned by the feature host and remain live
|
|
901
|
+
// across recovery. Only device-bound buffers and pipelines are rebuilt here.
|
|
1944
902
|
// D-3 / D-8: per-shader params UBO resource table (id -> GPU Buffer).
|
|
1945
903
|
// Eager-created at register time when entry.params is present (byteSize >= 16,
|
|
1946
904
|
// defaultValue.length === byteSize); reused frame-to-frame via queue.writeBuffer.
|
|
1947
905
|
const postProcessParamsBuffers = new Map<string, Buffer>();
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
// resolve a registered shader id without reaching into RenderSystemInternals.
|
|
1951
|
-
// Inject as a post-construction property on `internals` (typed as readonly
|
|
1952
|
-
// optional on RenderSystemRuntime, so a one-time cast is the only path);
|
|
1953
|
-
// every consumer that types `RenderPipelineContext.runtime` will see it.
|
|
906
|
+
const builtinPostProcessEntries = new Map<string, PostProcessShaderEntry>();
|
|
907
|
+
let activeFeaturePostProcessEntries: ReadonlyMap<string, PostProcessShaderEntry> = new Map();
|
|
1954
908
|
const lookupPostProcess = (id: string): PostProcessShaderEntry | undefined =>
|
|
1955
|
-
|
|
1956
|
-
(internals as unknown as { lookupPostProcess: typeof lookupPostProcess }).lookupPostProcess =
|
|
1957
|
-
lookupPostProcess;
|
|
909
|
+
builtinPostProcessEntries.get(id) ?? activeFeaturePostProcessEntries.get(id);
|
|
1958
910
|
// feat-20260621 M-A2 / w8: expose the eager-created per-id params UBO through
|
|
1959
911
|
// the narrow runtime surface so dispatchFullscreenPass can writeBuffer the
|
|
1960
912
|
// per-frame snapshot + bind it at group(1) binding(2).
|
|
1961
913
|
const getPostProcessParamsBuffer = (id: string): Buffer | undefined =>
|
|
1962
914
|
postProcessParamsBuffers.get(id);
|
|
1963
|
-
(
|
|
1964
|
-
|
|
1965
|
-
)
|
|
915
|
+
const ensurePostProcessParamsResources = (
|
|
916
|
+
featureEntries: ReadonlyMap<string, PostProcessShaderEntry>,
|
|
917
|
+
): void => {
|
|
918
|
+
const declarations = new Map<string, PostProcessShaderEntry>([
|
|
919
|
+
...builtinPostProcessEntries.entries(),
|
|
920
|
+
...featureEntries.entries(),
|
|
921
|
+
]);
|
|
922
|
+
for (const [id, entry] of declarations) {
|
|
923
|
+
const byteSize =
|
|
924
|
+
entry.params?.byteSize ?? (entryHasDepthRead(entry) ? DEPTH_MIN_PARAMS_BYTE_SIZE : 0);
|
|
925
|
+
if (byteSize === 0 || postProcessParamsBuffers.has(id)) continue;
|
|
926
|
+
const created = internals.device.createBuffer({
|
|
927
|
+
label: `post-process-params-${id}`,
|
|
928
|
+
size: byteSize,
|
|
929
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
930
|
+
mappedAtCreation: false,
|
|
931
|
+
});
|
|
932
|
+
if (!created.ok) throw created.error;
|
|
933
|
+
const buffer = created.value;
|
|
934
|
+
if (entry.params !== undefined) {
|
|
935
|
+
const written = internals.device.queue.writeBuffer(buffer, 0, entry.params.defaultValue);
|
|
936
|
+
if (!written.ok) {
|
|
937
|
+
internals.device.destroyBuffer(buffer);
|
|
938
|
+
throw written.error;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
postProcessParamsBuffers.set(id, buffer);
|
|
942
|
+
}
|
|
943
|
+
for (const [id, buffer] of postProcessParamsBuffers) {
|
|
944
|
+
if (declarations.has(id)) continue;
|
|
945
|
+
internals.device.destroyBuffer(buffer);
|
|
946
|
+
postProcessParamsBuffers.delete(id);
|
|
947
|
+
}
|
|
948
|
+
};
|
|
1966
949
|
// feat-20260609 M4 / T-10-a: post-process pipeline cache (id|colorFormat -> RhiRenderPipeline).
|
|
1967
950
|
// Solves CONCERN-1: dispatcher previously passed `pipeline=null` to
|
|
1968
951
|
// built.createHandle because per-frame execute closures cannot await async
|
|
@@ -1978,7 +961,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1978
961
|
const key = `${id}|${colorFormat}`;
|
|
1979
962
|
const cached = postProcessPipelineCache.get(key);
|
|
1980
963
|
if (cached !== undefined) return cached;
|
|
1981
|
-
const entry =
|
|
964
|
+
const entry = lookupPostProcess(id);
|
|
1982
965
|
if (entry === undefined) return null;
|
|
1983
966
|
const factory = internals.buildPostProcessPipeline;
|
|
1984
967
|
if (factory === undefined) return null;
|
|
@@ -1987,9 +970,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
1987
970
|
postProcessPipelineCache.set(key, built);
|
|
1988
971
|
return built;
|
|
1989
972
|
};
|
|
1990
|
-
(
|
|
1991
|
-
|
|
1992
|
-
|
|
973
|
+
Object.assign(internals, {
|
|
974
|
+
lookupPostProcess,
|
|
975
|
+
getPostProcessParamsBuffer,
|
|
976
|
+
getPostProcessPipeline,
|
|
977
|
+
});
|
|
1993
978
|
// w15 M5 (plan-strategy D-P4 / AC-07): per-frame dispatch counters. Reset
|
|
1994
979
|
// on every `draw([world], { cameraOwner: 0, resourceOwner: 0 })` entry; bumped once per actual `pass.setPipeline`
|
|
1995
980
|
// dispatch in render-system-record.ts. Two-way split mirrors the two
|
|
@@ -2007,12 +992,10 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2007
992
|
createBindGroup: 0,
|
|
2008
993
|
keys: [],
|
|
2009
994
|
};
|
|
2010
|
-
let preparedWorlds: readonly World[] = [];
|
|
2011
995
|
const preparedPipelineIds = new WeakMap<object, string>();
|
|
2012
996
|
const preparedMaterialPipelineShaders = new WeakMap<object, string>();
|
|
2013
997
|
const preparedGroup0Pipelines = new WeakSet<object>();
|
|
2014
998
|
const preparedViewOnlyPipelines = new WeakSet<object>();
|
|
2015
|
-
const preparedViewWithResourcePipelines = new WeakSet<object>();
|
|
2016
999
|
const preparedRenderMaterialPipelines = new WeakSet<object>();
|
|
2017
1000
|
const preparedAssetHandles = new WeakMap<World, Map<string, number>>();
|
|
2018
1001
|
const preparedHandle = <Brand extends string>(
|
|
@@ -2046,31 +1029,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2046
1029
|
if (world === undefined || material?.kind !== 'material' || pipelineState === null) {
|
|
2047
1030
|
return err(new Error('prepared material asset is unavailable'));
|
|
2048
1031
|
}
|
|
2049
|
-
const
|
|
2050
|
-
const schema = schemaProjection?.schema ?? internals.getParamSchema?.(materialShaderId) ?? [];
|
|
1032
|
+
const schema = internals.getParamSchema?.(materialShaderId) ?? [];
|
|
2051
1033
|
const values = material.values ?? {};
|
|
2052
|
-
const derived =
|
|
2053
|
-
schemaProjection?.derivedInterface ??
|
|
2054
|
-
deriveParamSchemaForCensus(
|
|
2055
|
-
schema,
|
|
2056
|
-
internals.paramSchemaProjectionCensus,
|
|
2057
|
-
'prepared-material',
|
|
2058
|
-
);
|
|
2059
|
-
if (internals.paramSchemaProjectionCensus !== undefined && schemaProjection !== undefined) {
|
|
2060
|
-
internals.paramSchemaProjectionCensus.record(
|
|
2061
|
-
'registered-projection-read',
|
|
2062
|
-
'prepared-material',
|
|
2063
|
-
);
|
|
2064
|
-
if (
|
|
2065
|
-
schemaProjection.schema.length === 0 &&
|
|
2066
|
-
Object.keys(values).some((name) => name.endsWith('Texture'))
|
|
2067
|
-
) {
|
|
2068
|
-
internals.paramSchemaProjectionCensus.record(
|
|
2069
|
-
'empty-schema-legacy-inference',
|
|
2070
|
-
'prepared-material',
|
|
2071
|
-
);
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
1034
|
+
const derived = derive(schema);
|
|
2074
1035
|
const fields = [...derived.textureFieldNames];
|
|
2075
1036
|
let buffer: Buffer | undefined;
|
|
2076
1037
|
let payloadByteLength = 0;
|
|
@@ -2080,7 +1041,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2080
1041
|
payload,
|
|
2081
1042
|
schema,
|
|
2082
1043
|
values as Readonly<Record<string, number | readonly number[]>>,
|
|
2083
|
-
internals.paramSchemaProjectionCensus,
|
|
2084
1044
|
);
|
|
2085
1045
|
const created = internals.device.createBuffer({
|
|
2086
1046
|
label: `prepared-material:${materialGuid}`,
|
|
@@ -2111,13 +1071,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2111
1071
|
if (textureValue?.sampler !== undefined) {
|
|
2112
1072
|
const handle = preparedHandle(world, String(textureValue.sampler), 'SamplerAsset');
|
|
2113
1073
|
if (handle !== undefined) {
|
|
2114
|
-
const asset =
|
|
1074
|
+
const asset = resolveAssetHandle<SamplerAsset>(world, handle);
|
|
2115
1075
|
if (asset.ok) {
|
|
2116
|
-
const resident = internals.gpuStore.ensureSamplerResident(
|
|
2117
|
-
handle,
|
|
2118
|
-
asset.value,
|
|
2119
|
-
worldIndex,
|
|
2120
|
-
);
|
|
1076
|
+
const resident = internals.gpuStore.ensureSamplerResident(handle, asset.value, world);
|
|
2121
1077
|
if (resident.ok) sampler = resident.value;
|
|
2122
1078
|
}
|
|
2123
1079
|
}
|
|
@@ -2126,8 +1082,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2126
1082
|
if (textureValue !== undefined) {
|
|
2127
1083
|
const handle = preparedHandle(world, String(textureValue.texture), 'TextureAsset');
|
|
2128
1084
|
if (handle !== undefined) {
|
|
2129
|
-
view =
|
|
2130
|
-
residentTextureView(world, internals.gpuStore, internals, handle, worldIndex) ?? view;
|
|
1085
|
+
view = residentTextureView(world, internals.gpuStore, internals, handle) ?? view;
|
|
2131
1086
|
}
|
|
2132
1087
|
}
|
|
2133
1088
|
textureResources.push({ sampler, view });
|
|
@@ -2220,8 +1175,10 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2220
1175
|
let lastMeshMaterialBindingFrame: ExtractedFrame | undefined;
|
|
2221
1176
|
const preparedResolverFactory = (
|
|
2222
1177
|
input: RenderFeaturePreparedGraphicsResolverInput,
|
|
2223
|
-
): PreparedGraphicsResolver =>
|
|
2224
|
-
|
|
1178
|
+
): PreparedGraphicsResolver => {
|
|
1179
|
+
activeFeaturePostProcessEntries = input.fullscreenEffects;
|
|
1180
|
+
ensurePostProcessParamsResources(input.fullscreenEffects);
|
|
1181
|
+
return createPreparedGraphicsResolver({
|
|
2225
1182
|
device: internals.device,
|
|
2226
1183
|
featureIdentity: input.featureIdentity,
|
|
2227
1184
|
generation: input.generation,
|
|
@@ -2229,9 +1186,11 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2229
1186
|
featureOrder: input.order,
|
|
2230
1187
|
lookup: input.lookup,
|
|
2231
1188
|
resolveGpuBuffer: (reference) =>
|
|
2232
|
-
|
|
1189
|
+
featureGpuWork.resolveBuffer(input.featureIdentity, reference),
|
|
2233
1190
|
resolvePipeline: (descriptor) => {
|
|
2234
|
-
const postProcessEntry =
|
|
1191
|
+
const postProcessEntry =
|
|
1192
|
+
input.fullscreenEffects.get(descriptor.shader) ??
|
|
1193
|
+
builtinPostProcessEntries.get(descriptor.shader);
|
|
2235
1194
|
if (postProcessEntry !== undefined) {
|
|
2236
1195
|
const fullscreen = buildFullscreenPostProcessPass(
|
|
2237
1196
|
{ device: internals.device, errorRegistry: internals.errorRegistry },
|
|
@@ -2286,8 +1245,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2286
1245
|
preparedGroup0Pipelines.add(pipeline as object);
|
|
2287
1246
|
} else if (bindingContract === 'view-only') {
|
|
2288
1247
|
preparedViewOnlyPipelines.add(pipeline as object);
|
|
2289
|
-
} else if (bindingContract === 'view-with-resource') {
|
|
2290
|
-
preparedViewWithResourcePipelines.add(pipeline as object);
|
|
2291
1248
|
} else {
|
|
2292
1249
|
preparedRenderMaterialPipelines.add(pipeline as object);
|
|
2293
1250
|
}
|
|
@@ -2305,6 +1262,9 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2305
1262
|
? err(new Error('prepared group-0 resource pipeline requires a scene target'))
|
|
2306
1263
|
: ok(undefined);
|
|
2307
1264
|
}
|
|
1265
|
+
if (bindingContract === 'view-and-scene-depth') {
|
|
1266
|
+
return ok(undefined);
|
|
1267
|
+
}
|
|
2308
1268
|
if (preparedGroup0Pipelines.has(pipeline as object)) {
|
|
2309
1269
|
const layout =
|
|
2310
1270
|
(materialShaderId === undefined
|
|
@@ -2321,7 +1281,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2321
1281
|
}
|
|
2322
1282
|
if (
|
|
2323
1283
|
preparedViewOnlyPipelines.has(pipeline as object) ||
|
|
2324
|
-
preparedViewWithResourcePipelines.has(pipeline as object) ||
|
|
2325
1284
|
(preparedRenderMaterialPipelines.has(pipeline as object) &&
|
|
2326
1285
|
descriptor.values.group === 0) ||
|
|
2327
1286
|
pipeline === internals.getPipelineState()?.unlitPipeline
|
|
@@ -2396,60 +1355,29 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2396
1355
|
});
|
|
2397
1356
|
},
|
|
2398
1357
|
});
|
|
1358
|
+
};
|
|
2399
1359
|
return {
|
|
2400
|
-
applyShadowSubmitMode(
|
|
2401
|
-
mode: ShadowSubmitMode,
|
|
2402
|
-
rendererGeneration: string,
|
|
2403
|
-
): ShadowSubmitResult<ShadowSubmitModeState> {
|
|
2404
|
-
const result = applyShadowSubmitMode(
|
|
2405
|
-
frameState.shadowSubmitModeState,
|
|
2406
|
-
mode,
|
|
2407
|
-
rendererGeneration,
|
|
2408
|
-
);
|
|
2409
|
-
if (result.ok) frameState.shadowSubmitModeState = result.value;
|
|
2410
|
-
return result;
|
|
2411
|
-
},
|
|
2412
|
-
restoreShadowSubmitMode(rendererGeneration: string): ShadowSubmitResult<ShadowSubmitModeState> {
|
|
2413
|
-
const result = restoreShadowSubmitMode(frameState.shadowSubmitModeState, rendererGeneration);
|
|
2414
|
-
if (result.ok) {
|
|
2415
|
-
frameState.shadowSubmitModeState = result.value;
|
|
2416
|
-
frameState.pendingShadowCommandBuffers = [];
|
|
2417
|
-
}
|
|
2418
|
-
return result;
|
|
2419
|
-
},
|
|
2420
|
-
getShadowSubmitMode(): ShadowSubmitModeState {
|
|
2421
|
-
return frameState.shadowSubmitModeState;
|
|
2422
|
-
},
|
|
2423
1360
|
releaseProfilerCatalog(): void {
|
|
2424
1361
|
releaseProfilerCatalog?.();
|
|
2425
1362
|
releaseProfilerCatalog = undefined;
|
|
2426
1363
|
},
|
|
2427
1364
|
get renderScene(): RenderSceneInspection {
|
|
2428
|
-
const temporal = projectTemporalInspection(temporalFrameTransaction.inspect());
|
|
2429
1365
|
return {
|
|
2430
1366
|
...persistentRenderScene.inspect(),
|
|
2431
1367
|
gpuDriven: gpuDrivenProduction.inspect(),
|
|
2432
|
-
temporal,
|
|
2433
|
-
environment: inspectEnvironmentGeneration(frameState.environmentGeneration, {
|
|
2434
|
-
sourceWorldId: environmentConsumerSnapshot(frameState.environmentGeneration)?.source
|
|
2435
|
-
?.worldId,
|
|
2436
|
-
sourceEntity: environmentConsumerSnapshot(frameState.environmentGeneration)?.source
|
|
2437
|
-
?.entity,
|
|
2438
|
-
taa: {
|
|
2439
|
-
activeSignature: temporal.environmentSignature,
|
|
2440
|
-
publishCount: temporal.environmentPublishCount,
|
|
2441
|
-
resetCount: temporal.environmentResetCount,
|
|
2442
|
-
lastResetReason: temporal.resetReason,
|
|
2443
|
-
},
|
|
2444
|
-
}),
|
|
2445
1368
|
};
|
|
2446
1369
|
},
|
|
2447
|
-
|
|
2448
|
-
|
|
1370
|
+
get pointsLinesSnapshots(): readonly PointsLinesRetainedSnapshot[] {
|
|
1371
|
+
return persistentRenderScene.pointsLinesSnapshots();
|
|
1372
|
+
},
|
|
1373
|
+
detachScene(world: World): void {
|
|
2449
1374
|
persistentRenderScene.detach(world);
|
|
2450
|
-
materialSnapshotCachesByWorld.delete(world);
|
|
2451
1375
|
},
|
|
2452
|
-
draw(
|
|
1376
|
+
draw(
|
|
1377
|
+
worlds: readonly World[],
|
|
1378
|
+
opts: DrawOwnerOptions,
|
|
1379
|
+
renderReadLeases?: readonly RenderReadLease[],
|
|
1380
|
+
): boolean {
|
|
2453
1381
|
preparedWorlds = worlds;
|
|
2454
1382
|
const profileSession = internals.profiler?.activeSession();
|
|
2455
1383
|
let ownsProfileFrame = false;
|
|
@@ -2462,6 +1390,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2462
1390
|
}
|
|
2463
1391
|
}
|
|
2464
1392
|
try {
|
|
1393
|
+
pointsLinesOwner.beginFrame();
|
|
2465
1394
|
// cameraOwner drives the surfaced cameras + frustum
|
|
2466
1395
|
// cull; resourceOwner drives skylight/skybox/postProcess + per-world
|
|
2467
1396
|
// record config.
|
|
@@ -2471,7 +1400,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2471
1400
|
// replacement retires it atomically in ensureCompiledFrameGraph.
|
|
2472
1401
|
if (frameState.installedPipelineHandle !== lastBuiltPipelineHandle) {
|
|
2473
1402
|
// Feature contributions describe the active pipeline graph. Drop the
|
|
2474
|
-
// old
|
|
1403
|
+
// old graph before the next frame re-runs feature contribution
|
|
2475
1404
|
// so a hot-swap cannot reuse passes compiled for the retired pipeline.
|
|
2476
1405
|
resetRenderFeatureGraphState(internals);
|
|
2477
1406
|
lastBuiltPipelineHandle = frameState.installedPipelineHandle;
|
|
@@ -2502,183 +1431,35 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2502
1431
|
// are resource-owner reads, not camera reads).
|
|
2503
1432
|
const resourceWorld = worlds[resourceOwner] as World;
|
|
2504
1433
|
const frame = runProfiledRenderPhase(profileSession, 'extract', () => {
|
|
2505
|
-
|
|
2506
|
-
worlds
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
worlds,
|
|
2510
|
-
{ cameraOwner, resourceOwner },
|
|
2511
|
-
internals.assets.epoch,
|
|
2512
|
-
() =>
|
|
2513
|
-
extractFrames(
|
|
2514
|
-
worlds,
|
|
2515
|
-
{ cameraOwner, resourceOwner },
|
|
2516
|
-
internals.assets,
|
|
2517
|
-
internals.getPipelineState(),
|
|
2518
|
-
internals.gpuStore,
|
|
2519
|
-
materialSnapshotCachesByWorld,
|
|
2520
|
-
{
|
|
2521
|
-
cull: 'none',
|
|
2522
|
-
...(internals.paramSchemaProjectionCensus === undefined
|
|
2523
|
-
? {}
|
|
2524
|
-
: { paramSchemaProjectionCensus: internals.paramSchemaProjectionCensus }),
|
|
2525
|
-
},
|
|
2526
|
-
),
|
|
2527
|
-
(world, worldId, entity, currentFrame) => {
|
|
2528
|
-
let materialSnapshotCache = materialSnapshotCachesByWorld.get(world);
|
|
2529
|
-
if (materialSnapshotCache === undefined) {
|
|
2530
|
-
materialSnapshotCache = new Map();
|
|
2531
|
-
materialSnapshotCachesByWorld.set(world, materialSnapshotCache);
|
|
2532
|
-
}
|
|
2533
|
-
const visibility = currentFrame.featureVisibilitySnapshots.find(
|
|
2534
|
-
(entry) => entry.world === world,
|
|
2535
|
-
)?.snapshot ?? {
|
|
2536
|
-
diagnostics: [],
|
|
2537
|
-
hasAnyIntent: false,
|
|
2538
|
-
hasAnyHiddenIntent: false,
|
|
2539
|
-
get: () => undefined,
|
|
2540
|
-
effective: () => 'visible' as const,
|
|
2541
|
-
};
|
|
2542
|
-
const extracted = extractRenderableEntity(world, entity, {
|
|
2543
|
-
assets: internals.assets,
|
|
2544
|
-
pipelineState: internals.getPipelineState(),
|
|
2545
|
-
gpuStore: internals.gpuStore,
|
|
2546
|
-
materialSnapshotCache,
|
|
2547
|
-
...(internals.paramSchemaProjectionCensus === undefined
|
|
2548
|
-
? {}
|
|
2549
|
-
: { paramSchemaProjectionCensus: internals.paramSchemaProjectionCensus }),
|
|
2550
|
-
cull: 'none',
|
|
2551
|
-
cullCameras: undefined,
|
|
2552
|
-
hierarchy: {
|
|
2553
|
-
parentOf: new Map(),
|
|
2554
|
-
diagnostics: [],
|
|
2555
|
-
getParent: () => undefined,
|
|
2556
|
-
},
|
|
2557
|
-
visibility,
|
|
2558
|
-
});
|
|
2559
|
-
if (extracted === undefined) return undefined;
|
|
2560
|
-
return {
|
|
2561
|
-
snapshot:
|
|
2562
|
-
extracted.snapshot.worldId === worldId
|
|
2563
|
-
? extracted.snapshot
|
|
2564
|
-
: { ...extracted.snapshot, worldId },
|
|
2565
|
-
candidateDispatch: extracted.candidateDispatch,
|
|
2566
|
-
};
|
|
2567
|
-
},
|
|
2568
|
-
);
|
|
2569
|
-
}
|
|
2570
|
-
const pipelineState = internals.getPipelineState();
|
|
2571
|
-
pipelineState?.skinPaletteAllocator?.resetForFrame();
|
|
2572
|
-
let materialSnapshotCache = materialSnapshotCachesByWorld.get(ordinaryWorld);
|
|
2573
|
-
if (materialSnapshotCache === undefined) {
|
|
2574
|
-
materialSnapshotCache = new Map();
|
|
2575
|
-
materialSnapshotCachesByWorld.set(ordinaryWorld, materialSnapshotCache);
|
|
2576
|
-
}
|
|
2577
|
-
return persistentRenderScene.extract(
|
|
2578
|
-
ordinaryWorld,
|
|
2579
|
-
internals.assets.epoch,
|
|
1434
|
+
return persistentRenderScene.extractComposition(
|
|
1435
|
+
worlds,
|
|
1436
|
+
{ cameraOwner, resourceOwner },
|
|
1437
|
+
internals.assets.catalogEpoch,
|
|
2580
1438
|
() =>
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
assets: internals.assets,
|
|
2585
|
-
pipelineState,
|
|
2586
|
-
gpuStore: internals.gpuStore,
|
|
2587
|
-
materialSnapshotCache,
|
|
2588
|
-
...(internals.paramSchemaProjectionCensus === undefined
|
|
2589
|
-
? {}
|
|
2590
|
-
: { paramSchemaProjectionCensus: internals.paramSchemaProjectionCensus }),
|
|
2591
|
-
cull: 'none',
|
|
2592
|
-
}),
|
|
2593
|
-
),
|
|
2594
|
-
(snapshot, materialHandle) =>
|
|
2595
|
-
refreshRenderableMaterialSnapshot(
|
|
2596
|
-
snapshot,
|
|
2597
|
-
materialHandle,
|
|
2598
|
-
ordinaryWorld,
|
|
1439
|
+
extractFrames(
|
|
1440
|
+
worlds,
|
|
1441
|
+
{ cameraOwner, resourceOwner },
|
|
2599
1442
|
internals.assets,
|
|
2600
|
-
internals.
|
|
2601
|
-
|
|
2602
|
-
|
|
1443
|
+
internals.getPipelineState(),
|
|
1444
|
+
persistentRenderScene.materialSnapshotCacheStore(),
|
|
1445
|
+
{ cull: 'none' },
|
|
2603
1446
|
),
|
|
2604
|
-
|
|
2605
|
-
`${internals.gpuStore.meshResidencyEpoch}:${frameState.isHdrpActive ? 'hdrp' : 'urp'}`,
|
|
2606
|
-
(current) => refreshExtractedFramePostProcessParams(ordinaryWorld, current),
|
|
2607
|
-
(current) => refreshExtractedFrameLights(ordinaryWorld, current),
|
|
2608
|
-
(entity: EntityHandle, currentFrame) =>
|
|
2609
|
-
extractRenderableEntity(ordinaryWorld, entity, {
|
|
2610
|
-
assets: internals.assets,
|
|
2611
|
-
pipelineState,
|
|
2612
|
-
gpuStore: internals.gpuStore,
|
|
2613
|
-
materialSnapshotCache,
|
|
2614
|
-
cull: 'none',
|
|
2615
|
-
cullCameras: undefined,
|
|
2616
|
-
hierarchy: {
|
|
2617
|
-
parentOf: new Map(),
|
|
2618
|
-
diagnostics: [],
|
|
2619
|
-
getParent: () => undefined,
|
|
2620
|
-
},
|
|
2621
|
-
visibility: currentFrame.visibilitySnapshots[0] ?? {
|
|
2622
|
-
diagnostics: [],
|
|
2623
|
-
hasAnyIntent: false,
|
|
2624
|
-
hasAnyHiddenIntent: false,
|
|
2625
|
-
get: () => undefined,
|
|
2626
|
-
effective: () => 'visible',
|
|
2627
|
-
},
|
|
2628
|
-
}),
|
|
1447
|
+
renderReadLeases,
|
|
2629
1448
|
);
|
|
2630
1449
|
});
|
|
2631
|
-
const fogSelection = selectFog(resourceWorld, resourceOwner);
|
|
2632
|
-
if (fogSelection.ok) {
|
|
2633
|
-
persistentRenderScene.stageFogCandidate(fogSelection.value);
|
|
2634
|
-
} else {
|
|
2635
|
-
// Fog authoring failures are renderer-owned structured errors. Keep
|
|
2636
|
-
// the retained LKG candidate while publishing the same error object
|
|
2637
|
-
// through Renderer.onError for AI recovery.
|
|
2638
|
-
internals.errorRegistry.fire(fogSelection.error);
|
|
2639
|
-
persistentRenderScene.stageFogFailure({
|
|
2640
|
-
code: fogSelection.error.code,
|
|
2641
|
-
resourceOwner,
|
|
2642
|
-
});
|
|
2643
|
-
}
|
|
2644
1450
|
const {
|
|
2645
1451
|
cameras,
|
|
2646
1452
|
lights,
|
|
2647
|
-
renderables
|
|
1453
|
+
renderables,
|
|
2648
1454
|
dispatch,
|
|
2649
|
-
|
|
1455
|
+
skylight,
|
|
1456
|
+
skylightCount,
|
|
1457
|
+
skybox,
|
|
1458
|
+
skyboxCount,
|
|
2650
1459
|
frustumStats,
|
|
2651
1460
|
visibilityStats,
|
|
2652
1461
|
postProcessParams,
|
|
2653
1462
|
} = frame;
|
|
2654
|
-
const temporalProjection = persistentRenderScene.temporalProjection(worlds);
|
|
2655
|
-
const renderables =
|
|
2656
|
-
temporalProjection === undefined
|
|
2657
|
-
? extractedRenderables
|
|
2658
|
-
: extractedRenderables.map((snapshot) => temporalProjection.withTemporal(snapshot));
|
|
2659
|
-
const skinPaletteAllocator = internals.getPipelineState()?.skinPaletteAllocator;
|
|
2660
|
-
if (skinPaletteAllocator !== null && skinPaletteAllocator !== undefined) {
|
|
2661
|
-
for (const renderable of renderables) {
|
|
2662
|
-
const skin = renderable.skin;
|
|
2663
|
-
if (skin === undefined) continue;
|
|
2664
|
-
skinPaletteAllocator.writePreviousJointPalette(
|
|
2665
|
-
skin,
|
|
2666
|
-
renderable.temporal?.previousSkin?.jointMatrices ?? skin.jointMatrices,
|
|
2667
|
-
);
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
const gpuDrivenVisibilityScene =
|
|
2671
|
-
worlds.length === 1 && cameraOwner === 0 && resourceOwner === 0
|
|
2672
|
-
? persistentRenderScene.gpuDrivenState(worlds[0])
|
|
2673
|
-
: persistentRenderScene.compositionGpuDrivenState();
|
|
2674
|
-
// Publish one main + three-cascade visibility result before record.
|
|
2675
|
-
// The production batch topology joins these stable IDs once and both
|
|
2676
|
-
// indirect-capable and direct-RHI executors consume that submission.
|
|
2677
|
-
gpuDrivenProduction.prepareCpuVisibility({
|
|
2678
|
-
scene: gpuDrivenVisibilityScene,
|
|
2679
|
-
frame,
|
|
2680
|
-
camera: cameras[0],
|
|
2681
|
-
});
|
|
2682
1463
|
if (lastMeshMaterialBindingFrame !== frame) {
|
|
2683
1464
|
lastMeshMaterialBindingFrame = frame;
|
|
2684
1465
|
lastMeshMaterialBindings = renderables.map((renderable) => ({
|
|
@@ -2731,13 +1512,13 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2731
1512
|
preparedPipelineState === null || cameras[0] === undefined
|
|
2732
1513
|
? []
|
|
2733
1514
|
: resolveStandardRenderFeatureTargets({
|
|
2734
|
-
pipelineId: frameState.isHdrpActive ? 'forgeax::hdrp' : 'forgeax::urp',
|
|
2735
1515
|
tonemap: cameras[0].tonemap,
|
|
2736
1516
|
antialias: cameras[0].antialias,
|
|
2737
1517
|
colorAttachmentFormat: preparedPipelineState.colorAttachmentFormat,
|
|
2738
1518
|
storageBuffer: internals.device.caps.storageBuffer,
|
|
2739
1519
|
multisample: internals.device.caps.backendKind !== 'wgpu-webgl2',
|
|
2740
1520
|
});
|
|
1521
|
+
let featureGraphCandidate: RenderFeatureGraphCandidate | undefined;
|
|
2741
1522
|
const preparedResourceBatches = runProfiledRenderPhase(profileSession, 'features', () => {
|
|
2742
1523
|
if (internals.featureHost === undefined) return [];
|
|
2743
1524
|
const featureFrame = runRenderFeatureFrame(internals.featureHost, {
|
|
@@ -2749,54 +1530,32 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2749
1530
|
targets: featureTargets,
|
|
2750
1531
|
generation: internals.featureHost?.preparedGeneration ?? 0,
|
|
2751
1532
|
caps: internals.device.caps,
|
|
2752
|
-
createContributionStaging: (
|
|
2753
|
-
identity,
|
|
2754
|
-
order,
|
|
2755
|
-
validateGraphics,
|
|
2756
|
-
resolveGraphics,
|
|
2757
|
-
resolveGpuCompute,
|
|
2758
|
-
) =>
|
|
2759
|
-
createRenderFeatureContributionStaging<RenderFeaturePassContext>(
|
|
2760
|
-
identity,
|
|
2761
|
-
order,
|
|
2762
|
-
validateGraphics,
|
|
2763
|
-
resolveGraphics,
|
|
2764
|
-
resolveGpuCompute,
|
|
2765
|
-
),
|
|
2766
1533
|
createPreparedGraphicsResolver: preparedResolverFactory,
|
|
2767
|
-
|
|
2768
|
-
? {}
|
|
2769
|
-
: { getMaterialShaderBindingContract: internals.getMaterialShaderBindingContract }),
|
|
2770
|
-
resolveMaterialAsset: (material) => resolveMaterialFromPort(material, internals.assets),
|
|
2771
|
-
getGpuWorkResolver: getFeatureGpuWork,
|
|
1534
|
+
gpuWork: featureGpuWork,
|
|
2772
1535
|
});
|
|
1536
|
+
activeFeaturePostProcessEntries = featureFrame.fullscreenEffects;
|
|
1537
|
+
ensurePostProcessParamsResources(featureFrame.fullscreenEffects);
|
|
2773
1538
|
lastVisibilityStats.explicitlyHidden = featureFrame.hiddenEntityReports.length;
|
|
2774
1539
|
for (const featureError of featureFrame.errors) {
|
|
2775
1540
|
if (!isPendingRenderFeaturePreparation(featureError)) {
|
|
2776
1541
|
internals.errorRegistry.fire(featureError);
|
|
2777
1542
|
}
|
|
2778
1543
|
}
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
featureGraphState.topologySignature = merged.value.topologySignature;
|
|
2795
|
-
if (topologyChanged) {
|
|
2796
|
-
retireCompiledFrameGraph(frameState, taaHistoryStore);
|
|
2797
|
-
featureGraphState.composition = undefined;
|
|
2798
|
-
}
|
|
2799
|
-
}
|
|
1544
|
+
featureGraphCandidate = {
|
|
1545
|
+
plans: featureFrame.plans,
|
|
1546
|
+
fullscreenEffects: featureFrame.fullscreenEffects,
|
|
1547
|
+
...(featureFrame.preparedResourceBatches.length === 0
|
|
1548
|
+
? {}
|
|
1549
|
+
: { preparedResourceKey: `frame-${frameState.frameNumber}` }),
|
|
1550
|
+
onRejected: () => {
|
|
1551
|
+
for (const batch of featureFrame.preparedResourceBatches) {
|
|
1552
|
+
const released = batch.release();
|
|
1553
|
+
if (!released.ok) {
|
|
1554
|
+
internals.errorRegistry.fire(released.error);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
},
|
|
1558
|
+
};
|
|
2800
1559
|
return featureFrame.preparedResourceBatches;
|
|
2801
1560
|
});
|
|
2802
1561
|
|
|
@@ -2809,11 +1568,6 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2809
1568
|
const orderedDispatch = runProfiledRenderPhase(profileSession, 'sort', () =>
|
|
2810
1569
|
sortTransparentDispatch(dispatch, resourceWorld, cameras, renderables),
|
|
2811
1570
|
);
|
|
2812
|
-
const temporalCoverage = sceneTemporalCoverage({
|
|
2813
|
-
renderables,
|
|
2814
|
-
dispatch: orderedDispatch,
|
|
2815
|
-
features: getRenderFeatureGraphState(internals).contributions,
|
|
2816
|
-
});
|
|
2817
1571
|
|
|
2818
1572
|
// M3 / w26: single dispatch list replaces old three-bucket model.
|
|
2819
1573
|
// Pass dispatch to recordFrame — the record stage iterates dispatch
|
|
@@ -2822,17 +1576,17 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2822
1576
|
// feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2):
|
|
2823
1577
|
// the record stage must resolve each renderable's mesh + material
|
|
2824
1578
|
// textures against the world it was EXTRACTED from — never the single
|
|
2825
|
-
// resourceWorld.
|
|
2826
|
-
//
|
|
2827
|
-
//
|
|
2828
|
-
// cameraOwner world
|
|
1579
|
+
// resourceWorld. The extract stage already resolves per-world
|
|
1580
|
+
// (extractFrames loops worlds[] and stamps RenderableSnapshot.worldId);
|
|
1581
|
+
// the record stage regressed to resolving everything against
|
|
1582
|
+
// resourceWorld, so a user-tier mesh living in the cameraOwner world
|
|
2829
1583
|
// (e.g. an editor gizmo handle) resolves against resourceWorld's
|
|
2830
1584
|
// sharedRefs — either a miss (asset-not-registered) or, when that slot
|
|
2831
|
-
// is occupied by an unrelated payload (equirect), a
|
|
1585
|
+
// is occupied by an unrelated user-tier payload (equirect), a
|
|
2832
1586
|
// wrong-kind resolve that throws in ensureResident. `worlds` is threaded
|
|
2833
|
-
// so the record stage
|
|
2834
|
-
//
|
|
2835
|
-
//
|
|
1587
|
+
// so the record stage can index worlds[renderable.worldId]. `resourceWorld`
|
|
1588
|
+
// stays the singleton-resource owner (skybox equirect / transparent-sort
|
|
1589
|
+
// config / video provider) — those ARE resource-owner reads.
|
|
2836
1590
|
const recordProfilePhase: RecordProfileRunner | undefined =
|
|
2837
1591
|
profileSession === undefined || profileSession.detail === 'owner'
|
|
2838
1592
|
? undefined
|
|
@@ -2862,43 +1616,27 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2862
1616
|
frameState,
|
|
2863
1617
|
dispatchCounts,
|
|
2864
1618
|
bindGroupCounts,
|
|
2865
|
-
|
|
1619
|
+
skylight,
|
|
1620
|
+
skylightCount,
|
|
1621
|
+
skybox,
|
|
1622
|
+
skyboxCount,
|
|
2866
1623
|
postProcessParams,
|
|
2867
1624
|
worlds,
|
|
2868
1625
|
recordProfilePhase,
|
|
2869
1626
|
{
|
|
2870
1627
|
owner: gpuDrivenProduction,
|
|
2871
|
-
scene:
|
|
2872
|
-
worlds.length === 1 && cameraOwner === 0 && resourceOwner === 0
|
|
2873
|
-
? persistentRenderScene.gpuDrivenState(worlds[0])
|
|
2874
|
-
: persistentRenderScene.compositionGpuDrivenState(),
|
|
1628
|
+
scene: persistentRenderScene.compositionGpuDrivenState(),
|
|
2875
1629
|
onCoverage: (ownsAll) => {
|
|
2876
1630
|
const key = `${internals.gpuStore.meshResidencyEpoch}:${frameState.isHdrpActive ? 'hdrp' : 'urp'}`;
|
|
2877
|
-
|
|
2878
|
-
const ordinaryWorld = worlds[0];
|
|
2879
|
-
if (ordinaryWorld !== undefined) {
|
|
2880
|
-
persistentRenderScene.setGpuDrivenCoverage(ordinaryWorld, key, ownsAll);
|
|
2881
|
-
}
|
|
2882
|
-
} else {
|
|
2883
|
-
persistentRenderScene.setCompositionGpuDrivenCoverage(key, ownsAll);
|
|
2884
|
-
}
|
|
1631
|
+
persistentRenderScene.setCompositionGpuDrivenCoverage(key, ownsAll);
|
|
2885
1632
|
},
|
|
2886
1633
|
},
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
: {
|
|
2891
|
-
transaction: temporalFrameTransaction,
|
|
2892
|
-
projection: temporalProjection,
|
|
2893
|
-
coverage: temporalCoverage,
|
|
2894
|
-
...(fogSelection.ok
|
|
2895
|
-
? { reactiveRevision: fogSelection.value.snapshot?.revision }
|
|
2896
|
-
: {}),
|
|
2897
|
-
onSceneSubmitted: (delta) => persistentRenderScene.stageTemporalSubmission(delta),
|
|
2898
|
-
},
|
|
2899
|
-
fogSelection.ok ? fogSelection.value.snapshot : persistentRenderScene.fogSnapshot(),
|
|
1634
|
+
renderReadLeases,
|
|
1635
|
+
featureGraphCandidate,
|
|
1636
|
+
pointsLinesOwner,
|
|
2900
1637
|
),
|
|
2901
1638
|
);
|
|
1639
|
+
persistentRenderScene.setPointsLinesInspections(pointsLinesOwner.inspections());
|
|
2902
1640
|
if (internals.featureHost !== undefined && preparedResourceBatches.length > 0) {
|
|
2903
1641
|
const batches = preparedResourceBatches;
|
|
2904
1642
|
if (submitted) {
|
|
@@ -2915,25 +1653,19 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2915
1653
|
}
|
|
2916
1654
|
}
|
|
2917
1655
|
} catch (err) {
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
detail: { error: innerError },
|
|
2931
|
-
}),
|
|
2932
|
-
);
|
|
2933
|
-
}
|
|
1656
|
+
const innerError =
|
|
1657
|
+
err instanceof RhiError
|
|
1658
|
+
? err
|
|
1659
|
+
: { code: 'unknown' as const, message: String(err), name: (err as Error)?.name };
|
|
1660
|
+
internals.errorRegistry.fire(
|
|
1661
|
+
new RhiError({
|
|
1662
|
+
code: 'webgpu-runtime-error',
|
|
1663
|
+
expected: 'RenderSystem to record one frame without an internal exception',
|
|
1664
|
+
hint: 'inspect detail.error for the underlying cause; next frame will retry',
|
|
1665
|
+
detail: { error: innerError },
|
|
1666
|
+
}),
|
|
1667
|
+
);
|
|
2934
1668
|
} finally {
|
|
2935
|
-
if (submitted) persistentRenderScene.stageFogSubmission();
|
|
2936
|
-
else persistentRenderScene.discardFogCandidate();
|
|
2937
1669
|
if (ownsProfileFrame) {
|
|
2938
1670
|
try {
|
|
2939
1671
|
profileSession?.endFrame();
|
|
@@ -2958,158 +1690,102 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
2958
1690
|
get perFramePassNames(): readonly string[] {
|
|
2959
1691
|
return frameState.compiledFrameGraph?.inspect().passes.map((pass) => pass.name) ?? [];
|
|
2960
1692
|
},
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
if (
|
|
2974
|
-
|
|
2975
|
-
new PipelineError({ code: 'pipeline-not-found', detail: { handle: installEpoch } }),
|
|
2976
|
-
);
|
|
2977
|
-
}
|
|
2978
|
-
// feat-20260608-cluster-lighting M2 / w10: HDRP grid validation.
|
|
2979
|
-
// When the pipelineId is 'forgeax::hdrp', validate clusterGrid dimensions
|
|
2980
|
-
// before installing. Grid-invalid throws a standalone HdrpInstallError
|
|
2981
|
-
// (NOT in RuntimeErrorCode; synchronous install-time config error).
|
|
2982
|
-
//
|
|
2983
|
-
// feat-20260608-cluster-lighting M5 / w20: HDRP caps gate.
|
|
2984
|
-
// A zero storage-buffer ceiling is the supported WebGL2 downlevel path:
|
|
2985
|
-
// HDRP's cluster light list is transported through a bounded uniform
|
|
2986
|
-
// array. Partial storage support (1..3 slots) is still unsupported
|
|
2987
|
-
// because it cannot satisfy either the clustered or the complete
|
|
2988
|
-
// uniform fallback contract.
|
|
2989
|
-
if (asset.pipelineId === 'forgeax::hdrp') {
|
|
2990
|
-
const storageBuffersCap = internals.device.limits.maxStorageBuffersPerShaderStage;
|
|
2991
|
-
if (storageBuffersCap > 0 && storageBuffersCap < 4) {
|
|
2992
|
-
throw new HdrpCapsInsufficientError(
|
|
2993
|
-
'maxStorageBuffersPerShaderStage',
|
|
2994
|
-
storageBuffersCap,
|
|
2995
|
-
4,
|
|
2996
|
-
);
|
|
2997
|
-
}
|
|
2998
|
-
const grid = asset.config?.clusterGrid ?? { x: 16, y: 9, z: 24 };
|
|
1693
|
+
configureStandard(config: RenderPipelineAsset['config']): void {
|
|
1694
|
+
const profileConfig = internals.standardProfile;
|
|
1695
|
+
const resolvedConfig =
|
|
1696
|
+
profileConfig === undefined
|
|
1697
|
+
? config
|
|
1698
|
+
: {
|
|
1699
|
+
...(profileConfig.lighting === 'clustered'
|
|
1700
|
+
? { clusterGrid: DEFAULT_CLUSTER_GRID }
|
|
1701
|
+
: {}),
|
|
1702
|
+
...(profileConfig.ssao ? { ssao: { enabled: true } } : {}),
|
|
1703
|
+
...config,
|
|
1704
|
+
};
|
|
1705
|
+
if (resolvedConfig?.clusterGrid !== undefined) {
|
|
1706
|
+
const grid = resolvedConfig.clusterGrid;
|
|
2999
1707
|
const gridResult = validateClusterGrid(grid);
|
|
3000
1708
|
if (!gridResult.ok) {
|
|
3001
1709
|
throw gridResult.error;
|
|
3002
1710
|
}
|
|
3003
1711
|
}
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
frameState.activePipeline = install.pipeline;
|
|
3013
|
-
frameState.activePipelineId = asset.pipelineId;
|
|
3014
|
-
// installPipeline no longer carries a handle (D-19: RenderPipelineAsset is
|
|
3015
|
-
// installed as a POD at boot/swap time before any World exists). The
|
|
1712
|
+
if (resolvedConfig?.ssao?.enabled === true) {
|
|
1713
|
+
const ssaoResult = resolveSsaoParameters(resolvedConfig.ssao);
|
|
1714
|
+
if (!ssaoResult.ok) {
|
|
1715
|
+
throw ssaoResult.error;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
// Standard configuration no longer carries a handle (D-19: RenderPipelineAsset is
|
|
1719
|
+
// supplied as a POD at boot/swap time before a World exists). The
|
|
3016
1720
|
// brand-number that `draw` compares to force a per-frame graph rebuild is
|
|
3017
1721
|
// now a monotonic epoch bumped on every install -- distinct configs (and
|
|
3018
1722
|
// even identical re-installs) trigger the rebuild, which is correct: install
|
|
3019
1723
|
// is a rare boot/swap event, never a per-frame cost.
|
|
3020
1724
|
installEpoch += 1;
|
|
3021
1725
|
frameState.installedPipelineHandle = installEpoch;
|
|
3022
|
-
frameState.isHdrpActive =
|
|
3023
|
-
|
|
3024
|
-
frameState.installedPipelineConfig = install.config;
|
|
3025
|
-
let active = true;
|
|
3026
|
-
return ok(() => {
|
|
3027
|
-
if (!active) return;
|
|
3028
|
-
active = false;
|
|
3029
|
-
const index = pipelineInstalls.findIndex((candidate) => candidate.token === install.token);
|
|
3030
|
-
if (index < 0) return;
|
|
3031
|
-
const wasCurrent = index === pipelineInstalls.length - 1;
|
|
3032
|
-
pipelineInstalls.splice(index, 1);
|
|
3033
|
-
if (!wasCurrent) return;
|
|
3034
|
-
const current = pipelineInstalls.at(-1);
|
|
3035
|
-
frameState.activePipeline = current?.pipeline ?? urpPipeline;
|
|
3036
|
-
frameState.activePipelineId = current?.id ?? URP_PIPELINE_ID;
|
|
3037
|
-
frameState.isHdrpActive = current?.isHdrp ?? false;
|
|
3038
|
-
frameState.installedPipelineConfig = current?.config;
|
|
3039
|
-
installEpoch += 1;
|
|
3040
|
-
frameState.installedPipelineHandle = installEpoch;
|
|
3041
|
-
});
|
|
1726
|
+
frameState.isHdrpActive = resolvedConfig?.clusterGrid !== undefined;
|
|
1727
|
+
frameState.installedPipelineConfig = resolvedConfig;
|
|
3042
1728
|
},
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
1729
|
+
registerBuiltinPostProcess(id: string, entry: PostProcessShaderEntry): () => void {
|
|
1730
|
+
// D-3: eager-create params UBO at register time + fail-fast
|
|
1731
|
+
// byteSize / defaultValue validation (q5=A).
|
|
1732
|
+
let paramsBuffer: Buffer | undefined;
|
|
1733
|
+
try {
|
|
1734
|
+
if (entry.params !== undefined) {
|
|
1735
|
+
const { byteSize, defaultValue } = entry.params;
|
|
1736
|
+
if (byteSize < 16 || defaultValue.length !== byteSize) {
|
|
1737
|
+
throw new PostProcessError({
|
|
1738
|
+
code: 'params-size-mismatch',
|
|
1739
|
+
detail: { byteSize, actualLength: defaultValue.length },
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
const paramsBufferResult = internals.device.createBuffer({
|
|
1743
|
+
label: `post-process-params-${id}`,
|
|
1744
|
+
size: byteSize,
|
|
1745
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
1746
|
+
mappedAtCreation: false,
|
|
1747
|
+
});
|
|
1748
|
+
if (!paramsBufferResult.ok) throw paramsBufferResult.error;
|
|
1749
|
+
paramsBuffer = paramsBufferResult.value;
|
|
1750
|
+
const writeResult = internals.device.queue.writeBuffer(paramsBuffer, 0, defaultValue);
|
|
1751
|
+
if (!writeResult.ok) throw writeResult.error;
|
|
1752
|
+
} else if (entryHasDepthRead(entry)) {
|
|
1753
|
+
const paramsBufferResult = internals.device.createBuffer({
|
|
1754
|
+
label: `post-process-params-${id}`,
|
|
1755
|
+
size: DEPTH_MIN_PARAMS_BYTE_SIZE,
|
|
1756
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
1757
|
+
mappedAtCreation: false,
|
|
1758
|
+
});
|
|
1759
|
+
if (!paramsBufferResult.ok) throw paramsBufferResult.error;
|
|
1760
|
+
paramsBuffer = paramsBufferResult.value;
|
|
1761
|
+
}
|
|
1762
|
+
if (builtinPostProcessEntries.has(id)) {
|
|
3046
1763
|
throw new PostProcessError({
|
|
3047
1764
|
code: 'post-process-already-registered',
|
|
3048
1765
|
detail: { id },
|
|
3049
1766
|
});
|
|
3050
1767
|
}
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
let paramsBuffer: Buffer | undefined;
|
|
3054
|
-
try {
|
|
3055
|
-
if (entry.params !== undefined) {
|
|
3056
|
-
const { byteSize, defaultValue } = entry.params;
|
|
3057
|
-
if (byteSize < 16 || defaultValue.length !== byteSize) {
|
|
3058
|
-
throw new PostProcessError({
|
|
3059
|
-
code: 'params-size-mismatch',
|
|
3060
|
-
detail: { byteSize, actualLength: defaultValue.length },
|
|
3061
|
-
});
|
|
3062
|
-
}
|
|
3063
|
-
const paramsBufferResult = internals.device.createBuffer({
|
|
3064
|
-
label: `post-process-params-${id}`,
|
|
3065
|
-
size: byteSize,
|
|
3066
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
3067
|
-
mappedAtCreation: false,
|
|
3068
|
-
});
|
|
3069
|
-
if (!paramsBufferResult.ok) throw paramsBufferResult.error;
|
|
3070
|
-
paramsBuffer = paramsBufferResult.value;
|
|
3071
|
-
const writeResult = internals.device.queue.writeBuffer(paramsBuffer, 0, defaultValue);
|
|
3072
|
-
if (!writeResult.ok) throw writeResult.error;
|
|
3073
|
-
} else if (entryHasDepthRead(entry)) {
|
|
3074
|
-
const paramsBufferResult = internals.device.createBuffer({
|
|
3075
|
-
label: `post-process-params-${id}`,
|
|
3076
|
-
size: DEPTH_MIN_PARAMS_BYTE_SIZE,
|
|
3077
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
3078
|
-
mappedAtCreation: false,
|
|
3079
|
-
});
|
|
3080
|
-
if (!paramsBufferResult.ok) throw paramsBufferResult.error;
|
|
3081
|
-
paramsBuffer = paramsBufferResult.value;
|
|
3082
|
-
}
|
|
3083
|
-
postProcessRegistry.set(id, entry);
|
|
3084
|
-
if (paramsBuffer !== undefined) postProcessParamsBuffers.set(id, paramsBuffer);
|
|
3085
|
-
} catch (cause) {
|
|
3086
|
-
if (paramsBuffer !== undefined) internals.device.destroyBuffer(paramsBuffer);
|
|
3087
|
-
throw cause;
|
|
3088
|
-
}
|
|
1768
|
+
builtinPostProcessEntries.set(id, entry);
|
|
1769
|
+
if (paramsBuffer !== undefined) postProcessParamsBuffers.set(id, paramsBuffer);
|
|
3089
1770
|
return () => {
|
|
3090
|
-
if (
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
1771
|
+
if (builtinPostProcessEntries.get(id) === entry) {
|
|
1772
|
+
builtinPostProcessEntries.delete(id);
|
|
1773
|
+
}
|
|
1774
|
+
const current = postProcessParamsBuffers.get(id);
|
|
1775
|
+
if (current !== undefined) {
|
|
1776
|
+
internals.device.destroyBuffer(current);
|
|
3095
1777
|
postProcessParamsBuffers.delete(id);
|
|
3096
1778
|
}
|
|
3097
1779
|
for (const key of postProcessPipelineCache.keys()) {
|
|
3098
1780
|
if (key.startsWith(`${id}|`)) postProcessPipelineCache.delete(key);
|
|
3099
1781
|
}
|
|
3100
1782
|
};
|
|
3101
|
-
}
|
|
1783
|
+
} catch (cause) {
|
|
1784
|
+
if (paramsBuffer !== undefined) internals.device.destroyBuffer(paramsBuffer);
|
|
1785
|
+
throw cause;
|
|
1786
|
+
}
|
|
3102
1787
|
},
|
|
3103
1788
|
disposeFrameState(): void {
|
|
3104
|
-
const environmentHandles = [
|
|
3105
|
-
frameState.environmentGeneration.active?.handle,
|
|
3106
|
-
frameState.environmentGeneration.candidate?.handle,
|
|
3107
|
-
];
|
|
3108
|
-
disposeEnvironmentGeneration(frameState.environmentGeneration);
|
|
3109
|
-
for (const handle of environmentHandles) {
|
|
3110
|
-
if (isEnvironmentGpuBundle(handle)) handle.destroy(internals.device);
|
|
3111
|
-
}
|
|
3112
|
-
temporalFrameTransaction.dispose();
|
|
3113
1789
|
persistentRenderScene.dispose();
|
|
3114
1790
|
gpuDrivenProduction.dispose();
|
|
3115
1791
|
disposeFeatureGpuWork();
|
|
@@ -3121,17 +1797,15 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3121
1797
|
// 6 steps).
|
|
3122
1798
|
frameState.directionalShadowCache = null;
|
|
3123
1799
|
frameState.directionalShadowCacheRecorded = false;
|
|
3124
|
-
frameState.pendingShadowCommandBuffers = [];
|
|
3125
|
-
frameState.shadowSubmitModeState = createShadowSubmitModeState();
|
|
3126
1800
|
frameState.currentFrameObservationSource = undefined;
|
|
3127
1801
|
frameState.currentDirectionalShadowView = null;
|
|
3128
1802
|
frameState.currentSpotShadowView = null;
|
|
3129
1803
|
const compiled = frameState.compiledFrameGraph;
|
|
3130
1804
|
frameState.compiledFrameGraph = null;
|
|
3131
1805
|
frameState.compiledFrameGraphTopologyKey = null;
|
|
3132
|
-
if (compiled !== null)
|
|
1806
|
+
if (compiled !== null) compiled.retire().catch(() => undefined);
|
|
3133
1807
|
for (const retired of frameState.retiredCompiledFrameGraphs) {
|
|
3134
|
-
|
|
1808
|
+
retired.retire().catch(() => undefined);
|
|
3135
1809
|
}
|
|
3136
1810
|
frameState.retiredCompiledFrameGraphs.clear();
|
|
3137
1811
|
// feat-20260619 M4 (D-6): pass errorRegistry to disposeInstanceBuffers
|
|
@@ -3139,6 +1813,15 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3139
1813
|
// dispose error strategy).
|
|
3140
1814
|
disposeInstanceBuffers(frameState.instanceBuffers, internals.errorRegistry);
|
|
3141
1815
|
disposeTransientInstanceBuffers(frameState.transientInstanceBuffers, internals.errorRegistry);
|
|
1816
|
+
if (frameState.morphBuffers !== undefined) {
|
|
1817
|
+
for (const entry of frameState.morphBuffers.values()) {
|
|
1818
|
+
if (!entry.buffer.isDestroyed) {
|
|
1819
|
+
const result = entry.buffer.destroy();
|
|
1820
|
+
if (!result.ok) internals.errorRegistry.fire(result.error);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
frameState.morphBuffers.clear();
|
|
1824
|
+
}
|
|
3142
1825
|
// feat-20260612-point-light-shadows-urp-hdrp M4 / T-M4-2: dispose the
|
|
3143
1826
|
// cube_array shadow atlas owned by the RenderSystem closure. The atlas
|
|
3144
1827
|
// is per-RenderSystem (= per Renderer) and is shared transparently
|
|
@@ -3148,46 +1831,31 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3148
1831
|
frameState.pointShadowAtlas.dispose();
|
|
3149
1832
|
frameState.pointShadowAtlas = null;
|
|
3150
1833
|
}
|
|
3151
|
-
const taaParams = internals.getPipelineState()?.perPassResources.taaResolveParamsBuffer;
|
|
3152
|
-
if (taaParams !== null && taaParams !== undefined) {
|
|
3153
|
-
internals.device.destroyBuffer(taaParams);
|
|
3154
|
-
const pipelineState = internals.getPipelineState();
|
|
3155
|
-
if (pipelineState !== null) pipelineState.perPassResources.taaResolveParamsBuffer = null;
|
|
3156
|
-
}
|
|
3157
1834
|
},
|
|
3158
1835
|
resetForRecover(): void {
|
|
3159
|
-
abandonEnvironmentDevice(
|
|
3160
|
-
frameState.environmentGeneration,
|
|
3161
|
-
frameState.environmentGeneration.deviceGeneration,
|
|
3162
|
-
);
|
|
3163
|
-
temporalFrameTransaction.resetForRecover();
|
|
3164
|
-
const pipelineState = internals.getPipelineState();
|
|
3165
|
-
if (pipelineState !== null) pipelineState.perPassResources.taaResolveParamsBuffer = null;
|
|
3166
1836
|
persistentRenderScene.resetGpuForRecover();
|
|
3167
1837
|
gpuDrivenProduction.dispose();
|
|
1838
|
+
gpuDrivenProduction = new GpuDrivenProduction(internals.device, gpuDrivenShaderFactory);
|
|
3168
1839
|
disposeFeatureGpuWork();
|
|
3169
1840
|
// feat-20260622-s5 M3 / B-2 / w18: recover() rebuild drops device-bound
|
|
3170
1841
|
// state minted by the lost device. The active graph and per-entity caches
|
|
3171
1842
|
// must be discarded, not merely marked for destruction: their opaque
|
|
3172
1843
|
// handles cannot be used on the fresh device and the next draw must
|
|
3173
1844
|
// lazily build a new graph from the preserved ECS / asset POD caches.
|
|
3174
|
-
|
|
3175
|
-
taaHistoryStore.retireGraph(frameState.compiledFrameGraph).catch(() => undefined);
|
|
3176
|
-
}
|
|
1845
|
+
frameState.compiledFrameGraph?.retire().catch(() => undefined);
|
|
3177
1846
|
frameState.compiledFrameGraph = null;
|
|
3178
1847
|
frameState.compiledFrameGraphTopologyKey = null;
|
|
3179
1848
|
frameState.directionalShadowCache = null;
|
|
3180
1849
|
frameState.directionalShadowCacheRecorded = false;
|
|
3181
|
-
frameState.pendingShadowCommandBuffers = [];
|
|
3182
|
-
frameState.shadowSubmitModeState = createShadowSubmitModeState();
|
|
3183
1850
|
frameState.currentFrameObservationSource = undefined;
|
|
3184
1851
|
frameState.currentDirectionalShadowView = null;
|
|
3185
1852
|
frameState.currentSpotShadowView = null;
|
|
3186
1853
|
for (const retired of frameState.retiredCompiledFrameGraphs) {
|
|
3187
|
-
|
|
1854
|
+
retired.retire().catch(() => undefined);
|
|
3188
1855
|
}
|
|
3189
1856
|
frameState.retiredCompiledFrameGraphs.clear();
|
|
3190
1857
|
frameState.instanceBuffers.clear();
|
|
1858
|
+
frameState.morphBuffers?.clear();
|
|
3191
1859
|
frameState.transientInstanceBuffers = [];
|
|
3192
1860
|
frameState.pointShadowAtlas = null;
|
|
3193
1861
|
// Bind groups retain opaque handles from the lost device. WeakMap roots
|
|
@@ -3202,26 +1870,22 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3202
1870
|
frameState.materialBgAssemblyCache.clear();
|
|
3203
1871
|
frameState.singletonMaterialCache.clear();
|
|
3204
1872
|
frameState.postProcessBgCache = new WeakMap();
|
|
3205
|
-
//
|
|
1873
|
+
// Fullscreen PSOs are cached outside frameState because the normal
|
|
3206
1874
|
// path reuses them across frames. They still carry opaque handles from
|
|
3207
1875
|
// the lost device, so recovery must invalidate this cache alongside the
|
|
3208
|
-
//
|
|
1876
|
+
// feature-host declarations and UBOs below.
|
|
3209
1877
|
postProcessPipelineCache.clear();
|
|
3210
|
-
// Logical declarations stay live
|
|
3211
|
-
//
|
|
1878
|
+
// Logical declarations stay live in the feature host while a replacement
|
|
1879
|
+
// device is being requested.
|
|
3212
1880
|
postProcessParamsBuffers.clear();
|
|
3213
1881
|
resetRenderFeatureGraphState(internals);
|
|
3214
1882
|
},
|
|
3215
|
-
restoreGpuDrivenForRecover(): void {
|
|
3216
|
-
// resetForRecover() runs before Renderer.recover() acquires the
|
|
3217
|
-
// replacement device. Rebinding here is deliberately a separate phase;
|
|
3218
|
-
// constructing GpuDrivenProduction during reset would capture the lost
|
|
3219
|
-
// device and make the first eligible post-recovery frame submit stale
|
|
3220
|
-
// buffers/pipelines.
|
|
3221
|
-
gpuDrivenProduction = new GpuDrivenProduction(internals.device, gpuDrivenShaderFactory);
|
|
3222
|
-
},
|
|
3223
1883
|
restorePostProcessResources(): void {
|
|
3224
|
-
|
|
1884
|
+
const declarations = [
|
|
1885
|
+
...builtinPostProcessEntries.entries(),
|
|
1886
|
+
...activeFeaturePostProcessEntries.entries(),
|
|
1887
|
+
];
|
|
1888
|
+
for (const [id, entry] of declarations) {
|
|
3225
1889
|
let buffer: Buffer | undefined;
|
|
3226
1890
|
if (entry.params !== undefined) {
|
|
3227
1891
|
const created = internals.device.createBuffer({
|
|
@@ -3247,7 +1911,7 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3247
1911
|
if (!created.ok) throw created.error;
|
|
3248
1912
|
buffer = created.value;
|
|
3249
1913
|
}
|
|
3250
|
-
if (buffer !== undefined &&
|
|
1914
|
+
if (buffer !== undefined && lookupPostProcess(id) === entry) {
|
|
3251
1915
|
postProcessParamsBuffers.set(id, buffer);
|
|
3252
1916
|
} else if (buffer !== undefined) {
|
|
3253
1917
|
internals.device.destroyBuffer(buffer);
|
|
@@ -3256,3 +1920,14 @@ export function createRenderSystem(internals: RenderSystemInternals): RenderSyst
|
|
|
3256
1920
|
},
|
|
3257
1921
|
};
|
|
3258
1922
|
}
|
|
1923
|
+
|
|
1924
|
+
// @forgeax/engine-render - built-in record context extension.
|
|
1925
|
+
//
|
|
1926
|
+
// The public RenderPipelineContext is canonical in render-contract.ts. This
|
|
1927
|
+
// owner adds only concrete assembly state for built-in record closures.
|
|
1928
|
+
|
|
1929
|
+
/**
|
|
1930
|
+
* Package-private extension consumed by the built-in record closures only.
|
|
1931
|
+
* Custom pipeline authors see the leaf RenderPipelineContext contract and
|
|
1932
|
+
* never this surface.
|
|
1933
|
+
*/
|