@forgeax/engine-render 0.1.27 → 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 +842 -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 +52 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -0
- 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 +19 -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 +3 -3
- package/dist/{chunk-ADHHVYLW.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-KM2NOX2I.mjs → chunk-BQVQOQJH.mjs} +958 -254
- package/dist/chunk-BQVQOQJH.mjs.map +1 -0
- package/dist/{chunk-HKXTW355.mjs → chunk-BTWH7BP4.mjs} +386 -17
- package/dist/chunk-BTWH7BP4.mjs.map +1 -0
- package/dist/{chunk-TMIASV2N.mjs → chunk-FUODTTIA.mjs} +3934 -676
- package/dist/chunk-FUODTTIA.mjs.map +1 -0
- package/dist/{chunk-FJ6P52EE.mjs → chunk-ILQ5MEZF.mjs} +3 -17
- package/dist/chunk-ILQ5MEZF.mjs.map +1 -0
- package/dist/{chunk-E34VL5VI.mjs → chunk-JQTSEI3Z.mjs} +3 -3
- package/dist/{chunk-E34VL5VI.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-ZZ4YQ474.mjs → chunk-NRNXBLIJ.mjs} +577 -7
- package/dist/chunk-NRNXBLIJ.mjs.map +1 -0
- package/dist/{chunk-JDWARUOI.mjs → chunk-PA5DRJNI.mjs} +17 -3
- package/dist/chunk-PA5DRJNI.mjs.map +1 -0
- package/dist/{chunk-XDP6XE5V.mjs → chunk-S4DNKQYQ.mjs} +1118 -446
- 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 +13805 -7186
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +11 -17
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/device/mesh-residency-lifetime.d.ts +21 -0
- package/dist/device/mesh-residency-lifetime.d.ts.map +1 -0
- package/dist/dynamic-geometry.d.ts +125 -0
- package/dist/dynamic-geometry.d.ts.map +1 -0
- 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 +21 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +8 -7
- package/dist/index.mjs.map +1 -1
- 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 +118 -10
- 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 +76 -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 +185 -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 +34 -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__/dynamic-geometry-host.unit.test.ts +1123 -0
- package/src/__tests__/dynamic-geometry.unit.test.ts +233 -0
- package/src/__tests__/factory-contract.integration.test.ts +73 -22
- 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__/mesh-submission-lifetime.unit.test.ts +53 -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 +61 -1
- 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 +887 -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 +36 -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 +29 -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 +675 -614
- 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 +3 -3
- package/src/components/sprite-instances.ts +1 -1
- package/src/device/gpu-residency.ts +96 -93
- package/src/device/mesh-residency-lifetime.ts +73 -0
- package/src/dynamic-geometry.ts +736 -0
- 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 +125 -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 +727 -133
- package/src/record/main-pass-geometry.ts +162 -98
- package/src/record/main-pass-material.ts +115 -26
- package/src/record/main-pass-sprite-draws.ts +18 -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 +92 -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 +1006 -53
- 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 +219 -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 +692 -791
- 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-ADHHVYLW.mjs.map +0 -1
- package/dist/chunk-FJ6P52EE.mjs.map +0 -1
- package/dist/chunk-GE3SDD72.mjs.map +0 -1
- package/dist/chunk-GF523LHF.mjs.map +0 -1
- package/dist/chunk-HKXTW355.mjs.map +0 -1
- package/dist/chunk-JDWARUOI.mjs.map +0 -1
- package/dist/chunk-KM2NOX2I.mjs.map +0 -1
- package/dist/chunk-TMIASV2N.mjs.map +0 -1
- package/dist/chunk-XDP6XE5V.mjs.map +0 -1
- package/dist/chunk-ZZ4YQ474.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,7 +32,6 @@ 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,
|
|
@@ -61,7 +42,6 @@ import type {
|
|
|
61
42
|
export type { MaterialShaderManifestEntry } from '@forgeax/engine-shader';
|
|
62
43
|
|
|
63
44
|
import { GpuResidencyCache } from '../device/gpu-residency';
|
|
64
|
-
import type { DeviceResourceKind } from '../device/resource-types';
|
|
65
45
|
import { createEngineMetrics } from '../engine-metrics';
|
|
66
46
|
import { createRecoveryFailedError, RecoverError, type RecoverFailure } from '../errors/recover';
|
|
67
47
|
import {
|
|
@@ -73,7 +53,7 @@ import {
|
|
|
73
53
|
RenderFeatureCapabilityMissingError,
|
|
74
54
|
RenderFeatureStageFailedError,
|
|
75
55
|
} from '../errors/render';
|
|
76
|
-
import { createRenderFeatureHost
|
|
56
|
+
import { createRenderFeatureHost } from '../features/host';
|
|
77
57
|
import { RENDER_FEATURE_VERTEX_LAYOUTS } from '../features/prepared-graphics';
|
|
78
58
|
import type { RenderFeature, RenderFeatureShaderModuleMode } from '../features/types';
|
|
79
59
|
import {
|
|
@@ -81,15 +61,12 @@ import {
|
|
|
81
61
|
postProcessShaderModuleLabel,
|
|
82
62
|
} from '../fullscreen-post-process-pass';
|
|
83
63
|
import { glyphTextLayoutSystem } from '../glyph-text-layout-system';
|
|
64
|
+
import { resolveStandardPbrProgram } from '../gpu-driven/pbr-program';
|
|
84
65
|
import { GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX } from '../gpu-stage';
|
|
85
66
|
import type { BloomInspection } from '../inspection-types';
|
|
67
|
+
import { DeviceScope } from '../lifecycle';
|
|
86
68
|
import {
|
|
87
|
-
|
|
88
|
-
HealthListenerRegistry,
|
|
89
|
-
LostListenerRegistry,
|
|
90
|
-
RhiErrorListenerRegistry,
|
|
91
|
-
} from '../lifecycle';
|
|
92
|
-
import {
|
|
69
|
+
createHdrpBindGroupLayoutDescriptor,
|
|
93
70
|
isCanonicalStandardPbrMaterialShader,
|
|
94
71
|
isStandardPbrMaterialShader,
|
|
95
72
|
type PipelineGroup2Contract,
|
|
@@ -104,6 +81,7 @@ import {
|
|
|
104
81
|
cacheKeyOf,
|
|
105
82
|
colorFormatsForPassKind,
|
|
106
83
|
type PipelineSpec,
|
|
84
|
+
PipelineSpecError,
|
|
107
85
|
passKindPolicyTable,
|
|
108
86
|
} from '../pipeline-spec';
|
|
109
87
|
import type { ExtendedLightingResourceCandidate } from '../prepare/extended-lighting/resources';
|
|
@@ -121,6 +99,7 @@ import {
|
|
|
121
99
|
promoteExtendedLightingCandidate,
|
|
122
100
|
} from '../prepare/extended-lighting/state';
|
|
123
101
|
import type { FrameObservationOptions } from '../record/frame';
|
|
102
|
+
import { getTextureIdentity } from '../record/frame-snapshot';
|
|
124
103
|
import type { GpuPassTimingReason } from '../record/gpu-pass-timing/errors.js';
|
|
125
104
|
import {
|
|
126
105
|
createGpuPassTimingSession,
|
|
@@ -130,14 +109,14 @@ import {
|
|
|
130
109
|
type GpuPassTimingSession,
|
|
131
110
|
} from '../record/gpu-pass-timing/index.js';
|
|
132
111
|
import { GpuTimingCapture, type VolumeTimingObservation } from '../record/gpu-timing';
|
|
133
|
-
import type { RecoveryColdWorkGuard } from '../record/render-context';
|
|
134
112
|
import type {
|
|
113
|
+
AtmosphereShaderSources,
|
|
135
114
|
DrawOwnerOptions,
|
|
115
|
+
FrameObservationDomain,
|
|
136
116
|
FrameObservationRequest,
|
|
137
117
|
FrameReceipt,
|
|
138
118
|
FrameReceiptObservation,
|
|
139
119
|
HealthSnapshot,
|
|
140
|
-
RenderDebugOverlay,
|
|
141
120
|
RendererErrorListener,
|
|
142
121
|
RendererLostListener,
|
|
143
122
|
RendererOptions,
|
|
@@ -146,6 +125,7 @@ import type {
|
|
|
146
125
|
RenderProfile,
|
|
147
126
|
RenderResult,
|
|
148
127
|
RenderWorldLease,
|
|
128
|
+
SsrShaderSources,
|
|
149
129
|
VolumetricFogShaderSources,
|
|
150
130
|
} from '../render-contract';
|
|
151
131
|
import { FXAA_POST_PROCESS_ID, STANDARD_OUTPUT_TRANSFORM_FEATURE_ID } from '../render-contract';
|
|
@@ -162,9 +142,10 @@ import { createGpuPassTimingObservationStore, observeGpuPassTimingDisabled } fro
|
|
|
162
142
|
import { postSpawnResolveJoints } from '../scene-instances/post-spawn-resolve-joints';
|
|
163
143
|
import type { RenderTarget } from '../targets/contracts';
|
|
164
144
|
import { tilemapChunkExtractSystem } from '../tilemap-chunk-extract-system';
|
|
145
|
+
import type { TypedFrameObservationCapture } from '../typed-render-graph-primitives';
|
|
165
146
|
import type { RhiBackendPack } from './backend-contract';
|
|
166
147
|
import type { BundlerOptions } from './bundler-contract';
|
|
167
|
-
import {
|
|
148
|
+
import { createRendererDynamicGeometryController } from './dynamic-geometry-host';
|
|
168
149
|
import type { RendererAssemblyImplementation } from './host-contract';
|
|
169
150
|
import {
|
|
170
151
|
allowsUnlitPreparedFallback,
|
|
@@ -186,7 +167,6 @@ import {
|
|
|
186
167
|
shouldDeferMissingPreparedMaterialShader,
|
|
187
168
|
} from './material-shader-policy';
|
|
188
169
|
import type { MeshSsboGrowResult, MeshSsboState } from './mesh-ssbo-grow';
|
|
189
|
-
import { attachDeviceLostFanout } from './recovery/device-loss-fanout';
|
|
190
170
|
import {
|
|
191
171
|
type GenerationAggregate,
|
|
192
172
|
type GenerationPublication,
|
|
@@ -199,32 +179,48 @@ import {
|
|
|
199
179
|
structuredRendererCause,
|
|
200
180
|
validateRenderProfile,
|
|
201
181
|
} from './renderer-facade';
|
|
182
|
+
import { projectRendererFeatureInspection } from './renderer-feature-inspection';
|
|
202
183
|
import {
|
|
203
184
|
type ContinuationTerminator,
|
|
204
185
|
createContinuationTerminator,
|
|
205
186
|
} from './renderer-frame-transaction';
|
|
206
|
-
import {
|
|
187
|
+
import {
|
|
188
|
+
ensureContextConfigured,
|
|
189
|
+
rendererInitializationError,
|
|
190
|
+
rendererInitializationPipelineError,
|
|
191
|
+
wrapDisposeError,
|
|
192
|
+
} from './renderer-helpers';
|
|
207
193
|
import { projectRendererOutputInspection } from './renderer-inspection';
|
|
208
194
|
import {
|
|
209
195
|
createRecoveryContinuation,
|
|
210
196
|
createRecoveryDeadline,
|
|
211
197
|
createSingleFlight,
|
|
212
|
-
type RecoveryGuidance,
|
|
213
198
|
type RecoveryPhase,
|
|
214
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
|
+
|
|
215
208
|
import { STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES } from './shader-prewarm-policy';
|
|
216
209
|
import { buildReadyWebGPU } from './webgpu-ready';
|
|
217
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';
|
|
218
218
|
import {
|
|
219
|
-
|
|
220
|
-
DEFAULT_VERTEX_ATTRS,
|
|
221
|
-
MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS,
|
|
222
|
-
PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
223
|
-
POSITION_SIZE_COLOR_INSTANCE_VERTEX_BUFFERS,
|
|
219
|
+
isPreparedMaterialVertexLayout,
|
|
224
220
|
PREPARED_INSTANCE_VERTEX_ATTRS,
|
|
225
221
|
PREPARED_MATERIAL_INSTANCE_VERTEX_ATTRS,
|
|
226
|
-
|
|
227
|
-
|
|
222
|
+
particleMaterialInputVertexBuffers,
|
|
223
|
+
resolveWebGPUVertexBufferLayouts,
|
|
228
224
|
} from './webgpu-vertex-layouts';
|
|
229
225
|
|
|
230
226
|
export type { BundlerOptions } from './bundler-contract';
|
|
@@ -247,362 +243,41 @@ export {
|
|
|
247
243
|
selectPipelineLayoutForVariant,
|
|
248
244
|
shouldDeferMissingPreparedMaterialShader,
|
|
249
245
|
} from './material-shader-policy';
|
|
250
|
-
|
|
251
|
-
MeshSsboBufferWrapper,
|
|
252
|
-
MeshSsboGrowController,
|
|
253
|
-
MeshSsboGrowControllerInit,
|
|
254
|
-
MeshSsboGrowDevice,
|
|
255
|
-
MeshSsboGrowErrorRegistry,
|
|
256
|
-
MeshSsboGrowResult,
|
|
257
|
-
MeshSsboState,
|
|
258
|
-
} from './mesh-ssbo-grow';
|
|
246
|
+
|
|
259
247
|
export {
|
|
260
248
|
createMeshSsboGrowController,
|
|
261
249
|
deriveStorageBufferCeiling,
|
|
262
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,
|
|
263
258
|
requireMeshSsboBuffer,
|
|
264
259
|
} from './mesh-ssbo-grow';
|
|
265
260
|
export { exposeRenderer } from './renderer-facade';
|
|
266
261
|
export {
|
|
262
|
+
selectGpuDrivenSceneIndexVariant,
|
|
267
263
|
selectHdrpPbrPrewarmVariants,
|
|
268
264
|
selectProbePrewarmVariants,
|
|
269
265
|
selectSkinPrewarmVariants,
|
|
270
266
|
selectStandardPbrTransmissionPrewarmVariants,
|
|
271
267
|
} from './shader-prewarm-policy';
|
|
268
|
+
export type { WebGPUOutcome, WebGPURendererInternals } from './webgpu-renderer-contract';
|
|
269
|
+
export { disposeObservationCaptureSet } from './webgpu-renderer-observation';
|
|
272
270
|
|
|
273
|
-
|
|
274
|
-
factory: RhiBackendPack['createShaderModule'],
|
|
275
|
-
): MipmapShaderModuleFactory | undefined {
|
|
276
|
-
if (factory === undefined) return undefined;
|
|
277
|
-
return (device, descriptor) => factory(device as RhiDevice, descriptor);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Re-export registerAdvanceAnimationPlayer so consumers can wire it.
|
|
281
|
-
// Re-export registerPropagateTransforms so consumers can wire the
|
|
282
|
-
// Transform.world mat4 derivation (audio listener sync + picking read the
|
|
283
|
-
// derived Transform.world from scripts).
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Bundler-layer injection accepted by `createRenderer` (and proxied by
|
|
287
|
-
* `createApp`) as the optional third argument.
|
|
288
|
-
*
|
|
289
|
-
* feat-20260608-create-app-param-surface-trim / M2 / D-3: aggregates the two
|
|
290
|
-
* host-injected build-tool channels:
|
|
291
|
-
*
|
|
292
|
-
* - shaderManifestUrl: the URL the host's vite-plugin-shader emit step
|
|
293
|
-
* wrote `manifest.json` to. When this field is omitted (or `bundler`
|
|
294
|
-
* itself is omitted), createRenderer falls back to
|
|
295
|
-
* '/shaders/manifest.json' (D-2 q5-A) so the LO 1.1 zero-config takeoff
|
|
296
|
-
* path keeps working without explicit injection. Tests can inject via a
|
|
297
|
-
* `data:application/json,...` URL to bypass fetch.
|
|
298
|
-
*
|
|
299
|
-
* - importTransport: dev-only ImportTransport forwarded verbatim to the
|
|
300
|
-
* AssetRegistry third ctor slot so DDC-miss assets can lazy-import.
|
|
301
|
-
* Absent => shipped form (a DDC miss fails fast with `asset-not-imported`).
|
|
302
|
-
*
|
|
303
|
-
* The interface is structurally shared with `@forgeax/engine-app` while this
|
|
304
|
-
* package keeps the renderer independent from the host package.
|
|
305
|
-
*/
|
|
306
|
-
// ─── Backend pack — M3 auto-select internal shape ───────────────────────────
|
|
307
|
-
|
|
308
|
-
// ─── WebGPU branch ──────────────────────────────────────────────────────────
|
|
309
|
-
|
|
310
|
-
/**
|
|
311
|
-
* Wire the spec `device.lost` Promise into the lost / error / health channels
|
|
312
|
-
* (research §F-4 / R2). A genuine device loss fans out through `errorRegistry`
|
|
313
|
-
* so the public `renderer.subscribe` error event triggers, and
|
|
314
|
-
* through `healthRegistry` so `health().reason` flips to `'device-lost'`
|
|
315
|
-
* (feat-20260622-s5 M1/M2). Explicit renderer teardown is observable through
|
|
316
|
-
* `onLost`, but is not a runtime error and must not manufacture an RhiError.
|
|
317
|
-
*
|
|
318
|
-
* Extracted to a single helper (SSOT) so the createRenderer assembly path AND
|
|
319
|
-
* the recover() rebuild path attach byte-identical fan-out to whichever device
|
|
320
|
-
* is current — recover() mints a fresh device whose own `lost` Promise must be
|
|
321
|
-
* re-wired to the SAME registries the host already subscribed to.
|
|
322
|
-
*/
|
|
323
|
-
// Device loss wiring lives in the recovery assembly seam so boot and recovery
|
|
324
|
-
// cannot drift into separate lifecycle event paths.
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* tryCreateWebGPURenderer returns three kinds of outcome (fix-f1 / w16):
|
|
328
|
-
* - `ok` → Renderer created successfully
|
|
329
|
-
* - `rhi-err` → RHI Result.err path (preserves the original RhiError
|
|
330
|
-
* for AI consumers to read; now includes context acquisition
|
|
331
|
-
* failures via pack.rhi.acquireCanvasContext)
|
|
332
|
-
* - `throw` → exception path (caught and wrapped by the caller as
|
|
333
|
-
* an Error)
|
|
334
|
-
*/
|
|
335
|
-
export type WebGPUOutcome =
|
|
336
|
-
| { kind: 'ok'; renderer: RendererAssemblyImplementation }
|
|
337
|
-
| { kind: 'rhi-err'; error: RhiError }
|
|
338
|
-
| { kind: 'throw'; error: Error };
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Goes through the strict two-step path exposed by the injected backend pack:
|
|
342
|
-
* `rhi.requestAdapter()` -> `adapter.requestDevice()`
|
|
343
|
-
* (M6 fix-up [w51] retires the legacy single-step factory per plan-strategy
|
|
344
|
-
* §6 M3 break-point #2 + AGENTS.md break-point list 2026-05-10 #2). Returns
|
|
345
|
-
* `Result<RhiAdapter, RhiError>` then `Result<RhiDevice, RhiError>`; each
|
|
346
|
-
* failure / failure to acquire the canvas WebGPU context returns a
|
|
347
|
-
* structured outcome so the caller can decide on fallback and error-
|
|
348
|
-
* preservation strategy (plan-strategy §7.3).
|
|
349
|
-
*
|
|
350
|
-
* fix-f1: on either Result.err path the original `RhiError` is preserved
|
|
351
|
-
* via the `rhi-err` outcome — when fallback also fails, AI consumers read
|
|
352
|
-
* it via `EngineEnvironmentError.detail.webgpuError.code`.
|
|
353
|
-
*
|
|
354
|
-
* w19 / M4: context acquisition goes through `pack.rhi.acquireCanvasContext(canvas)`
|
|
355
|
-
* — a polymorphic entry owned by the selected backend adapter.
|
|
356
|
-
* The backend pack is selected BEFORE this call (in `loadBackendPack` /
|
|
357
|
-
* the Channel 2→3 fallback in `createRenderer`), so the canvas context
|
|
358
|
-
* is never type-locked by a wrong backend's acquisition attempt.
|
|
359
|
-
*/
|
|
271
|
+
/** Bundler injection and the WebGPU renderer construction entry points. */
|
|
360
272
|
let nextDerivedSystemId = 0;
|
|
361
|
-
|
|
362
273
|
export async function tryCreateWebGPURenderer(
|
|
363
274
|
canvas: HTMLCanvasElement | OffscreenCanvas,
|
|
364
275
|
options: RendererOptions | undefined,
|
|
365
276
|
pack: RhiBackendPack,
|
|
366
|
-
// feat-20260608 M2 / D-3: BundlerOptions threaded verbatim. Carries
|
|
367
|
-
// shaderManifestUrl (consumed in getShader fallback ~30 lines below) and
|
|
368
|
-
// importTransport (forwarded to the AssetRegistry third ctor slot;
|
|
369
|
-
// undefined keeps the shipped form, AC-08).
|
|
370
277
|
bundler: BundlerOptions | undefined,
|
|
371
278
|
): Promise<WebGPUOutcome> {
|
|
372
|
-
|
|
373
|
-
// M6 fix-up [w51]: spec-aligned two-step path. Step 1 - requestAdapter()
|
|
374
|
-
// surfaces adapter.features / adapter.limits (capability pre-screen,
|
|
375
|
-
// charter proposition 4 forward-reachable). Step 2 - adapter.requestDevice()
|
|
376
|
-
// requests the actual device. M3 D-P4: `pack.rhi` is the auto-selected
|
|
377
|
-
// backend singleton (rhi-webgpu / rhi-wgpu / explicit escape hatch).
|
|
378
|
-
// w19: thread canvas as compatibleSurface so the wgpu GL backend can
|
|
379
|
-
// enumerate adapters against it (escape hatch, capability-gated).
|
|
380
|
-
const adapterResult = await pack.rhi.requestAdapter(undefined, canvas);
|
|
381
|
-
if (!adapterResult.ok) {
|
|
382
|
-
return { kind: 'rhi-err', error: adapterResult.error };
|
|
383
|
-
}
|
|
384
|
-
const adapter = adapterResult.value;
|
|
385
|
-
// M4 w28: filter compression features from adapter.features (AC-07). The
|
|
386
|
-
// same helper is used by recover() below so both paths share one profile.
|
|
387
|
-
const deviceOpts = deviceOptionsForAdapter(adapter, options);
|
|
388
|
-
const result = await adapter.requestDevice(deviceOpts);
|
|
389
|
-
if (!result.ok) {
|
|
390
|
-
return { kind: 'rhi-err', error: result.error };
|
|
391
|
-
}
|
|
392
|
-
const device: RhiDevice = result.value;
|
|
393
|
-
|
|
394
|
-
// Acquire the canvas context through the backend pack (M3 / w16).
|
|
395
|
-
// rhi-webgpu: internally calls canvas.getContext('webgpu') + branded wrap.
|
|
396
|
-
// rhi-wgpu: internally calls wasm createSurface(canvas) + configure.
|
|
397
|
-
// When context acquisition fails (null from canvas.getContext or wasm
|
|
398
|
-
// createSurface throws), the pack returns Result.err(RhiError).
|
|
399
|
-
const ctxResult = pack.rhi.acquireCanvasContext(canvas);
|
|
400
|
-
if (!ctxResult.ok) {
|
|
401
|
-
return { kind: 'rhi-err', error: ctxResult.error };
|
|
402
|
-
}
|
|
403
|
-
const context: RhiCanvasContext = ctxResult.value;
|
|
404
|
-
|
|
405
|
-
const lostRegistry = new LostListenerRegistry();
|
|
406
|
-
const errorRegistry = new RhiErrorListenerRegistry();
|
|
407
|
-
const healthRegistry = new HealthListenerRegistry();
|
|
408
|
-
const lossObserver: {
|
|
409
|
-
current?: (detail: string) => void;
|
|
410
|
-
} = {};
|
|
411
|
-
const generationState: {
|
|
412
|
-
current: number;
|
|
413
|
-
onStaleLoss?: () => void;
|
|
414
|
-
} = { current: 0 };
|
|
415
|
-
// device.lost dual-track (research §F-4 / R2 countermeasure): the spec
|
|
416
|
-
// Promise is passed through to the lost-fan-out registry (extracted to
|
|
417
|
-
// attachDeviceLostFanout so the recover() rebuild path re-attaches the
|
|
418
|
-
// SAME wiring to the freshly-minted device — SSOT, one fan-out shape).
|
|
419
|
-
attachDeviceLostFanout(device, pack, {
|
|
420
|
-
lostRegistry,
|
|
421
|
-
errorRegistry,
|
|
422
|
-
healthRegistry,
|
|
423
|
-
generation: 0,
|
|
424
|
-
currentGeneration: () => generationState.current,
|
|
425
|
-
onStaleLoss: () => generationState.onStaleLoss?.(),
|
|
426
|
-
onDeviceLost: (detail) => lossObserver.current?.(detail),
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// D-VD2 Round 2 wire-up part 2: register the spec `onuncapturederror`
|
|
430
|
-
// listener on the raw GPUDevice so GPUUncapturedErrorEvent (validation /
|
|
431
|
-
// oom / internal) is translated to the 17-member RhiErrorCode union +
|
|
432
|
-
// dispatched through the public Renderer event subscription. When the pack does not expose a
|
|
433
|
-
// raw-device escape hatch (rhi-wgpu wasm path, custom escape hatch), the
|
|
434
|
-
// listener registration is skipped — the device.lost dual-channel above
|
|
435
|
-
// still fires through `RhiDevice.lost` Promise on every path (graceful
|
|
436
|
-
// degradation per charter proposition 9; per-path coverage matrix in
|
|
437
|
-
// packages/engine/README.md `## Error model`).
|
|
438
|
-
if (pack._internal_getRawDevice && pack.translateErrorEventToRhiError) {
|
|
439
|
-
const backendDevice = pack._internal_getRawDevice(device);
|
|
440
|
-
if (backendDevice && typeof backendDevice === 'object') {
|
|
441
|
-
const target = backendDevice as {
|
|
442
|
-
onuncapturederror?: ((event: unknown) => void) | null;
|
|
443
|
-
};
|
|
444
|
-
// Spec form: `device.onuncapturederror = (event) => ...`. The listener
|
|
445
|
-
// sets a property handler (spec normative GPUDevice extends EventTarget
|
|
446
|
-
// and exposes `onuncapturederror` as a settable callback property);
|
|
447
|
-
// engine writes through the property so each pre-existing handler that
|
|
448
|
-
// an AI user assigned would be replaced (last-write-wins; spec lifetime
|
|
449
|
-
// is device-scoped so engine ownership is OK — AI users observe via
|
|
450
|
-
// the Renderer event subscription, not the raw property).
|
|
451
|
-
target.onuncapturederror = (event: unknown): void => {
|
|
452
|
-
const translated = pack.translateErrorEventToRhiError?.(event);
|
|
453
|
-
if (translated && !translated.ok) {
|
|
454
|
-
errorRegistry.fire(translated.error);
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return {
|
|
460
|
-
kind: 'ok',
|
|
461
|
-
renderer: await makeWebGPURenderer({
|
|
462
|
-
canvas,
|
|
463
|
-
device,
|
|
464
|
-
context,
|
|
465
|
-
options,
|
|
466
|
-
bundler,
|
|
467
|
-
lostRegistry,
|
|
468
|
-
errorRegistry,
|
|
469
|
-
healthRegistry,
|
|
470
|
-
pack,
|
|
471
|
-
importTransport,
|
|
472
|
-
lossObserver,
|
|
473
|
-
generationState,
|
|
474
|
-
}),
|
|
475
|
-
};
|
|
279
|
+
return tryCreateWebGPURendererBootstrap(canvas, options, pack, bundler, makeWebGPURenderer);
|
|
476
280
|
}
|
|
477
|
-
|
|
478
|
-
export interface WebGPURendererInternals {
|
|
479
|
-
canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
480
|
-
device: RhiDevice;
|
|
481
|
-
// M6 / w41 (feat-20260510-rhi-resource-creation): forgeax RhiCanvasContext
|
|
482
|
-
// brand; RenderSystem + ensureContextConfigured both go through the RHI
|
|
483
|
-
// surface (charter proposition 5 consistent abstraction red line).
|
|
484
|
-
context: RhiCanvasContext;
|
|
485
|
-
options: RendererOptions | undefined;
|
|
486
|
-
/**
|
|
487
|
-
* feat-20260608-create-app-param-surface-trim / M2 / D-3: BundlerOptions
|
|
488
|
-
* forwarded verbatim from createRenderer's third arg. Carries
|
|
489
|
-
* shaderManifestUrl (host-injected vite-plugin-shader emit URL; absent =>
|
|
490
|
-
* createRenderer falls back to '/shaders/manifest.json') and the
|
|
491
|
-
* importTransport that previously rode a dedicated internal slot.
|
|
492
|
-
*/
|
|
493
|
-
bundler: BundlerOptions | undefined;
|
|
494
|
-
lostRegistry: LostListenerRegistry;
|
|
495
|
-
errorRegistry: RhiErrorListenerRegistry;
|
|
496
|
-
healthRegistry: HealthListenerRegistry;
|
|
497
|
-
/** M3 D-P4 auto-select pack — carries the dynamic-imported rhi-webgpu / rhi-wgpu singleton + optional async shader factory. */
|
|
498
|
-
pack: RhiBackendPack;
|
|
499
|
-
/**
|
|
500
|
-
* feat-20260604-hdr-equirect-cube-importer-loader M4 / w16 (D-3): the
|
|
501
|
-
* dev-only ImportTransport, forwarded verbatim from createRenderer to the
|
|
502
|
-
* AssetRegistry third ctor slot. `undefined` keeps the shipped form (DDC
|
|
503
|
-
* miss fails fast with `asset-not-imported`, AC-08).
|
|
504
|
-
*/
|
|
505
|
-
importTransport?: ImportTransport | undefined;
|
|
506
|
-
/**
|
|
507
|
-
* feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M2 / T-M2-05:
|
|
508
|
-
* mesh-SSBO grow hook surfaced by `makeWebGPURenderer` after the controller
|
|
509
|
-
* is constructed. Undefined before `makeWebGPURenderer` runs; populated by
|
|
510
|
-
* the time the renderer is returned to the caller. M3's record stage reads
|
|
511
|
-
* this field via the runtime path that bridges into render-system-record.ts
|
|
512
|
-
* (the bridging plumbing is in M3's targetFiles).
|
|
513
|
-
*
|
|
514
|
-
* Returns `{ ok: true }` when the grow completed (or short-circuited
|
|
515
|
-
* idempotently); `{ ok: false, code }` with a fired structured error
|
|
516
|
-
* (`MeshSsboCeilingReachedError` or `MeshSsboCapacityExceededError`) when
|
|
517
|
-
* the request cannot be satisfied. Never throws (D-5).
|
|
518
|
-
*/
|
|
519
|
-
growMeshSsbo?: (neededSlots: number) => MeshSsboGrowResult;
|
|
520
|
-
/**
|
|
521
|
-
* feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M3 / T-M3-04:
|
|
522
|
-
* read-only handle to the grow controller's state (slotCount + wrapper
|
|
523
|
-
* refs), surfaced alongside `growMeshSsbo` so `ensureMeshSsboCapacity`
|
|
524
|
-
* (record-stage wiring) can short-circuit when slotCount already covers the
|
|
525
|
-
* frame's renderable count and so the dev-mode info log can report the
|
|
526
|
-
* before/after slot count transition. Undefined until buildReadyWebGPU
|
|
527
|
-
* has wired the controller; identity stable across grow events.
|
|
528
|
-
*/
|
|
529
|
-
meshSsboState?: MeshSsboState;
|
|
530
|
-
/** Host-owned producer features; created before device-bound assembly starts. */
|
|
531
|
-
featureHost?: RenderFeatureHost;
|
|
532
|
-
/** App-owned overlay capability read by the typed graph at frame time. */
|
|
533
|
-
debugOverlay?: RenderDebugOverlay;
|
|
534
|
-
/** Composed renderer utility sources installed atomically at build-ready. */
|
|
535
|
-
volumetricFogShaders?: VolumetricFogShaderSources | undefined;
|
|
536
|
-
gpuPassTimingSubmittedWork?: Promise<void> | undefined;
|
|
537
|
-
lossObserver: { current?: (detail: string) => void };
|
|
538
|
-
generationState: {
|
|
539
|
-
current: number;
|
|
540
|
-
onStaleLoss?: () => void;
|
|
541
|
-
};
|
|
542
|
-
recoveryColdWorkGuard?: RecoveryColdWorkGuard | undefined;
|
|
543
|
-
getMaterialShaderUvSetCount?: (materialShaderId: string) => number | undefined;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
export interface RecoveryFailureLocation {
|
|
547
|
-
readonly phase: RecoveryPhase;
|
|
548
|
-
readonly retryable: boolean;
|
|
549
|
-
readonly guidance: RecoveryGuidance;
|
|
550
|
-
readonly owner: string;
|
|
551
|
-
readonly resourceKind: DeviceResourceKind;
|
|
552
|
-
readonly cause: unknown;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function recoveryCauseCode(cause: unknown): string | undefined {
|
|
556
|
-
if (typeof cause !== 'object' || cause === null) return undefined;
|
|
557
|
-
const directCode = (cause as { readonly code?: unknown }).code;
|
|
558
|
-
if (typeof directCode === 'string') return directCode;
|
|
559
|
-
const detail = (cause as { readonly detail?: unknown }).detail;
|
|
560
|
-
if (typeof detail !== 'object' || detail === null) return undefined;
|
|
561
|
-
const detailCode = (detail as { readonly code?: unknown }).code;
|
|
562
|
-
if (typeof detailCode === 'string') return detailCode;
|
|
563
|
-
return recoveryCauseCode((detail as { readonly cause?: unknown }).cause);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
function createRecoveryFailureLocation(
|
|
567
|
-
phase: RecoveryPhase,
|
|
568
|
-
cause: unknown,
|
|
569
|
-
overrides: Partial<Omit<RecoveryFailureLocation, 'phase' | 'cause'>> = {},
|
|
570
|
-
): RecoveryFailureLocation {
|
|
571
|
-
const code = recoveryCauseCode(cause) ?? '';
|
|
572
|
-
const resourceKind: DeviceResourceKind = code.includes('shader')
|
|
573
|
-
? 'shader'
|
|
574
|
-
: code.includes('buffer')
|
|
575
|
-
? 'buffer'
|
|
576
|
-
: code.includes('texture')
|
|
577
|
-
? 'texture'
|
|
578
|
-
: code.includes('binding')
|
|
579
|
-
? 'binding'
|
|
580
|
-
: phase === 'rehydrate'
|
|
581
|
-
? 'shader'
|
|
582
|
-
: phase === 'acquire-adapter' || phase === 'acquire-device'
|
|
583
|
-
? 'surface'
|
|
584
|
-
: phase === 'publish' || phase === 'cleanup'
|
|
585
|
-
? 'surface'
|
|
586
|
-
: 'pipeline';
|
|
587
|
-
const owner =
|
|
588
|
-
code.includes('shader') || phase === 'rehydrate'
|
|
589
|
-
? 'shader'
|
|
590
|
-
: phase === 'acquire-adapter' || phase === 'acquire-device'
|
|
591
|
-
? 'backend'
|
|
592
|
-
: phase === 'compile-graph'
|
|
593
|
-
? 'pipeline'
|
|
594
|
-
: 'renderer';
|
|
595
|
-
return {
|
|
596
|
-
phase,
|
|
597
|
-
retryable: false,
|
|
598
|
-
guidance: 'repair-owner',
|
|
599
|
-
owner,
|
|
600
|
-
resourceKind,
|
|
601
|
-
cause,
|
|
602
|
-
...overrides,
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
|
|
606
281
|
async function makeWebGPURenderer(
|
|
607
282
|
internals: WebGPURendererInternals,
|
|
608
283
|
): Promise<RendererAssemblyImplementation> {
|
|
@@ -891,16 +566,13 @@ async function makeWebGPURenderer(
|
|
|
891
566
|
// looks up MeshFilter.assetHandle here per frame; buildReadyWebGPU step 3
|
|
892
567
|
// uploads the registry's builtin mesh geometry to GPU buffers so the
|
|
893
568
|
// RenderSystem can route MeshFilter.assetHandle to a real (vbo, ibo) pair.
|
|
894
|
-
//
|
|
895
569
|
// feat-20260527 M1 / w1: ShaderCatalog is now eager-constructed and
|
|
896
570
|
// constructor-injected into AssetRegistry (plan-strategy D-1). The
|
|
897
571
|
// ShaderCatalogDevice adapter (shared between the catalog and per-MaterialShader
|
|
898
572
|
// pipeline cache) is already available at this point.
|
|
899
573
|
const shaderCatalog = getShader();
|
|
900
|
-
|
|
901
574
|
// feat-20260528-material-shader-registration-unification M3 / w14:
|
|
902
575
|
// placeholder hardcoded installMaterialArtifact calls deleted.
|
|
903
|
-
//
|
|
904
576
|
// bug-20260601-hello-tonemap-material-register M1 (plan-strategy D-1):
|
|
905
577
|
// All material-shader entries are now registered from the manifest
|
|
906
578
|
// BEFORE the renderer is returned, so `register<MaterialAsset>` referencing
|
|
@@ -943,8 +615,7 @@ async function makeWebGPURenderer(
|
|
|
943
615
|
renderSystemForTargetPromotion?.isCubeCapturePending(target) !== true,
|
|
944
616
|
});
|
|
945
617
|
// feat-20260527-sprite-nineslice M4 / w16 (D-5): detached host-owned
|
|
946
|
-
// EngineMetrics counter
|
|
947
|
-
// stage via `RenderSystemRuntime.metrics` so soft-warns
|
|
618
|
+
// EngineMetrics counter reaches the record stage via `RenderSystemRuntime.metrics`.
|
|
948
619
|
// (`nineslice.scale-too-small`, `nineslice.tile-needs-repeat-sampler`) bump
|
|
949
620
|
// counters through the owning service (charter P3 machine-readable signals
|
|
950
621
|
// over a per-frame console.warn flood). Each renderer assembly owns its own
|
|
@@ -1014,8 +685,7 @@ async function makeWebGPURenderer(
|
|
|
1014
685
|
// feat-20260707 M5 / w33 (D-11 + D-8): project the RhiCaps three-way
|
|
1015
686
|
// compression triple into the codec-facing `TranscodeCaps` and wire it into
|
|
1016
687
|
// the registry so the texture / equirect Basis arms can pick a transcode
|
|
1017
|
-
// target. One-line projection; the loader stays a pure consumer of
|
|
1018
|
-
// declared ctx input (Pipeline Isolation).
|
|
688
|
+
// target. One-line projection; the loader stays a pure consumer of declared ctx input (Pipeline Isolation).
|
|
1019
689
|
assets.setTranscodeCaps({
|
|
1020
690
|
bc: internals.device.caps.textureCompressionBc,
|
|
1021
691
|
etc2: internals.device.caps.textureCompressionEtc2,
|
|
@@ -1037,25 +707,6 @@ async function makeWebGPURenderer(
|
|
|
1037
707
|
// count is asserted explicitly. Missing mesh sets are clamp-to-last aliases.
|
|
1038
708
|
materialShaderUvSetCounts.set('forgeax::default-standard-pbr', 8);
|
|
1039
709
|
materialShaderUvSetCounts.set(SKIN_MATERIAL_SHADER_ID, 8);
|
|
1040
|
-
// M5 wiring (feat-20260517-vite-plugin-image-build-time-cook w14b): hand
|
|
1041
|
-
// the RhiDevice to the AssetRegistry so `loadByGuid<TextureAsset>` ->
|
|
1042
|
-
// `loadTextureFromEntry` -> `uploadTexture` actually runs the GPU
|
|
1043
|
-
// upload step plan-strategy section 3.2 sequence specifies. Without
|
|
1044
|
-
// this call uploadTexture step 3 short-circuits with `Result.ok(undefined)`
|
|
1045
|
-
// and the render-system materialBindGroup falls through to the 1x1
|
|
1046
|
-
// white fallback view (charter P3 violation: silent loss of texture).
|
|
1047
|
-
// bug-20260518 D-1: configureGpuDevice gains a 2nd parameter --
|
|
1048
|
-
// `pack.createShaderModule`, the top-level async shader-module factory
|
|
1049
|
-
// (rhi-webgpu / rhi-wgpu both expose it). The runtime mipmap utility uses
|
|
1050
|
-
// this factory inside `generateMipmaps` instead of the (now-deleted)
|
|
1051
|
-
// synchronous `device.createShaderModule` member. The parameter is
|
|
1052
|
-
// structurally optional at this call-site so the explicit-rhi escape
|
|
1053
|
-
// hatch (D-R5) without a top-level factory still routes through the
|
|
1054
|
-
// existing `invokeDeviceCreateShaderModule` fallback in
|
|
1055
|
-
// buildReadyWebGPU; AssetRegistry.uploadTexture's mipmap branch surfaces
|
|
1056
|
-
// the missing-factory case as a structured `rhi-not-available` error
|
|
1057
|
-
// (charter P3 explicit failure) rather than throwing here.
|
|
1058
|
-
//
|
|
1059
710
|
const packShaderFactory = adaptMipmapShaderModuleFactory(internals.pack.createShaderModule);
|
|
1060
711
|
// The RhiDevice surface satisfies `MipmapBlitDevice` (createTexture +
|
|
1061
712
|
// createTextureView + createCommandEncoder + createBindGroup +
|
|
@@ -1109,6 +760,7 @@ async function makeWebGPURenderer(
|
|
|
1109
760
|
const publishRendererGeneration = (candidate: RendererGeneration): void => {
|
|
1110
761
|
publishGeneration(generationPublication, candidate, (value) => value.scope.isAlive());
|
|
1111
762
|
activeDeviceScope = candidate.scope;
|
|
763
|
+
dynamicGeometry.invalidateGeneration(activeDeviceScope.generation);
|
|
1112
764
|
internals.generationState.current = activeDeviceScope.generation;
|
|
1113
765
|
pipelineState = candidate.pipeline;
|
|
1114
766
|
const bindings = candidate.producerBindings;
|
|
@@ -1125,28 +777,6 @@ async function makeWebGPURenderer(
|
|
|
1125
777
|
gpuStore.bindDeviceScope(activeDeviceScope);
|
|
1126
778
|
};
|
|
1127
779
|
let readySettled = false;
|
|
1128
|
-
// w24 — Renderer.initialization returns Promise<Result<void, RhiError>>: resolve
|
|
1129
|
-
// ok(undefined) on success / resolve err(RhiError) on failure. The legacy
|
|
1130
|
-
// reject path is converted by a `.then() / .catch()` wrap so the inner
|
|
1131
|
-
// factory (`buildReadyWebGPU`) keeps its reject-on-error shape (research
|
|
1132
|
-
// F-3 internal contract preserved) while the public surface settles
|
|
1133
|
-
// strictly resolve-only (charter proposition 4 explicit failure - AI
|
|
1134
|
-
// users branch on `.ok` instead of try/catch).
|
|
1135
|
-
// feat-20260523-shader-template-instance-split M9-T03 (D-PipelineBuilder):
|
|
1136
|
-
// per-MaterialShader pipeline cache (LDR + HDR variants keyed by
|
|
1137
|
-
// `${materialShaderId}:${ldr|hdr}`). Map is owned by the renderer (lifetime
|
|
1138
|
-
// = Renderer) so cache hits persist across draw calls but reset between
|
|
1139
|
-
// renderers. Map.get / .set are sync; the underlying shader-module compile
|
|
1140
|
-
// is async-cached inside `makeShaderDeviceAdapter` (1-frame warmup -- first
|
|
1141
|
-
// miss returns Result.err('rhi-not-available'); after the async build
|
|
1142
|
-
// resolves the adapter caches the module, the next miss-then-build returns
|
|
1143
|
-
// Ok and lands in this Map).
|
|
1144
|
-
//
|
|
1145
|
-
// M6 fix-up: declared BEFORE `buildReadyWebGPU` so the boot-time SPEC_CONST
|
|
1146
|
-
// prewarm step inside that promise can seed URP-variant entries directly
|
|
1147
|
-
// (the seeding closure captures this Map by reference; ordering is safe
|
|
1148
|
-
// because the prewarm step runs strictly before the first frame's
|
|
1149
|
-
// `getMaterialShaderPipeline` lookup that would consume it).
|
|
1150
780
|
const materialShaderPipelineCache = new Map<string, RenderPipeline>();
|
|
1151
781
|
const materialShaderPipelineGroup2Contracts = new WeakMap<object, PipelineGroup2Contract>();
|
|
1152
782
|
const materialShaderManifestEntryCache = new Map<string, MaterialShaderManifestEntry>();
|
|
@@ -1412,9 +1042,23 @@ async function makeWebGPURenderer(
|
|
|
1412
1042
|
};
|
|
1413
1043
|
const getOrBuildPreparedMaterialPipelineLayout = (
|
|
1414
1044
|
materialShaderId: string,
|
|
1045
|
+
clustered = false,
|
|
1415
1046
|
): PipelineLayout | null => {
|
|
1416
1047
|
const cacheState = currentPipelineCacheState();
|
|
1417
|
-
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);
|
|
1418
1062
|
if (cached !== undefined) return cached;
|
|
1419
1063
|
const currentState = currentPipelineState();
|
|
1420
1064
|
if (currentState === null) return null;
|
|
@@ -1423,15 +1067,72 @@ async function makeWebGPURenderer(
|
|
|
1423
1067
|
? undefined
|
|
1424
1068
|
: getOrBuildPerShaderMaterialLayout(materialShaderId)?.materialBgl) ??
|
|
1425
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
|
+
}
|
|
1426
1123
|
const result = currentBuildDevice().createPipelineLayout({
|
|
1427
1124
|
label: `prepared-material-pl-${materialShaderId}`,
|
|
1428
|
-
bindGroupLayouts: [
|
|
1125
|
+
bindGroupLayouts: [
|
|
1126
|
+
sceneInputLayout,
|
|
1127
|
+
...(usesMaterialGroup ? [materialBgl] : []),
|
|
1128
|
+
...(lightingLayout?.ok ? [lightingLayout.value] : []),
|
|
1129
|
+
],
|
|
1429
1130
|
});
|
|
1430
1131
|
if (!result.ok) {
|
|
1431
1132
|
internals.errorRegistry.fire(result.error);
|
|
1432
1133
|
return null;
|
|
1433
1134
|
}
|
|
1434
|
-
cacheState.preparedMaterialPipelineLayoutCache.set(
|
|
1135
|
+
cacheState.preparedMaterialPipelineLayoutCache.set(cacheKey, result.value);
|
|
1435
1136
|
return result.value;
|
|
1436
1137
|
};
|
|
1437
1138
|
const getOrBuildPerShaderMaterialLayout = (
|
|
@@ -1481,6 +1182,7 @@ async function makeWebGPURenderer(
|
|
|
1481
1182
|
const desc = buildBindGroupLayoutDescriptor(spec, {
|
|
1482
1183
|
kind: 'pbr-material-merged',
|
|
1483
1184
|
materialParamSchema: paramSchema,
|
|
1185
|
+
caps: { storageBuffer: currentBuildDevice().caps.storageBuffer },
|
|
1484
1186
|
});
|
|
1485
1187
|
const bglRes = currentBuildDevice().createBindGroupLayout(desc);
|
|
1486
1188
|
if (!bglRes.ok) {
|
|
@@ -1619,13 +1321,9 @@ async function makeWebGPURenderer(
|
|
|
1619
1321
|
internals.pack.createShaderModule,
|
|
1620
1322
|
internals.errorRegistry,
|
|
1621
1323
|
requiredMaterialShaders,
|
|
1324
|
+
materialShaderUvSetCounts.get('forgeax::default-standard-pbr') ?? 0,
|
|
1622
1325
|
requiredFullscreenPostProcesses,
|
|
1623
|
-
//
|
|
1624
|
-
// T-M2-05 + M3 / T-M3-04: callback set by buildReadyWebGPU once
|
|
1625
|
-
// meshSsboController is wired. Both the grow hook AND the read-only
|
|
1626
|
-
// state ref land on `internals` so the record stage (M3
|
|
1627
|
-
// ensureMeshSsboCapacity) can read slotCount + call grow through
|
|
1628
|
-
// RenderSystemInternals.
|
|
1326
|
+
// Publish growth and capacity together for the active or candidate device.
|
|
1629
1327
|
(hook, state) => {
|
|
1630
1328
|
if (candidateBuildState !== undefined) {
|
|
1631
1329
|
candidateBuildState.growMeshSsbo = hook;
|
|
@@ -1650,13 +1348,7 @@ async function makeWebGPURenderer(
|
|
|
1650
1348
|
}
|
|
1651
1349
|
materialShaderPipelineGroup2Contracts.set(pso as object, group2Contract);
|
|
1652
1350
|
},
|
|
1653
|
-
//
|
|
1654
|
-
// post-process channel once buildReadyWebGPU resolves the tonemap manifest
|
|
1655
|
-
// entry's composed WGSL. Fires after the manifest-load await, by which point
|
|
1656
|
-
// `renderSystem` (declared synchronously below) is defined. The 16 B params
|
|
1657
|
-
// schema mirrors the prior dedicated UBO: [exposure(f32), whitePoint(f32),
|
|
1658
|
-
// mode(u32), pad(f32)]; the extract stage bridges Camera.exposure/whitePoint/
|
|
1659
|
-
// tonemap into this channel each frame (render-system-extract.ts w13).
|
|
1351
|
+
// Register the output transform after manifest loading, once RenderSystem exists.
|
|
1660
1352
|
(source: string) => {
|
|
1661
1353
|
if (candidateBuildState !== undefined || tonemapRegistered) return;
|
|
1662
1354
|
renderSystem.registerBuiltinPostProcess(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, {
|
|
@@ -1696,7 +1388,18 @@ async function makeWebGPURenderer(
|
|
|
1696
1388
|
(sources: VolumetricFogShaderSources) => {
|
|
1697
1389
|
internals.volumetricFogShaders = sources;
|
|
1698
1390
|
},
|
|
1699
|
-
|
|
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
|
+
});
|
|
1700
1403
|
const ready: Promise<Result<void, RhiError>> = buildPipeline().then(
|
|
1701
1404
|
(state): Result<void, RhiError> => {
|
|
1702
1405
|
pipelineState = state;
|
|
@@ -1707,14 +1410,8 @@ async function makeWebGPURenderer(
|
|
|
1707
1410
|
(e: unknown): Result<void, RhiError> => {
|
|
1708
1411
|
readySettled = true;
|
|
1709
1412
|
if (e instanceof RhiError) return err(e);
|
|
1710
|
-
|
|
1711
|
-
return err(
|
|
1712
|
-
new RhiError({
|
|
1713
|
-
code: 'webgpu-runtime-error',
|
|
1714
|
-
expected: 'Renderer.initialization three-step strict-serial succeeds',
|
|
1715
|
-
hint: `pipeline build raised: ${message}`,
|
|
1716
|
-
}),
|
|
1717
|
-
);
|
|
1413
|
+
if (e instanceof PipelineSpecError) return err(rendererInitializationPipelineError(e));
|
|
1414
|
+
return err(rendererInitializationError(e));
|
|
1718
1415
|
},
|
|
1719
1416
|
);
|
|
1720
1417
|
/**
|
|
@@ -1757,33 +1454,17 @@ async function makeWebGPURenderer(
|
|
|
1757
1454
|
}
|
|
1758
1455
|
return standardEntry;
|
|
1759
1456
|
};
|
|
1760
|
-
// Shared builder context for the per-MaterialShader pipeline cache.
|
|
1761
|
-
// Built once; reused for both registered shader IDs and fallback
|
|
1762
|
-
// paths (D-3). Null when manifest is empty (Camera-only path).
|
|
1763
|
-
//
|
|
1764
|
-
// feat-20260609-hdrp-cluster-fragment-ggx M4.5 / w37 (D-10 option A):
|
|
1765
|
-
// `variantSet` parameter selects between URP `pbrPipelineLayout` (1-slot
|
|
1766
|
-
// group(2) BGL) and HDRP `hdrpPbrPipelineLayout` (7-slot group(2) BGL)
|
|
1767
|
-
// via `selectPipelineLayoutForVariant`. When undefined (legacy callers
|
|
1768
|
-
// / fallback path) the URP layout is used preserving prior behaviour.
|
|
1769
1457
|
const buildPipelineContext = (
|
|
1770
1458
|
variantSet?: string,
|
|
1771
1459
|
materialShaderId?: string,
|
|
1772
1460
|
group2Contract: PipelineGroup2Contract = 'mesh',
|
|
1773
|
-
//
|
|
1774
|
-
// when layoutKind === 'pbr-skin' the SSOT for the vertex buffer layout
|
|
1775
|
-
// is `deriveVertexBufferLayout` (vertex-attribute-layout.ts) — the same
|
|
1776
|
-
// function that the WGSL @location(N) declarations / naga reflect tests
|
|
1777
|
-
// consume. A caller passing a real `MeshAsset.attributes` produces the
|
|
1778
|
-
// exact 6-attribute / 72-byte layout the skin shader expects; passing
|
|
1779
|
-
// undefined falls into the synthetic 6-key sentinel below (the skin
|
|
1780
|
-
// path is fully determined by layoutKind, so key-presence is the only
|
|
1781
|
-
// signal `deriveVertexBufferLayout` reads — values never matter).
|
|
1461
|
+
// Skin layouts derive their vertex ABI from the shared attribute projection.
|
|
1782
1462
|
meshAttributes?: VertexAttributeMap,
|
|
1783
1463
|
vertexLayout?: string,
|
|
1784
1464
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
1785
1465
|
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
1786
1466
|
layoutKindOverride?: LayoutKind,
|
|
1467
|
+
particleInputLanes?: number,
|
|
1787
1468
|
) => {
|
|
1788
1469
|
const contractLayoutKind: LayoutKind =
|
|
1789
1470
|
group2Contract === 'cluster'
|
|
@@ -1796,18 +1477,18 @@ async function makeWebGPURenderer(
|
|
|
1796
1477
|
const layoutKind = layoutKindOverride ?? contractLayoutKind;
|
|
1797
1478
|
const usesSharedBootMaterialLayout =
|
|
1798
1479
|
materialShaderId !== undefined && isEngineOwnedMaterialShader(materialShaderId);
|
|
1799
|
-
// Built-in
|
|
1800
|
-
//
|
|
1801
|
-
// 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.
|
|
1802
1482
|
const perShaderLayout =
|
|
1803
|
-
!usesSharedBootMaterialLayout &&
|
|
1483
|
+
!usesSharedBootMaterialLayout &&
|
|
1484
|
+
layoutKind !== 'pbr-skin' &&
|
|
1485
|
+
layoutKind !== 'gpu-driven-pbr' &&
|
|
1486
|
+
layoutKind !== 'gpu-driven-skin' &&
|
|
1487
|
+
materialShaderId !== undefined
|
|
1804
1488
|
? getOrBuildPerShaderMaterialLayout(materialShaderId, undefined, layoutKind)
|
|
1805
1489
|
: null;
|
|
1806
|
-
//
|
|
1807
|
-
//
|
|
1808
|
-
// because its compact texture order may differ from standard PBR even
|
|
1809
|
-
// when it declares only a few textures. Skin / HDRP keep their dedicated
|
|
1810
|
-
// 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.
|
|
1811
1492
|
const bindingContract =
|
|
1812
1493
|
materialShaderId === undefined
|
|
1813
1494
|
? 'render-material'
|
|
@@ -1824,11 +1505,12 @@ async function makeWebGPURenderer(
|
|
|
1824
1505
|
? getOrBuildViewAndSceneDepthMaterialPipelineLayout()
|
|
1825
1506
|
: null;
|
|
1826
1507
|
const preparedMaterialLayout =
|
|
1827
|
-
bindingContract === 'render-material'
|
|
1508
|
+
(bindingContract === 'render-material' ||
|
|
1509
|
+
bindingContract === 'render-material-with-scene-depth' ||
|
|
1510
|
+
bindingContract === 'render-material-and-scene-depth') &&
|
|
1828
1511
|
materialShaderId !== undefined &&
|
|
1829
|
-
(vertexLayout
|
|
1830
|
-
|
|
1831
|
-
? getOrBuildPreparedMaterialPipelineLayout(materialShaderId)
|
|
1512
|
+
isPreparedMaterialVertexLayout(vertexLayout)
|
|
1513
|
+
? getOrBuildPreparedMaterialPipelineLayout(materialShaderId, group2Contract === 'cluster')
|
|
1832
1514
|
: null;
|
|
1833
1515
|
const materialShaderLookup =
|
|
1834
1516
|
materialShaderId === undefined
|
|
@@ -1870,64 +1552,15 @@ async function makeWebGPURenderer(
|
|
|
1870
1552
|
)
|
|
1871
1553
|
: undefined))
|
|
1872
1554
|
: undefined;
|
|
1873
|
-
const vertexBuffers
|
|
1874
|
-
vertexInputContract
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
: vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.meshGeometryMaterialInstance
|
|
1883
|
-
? MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS
|
|
1884
|
-
: vertexLayoutProjection !== undefined
|
|
1885
|
-
? toGpuVertexBufferLayouts(
|
|
1886
|
-
deriveVertexBufferLayoutFromProjection(
|
|
1887
|
-
vertexLayoutProjection,
|
|
1888
|
-
resolvedUvSetCount !== undefined
|
|
1889
|
-
? { shaderUvSetCount: resolvedUvSetCount }
|
|
1890
|
-
: undefined,
|
|
1891
|
-
),
|
|
1892
|
-
)
|
|
1893
|
-
: layoutKind === 'pbr-skin'
|
|
1894
|
-
? toGpuVertexBufferLayouts(
|
|
1895
|
-
deriveVertexBufferLayout(
|
|
1896
|
-
meshAttributes ??
|
|
1897
|
-
({
|
|
1898
|
-
position: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1899
|
-
normal: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1900
|
-
uv: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1901
|
-
tangent: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1902
|
-
skinIndex: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1903
|
-
skinWeight: PBR_SKIN_SENTINEL_ATTR_BUFFER,
|
|
1904
|
-
} satisfies VertexAttributeMap),
|
|
1905
|
-
resolvedUvSetCount !== undefined
|
|
1906
|
-
? { shaderUvSetCount: resolvedUvSetCount }
|
|
1907
|
-
: undefined,
|
|
1908
|
-
),
|
|
1909
|
-
)
|
|
1910
|
-
: meshAttributes !== undefined ||
|
|
1911
|
-
(resolvedUvSetCount !== undefined && resolvedUvSetCount > 1)
|
|
1912
|
-
? toGpuVertexBufferLayouts(
|
|
1913
|
-
deriveVertexBufferLayout(
|
|
1914
|
-
meshAttributes ?? DEFAULT_VERTEX_ATTRS,
|
|
1915
|
-
resolvedUvSetCount !== undefined && resolvedUvSetCount > 1
|
|
1916
|
-
? { shaderUvSetCount: resolvedUvSetCount }
|
|
1917
|
-
: undefined,
|
|
1918
|
-
),
|
|
1919
|
-
)
|
|
1920
|
-
: ([
|
|
1921
|
-
{
|
|
1922
|
-
arrayStride: 12 * 4,
|
|
1923
|
-
attributes: [
|
|
1924
|
-
{ shaderLocation: 0, offset: 0, format: 'float32x3' as const },
|
|
1925
|
-
{ shaderLocation: 1, offset: 3 * 4, format: 'float32x3' as const },
|
|
1926
|
-
{ shaderLocation: 2, offset: 6 * 4, format: 'float32x2' as const },
|
|
1927
|
-
{ shaderLocation: 3, offset: 8 * 4, format: 'float32x4' as const },
|
|
1928
|
-
],
|
|
1929
|
-
},
|
|
1930
|
-
] satisfies readonly GPUVertexBufferLayout[]);
|
|
1555
|
+
const vertexBuffers = resolveWebGPUVertexBufferLayouts({
|
|
1556
|
+
vertexInputContract,
|
|
1557
|
+
vertexLayout,
|
|
1558
|
+
vertexLayoutProjection,
|
|
1559
|
+
resolvedUvSetCount,
|
|
1560
|
+
layoutKind,
|
|
1561
|
+
meshAttributes,
|
|
1562
|
+
particleInputLanes,
|
|
1563
|
+
});
|
|
1931
1564
|
return {
|
|
1932
1565
|
device: currentBuildDevice(),
|
|
1933
1566
|
shaderModuleFactory:
|
|
@@ -1936,7 +1569,7 @@ async function makeWebGPURenderer(
|
|
|
1936
1569
|
: getShaderModuleAdapter(),
|
|
1937
1570
|
pipelineLayout,
|
|
1938
1571
|
vertexBuffers,
|
|
1939
|
-
...(layoutKind === 'pbr-skin' ? { layoutKind } : {}),
|
|
1572
|
+
...(layoutKind === 'pbr-skin' || layoutKind === 'gpu-driven-skin' ? { layoutKind } : {}),
|
|
1940
1573
|
};
|
|
1941
1574
|
};
|
|
1942
1575
|
/**
|
|
@@ -1996,11 +1629,20 @@ async function makeWebGPURenderer(
|
|
|
1996
1629
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
1997
1630
|
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
1998
1631
|
layoutKindOverride?: LayoutKind,
|
|
1632
|
+
vertexEntryPoint?: string,
|
|
1999
1633
|
additionalColorFormats?: readonly GPUTextureFormat[],
|
|
2000
1634
|
vertexEntry?: string,
|
|
2001
1635
|
authoredFragmentEntry?: string,
|
|
1636
|
+
particleInputLanes?: number,
|
|
2002
1637
|
): RenderPipeline | null => {
|
|
2003
|
-
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;
|
|
2004
1646
|
const ctx = buildPipelineContext(
|
|
2005
1647
|
variantSet,
|
|
2006
1648
|
materialShaderId,
|
|
@@ -2010,19 +1652,16 @@ async function makeWebGPURenderer(
|
|
|
2010
1652
|
vertexLayoutProjection,
|
|
2011
1653
|
shaderModuleMode,
|
|
2012
1654
|
layoutKindOverride,
|
|
1655
|
+
particleInputLanes,
|
|
2013
1656
|
);
|
|
2014
1657
|
if (ctx === null) return null;
|
|
2015
|
-
// bug-20260612: route the LDR color format through pipelineState so the
|
|
2016
|
-
// backend-aware swap-chain format chosen at buildReadyWebGPU flows here.
|
|
2017
|
-
// Callers gate on pipelineState !== null before reaching this lambda
|
|
2018
|
-
// (search "buildAndCachePipeline" call sites — every one is preceded by
|
|
2019
|
-
// the explicit null check).
|
|
2020
1658
|
const currentState = currentPipelineState();
|
|
2021
1659
|
if (currentState === null) return null;
|
|
2022
1660
|
const ldrColorFormat = colorFormatOverride ?? currentState.colorAttachmentFormat;
|
|
2023
1661
|
const fragmentEntry =
|
|
2024
1662
|
authoredFragmentEntry ??
|
|
2025
1663
|
(isHdr &&
|
|
1664
|
+
passKind === 'forward' &&
|
|
2026
1665
|
(materialShaderId === 'forgeax::sprite' || materialShaderId === 'forgeax::sprite-lit')
|
|
2027
1666
|
? 'fs_main_hdr'
|
|
2028
1667
|
: undefined);
|
|
@@ -2038,12 +1677,13 @@ async function makeWebGPURenderer(
|
|
|
2038
1677
|
? undefined
|
|
2039
1678
|
: [isHdr ? HDR_COLOR_ATTACHMENT_FORMAT : ldrColorFormat, ...additionalColorFormats],
|
|
2040
1679
|
depthFormat:
|
|
2041
|
-
depthFormatOverride === null
|
|
1680
|
+
depthFormatOverride === null
|
|
1681
|
+
? undefined
|
|
1682
|
+
: passKindPolicyTable[passKind]?.shape === 'depth-only'
|
|
1683
|
+
? (depthFormatOverride ?? 'depth32float')
|
|
1684
|
+
: (depthFormatOverride ?? DEPTH_TEXTURE_FORMAT),
|
|
2042
1685
|
label,
|
|
2043
1686
|
// feat-20260604 w16-b: the shader-MODULE cache identity. Stable across
|
|
2044
|
-
// topology / renderState / isHdr / indexFormat (all baked into the PSO,
|
|
2045
|
-
// not the module), so every pipeline variant of the same shader source
|
|
2046
|
-
// reuses one compiled module instead of forcing a fresh async compile
|
|
2047
1687
|
// per variant. See PipelineBuilderContext.moduleLabel.
|
|
2048
1688
|
moduleLabel,
|
|
2049
1689
|
},
|
|
@@ -2062,7 +1702,7 @@ async function makeWebGPURenderer(
|
|
|
2062
1702
|
: {}),
|
|
2063
1703
|
}
|
|
2064
1704
|
: undefined,
|
|
2065
|
-
vertexEntry,
|
|
1705
|
+
vertexEntry ?? vertexEntryPoint,
|
|
2066
1706
|
fragmentEntry,
|
|
2067
1707
|
undefined, // defines — none
|
|
2068
1708
|
passKind,
|
|
@@ -2075,10 +1715,7 @@ async function makeWebGPURenderer(
|
|
|
2075
1715
|
return null;
|
|
2076
1716
|
}
|
|
2077
1717
|
currentPipelineCacheState().materialShaderPipelineCache.set(cacheKey, built.value);
|
|
2078
|
-
materialShaderPipelineGroup2Contracts.set(
|
|
2079
|
-
built.value as object,
|
|
2080
|
-
resolvePipelineGroup2Contract(entry.source),
|
|
2081
|
-
);
|
|
1718
|
+
materialShaderPipelineGroup2Contracts.set(built.value as object, group2Contract);
|
|
2082
1719
|
return built.value;
|
|
2083
1720
|
};
|
|
2084
1721
|
const getMaterialShaderPipeline = (
|
|
@@ -2089,11 +1726,6 @@ async function makeWebGPURenderer(
|
|
|
2089
1726
|
indexFormat?: 'uint16' | 'uint32',
|
|
2090
1727
|
variantSet?: string,
|
|
2091
1728
|
passKind: PassKind = 'forward',
|
|
2092
|
-
// feat-20260611-fox-skinning-vertex-attribute-chain M4 / w16 (D-4):
|
|
2093
|
-
// forwarded to `buildAndCachePipeline -> buildPipelineContext` so the
|
|
2094
|
-
// pbr-skin layout chain reads from the `deriveVertexBufferLayout` SSOT.
|
|
2095
|
-
// Optional: existing callers (URP / HDRP / shadow-depth) pass undefined
|
|
2096
|
-
// and the hardcoded 4-attribute layout is preserved (AC-04 zero-regression).
|
|
2097
1729
|
meshAttributes?: VertexAttributeMap,
|
|
2098
1730
|
// bug-20260615 M2 / m2-1: sampleCount is threaded through to the cache key,
|
|
2099
1731
|
// buildAndCachePipeline, and ultimately buildPipelineForMaterialShader which
|
|
@@ -2128,9 +1760,11 @@ async function makeWebGPURenderer(
|
|
|
2128
1760
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2129
1761
|
shaderModuleMode: RenderFeatureShaderModuleMode = 'validated',
|
|
2130
1762
|
layoutKindOverride?: LayoutKind,
|
|
1763
|
+
vertexEntryPoint?: string,
|
|
2131
1764
|
additionalColorFormats?: readonly GPUTextureFormat[],
|
|
2132
1765
|
vertexEntry?: string,
|
|
2133
1766
|
fragmentEntry?: string,
|
|
1767
|
+
particleInputLanes?: number,
|
|
2134
1768
|
): RenderPipeline | null => {
|
|
2135
1769
|
// WebGL2 is a downlevel backend even when the record stage asks for the
|
|
2136
1770
|
// native-capability variant string. Resolve the variant axes at this
|
|
@@ -2171,20 +1805,14 @@ async function makeWebGPURenderer(
|
|
|
2171
1805
|
(pipelineShaderId === 'forgeax::sprite' || pipelineShaderId === 'forgeax::sprite-lit')
|
|
2172
1806
|
? undefined
|
|
2173
1807
|
: resolveCachedMaterialShaderVariantSet(variantSet, manifestEntry);
|
|
2174
|
-
// The record stage
|
|
2175
|
-
//
|
|
2176
|
-
//
|
|
2177
|
-
// canonical HDRP variant key and would pair an HDRP PSO with the URP mesh
|
|
2178
|
-
// 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
|
|
2179
1811
|
// when a reduced test/legacy manifest omits variant metadata.
|
|
2180
1812
|
// A single-source manifest entry must stay on its canonical module key;
|
|
2181
1813
|
// otherwise a synthesized capability axis races the prewarmed module.
|
|
2182
1814
|
let effectiveVariantSet = normalizeMaterialShaderVariantSet(resolvedVariantSet, manifestEntry);
|
|
2183
|
-
//
|
|
2184
|
-
// cooked artifact, not from the build-time material-shader manifest. The
|
|
2185
|
-
// published artifact identity already selects the backend-specific bytes;
|
|
2186
|
-
// an absent manifest therefore means the source itself is the canonical
|
|
2187
|
-
// pipeline module and must not carry a synthesized capability key.
|
|
1815
|
+
// A published artifact is already backend-specific; no manifest means no synthesized key.
|
|
2188
1816
|
if (manifestEntry === undefined) effectiveVariantSet = undefined;
|
|
2189
1817
|
if (
|
|
2190
1818
|
vertexLayoutProjection === undefined &&
|
|
@@ -2223,25 +1851,16 @@ async function makeWebGPURenderer(
|
|
|
2223
1851
|
effectiveVariantSet =
|
|
2224
1852
|
filteredVariantParts.length === 0 ? undefined : filteredVariantParts.join('+');
|
|
2225
1853
|
}
|
|
2226
|
-
// feat-20260615-pipeline-spec-ssot M2-T2: cache key derived from PipelineSpec
|
|
2227
|
-
// 4-axis SSOT via cacheKeyOf(spec). The spec carries all 4 axes (shader /
|
|
2228
|
-
// attachments / geometry / renderState), replacing the legacy 8-segment string
|
|
2229
|
-
// construction. vertexLayout is now included in the cache key (was previously
|
|
2230
|
-
// threaded but not hashed — research F1 R-VertexLayout-Cache). sampleCount is
|
|
2231
|
-
// cast to 1 | 4 for the closed spec axis.
|
|
2232
|
-
// M2-fixup follow-up: LDR color format SSOT is `pipelineState.colorAttachmentFormat`
|
|
2233
|
-
// (set in buildReadyWebGPU from selectSwapChainFormat). Hardcoding 'bgra8unorm-srgb'
|
|
2234
|
-
// here let the cache key drift from the prewarm key on backends where
|
|
2235
|
-
// getPreferredCanvasFormat returns rgba8unorm (dawn-node, lavapipe, wgpu-wasm GLES),
|
|
2236
|
-
// forcing a redundant second PSO build on first-frame URP record path.
|
|
2237
1854
|
const colorFormat: GPUTextureFormat = isHdr ? 'rgba16float' : ldrColorFormat;
|
|
2238
1855
|
const isDepthOnlyPass = passKindPolicyTable[passKind]?.shape === 'depth-only';
|
|
1856
|
+
const resolvedVertexEntry = vertexEntry ?? vertexEntryPoint;
|
|
1857
|
+
const inputVertexBuffers = particleMaterialInputVertexBuffers(vertexLayout, particleInputLanes);
|
|
2239
1858
|
const spec: PipelineSpec = {
|
|
2240
1859
|
shader: {
|
|
2241
1860
|
id: pipelineShaderId,
|
|
2242
1861
|
passKind,
|
|
2243
1862
|
variantSet: effectiveVariantSet,
|
|
2244
|
-
...(
|
|
1863
|
+
...(resolvedVertexEntry === undefined ? {} : { vertexEntry: resolvedVertexEntry }),
|
|
2245
1864
|
...(fragmentEntry === undefined ? {} : { fragmentEntry }),
|
|
2246
1865
|
},
|
|
2247
1866
|
attachments: {
|
|
@@ -2250,7 +1869,7 @@ async function makeWebGPURenderer(
|
|
|
2250
1869
|
? colorFormatsForPassKind(passKind, colorFormat)
|
|
2251
1870
|
: [colorFormat, ...additionalColorFormats],
|
|
2252
1871
|
depthFormat: isDepthOnlyPass
|
|
2253
|
-
? 'depth32float'
|
|
1872
|
+
? (depthFormatOverride ?? 'depth32float')
|
|
2254
1873
|
: depthFormatOverride === null
|
|
2255
1874
|
? undefined
|
|
2256
1875
|
: (depthFormatOverride ?? 'depth24plus-stencil8'),
|
|
@@ -2263,11 +1882,9 @@ async function makeWebGPURenderer(
|
|
|
2263
1882
|
meshAttributes ??
|
|
2264
1883
|
(vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.positionSizeColorInstance
|
|
2265
1884
|
? PREPARED_INSTANCE_VERTEX_ATTRS
|
|
2266
|
-
: vertexLayout
|
|
2267
|
-
vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.topologySegmentInstance ||
|
|
2268
|
-
vertexLayout === RENDER_FEATURE_VERTEX_LAYOUTS.meshGeometryMaterialInstance
|
|
1885
|
+
: isPreparedMaterialVertexLayout(vertexLayout)
|
|
2269
1886
|
? PREPARED_MATERIAL_INSTANCE_VERTEX_ATTRS
|
|
2270
|
-
:
|
|
1887
|
+
: DEFAULT_VERTEX_ATTRIBUTE_MAP),
|
|
2271
1888
|
// Thread the shader-declared count whenever it is greater than one.
|
|
2272
1889
|
// Built-in PBR reserves all eight supported UV inputs; single-UV meshes
|
|
2273
1890
|
// remain byte-stable because missing sets are clamp-to-last aliases.
|
|
@@ -2275,10 +1892,11 @@ async function makeWebGPURenderer(
|
|
|
2275
1892
|
? { shaderUvSetCount: resolvedUvSetCount }
|
|
2276
1893
|
: {}),
|
|
2277
1894
|
...(vertexLayoutProjection === undefined ? {} : { vertexLayoutProjection }),
|
|
1895
|
+
...(inputVertexBuffers === undefined ? {} : { vertexBuffers: inputVertexBuffers }),
|
|
2278
1896
|
},
|
|
2279
1897
|
renderState,
|
|
2280
1898
|
};
|
|
2281
|
-
const cacheKey = cacheKeyOf(spec)
|
|
1899
|
+
const cacheKey = `${cacheKeyOf(spec)}|vertex:${resolvedVertexEntry ?? 'vs_main'}`;
|
|
2282
1900
|
const cached = currentPipelineCacheState().materialShaderPipelineCache.get(cacheKey);
|
|
2283
1901
|
if (cached !== undefined) return cached;
|
|
2284
1902
|
if (pipelineState === null) return null;
|
|
@@ -2332,6 +1950,7 @@ async function makeWebGPURenderer(
|
|
|
2332
1950
|
vertexLayoutProjection,
|
|
2333
1951
|
shaderModuleMode,
|
|
2334
1952
|
layoutKindOverride,
|
|
1953
|
+
vertexEntryPoint,
|
|
2335
1954
|
additionalColorFormats,
|
|
2336
1955
|
);
|
|
2337
1956
|
}
|
|
@@ -2388,6 +2007,7 @@ async function makeWebGPURenderer(
|
|
|
2388
2007
|
vertexLayoutProjection,
|
|
2389
2008
|
shaderModuleMode,
|
|
2390
2009
|
layoutKindOverride,
|
|
2010
|
+
vertexEntryPoint,
|
|
2391
2011
|
additionalColorFormats,
|
|
2392
2012
|
);
|
|
2393
2013
|
}
|
|
@@ -2481,9 +2101,11 @@ async function makeWebGPURenderer(
|
|
|
2481
2101
|
vertexLayoutProjection,
|
|
2482
2102
|
shaderModuleMode,
|
|
2483
2103
|
layoutKindOverride,
|
|
2104
|
+
vertexEntryPoint,
|
|
2484
2105
|
additionalColorFormats,
|
|
2485
2106
|
vertexEntry,
|
|
2486
2107
|
fragmentEntry,
|
|
2108
|
+
particleInputLanes,
|
|
2487
2109
|
);
|
|
2488
2110
|
};
|
|
2489
2111
|
const getMaterialShaderPipelineEntry = (
|
|
@@ -2545,7 +2167,7 @@ async function makeWebGPURenderer(
|
|
|
2545
2167
|
const buildPostProcessPipeline = (
|
|
2546
2168
|
entry: PostProcessShaderEntry,
|
|
2547
2169
|
bgl: BindGroupLayout,
|
|
2548
|
-
|
|
2170
|
+
colorFormats: readonly GPUTextureFormat[],
|
|
2549
2171
|
label: string,
|
|
2550
2172
|
): RenderPipeline | null => {
|
|
2551
2173
|
const moduleFactory = getShaderModuleAdapter();
|
|
@@ -2593,12 +2215,12 @@ async function makeWebGPURenderer(
|
|
|
2593
2215
|
},
|
|
2594
2216
|
fragment: {
|
|
2595
2217
|
module: moduleResult.value,
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
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 })),
|
|
2602
2224
|
},
|
|
2603
2225
|
primitive: { topology: 'triangle-list', cullMode: 'none', frontFace: 'ccw' },
|
|
2604
2226
|
depthStencil: undefined,
|
|
@@ -2629,10 +2251,7 @@ async function makeWebGPURenderer(
|
|
|
2629
2251
|
setFeatureHost: (host) => {
|
|
2630
2252
|
internals.featureHost = host;
|
|
2631
2253
|
},
|
|
2632
|
-
//
|
|
2633
|
-
// call time. Renderer recovery swaps the device and invalidates the
|
|
2634
|
-
// per-device adapter; a boot-time snapshot would feed recovered VFX
|
|
2635
|
-
// programs shader modules owned by the lost device.
|
|
2254
|
+
// Resolve the adapter at call time so recovered devices get fresh modules.
|
|
2636
2255
|
shaderModuleFactory: {
|
|
2637
2256
|
createShaderModule: (descriptor) => getShaderModuleAdapter().createShaderModule(descriptor),
|
|
2638
2257
|
},
|
|
@@ -2662,6 +2281,28 @@ async function makeWebGPURenderer(
|
|
|
2662
2281
|
...(internals.pack.instrumentation?.resolveSurfaceDevice === undefined
|
|
2663
2282
|
? {}
|
|
2664
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
|
+
},
|
|
2665
2306
|
invalidateShaderModule: (label: string) => {
|
|
2666
2307
|
activeShaderState.sharedShaderModuleAdapter?.invalidateModule(label);
|
|
2667
2308
|
},
|
|
@@ -2688,10 +2329,15 @@ async function makeWebGPURenderer(
|
|
|
2688
2329
|
getMaterialShaderPipeline,
|
|
2689
2330
|
getMaterialShaderPipelineEntry,
|
|
2690
2331
|
getMaterialShaderBindingContract: getCachedMaterialShaderBindingContract,
|
|
2332
|
+
getMaterialShaderArtifact: (materialShaderId, vertexColorAvailable = false) =>
|
|
2333
|
+
resolveStandardPbrProgram(
|
|
2334
|
+
pipelineState?.gpuDrivenPbrPrograms,
|
|
2335
|
+
materialShaderId,
|
|
2336
|
+
vertexColorAvailable,
|
|
2337
|
+
)?.artifact,
|
|
2691
2338
|
getParamSchema,
|
|
2692
2339
|
getMaterialBindGroupLayout,
|
|
2693
2340
|
metrics,
|
|
2694
|
-
// feat-20260609 M4 / T-10-a: post-process pipeline factory (CONCERN-1 fix).
|
|
2695
2341
|
// createRenderSystem wraps this in a per-RenderSystem cache + the public
|
|
2696
2342
|
// getPostProcessPipeline lookup the dispatcher reads at frame time.
|
|
2697
2343
|
buildPostProcessPipeline,
|
|
@@ -2782,6 +2428,13 @@ async function makeWebGPURenderer(
|
|
|
2782
2428
|
get volumetricFogShaders() {
|
|
2783
2429
|
return internals.volumetricFogShaders;
|
|
2784
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
|
+
},
|
|
2785
2438
|
});
|
|
2786
2439
|
attachGpuPassTimingSession(internals, gpuPassTimingSession);
|
|
2787
2440
|
renderSystemForTargetPromotion = renderSystem;
|
|
@@ -2805,6 +2458,13 @@ async function makeWebGPURenderer(
|
|
|
2805
2458
|
const attachedWorlds = new Set<World>();
|
|
2806
2459
|
const attachedLeases = new Map<RenderReadLease, World>();
|
|
2807
2460
|
const leasesByWorld = new Map<World, RenderReadLease>();
|
|
2461
|
+
const { lifecycle: dynamicGeometry, host: dynamicGeometryHost } =
|
|
2462
|
+
createRendererDynamicGeometryController({
|
|
2463
|
+
attachedWorlds,
|
|
2464
|
+
getGpuStore: () => gpuStore,
|
|
2465
|
+
currentGeneration: () => activeDeviceScope.generation,
|
|
2466
|
+
renderSystem,
|
|
2467
|
+
});
|
|
2808
2468
|
const derivedSystemNames = new Map<World, string>();
|
|
2809
2469
|
const transformReleases = new Map<World, () => void>();
|
|
2810
2470
|
const attachmentOwner = {};
|
|
@@ -2816,9 +2476,184 @@ async function makeWebGPURenderer(
|
|
|
2816
2476
|
const issuedReceipts = new WeakSet<FrameReceipt>();
|
|
2817
2477
|
const receiptTimings = new WeakMap<FrameReceipt, Promise<VolumeTimingObservation>>();
|
|
2818
2478
|
let latestReceipt: FrameReceipt | undefined;
|
|
2819
|
-
let
|
|
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
|
+
};
|
|
2820
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
|
+
};
|
|
2654
|
+
let surfaceReleased = false;
|
|
2821
2655
|
const renderer: RendererAssemblyImplementation = {
|
|
2656
|
+
instances: renderSystem.instances,
|
|
2822
2657
|
attach(world: World): RenderResult<RenderWorldLease, RenderError> {
|
|
2823
2658
|
const attached = this.attachScene(world);
|
|
2824
2659
|
if (!attached.ok) {
|
|
@@ -2829,6 +2664,7 @@ async function makeWebGPURenderer(
|
|
|
2829
2664
|
leasesByWorld.set(world, lease);
|
|
2830
2665
|
return ok(lease);
|
|
2831
2666
|
},
|
|
2667
|
+
...dynamicGeometryHost,
|
|
2832
2668
|
createRenderTarget: (descriptor) => renderTargetHost.createRenderTarget(descriptor),
|
|
2833
2669
|
resizeRenderTarget: (target, descriptor) =>
|
|
2834
2670
|
renderTargetHost.resizeRenderTarget(target, descriptor),
|
|
@@ -2868,6 +2704,7 @@ async function makeWebGPURenderer(
|
|
|
2868
2704
|
gpuDriven: renderSystem.gpuDrivenInspection,
|
|
2869
2705
|
};
|
|
2870
2706
|
},
|
|
2707
|
+
bounds: (world, entity) => (disposed ? undefined : renderSystem.bounds(world, entity)),
|
|
2871
2708
|
inspect(): RenderInspection {
|
|
2872
2709
|
const health = internals.healthRegistry.getLastSnapshot();
|
|
2873
2710
|
const deviceLost = health.reason === 'device-lost';
|
|
@@ -2908,6 +2745,8 @@ async function makeWebGPURenderer(
|
|
|
2908
2745
|
? {}
|
|
2909
2746
|
: { surfaceProfile: pipelineState.surfaceProfile }),
|
|
2910
2747
|
rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
|
|
2748
|
+
autoExposure: renderSystem.autoExposure,
|
|
2749
|
+
standardLut: renderSystem.standardLut,
|
|
2911
2750
|
...(internals.context?.presentationProof === undefined
|
|
2912
2751
|
? {}
|
|
2913
2752
|
: { presentationProof: internals.context.presentationProof }),
|
|
@@ -2925,16 +2764,18 @@ async function makeWebGPURenderer(
|
|
|
2925
2764
|
frameId,
|
|
2926
2765
|
deviceGeneration: activeDeviceScope.generation,
|
|
2927
2766
|
}),
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
featureDiagnostics: internals.featureHost?.diagnostics() ?? Object.freeze([]),
|
|
2767
|
+
...projectRendererFeatureInspection(internals.featureHost),
|
|
2768
|
+
featureGraph: renderSystem.featureGraphInspection,
|
|
2769
|
+
materialTextureSources: renderSystem.materialTextureSources,
|
|
2932
2770
|
frustumStats: Object.freeze({ ...renderSystem.frustumStats }),
|
|
2933
2771
|
visibilityStats: Object.freeze({ ...renderSystem.visibilityStats }),
|
|
2934
2772
|
instanceCollections: Object.freeze(
|
|
2935
2773
|
renderSystem.instanceCollectionsInspection.map((collection) =>
|
|
2936
2774
|
Object.freeze({
|
|
2937
2775
|
...collection,
|
|
2776
|
+
dirtyRanges: Object.freeze(
|
|
2777
|
+
collection.dirtyRanges.map((range) => Object.freeze({ ...range })),
|
|
2778
|
+
),
|
|
2938
2779
|
uploadRanges: Object.freeze(
|
|
2939
2780
|
collection.uploadRanges.map((range) => Object.freeze({ ...range })),
|
|
2940
2781
|
),
|
|
@@ -2949,9 +2790,11 @@ async function makeWebGPURenderer(
|
|
|
2949
2790
|
}),
|
|
2950
2791
|
),
|
|
2951
2792
|
),
|
|
2793
|
+
dynamicGeometry: dynamicGeometry.inspect(),
|
|
2952
2794
|
renderScene: renderSystem.renderScene,
|
|
2953
2795
|
reflectionProbes: renderSystem.reflectionProbes,
|
|
2954
2796
|
ssrDependencies: renderSystem.ssrDependencies,
|
|
2797
|
+
ssr: renderSystem.ssr,
|
|
2955
2798
|
volumetricFog,
|
|
2956
2799
|
environment: renderSystem.environment,
|
|
2957
2800
|
temporal: renderSystem.temporal,
|
|
@@ -3005,7 +2848,13 @@ async function makeWebGPURenderer(
|
|
|
3005
2848
|
lastGeneration: latestReceipt?.deviceGeneration,
|
|
3006
2849
|
}),
|
|
3007
2850
|
}),
|
|
3008
|
-
|
|
2851
|
+
output: Object.freeze({
|
|
2852
|
+
...outputInspection,
|
|
2853
|
+
graphPassNames: Object.freeze([...outputInspection.graphPassNames]),
|
|
2854
|
+
...(outputInspection.standardOutputColor === undefined
|
|
2855
|
+
? {}
|
|
2856
|
+
: { standardOutputColor: Object.freeze({ ...outputInspection.standardOutputColor }) }),
|
|
2857
|
+
}),
|
|
3009
2858
|
observation: Object.freeze({
|
|
3010
2859
|
observationId,
|
|
3011
2860
|
frameId,
|
|
@@ -3020,6 +2869,14 @@ async function makeWebGPURenderer(
|
|
|
3020
2869
|
...(outputInspection.standardOutputColor === undefined
|
|
3021
2870
|
? {}
|
|
3022
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
|
+
}),
|
|
3023
2880
|
}),
|
|
3024
2881
|
});
|
|
3025
2882
|
},
|
|
@@ -3092,6 +2949,7 @@ async function makeWebGPURenderer(
|
|
|
3092
2949
|
},
|
|
3093
2950
|
detachScene(world: World): void {
|
|
3094
2951
|
if (!attachedWorlds.delete(world)) return;
|
|
2952
|
+
dynamicGeometryHost.invalidateDynamicGeometryWorld(world);
|
|
3095
2953
|
for (const [lease, leaseWorld] of attachedLeases) {
|
|
3096
2954
|
if (leaseWorld === world) {
|
|
3097
2955
|
lease.dispose();
|
|
@@ -3111,6 +2969,38 @@ async function makeWebGPURenderer(
|
|
|
3111
2969
|
observeCurrentFrame(options: FrameObservationOptions) {
|
|
3112
2970
|
return renderSystem.observeCurrentFrame(options);
|
|
3113
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
|
+
},
|
|
3114
3004
|
getCurrentGraphTarget(name: string) {
|
|
3115
3005
|
return renderSystem.getCurrentGraphTarget(name);
|
|
3116
3006
|
},
|
|
@@ -3293,6 +3183,8 @@ async function makeWebGPURenderer(
|
|
|
3293
3183
|
: worlds.map((world) => leasesByWorld.get(world)).every((lease) => lease !== undefined)
|
|
3294
3184
|
? worlds.map((world) => leasesByWorld.get(world) as RenderReadLease)
|
|
3295
3185
|
: undefined;
|
|
3186
|
+
const physicsFrameError = derivedPhysicsFrameError(worlds);
|
|
3187
|
+
if (physicsFrameError !== undefined) return err(physicsFrameError);
|
|
3296
3188
|
if (frameRequest !== undefined && worlds.length !== frameRequest.leases.length) {
|
|
3297
3189
|
return err(
|
|
3298
3190
|
new RendererOperationError('world-lease-invalid', {
|
|
@@ -3328,6 +3220,9 @@ async function makeWebGPURenderer(
|
|
|
3328
3220
|
? {
|
|
3329
3221
|
cameraOwner,
|
|
3330
3222
|
resourceOwner,
|
|
3223
|
+
...(frameRequest.camera.entityKey === undefined
|
|
3224
|
+
? {}
|
|
3225
|
+
: { cameraEntityKey: frameRequest.camera.entityKey }),
|
|
3331
3226
|
...(frameRequest.profileFrame === undefined
|
|
3332
3227
|
? {}
|
|
3333
3228
|
: { profileFrame: frameRequest.profileFrame }),
|
|
@@ -3445,6 +3340,9 @@ async function makeWebGPURenderer(
|
|
|
3445
3340
|
gpuPassTimingCapture?: GpuPassTimingCapture | undefined;
|
|
3446
3341
|
gpuPassTimingSubmittedWork?: Promise<void> | undefined;
|
|
3447
3342
|
gpuPassTimingBeginReason?: GpuPassTimingReason | undefined;
|
|
3343
|
+
observationFrameId?: number | undefined;
|
|
3344
|
+
observationGraphGeneration?: number | undefined;
|
|
3345
|
+
observationCaptureDomains?: readonly FrameObservationDomain[] | undefined;
|
|
3448
3346
|
};
|
|
3449
3347
|
const timingRequested =
|
|
3450
3348
|
isFrameRequest &&
|
|
@@ -3459,6 +3357,9 @@ async function makeWebGPURenderer(
|
|
|
3459
3357
|
if (createdTiming.ok) volumeTimingCapture = createdTiming.value;
|
|
3460
3358
|
}
|
|
3461
3359
|
if (isFrameRequest) {
|
|
3360
|
+
timingHost.observationFrameId = frameId + 1;
|
|
3361
|
+
timingHost.observationGraphGeneration = undefined;
|
|
3362
|
+
timingHost.observationCaptureDomains = pendingObservationDomains;
|
|
3462
3363
|
timingHost.gpuPassTimingFrameIdentity = {
|
|
3463
3364
|
frameId: frameId + 1,
|
|
3464
3365
|
deviceGeneration: activeDeviceScope.generation,
|
|
@@ -3466,9 +3367,13 @@ async function makeWebGPURenderer(
|
|
|
3466
3367
|
};
|
|
3467
3368
|
}
|
|
3468
3369
|
const submitted = renderSystem.draw(worlds, drawOptions, readLeases, volumeTimingCapture);
|
|
3370
|
+
timingHost.observationFrameId = undefined;
|
|
3371
|
+
timingHost.observationCaptureDomains = undefined;
|
|
3469
3372
|
timingHost.gpuPassTimingFrameIdentity = undefined;
|
|
3470
3373
|
if (!submitted) {
|
|
3374
|
+
const cleanupFailure = disposeOwnedObservationCaptures(observationCaptureOwner.drain());
|
|
3471
3375
|
volumeTimingCapture?.discard();
|
|
3376
|
+
if (cleanupFailure !== undefined) return err(cleanupFailure);
|
|
3472
3377
|
return err(
|
|
3473
3378
|
new RendererContractFailureError(
|
|
3474
3379
|
'draw',
|
|
@@ -3479,14 +3384,17 @@ async function makeWebGPURenderer(
|
|
|
3479
3384
|
const timingCompletion = timingHost.gpuPassTimingSubmittedWork;
|
|
3480
3385
|
timingHost.gpuPassTimingSubmittedWork = undefined;
|
|
3481
3386
|
const reflectionFallbackCompletion = renderSystem.reflectionFallbackCompletion;
|
|
3387
|
+
const queueCompletion = timingCompletion ?? internals.device.queue.onSubmittedWorkDone();
|
|
3388
|
+
gpuStore.trackMeshSubmission(queueCompletion);
|
|
3482
3389
|
internals.pack.instrumentation?.onFrameBoundary?.();
|
|
3483
3390
|
if (!isFrameRequest) {
|
|
3391
|
+
const cleanupFailure = disposeOwnedObservationCaptures(observationCaptureOwner.drain());
|
|
3484
3392
|
renderTargetHost.onFrameSubmitted();
|
|
3393
|
+
if (cleanupFailure !== undefined) return err(cleanupFailure);
|
|
3485
3394
|
return ok(undefined);
|
|
3486
3395
|
}
|
|
3487
3396
|
const receiptFrameId = ++frameId;
|
|
3488
3397
|
const receiptGeneration = activeDeviceScope.generation;
|
|
3489
|
-
const queueCompletion = timingCompletion ?? internals.device.queue.onSubmittedWorkDone();
|
|
3490
3398
|
const completion: FrameReceipt['completed'] = Promise.all([
|
|
3491
3399
|
queueCompletion,
|
|
3492
3400
|
reflectionFallbackCompletion ?? Promise.resolve(),
|
|
@@ -3557,13 +3465,39 @@ async function makeWebGPURenderer(
|
|
|
3557
3465
|
]).finally(() => {
|
|
3558
3466
|
frameContinuations.delete(continuation);
|
|
3559
3467
|
});
|
|
3468
|
+
const captures = observationCaptureOwner.consume(receiptFrameId);
|
|
3469
|
+
pendingObservationDomains = undefined;
|
|
3470
|
+
const receiptGraphGeneration = timingHost.observationGraphGeneration ?? 0;
|
|
3560
3471
|
const receipt = Object.freeze({
|
|
3561
3472
|
frameId: receiptFrameId,
|
|
3562
3473
|
deviceGeneration: receiptGeneration,
|
|
3474
|
+
backendId: internals.device.caps.backendKind,
|
|
3475
|
+
graphGeneration: receiptGraphGeneration,
|
|
3563
3476
|
completed,
|
|
3564
3477
|
});
|
|
3565
3478
|
renderTargetHost.onFrameSubmitted(completed);
|
|
3566
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
|
+
}
|
|
3567
3501
|
const passTimingCapture = timingHost.gpuPassTimingCapture;
|
|
3568
3502
|
const timingBeginReason = timingHost.gpuPassTimingBeginReason;
|
|
3569
3503
|
const timingSource =
|
|
@@ -3608,8 +3542,14 @@ async function makeWebGPURenderer(
|
|
|
3608
3542
|
);
|
|
3609
3543
|
}
|
|
3610
3544
|
issuedReceipts.add(receipt);
|
|
3545
|
+
dynamicGeometryHost.publishDynamicGeometry(receipt, worlds, frameRequest?.fixedStep);
|
|
3611
3546
|
return ok(receipt);
|
|
3612
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);
|
|
3613
3553
|
const error =
|
|
3614
3554
|
cause instanceof Error
|
|
3615
3555
|
? { code: 'unknown' as const, message: cause.message, name: cause.name }
|
|
@@ -3629,7 +3569,28 @@ async function makeWebGPURenderer(
|
|
|
3629
3569
|
receipt: FrameReceipt,
|
|
3630
3570
|
request: FrameObservationRequest,
|
|
3631
3571
|
): Promise<RenderResult<FrameReceiptObservation, RenderError>> {
|
|
3632
|
-
|
|
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) {
|
|
3633
3594
|
return timingObservationStore === undefined
|
|
3634
3595
|
? observeGpuPassTimingDisabled(receipt, request, () => activeDeviceScope.generation)
|
|
3635
3596
|
: timingObservationStore.observe(receipt, request);
|
|
@@ -3637,7 +3598,8 @@ async function makeWebGPURenderer(
|
|
|
3637
3598
|
if (
|
|
3638
3599
|
!currentReceipts.has(receipt) ||
|
|
3639
3600
|
!issuedReceipts.has(receipt) ||
|
|
3640
|
-
receipt.deviceGeneration !== activeDeviceScope.generation
|
|
3601
|
+
receipt.deviceGeneration !== activeDeviceScope.generation ||
|
|
3602
|
+
receipt.backendId !== internals.device.caps.backendKind
|
|
3641
3603
|
) {
|
|
3642
3604
|
const currentGeneration = activeDeviceScope.generation;
|
|
3643
3605
|
return err(
|
|
@@ -3660,6 +3622,97 @@ async function makeWebGPURenderer(
|
|
|
3660
3622
|
}),
|
|
3661
3623
|
);
|
|
3662
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
|
+
}
|
|
3663
3716
|
await renderSystem.observeLodOcclusion();
|
|
3664
3717
|
const targetReadbacks =
|
|
3665
3718
|
request.targetReadbacks === undefined || request.targetReadbacks.length === 0
|
|
@@ -3681,7 +3734,9 @@ async function makeWebGPURenderer(
|
|
|
3681
3734
|
...(targetReadbacks === undefined
|
|
3682
3735
|
? {}
|
|
3683
3736
|
: { targetReadbacks: Object.freeze(targetReadbacks.value) }),
|
|
3737
|
+
...(timingObservation === undefined ? {} : { timings: timingObservation }),
|
|
3684
3738
|
...(volumeTimings === undefined ? {} : { volumeTimings }),
|
|
3739
|
+
...(observations === undefined ? {} : { observations }),
|
|
3685
3740
|
}),
|
|
3686
3741
|
);
|
|
3687
3742
|
},
|
|
@@ -3756,11 +3811,17 @@ async function makeWebGPURenderer(
|
|
|
3756
3811
|
dispose(): RenderResult<void, RenderError> {
|
|
3757
3812
|
if (disposed) return ok(undefined);
|
|
3758
3813
|
disposed = true;
|
|
3814
|
+
for (const world of attachedWorlds) dynamicGeometryHost.invalidateDynamicGeometryWorld(world);
|
|
3815
|
+
dynamicGeometry.dispose();
|
|
3759
3816
|
for (const continuation of frameContinuations) {
|
|
3760
3817
|
continuation.terminate({ code: 'disposed' });
|
|
3761
3818
|
}
|
|
3762
3819
|
currentReceipts.clear();
|
|
3763
3820
|
const cleanupFailures: RendererOperationCause[] = [];
|
|
3821
|
+
const observationCleanupFailure = disposeReceiptObservationCaptures();
|
|
3822
|
+
if (observationCleanupFailure !== undefined) {
|
|
3823
|
+
cleanupFailures.push(observationCleanupFailure);
|
|
3824
|
+
}
|
|
3764
3825
|
for (const world of attachedWorlds) {
|
|
3765
3826
|
try {
|
|
3766
3827
|
const derivedSystemName = derivedSystemNames.get(world);
|
|
@@ -3909,6 +3970,10 @@ async function makeWebGPURenderer(
|
|
|
3909
3970
|
return internals.healthRegistry.getLastSnapshot();
|
|
3910
3971
|
},
|
|
3911
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
|
+
}
|
|
3912
3977
|
return recoveryFlight.run();
|
|
3913
3978
|
},
|
|
3914
3979
|
};
|
|
@@ -3926,7 +3991,6 @@ async function makeWebGPURenderer(
|
|
|
3926
3991
|
candidatePostProcessResources: RecoveryPostProcessResources,
|
|
3927
3992
|
recoveryRootBundle: RecoveryRootBundle,
|
|
3928
3993
|
recoveryGraphCandidate: RecoveryGraphCandidate | undefined,
|
|
3929
|
-
recoveryColdWorkGuard: RecoveryColdWorkGuard,
|
|
3930
3994
|
): void => {
|
|
3931
3995
|
if (!candidate.scope.isAlive()) {
|
|
3932
3996
|
throw new Error('Renderer generation candidate is not publishable.');
|
|
@@ -3973,9 +4037,6 @@ async function makeWebGPURenderer(
|
|
|
3973
4037
|
// fallback pipeline or residency entry. Candidate preparation populated
|
|
3974
4038
|
// these caches already; a miss here is a hard recovery failure, not an
|
|
3975
4039
|
// opportunity to hide cold work inside the first visible frame.
|
|
3976
|
-
candidate.producerBindings.gpuStore.setRecoveryColdWorkGuard(recoveryColdWorkGuard);
|
|
3977
|
-
internals.recoveryColdWorkGuard = recoveryColdWorkGuard;
|
|
3978
|
-
if (recoveryGraphCandidate !== undefined) recoveryColdWorkGuard.arm();
|
|
3979
4040
|
renderSystem.publishRecoveryPostProcessResources(candidatePostProcessResources);
|
|
3980
4041
|
if (recoveryGraphCandidate !== undefined) {
|
|
3981
4042
|
renderSystem.publishRecoveryGraphCandidate(recoveryGraphCandidate);
|