@forgeax/engine-render 0.1.28 → 0.1.29
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 +627 -124
- package/dist/assembly/atmosphere-shader-prewarm.d.ts +6 -0
- package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/dynamic-geometry-host.d.ts +21 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-runtime.d.ts +20 -0
- package/dist/assembly/dynamic-geometry-runtime.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +3 -1
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/gpu-driven-owner.d.ts +10 -0
- package/dist/assembly/gpu-driven-owner.d.ts.map +1 -0
- package/dist/assembly/host-contract.d.ts +8 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +7 -0
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material-shader-policy.d.ts +3 -1
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/recovery/failure-location.d.ts +12 -0
- package/dist/assembly/recovery/failure-location.d.ts.map +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts +6 -4
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/renderer-feature-inspection.d.ts +5 -0
- package/dist/assembly/renderer-feature-inspection.d.ts.map +1 -0
- package/dist/assembly/renderer-frame-transaction.d.ts +19 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-helpers.d.ts +3 -0
- package/dist/assembly/renderer-helpers.d.ts.map +1 -1
- package/dist/assembly/renderer-inspection.d.ts +4 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +6 -0
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
- package/dist/assembly/skin-palette-owner.d.ts +8 -0
- package/dist/assembly/skin-palette-owner.d.ts.map +1 -0
- package/dist/assembly/ssr-shader-prewarm.d.ts +6 -0
- package/dist/assembly/ssr-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-device-loss.d.ts +15 -0
- package/dist/assembly/webgpu-device-loss.d.ts.map +1 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts +38 -0
- package/dist/assembly/webgpu-pbr-ready.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-mipmap.d.ts +11 -0
- package/dist/assembly/webgpu-ready-mipmap.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +7 -3
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts +10 -0
- package/dist/assembly/webgpu-renderer-bootstrap.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-contract.d.ts +62 -0
- package/dist/assembly/webgpu-renderer-contract.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-guards.d.ts +9 -0
- package/dist/assembly/webgpu-renderer-guards.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-observation.d.ts +6 -0
- package/dist/assembly/webgpu-renderer-observation.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer-recovery-failure.d.ts +12 -0
- package/dist/assembly/webgpu-renderer-recovery-failure.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +7 -141
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/assembly/webgpu-vertex-layouts.d.ts +28 -84
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
- package/dist/authoring.mjs +2 -2
- package/dist/{chunk-JAASCMXA.mjs → chunk-33XZFTQU.mjs} +245 -39
- package/dist/chunk-33XZFTQU.mjs.map +1 -0
- package/dist/{chunk-GF523LHF.mjs → chunk-BLXGONR2.mjs} +30 -12
- package/dist/chunk-BLXGONR2.mjs.map +1 -0
- package/dist/{chunk-J52NMQKR.mjs → chunk-BQVQOQJH.mjs} +1086 -838
- package/dist/chunk-BQVQOQJH.mjs.map +1 -0
- package/dist/{chunk-IQMKJLNU.mjs → chunk-BTWH7BP4.mjs} +386 -17
- package/dist/chunk-BTWH7BP4.mjs.map +1 -0
- package/dist/{chunk-MBU4ZVE7.mjs → chunk-FUODTTIA.mjs} +3921 -671
- package/dist/chunk-FUODTTIA.mjs.map +1 -0
- package/dist/{chunk-MYJ5RPJO.mjs → chunk-JQTSEI3Z.mjs} +3 -3
- package/dist/{chunk-MYJ5RPJO.mjs.map → chunk-JQTSEI3Z.mjs.map} +1 -1
- package/dist/{chunk-GE3SDD72.mjs → chunk-NICXSHAT.mjs} +16 -38
- package/dist/chunk-NICXSHAT.mjs.map +1 -0
- package/dist/{chunk-J6KACNOH.mjs → chunk-NRNXBLIJ.mjs} +577 -7
- package/dist/chunk-NRNXBLIJ.mjs.map +1 -0
- package/dist/{chunk-RBDBZ5R7.mjs → chunk-PA5DRJNI.mjs} +2 -2
- package/dist/chunk-PA5DRJNI.mjs.map +1 -0
- package/dist/{chunk-VSL23LZE.mjs → chunk-S4DNKQYQ.mjs} +1117 -445
- package/dist/chunk-S4DNKQYQ.mjs.map +1 -0
- package/dist/components/camera.d.ts +62 -2
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/instances.d.ts +6 -20
- package/dist/components/instances.d.ts.map +1 -1
- package/dist/components/scene-instance.d.ts +2 -1
- package/dist/components/scene-instance.d.ts.map +1 -1
- package/dist/components/screen-space-reflection.d.ts +14 -0
- package/dist/components/screen-space-reflection.d.ts.map +1 -0
- package/dist/construct-renderer.mjs +12888 -7025
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +1 -12
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/environment/background.d.ts +6 -0
- package/dist/environment/background.d.ts.map +1 -0
- package/dist/errors/gpu-driven.d.ts +6 -3
- package/dist/errors/gpu-driven.d.ts.map +1 -1
- package/dist/errors/render.d.ts +7 -4
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +33 -3
- package/dist/extract/gpu-driven.d.ts.map +1 -1
- package/dist/features/host.d.ts +23 -1
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/noise-texture.d.ts +17 -0
- package/dist/features/noise-texture.d.ts.map +1 -0
- package/dist/features/plan.d.ts +83 -2
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +37 -4
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts.map +1 -1
- package/dist/features/prepared-graphics.d.ts +5 -0
- package/dist/features/prepared-graphics.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts +5 -3
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts +11 -3
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +19 -3
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/types.d.ts +11 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +2 -0
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts +34 -1
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/pbr-program.d.ts +10 -0
- package/dist/gpu-driven/pbr-program.d.ts.map +1 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +144 -23
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-views.d.ts +126 -0
- package/dist/gpu-driven/shadow-views.d.ts.map +1 -0
- package/dist/gpu-driven/view-gpu.d.ts +18 -2
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts +2 -8
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +14 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/gpu-texture-usage.d.ts +1 -0
- package/dist/gpu-texture-usage.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +6 -0
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/frame-resources.d.ts +8 -0
- package/dist/ibl/frame-resources.d.ts.map +1 -0
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +8 -8
- package/dist/inspection-types.d.ts +164 -0
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances-derived-bounds.d.ts +11 -0
- package/dist/instances-derived-bounds.d.ts.map +1 -1
- package/dist/instances.d.ts +89 -44
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +117 -9
- package/dist/internal.mjs.map +1 -1
- package/dist/lifecycle.d.ts +2 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/material-row.d.ts +5 -0
- package/dist/material-row.d.ts.map +1 -0
- package/dist/materials.d.ts +15 -0
- package/dist/materials.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +21 -5
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-output/auto-exposure/capability.d.ts +12 -0
- package/dist/pipeline/standard-output/auto-exposure/capability.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts +70 -0
- package/dist/pipeline/standard-output/auto-exposure/gpu.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/graph.d.ts +75 -0
- package/dist/pipeline/standard-output/auto-exposure/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts +111 -0
- package/dist/pipeline/standard-output/auto-exposure/inspection.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts +16 -0
- package/dist/pipeline/standard-output/auto-exposure/oracle.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/preset.d.ts +14 -0
- package/dist/pipeline/standard-output/auto-exposure/preset.d.ts.map +1 -0
- package/dist/pipeline/standard-output/auto-exposure/state.d.ts +46 -0
- package/dist/pipeline/standard-output/auto-exposure/state.d.ts.map +1 -0
- package/dist/pipeline/standard-output/color-transform.d.ts +27 -0
- package/dist/pipeline/standard-output/color-transform.d.ts.map +1 -0
- package/dist/pipeline/standard-output/graph.d.ts +15 -0
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-admission.d.ts +29 -0
- package/dist/pipeline/standard-output/lut-admission.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-gpu.d.ts +48 -0
- package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -0
- package/dist/pipeline/standard-output/lut-state.d.ts +68 -0
- package/dist/pipeline/standard-output/lut-state.d.ts.map +1 -0
- package/dist/pipeline/standard-output/resources.d.ts +10 -0
- package/dist/pipeline/standard-output/resources.d.ts.map +1 -0
- package/dist/pipeline/standard-output/types.d.ts +39 -0
- package/dist/pipeline/standard-output/types.d.ts.map +1 -0
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +2 -2
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +2 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec-types.d.ts +4 -0
- package/dist/pipeline-spec-types.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +1 -9
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/points-lines/standard-owner.d.ts +1 -1
- package/dist/points-lines/standard-owner.d.ts.map +1 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts +6 -1
- package/dist/prepare/prepared-graphics-resolver.d.ts.map +1 -1
- package/dist/record/dynamic-geometry-consumption.d.ts +15 -0
- package/dist/record/dynamic-geometry-consumption.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts +3 -3
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +106 -5
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +39 -4
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +7 -8
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +38 -1
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +2 -10
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +12 -2
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +15 -6
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/prepared-material-bindings.d.ts +19 -0
- package/dist/record/prepared-material-bindings.d.ts.map +1 -0
- package/dist/record/recovery-pipeline.d.ts +2 -9
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +74 -26
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +62 -7
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +1 -1
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +35 -7
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +1 -0
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +10 -2
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/recovery/render-system-roots.d.ts +11 -0
- package/dist/recovery/render-system-roots.d.ts.map +1 -0
- package/dist/recovery/types.d.ts +76 -0
- package/dist/recovery/types.d.ts.map +1 -0
- package/dist/reflection/record-owner.d.ts +0 -2
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +173 -30
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +1 -0
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +64 -5
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +7 -5
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +101 -41
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-projections.d.ts +22 -0
- package/dist/render-system-projections.d.ts.map +1 -0
- package/dist/render-system.d.ts +27 -77
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +14 -7
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +29 -11
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/shadow-visibility.d.ts +5 -0
- package/dist/scene/shadow-visibility.d.ts.map +1 -0
- package/dist/ssr/admission.d.ts +70 -1
- package/dist/ssr/admission.d.ts.map +1 -1
- package/dist/ssr/compose.d.ts +13 -0
- package/dist/ssr/compose.d.ts.map +1 -0
- package/dist/ssr/errors.d.ts +35 -0
- package/dist/ssr/errors.d.ts.map +1 -1
- package/dist/ssr/graph.d.ts +60 -0
- package/dist/ssr/graph.d.ts.map +1 -0
- package/dist/ssr/history.d.ts +97 -0
- package/dist/ssr/history.d.ts.map +1 -0
- package/dist/ssr/hiz.d.ts +37 -0
- package/dist/ssr/hiz.d.ts.map +1 -0
- package/dist/ssr/inspection.d.ts +42 -0
- package/dist/ssr/inspection.d.ts.map +1 -0
- package/dist/ssr/resources.d.ts +58 -0
- package/dist/ssr/resources.d.ts.map +1 -0
- package/dist/ssr/temporal.d.ts +38 -0
- package/dist/ssr/temporal.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts +32 -10
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +15 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -1
- package/dist/systems/transparent-dispatch.d.ts +9 -0
- package/dist/systems/transparent-dispatch.d.ts.map +1 -0
- package/dist/targets/material-source.d.ts +7 -0
- package/dist/targets/material-source.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts +7 -0
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts +8 -0
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/history.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -6
- package/dist/temporal/inspection.d.ts.map +1 -1
- package/dist/temporal/standard-scene-data.d.ts +2 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/temporal-view.d.ts +2 -0
- package/dist/temporal/temporal-view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +35 -1
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/typed-shadow-passes.d.ts +3 -1
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +6 -0
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +21 -20
- package/src/__tests__/auto-exposure-gpu-evidence.ts +291 -0
- package/src/__tests__/auto-exposure-inspection.unit.test.ts +80 -0
- package/src/__tests__/auto-exposure-transaction.integration.test.ts +99 -0
- package/src/__tests__/auto-exposure.browser.test.ts +33 -0
- package/src/__tests__/auto-exposure.dawn.test.ts +33 -0
- package/src/__tests__/detached-inspection.integration.test.ts +5 -0
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +18 -0
- package/src/__tests__/device-scope-stale-matrix.unit.test.ts +18 -0
- package/src/__tests__/docs-gate-green.unit.test.ts +3 -3
- package/src/__tests__/factory-contract.integration.test.ts +67 -8
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +19 -0
- package/src/__tests__/feature-depth-input.dawn.test.ts +121 -0
- package/src/__tests__/feature-noise.unit.test.ts +38 -0
- package/src/__tests__/fixtures/taa-resolve-pinned-baseline.wgsl +301 -0
- package/src/__tests__/frame-camera-selection.unit.test.ts +131 -0
- package/src/__tests__/frame-plan-contract.unit.test.ts +24 -0
- package/src/__tests__/frame-targets.unit.test.ts +24 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +3 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +79 -0
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +287 -6
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +122 -142
- package/src/__tests__/gpu-driven-indirect-raster.browser.test.ts +10 -1
- package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +264 -0
- package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +349 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +1417 -0
- package/src/__tests__/gpu-driven-pbr.integration.test.ts +21 -0
- package/src/__tests__/gpu-driven-production-fixture.ts +96 -0
- package/src/__tests__/gpu-driven-production.integration.test.ts +1075 -93
- package/src/__tests__/gpu-driven-scaling.unit.test.ts +1 -3
- package/src/__tests__/gpu-driven-shadow-views.browser.test.ts +63 -0
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +294 -0
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +97 -0
- package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +168 -0
- package/src/__tests__/gpu-driven-skin-contract.test-d.ts +11 -0
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +72 -20
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +30 -9
- package/src/__tests__/gpu-driven-view.unit.test.ts +159 -52
- package/src/__tests__/gpu-pass-timing.browser.test.ts +6 -1
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +42 -0
- package/src/__tests__/gpu-scene-render-graph-gpu.ts +19 -5
- package/src/__tests__/gpu-scene.dawn.test.ts +40 -13
- package/src/__tests__/gpu-scene.unit.test.ts +559 -33
- package/src/__tests__/instances-culling.integration.test.ts +36 -10
- package/src/__tests__/instances-derived-bounds.unit.test.ts +54 -0
- package/src/__tests__/instances-store.unit.test.ts +242 -39
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +6 -0
- package/src/__tests__/lighting-scene-roundtrip.integration.test.ts +13 -16
- package/src/__tests__/lut-lkg-transaction.integration.test.ts +122 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +5 -6
- package/src/__tests__/material-dispatch-shader-id.unit.test.ts +51 -0
- package/src/__tests__/material-lane-selection.unit.test.ts +60 -0
- package/src/__tests__/material-numeric-upload.unit.test.ts +53 -0
- package/src/__tests__/material-texture-handles.unit.test.ts +18 -0
- package/src/__tests__/material-texture-source.unit.test.ts +87 -0
- package/src/__tests__/materials-authoring-validation.unit.test.ts +41 -0
- package/src/__tests__/materials-standard-contract.unit.test.ts +52 -2
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +4 -0
- package/src/__tests__/normal-fallback.dawn.test.ts +122 -0
- package/src/__tests__/particle-material-input-layout.unit.test.ts +104 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +37 -3
- package/src/__tests__/pipeline-texture-specialization.unit.test.ts +138 -0
- package/src/__tests__/point-shadow-inspection.unit.test.ts +2 -0
- package/src/__tests__/prepared-gpu-driven-pbr-contract.test-d.ts +31 -0
- package/src/__tests__/prepared-gpu-driven-pbr-contract.unit.test.ts +39 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +35 -4
- package/src/__tests__/prepared-graphics-capability.unit.test.ts +21 -0
- package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +6 -2
- package/src/__tests__/profiler-phase-catalog.test.ts +5 -0
- package/src/__tests__/public-surface.test-d.ts +3 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +30 -12
- package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +104 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +2 -1
- package/src/__tests__/render-error-code-owner.test-d.ts +4 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +22 -0
- package/src/__tests__/render-error-exhaustive.test-d.ts +15 -0
- package/src/__tests__/render-feature-gpu-work.integration.test.ts +285 -2
- package/src/__tests__/render-feature-plan.unit.test.ts +245 -0
- package/src/__tests__/render-feature-shadow-order.unit.test.ts +228 -0
- package/src/__tests__/render-feature-zero-work.unit.test.ts +35 -1
- package/src/__tests__/render-graph-fallback.integration.test.ts +2 -2
- package/src/__tests__/render-owner-cohesion.unit.test.ts +55 -0
- package/src/__tests__/render-scene-mixed-updates.integration.test.ts +426 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +914 -23
- package/src/__tests__/render-scene-resource-updates.integration.test.ts +214 -0
- package/src/__tests__/render-scene-skin-lifecycle.integration.test.ts +173 -0
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +133 -0
- package/src/__tests__/render-target-material-source.test-d.ts +27 -3
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +60 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +923 -5
- package/src/__tests__/shader-manifest-fixture.ts +39 -0
- package/src/__tests__/skin-motion-regression.unit.test.ts +0 -33
- package/src/__tests__/skin-palette-persistent.integration.test.ts +28 -0
- package/src/__tests__/skin-palette-persistent.unit.test.ts +247 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +118 -14
- package/src/__tests__/specular-environment-scale.dawn.test.ts +156 -0
- package/src/__tests__/ssr-admission-matrix.integration.test.ts +7 -1
- package/src/__tests__/ssr-confidence-compose.unit.test.ts +84 -0
- package/src/__tests__/ssr-coordinate-oracle.unit.test.ts +28 -0
- package/src/__tests__/ssr-format-probe-gating.integration.test.ts +82 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +91 -0
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +112 -0
- package/src/__tests__/ssr-gpu-dispatch.ts +946 -0
- package/src/__tests__/ssr-graph-topology.unit.test.ts +104 -0
- package/src/__tests__/ssr-history-filter.dawn.test.ts +198 -0
- package/src/__tests__/ssr-hit-color.dawn.test.ts +273 -0
- package/src/__tests__/ssr-hiz-odd-reduction.dawn.test.ts +569 -0
- package/src/__tests__/ssr-hiz-rescue.dawn.test.ts +279 -0
- package/src/__tests__/ssr-hiz.browser.test.ts +150 -0
- package/src/__tests__/ssr-hiz.dawn.test.ts +150 -0
- package/src/__tests__/ssr-hiz.unit.test.ts +56 -0
- package/src/__tests__/ssr-inspection.unit.test.ts +105 -0
- package/src/__tests__/ssr-memory-budget.unit.test.ts +45 -0
- package/src/__tests__/ssr-performance-sequence-aggregation.unit.test.ts +15 -0
- package/src/__tests__/ssr-public-surface.test-d.ts +37 -0
- package/src/__tests__/ssr-receiver-coverage.dawn.test.ts +387 -0
- package/src/__tests__/ssr-reflection-mip-reducer.dawn.test.ts +309 -0
- package/src/__tests__/ssr-reprojection-probe.ts +90 -0
- package/src/__tests__/ssr-roughness-mip.dawn.test.ts +163 -0
- package/src/__tests__/ssr-shader-prewarm.unit.test.ts +59 -0
- package/src/__tests__/ssr-spatial.integration.test.ts +141 -0
- package/src/__tests__/ssr-stable-lattice.dawn.test.ts +475 -0
- package/src/__tests__/ssr-temporal-history.unit.test.ts +202 -0
- package/src/__tests__/ssr-texel-crossing.dawn.test.ts +448 -0
- package/src/__tests__/ssr-zero-work.integration.test.ts +87 -0
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +44 -12
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +29 -0
- package/src/__tests__/standard-output-chain-contract.unit.test.ts +99 -0
- package/src/__tests__/standard-output-chain.integration.test.ts +73 -0
- package/src/__tests__/standard-output-gpu-evidence.ts +205 -0
- package/src/__tests__/standard-output.browser.test.ts +15 -0
- package/src/__tests__/standard-output.dawn.test.ts +15 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +38 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +320 -3
- package/src/__tests__/standard-profile-oracle.unit.test.ts +22 -0
- package/src/__tests__/standard-surface-lighting.unit.test.ts +41 -0
- package/src/__tests__/standard-texture-specialization.dawn.test.ts +141 -0
- package/src/__tests__/surface-adjacent-regressions.integration.test.ts +6 -3
- package/src/__tests__/taa-coverage-edge.dawn.test.ts +193 -0
- package/src/__tests__/taa-history-clipping.dawn.test.ts +328 -0
- package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +778 -0
- package/src/__tests__/taa-stability-feedback.browser.test.ts +23 -0
- package/src/__tests__/taa-stability-feedback.dawn.test.ts +68 -0
- package/src/__tests__/taa-stability-feedback.ts +341 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +49 -7
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +9 -0
- package/src/__tests__/temporal-transaction.integration.test.ts +28 -0
- package/src/__tests__/texture-usage-surface.unit.test.ts +4 -2
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +109 -4
- package/src/__tests__/typed-scene-pass-material-cache.integration.test.ts +126 -0
- package/src/__tests__/vertex-layouts-ssot.unit.test.ts +47 -0
- package/src/__tests__/visibility-extract.perf.test.ts +20 -8
- package/src/__tests__/visibility-instances.integration.test.ts +19 -7
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +20 -9
- package/src/assembly/atmosphere-shader-prewarm.ts +38 -0
- package/src/assembly/dynamic-geometry-host.ts +48 -0
- package/src/assembly/dynamic-geometry-runtime.ts +71 -0
- package/src/assembly/factory.ts +4 -0
- package/src/assembly/gpu-driven-owner.ts +29 -0
- package/src/assembly/host-contract.ts +10 -0
- package/src/assembly/material/assembly.ts +38 -0
- package/src/assembly/material-shader-policy.ts +71 -28
- package/src/assembly/recovery/failure-location.ts +62 -0
- package/src/assembly/recovery/renderer-recover.ts +19 -17
- package/src/assembly/renderer-facade.ts +12 -0
- package/src/assembly/renderer-feature-inspection.ts +13 -0
- package/src/assembly/renderer-frame-transaction.ts +63 -0
- package/src/assembly/renderer-helpers.ts +26 -0
- package/src/assembly/renderer-inspection.ts +6 -0
- package/src/assembly/shader-prewarm-policy.ts +32 -0
- package/src/assembly/skin-palette-owner.ts +21 -0
- package/src/assembly/ssr-shader-prewarm.ts +41 -0
- package/src/assembly/webgpu-device-loss.ts +75 -0
- package/src/assembly/webgpu-pbr-ready.ts +232 -0
- package/src/assembly/webgpu-ready-mipmap.ts +26 -0
- package/src/assembly/webgpu-ready.ts +142 -121
- package/src/assembly/webgpu-renderer-bootstrap.ts +85 -0
- package/src/assembly/webgpu-renderer-contract.ts +69 -0
- package/src/assembly/webgpu-renderer-guards.ts +32 -0
- package/src/assembly/webgpu-renderer-observation.ts +43 -0
- package/src/assembly/webgpu-renderer-recovery-failure.ts +62 -0
- package/src/assembly/webgpu-renderer.ts +666 -652
- package/src/assembly/webgpu-vertex-layouts.ts +233 -87
- package/src/components/__tests__/camera-exposure.contract.unit.test.ts +153 -0
- package/src/components/camera.ts +287 -7
- package/src/components/index.ts +1 -0
- package/src/components/instances.ts +45 -18
- package/src/components/scene-instance.ts +2 -1
- package/src/components/screen-space-reflection.ts +21 -0
- package/src/components/sprite-animation.ts +2 -2
- package/src/components/sprite-instances.ts +1 -1
- package/src/device/gpu-residency.ts +34 -31
- package/src/environment/background.ts +263 -0
- package/src/errors/gpu-driven.ts +39 -5
- package/src/errors/render.ts +27 -3
- package/src/extract/camera.ts +26 -1
- package/src/extract/gpu-driven.ts +185 -10
- package/src/features/host.ts +237 -19
- package/src/features/noise-texture.ts +91 -0
- package/src/features/plan.ts +553 -24
- package/src/features/prepared-gpu-work.ts +631 -49
- package/src/features/prepared-graphics-store.ts +31 -0
- package/src/features/prepared-graphics.ts +5 -0
- package/src/features/render-graph-compute.ts +41 -8
- package/src/features/render-graph-contribution.ts +141 -3
- package/src/features/render-graph-raster.ts +143 -30
- package/src/features/types.ts +11 -0
- package/src/fullscreen-post-process-pass.ts +3 -0
- package/src/gpu-driven/batch-topology.ts +424 -102
- package/src/gpu-driven/pbr-program.ts +31 -0
- package/src/gpu-driven/prepared-draw.ts +29 -4
- package/src/gpu-driven/production-raster.ts +2267 -694
- package/src/gpu-driven/shadow-views.ts +539 -0
- package/src/gpu-driven/view-gpu.ts +188 -41
- package/src/gpu-scene-schema.ts +52 -11
- package/src/gpu-scene.ts +186 -99
- package/src/gpu-texture-usage.ts +1 -0
- package/src/hdrp-buffers.ts +72 -36
- package/src/ibl/frame-resources.ts +38 -0
- package/src/ibl/skylight-bind-group.ts +5 -4
- package/src/index.ts +109 -1
- package/src/inspection-types.ts +191 -0
- package/src/instances-derived-bounds.ts +258 -34
- package/src/instances.ts +459 -150
- package/src/internal.ts +22 -0
- package/src/lifecycle.ts +2 -0
- package/src/material-row.ts +145 -0
- package/src/materials.ts +44 -8
- package/src/pbr-pipeline.ts +159 -17
- package/src/pipeline/standard-forward-lane.ts +52 -24
- package/src/pipeline/standard-output/__tests__/lut-admission.unit.test.ts +103 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/capability.unit.test.ts +40 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/gpu.contract.unit.test.ts +125 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/graph.contract.unit.test.ts +94 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/oracle.test.ts +108 -0
- package/src/pipeline/standard-output/auto-exposure/__tests__/preset.test.ts +26 -0
- package/src/pipeline/standard-output/auto-exposure/capability.ts +38 -0
- package/src/pipeline/standard-output/auto-exposure/gpu.ts +567 -0
- package/src/pipeline/standard-output/auto-exposure/graph.ts +221 -0
- package/src/pipeline/standard-output/auto-exposure/inspection.ts +231 -0
- package/src/pipeline/standard-output/auto-exposure/oracle.ts +291 -0
- package/src/pipeline/standard-output/auto-exposure/preset.ts +28 -0
- package/src/pipeline/standard-output/auto-exposure/state.ts +156 -0
- package/src/pipeline/standard-output/color-transform.ts +124 -0
- package/src/pipeline/standard-output/graph.ts +139 -0
- package/src/pipeline/standard-output/lut-admission.ts +121 -0
- package/src/pipeline/standard-output/lut-gpu.ts +399 -0
- package/src/pipeline/standard-output/lut-state.ts +176 -0
- package/src/pipeline/standard-output/resources.ts +121 -0
- package/src/pipeline/standard-output/types.ts +58 -0
- package/src/pipeline/standard-pipeline.ts +192 -59
- package/src/pipeline/standard-post.ts +186 -27
- package/src/pipeline-builder.ts +16 -6
- package/src/pipeline-spec-types.ts +4 -0
- package/src/pipeline-spec.ts +37 -29
- package/src/points-lines/standard-owner.ts +6 -14
- package/src/prepare/prepared-graphics-resolver.ts +9 -2
- package/src/record/__tests__/material-slot-plan.unit.test.ts +48 -0
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +40 -1
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +2 -29
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +36 -5
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +45 -0
- package/src/record/__tests__/view-ubo-layout.unit.test.ts +5 -0
- package/src/record/dynamic-geometry-consumption.ts +59 -0
- package/src/record/frame-lighting.ts +12 -11
- package/src/record/frame-snapshot.ts +133 -23
- package/src/record/frame-targets.ts +9 -0
- package/src/record/frame.ts +725 -133
- package/src/record/main-pass-geometry.ts +149 -94
- package/src/record/main-pass-material.ts +115 -26
- package/src/record/main-pass-sprite-draws.ts +16 -107
- package/src/record/main-pass.ts +78 -8
- package/src/record/material-uniforms.ts +1 -1
- package/src/record/mesh-ssbo.ts +59 -15
- package/src/record/prepared-material-bindings.ts +234 -0
- package/src/record/recovery-pipeline.ts +59 -136
- package/src/record/render-context.ts +90 -22
- package/src/record/shadow-pass.ts +1231 -209
- package/src/record/skybox-post-pass.ts +14 -3
- package/src/record/typed-frame-graph.ts +1004 -44
- package/src/record/view-ubo.ts +11 -0
- package/src/recovery/render-system-candidate.ts +90 -66
- package/src/recovery/render-system-roots.ts +113 -0
- package/src/recovery/types.ts +77 -0
- package/src/reflection/record-owner.ts +26 -54
- package/src/render-contract.ts +195 -25
- package/src/render-graph-primitives.ts +6 -2
- package/src/render-pipeline.ts +70 -1
- package/src/render-system-extract-tail.ts +409 -160
- package/src/render-system-extract.ts +376 -102
- package/src/render-system-projections.ts +241 -0
- package/src/render-system.ts +681 -822
- package/src/scene/render-scene-types.ts +13 -4
- package/src/scene/render-scene.ts +569 -355
- package/src/scene/shadow-visibility.ts +21 -0
- package/src/ssr/admission.ts +380 -14
- package/src/ssr/compose.ts +144 -0
- package/src/ssr/errors.ts +48 -0
- package/src/ssr/graph.ts +777 -0
- package/src/ssr/history.ts +436 -0
- package/src/ssr/hiz.ts +124 -0
- package/src/ssr/inspection.ts +124 -0
- package/src/ssr/resources.ts +202 -0
- package/src/ssr/temporal.ts +164 -0
- package/src/systems/skin-palette-allocator.ts +495 -148
- package/src/systems/skin-palette-types.ts +17 -0
- package/src/systems/transparent-dispatch.ts +90 -0
- package/src/targets/material-source.ts +34 -0
- package/src/temporal/coverage.ts +1 -1
- package/src/temporal/frame-coordinator.ts +23 -2
- package/src/temporal/gpu.ts +74 -12
- package/src/temporal/history.ts +3 -1
- package/src/temporal/inspection.ts +2 -1
- package/src/temporal/standard-scene-data.ts +3 -1
- package/src/temporal/temporal-view.ts +3 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +204 -139
- package/src/typed-render-graph-primitives.ts +266 -74
- package/src/typed-shadow-passes.ts +122 -12
- package/src/volume/temporal.ts +10 -0
- package/dist/chunk-GE3SDD72.mjs.map +0 -1
- package/dist/chunk-GF523LHF.mjs.map +0 -1
- package/dist/chunk-IQMKJLNU.mjs.map +0 -1
- package/dist/chunk-J52NMQKR.mjs.map +0 -1
- package/dist/chunk-J6KACNOH.mjs.map +0 -1
- package/dist/chunk-JAASCMXA.mjs.map +0 -1
- package/dist/chunk-MBU4ZVE7.mjs.map +0 -1
- package/dist/chunk-RBDBZ5R7.mjs.map +0 -1
- package/dist/chunk-VSL23LZE.mjs.map +0 -1
- package/src/__tests__/instances-world-ownership.integration.test.ts +0 -62
- package/src/scene/__tests__/changed-block-summary.candidate.test.ts +0 -36
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
// @forgeax/engine-render —
|
|
2
|
-
//
|
|
3
|
-
// The Runtime host selects one typed backend pack before this assembly runs.
|
|
4
|
-
//
|
|
5
|
-
// **D-P4 three-channel error propagation** (RK-04 + plan-strategy §7.3):
|
|
6
|
-
// (a) Construction-time → `createRenderer(...)` rejects with
|
|
7
|
-
// `EngineEnvironmentError` whose `detail.webgpuError` is the Channel 2
|
|
8
|
-
// `RhiError` and `detail.wgpuError` is the Channel 3 failure (when both
|
|
9
|
-
// channels fail; AI users `try { await createRenderer(...) } catch (e) {
|
|
10
|
-
// switch (e.detail.webgpuError?.code) { ... } }`).
|
|
11
|
-
// (b) Run-time pipeline build → `renderer.initialization` rejects with the
|
|
12
|
-
// structured `RhiError` (`shader-compile-failed` / `limit-exceeded` /
|
|
13
|
-
// `webgpu-runtime-error` / ...); AI users `await renderer.initialization`.
|
|
14
|
-
// (c) error fan-out → `renderer.subscribe(listener)` captures the same
|
|
15
|
-
// errors as a fallback observability channel (charter proposition 4
|
|
16
|
-
// structured + proposition 9 graceful degradation).
|
|
17
|
-
//
|
|
18
|
-
// AC-15 source-level: this file uses `globalThis.navigator` rather than
|
|
19
|
-
// `navigator` directly; it never touches `window` or `document`.
|
|
1
|
+
// @forgeax/engine-render — WebGPU renderer lifecycle/frame interpreter.
|
|
2
|
+
// Runtime selects the backend; structured RHI errors and browser isolation stay intact.
|
|
20
3
|
|
|
21
|
-
import type { MipmapShaderModuleFactory } from '@forgeax/engine-assets-runtime';
|
|
22
4
|
import {
|
|
23
5
|
AssetRegistry,
|
|
24
6
|
adaptDynamicTextureDevice,
|
|
@@ -28,12 +10,12 @@ import { audioLoader } from '@forgeax/engine-audio-webaudio';
|
|
|
28
10
|
import { Update, type World } from '@forgeax/engine-ecs';
|
|
29
11
|
import { createRenderReadLease, type RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
30
12
|
import {
|
|
31
|
-
|
|
32
|
-
deriveVertexBufferLayoutFromProjection,
|
|
13
|
+
DEFAULT_VERTEX_ATTRIBUTE_MAP,
|
|
33
14
|
type VertexLayoutProjection,
|
|
34
15
|
} from '@forgeax/engine-geometry';
|
|
35
16
|
import type {
|
|
36
17
|
BindGroupLayout,
|
|
18
|
+
Buffer,
|
|
37
19
|
PipelineLayout,
|
|
38
20
|
RenderPipeline,
|
|
39
21
|
Result,
|
|
@@ -50,19 +32,16 @@ import {
|
|
|
50
32
|
standardPhysicalTextureFields,
|
|
51
33
|
} from '@forgeax/engine-shader';
|
|
52
34
|
import type {
|
|
53
|
-
ImportTransport,
|
|
54
35
|
MaterialRenderState,
|
|
55
36
|
ParamSchemaEntry,
|
|
56
37
|
PassKind,
|
|
57
38
|
PrimitiveTopology,
|
|
58
39
|
VertexAttributeMap,
|
|
59
40
|
} from '@forgeax/engine-types';
|
|
60
|
-
import { createDynamicGeometryLifecycle } from '../dynamic-geometry';
|
|
61
41
|
|
|
62
42
|
export type { MaterialShaderManifestEntry } from '@forgeax/engine-shader';
|
|
63
43
|
|
|
64
44
|
import { GpuResidencyCache } from '../device/gpu-residency';
|
|
65
|
-
import type { DeviceResourceKind } from '../device/resource-types';
|
|
66
45
|
import { createEngineMetrics } from '../engine-metrics';
|
|
67
46
|
import { createRecoveryFailedError, RecoverError, type RecoverFailure } from '../errors/recover';
|
|
68
47
|
import {
|
|
@@ -74,7 +53,7 @@ import {
|
|
|
74
53
|
RenderFeatureCapabilityMissingError,
|
|
75
54
|
RenderFeatureStageFailedError,
|
|
76
55
|
} from '../errors/render';
|
|
77
|
-
import { createRenderFeatureHost
|
|
56
|
+
import { createRenderFeatureHost } from '../features/host';
|
|
78
57
|
import { RENDER_FEATURE_VERTEX_LAYOUTS } from '../features/prepared-graphics';
|
|
79
58
|
import type { RenderFeature, RenderFeatureShaderModuleMode } from '../features/types';
|
|
80
59
|
import {
|
|
@@ -82,15 +61,12 @@ import {
|
|
|
82
61
|
postProcessShaderModuleLabel,
|
|
83
62
|
} from '../fullscreen-post-process-pass';
|
|
84
63
|
import { glyphTextLayoutSystem } from '../glyph-text-layout-system';
|
|
64
|
+
import { resolveStandardPbrProgram } from '../gpu-driven/pbr-program';
|
|
85
65
|
import { GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX } from '../gpu-stage';
|
|
86
66
|
import type { BloomInspection } from '../inspection-types';
|
|
67
|
+
import { DeviceScope } from '../lifecycle';
|
|
87
68
|
import {
|
|
88
|
-
|
|
89
|
-
HealthListenerRegistry,
|
|
90
|
-
LostListenerRegistry,
|
|
91
|
-
RhiErrorListenerRegistry,
|
|
92
|
-
} from '../lifecycle';
|
|
93
|
-
import {
|
|
69
|
+
createHdrpBindGroupLayoutDescriptor,
|
|
94
70
|
isCanonicalStandardPbrMaterialShader,
|
|
95
71
|
isStandardPbrMaterialShader,
|
|
96
72
|
type PipelineGroup2Contract,
|
|
@@ -105,6 +81,7 @@ import {
|
|
|
105
81
|
cacheKeyOf,
|
|
106
82
|
colorFormatsForPassKind,
|
|
107
83
|
type PipelineSpec,
|
|
84
|
+
PipelineSpecError,
|
|
108
85
|
passKindPolicyTable,
|
|
109
86
|
} from '../pipeline-spec';
|
|
110
87
|
import type { ExtendedLightingResourceCandidate } from '../prepare/extended-lighting/resources';
|
|
@@ -122,6 +99,7 @@ import {
|
|
|
122
99
|
promoteExtendedLightingCandidate,
|
|
123
100
|
} from '../prepare/extended-lighting/state';
|
|
124
101
|
import type { FrameObservationOptions } from '../record/frame';
|
|
102
|
+
import { getTextureIdentity } from '../record/frame-snapshot';
|
|
125
103
|
import type { GpuPassTimingReason } from '../record/gpu-pass-timing/errors.js';
|
|
126
104
|
import {
|
|
127
105
|
createGpuPassTimingSession,
|
|
@@ -131,14 +109,14 @@ import {
|
|
|
131
109
|
type GpuPassTimingSession,
|
|
132
110
|
} from '../record/gpu-pass-timing/index.js';
|
|
133
111
|
import { GpuTimingCapture, type VolumeTimingObservation } from '../record/gpu-timing';
|
|
134
|
-
import type { RecoveryColdWorkGuard } from '../record/render-context';
|
|
135
112
|
import type {
|
|
113
|
+
AtmosphereShaderSources,
|
|
136
114
|
DrawOwnerOptions,
|
|
115
|
+
FrameObservationDomain,
|
|
137
116
|
FrameObservationRequest,
|
|
138
117
|
FrameReceipt,
|
|
139
118
|
FrameReceiptObservation,
|
|
140
119
|
HealthSnapshot,
|
|
141
|
-
RenderDebugOverlay,
|
|
142
120
|
RendererErrorListener,
|
|
143
121
|
RendererLostListener,
|
|
144
122
|
RendererOptions,
|
|
@@ -147,6 +125,7 @@ import type {
|
|
|
147
125
|
RenderProfile,
|
|
148
126
|
RenderResult,
|
|
149
127
|
RenderWorldLease,
|
|
128
|
+
SsrShaderSources,
|
|
150
129
|
VolumetricFogShaderSources,
|
|
151
130
|
} from '../render-contract';
|
|
152
131
|
import { FXAA_POST_PROCESS_ID, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID } from '../render-contract';
|
|
@@ -163,10 +142,10 @@ import { createGpuPassTimingObservationStore, observeGpuPassTimingDisabled } fro
|
|
|
163
142
|
import { postSpawnResolveJoints } from '../scene-instances/post-spawn-resolve-joints';
|
|
164
143
|
import type { RenderTarget } from '../targets/contracts';
|
|
165
144
|
import { tilemapChunkExtractSystem } from '../tilemap-chunk-extract-system';
|
|
145
|
+
import type { TypedFrameObservationCapture } from '../typed-render-graph-primitives';
|
|
166
146
|
import type { RhiBackendPack } from './backend-contract';
|
|
167
147
|
import type { BundlerOptions } from './bundler-contract';
|
|
168
|
-
import {
|
|
169
|
-
import { createDynamicGeometryHost } from './dynamic-geometry-host';
|
|
148
|
+
import { createRendererDynamicGeometryController } from './dynamic-geometry-host';
|
|
170
149
|
import type { RendererAssemblyImplementation } from './host-contract';
|
|
171
150
|
import {
|
|
172
151
|
allowsUnlitPreparedFallback,
|
|
@@ -188,7 +167,6 @@ import {
|
|
|
188
167
|
shouldDeferMissingPreparedMaterialShader,
|
|
189
168
|
} from './material-shader-policy';
|
|
190
169
|
import type { MeshSsboGrowResult, MeshSsboState } from './mesh-ssbo-grow';
|
|
191
|
-
import { attachDeviceLostFanout } from './recovery/device-loss-fanout';
|
|
192
170
|
import {
|
|
193
171
|
type GenerationAggregate,
|
|
194
172
|
type GenerationPublication,
|
|
@@ -201,32 +179,48 @@ import {
|
|
|
201
179
|
structuredRendererCause,
|
|
202
180
|
validateRenderProfile,
|
|
203
181
|
} from './renderer-facade';
|
|
182
|
+
import { projectRendererFeatureInspection } from './renderer-feature-inspection';
|
|
204
183
|
import {
|
|
205
184
|
type ContinuationTerminator,
|
|
206
185
|
createContinuationTerminator,
|
|
207
186
|
} from './renderer-frame-transaction';
|
|
208
|
-
import {
|
|
187
|
+
import {
|
|
188
|
+
ensureContextConfigured,
|
|
189
|
+
rendererInitializationError,
|
|
190
|
+
rendererInitializationPipelineError,
|
|
191
|
+
wrapDisposeError,
|
|
192
|
+
} from './renderer-helpers';
|
|
209
193
|
import { projectRendererOutputInspection } from './renderer-inspection';
|
|
210
194
|
import {
|
|
211
195
|
createRecoveryContinuation,
|
|
212
196
|
createRecoveryDeadline,
|
|
213
197
|
createSingleFlight,
|
|
214
|
-
type RecoveryGuidance,
|
|
215
198
|
type RecoveryPhase,
|
|
216
199
|
} from './renderer-lifecycle';
|
|
200
|
+
import { derivedPhysicsFrameError } from './webgpu-renderer-guards';
|
|
201
|
+
import {
|
|
202
|
+
createRecoveryFailureLocation,
|
|
203
|
+
type RecoveryFailureLocation,
|
|
204
|
+
} from './webgpu-renderer-recovery-failure';
|
|
205
|
+
|
|
206
|
+
export type { RecoveryFailureLocation } from './webgpu-renderer-recovery-failure';
|
|
207
|
+
|
|
217
208
|
import { STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES } from './shader-prewarm-policy';
|
|
218
209
|
import { buildReadyWebGPU } from './webgpu-ready';
|
|
219
210
|
import { DEPTH_TEXTURE_FORMAT, HDR_COLOR_ATTACHMENT_FORMAT } from './webgpu-ready-contract';
|
|
211
|
+
import { adaptMipmapShaderModuleFactory } from './webgpu-ready-mipmap';
|
|
212
|
+
import {
|
|
213
|
+
tryCreateWebGPURenderer as tryCreateWebGPURendererBootstrap,
|
|
214
|
+
type WebGPURendererInternals,
|
|
215
|
+
} from './webgpu-renderer-bootstrap';
|
|
216
|
+
import type { WebGPUOutcome } from './webgpu-renderer-contract';
|
|
217
|
+
import { disposeObservationCaptureSet } from './webgpu-renderer-observation';
|
|
220
218
|
import {
|
|
221
|
-
|
|
222
|
-
DEFAULT_VERTEX_ATTRS,
|
|
223
|
-
MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS,
|
|
224
|
-
PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
225
|
-
POSITION_SIZE_COLOR_INSTANCE_VERTEX_BUFFERS,
|
|
219
|
+
isPreparedMaterialVertexLayout,
|
|
226
220
|
PREPARED_INSTANCE_VERTEX_ATTRS,
|
|
227
221
|
PREPARED_MATERIAL_INSTANCE_VERTEX_ATTRS,
|
|
228
|
-
|
|
229
|
-
|
|
222
|
+
particleMaterialInputVertexBuffers,
|
|
223
|
+
resolveWebGPUVertexBufferLayouts,
|
|
230
224
|
} from './webgpu-vertex-layouts';
|
|
231
225
|
|
|
232
226
|
export type { BundlerOptions } from './bundler-contract';
|
|
@@ -249,362 +243,41 @@ export {
|
|
|
249
243
|
selectPipelineLayoutForVariant,
|
|
250
244
|
shouldDeferMissingPreparedMaterialShader,
|
|
251
245
|
} from './material-shader-policy';
|
|
252
|
-
|
|
253
|
-
MeshSsboBufferWrapper,
|
|
254
|
-
MeshSsboGrowController,
|
|
255
|
-
MeshSsboGrowControllerInit,
|
|
256
|
-
MeshSsboGrowDevice,
|
|
257
|
-
MeshSsboGrowErrorRegistry,
|
|
258
|
-
MeshSsboGrowResult,
|
|
259
|
-
MeshSsboState,
|
|
260
|
-
} from './mesh-ssbo-grow';
|
|
246
|
+
|
|
261
247
|
export {
|
|
262
248
|
createMeshSsboGrowController,
|
|
263
249
|
deriveStorageBufferCeiling,
|
|
264
250
|
INITIAL_MESH_SSBO_SLOT_COUNT,
|
|
251
|
+
type MeshSsboBufferWrapper,
|
|
252
|
+
type MeshSsboGrowController,
|
|
253
|
+
type MeshSsboGrowControllerInit,
|
|
254
|
+
type MeshSsboGrowDevice,
|
|
255
|
+
type MeshSsboGrowErrorRegistry,
|
|
256
|
+
type MeshSsboGrowResult,
|
|
257
|
+
type MeshSsboState,
|
|
265
258
|
requireMeshSsboBuffer,
|
|
266
259
|
} from './mesh-ssbo-grow';
|
|
267
260
|
export { exposeRenderer } from './renderer-facade';
|
|
268
261
|
export {
|
|
262
|
+
selectGpuDrivenSceneIndexVariant,
|
|
269
263
|
selectHdrpPbrPrewarmVariants,
|
|
270
264
|
selectProbePrewarmVariants,
|
|
271
265
|
selectSkinPrewarmVariants,
|
|
272
266
|
selectStandardPbrTransmissionPrewarmVariants,
|
|
273
267
|
} from './shader-prewarm-policy';
|
|
268
|
+
export type { WebGPUOutcome, WebGPURendererInternals } from './webgpu-renderer-contract';
|
|
269
|
+
export { disposeObservationCaptureSet } from './webgpu-renderer-observation';
|
|
274
270
|
|
|
275
|
-
|
|
276
|
-
factory: RhiBackendPack['createShaderModule'],
|
|
277
|
-
): MipmapShaderModuleFactory | undefined {
|
|
278
|
-
if (factory === undefined) return undefined;
|
|
279
|
-
return (device, descriptor) => factory(device as RhiDevice, descriptor);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Re-export registerAdvanceAnimationPlayer so consumers can wire it.
|
|
283
|
-
// Re-export registerPropagateTransforms so consumers can wire the
|
|
284
|
-
// Transform.world mat4 derivation (audio listener sync + picking read the
|
|
285
|
-
// derived Transform.world from scripts).
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Bundler-layer injection accepted by `createRenderer` (and proxied by
|
|
289
|
-
* `createApp`) as the optional third argument.
|
|
290
|
-
*
|
|
291
|
-
* feat-20260608-create-app-param-surface-trim / M2 / D-3: aggregates the two
|
|
292
|
-
* host-injected build-tool channels:
|
|
293
|
-
*
|
|
294
|
-
* - shaderManifestUrl: the URL the host's vite-plugin-shader emit step
|
|
295
|
-
* wrote `manifest.json` to. When this field is omitted (or `bundler`
|
|
296
|
-
* itself is omitted), createRenderer falls back to
|
|
297
|
-
* '/shaders/manifest.json' (D-2 q5-A) so the LO 1.1 zero-config takeoff
|
|
298
|
-
* path keeps working without explicit injection. Tests can inject via a
|
|
299
|
-
* `data:application/json,...` URL to bypass fetch.
|
|
300
|
-
*
|
|
301
|
-
* - importTransport: dev-only ImportTransport forwarded verbatim to the
|
|
302
|
-
* AssetRegistry third ctor slot so DDC-miss assets can lazy-import.
|
|
303
|
-
* Absent => shipped form (a DDC miss fails fast with `asset-not-imported`).
|
|
304
|
-
*
|
|
305
|
-
* The interface is structurally shared with `@forgeax/engine-app` while this
|
|
306
|
-
* package keeps the renderer independent from the host package.
|
|
307
|
-
*/
|
|
308
|
-
// ─── Backend pack — M3 auto-select internal shape ───────────────────────────
|
|
309
|
-
|
|
310
|
-
// ─── WebGPU branch ──────────────────────────────────────────────────────────
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Wire the spec `device.lost` Promise into the lost / error / health channels
|
|
314
|
-
* (research §F-4 / R2). A genuine device loss fans out through `errorRegistry`
|
|
315
|
-
* so the public `renderer.subscribe` error event triggers, and
|
|
316
|
-
* through `healthRegistry` so `health().reason` flips to `'device-lost'`
|
|
317
|
-
* (feat-20260622-s5 M1/M2). Explicit renderer teardown is observable through
|
|
318
|
-
* `onLost`, but is not a runtime error and must not manufacture an RhiError.
|
|
319
|
-
*
|
|
320
|
-
* Extracted to a single helper (SSOT) so the createRenderer assembly path AND
|
|
321
|
-
* the recover() rebuild path attach byte-identical fan-out to whichever device
|
|
322
|
-
* is current — recover() mints a fresh device whose own `lost` Promise must be
|
|
323
|
-
* re-wired to the SAME registries the host already subscribed to.
|
|
324
|
-
*/
|
|
325
|
-
// Device loss wiring lives in the recovery assembly seam so boot and recovery
|
|
326
|
-
// cannot drift into separate lifecycle event paths.
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* tryCreateWebGPURenderer returns three kinds of outcome (fix-f1 / w16):
|
|
330
|
-
* - `ok` → Renderer created successfully
|
|
331
|
-
* - `rhi-err` → RHI Result.err path (preserves the original RhiError
|
|
332
|
-
* for AI consumers to read; now includes context acquisition
|
|
333
|
-
* failures via pack.rhi.acquireCanvasContext)
|
|
334
|
-
* - `throw` → exception path (caught and wrapped by the caller as
|
|
335
|
-
* an Error)
|
|
336
|
-
*/
|
|
337
|
-
export type WebGPUOutcome =
|
|
338
|
-
| { kind: 'ok'; renderer: RendererAssemblyImplementation }
|
|
339
|
-
| { kind: 'rhi-err'; error: RhiError }
|
|
340
|
-
| { kind: 'throw'; error: Error };
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Goes through the strict two-step path exposed by the injected backend pack:
|
|
344
|
-
* `rhi.requestAdapter()` -> `adapter.requestDevice()`
|
|
345
|
-
* (M6 fix-up [w51] retires the legacy single-step factory per plan-strategy
|
|
346
|
-
* §6 M3 break-point #2 + AGENTS.md break-point list 2026-05-10 #2). Returns
|
|
347
|
-
* `Result<RhiAdapter, RhiError>` then `Result<RhiDevice, RhiError>`; each
|
|
348
|
-
* failure / failure to acquire the canvas WebGPU context returns a
|
|
349
|
-
* structured outcome so the caller can decide on fallback and error-
|
|
350
|
-
* preservation strategy (plan-strategy §7.3).
|
|
351
|
-
*
|
|
352
|
-
* fix-f1: on either Result.err path the original `RhiError` is preserved
|
|
353
|
-
* via the `rhi-err` outcome — when fallback also fails, AI consumers read
|
|
354
|
-
* it via `EngineEnvironmentError.detail.webgpuError.code`.
|
|
355
|
-
*
|
|
356
|
-
* w19 / M4: context acquisition goes through `pack.rhi.acquireCanvasContext(canvas)`
|
|
357
|
-
* — a polymorphic entry owned by the selected backend adapter.
|
|
358
|
-
* The backend pack is selected BEFORE this call (in `loadBackendPack` /
|
|
359
|
-
* the Channel 2→3 fallback in `createRenderer`), so the canvas context
|
|
360
|
-
* is never type-locked by a wrong backend's acquisition attempt.
|
|
361
|
-
*/
|
|
271
|
+
/** Bundler injection and the WebGPU renderer construction entry points. */
|
|
362
272
|
let nextDerivedSystemId = 0;
|
|
363
|
-
|
|
364
273
|
export async function tryCreateWebGPURenderer(
|
|
365
274
|
canvas: HTMLCanvasElement | OffscreenCanvas,
|
|
366
275
|
options: RendererOptions | undefined,
|
|
367
276
|
pack: RhiBackendPack,
|
|
368
|
-
// feat-20260608 M2 / D-3: BundlerOptions threaded verbatim. Carries
|
|
369
|
-
// shaderManifestUrl (consumed in getShader fallback ~30 lines below) and
|
|
370
|
-
// importTransport (forwarded to the AssetRegistry third ctor slot;
|
|
371
|
-
// undefined keeps the shipped form, AC-08).
|
|
372
277
|
bundler: BundlerOptions | undefined,
|
|
373
278
|
): Promise<WebGPUOutcome> {
|
|
374
|
-
|
|
375
|
-
// M6 fix-up [w51]: spec-aligned two-step path. Step 1 - requestAdapter()
|
|
376
|
-
// surfaces adapter.features / adapter.limits (capability pre-screen,
|
|
377
|
-
// charter proposition 4 forward-reachable). Step 2 - adapter.requestDevice()
|
|
378
|
-
// requests the actual device. M3 D-P4: `pack.rhi` is the auto-selected
|
|
379
|
-
// backend singleton (rhi-webgpu / rhi-wgpu / explicit escape hatch).
|
|
380
|
-
// w19: thread canvas as compatibleSurface so the wgpu GL backend can
|
|
381
|
-
// enumerate adapters against it (escape hatch, capability-gated).
|
|
382
|
-
const adapterResult = await pack.rhi.requestAdapter(undefined, canvas);
|
|
383
|
-
if (!adapterResult.ok) {
|
|
384
|
-
return { kind: 'rhi-err', error: adapterResult.error };
|
|
385
|
-
}
|
|
386
|
-
const adapter = adapterResult.value;
|
|
387
|
-
// M4 w28: filter compression features from adapter.features (AC-07). The
|
|
388
|
-
// same helper is used by recover() below so both paths share one profile.
|
|
389
|
-
const deviceOpts = deviceOptionsForAdapter(adapter, options);
|
|
390
|
-
const result = await adapter.requestDevice(deviceOpts);
|
|
391
|
-
if (!result.ok) {
|
|
392
|
-
return { kind: 'rhi-err', error: result.error };
|
|
393
|
-
}
|
|
394
|
-
const device: RhiDevice = result.value;
|
|
395
|
-
|
|
396
|
-
// Acquire the canvas context through the backend pack (M3 / w16).
|
|
397
|
-
// rhi-webgpu: internally calls canvas.getContext('webgpu') + branded wrap.
|
|
398
|
-
// rhi-wgpu: internally calls wasm createSurface(canvas) + configure.
|
|
399
|
-
// When context acquisition fails (null from canvas.getContext or wasm
|
|
400
|
-
// createSurface throws), the pack returns Result.err(RhiError).
|
|
401
|
-
const ctxResult = pack.rhi.acquireCanvasContext(canvas);
|
|
402
|
-
if (!ctxResult.ok) {
|
|
403
|
-
return { kind: 'rhi-err', error: ctxResult.error };
|
|
404
|
-
}
|
|
405
|
-
const context: RhiCanvasContext = ctxResult.value;
|
|
406
|
-
|
|
407
|
-
const lostRegistry = new LostListenerRegistry();
|
|
408
|
-
const errorRegistry = new RhiErrorListenerRegistry();
|
|
409
|
-
const healthRegistry = new HealthListenerRegistry();
|
|
410
|
-
const lossObserver: {
|
|
411
|
-
current?: (detail: string) => void;
|
|
412
|
-
} = {};
|
|
413
|
-
const generationState: {
|
|
414
|
-
current: number;
|
|
415
|
-
onStaleLoss?: () => void;
|
|
416
|
-
} = { current: 0 };
|
|
417
|
-
// device.lost dual-track (research §F-4 / R2 countermeasure): the spec
|
|
418
|
-
// Promise is passed through to the lost-fan-out registry (extracted to
|
|
419
|
-
// attachDeviceLostFanout so the recover() rebuild path re-attaches the
|
|
420
|
-
// SAME wiring to the freshly-minted device — SSOT, one fan-out shape).
|
|
421
|
-
attachDeviceLostFanout(device, pack, {
|
|
422
|
-
lostRegistry,
|
|
423
|
-
errorRegistry,
|
|
424
|
-
healthRegistry,
|
|
425
|
-
generation: 0,
|
|
426
|
-
currentGeneration: () => generationState.current,
|
|
427
|
-
onStaleLoss: () => generationState.onStaleLoss?.(),
|
|
428
|
-
onDeviceLost: (detail) => lossObserver.current?.(detail),
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
// D-VD2 Round 2 wire-up part 2: register the spec `onuncapturederror`
|
|
432
|
-
// listener on the raw GPUDevice so GPUUncapturedErrorEvent (validation /
|
|
433
|
-
// oom / internal) is translated to the 17-member RhiErrorCode union +
|
|
434
|
-
// dispatched through the public Renderer event subscription. When the pack does not expose a
|
|
435
|
-
// raw-device escape hatch (rhi-wgpu wasm path, custom escape hatch), the
|
|
436
|
-
// listener registration is skipped — the device.lost dual-channel above
|
|
437
|
-
// still fires through `RhiDevice.lost` Promise on every path (graceful
|
|
438
|
-
// degradation per charter proposition 9; per-path coverage matrix in
|
|
439
|
-
// packages/engine/README.md `## Error model`).
|
|
440
|
-
if (pack._internal_getRawDevice && pack.translateErrorEventToRhiError) {
|
|
441
|
-
const backendDevice = pack._internal_getRawDevice(device);
|
|
442
|
-
if (backendDevice && typeof backendDevice === 'object') {
|
|
443
|
-
const target = backendDevice as {
|
|
444
|
-
onuncapturederror?: ((event: unknown) => void) | null;
|
|
445
|
-
};
|
|
446
|
-
// Spec form: `device.onuncapturederror = (event) => ...`. The listener
|
|
447
|
-
// sets a property handler (spec normative GPUDevice extends EventTarget
|
|
448
|
-
// and exposes `onuncapturederror` as a settable callback property);
|
|
449
|
-
// engine writes through the property so each pre-existing handler that
|
|
450
|
-
// an AI user assigned would be replaced (last-write-wins; spec lifetime
|
|
451
|
-
// is device-scoped so engine ownership is OK — AI users observe via
|
|
452
|
-
// the Renderer event subscription, not the raw property).
|
|
453
|
-
target.onuncapturederror = (event: unknown): void => {
|
|
454
|
-
const translated = pack.translateErrorEventToRhiError?.(event);
|
|
455
|
-
if (translated && !translated.ok) {
|
|
456
|
-
errorRegistry.fire(translated.error);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return {
|
|
462
|
-
kind: 'ok',
|
|
463
|
-
renderer: await makeWebGPURenderer({
|
|
464
|
-
canvas,
|
|
465
|
-
device,
|
|
466
|
-
context,
|
|
467
|
-
options,
|
|
468
|
-
bundler,
|
|
469
|
-
lostRegistry,
|
|
470
|
-
errorRegistry,
|
|
471
|
-
healthRegistry,
|
|
472
|
-
pack,
|
|
473
|
-
importTransport,
|
|
474
|
-
lossObserver,
|
|
475
|
-
generationState,
|
|
476
|
-
}),
|
|
477
|
-
};
|
|
279
|
+
return tryCreateWebGPURendererBootstrap(canvas, options, pack, bundler, makeWebGPURenderer);
|
|
478
280
|
}
|
|
479
|
-
|
|
480
|
-
export interface WebGPURendererInternals {
|
|
481
|
-
canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
482
|
-
device: RhiDevice;
|
|
483
|
-
// M6 / w41 (feat-20260510-rhi-resource-creation): forgeax RhiCanvasContext
|
|
484
|
-
// brand; RenderSystem + ensureContextConfigured both go through the RHI
|
|
485
|
-
// surface (charter proposition 5 consistent abstraction red line).
|
|
486
|
-
context: RhiCanvasContext;
|
|
487
|
-
options: RendererOptions | undefined;
|
|
488
|
-
/**
|
|
489
|
-
* feat-20260608-create-app-param-surface-trim / M2 / D-3: BundlerOptions
|
|
490
|
-
* forwarded verbatim from createRenderer's third arg. Carries
|
|
491
|
-
* shaderManifestUrl (host-injected vite-plugin-shader emit URL; absent =>
|
|
492
|
-
* createRenderer falls back to '/shaders/manifest.json') and the
|
|
493
|
-
* importTransport that previously rode a dedicated internal slot.
|
|
494
|
-
*/
|
|
495
|
-
bundler: BundlerOptions | undefined;
|
|
496
|
-
lostRegistry: LostListenerRegistry;
|
|
497
|
-
errorRegistry: RhiErrorListenerRegistry;
|
|
498
|
-
healthRegistry: HealthListenerRegistry;
|
|
499
|
-
/** M3 D-P4 auto-select pack — carries the dynamic-imported rhi-webgpu / rhi-wgpu singleton + optional async shader factory. */
|
|
500
|
-
pack: RhiBackendPack;
|
|
501
|
-
/**
|
|
502
|
-
* feat-20260604-hdr-equirect-cube-importer-loader M4 / w16 (D-3): the
|
|
503
|
-
* dev-only ImportTransport, forwarded verbatim from createRenderer to the
|
|
504
|
-
* AssetRegistry third ctor slot. `undefined` keeps the shipped form (DDC
|
|
505
|
-
* miss fails fast with `asset-not-imported`, AC-08).
|
|
506
|
-
*/
|
|
507
|
-
importTransport?: ImportTransport | undefined;
|
|
508
|
-
/**
|
|
509
|
-
* feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M2 / T-M2-05:
|
|
510
|
-
* mesh-SSBO grow hook surfaced by `makeWebGPURenderer` after the controller
|
|
511
|
-
* is constructed. Undefined before `makeWebGPURenderer` runs; populated by
|
|
512
|
-
* the time the renderer is returned to the caller. M3's record stage reads
|
|
513
|
-
* this field via the runtime path that bridges into render-system-record.ts
|
|
514
|
-
* (the bridging plumbing is in M3's targetFiles).
|
|
515
|
-
*
|
|
516
|
-
* Returns `{ ok: true }` when the grow completed (or short-circuited
|
|
517
|
-
* idempotently); `{ ok: false, code }` with a fired structured error
|
|
518
|
-
* (`MeshSsboCeilingReachedError` or `MeshSsboCapacityExceededError`) when
|
|
519
|
-
* the request cannot be satisfied. Never throws (D-5).
|
|
520
|
-
*/
|
|
521
|
-
growMeshSsbo?: (neededSlots: number) => MeshSsboGrowResult;
|
|
522
|
-
/**
|
|
523
|
-
* feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M3 / T-M3-04:
|
|
524
|
-
* read-only handle to the grow controller's state (slotCount + wrapper
|
|
525
|
-
* refs), surfaced alongside `growMeshSsbo` so `ensureMeshSsboCapacity`
|
|
526
|
-
* (record-stage wiring) can short-circuit when slotCount already covers the
|
|
527
|
-
* frame's renderable count and so the dev-mode info log can report the
|
|
528
|
-
* before/after slot count transition. Undefined until buildReadyWebGPU
|
|
529
|
-
* has wired the controller; identity stable across grow events.
|
|
530
|
-
*/
|
|
531
|
-
meshSsboState?: MeshSsboState;
|
|
532
|
-
/** Host-owned producer features; created before device-bound assembly starts. */
|
|
533
|
-
featureHost?: RenderFeatureHost;
|
|
534
|
-
/** App-owned overlay capability read by the typed graph at frame time. */
|
|
535
|
-
debugOverlay?: RenderDebugOverlay;
|
|
536
|
-
/** Composed renderer utility sources installed atomically at build-ready. */
|
|
537
|
-
volumetricFogShaders?: VolumetricFogShaderSources | undefined;
|
|
538
|
-
gpuPassTimingSubmittedWork?: Promise<void> | undefined;
|
|
539
|
-
lossObserver: { current?: (detail: string) => void };
|
|
540
|
-
generationState: {
|
|
541
|
-
current: number;
|
|
542
|
-
onStaleLoss?: () => void;
|
|
543
|
-
};
|
|
544
|
-
recoveryColdWorkGuard?: RecoveryColdWorkGuard | undefined;
|
|
545
|
-
getMaterialShaderUvSetCount?: (materialShaderId: string) => number | undefined;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
export interface RecoveryFailureLocation {
|
|
549
|
-
readonly phase: RecoveryPhase;
|
|
550
|
-
readonly retryable: boolean;
|
|
551
|
-
readonly guidance: RecoveryGuidance;
|
|
552
|
-
readonly owner: string;
|
|
553
|
-
readonly resourceKind: DeviceResourceKind;
|
|
554
|
-
readonly cause: unknown;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function recoveryCauseCode(cause: unknown): string | undefined {
|
|
558
|
-
if (typeof cause !== 'object' || cause === null) return undefined;
|
|
559
|
-
const directCode = (cause as { readonly code?: unknown }).code;
|
|
560
|
-
if (typeof directCode === 'string') return directCode;
|
|
561
|
-
const detail = (cause as { readonly detail?: unknown }).detail;
|
|
562
|
-
if (typeof detail !== 'object' || detail === null) return undefined;
|
|
563
|
-
const detailCode = (detail as { readonly code?: unknown }).code;
|
|
564
|
-
if (typeof detailCode === 'string') return detailCode;
|
|
565
|
-
return recoveryCauseCode((detail as { readonly cause?: unknown }).cause);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
function createRecoveryFailureLocation(
|
|
569
|
-
phase: RecoveryPhase,
|
|
570
|
-
cause: unknown,
|
|
571
|
-
overrides: Partial<Omit<RecoveryFailureLocation, 'phase' | 'cause'>> = {},
|
|
572
|
-
): RecoveryFailureLocation {
|
|
573
|
-
const code = recoveryCauseCode(cause) ?? '';
|
|
574
|
-
const resourceKind: DeviceResourceKind = code.includes('shader')
|
|
575
|
-
? 'shader'
|
|
576
|
-
: code.includes('buffer')
|
|
577
|
-
? 'buffer'
|
|
578
|
-
: code.includes('texture')
|
|
579
|
-
? 'texture'
|
|
580
|
-
: code.includes('binding')
|
|
581
|
-
? 'binding'
|
|
582
|
-
: phase === 'rehydrate'
|
|
583
|
-
? 'shader'
|
|
584
|
-
: phase === 'acquire-adapter' || phase === 'acquire-device'
|
|
585
|
-
? 'surface'
|
|
586
|
-
: phase === 'publish' || phase === 'cleanup'
|
|
587
|
-
? 'surface'
|
|
588
|
-
: 'pipeline';
|
|
589
|
-
const owner =
|
|
590
|
-
code.includes('shader') || phase === 'rehydrate'
|
|
591
|
-
? 'shader'
|
|
592
|
-
: phase === 'acquire-adapter' || phase === 'acquire-device'
|
|
593
|
-
? 'backend'
|
|
594
|
-
: phase === 'compile-graph'
|
|
595
|
-
? 'pipeline'
|
|
596
|
-
: 'renderer';
|
|
597
|
-
return {
|
|
598
|
-
phase,
|
|
599
|
-
retryable: false,
|
|
600
|
-
guidance: 'repair-owner',
|
|
601
|
-
owner,
|
|
602
|
-
resourceKind,
|
|
603
|
-
cause,
|
|
604
|
-
...overrides,
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
|
|
608
281
|
async function makeWebGPURenderer(
|
|
609
282
|
internals: WebGPURendererInternals,
|
|
610
283
|
): Promise<RendererAssemblyImplementation> {
|
|
@@ -893,16 +566,13 @@ async function makeWebGPURenderer(
|
|
|
893
566
|
// looks up MeshFilter.assetHandle here per frame; buildReadyWebGPU step 3
|
|
894
567
|
// uploads the registry's builtin mesh geometry to GPU buffers so the
|
|
895
568
|
// RenderSystem can route MeshFilter.assetHandle to a real (vbo, ibo) pair.
|
|
896
|
-
//
|
|
897
569
|
// feat-20260527 M1 / w1: ShaderCatalog is now eager-constructed and
|
|
898
570
|
// constructor-injected into AssetRegistry (plan-strategy D-1). The
|
|
899
571
|
// ShaderCatalogDevice adapter (shared between the catalog and per-MaterialShader
|
|
900
572
|
// pipeline cache) is already available at this point.
|
|
901
573
|
const shaderCatalog = getShader();
|
|
902
|
-
|
|
903
574
|
// feat-20260528-material-shader-registration-unification M3 / w14:
|
|
904
575
|
// placeholder hardcoded installMaterialArtifact calls deleted.
|
|
905
|
-
//
|
|
906
576
|
// bug-20260601-hello-tonemap-material-register M1 (plan-strategy D-1):
|
|
907
577
|
// All material-shader entries are now registered from the manifest
|
|
908
578
|
// BEFORE the renderer is returned, so `register<MaterialAsset>` referencing
|
|
@@ -945,8 +615,7 @@ async function makeWebGPURenderer(
|
|
|
945
615
|
renderSystemForTargetPromotion?.isCubeCapturePending(target) !== true,
|
|
946
616
|
});
|
|
947
617
|
// feat-20260527-sprite-nineslice M4 / w16 (D-5): detached host-owned
|
|
948
|
-
// EngineMetrics counter
|
|
949
|
-
// stage via `RenderSystemRuntime.metrics` so soft-warns
|
|
618
|
+
// EngineMetrics counter reaches the record stage via `RenderSystemRuntime.metrics`.
|
|
950
619
|
// (`nineslice.scale-too-small`, `nineslice.tile-needs-repeat-sampler`) bump
|
|
951
620
|
// counters through the owning service (charter P3 machine-readable signals
|
|
952
621
|
// over a per-frame console.warn flood). Each renderer assembly owns its own
|
|
@@ -1016,8 +685,7 @@ async function makeWebGPURenderer(
|
|
|
1016
685
|
// feat-20260707 M5 / w33 (D-11 + D-8): project the RhiCaps three-way
|
|
1017
686
|
// compression triple into the codec-facing `TranscodeCaps` and wire it into
|
|
1018
687
|
// the registry so the texture / equirect Basis arms can pick a transcode
|
|
1019
|
-
// target. One-line projection; the loader stays a pure consumer of
|
|
1020
|
-
// declared ctx input (Pipeline Isolation).
|
|
688
|
+
// target. One-line projection; the loader stays a pure consumer of declared ctx input (Pipeline Isolation).
|
|
1021
689
|
assets.setTranscodeCaps({
|
|
1022
690
|
bc: internals.device.caps.textureCompressionBc,
|
|
1023
691
|
etc2: internals.device.caps.textureCompressionEtc2,
|
|
@@ -1039,25 +707,6 @@ async function makeWebGPURenderer(
|
|
|
1039
707
|
// count is asserted explicitly. Missing mesh sets are clamp-to-last aliases.
|
|
1040
708
|
materialShaderUvSetCounts.set('forgeax::default-standard-pbr', 8);
|
|
1041
709
|
materialShaderUvSetCounts.set(SKIN_MATERIAL_SHADER_ID, 8);
|
|
1042
|
-
// M5 wiring (feat-20260517-vite-plugin-image-build-time-cook w14b): hand
|
|
1043
|
-
// the RhiDevice to the AssetRegistry so `loadByGuid<TextureAsset>` ->
|
|
1044
|
-
// `loadTextureFromEntry` -> `uploadTexture` actually runs the GPU
|
|
1045
|
-
// upload step plan-strategy section 3.2 sequence specifies. Without
|
|
1046
|
-
// this call uploadTexture step 3 short-circuits with `Result.ok(undefined)`
|
|
1047
|
-
// and the render-system materialBindGroup falls through to the 1x1
|
|
1048
|
-
// white fallback view (charter P3 violation: silent loss of texture).
|
|
1049
|
-
// bug-20260518 D-1: configureGpuDevice gains a 2nd parameter --
|
|
1050
|
-
// `pack.createShaderModule`, the top-level async shader-module factory
|
|
1051
|
-
// (rhi-webgpu / rhi-wgpu both expose it). The runtime mipmap utility uses
|
|
1052
|
-
// this factory inside `generateMipmaps` instead of the (now-deleted)
|
|
1053
|
-
// synchronous `device.createShaderModule` member. The parameter is
|
|
1054
|
-
// structurally optional at this call-site so the explicit-rhi escape
|
|
1055
|
-
// hatch (D-R5) without a top-level factory still routes through the
|
|
1056
|
-
// existing `invokeDeviceCreateShaderModule` fallback in
|
|
1057
|
-
// buildReadyWebGPU; AssetRegistry.uploadTexture's mipmap branch surfaces
|
|
1058
|
-
// the missing-factory case as a structured `rhi-not-available` error
|
|
1059
|
-
// (charter P3 explicit failure) rather than throwing here.
|
|
1060
|
-
//
|
|
1061
710
|
const packShaderFactory = adaptMipmapShaderModuleFactory(internals.pack.createShaderModule);
|
|
1062
711
|
// The RhiDevice surface satisfies `MipmapBlitDevice` (createTexture +
|
|
1063
712
|
// createTextureView + createCommandEncoder + createBindGroup +
|
|
@@ -1128,28 +777,6 @@ async function makeWebGPURenderer(
|
|
|
1128
777
|
gpuStore.bindDeviceScope(activeDeviceScope);
|
|
1129
778
|
};
|
|
1130
779
|
let readySettled = false;
|
|
1131
|
-
// w24 — Renderer.initialization returns Promise<Result<void, RhiError>>: resolve
|
|
1132
|
-
// ok(undefined) on success / resolve err(RhiError) on failure. The legacy
|
|
1133
|
-
// reject path is converted by a `.then() / .catch()` wrap so the inner
|
|
1134
|
-
// factory (`buildReadyWebGPU`) keeps its reject-on-error shape (research
|
|
1135
|
-
// F-3 internal contract preserved) while the public surface settles
|
|
1136
|
-
// strictly resolve-only (charter proposition 4 explicit failure - AI
|
|
1137
|
-
// users branch on `.ok` instead of try/catch).
|
|
1138
|
-
// feat-20260523-shader-template-instance-split M9-T03 (D-PipelineBuilder):
|
|
1139
|
-
// per-MaterialShader pipeline cache (LDR + HDR variants keyed by
|
|
1140
|
-
// `${materialShaderId}:${ldr|hdr}`). Map is owned by the renderer (lifetime
|
|
1141
|
-
// = Renderer) so cache hits persist across draw calls but reset between
|
|
1142
|
-
// renderers. Map.get / .set are sync; the underlying shader-module compile
|
|
1143
|
-
// is async-cached inside `makeShaderDeviceAdapter` (1-frame warmup -- first
|
|
1144
|
-
// miss returns Result.err('rhi-not-available'); after the async build
|
|
1145
|
-
// resolves the adapter caches the module, the next miss-then-build returns
|
|
1146
|
-
// Ok and lands in this Map).
|
|
1147
|
-
//
|
|
1148
|
-
// M6 fix-up: declared BEFORE `buildReadyWebGPU` so the boot-time SPEC_CONST
|
|
1149
|
-
// prewarm step inside that promise can seed URP-variant entries directly
|
|
1150
|
-
// (the seeding closure captures this Map by reference; ordering is safe
|
|
1151
|
-
// because the prewarm step runs strictly before the first frame's
|
|
1152
|
-
// `getMaterialShaderPipeline` lookup that would consume it).
|
|
1153
780
|
const materialShaderPipelineCache = new Map<string, RenderPipeline>();
|
|
1154
781
|
const materialShaderPipelineGroup2Contracts = new WeakMap<object, PipelineGroup2Contract>();
|
|
1155
782
|
const materialShaderManifestEntryCache = new Map<string, MaterialShaderManifestEntry>();
|
|
@@ -1415,9 +1042,23 @@ async function makeWebGPURenderer(
|
|
|
1415
1042
|
};
|
|
1416
1043
|
const getOrBuildPreparedMaterialPipelineLayout = (
|
|
1417
1044
|
materialShaderId: string,
|
|
1045
|
+
clustered = false,
|
|
1418
1046
|
): PipelineLayout | null => {
|
|
1419
1047
|
const cacheState = currentPipelineCacheState();
|
|
1420
|
-
const
|
|
1048
|
+
const lookup = getShader().findMaterialArtifact(materialShaderId);
|
|
1049
|
+
const source = lookup.ok ? lookup.value.source : '';
|
|
1050
|
+
const hasView =
|
|
1051
|
+
/@group\s*\(\s*0\s*\)\s*@binding\s*\(\s*0\s*\)\s*var\s*<\s*uniform\s*>\s*view(?:X_naga_oil_mod_[A-Z0-9]+)?\b/u.test(
|
|
1052
|
+
source,
|
|
1053
|
+
);
|
|
1054
|
+
const hasSceneDepthAtZero =
|
|
1055
|
+
/@group\s*\(\s*0\s*\)\s*@binding\s*\(\s*0\s*\)[^;]*texture_depth_2d\b/u.test(source);
|
|
1056
|
+
const hasSceneDepthAtOne =
|
|
1057
|
+
/@group\s*\(\s*0\s*\)\s*@binding\s*\(\s*1\s*\)[^;]*texture_depth_2d\b/u.test(source);
|
|
1058
|
+
const sceneInputKind =
|
|
1059
|
+
hasSceneDepthAtOne && hasView ? 'view-depth' : hasSceneDepthAtZero ? 'depth' : 'view';
|
|
1060
|
+
const cacheKey = `${materialShaderId}:${clustered ? 'cluster' : 'mesh'}:${sceneInputKind}`;
|
|
1061
|
+
const cached = cacheState.preparedMaterialPipelineLayoutCache.get(cacheKey);
|
|
1421
1062
|
if (cached !== undefined) return cached;
|
|
1422
1063
|
const currentState = currentPipelineState();
|
|
1423
1064
|
if (currentState === null) return null;
|
|
@@ -1426,15 +1067,72 @@ async function makeWebGPURenderer(
|
|
|
1426
1067
|
? undefined
|
|
1427
1068
|
: getOrBuildPerShaderMaterialLayout(materialShaderId)?.materialBgl) ??
|
|
1428
1069
|
currentState.materialBindGroupLayout;
|
|
1070
|
+
let sceneInputLayout: BindGroupLayout = currentState.viewBindGroupLayout;
|
|
1071
|
+
if (sceneInputKind !== 'view') {
|
|
1072
|
+
const sceneInputLayoutResult = currentBuildDevice().createBindGroupLayout({
|
|
1073
|
+
label: `prepared-material-${sceneInputKind}-${materialShaderId}`,
|
|
1074
|
+
entries:
|
|
1075
|
+
sceneInputKind === 'view-depth'
|
|
1076
|
+
? [
|
|
1077
|
+
{
|
|
1078
|
+
binding: 0,
|
|
1079
|
+
visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
|
|
1080
|
+
buffer: { type: 'uniform' as const },
|
|
1081
|
+
},
|
|
1082
|
+
{
|
|
1083
|
+
binding: 1,
|
|
1084
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
1085
|
+
texture: {
|
|
1086
|
+
sampleType: 'depth' as const,
|
|
1087
|
+
viewDimension: '2d' as const,
|
|
1088
|
+
multisampled: false,
|
|
1089
|
+
},
|
|
1090
|
+
},
|
|
1091
|
+
]
|
|
1092
|
+
: [
|
|
1093
|
+
{
|
|
1094
|
+
binding: 0,
|
|
1095
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
1096
|
+
texture: {
|
|
1097
|
+
sampleType: 'depth' as const,
|
|
1098
|
+
viewDimension: '2d' as const,
|
|
1099
|
+
multisampled: false,
|
|
1100
|
+
},
|
|
1101
|
+
},
|
|
1102
|
+
],
|
|
1103
|
+
});
|
|
1104
|
+
if (!sceneInputLayoutResult.ok) {
|
|
1105
|
+
internals.errorRegistry.fire(sceneInputLayoutResult.error);
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
sceneInputLayout = sceneInputLayoutResult.value;
|
|
1109
|
+
}
|
|
1110
|
+
const usesMaterialGroup = clustered || /@group\s*\(\s*1\s*\)/u.test(source);
|
|
1111
|
+
const lightingLayout = clustered
|
|
1112
|
+
? currentBuildDevice().createBindGroupLayout({
|
|
1113
|
+
label: `prepared-material-lighting-${materialShaderId}`,
|
|
1114
|
+
entries: createHdrpBindGroupLayoutDescriptor().entries.filter(
|
|
1115
|
+
(entry) => entry.binding >= 3,
|
|
1116
|
+
),
|
|
1117
|
+
})
|
|
1118
|
+
: undefined;
|
|
1119
|
+
if (lightingLayout !== undefined && !lightingLayout.ok) {
|
|
1120
|
+
internals.errorRegistry.fire(lightingLayout.error);
|
|
1121
|
+
return null;
|
|
1122
|
+
}
|
|
1429
1123
|
const result = currentBuildDevice().createPipelineLayout({
|
|
1430
1124
|
label: `prepared-material-pl-${materialShaderId}`,
|
|
1431
|
-
bindGroupLayouts: [
|
|
1125
|
+
bindGroupLayouts: [
|
|
1126
|
+
sceneInputLayout,
|
|
1127
|
+
...(usesMaterialGroup ? [materialBgl] : []),
|
|
1128
|
+
...(lightingLayout?.ok ? [lightingLayout.value] : []),
|
|
1129
|
+
],
|
|
1432
1130
|
});
|
|
1433
1131
|
if (!result.ok) {
|
|
1434
1132
|
internals.errorRegistry.fire(result.error);
|
|
1435
1133
|
return null;
|
|
1436
1134
|
}
|
|
1437
|
-
cacheState.preparedMaterialPipelineLayoutCache.set(
|
|
1135
|
+
cacheState.preparedMaterialPipelineLayoutCache.set(cacheKey, result.value);
|
|
1438
1136
|
return result.value;
|
|
1439
1137
|
};
|
|
1440
1138
|
const getOrBuildPerShaderMaterialLayout = (
|
|
@@ -1484,6 +1182,7 @@ async function makeWebGPURenderer(
|
|
|
1484
1182
|
const desc = buildBindGroupLayoutDescriptor(spec, {
|
|
1485
1183
|
kind: 'pbr-material-merged',
|
|
1486
1184
|
materialParamSchema: paramSchema,
|
|
1185
|
+
caps: { storageBuffer: currentBuildDevice().caps.storageBuffer },
|
|
1487
1186
|
});
|
|
1488
1187
|
const bglRes = currentBuildDevice().createBindGroupLayout(desc);
|
|
1489
1188
|
if (!bglRes.ok) {
|
|
@@ -1622,13 +1321,9 @@ async function makeWebGPURenderer(
|
|
|
1622
1321
|
internals.pack.createShaderModule,
|
|
1623
1322
|
internals.errorRegistry,
|
|
1624
1323
|
requiredMaterialShaders,
|
|
1324
|
+
materialShaderUvSetCounts.get('forgeax::default-standard-pbr') ?? 0,
|
|
1625
1325
|
requiredFullscreenPostProcesses,
|
|
1626
|
-
//
|
|
1627
|
-
// T-M2-05 + M3 / T-M3-04: callback set by buildReadyWebGPU once
|
|
1628
|
-
// meshSsboController is wired. Both the grow hook AND the read-only
|
|
1629
|
-
// state ref land on `internals` so the record stage (M3
|
|
1630
|
-
// ensureMeshSsboCapacity) can read slotCount + call grow through
|
|
1631
|
-
// RenderSystemInternals.
|
|
1326
|
+
// Publish growth and capacity together for the active or candidate device.
|
|
1632
1327
|
(hook, state) => {
|
|
1633
1328
|
if (candidateBuildState !== undefined) {
|
|
1634
1329
|
candidateBuildState.growMeshSsbo = hook;
|
|
@@ -1653,13 +1348,7 @@ async function makeWebGPURenderer(
|
|
|
1653
1348
|
}
|
|
1654
1349
|
materialShaderPipelineGroup2Contracts.set(pso as object, group2Contract);
|
|
1655
1350
|
},
|
|
1656
|
-
//
|
|
1657
|
-
// post-process channel once buildReadyWebGPU resolves the tonemap manifest
|
|
1658
|
-
// entry's composed WGSL. Fires after the manifest-load await, by which point
|
|
1659
|
-
// `renderSystem` (declared synchronously below) is defined. The 16 B params
|
|
1660
|
-
// schema mirrors the prior dedicated UBO: [exposure(f32), whitePoint(f32),
|
|
1661
|
-
// mode(u32), pad(f32)]; the extract stage bridges Camera.exposure/whitePoint/
|
|
1662
|
-
// tonemap into this channel each frame (render-system-extract.ts w13).
|
|
1351
|
+
// Register the output transform after manifest loading, once RenderSystem exists.
|
|
1663
1352
|
(source: string) => {
|
|
1664
1353
|
if (candidateBuildState !== undefined || tonemapRegistered) return;
|
|
1665
1354
|
renderSystem.registerBuiltinPostProcess(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, {
|
|
@@ -1699,7 +1388,18 @@ async function makeWebGPURenderer(
|
|
|
1699
1388
|
(sources: VolumetricFogShaderSources) => {
|
|
1700
1389
|
internals.volumetricFogShaders = sources;
|
|
1701
1390
|
},
|
|
1702
|
-
|
|
1391
|
+
(sources: SsrShaderSources) => {
|
|
1392
|
+
internals.ssrShaders = sources;
|
|
1393
|
+
},
|
|
1394
|
+
(sources: AtmosphereShaderSources) => {
|
|
1395
|
+
internals.atmosphereShaders = sources;
|
|
1396
|
+
},
|
|
1397
|
+
).then(async (state) => {
|
|
1398
|
+
// draw() is synchronous. Complete the opt-in capability probe before
|
|
1399
|
+
// publishing readiness, including when rebuilding a lost device.
|
|
1400
|
+
await renderSystem.initializeSsr(device);
|
|
1401
|
+
return state;
|
|
1402
|
+
});
|
|
1703
1403
|
const ready: Promise<Result<void, RhiError>> = buildPipeline().then(
|
|
1704
1404
|
(state): Result<void, RhiError> => {
|
|
1705
1405
|
pipelineState = state;
|
|
@@ -1710,14 +1410,8 @@ async function makeWebGPURenderer(
|
|
|
1710
1410
|
(e: unknown): Result<void, RhiError> => {
|
|
1711
1411
|
readySettled = true;
|
|
1712
1412
|
if (e instanceof RhiError) return err(e);
|
|
1713
|
-
|
|
1714
|
-
return err(
|
|
1715
|
-
new RhiError({
|
|
1716
|
-
code: 'webgpu-runtime-error',
|
|
1717
|
-
expected: 'Renderer.initialization three-step strict-serial succeeds',
|
|
1718
|
-
hint: `pipeline build raised: ${message}`,
|
|
1719
|
-
}),
|
|
1720
|
-
);
|
|
1413
|
+
if (e instanceof PipelineSpecError) return err(rendererInitializationPipelineError(e));
|
|
1414
|
+
return err(rendererInitializationError(e));
|
|
1721
1415
|
},
|
|
1722
1416
|
);
|
|
1723
1417
|
/**
|
|
@@ -1760,33 +1454,17 @@ async function makeWebGPURenderer(
|
|
|
1760
1454
|
}
|
|
1761
1455
|
return standardEntry;
|
|
1762
1456
|
};
|
|
1763
|
-
// Shared builder context for the per-MaterialShader pipeline cache.
|
|
1764
|
-
// Built once; reused for both registered shader IDs and fallback
|
|
1765
|
-
// paths (D-3). Null when manifest is empty (Camera-only path).
|
|
1766
|
-
//
|
|
1767
|
-
// feat-20260609-hdrp-cluster-fragment-ggx M4.5 / w37 (D-10 option A):
|
|
1768
|
-
// `variantSet` parameter selects between URP `pbrPipelineLayout` (1-slot
|
|
1769
|
-
// group(2) BGL) and HDRP `hdrpPbrPipelineLayout` (7-slot group(2) BGL)
|
|
1770
|
-
// via `selectPipelineLayoutForVariant`. When undefined (legacy callers
|
|
1771
|
-
// / fallback path) the URP layout is used preserving prior behaviour.
|
|
1772
1457
|
const buildPipelineContext = (
|
|
1773
1458
|
variantSet?: string,
|
|
1774
1459
|
materialShaderId?: string,
|
|
1775
1460
|
group2Contract: PipelineGroup2Contract = 'mesh',
|
|
1776
|
-
//
|
|
1777
|
-
// when layoutKind === 'pbr-skin' the SSOT for the vertex buffer layout
|
|
1778
|
-
// is `deriveVertexBufferLayout` (vertex-attribute-layout.ts) — the same
|
|
1779
|
-
// function that the WGSL @location(N) declarations / naga reflect tests
|
|
1780
|
-
// consume. A caller passing a real `MeshAsset.attributes` produces the
|
|
1781
|
-
// exact 6-attribute / 72-byte layout the skin shader expects; passing
|
|
1782
|
-
// undefined falls into the synthetic 6-key sentinel below (the skin
|
|
1783
|
-
// path is fully determined by layoutKind, so key-presence is the only
|
|
1784
|
-
// signal `deriveVertexBufferLayout` reads — values never matter).
|
|
1461
|
+
// Skin layouts derive their vertex ABI from the shared attribute projection.
|
|
1785
1462
|
meshAttributes?: VertexAttributeMap,
|
|
1786
1463
|
vertexLayout?: string,
|
|
1787
1464
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
1788
1465
|
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
1789
1466
|
layoutKindOverride?: LayoutKind,
|
|
1467
|
+
particleInputLanes?: number,
|
|
1790
1468
|
) => {
|
|
1791
1469
|
const contractLayoutKind: LayoutKind =
|
|
1792
1470
|
group2Contract === 'cluster'
|
|
@@ -1799,18 +1477,18 @@ async function makeWebGPURenderer(
|
|
|
1799
1477
|
const layoutKind = layoutKindOverride ?? contractLayoutKind;
|
|
1800
1478
|
const usesSharedBootMaterialLayout =
|
|
1801
1479
|
materialShaderId !== undefined && isEngineOwnedMaterialShader(materialShaderId);
|
|
1802
|
-
// Built-in
|
|
1803
|
-
//
|
|
1804
|
-
// group(2) selector and bind the URP mesh BGL to the cluster pipeline.
|
|
1480
|
+
// Built-in layouts are assembled at boot; authored layouts must not bypass
|
|
1481
|
+
// the HDRP group(2) selector or bind the URP mesh BGL to a cluster PSO.
|
|
1805
1482
|
const perShaderLayout =
|
|
1806
|
-
!usesSharedBootMaterialLayout &&
|
|
1483
|
+
!usesSharedBootMaterialLayout &&
|
|
1484
|
+
layoutKind !== 'pbr-skin' &&
|
|
1485
|
+
layoutKind !== 'gpu-driven-pbr' &&
|
|
1486
|
+
layoutKind !== 'gpu-driven-skin' &&
|
|
1487
|
+
materialShaderId !== undefined
|
|
1807
1488
|
? getOrBuildPerShaderMaterialLayout(materialShaderId, undefined, layoutKind)
|
|
1808
1489
|
: null;
|
|
1809
|
-
//
|
|
1810
|
-
//
|
|
1811
|
-
// because its compact texture order may differ from standard PBR even
|
|
1812
|
-
// when it declares only a few textures. Skin / HDRP keep their dedicated
|
|
1813
|
-
// layouts; built-in material IDs remain on the shared boot-time layout.
|
|
1490
|
+
// Authored shaders use their paramSchema; skin/HDRP retain dedicated
|
|
1491
|
+
// layouts and built-in IDs stay on the shared boot-time layout.
|
|
1814
1492
|
const bindingContract =
|
|
1815
1493
|
materialShaderId === undefined
|
|
1816
1494
|
? 'render-material'
|
|
@@ -1827,11 +1505,12 @@ async function makeWebGPURenderer(
|
|
|
1827
1505
|
? getOrBuildViewAndSceneDepthMaterialPipelineLayout()
|
|
1828
1506
|
: null;
|
|
1829
1507
|
const preparedMaterialLayout =
|
|
1830
|
-
bindingContract === 'render-material'
|
|
1508
|
+
(bindingContract === 'render-material' ||
|
|
1509
|
+
bindingContract === 'render-material-with-scene-depth' ||
|
|
1510
|
+
bindingContract === 'render-material-and-scene-depth') &&
|
|
1831
1511
|
materialShaderId !== undefined &&
|
|
1832
|
-
(vertexLayout
|
|
1833
|
-
|
|
1834
|
-
? getOrBuildPreparedMaterialPipelineLayout(materialShaderId)
|
|
1512
|
+
isPreparedMaterialVertexLayout(vertexLayout)
|
|
1513
|
+
? getOrBuildPreparedMaterialPipelineLayout(materialShaderId, group2Contract === 'cluster')
|
|
1835
1514
|
: null;
|
|
1836
1515
|
const materialShaderLookup =
|
|
1837
1516
|
materialShaderId === undefined
|
|
@@ -1873,64 +1552,15 @@ async function makeWebGPURenderer(
|
|
|
1873
1552
|
)
|
|
1874
1553
|
: undefined))
|
|
1875
1554
|
: undefined;
|
|
1876
|
-
const vertexBuffers
|
|
1877
|
-
vertexInputContract
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
: vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.meshGeometryMaterialInstance
|
|
1886
|
-
? MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS
|
|
1887
|
-
: vertexLayoutProjection !== undefined
|
|
1888
|
-
? toGpuVertexBufferLayouts(
|
|
1889
|
-
deriveVertexBufferLayoutFromProjection(
|
|
1890
|
-
vertexLayoutProjection,
|
|
1891
|
-
resolvedUvSetCount !== undefined
|
|
1892
|
-
? { shaderUvSetCount: resolvedUvSetCount }
|
|
1893
|
-
: undefined,
|
|
1894
|
-
),
|
|
1895
|
-
)
|
|
1896
|
-
: layoutKind === 'pbr-skin'
|
|
1897
|
-
? toGpuVertexBufferLayouts(
|
|
1898
|
-
deriveVertexBufferLayout(
|
|
1899
|
-
meshAttributes ??
|
|
1900
|
-
({
|
|
1901
|
-
position: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1902
|
-
normal: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1903
|
-
uv: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1904
|
-
tangent: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1905
|
-
skinIndex: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1906
|
-
skinWeight: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1907
|
-
} satisfies VertexAttributeMap),
|
|
1908
|
-
resolvedUvSetCount !== undefined
|
|
1909
|
-
? { shaderUvSetCount: resolvedUvSetCount }
|
|
1910
|
-
: undefined,
|
|
1911
|
-
),
|
|
1912
|
-
)
|
|
1913
|
-
: meshAttributes !== undefined ||
|
|
1914
|
-
(resolvedUvSetCount !== undefined && resolvedUvSetCount > 1)
|
|
1915
|
-
? toGpuVertexBufferLayouts(
|
|
1916
|
-
deriveVertexBufferLayout(
|
|
1917
|
-
meshAttributes ?? DEFAULT_VERTEX_ATTRS,
|
|
1918
|
-
resolvedUvSetCount !== undefined && resolvedUvSetCount > 1
|
|
1919
|
-
? { shaderUvSetCount: resolvedUvSetCount }
|
|
1920
|
-
: undefined,
|
|
1921
|
-
),
|
|
1922
|
-
)
|
|
1923
|
-
: ([
|
|
1924
|
-
{
|
|
1925
|
-
arrayStride: 12 * 4,
|
|
1926
|
-
attributes: [
|
|
1927
|
-
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
1928
|
-
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x3' as const },
|
|
1929
|
-
{ shaderLocation: 2, offset: 6 * 4, format: 'float32x2' as const },
|
|
1930
|
-
{ shaderLocation: 3, offset: 8 * 4, format: 'float32x4' as const },
|
|
1931
|
-
],
|
|
1932
|
-
},
|
|
1933
|
-
] satisfies readonly GPUVertexBufferLayout[]);
|
|
1555
|
+
const vertexBuffers = resolveWebGPUVertexBufferLayouts({
|
|
1556
|
+
vertexInputContract,
|
|
1557
|
+
vertexLayout,
|
|
1558
|
+
vertexLayoutProjection,
|
|
1559
|
+
resolvedUvSetCount,
|
|
1560
|
+
layoutKind,
|
|
1561
|
+
meshAttributes,
|
|
1562
|
+
particleInputLanes,
|
|
1563
|
+
});
|
|
1934
1564
|
return {
|
|
1935
1565
|
device: currentBuildDevice(),
|
|
1936
1566
|
shaderModuleFactory:
|
|
@@ -1939,7 +1569,7 @@ async function makeWebGPURenderer(
|
|
|
1939
1569
|
: getShaderModuleAdapter(),
|
|
1940
1570
|
pipelineLayout,
|
|
1941
1571
|
vertexBuffers,
|
|
1942
|
-
...(layoutKind === 'pbr-skin' ? { layoutKind } : {}),
|
|
1572
|
+
...(layoutKind === 'pbr-skin' || layoutKind === 'gpu-driven-skin' ? { layoutKind } : {}),
|
|
1943
1573
|
};
|
|
1944
1574
|
};
|
|
1945
1575
|
/**
|
|
@@ -1999,11 +1629,20 @@ async function makeWebGPURenderer(
|
|
|
1999
1629
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2000
1630
|
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
2001
1631
|
layoutKindOverride?: LayoutKind,
|
|
1632
|
+
vertexEntryPoint?: string,
|
|
2002
1633
|
additionalColorFormats?: readonly GPUTextureFormat[],
|
|
2003
1634
|
vertexEntry?: string,
|
|
2004
1635
|
authoredFragmentEntry?: string,
|
|
1636
|
+
particleInputLanes?: number,
|
|
2005
1637
|
): RenderPipeline | null => {
|
|
2006
|
-
const
|
|
1638
|
+
const declaredGroup2Contract = resolvePipelineGroup2Contract(entry.source);
|
|
1639
|
+
const group2Contract =
|
|
1640
|
+
declaredGroup2Contract === 'mesh' &&
|
|
1641
|
+
(materialShaderId === 'forgeax::vfx-render.particles.mesh' ||
|
|
1642
|
+
materialShaderId === 'forgeax::vfx-render.particles.mesh-inputs') &&
|
|
1643
|
+
(variantSet === '' || variantSet?.includes('CLUSTER_FORWARD_AVAILABLE=true') === true)
|
|
1644
|
+
? 'cluster'
|
|
1645
|
+
: declaredGroup2Contract;
|
|
2007
1646
|
const ctx = buildPipelineContext(
|
|
2008
1647
|
variantSet,
|
|
2009
1648
|
materialShaderId,
|
|
@@ -2013,13 +1652,9 @@ async function makeWebGPURenderer(
|
|
|
2013
1652
|
vertexLayoutProjection,
|
|
2014
1653
|
shaderModuleMode,
|
|
2015
1654
|
layoutKindOverride,
|
|
1655
|
+
particleInputLanes,
|
|
2016
1656
|
);
|
|
2017
1657
|
if (ctx === null) return null;
|
|
2018
|
-
// bug-20260612: route the LDR color format through pipelineState so the
|
|
2019
|
-
// backend-aware swap-chain format chosen at buildReadyWebGPU flows here.
|
|
2020
|
-
// Callers gate on pipelineState !== null before reaching this lambda
|
|
2021
|
-
// (search "buildAndCachePipeline" call sites — every one is preceded by
|
|
2022
|
-
// the explicit null check).
|
|
2023
1658
|
const currentState = currentPipelineState();
|
|
2024
1659
|
if (currentState === null) return null;
|
|
2025
1660
|
const ldrColorFormat = colorFormatOverride ?? currentState.colorAttachmentFormat;
|
|
@@ -2042,12 +1677,13 @@ async function makeWebGPURenderer(
|
|
|
2042
1677
|
? undefined
|
|
2043
1678
|
: [isHdr ? HDR_COLOR_ATTACHMENT_FORMAT : ldrColorFormat, ...additionalColorFormats],
|
|
2044
1679
|
depthFormat:
|
|
2045
|
-
depthFormatOverride === null
|
|
1680
|
+
depthFormatOverride === null
|
|
1681
|
+
? undefined
|
|
1682
|
+
: passKindPolicyTable[passKind]?.shape === 'depth-only'
|
|
1683
|
+
? (depthFormatOverride ?? 'depth32float')
|
|
1684
|
+
: (depthFormatOverride ?? DEPTH_TEXTURE_FORMAT),
|
|
2046
1685
|
label,
|
|
2047
1686
|
// feat-20260604 w16-b: the shader-MODULE cache identity. Stable across
|
|
2048
|
-
// topology / renderState / isHdr / indexFormat (all baked into the PSO,
|
|
2049
|
-
// not the module), so every pipeline variant of the same shader source
|
|
2050
|
-
// reuses one compiled module instead of forcing a fresh async compile
|
|
2051
1687
|
// per variant. See PipelineBuilderContext.moduleLabel.
|
|
2052
1688
|
moduleLabel,
|
|
2053
1689
|
},
|
|
@@ -2066,7 +1702,7 @@ async function makeWebGPURenderer(
|
|
|
2066
1702
|
: {}),
|
|
2067
1703
|
}
|
|
2068
1704
|
: undefined,
|
|
2069
|
-
vertexEntry,
|
|
1705
|
+
vertexEntry ?? vertexEntryPoint,
|
|
2070
1706
|
fragmentEntry,
|
|
2071
1707
|
undefined, // defines — none
|
|
2072
1708
|
passKind,
|
|
@@ -2079,10 +1715,7 @@ async function makeWebGPURenderer(
|
|
|
2079
1715
|
return null;
|
|
2080
1716
|
}
|
|
2081
1717
|
currentPipelineCacheState().materialShaderPipelineCache.set(cacheKey, built.value);
|
|
2082
|
-
materialShaderPipelineGroup2Contracts.set(
|
|
2083
|
-
built.value as object,
|
|
2084
|
-
resolvePipelineGroup2Contract(entry.source),
|
|
2085
|
-
);
|
|
1718
|
+
materialShaderPipelineGroup2Contracts.set(built.value as object, group2Contract);
|
|
2086
1719
|
return built.value;
|
|
2087
1720
|
};
|
|
2088
1721
|
const getMaterialShaderPipeline = (
|
|
@@ -2093,11 +1726,6 @@ async function makeWebGPURenderer(
|
|
|
2093
1726
|
indexFormat?: 'uint16' | 'uint32',
|
|
2094
1727
|
variantSet?: string,
|
|
2095
1728
|
passKind: PassKind = 'forward',
|
|
2096
|
-
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w16 (D-4):
|
|
2097
|
-
// forwarded to `buildAndCachePipeline -> buildPipelineContext` so the
|
|
2098
|
-
// pbr-skin layout chain reads from the `deriveVertexBufferLayout` SSOT.
|
|
2099
|
-
// Optional: existing callers (URP / HDRP / shadow-depth) pass undefined
|
|
2100
|
-
// and the hardcoded 4-attribute layout is preserved (AC-04 zero-regression).
|
|
2101
1729
|
meshAttributes?: VertexAttributeMap,
|
|
2102
1730
|
// bug-20260615 M2 / m2-1: sampleCount is threaded through to the cache key,
|
|
2103
1731
|
// buildAndCachePipeline, and ultimately buildPipelineForMaterialShader which
|
|
@@ -2132,9 +1760,11 @@ async function makeWebGPURenderer(
|
|
|
2132
1760
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2133
1761
|
shaderModuleMode: RenderFeatureShaderModuleMode = 'validated',
|
|
2134
1762
|
layoutKindOverride?: LayoutKind,
|
|
1763
|
+
vertexEntryPoint?: string,
|
|
2135
1764
|
additionalColorFormats?: readonly GPUTextureFormat[],
|
|
2136
1765
|
vertexEntry?: string,
|
|
2137
1766
|
fragmentEntry?: string,
|
|
1767
|
+
particleInputLanes?: number,
|
|
2138
1768
|
): RenderPipeline | null => {
|
|
2139
1769
|
// WebGL2 is a downlevel backend even when the record stage asks for the
|
|
2140
1770
|
// native-capability variant string. Resolve the variant axes at this
|
|
@@ -2175,20 +1805,14 @@ async function makeWebGPURenderer(
|
|
|
2175
1805
|
(pipelineShaderId === 'forgeax::sprite' || pipelineShaderId === 'forgeax::sprite-lit')
|
|
2176
1806
|
? undefined
|
|
2177
1807
|
: resolveCachedMaterialShaderVariantSet(variantSet, manifestEntry);
|
|
2178
|
-
// The record stage
|
|
2179
|
-
//
|
|
2180
|
-
//
|
|
2181
|
-
// canonical HDRP variant key and would pair an HDRP PSO with the URP mesh
|
|
2182
|
-
// bind group. Builtin forgeax:: shaders keep the compatibility request
|
|
1808
|
+
// The record stage composes capability axes; plain custom shaders retain
|
|
1809
|
+
// only manifest-declared axes so '' cannot select an HDRP mesh layout.
|
|
1810
|
+
// Builtin forgeax:: shaders keep the compatibility request
|
|
2183
1811
|
// when a reduced test/legacy manifest omits variant metadata.
|
|
2184
1812
|
// A single-source manifest entry must stay on its canonical module key;
|
|
2185
1813
|
// otherwise a synthesized capability axis races the prewarmed module.
|
|
2186
1814
|
let effectiveVariantSet = normalizeMaterialShaderVariantSet(resolvedVariantSet, manifestEntry);
|
|
2187
|
-
//
|
|
2188
|
-
// cooked artifact, not from the build-time material-shader manifest. The
|
|
2189
|
-
// published artifact identity already selects the backend-specific bytes;
|
|
2190
|
-
// an absent manifest therefore means the source itself is the canonical
|
|
2191
|
-
// pipeline module and must not carry a synthesized capability key.
|
|
1815
|
+
// A published artifact is already backend-specific; no manifest means no synthesized key.
|
|
2192
1816
|
if (manifestEntry === undefined) effectiveVariantSet = undefined;
|
|
2193
1817
|
if (
|
|
2194
1818
|
vertexLayoutProjection === undefined &&
|
|
@@ -2227,25 +1851,16 @@ async function makeWebGPURenderer(
|
|
|
2227
1851
|
effectiveVariantSet =
|
|
2228
1852
|
filteredVariantParts.length === 0 ? undefined : filteredVariantParts.join('+');
|
|
2229
1853
|
}
|
|
2230
|
-
// feat-20260615-pipeline-spec-ssot M2-T2: cache key derived from PipelineSpec
|
|
2231
|
-
// 4-axis SSOT via cacheKeyOf(spec). The spec carries all 4 axes (shader /
|
|
2232
|
-
// attachments / geometry / renderState), replacing the legacy 8-segment string
|
|
2233
|
-
// construction. vertexLayout is now included in the cache key (was previously
|
|
2234
|
-
// threaded but not hashed — research F1 R-VertexLayout-Cache). sampleCount is
|
|
2235
|
-
// cast to 1 | 4 for the closed spec axis.
|
|
2236
|
-
// M2-fixup follow-up: LDR color format SSOT is `pipelineState.colorAttachmentFormat`
|
|
2237
|
-
// (set in buildReadyWebGPU from selectSwapChainFormat). Hardcoding 'bgra8unorm-srgb'
|
|
2238
|
-
// here let the cache key drift from the prewarm key on backends where
|
|
2239
|
-
// getPreferredCanvasFormat returns rgba8unorm (dawn-node, lavapipe, wgpu-wasm GLES),
|
|
2240
|
-
// forcing a redundant second PSO build on first-frame URP record path.
|
|
2241
1854
|
const colorFormat: GPUTextureFormat = isHdr ? 'rgba16float' : ldrColorFormat;
|
|
2242
1855
|
const isDepthOnlyPass = passKindPolicyTable[passKind]?.shape === 'depth-only';
|
|
1856
|
+
const resolvedVertexEntry = vertexEntry ?? vertexEntryPoint;
|
|
1857
|
+
const inputVertexBuffers = particleMaterialInputVertexBuffers(vertexLayout, particleInputLanes);
|
|
2243
1858
|
const spec: PipelineSpec = {
|
|
2244
1859
|
shader: {
|
|
2245
1860
|
id: pipelineShaderId,
|
|
2246
1861
|
passKind,
|
|
2247
1862
|
variantSet: effectiveVariantSet,
|
|
2248
|
-
...(
|
|
1863
|
+
...(resolvedVertexEntry === undefined ? {} : { vertexEntry: resolvedVertexEntry }),
|
|
2249
1864
|
...(fragmentEntry === undefined ? {} : { fragmentEntry }),
|
|
2250
1865
|
},
|
|
2251
1866
|
attachments: {
|
|
@@ -2254,7 +1869,7 @@ async function makeWebGPURenderer(
|
|
|
2254
1869
|
? colorFormatsForPassKind(passKind, colorFormat)
|
|
2255
1870
|
: [colorFormat, ...additionalColorFormats],
|
|
2256
1871
|
depthFormat: isDepthOnlyPass
|
|
2257
|
-
? 'depth32float'
|
|
1872
|
+
? (depthFormatOverride ?? 'depth32float')
|
|
2258
1873
|
: depthFormatOverride === null
|
|
2259
1874
|
? undefined
|
|
2260
1875
|
: (depthFormatOverride ?? 'depth24plus-stencil8'),
|
|
@@ -2267,11 +1882,9 @@ async function makeWebGPURenderer(
|
|
|
2267
1882
|
meshAttributes ??
|
|
2268
1883
|
(vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.positionSizeColorInstance
|
|
2269
1884
|
? PREPARED_INSTANCE_VERTEX_ATTRS
|
|
2270
|
-
: vertexLayout
|
|
2271
|
-
vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.topologySegmentInstance ||
|
|
2272
|
-
vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.meshGeometryMaterialInstance
|
|
1885
|
+
: isPreparedMaterialVertexLayout(vertexLayout)
|
|
2273
1886
|
? PREPARED_MATERIAL_INSTANCE_VERTEX_ATTRS
|
|
2274
|
-
:
|
|
1887
|
+
: DEFAULT_VERTEX_ATTRIBUTE_MAP),
|
|
2275
1888
|
// Thread the shader-declared count whenever it is greater than one.
|
|
2276
1889
|
// Built-in PBR reserves all eight supported UV inputs; single-UV meshes
|
|
2277
1890
|
// remain byte-stable because missing sets are clamp-to-last aliases.
|
|
@@ -2279,10 +1892,11 @@ async function makeWebGPURenderer(
|
|
|
2279
1892
|
? { shaderUvSetCount: resolvedUvSetCount }
|
|
2280
1893
|
: {}),
|
|
2281
1894
|
...(vertexLayoutProjection === undefined ? {} : { vertexLayoutProjection }),
|
|
1895
|
+
...(inputVertexBuffers === undefined ? {} : { vertexBuffers: inputVertexBuffers }),
|
|
2282
1896
|
},
|
|
2283
1897
|
renderState,
|
|
2284
1898
|
};
|
|
2285
|
-
const cacheKey = cacheKeyOf(spec)
|
|
1899
|
+
const cacheKey = `${cacheKeyOf(spec)}|vertex:${resolvedVertexEntry ?? 'vs_main'}`;
|
|
2286
1900
|
const cached = currentPipelineCacheState().materialShaderPipelineCache.get(cacheKey);
|
|
2287
1901
|
if (cached !== undefined) return cached;
|
|
2288
1902
|
if (pipelineState === null) return null;
|
|
@@ -2336,6 +1950,7 @@ async function makeWebGPURenderer(
|
|
|
2336
1950
|
vertexLayoutProjection,
|
|
2337
1951
|
shaderModuleMode,
|
|
2338
1952
|
layoutKindOverride,
|
|
1953
|
+
vertexEntryPoint,
|
|
2339
1954
|
additionalColorFormats,
|
|
2340
1955
|
);
|
|
2341
1956
|
}
|
|
@@ -2392,6 +2007,7 @@ async function makeWebGPURenderer(
|
|
|
2392
2007
|
vertexLayoutProjection,
|
|
2393
2008
|
shaderModuleMode,
|
|
2394
2009
|
layoutKindOverride,
|
|
2010
|
+
vertexEntryPoint,
|
|
2395
2011
|
additionalColorFormats,
|
|
2396
2012
|
);
|
|
2397
2013
|
}
|
|
@@ -2485,9 +2101,11 @@ async function makeWebGPURenderer(
|
|
|
2485
2101
|
vertexLayoutProjection,
|
|
2486
2102
|
shaderModuleMode,
|
|
2487
2103
|
layoutKindOverride,
|
|
2104
|
+
vertexEntryPoint,
|
|
2488
2105
|
additionalColorFormats,
|
|
2489
2106
|
vertexEntry,
|
|
2490
2107
|
fragmentEntry,
|
|
2108
|
+
particleInputLanes,
|
|
2491
2109
|
);
|
|
2492
2110
|
};
|
|
2493
2111
|
const getMaterialShaderPipelineEntry = (
|
|
@@ -2549,7 +2167,7 @@ async function makeWebGPURenderer(
|
|
|
2549
2167
|
const buildPostProcessPipeline = (
|
|
2550
2168
|
entry: PostProcessShaderEntry,
|
|
2551
2169
|
bgl: BindGroupLayout,
|
|
2552
|
-
|
|
2170
|
+
colorFormats: readonly GPUTextureFormat[],
|
|
2553
2171
|
label: string,
|
|
2554
2172
|
): RenderPipeline | null => {
|
|
2555
2173
|
const moduleFactory = getShaderModuleAdapter();
|
|
@@ -2597,12 +2215,12 @@ async function makeWebGPURenderer(
|
|
|
2597
2215
|
},
|
|
2598
2216
|
fragment: {
|
|
2599
2217
|
module: moduleResult.value,
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2218
|
+
// The typed graph reuses the composed tonemap module for separate
|
|
2219
|
+
// linear-LDR and final-encoding passes. Preserve the selected entry
|
|
2220
|
+
// point; forcing fs_main encodes the linear-LDR intermediate a second
|
|
2221
|
+
// time and breaks the Three.js stage contract.
|
|
2222
|
+
entryPoint: entry.fragmentEntryPoint ?? 'fs_main',
|
|
2223
|
+
targets: colorFormats.map((format) => ({ format })),
|
|
2606
2224
|
},
|
|
2607
2225
|
primitive: { topology: 'triangle-list', cullMode: 'none', frontFace: 'ccw' },
|
|
2608
2226
|
depthStencil: undefined,
|
|
@@ -2633,10 +2251,7 @@ async function makeWebGPURenderer(
|
|
|
2633
2251
|
setFeatureHost: (host) => {
|
|
2634
2252
|
internals.featureHost = host;
|
|
2635
2253
|
},
|
|
2636
|
-
//
|
|
2637
|
-
// call time. Renderer recovery swaps the device and invalidates the
|
|
2638
|
-
// per-device adapter; a boot-time snapshot would feed recovered VFX
|
|
2639
|
-
// programs shader modules owned by the lost device.
|
|
2254
|
+
// Resolve the adapter at call time so recovered devices get fresh modules.
|
|
2640
2255
|
shaderModuleFactory: {
|
|
2641
2256
|
createShaderModule: (descriptor) => getShaderModuleAdapter().createShaderModule(descriptor),
|
|
2642
2257
|
},
|
|
@@ -2666,6 +2281,28 @@ async function makeWebGPURenderer(
|
|
|
2666
2281
|
...(internals.pack.instrumentation?.resolveSurfaceDevice === undefined
|
|
2667
2282
|
? {}
|
|
2668
2283
|
: { resolveSurfaceDevice: internals.pack.instrumentation.resolveSurfaceDevice }),
|
|
2284
|
+
// The graph records receipt-bound color observations through the same
|
|
2285
|
+
// renderer-owned capture bridge consumed after queue submission. Keep the
|
|
2286
|
+
// identity and demand live on internals so recovery and per-frame graph
|
|
2287
|
+
// rebuilds observe the current device without reconstructing RenderSystem.
|
|
2288
|
+
get deviceGeneration() {
|
|
2289
|
+
return activeDeviceScope.generation;
|
|
2290
|
+
},
|
|
2291
|
+
get observationFrameId() {
|
|
2292
|
+
return internals.observationFrameId;
|
|
2293
|
+
},
|
|
2294
|
+
get observationGraphGeneration() {
|
|
2295
|
+
return internals.observationGraphGeneration;
|
|
2296
|
+
},
|
|
2297
|
+
set observationGraphGeneration(generation: number | undefined) {
|
|
2298
|
+
internals.observationGraphGeneration = generation;
|
|
2299
|
+
},
|
|
2300
|
+
get observationCaptureOwner() {
|
|
2301
|
+
return internals.observationCaptureOwner;
|
|
2302
|
+
},
|
|
2303
|
+
get observationCaptureDomains() {
|
|
2304
|
+
return internals.observationCaptureDomains;
|
|
2305
|
+
},
|
|
2669
2306
|
invalidateShaderModule: (label: string) => {
|
|
2670
2307
|
activeShaderState.sharedShaderModuleAdapter?.invalidateModule(label);
|
|
2671
2308
|
},
|
|
@@ -2692,10 +2329,15 @@ async function makeWebGPURenderer(
|
|
|
2692
2329
|
getMaterialShaderPipeline,
|
|
2693
2330
|
getMaterialShaderPipelineEntry,
|
|
2694
2331
|
getMaterialShaderBindingContract: getCachedMaterialShaderBindingContract,
|
|
2332
|
+
getMaterialShaderArtifact: (materialShaderId, vertexColorAvailable = false) =>
|
|
2333
|
+
resolveStandardPbrProgram(
|
|
2334
|
+
pipelineState?.gpuDrivenPbrPrograms,
|
|
2335
|
+
materialShaderId,
|
|
2336
|
+
vertexColorAvailable,
|
|
2337
|
+
)?.artifact,
|
|
2695
2338
|
getParamSchema,
|
|
2696
2339
|
getMaterialBindGroupLayout,
|
|
2697
2340
|
metrics,
|
|
2698
|
-
// feat-20260609 M4 / T-10-a: post-process pipeline factory (CONCERN-1 fix).
|
|
2699
2341
|
// createRenderSystem wraps this in a per-RenderSystem cache + the public
|
|
2700
2342
|
// getPostProcessPipeline lookup the dispatcher reads at frame time.
|
|
2701
2343
|
buildPostProcessPipeline,
|
|
@@ -2786,6 +2428,13 @@ async function makeWebGPURenderer(
|
|
|
2786
2428
|
get volumetricFogShaders() {
|
|
2787
2429
|
return internals.volumetricFogShaders;
|
|
2788
2430
|
},
|
|
2431
|
+
// Readiness-prewarmed SSR sources stay visible through the live internals seam.
|
|
2432
|
+
get ssrShaders() {
|
|
2433
|
+
return internals.ssrShaders;
|
|
2434
|
+
},
|
|
2435
|
+
get atmosphereShaders() {
|
|
2436
|
+
return internals.atmosphereShaders;
|
|
2437
|
+
},
|
|
2789
2438
|
});
|
|
2790
2439
|
attachGpuPassTimingSession(internals, gpuPassTimingSession);
|
|
2791
2440
|
renderSystemForTargetPromotion = renderSystem;
|
|
@@ -2809,20 +2458,13 @@ async function makeWebGPURenderer(
|
|
|
2809
2458
|
const attachedWorlds = new Set<World>();
|
|
2810
2459
|
const attachedLeases = new Map<RenderReadLease, World>();
|
|
2811
2460
|
const leasesByWorld = new Map<World, RenderReadLease>();
|
|
2812
|
-
const dynamicGeometry =
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
isConsumedByRenderFrame: (candidate) =>
|
|
2820
|
-
renderSystem.isDynamicGeometryConsumed(
|
|
2821
|
-
candidate.world as World,
|
|
2822
|
-
candidate.entity ?? -1,
|
|
2823
|
-
candidate.meshHandle,
|
|
2824
|
-
),
|
|
2825
|
-
});
|
|
2461
|
+
const { lifecycle: dynamicGeometry, host: dynamicGeometryHost } =
|
|
2462
|
+
createRendererDynamicGeometryController({
|
|
2463
|
+
attachedWorlds,
|
|
2464
|
+
getGpuStore: () => gpuStore,
|
|
2465
|
+
currentGeneration: () => activeDeviceScope.generation,
|
|
2466
|
+
renderSystem,
|
|
2467
|
+
});
|
|
2826
2468
|
const derivedSystemNames = new Map<World, string>();
|
|
2827
2469
|
const transformReleases = new Map<World, () => void>();
|
|
2828
2470
|
const attachmentOwner = {};
|
|
@@ -2834,8 +2476,184 @@ async function makeWebGPURenderer(
|
|
|
2834
2476
|
const issuedReceipts = new WeakSet<FrameReceipt>();
|
|
2835
2477
|
const receiptTimings = new WeakMap<FrameReceipt, Promise<VolumeTimingObservation>>();
|
|
2836
2478
|
let latestReceipt: FrameReceipt | undefined;
|
|
2479
|
+
let pendingObservationDomains: readonly FrameObservationDomain[] | undefined;
|
|
2480
|
+
let observationAllocationCount = 0;
|
|
2481
|
+
let observationLiveCount = 0;
|
|
2482
|
+
let observationPeakLiveCount = 0;
|
|
2483
|
+
let observationMapCount = 0;
|
|
2484
|
+
let observationReadbackCount = 0;
|
|
2485
|
+
let observationLiveByteLength = 0;
|
|
2486
|
+
const observationCaptureOwner = (() => {
|
|
2487
|
+
const capturesByFrame = new Map<number, TypedFrameObservationCapture[]>();
|
|
2488
|
+
return {
|
|
2489
|
+
register(capture: TypedFrameObservationCapture): void {
|
|
2490
|
+
const captures = capturesByFrame.get(capture.frameNumber) ?? [];
|
|
2491
|
+
captures.push(capture);
|
|
2492
|
+
capturesByFrame.set(capture.frameNumber, captures);
|
|
2493
|
+
observationAllocationCount += 1;
|
|
2494
|
+
observationLiveCount += 1;
|
|
2495
|
+
observationPeakLiveCount = Math.max(observationPeakLiveCount, observationLiveCount);
|
|
2496
|
+
observationLiveByteLength += capture.bytesPerRow * capture.height;
|
|
2497
|
+
},
|
|
2498
|
+
consume(frameNumber: number): readonly TypedFrameObservationCapture[] {
|
|
2499
|
+
const captures = capturesByFrame.get(frameNumber) ?? [];
|
|
2500
|
+
capturesByFrame.delete(frameNumber);
|
|
2501
|
+
return Object.freeze(captures);
|
|
2502
|
+
},
|
|
2503
|
+
drain(): readonly TypedFrameObservationCapture[] {
|
|
2504
|
+
const captures = [...capturesByFrame.values()].flat();
|
|
2505
|
+
capturesByFrame.clear();
|
|
2506
|
+
return Object.freeze(captures);
|
|
2507
|
+
},
|
|
2508
|
+
};
|
|
2509
|
+
})();
|
|
2510
|
+
Object.assign(internals, { observationCaptureOwner });
|
|
2511
|
+
let expectedObservationGraphGeneration: number | undefined;
|
|
2512
|
+
Object.defineProperty(internals, 'observationGraphGeneration', {
|
|
2513
|
+
configurable: true,
|
|
2514
|
+
get: () => expectedObservationGraphGeneration,
|
|
2515
|
+
set: (generation: number | undefined) => {
|
|
2516
|
+
expectedObservationGraphGeneration = generation;
|
|
2517
|
+
},
|
|
2518
|
+
});
|
|
2519
|
+
type ObservationCaptureRead = () => Promise<
|
|
2520
|
+
RenderResult<
|
|
2521
|
+
readonly {
|
|
2522
|
+
readonly capture: TypedFrameObservationCapture;
|
|
2523
|
+
readonly bytes: Uint8Array;
|
|
2524
|
+
}[],
|
|
2525
|
+
RenderError
|
|
2526
|
+
>
|
|
2527
|
+
>;
|
|
2528
|
+
const receiptObservationCaptures = new Map<FrameReceipt, ObservationCaptureRead>();
|
|
2529
|
+
const receiptObservationBuffers = new Map<
|
|
2530
|
+
FrameReceipt,
|
|
2531
|
+
readonly TypedFrameObservationCapture[]
|
|
2532
|
+
>();
|
|
2533
|
+
const destroyedObservationBuffers = new WeakSet<Buffer>();
|
|
2534
|
+
const observationBufferCleanupFailures = new WeakMap<Buffer, RendererContractFailureError>();
|
|
2535
|
+
const failedObservationCaptures = new Map<Buffer, TypedFrameObservationCapture>();
|
|
2536
|
+
const disposeObservationCaptures = (
|
|
2537
|
+
captures: readonly TypedFrameObservationCapture[],
|
|
2538
|
+
): RendererContractFailureError | undefined => {
|
|
2539
|
+
const wasDestroyed = new Set(
|
|
2540
|
+
captures
|
|
2541
|
+
.filter((capture) => destroyedObservationBuffers.has(capture.buffer))
|
|
2542
|
+
.map((capture) => capture.buffer),
|
|
2543
|
+
);
|
|
2544
|
+
const result = disposeObservationCaptureSet(
|
|
2545
|
+
captures,
|
|
2546
|
+
destroyedObservationBuffers,
|
|
2547
|
+
observationBufferCleanupFailures,
|
|
2548
|
+
(failure) => internals.errorRegistry.fire(failure),
|
|
2549
|
+
);
|
|
2550
|
+
for (const capture of captures) {
|
|
2551
|
+
if (!wasDestroyed.has(capture.buffer) && destroyedObservationBuffers.has(capture.buffer)) {
|
|
2552
|
+
observationLiveCount = Math.max(0, observationLiveCount - 1);
|
|
2553
|
+
observationLiveByteLength = Math.max(
|
|
2554
|
+
0,
|
|
2555
|
+
observationLiveByteLength - capture.bytesPerRow * capture.height,
|
|
2556
|
+
);
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
return result;
|
|
2560
|
+
};
|
|
2561
|
+
const disposeOwnedObservationCaptures = (
|
|
2562
|
+
captures: readonly TypedFrameObservationCapture[],
|
|
2563
|
+
): RendererContractFailureError | undefined => {
|
|
2564
|
+
const failure = disposeObservationCaptures(captures);
|
|
2565
|
+
if (failure !== undefined) {
|
|
2566
|
+
for (const capture of captures) {
|
|
2567
|
+
if (!destroyedObservationBuffers.has(capture.buffer)) {
|
|
2568
|
+
failedObservationCaptures.set(capture.buffer, capture);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
return failure;
|
|
2573
|
+
};
|
|
2574
|
+
const readObservationCapture = async (
|
|
2575
|
+
capture: TypedFrameObservationCapture,
|
|
2576
|
+
): Promise<RenderResult<Uint8Array, RenderError>> => {
|
|
2577
|
+
try {
|
|
2578
|
+
if (typeof capture.buffer.mapAsync !== 'function') {
|
|
2579
|
+
const cleanupFailure = disposeObservationCaptures([capture]);
|
|
2580
|
+
return err(
|
|
2581
|
+
cleanupFailure ??
|
|
2582
|
+
new RendererContractFailureError(
|
|
2583
|
+
'observe',
|
|
2584
|
+
`the ${capture.domain} receipt-bound readback backend has no mapAsync capability`,
|
|
2585
|
+
),
|
|
2586
|
+
);
|
|
2587
|
+
}
|
|
2588
|
+
observationMapCount += 1;
|
|
2589
|
+
const mapped = await capture.buffer.mapAsync(1);
|
|
2590
|
+
if (!mapped.ok) {
|
|
2591
|
+
const cleanupFailure = disposeObservationCaptures([capture]);
|
|
2592
|
+
return err(
|
|
2593
|
+
cleanupFailure ??
|
|
2594
|
+
new RendererContractFailureError(
|
|
2595
|
+
'observe',
|
|
2596
|
+
`mapping ${capture.domain} receipt-bound readback failed: ${mapped.error.code}`,
|
|
2597
|
+
),
|
|
2598
|
+
);
|
|
2599
|
+
}
|
|
2600
|
+
const range = mapped.value.getMappedRange();
|
|
2601
|
+
if (!range.ok) {
|
|
2602
|
+
mapped.value.unmap();
|
|
2603
|
+
const cleanupFailure = disposeObservationCaptures([capture]);
|
|
2604
|
+
return err(
|
|
2605
|
+
cleanupFailure ??
|
|
2606
|
+
new RendererContractFailureError(
|
|
2607
|
+
'observe',
|
|
2608
|
+
`reading ${capture.domain} receipt-bound readback failed: ${range.error.code}`,
|
|
2609
|
+
),
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
const bytes = new Uint8Array(range.value.slice(0));
|
|
2613
|
+
mapped.value.unmap();
|
|
2614
|
+
observationReadbackCount += 1;
|
|
2615
|
+
const cleanupFailure = disposeObservationCaptures([capture]);
|
|
2616
|
+
if (cleanupFailure !== undefined) return err(cleanupFailure);
|
|
2617
|
+
return ok(bytes);
|
|
2618
|
+
} catch (cause) {
|
|
2619
|
+
const cleanupFailure = disposeObservationCaptures([capture]);
|
|
2620
|
+
return err(
|
|
2621
|
+
cleanupFailure ??
|
|
2622
|
+
new RendererContractFailureError(
|
|
2623
|
+
'observe',
|
|
2624
|
+
`reading ${capture.domain} receipt-bound readback threw: ${
|
|
2625
|
+
cause instanceof Error ? cause.message : String(cause)
|
|
2626
|
+
}`,
|
|
2627
|
+
),
|
|
2628
|
+
);
|
|
2629
|
+
}
|
|
2630
|
+
};
|
|
2631
|
+
|
|
2632
|
+
const disposeReceiptObservationCaptures = (): RendererContractFailureError | undefined => {
|
|
2633
|
+
let firstFailure: RendererContractFailureError | undefined;
|
|
2634
|
+
for (const [receipt, captures] of receiptObservationBuffers) {
|
|
2635
|
+
const failure = disposeObservationCaptures(captures);
|
|
2636
|
+
firstFailure ??= failure;
|
|
2637
|
+
if (captures.every((capture) => destroyedObservationBuffers.has(capture.buffer))) {
|
|
2638
|
+
receiptObservationBuffers.delete(receipt);
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
receiptObservationCaptures.clear();
|
|
2642
|
+
const retryCaptures = [...failedObservationCaptures.values()];
|
|
2643
|
+
const retryFailure = disposeObservationCaptures(retryCaptures);
|
|
2644
|
+
firstFailure ??= retryFailure;
|
|
2645
|
+
for (const capture of retryCaptures) {
|
|
2646
|
+
if (destroyedObservationBuffers.has(capture.buffer)) {
|
|
2647
|
+
failedObservationCaptures.delete(capture.buffer);
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
const ownedFailure = disposeOwnedObservationCaptures(observationCaptureOwner.drain());
|
|
2651
|
+
firstFailure ??= ownedFailure;
|
|
2652
|
+
return firstFailure;
|
|
2653
|
+
};
|
|
2837
2654
|
let surfaceReleased = false;
|
|
2838
2655
|
const renderer: RendererAssemblyImplementation = {
|
|
2656
|
+
instances: renderSystem.instances,
|
|
2839
2657
|
attach(world: World): RenderResult<RenderWorldLease, RenderError> {
|
|
2840
2658
|
const attached = this.attachScene(world);
|
|
2841
2659
|
if (!attached.ok) {
|
|
@@ -2886,6 +2704,7 @@ async function makeWebGPURenderer(
|
|
|
2886
2704
|
gpuDriven: renderSystem.gpuDrivenInspection,
|
|
2887
2705
|
};
|
|
2888
2706
|
},
|
|
2707
|
+
bounds: (world, entity) => (disposed ? undefined : renderSystem.bounds(world, entity)),
|
|
2889
2708
|
inspect(): RenderInspection {
|
|
2890
2709
|
const health = internals.healthRegistry.getLastSnapshot();
|
|
2891
2710
|
const deviceLost = health.reason === 'device-lost';
|
|
@@ -2926,6 +2745,8 @@ async function makeWebGPURenderer(
|
|
|
2926
2745
|
? {}
|
|
2927
2746
|
: { surfaceProfile: pipelineState.surfaceProfile }),
|
|
2928
2747
|
rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
|
|
2748
|
+
autoExposure: renderSystem.autoExposure,
|
|
2749
|
+
standardLut: renderSystem.standardLut,
|
|
2929
2750
|
...(internals.context?.presentationProof === undefined
|
|
2930
2751
|
? {}
|
|
2931
2752
|
: { presentationProof: internals.context.presentationProof }),
|
|
@@ -2943,16 +2764,18 @@ async function makeWebGPURenderer(
|
|
|
2943
2764
|
frameId,
|
|
2944
2765
|
deviceGeneration: activeDeviceScope.generation,
|
|
2945
2766
|
}),
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
featureDiagnostics: internals.featureHost?.diagnostics() ?? Object.freeze([]),
|
|
2767
|
+
...projectRendererFeatureInspection(internals.featureHost),
|
|
2768
|
+
featureGraph: renderSystem.featureGraphInspection,
|
|
2769
|
+
materialTextureSources: renderSystem.materialTextureSources,
|
|
2950
2770
|
frustumStats: Object.freeze({ ...renderSystem.frustumStats }),
|
|
2951
2771
|
visibilityStats: Object.freeze({ ...renderSystem.visibilityStats }),
|
|
2952
2772
|
instanceCollections: Object.freeze(
|
|
2953
2773
|
renderSystem.instanceCollectionsInspection.map((collection) =>
|
|
2954
2774
|
Object.freeze({
|
|
2955
2775
|
...collection,
|
|
2776
|
+
dirtyRanges: Object.freeze(
|
|
2777
|
+
collection.dirtyRanges.map((range) => Object.freeze({ ...range })),
|
|
2778
|
+
),
|
|
2956
2779
|
uploadRanges: Object.freeze(
|
|
2957
2780
|
collection.uploadRanges.map((range) => Object.freeze({ ...range })),
|
|
2958
2781
|
),
|
|
@@ -2971,6 +2794,7 @@ async function makeWebGPURenderer(
|
|
|
2971
2794
|
renderScene: renderSystem.renderScene,
|
|
2972
2795
|
reflectionProbes: renderSystem.reflectionProbes,
|
|
2973
2796
|
ssrDependencies: renderSystem.ssrDependencies,
|
|
2797
|
+
ssr: renderSystem.ssr,
|
|
2974
2798
|
volumetricFog,
|
|
2975
2799
|
environment: renderSystem.environment,
|
|
2976
2800
|
temporal: renderSystem.temporal,
|
|
@@ -3024,7 +2848,13 @@ async function makeWebGPURenderer(
|
|
|
3024
2848
|
lastGeneration: latestReceipt?.deviceGeneration,
|
|
3025
2849
|
}),
|
|
3026
2850
|
}),
|
|
3027
|
-
|
|
2851
|
+
output: Object.freeze({
|
|
2852
|
+
...outputInspection,
|
|
2853
|
+
graphPassNames: Object.freeze([...outputInspection.graphPassNames]),
|
|
2854
|
+
...(outputInspection.standardOutputColor === undefined
|
|
2855
|
+
? {}
|
|
2856
|
+
: { standardOutputColor: Object.freeze({ ...outputInspection.standardOutputColor }) }),
|
|
2857
|
+
}),
|
|
3028
2858
|
observation: Object.freeze({
|
|
3029
2859
|
observationId,
|
|
3030
2860
|
frameId,
|
|
@@ -3039,6 +2869,14 @@ async function makeWebGPURenderer(
|
|
|
3039
2869
|
...(outputInspection.standardOutputColor === undefined
|
|
3040
2870
|
? {}
|
|
3041
2871
|
: { standardOutputColor: Object.freeze({ ...outputInspection.standardOutputColor }) }),
|
|
2872
|
+
resourceStats: Object.freeze({
|
|
2873
|
+
allocationCount: observationAllocationCount,
|
|
2874
|
+
liveCount: observationLiveCount,
|
|
2875
|
+
peakLiveCount: observationPeakLiveCount,
|
|
2876
|
+
mapCount: observationMapCount,
|
|
2877
|
+
readbackCount: observationReadbackCount,
|
|
2878
|
+
liveByteLength: observationLiveByteLength,
|
|
2879
|
+
}),
|
|
3042
2880
|
}),
|
|
3043
2881
|
});
|
|
3044
2882
|
},
|
|
@@ -3131,6 +2969,38 @@ async function makeWebGPURenderer(
|
|
|
3131
2969
|
observeCurrentFrame(options: FrameObservationOptions) {
|
|
3132
2970
|
return renderSystem.observeCurrentFrame(options);
|
|
3133
2971
|
},
|
|
2972
|
+
requestObservation(
|
|
2973
|
+
domains: readonly FrameObservationDomain[],
|
|
2974
|
+
): RenderResult<void, RenderError> {
|
|
2975
|
+
if (disposed) {
|
|
2976
|
+
return err(
|
|
2977
|
+
new RendererOperationError('renderer-state-invalid', {
|
|
2978
|
+
operation: 'request-observation',
|
|
2979
|
+
state: 'disposed',
|
|
2980
|
+
}),
|
|
2981
|
+
);
|
|
2982
|
+
}
|
|
2983
|
+
if (
|
|
2984
|
+
!Array.isArray(domains) ||
|
|
2985
|
+
domains.length === 0 ||
|
|
2986
|
+
domains.some(
|
|
2987
|
+
(domain) => domain !== 'linear-hdr' && domain !== 'linear-ldr' && domain !== 'final-srgb',
|
|
2988
|
+
) ||
|
|
2989
|
+
new Set(domains).size !== domains.length
|
|
2990
|
+
) {
|
|
2991
|
+
return err(
|
|
2992
|
+
new RendererOperationError('frame-input-invalid', {
|
|
2993
|
+
operation: 'request-observation',
|
|
2994
|
+
cause: new RendererContractFailureError(
|
|
2995
|
+
'request-observation',
|
|
2996
|
+
'domains must be a non-empty set of linear-hdr, linear-ldr, and final-srgb values',
|
|
2997
|
+
),
|
|
2998
|
+
}),
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
pendingObservationDomains = Object.freeze([...domains]);
|
|
3002
|
+
return ok(undefined);
|
|
3003
|
+
},
|
|
3134
3004
|
getCurrentGraphTarget(name: string) {
|
|
3135
3005
|
return renderSystem.getCurrentGraphTarget(name);
|
|
3136
3006
|
},
|
|
@@ -3313,27 +3183,8 @@ async function makeWebGPURenderer(
|
|
|
3313
3183
|
: worlds.map((world) => leasesByWorld.get(world)).every((lease) => lease !== undefined)
|
|
3314
3184
|
? worlds.map((world) => leasesByWorld.get(world) as RenderReadLease)
|
|
3315
3185
|
: undefined;
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
const physics = world.getResource<{
|
|
3319
|
-
readonly getDerivedAdmission?: () => unknown;
|
|
3320
|
-
readonly getDerivedRecoveryState?: () => string;
|
|
3321
|
-
}>('PhysicsWorld');
|
|
3322
|
-
if (
|
|
3323
|
-
physics?.getDerivedAdmission?.() !== undefined ||
|
|
3324
|
-
physics?.getDerivedRecoveryState?.() === 'rebuild-required'
|
|
3325
|
-
) {
|
|
3326
|
-
return err(
|
|
3327
|
-
new RendererOperationError('frame-input-invalid', {
|
|
3328
|
-
operation: 'draw',
|
|
3329
|
-
cause: new RendererContractFailureError(
|
|
3330
|
-
'draw',
|
|
3331
|
-
'paired geometry admission must finish physics step and writeback in a healthy World before draw',
|
|
3332
|
-
),
|
|
3333
|
-
}),
|
|
3334
|
-
);
|
|
3335
|
-
}
|
|
3336
|
-
}
|
|
3186
|
+
const physicsFrameError = derivedPhysicsFrameError(worlds);
|
|
3187
|
+
if (physicsFrameError !== undefined) return err(physicsFrameError);
|
|
3337
3188
|
if (frameRequest !== undefined && worlds.length !== frameRequest.leases.length) {
|
|
3338
3189
|
return err(
|
|
3339
3190
|
new RendererOperationError('world-lease-invalid', {
|
|
@@ -3369,6 +3220,9 @@ async function makeWebGPURenderer(
|
|
|
3369
3220
|
? {
|
|
3370
3221
|
cameraOwner,
|
|
3371
3222
|
resourceOwner,
|
|
3223
|
+
...(frameRequest.camera.entityKey === undefined
|
|
3224
|
+
? {}
|
|
3225
|
+
: { cameraEntityKey: frameRequest.camera.entityKey }),
|
|
3372
3226
|
...(frameRequest.profileFrame === undefined
|
|
3373
3227
|
? {}
|
|
3374
3228
|
: { profileFrame: frameRequest.profileFrame }),
|
|
@@ -3486,6 +3340,9 @@ async function makeWebGPURenderer(
|
|
|
3486
3340
|
gpuPassTimingCapture?: GpuPassTimingCapture | undefined;
|
|
3487
3341
|
gpuPassTimingSubmittedWork?: Promise<void> | undefined;
|
|
3488
3342
|
gpuPassTimingBeginReason?: GpuPassTimingReason | undefined;
|
|
3343
|
+
observationFrameId?: number | undefined;
|
|
3344
|
+
observationGraphGeneration?: number | undefined;
|
|
3345
|
+
observationCaptureDomains?: readonly FrameObservationDomain[] | undefined;
|
|
3489
3346
|
};
|
|
3490
3347
|
const timingRequested =
|
|
3491
3348
|
isFrameRequest &&
|
|
@@ -3500,6 +3357,9 @@ async function makeWebGPURenderer(
|
|
|
3500
3357
|
if (createdTiming.ok) volumeTimingCapture = createdTiming.value;
|
|
3501
3358
|
}
|
|
3502
3359
|
if (isFrameRequest) {
|
|
3360
|
+
timingHost.observationFrameId = frameId + 1;
|
|
3361
|
+
timingHost.observationGraphGeneration = undefined;
|
|
3362
|
+
timingHost.observationCaptureDomains = pendingObservationDomains;
|
|
3503
3363
|
timingHost.gpuPassTimingFrameIdentity = {
|
|
3504
3364
|
frameId: frameId + 1,
|
|
3505
3365
|
deviceGeneration: activeDeviceScope.generation,
|
|
@@ -3507,9 +3367,13 @@ async function makeWebGPURenderer(
|
|
|
3507
3367
|
};
|
|
3508
3368
|
}
|
|
3509
3369
|
const submitted = renderSystem.draw(worlds, drawOptions, readLeases, volumeTimingCapture);
|
|
3370
|
+
timingHost.observationFrameId = undefined;
|
|
3371
|
+
timingHost.observationCaptureDomains = undefined;
|
|
3510
3372
|
timingHost.gpuPassTimingFrameIdentity = undefined;
|
|
3511
3373
|
if (!submitted) {
|
|
3374
|
+
const cleanupFailure = disposeOwnedObservationCaptures(observationCaptureOwner.drain());
|
|
3512
3375
|
volumeTimingCapture?.discard();
|
|
3376
|
+
if (cleanupFailure !== undefined) return err(cleanupFailure);
|
|
3513
3377
|
return err(
|
|
3514
3378
|
new RendererContractFailureError(
|
|
3515
3379
|
'draw',
|
|
@@ -3524,7 +3388,9 @@ async function makeWebGPURenderer(
|
|
|
3524
3388
|
gpuStore.trackMeshSubmission(queueCompletion);
|
|
3525
3389
|
internals.pack.instrumentation?.onFrameBoundary?.();
|
|
3526
3390
|
if (!isFrameRequest) {
|
|
3391
|
+
const cleanupFailure = disposeOwnedObservationCaptures(observationCaptureOwner.drain());
|
|
3527
3392
|
renderTargetHost.onFrameSubmitted();
|
|
3393
|
+
if (cleanupFailure !== undefined) return err(cleanupFailure);
|
|
3528
3394
|
return ok(undefined);
|
|
3529
3395
|
}
|
|
3530
3396
|
const receiptFrameId = ++frameId;
|
|
@@ -3599,13 +3465,39 @@ async function makeWebGPURenderer(
|
|
|
3599
3465
|
]).finally(() => {
|
|
3600
3466
|
frameContinuations.delete(continuation);
|
|
3601
3467
|
});
|
|
3468
|
+
const captures = observationCaptureOwner.consume(receiptFrameId);
|
|
3469
|
+
pendingObservationDomains = undefined;
|
|
3470
|
+
const receiptGraphGeneration = timingHost.observationGraphGeneration ?? 0;
|
|
3602
3471
|
const receipt = Object.freeze({
|
|
3603
3472
|
frameId: receiptFrameId,
|
|
3604
3473
|
deviceGeneration: receiptGeneration,
|
|
3474
|
+
backendId: internals.device.caps.backendKind,
|
|
3475
|
+
graphGeneration: receiptGraphGeneration,
|
|
3605
3476
|
completed,
|
|
3606
3477
|
});
|
|
3607
3478
|
renderTargetHost.onFrameSubmitted(completed);
|
|
3608
3479
|
latestReceipt = receipt;
|
|
3480
|
+
if (captures.length > 0) {
|
|
3481
|
+
const captureRead: ObservationCaptureRead = async () => {
|
|
3482
|
+
const completion = await completed;
|
|
3483
|
+
if (!completion.ok) {
|
|
3484
|
+
const cleanupFailure = disposeObservationCaptures(captures);
|
|
3485
|
+
return cleanupFailure === undefined ? completion : err(cleanupFailure);
|
|
3486
|
+
}
|
|
3487
|
+
const read = [];
|
|
3488
|
+
for (const capture of captures) {
|
|
3489
|
+
const bytes = await readObservationCapture(capture);
|
|
3490
|
+
if (!bytes.ok) {
|
|
3491
|
+
const cleanupFailure = disposeObservationCaptures(captures);
|
|
3492
|
+
return cleanupFailure === undefined ? bytes : err(cleanupFailure);
|
|
3493
|
+
}
|
|
3494
|
+
read.push({ capture, bytes: bytes.value });
|
|
3495
|
+
}
|
|
3496
|
+
return ok(read);
|
|
3497
|
+
};
|
|
3498
|
+
receiptObservationBuffers.set(receipt, captures);
|
|
3499
|
+
receiptObservationCaptures.set(receipt, captureRead);
|
|
3500
|
+
}
|
|
3609
3501
|
const passTimingCapture = timingHost.gpuPassTimingCapture;
|
|
3610
3502
|
const timingBeginReason = timingHost.gpuPassTimingBeginReason;
|
|
3611
3503
|
const timingSource =
|
|
@@ -3653,6 +3545,11 @@ async function makeWebGPURenderer(
|
|
|
3653
3545
|
dynamicGeometryHost.publishDynamicGeometry(receipt, worlds, frameRequest?.fixedStep);
|
|
3654
3546
|
return ok(receipt);
|
|
3655
3547
|
} catch (cause) {
|
|
3548
|
+
internals.observationFrameId = undefined;
|
|
3549
|
+
internals.observationCaptureDomains = undefined;
|
|
3550
|
+
internals.gpuPassTimingFrameIdentity = undefined;
|
|
3551
|
+
const cleanupFailure = disposeOwnedObservationCaptures(observationCaptureOwner.drain());
|
|
3552
|
+
if (cleanupFailure !== undefined) return err(cleanupFailure);
|
|
3656
3553
|
const error =
|
|
3657
3554
|
cause instanceof Error
|
|
3658
3555
|
? { code: 'unknown' as const, message: cause.message, name: cause.name }
|
|
@@ -3672,7 +3569,28 @@ async function makeWebGPURenderer(
|
|
|
3672
3569
|
receipt: FrameReceipt,
|
|
3673
3570
|
request: FrameObservationRequest,
|
|
3674
3571
|
): Promise<RenderResult<FrameReceiptObservation, RenderError>> {
|
|
3675
|
-
|
|
3572
|
+
const includesDomain = (request.include as readonly string[]).some(
|
|
3573
|
+
(include) =>
|
|
3574
|
+
include === 'linear-hdr' || include === 'linear-ldr' || include === 'final-srgb',
|
|
3575
|
+
);
|
|
3576
|
+
let timingObservation: GpuPassTimingObservation | undefined;
|
|
3577
|
+
if (
|
|
3578
|
+
includesDomain &&
|
|
3579
|
+
gpuPassTimingOptions !== undefined &&
|
|
3580
|
+
request.include.includes('timings')
|
|
3581
|
+
) {
|
|
3582
|
+
const timingResult =
|
|
3583
|
+
timingObservationStore === undefined
|
|
3584
|
+
? await observeGpuPassTimingDisabled(
|
|
3585
|
+
receipt,
|
|
3586
|
+
request,
|
|
3587
|
+
() => activeDeviceScope.generation,
|
|
3588
|
+
)
|
|
3589
|
+
: await timingObservationStore.observe(receipt, request);
|
|
3590
|
+
if (!timingResult.ok) return timingResult;
|
|
3591
|
+
timingObservation = timingResult.value.timings;
|
|
3592
|
+
}
|
|
3593
|
+
if (gpuPassTimingOptions !== undefined && !includesDomain) {
|
|
3676
3594
|
return timingObservationStore === undefined
|
|
3677
3595
|
? observeGpuPassTimingDisabled(receipt, request, () => activeDeviceScope.generation)
|
|
3678
3596
|
: timingObservationStore.observe(receipt, request);
|
|
@@ -3680,7 +3598,8 @@ async function makeWebGPURenderer(
|
|
|
3680
3598
|
if (
|
|
3681
3599
|
!currentReceipts.has(receipt) ||
|
|
3682
3600
|
!issuedReceipts.has(receipt) ||
|
|
3683
|
-
receipt.deviceGeneration !== activeDeviceScope.generation
|
|
3601
|
+
receipt.deviceGeneration !== activeDeviceScope.generation ||
|
|
3602
|
+
receipt.backendId !== internals.device.caps.backendKind
|
|
3684
3603
|
) {
|
|
3685
3604
|
const currentGeneration = activeDeviceScope.generation;
|
|
3686
3605
|
return err(
|
|
@@ -3703,6 +3622,97 @@ async function makeWebGPURenderer(
|
|
|
3703
3622
|
}),
|
|
3704
3623
|
);
|
|
3705
3624
|
}
|
|
3625
|
+
const requestedDomains = (request.include as readonly string[]).filter(
|
|
3626
|
+
(include): include is TypedFrameObservationCapture['domain'] =>
|
|
3627
|
+
include === 'linear-hdr' || include === 'linear-ldr' || include === 'final-srgb',
|
|
3628
|
+
);
|
|
3629
|
+
let observations: FrameReceiptObservation['observations'];
|
|
3630
|
+
if (requestedDomains.length > 0) {
|
|
3631
|
+
if (new Set(requestedDomains).size !== requestedDomains.length) {
|
|
3632
|
+
return err(
|
|
3633
|
+
new RendererContractFailureError(
|
|
3634
|
+
'observe',
|
|
3635
|
+
'each receipt-bound color domain may be requested only once',
|
|
3636
|
+
),
|
|
3637
|
+
);
|
|
3638
|
+
}
|
|
3639
|
+
const readCaptures = receiptObservationCaptures.get(receipt);
|
|
3640
|
+
receiptObservationCaptures.delete(receipt);
|
|
3641
|
+
if (readCaptures === undefined) {
|
|
3642
|
+
return err(
|
|
3643
|
+
new RendererContractFailureError(
|
|
3644
|
+
'observe',
|
|
3645
|
+
'receipt-bound color-domain observations were not explicitly armed before draw, are stale, duplicated, or unavailable',
|
|
3646
|
+
),
|
|
3647
|
+
);
|
|
3648
|
+
}
|
|
3649
|
+
const capturesResult = await readCaptures();
|
|
3650
|
+
if (!capturesResult.ok) return capturesResult;
|
|
3651
|
+
const captures = capturesResult.value;
|
|
3652
|
+
const graphGenerations = new Set(captures.map(({ capture }) => capture.graphGeneration));
|
|
3653
|
+
if (
|
|
3654
|
+
captures.some(
|
|
3655
|
+
({ capture }) =>
|
|
3656
|
+
capture.frameNumber !== receipt.frameId ||
|
|
3657
|
+
capture.deviceGeneration !== receipt.deviceGeneration ||
|
|
3658
|
+
capture.backendId !== internals.device.caps.backendKind ||
|
|
3659
|
+
(receipt.graphGeneration !== undefined &&
|
|
3660
|
+
capture.graphGeneration !== receipt.graphGeneration),
|
|
3661
|
+
) ||
|
|
3662
|
+
graphGenerations.size !== 1
|
|
3663
|
+
) {
|
|
3664
|
+
return err(
|
|
3665
|
+
new RendererContractFailureError(
|
|
3666
|
+
'observe',
|
|
3667
|
+
'receipt-bound color-domain captures crossed frame, device, backend, or graph identity',
|
|
3668
|
+
),
|
|
3669
|
+
);
|
|
3670
|
+
}
|
|
3671
|
+
const byDomain = new Map(
|
|
3672
|
+
captures.map(({ capture, bytes }) => [capture.domain, { capture, bytes }]),
|
|
3673
|
+
);
|
|
3674
|
+
if (
|
|
3675
|
+
byDomain.size !== captures.length ||
|
|
3676
|
+
requestedDomains.some((domain) => !byDomain.has(domain))
|
|
3677
|
+
) {
|
|
3678
|
+
return err(
|
|
3679
|
+
new RendererContractFailureError(
|
|
3680
|
+
'observe',
|
|
3681
|
+
'receipt-bound color-domain captures must contain one source per requested domain',
|
|
3682
|
+
),
|
|
3683
|
+
);
|
|
3684
|
+
}
|
|
3685
|
+
const read: NonNullable<FrameReceiptObservation['observations']>[number][] = [];
|
|
3686
|
+
for (const domain of requestedDomains) {
|
|
3687
|
+
const resolved = byDomain.get(domain);
|
|
3688
|
+
if (resolved === undefined) continue;
|
|
3689
|
+
const { capture, bytes } = resolved;
|
|
3690
|
+
read.push({
|
|
3691
|
+
domain,
|
|
3692
|
+
bytes,
|
|
3693
|
+
metadata: {
|
|
3694
|
+
frameId: receipt.frameId,
|
|
3695
|
+
deviceGeneration: receipt.deviceGeneration,
|
|
3696
|
+
backendId: receipt.backendId,
|
|
3697
|
+
format: capture.format,
|
|
3698
|
+
graphGeneration: capture.graphGeneration,
|
|
3699
|
+
textureIdentity: getTextureIdentity(capture.texture),
|
|
3700
|
+
readbackIdentity: getTextureIdentity(
|
|
3701
|
+
capture.buffer as unknown as import('@forgeax/engine-rhi').Texture,
|
|
3702
|
+
),
|
|
3703
|
+
width: capture.width,
|
|
3704
|
+
height: capture.height,
|
|
3705
|
+
bytesPerRow: capture.bytesPerRow,
|
|
3706
|
+
footprint: { resourceCount: 1, bindGroupCount: 0 },
|
|
3707
|
+
},
|
|
3708
|
+
});
|
|
3709
|
+
}
|
|
3710
|
+
observations = Object.freeze(read);
|
|
3711
|
+
const receiptCaptures = receiptObservationBuffers.get(receipt);
|
|
3712
|
+
if (receiptCaptures?.every((capture) => destroyedObservationBuffers.has(capture.buffer))) {
|
|
3713
|
+
receiptObservationBuffers.delete(receipt);
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3706
3716
|
await renderSystem.observeLodOcclusion();
|
|
3707
3717
|
const targetReadbacks =
|
|
3708
3718
|
request.targetReadbacks === undefined || request.targetReadbacks.length === 0
|
|
@@ -3724,7 +3734,9 @@ async function makeWebGPURenderer(
|
|
|
3724
3734
|
...(targetReadbacks === undefined
|
|
3725
3735
|
? {}
|
|
3726
3736
|
: { targetReadbacks: Object.freeze(targetReadbacks.value) }),
|
|
3737
|
+
...(timingObservation === undefined ? {} : { timings: timingObservation }),
|
|
3727
3738
|
...(volumeTimings === undefined ? {} : { volumeTimings }),
|
|
3739
|
+
...(observations === undefined ? {} : { observations }),
|
|
3728
3740
|
}),
|
|
3729
3741
|
);
|
|
3730
3742
|
},
|
|
@@ -3799,15 +3811,17 @@ async function makeWebGPURenderer(
|
|
|
3799
3811
|
dispose(): RenderResult<void, RenderError> {
|
|
3800
3812
|
if (disposed) return ok(undefined);
|
|
3801
3813
|
disposed = true;
|
|
3802
|
-
for (const world of attachedWorlds)
|
|
3803
|
-
dynamicGeometryHost.invalidateDynamicGeometryWorld(world);
|
|
3804
|
-
}
|
|
3814
|
+
for (const world of attachedWorlds) dynamicGeometryHost.invalidateDynamicGeometryWorld(world);
|
|
3805
3815
|
dynamicGeometry.dispose();
|
|
3806
3816
|
for (const continuation of frameContinuations) {
|
|
3807
3817
|
continuation.terminate({ code: 'disposed' });
|
|
3808
3818
|
}
|
|
3809
3819
|
currentReceipts.clear();
|
|
3810
3820
|
const cleanupFailures: RendererOperationCause[] = [];
|
|
3821
|
+
const observationCleanupFailure = disposeReceiptObservationCaptures();
|
|
3822
|
+
if (observationCleanupFailure !== undefined) {
|
|
3823
|
+
cleanupFailures.push(observationCleanupFailure);
|
|
3824
|
+
}
|
|
3811
3825
|
for (const world of attachedWorlds) {
|
|
3812
3826
|
try {
|
|
3813
3827
|
const derivedSystemName = derivedSystemNames.get(world);
|
|
@@ -3956,6 +3970,10 @@ async function makeWebGPURenderer(
|
|
|
3956
3970
|
return internals.healthRegistry.getLastSnapshot();
|
|
3957
3971
|
},
|
|
3958
3972
|
recover(): Promise<Result<void, RecoverFailure>> {
|
|
3973
|
+
const observationCleanupFailure = disposeReceiptObservationCaptures();
|
|
3974
|
+
if (observationCleanupFailure !== undefined) {
|
|
3975
|
+
return Promise.resolve(err(new RecoverError('recover-device-unavailable')));
|
|
3976
|
+
}
|
|
3959
3977
|
return recoveryFlight.run();
|
|
3960
3978
|
},
|
|
3961
3979
|
};
|
|
@@ -3973,7 +3991,6 @@ async function makeWebGPURenderer(
|
|
|
3973
3991
|
candidatePostProcessResources: RecoveryPostProcessResources,
|
|
3974
3992
|
recoveryRootBundle: RecoveryRootBundle,
|
|
3975
3993
|
recoveryGraphCandidate: RecoveryGraphCandidate | undefined,
|
|
3976
|
-
recoveryColdWorkGuard: RecoveryColdWorkGuard,
|
|
3977
3994
|
): void => {
|
|
3978
3995
|
if (!candidate.scope.isAlive()) {
|
|
3979
3996
|
throw new Error('Renderer generation candidate is not publishable.');
|
|
@@ -4020,9 +4037,6 @@ async function makeWebGPURenderer(
|
|
|
4020
4037
|
// fallback pipeline or residency entry. Candidate preparation populated
|
|
4021
4038
|
// these caches already; a miss here is a hard recovery failure, not an
|
|
4022
4039
|
// opportunity to hide cold work inside the first visible frame.
|
|
4023
|
-
candidate.producerBindings.gpuStore.setRecoveryColdWorkGuard(recoveryColdWorkGuard);
|
|
4024
|
-
internals.recoveryColdWorkGuard = recoveryColdWorkGuard;
|
|
4025
|
-
if (recoveryGraphCandidate !== undefined) recoveryColdWorkGuard.arm();
|
|
4026
4040
|
renderSystem.publishRecoveryPostProcessResources(candidatePostProcessResources);
|
|
4027
4041
|
if (recoveryGraphCandidate !== undefined) {
|
|
4028
4042
|
renderSystem.publishRecoveryGraphCandidate(recoveryGraphCandidate);
|