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