@forgeax/engine-render 0.1.6 → 0.1.19
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 +279 -2
- package/dist/.tsbuildinfo +1 -1
- package/dist/assembly/backend-contract.d.ts +12 -0
- package/dist/assembly/backend-contract.d.ts.map +1 -1
- package/dist/assembly/factory.d.ts +19 -2
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +4 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/renderer-frame-transaction.d.ts +39 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -0
- package/dist/assembly/renderer-inspection.d.ts +22 -0
- package/dist/assembly/renderer-inspection.d.ts.map +1 -0
- package/dist/assembly/renderer-lifecycle.d.ts +4 -0
- package/dist/assembly/renderer-lifecycle.d.ts.map +1 -0
- package/dist/authoring.d.ts +1 -0
- package/dist/authoring.d.ts.map +1 -1
- package/dist/authoring.mjs +4 -2
- package/dist/authoring.mjs.map +1 -1
- package/dist/chunk-FJ6P52EE.mjs +46 -0
- package/dist/chunk-FJ6P52EE.mjs.map +1 -0
- package/dist/chunk-IPXCABEP.mjs +73 -0
- package/dist/chunk-IPXCABEP.mjs.map +1 -0
- package/dist/chunk-JDWARUOI.mjs +44 -0
- package/dist/chunk-JDWARUOI.mjs.map +1 -0
- package/dist/chunk-LOSO4CTV.mjs +901 -0
- package/dist/chunk-LOSO4CTV.mjs.map +1 -0
- package/dist/chunk-PCHBPYML.mjs +385 -0
- package/dist/chunk-PCHBPYML.mjs.map +1 -0
- package/dist/chunk-RJRUS55L.mjs +9323 -0
- package/dist/chunk-RJRUS55L.mjs.map +1 -0
- package/dist/{chunk-QC4VW3XJ.mjs → chunk-RXZ3RGOU.mjs} +4 -30
- package/dist/chunk-RXZ3RGOU.mjs.map +1 -0
- package/dist/{chunk-CQXZC5XI.mjs → chunk-VFPLTKCU.mjs} +79 -69
- package/dist/chunk-VFPLTKCU.mjs.map +1 -0
- package/dist/{chunk-SCRSAK5B.mjs → chunk-YTQ5C7YQ.mjs} +231 -127
- package/dist/chunk-YTQ5C7YQ.mjs.map +1 -0
- package/dist/components/atmosphere.d.ts +9 -0
- package/dist/components/atmosphere.d.ts.map +1 -0
- package/dist/components/camera.d.ts +2 -1
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/fog.d.ts +8 -0
- package/dist/components/fog.d.ts.map +1 -0
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/lines.d.ts +5 -0
- package/dist/components/lines.d.ts.map +1 -0
- package/dist/components/motion-blur.d.ts +7 -0
- package/dist/components/motion-blur.d.ts.map +1 -0
- package/dist/components/points.d.ts +15 -0
- package/dist/components/points.d.ts.map +1 -0
- package/dist/construct-renderer.d.ts +2 -2
- package/dist/construct-renderer.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +11419 -16442
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts +4 -0
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/environment/frame.d.ts +60 -0
- package/dist/environment/frame.d.ts.map +1 -0
- package/dist/environment/generation.d.ts +21 -0
- package/dist/environment/generation.d.ts.map +1 -0
- package/dist/environment/graph.d.ts +4 -0
- package/dist/environment/graph.d.ts.map +1 -0
- package/dist/environment/inspection.d.ts +48 -0
- package/dist/environment/inspection.d.ts.map +1 -0
- package/dist/environment/lifecycle.d.ts +39 -0
- package/dist/environment/lifecycle.d.ts.map +1 -0
- package/dist/environment/signature.d.ts +25 -0
- package/dist/environment/signature.d.ts.map +1 -0
- package/dist/errors/render.d.ts +217 -1
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts +5 -0
- package/dist/extract/camera.d.ts.map +1 -0
- package/dist/extract/environment.d.ts +74 -0
- package/dist/extract/environment.d.ts.map +1 -0
- package/dist/extract/visibility.d.ts +1 -1
- package/dist/extract/visibility.d.ts.map +1 -1
- package/dist/features/host.d.ts +9 -2
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-feature.d.ts +16 -0
- package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -0
- package/dist/features/motion-blur/motion-blur-params.d.ts +25 -0
- package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -0
- package/dist/features/plan.d.ts +17 -2
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +3 -1
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics.d.ts +2 -1
- package/dist/features/prepared-graphics.d.ts.map +1 -1
- package/dist/features/render-graph-raster.d.ts +7 -2
- package/dist/features/render-graph-raster.d.ts.map +1 -1
- package/dist/features/targets.d.ts +3 -0
- package/dist/features/targets.d.ts.map +1 -1
- package/dist/features/types.d.ts +12 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/fullscreen-post-process-pass.d.ts +7 -1
- package/dist/fullscreen-post-process-pass.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +2 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +5 -6
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +21 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +8 -4
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +46 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.mjs +2 -0
- package/dist/materials.d.ts +22 -0
- package/dist/materials.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +12 -6
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts +3 -0
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +48 -0
- package/dist/pipeline/standard-post.d.ts.map +1 -0
- package/dist/pipeline/standard-profile.d.ts +3 -6
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +18 -0
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/points-lines/admission.d.ts +36 -0
- package/dist/points-lines/admission.d.ts.map +1 -0
- package/dist/points-lines/bounds.d.ts +8 -0
- package/dist/points-lines/bounds.d.ts.map +1 -0
- package/dist/points-lines/expansion-cache.d.ts +28 -0
- package/dist/points-lines/expansion-cache.d.ts.map +1 -0
- package/dist/points-lines/inspection.d.ts +53 -0
- package/dist/points-lines/inspection.d.ts.map +1 -0
- package/dist/points-lines/prepare.d.ts +67 -0
- package/dist/points-lines/prepare.d.ts.map +1 -0
- package/dist/points-lines/record.d.ts +60 -0
- package/dist/points-lines/record.d.ts.map +1 -0
- package/dist/points-lines/snapshot.d.ts +49 -0
- package/dist/points-lines/snapshot.d.ts.map +1 -0
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +100 -5
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +4 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +18 -0
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +8 -5
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +8 -1
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts +2 -0
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +93 -18
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +1 -11
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +26 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts +10 -1
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/render-contract.d.ts +102 -10
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +50 -6
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +32 -5
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +27 -5
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene-types.d.ts +3 -0
- package/dist/scene/render-scene-types.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +43 -2
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/temporal/coverage.d.ts +42 -0
- package/dist/temporal/coverage.d.ts.map +1 -0
- package/dist/temporal/frame-coordinator.d.ts +99 -0
- package/dist/temporal/frame-coordinator.d.ts.map +1 -0
- package/dist/temporal/gpu.d.ts +39 -0
- package/dist/temporal/gpu.d.ts.map +1 -0
- package/dist/temporal/history.d.ts +37 -0
- package/dist/temporal/history.d.ts.map +1 -0
- package/dist/temporal/index.d.ts +7 -0
- package/dist/temporal/index.d.ts.map +1 -0
- package/dist/temporal/index.mjs +8 -0
- package/dist/temporal/index.mjs.map +1 -0
- package/dist/temporal/inspection.d.ts +36 -0
- package/dist/temporal/inspection.d.ts.map +1 -0
- package/dist/temporal/scene-data-catalog.d.ts +48 -0
- package/dist/temporal/scene-data-catalog.d.ts.map +1 -0
- package/dist/temporal/scene-data.d.ts +43 -0
- package/dist/temporal/scene-data.d.ts.map +1 -0
- package/dist/temporal/standard-scene-data.d.ts +61 -0
- package/dist/temporal/standard-scene-data.d.ts.map +1 -0
- package/dist/temporal/taa-history-resources.d.ts +22 -0
- package/dist/temporal/taa-history-resources.d.ts.map +1 -0
- package/dist/temporal/taa-history-store.d.ts +27 -0
- package/dist/temporal/taa-history-store.d.ts.map +1 -0
- package/dist/temporal/taa-resolve.d.ts +15 -0
- package/dist/temporal/taa-resolve.d.ts.map +1 -0
- package/dist/temporal/temporal-view.d.ts +40 -0
- package/dist/temporal/temporal-view.d.ts.map +1 -0
- package/dist/temporal/transaction.d.ts +9 -0
- package/dist/temporal/transaction.d.ts.map +1 -0
- package/dist/temporal/view.d.ts +56 -0
- package/dist/temporal/view.d.ts.map +1 -0
- package/dist/transmission/backdrop.d.ts +147 -0
- package/dist/transmission/backdrop.d.ts.map +1 -0
- package/dist/transmission/inspection.d.ts +57 -0
- package/dist/transmission/inspection.d.ts.map +1 -0
- package/dist/transmission/oracle.d.ts +42 -0
- package/dist/transmission/oracle.d.ts.map +1 -0
- package/dist/transmission/projection.d.ts +37 -0
- package/dist/transmission/projection.d.ts.map +1 -0
- package/dist/typed-render-graph-primitives.d.ts +25 -2
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/package.json +24 -19
- package/src/__tests__/ai-user-cold-start.integration.test.ts +59 -0
- package/src/__tests__/ai-user-public-surface.test-d.ts +45 -0
- package/src/__tests__/authoring-path.unit.test.ts +9 -0
- package/src/__tests__/bloom-exact-zero.unit.test.ts +202 -0
- package/src/__tests__/bloom-lifecycle.integration.test.ts +125 -0
- package/src/__tests__/construct-renderer-identity.unit.test.ts +4 -3
- package/src/__tests__/detached-inspection.integration.test.ts +177 -0
- package/src/__tests__/environment-fog-lanes.integration.test.ts +219 -0
- package/src/__tests__/environment-generation.integration.test.ts +141 -0
- package/src/__tests__/environment-selection.unit.test.ts +245 -0
- package/src/__tests__/factory-contract.integration.test.ts +1380 -9
- package/src/__tests__/frame-plan-contract.unit.test.ts +90 -0
- package/src/__tests__/fullscreen-feature-plan.unit.test.ts +7 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +12 -2
- package/src/__tests__/gpu-driven-production.integration.test.ts +74 -22
- package/src/__tests__/gpu-driven-view-graph.integration.test.ts +37 -0
- package/src/__tests__/gpu-scene.unit.test.ts +10 -0
- package/src/__tests__/injection-kind-owner.test-d.ts +2 -1
- package/src/__tests__/material-contract-inventory.unit.test.ts +62 -0
- package/src/__tests__/material-cooked-projection.unit.test.ts +18 -124
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +28 -28
- package/src/__tests__/materials-transmission-validation.unit.test.ts +36 -0
- package/src/__tests__/morph-feature-plan.unit.test.ts +7 -0
- package/src/__tests__/motion-blur.pixel.integration.test.ts +121 -0
- package/src/__tests__/public-surface.test-d.ts +15 -1
- package/src/__tests__/render-error-code-owner.test-d.ts +15 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +11 -2
- package/src/__tests__/render-error-exhaustive.test-d.ts +22 -0
- package/src/__tests__/render-feature-errors.unit.test.ts +33 -3
- package/src/__tests__/render-feature-gpu-work.integration.test.ts +44 -0
- package/src/__tests__/render-feature-plan.test-d.ts +22 -89
- package/src/__tests__/render-scene-projection.unit.test.ts +28 -0
- package/src/__tests__/render-temporal-environment-consumer.browser.test.ts +26 -0
- package/src/__tests__/render-temporal-environment-consumer.dawn.test.ts +21 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +433 -1
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +405 -0
- package/src/__tests__/renderer-lifecycle.integration.test.ts +22 -0
- package/src/__tests__/scene-data-catalog.unit.test.ts +59 -0
- package/src/__tests__/scene-data-consumer.integration.test.ts +67 -0
- package/src/__tests__/scene-data-contract.test-d.ts +35 -0
- package/src/__tests__/scene-data-inspection.unit.test.ts +41 -0
- package/src/__tests__/scene-data-public-surface.test-d.ts +34 -0
- package/src/__tests__/shader-manifest-fixture.ts +111 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +102 -0
- package/src/__tests__/standard-clustered-pipeline.unit.test.ts +24 -3
- package/src/__tests__/standard-pipeline.dawn.test.ts +15 -0
- package/src/__tests__/standard-pipeline.integration.test.ts +543 -1
- package/src/__tests__/standard-profile-oracle.unit.test.ts +10 -2
- package/src/__tests__/standard-temporal-producer.integration.test.ts +35 -0
- package/src/__tests__/surface-configuration.unit.test.ts +211 -12
- package/src/__tests__/taa-transaction.integration.test.ts +30 -0
- package/src/__tests__/temporal-admission.integration.test.ts +40 -0
- package/src/__tests__/temporal-coverage.unit.test.ts +35 -0
- package/src/__tests__/temporal-demand.unit.test.ts +44 -0
- package/src/__tests__/temporal-frame-coordinator.unit.test.ts +64 -0
- package/src/__tests__/temporal-graph-topology.unit.test.ts +51 -0
- package/src/__tests__/temporal-history-lifecycle.unit.test.ts +170 -0
- package/src/__tests__/temporal-performance.integration.test.ts +615 -0
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +97 -0
- package/src/__tests__/temporal-transaction.integration.test.ts +60 -0
- package/src/__tests__/temporal-view.unit.test.ts +152 -0
- package/src/__tests__/transmission-prewarm.unit.test.ts +93 -0
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +230 -1
- package/src/__tests__/urp-pipeline-targets.unit.test.ts +25 -1
- package/src/__tests__/urp-pipeline.unit.test.ts +4 -3
- package/src/__tests__/visibility-extract.integration.test.ts +27 -1
- package/src/__tests__/visibility-producer-matrix.integration.test.ts +10 -0
- package/src/assembly/backend-contract.ts +47 -1
- package/src/assembly/factory.ts +1295 -507
- package/src/assembly/host-contract.ts +4 -0
- package/src/assembly/material/assembly.ts +2 -5
- package/src/assembly/renderer-frame-transaction.ts +75 -0
- package/src/assembly/renderer-inspection.ts +112 -0
- package/src/assembly/renderer-lifecycle.ts +6 -0
- package/src/authoring.ts +1 -0
- package/src/components/__tests__/motion-blur.unit.test.ts +14 -0
- package/src/components/__tests__/points-lines-admission.unit.test.ts +219 -0
- package/src/components/__tests__/points-lines-schema.unit.test.ts +56 -0
- package/src/components/__tests__/schema-contract.unit.test.ts +6 -1
- package/src/components/atmosphere.ts +10 -0
- package/src/components/camera.ts +2 -1
- package/src/components/fog.ts +9 -0
- package/src/components/index.ts +10 -0
- package/src/components/lines.ts +18 -0
- package/src/components/motion-blur.ts +8 -0
- package/src/components/points.ts +40 -0
- package/src/construct-renderer.ts +40 -3
- package/src/device/device-scope.ts +15 -1
- package/src/environment/frame.ts +185 -0
- package/src/environment/generation.ts +23 -0
- package/src/environment/graph.ts +9 -0
- package/src/environment/inspection.ts +87 -0
- package/src/environment/lifecycle.ts +250 -0
- package/src/environment/signature.ts +55 -0
- package/src/errors/render.ts +429 -1
- package/src/extract/camera.ts +78 -0
- package/src/extract/environment.ts +171 -0
- package/src/extract/visibility.ts +38 -19
- package/src/features/host.ts +36 -5
- package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +67 -0
- package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +53 -0
- package/src/features/motion-blur/motion-blur-feature.ts +119 -0
- package/src/features/motion-blur/motion-blur-params.ts +89 -0
- package/src/features/plan.ts +32 -3
- package/src/features/prepared-gpu-work.ts +21 -6
- package/src/features/prepared-graphics.ts +2 -1
- package/src/features/render-graph-raster.ts +24 -4
- package/src/features/targets.ts +24 -2
- package/src/features/types.ts +13 -0
- package/src/fullscreen-post-process-pass.ts +34 -4
- package/src/gpu-driven/production-raster.ts +6 -1
- package/src/gpu-scene.ts +24 -0
- package/src/ibl/skylight-bind-group.ts +30 -38
- package/src/index.ts +83 -6
- package/src/inspection-types.ts +52 -0
- package/src/internal.ts +5 -0
- package/src/materials.ts +119 -24
- package/src/pbr-pipeline.ts +66 -20
- package/src/pipeline/standard-forward-lane.ts +158 -101
- package/src/pipeline/standard-pipeline.ts +156 -105
- package/src/pipeline/standard-post.ts +373 -0
- package/src/pipeline/standard-profile.ts +11 -11
- package/src/pipeline-builder.ts +102 -12
- package/src/pipeline-spec.ts +9 -0
- package/src/plugin.ts +6 -0
- package/src/points-lines/__tests__/bounds.unit.test.ts +21 -0
- package/src/points-lines/__tests__/cache-recovery.unit.test.ts +157 -0
- package/src/points-lines/__tests__/inspection-lanes.integration.test.ts +146 -0
- package/src/points-lines/__tests__/inspection.unit.test.ts +173 -0
- package/src/points-lines/__tests__/lane-structural.integration.test.ts +122 -0
- package/src/points-lines/__tests__/points-lines.bench.ts +400 -0
- package/src/points-lines/__tests__/raster-lane.integration.test.ts +44 -0
- package/src/points-lines/__tests__/snapshot.unit.test.ts +101 -0
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +73 -0
- package/src/points-lines/admission.ts +238 -0
- package/src/points-lines/bounds.ts +23 -0
- package/src/points-lines/expansion-cache.ts +215 -0
- package/src/points-lines/inspection.ts +97 -0
- package/src/points-lines/prepare.ts +177 -0
- package/src/points-lines/record.ts +125 -0
- package/src/points-lines/snapshot.ts +130 -0
- package/src/record/__tests__/fullscreen-temporal-routing.unit.test.ts +25 -0
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +47 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +12 -1
- package/src/record/__tests__/material-pipeline-pending-cache.unit.test.ts +22 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +75 -0
- package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +67 -0
- package/src/record/__tests__/temporal-payload.unit.test.ts +164 -0
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +127 -0
- package/src/record/frame-lighting.ts +14 -2
- package/src/record/frame-snapshot.ts +129 -5
- package/src/record/frame-targets.ts +15 -6
- package/src/record/frame.ts +119 -4
- package/src/record/main-pass-geometry.ts +178 -40
- package/src/record/main-pass-material.ts +114 -66
- package/src/record/main-pass-sprite-draws.ts +79 -102
- package/src/record/main-pass.ts +83 -7
- package/src/record/mesh-ssbo.ts +22 -3
- package/src/record/render-context.ts +209 -23
- package/src/record/shadow-pass.ts +129 -28
- package/src/record/skybox-post-pass.ts +99 -42
- package/src/record/typed-frame-graph.ts +582 -42
- package/src/record/view-ubo.ts +99 -4
- package/src/render-contract.ts +121 -7
- package/src/render-graph-primitives.ts +18 -1
- package/src/render-pipeline.ts +100 -27
- package/src/render-system-extract.ts +264 -77
- package/src/render-system.ts +786 -43
- package/src/scene/render-scene-types.ts +3 -0
- package/src/scene/render-scene.ts +307 -9
- package/src/temporal/__tests__/taa-history-resources.unit.test.ts +45 -0
- package/src/temporal/coverage.ts +76 -0
- package/src/temporal/frame-coordinator.ts +311 -0
- package/src/temporal/gpu.ts +287 -0
- package/src/temporal/history.ts +92 -0
- package/src/temporal/index.ts +6 -0
- package/src/temporal/inspection.ts +97 -0
- package/src/temporal/scene-data-catalog.ts +200 -0
- package/src/temporal/scene-data.ts +149 -0
- package/src/temporal/standard-scene-data.ts +182 -0
- package/src/temporal/taa-history-resources.ts +128 -0
- package/src/temporal/taa-history-store.ts +79 -0
- package/src/temporal/taa-resolve.ts +34 -0
- package/src/temporal/temporal-view.ts +169 -0
- package/src/temporal/transaction.ts +17 -0
- package/src/temporal/view.ts +161 -0
- package/src/transmission/__tests__/capability.unit.test.ts +83 -0
- package/src/transmission/__tests__/composition.integration.test.ts +161 -0
- package/src/transmission/__tests__/inspection.unit.test.ts +241 -0
- package/src/transmission/__tests__/lifecycle.integration.test.ts +96 -0
- package/src/transmission/__tests__/oracle.unit.test.ts +131 -0
- package/src/transmission/__tests__/projection.unit.test.ts +71 -0
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +438 -0
- package/src/transmission/__tests__/transmission-backdrop-topology.unit.test.ts +109 -0
- package/src/transmission/__tests__/transmission-backdrop.dawn.test.ts +105 -0
- package/src/transmission/backdrop.ts +572 -0
- package/src/transmission/inspection.ts +190 -0
- package/src/transmission/oracle.ts +141 -0
- package/src/transmission/projection.ts +83 -0
- package/src/typed-render-graph-primitives.ts +162 -9
- package/dist/__tests__/authoring-path.unit.test.d.ts +0 -2
- package/dist/__tests__/authoring-path.unit.test.d.ts.map +0 -1
- package/dist/__tests__/backend-selection-rhi-capture.test-d.d.ts +0 -2
- package/dist/__tests__/backend-selection-rhi-capture.test-d.d.ts.map +0 -1
- package/dist/__tests__/callback-inference.test-d.d.ts +0 -2
- package/dist/__tests__/callback-inference.test-d.d.ts.map +0 -1
- package/dist/__tests__/cluster-binner.test.d.ts +0 -2
- package/dist/__tests__/cluster-binner.test.d.ts.map +0 -1
- package/dist/__tests__/cluster-grid-default-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/cluster-grid-default-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/construct-renderer-identity.unit.test.d.ts +0 -2
- package/dist/__tests__/construct-renderer-identity.unit.test.d.ts.map +0 -1
- package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts +0 -2
- package/dist/__tests__/device-scope-lifecycle.unit.test.d.ts.map +0 -1
- package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts +0 -2
- package/dist/__tests__/device-scope-stale-matrix.unit.test.d.ts.map +0 -1
- package/dist/__tests__/direct-extract-consumer-contract.test-d.d.ts +0 -2
- package/dist/__tests__/direct-extract-consumer-contract.test-d.d.ts.map +0 -1
- package/dist/__tests__/direct-light-snapshot.unit.test.d.ts +0 -2
- package/dist/__tests__/direct-light-snapshot.unit.test.d.ts.map +0 -1
- package/dist/__tests__/factory-contract.integration.test.d.ts +0 -2
- package/dist/__tests__/factory-contract.integration.test.d.ts.map +0 -1
- package/dist/__tests__/fallback-row-stride-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/fallback-row-stride-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/frame-observation.contract.test.d.ts +0 -2
- package/dist/__tests__/frame-observation.contract.test.d.ts.map +0 -1
- package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts +0 -2
- package/dist/__tests__/fullscreen-feature-plan.unit.test.d.ts.map +0 -1
- package/dist/__tests__/glyph-text-layout-recovery.unit.test.d.ts +0 -2
- package/dist/__tests__/glyph-text-layout-recovery.unit.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts +0 -6
- package/dist/__tests__/gpu-driven-indirect-raster-evidence.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-indirect-raster.browser.test.d.ts +0 -2
- package/dist/__tests__/gpu-driven-indirect-raster.browser.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-indirect-raster.dawn.test.d.ts +0 -2
- package/dist/__tests__/gpu-driven-indirect-raster.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-production.integration.test.d.ts +0 -2
- package/dist/__tests__/gpu-driven-production.integration.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-scaling.unit.test.d.ts +0 -2
- package/dist/__tests__/gpu-driven-scaling.unit.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts +0 -18
- package/dist/__tests__/gpu-driven-view-gpu-evidence.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-view-graph.integration.test.d.ts +0 -2
- package/dist/__tests__/gpu-driven-view-graph.integration.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-view-reference.d.ts +0 -15
- package/dist/__tests__/gpu-driven-view-reference.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-view.browser.test.d.ts +0 -2
- package/dist/__tests__/gpu-driven-view.browser.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-view.dawn.test.d.ts +0 -2
- package/dist/__tests__/gpu-driven-view.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-driven-view.unit.test.d.ts +0 -2
- package/dist/__tests__/gpu-driven-view.unit.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-resource-store-stride.unit.test.d.ts +0 -2
- package/dist/__tests__/gpu-resource-store-stride.unit.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts +0 -7
- package/dist/__tests__/gpu-scene-render-graph-gpu.d.ts.map +0 -1
- package/dist/__tests__/gpu-scene-render-graph.browser.test.d.ts +0 -2
- package/dist/__tests__/gpu-scene-render-graph.browser.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-scene-render-graph.dawn.test.d.ts +0 -2
- package/dist/__tests__/gpu-scene-render-graph.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-scene-schema.unit.test.d.ts +0 -2
- package/dist/__tests__/gpu-scene-schema.unit.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-scene.dawn.test.d.ts +0 -2
- package/dist/__tests__/gpu-scene.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/gpu-scene.unit.test.d.ts +0 -2
- package/dist/__tests__/gpu-scene.unit.test.d.ts.map +0 -1
- package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl/face-uniforms.unit.test.d.ts.map +0 -1
- package/dist/__tests__/infinite-grid-no-vertex-probe.unit.test.d.ts +0 -2
- package/dist/__tests__/infinite-grid-no-vertex-probe.unit.test.d.ts.map +0 -1
- package/dist/__tests__/injection-kind-owner.test-d.d.ts +0 -2
- package/dist/__tests__/injection-kind-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/ktx2-basis-gpu-consumer.dawn.test.d.ts +0 -2
- package/dist/__tests__/ktx2-basis-gpu-consumer.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/light-buffer-layout.unit.test.d.ts +0 -2
- package/dist/__tests__/light-buffer-layout.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-contract-inventory.unit.test.d.ts +0 -2
- package/dist/__tests__/material-contract-inventory.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-cooked-projection.unit.test.d.ts +0 -2
- package/dist/__tests__/material-cooked-projection.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-snapshot-mutation.integration.test.d.ts +0 -2
- package/dist/__tests__/material-snapshot-mutation.integration.test.d.ts.map +0 -1
- package/dist/__tests__/material-static-dynamic-route.unit.test.d.ts +0 -2
- package/dist/__tests__/material-static-dynamic-route.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-stride-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/material-stride-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/mesh-buffer-usage-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/mesh-buffer-usage-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/mesh-material-bindings.unit.test.d.ts +0 -2
- package/dist/__tests__/mesh-material-bindings.unit.test.d.ts.map +0 -1
- package/dist/__tests__/mesh-stride-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/mesh-stride-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/morph-culling-reentry.dawn.test.d.ts +0 -2
- package/dist/__tests__/morph-culling-reentry.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/morph-feature-gpu-work.integration.test.d.ts +0 -2
- package/dist/__tests__/morph-feature-gpu-work.integration.test.d.ts.map +0 -1
- package/dist/__tests__/morph-feature-plan.unit.test.d.ts +0 -2
- package/dist/__tests__/morph-feature-plan.unit.test.d.ts.map +0 -1
- package/dist/__tests__/morph-visual-evidence.integration.test.d.ts +0 -2
- package/dist/__tests__/morph-visual-evidence.integration.test.d.ts.map +0 -1
- package/dist/__tests__/nineslice-handle-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/nineslice-handle-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/post-process-error-policy-owner.unit.test.d.ts +0 -2
- package/dist/__tests__/post-process-error-policy-owner.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-buffer-kind-owner.test-d.d.ts +0 -2
- package/dist/__tests__/prepared-buffer-kind-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-capability.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-capability.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-generation.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-generation.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-lifecycle.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-lifecycle.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-pipeline-warmup.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-pipeline-warmup.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-resolver.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-resolver.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-snapshot.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-snapshot.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-store.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-store.unit.test.d.ts.map +0 -1
- package/dist/__tests__/prepared-graphics-validation.unit.test.d.ts +0 -2
- package/dist/__tests__/prepared-graphics-validation.unit.test.d.ts.map +0 -1
- package/dist/__tests__/profiler-default-off.test.d.ts +0 -2
- package/dist/__tests__/profiler-default-off.test.d.ts.map +0 -1
- package/dist/__tests__/profiler-phase-catalog.test.d.ts +0 -2
- package/dist/__tests__/profiler-phase-catalog.test.d.ts.map +0 -1
- package/dist/__tests__/profiler-phase-integration.test.d.ts +0 -2
- package/dist/__tests__/profiler-phase-integration.test.d.ts.map +0 -1
- package/dist/__tests__/profiler-phase-token.test.d.ts +0 -2
- package/dist/__tests__/profiler-phase-token.test.d.ts.map +0 -1
- package/dist/__tests__/public-surface.test-d.d.ts +0 -2
- package/dist/__tests__/public-surface.test-d.d.ts.map +0 -1
- package/dist/__tests__/recover-error-policy-owner.unit.test.d.ts +0 -2
- package/dist/__tests__/recover-error-policy-owner.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-error-code-owner.test-d.d.ts +0 -2
- package/dist/__tests__/render-error-code-owner.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-error-detail-ssot.test-d.d.ts +0 -2
- package/dist/__tests__/render-error-detail-ssot.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts +0 -2
- package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-assembly.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-assembly.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-context.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-context.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-contract.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-contract.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-diagnostics.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-diagnostics.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-docs.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-docs.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-errors.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-errors.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-gpu-work.integration.test.d.ts +0 -2
- package/dist/__tests__/render-feature-gpu-work.integration.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-plan.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-plan.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-plan.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-plan.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-diagnostics.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-diagnostics.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-errors.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-errors.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-graphics-docs.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-graphics-isolation.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-graphics-isolation.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-graphics-negative.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-graphics-negative.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-graphics.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-graphics.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-prepared-graphics.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-prepared-graphics.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-public-surface.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-public-surface.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-retry.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-retry.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-scheduling.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-scheduling.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-stage-failed-policy-owner.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-stage-failed-policy-owner.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-stage-fault-recovery.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-stage-fault-recovery.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-visibility-context.test-d.d.ts +0 -2
- package/dist/__tests__/render-feature-visibility-context.test-d.d.ts.map +0 -1
- package/dist/__tests__/render-feature-visibility-context.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-visibility-context.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-feature-zero-work.unit.test.d.ts +0 -2
- package/dist/__tests__/render-feature-zero-work.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-graph-compute-raster-gpu.d.ts +0 -7
- package/dist/__tests__/render-graph-compute-raster-gpu.d.ts.map +0 -1
- package/dist/__tests__/render-graph-compute-raster.browser.test.d.ts +0 -2
- package/dist/__tests__/render-graph-compute-raster.browser.test.d.ts.map +0 -1
- package/dist/__tests__/render-graph-compute-raster.dawn.test.d.ts +0 -2
- package/dist/__tests__/render-graph-compute-raster.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/render-graph-hzb-gpu.d.ts +0 -7
- package/dist/__tests__/render-graph-hzb-gpu.d.ts.map +0 -1
- package/dist/__tests__/render-graph-hzb.browser.test.d.ts +0 -2
- package/dist/__tests__/render-graph-hzb.browser.test.d.ts.map +0 -1
- package/dist/__tests__/render-graph-hzb.dawn.test.d.ts +0 -2
- package/dist/__tests__/render-graph-hzb.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/render-graph-primitives.unit.test.d.ts +0 -2
- package/dist/__tests__/render-graph-primitives.unit.test.d.ts.map +0 -1
- package/dist/__tests__/render-graph-storage-indirect-gpu.d.ts +0 -12
- package/dist/__tests__/render-graph-storage-indirect-gpu.d.ts.map +0 -1
- package/dist/__tests__/render-graph-storage-indirect.browser.test.d.ts +0 -2
- package/dist/__tests__/render-graph-storage-indirect.browser.test.d.ts.map +0 -1
- package/dist/__tests__/render-graph-storage-indirect.dawn.test.d.ts +0 -2
- package/dist/__tests__/render-graph-storage-indirect.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/render-scene-projection.unit.test.d.ts +0 -2
- package/dist/__tests__/render-scene-projection.unit.test.d.ts.map +0 -1
- package/dist/__tests__/renderer-factory-material-contract.unit.test.d.ts +0 -2
- package/dist/__tests__/renderer-factory-material-contract.unit.test.d.ts.map +0 -1
- package/dist/__tests__/renderer-lifecycle.integration.test.d.ts +0 -2
- package/dist/__tests__/renderer-lifecycle.integration.test.d.ts.map +0 -1
- package/dist/__tests__/rhi-backend-package-boundary.unit.test.d.ts +0 -2
- package/dist/__tests__/rhi-backend-package-boundary.unit.test.d.ts.map +0 -1
- package/dist/__tests__/scriptable-pack-consumer.test-d.d.ts +0 -2
- package/dist/__tests__/scriptable-pack-consumer.test-d.d.ts.map +0 -1
- package/dist/__tests__/shader-stage-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/shader-stage-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/ssao-config.unit.test.d.ts +0 -2
- package/dist/__tests__/ssao-config.unit.test.d.ts.map +0 -1
- package/dist/__tests__/ssao-kernel-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/ssao-kernel-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts +0 -2
- package/dist/__tests__/standard-clustered-pipeline.unit.test.d.ts.map +0 -1
- package/dist/__tests__/standard-descriptors.test-d.d.ts +0 -2
- package/dist/__tests__/standard-descriptors.test-d.d.ts.map +0 -1
- package/dist/__tests__/standard-pipeline.dawn.test.d.ts +0 -2
- package/dist/__tests__/standard-pipeline.dawn.test.d.ts.map +0 -1
- package/dist/__tests__/standard-pipeline.integration.test.d.ts +0 -2
- package/dist/__tests__/standard-pipeline.integration.test.d.ts.map +0 -1
- package/dist/__tests__/standard-profile-oracle.unit.test.d.ts +0 -2
- package/dist/__tests__/standard-profile-oracle.unit.test.d.ts.map +0 -1
- package/dist/__tests__/surface-configuration.unit.test.d.ts +0 -2
- package/dist/__tests__/surface-configuration.unit.test.d.ts.map +0 -1
- package/dist/__tests__/texture-usage-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/texture-usage-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/typed-pipeline-topology.unit.test.d.ts +0 -2
- package/dist/__tests__/typed-pipeline-topology.unit.test.d.ts.map +0 -1
- package/dist/__tests__/urp-pipeline-targets.unit.test.d.ts +0 -2
- package/dist/__tests__/urp-pipeline-targets.unit.test.d.ts.map +0 -1
- package/dist/__tests__/urp-pipeline.unit.test.d.ts +0 -2
- package/dist/__tests__/urp-pipeline.unit.test.d.ts.map +0 -1
- package/dist/__tests__/vertex-color-pipeline.unit.test.d.ts +0 -2
- package/dist/__tests__/vertex-color-pipeline.unit.test.d.ts.map +0 -1
- package/dist/__tests__/vertex-layout-consumer.unit.test.d.ts +0 -2
- package/dist/__tests__/vertex-layout-consumer.unit.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-diagnostics.integration.test.d.ts +0 -2
- package/dist/__tests__/visibility-diagnostics.integration.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-extract.integration.test.d.ts +0 -2
- package/dist/__tests__/visibility-extract.integration.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-extract.perf.test.d.ts +0 -2
- package/dist/__tests__/visibility-extract.perf.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-feature-dedup.integration.test.d.ts +0 -2
- package/dist/__tests__/visibility-feature-dedup.integration.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-hidden-report.unit.test.d.ts +0 -2
- package/dist/__tests__/visibility-hidden-report.unit.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-instances.integration.test.d.ts +0 -2
- package/dist/__tests__/visibility-instances.integration.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-multi-world.integration.test.d.ts +0 -2
- package/dist/__tests__/visibility-multi-world.integration.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-producer-matrix.integration.test.d.ts +0 -2
- package/dist/__tests__/visibility-producer-matrix.integration.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-producer-matrix.unit.test.d.ts +0 -2
- package/dist/__tests__/visibility-producer-matrix.unit.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-public-surface.test-d.d.ts +0 -2
- package/dist/__tests__/visibility-public-surface.test-d.d.ts.map +0 -1
- package/dist/__tests__/visibility-resolver.unit.test.d.ts +0 -2
- package/dist/__tests__/visibility-resolver.unit.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts +0 -2
- package/dist/__tests__/visibility-resource-short-circuit.unit.test.d.ts.map +0 -1
- package/dist/__tests__/visibility-snapshot-contract.unit.test.d.ts +0 -2
- package/dist/__tests__/visibility-snapshot-contract.unit.test.d.ts.map +0 -1
- package/dist/__tests__/vocabulary.unit.test.d.ts +0 -2
- package/dist/__tests__/vocabulary.unit.test.d.ts.map +0 -1
- package/dist/chunk-CQXZC5XI.mjs.map +0 -1
- package/dist/chunk-QC4VW3XJ.mjs.map +0 -1
- package/dist/chunk-SCRSAK5B.mjs.map +0 -1
- package/dist/components/__tests__/schema-contract.unit.test.d.ts +0 -2
- package/dist/components/__tests__/schema-contract.unit.test.d.ts.map +0 -1
- package/dist/components/__tests__/schema-parity.unit.test.d.ts +0 -2
- package/dist/components/__tests__/schema-parity.unit.test.d.ts.map +0 -1
- package/dist/components/__tests__/visibility-schema.unit.test.d.ts +0 -2
- package/dist/components/__tests__/visibility-schema.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/material-bg-cache.unit.test.d.ts +0 -2
- package/dist/record/__tests__/material-bg-cache.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/material-slot-plan.unit.test.d.ts +0 -2
- package/dist/record/__tests__/material-slot-plan.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts +0 -2
- package/dist/record/__tests__/typed-frame-graph-plan.unit.test.d.ts.map +0 -1
- package/dist/record/__tests__/video-texture-view.unit.test.d.ts +0 -2
- package/dist/record/__tests__/video-texture-view.unit.test.d.ts.map +0 -1
package/src/assembly/factory.ts
CHANGED
|
@@ -77,9 +77,12 @@ import type {
|
|
|
77
77
|
VertexAttributeMap,
|
|
78
78
|
} from '@forgeax/engine-types';
|
|
79
79
|
import { derive, handleSlot } from '@forgeax/engine-types';
|
|
80
|
+
|
|
81
|
+
export type { MaterialShaderManifestEntry } from '@forgeax/engine-shader';
|
|
82
|
+
|
|
80
83
|
import { GpuResidencyCache, type MeshGpuHandles } from '../device/gpu-residency';
|
|
81
84
|
import { createEngineMetrics } from '../engine-metrics';
|
|
82
|
-
import { RecoverError, type RecoverFailure
|
|
85
|
+
import { RecoverError, type RecoverFailure } from '../errors/recover';
|
|
83
86
|
import {
|
|
84
87
|
FrameReceiptStaleError,
|
|
85
88
|
type RenderError,
|
|
@@ -90,8 +93,9 @@ import {
|
|
|
90
93
|
RenderFeatureStageFailedError,
|
|
91
94
|
} from '../errors/render';
|
|
92
95
|
import { createRenderFeatureHost, type RenderFeatureHost } from '../features/host';
|
|
96
|
+
import type { RenderFeatureMaterialShaderBindingContract } from '../features/plan';
|
|
93
97
|
import { RENDER_FEATURE_VERTEX_LAYOUTS } from '../features/prepared-graphics';
|
|
94
|
-
import type { RenderFeature } from '../features/types';
|
|
98
|
+
import type { RenderFeature, RenderFeatureShaderModuleMode } from '../features/types';
|
|
95
99
|
import type { PostProcessShaderEntry } from '../fullscreen-post-process-pass';
|
|
96
100
|
import { glyphTextLayoutSystem } from '../glyph-text-layout-system';
|
|
97
101
|
import { GpuBuffer } from '../gpu-resource';
|
|
@@ -115,6 +119,7 @@ import {
|
|
|
115
119
|
FALLBACK_BYTES_PER_ROW,
|
|
116
120
|
type SkylightFallback,
|
|
117
121
|
} from '../ibl/skylight-bind-group';
|
|
122
|
+
import type { BloomInspection } from '../inspection-types';
|
|
118
123
|
import {
|
|
119
124
|
DeviceScope,
|
|
120
125
|
HealthListenerRegistry,
|
|
@@ -131,13 +136,16 @@ import {
|
|
|
131
136
|
import {
|
|
132
137
|
buildPbrPipelineLayouts,
|
|
133
138
|
buildPbrSkinLayouts,
|
|
139
|
+
isSkinnedShadowCasterVariant,
|
|
134
140
|
SKIN_MATERIAL_SHADER_ID,
|
|
135
141
|
} from '../pbr-pipeline';
|
|
136
142
|
import { STANDARD_CLUSTER_MEMBERSHIP_WGSL, standardPipeline } from '../pipeline/standard-pipeline';
|
|
143
|
+
import { inspectStandardBloomGraph } from '../pipeline/standard-post';
|
|
137
144
|
import {
|
|
138
145
|
DEFAULT_STANDARD_PROFILE,
|
|
139
146
|
STANDARD_LIGHT_COUNTS,
|
|
140
147
|
STANDARD_PIPELINE_ID,
|
|
148
|
+
STANDARD_POST_STAGE_NAMES,
|
|
141
149
|
} from '../pipeline/standard-profile';
|
|
142
150
|
import { buildPipelineForMaterialShader } from '../pipeline-builder';
|
|
143
151
|
import {
|
|
@@ -153,9 +161,11 @@ import {
|
|
|
153
161
|
type PipelineSpec,
|
|
154
162
|
PipelineSpecError,
|
|
155
163
|
} from '../pipeline-spec';
|
|
164
|
+
import { POINTS_LINES_MATERIAL_SHADER_ID } from '../points-lines/record';
|
|
156
165
|
import type { FrameObservationOptions } from '../record/frame';
|
|
166
|
+
import type { BloomPersistentBundle } from '../record/render-context';
|
|
157
167
|
import { SHADOW_CASTER_BUFFER_SIZE } from '../record/shadow-pass';
|
|
158
|
-
import { VIEW_UNIFORM_BUFFER_SIZE } from '../record/view-ubo';
|
|
168
|
+
import { POINTS_LINES_VIEW_BUFFER_SIZE, VIEW_UNIFORM_BUFFER_SIZE } from '../record/view-ubo';
|
|
159
169
|
import type {
|
|
160
170
|
DrawOwnerOptions,
|
|
161
171
|
FrameObservationRequest,
|
|
@@ -176,7 +186,7 @@ import type {
|
|
|
176
186
|
RenderResult,
|
|
177
187
|
RenderWorldLease,
|
|
178
188
|
} from '../render-contract';
|
|
179
|
-
import {
|
|
189
|
+
import { STANDARD_OUTPUT_TRANSFORM_FEATURE_ID } from '../render-contract';
|
|
180
190
|
import {
|
|
181
191
|
configureSurface,
|
|
182
192
|
createRenderSystem,
|
|
@@ -193,6 +203,8 @@ import {
|
|
|
193
203
|
import { tilemapChunkExtractSystem } from '../tilemap-chunk-extract-system';
|
|
194
204
|
import { EngineEnvironmentError, type RhiBackendPack } from './backend-contract';
|
|
195
205
|
import type { RendererAssemblyImplementation, RendererHostImplementation } from './host-contract';
|
|
206
|
+
import { projectRendererOutputInspection } from './renderer-inspection';
|
|
207
|
+
import { isRendererStateOperational } from './renderer-lifecycle';
|
|
196
208
|
|
|
197
209
|
export { assembleMaterialProjection } from './material/assembly';
|
|
198
210
|
export {
|
|
@@ -231,6 +243,9 @@ function loadRhiPack(
|
|
|
231
243
|
const createShaderModule = mod.createShaderModule as
|
|
232
244
|
| RhiBackendPack['createShaderModule']
|
|
233
245
|
| undefined;
|
|
246
|
+
const createShaderModuleImmediate = mod.createShaderModuleImmediate as
|
|
247
|
+
| RhiBackendPack['createShaderModuleImmediate']
|
|
248
|
+
| undefined;
|
|
234
249
|
const translateErrorEventToRhiError = mod.translateErrorEventToRhiError as
|
|
235
250
|
| RhiBackendPack['translateErrorEventToRhiError']
|
|
236
251
|
| undefined;
|
|
@@ -240,6 +255,7 @@ function loadRhiPack(
|
|
|
240
255
|
return {
|
|
241
256
|
rhi,
|
|
242
257
|
...(createShaderModule === undefined ? {} : { createShaderModule }),
|
|
258
|
+
...(createShaderModuleImmediate === undefined ? {} : { createShaderModuleImmediate }),
|
|
243
259
|
...(translateErrorEventToRhiError === undefined ? {} : { translateErrorEventToRhiError }),
|
|
244
260
|
...(rawDeviceAccessor === undefined ? {} : { _internal_getRawDevice: rawDeviceAccessor }),
|
|
245
261
|
...(instrumentation === undefined ? {} : { instrumentation }),
|
|
@@ -476,20 +492,61 @@ export type LayoutKind = 'pbr' | 'pbr-skin' | 'hdrp-pbr' | 'sprite-urp' | 'unlit
|
|
|
476
492
|
* Return every device-capability HDRP PBR variant that needs a lazy module
|
|
477
493
|
* label seeded during renderer boot. Color availability is intentionally not
|
|
478
494
|
* filtered: colored and no-color meshes use distinct composed WGSL modules.
|
|
495
|
+
* Transmission is filtered by the same sampled-texture capability gate as
|
|
496
|
+
* Standard transmission because HDRP shares the material texture ABI.
|
|
479
497
|
*/
|
|
480
498
|
export function selectHdrpPbrPrewarmVariants(
|
|
481
499
|
manifestEntry: MaterialShaderManifestEntry | undefined,
|
|
482
500
|
storageBufferCapable: boolean,
|
|
501
|
+
transmissionCapable: boolean,
|
|
483
502
|
): readonly MaterialShaderManifestEntry['variants'][number][] {
|
|
484
503
|
return (
|
|
485
504
|
manifestEntry?.variants.filter(
|
|
486
505
|
(variant) =>
|
|
487
506
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
488
|
-
variant.defines.CLUSTER_FORWARD_AVAILABLE === true
|
|
507
|
+
variant.defines.CLUSTER_FORWARD_AVAILABLE === true &&
|
|
508
|
+
(transmissionCapable || variant.defines.TRANSMISSION_AVAILABLE !== true),
|
|
489
509
|
) ?? []
|
|
490
510
|
);
|
|
491
511
|
}
|
|
492
512
|
|
|
513
|
+
/**
|
|
514
|
+
* Select both Standard transmission states from the manifest for exact-label
|
|
515
|
+
* prewarming. The requested keys come from declared variants; no key is
|
|
516
|
+
* synthesized and no source fallback is permitted.
|
|
517
|
+
*/
|
|
518
|
+
export function selectStandardPbrTransmissionPrewarmVariants(
|
|
519
|
+
manifestEntry: MaterialShaderManifestEntry | undefined,
|
|
520
|
+
storageBufferCapable: boolean,
|
|
521
|
+
): readonly MaterialShaderManifestEntry['variants'][number][] {
|
|
522
|
+
if (manifestEntry === undefined) {
|
|
523
|
+
throw new Error('Standard material shader manifest row is missing');
|
|
524
|
+
}
|
|
525
|
+
const selected: MaterialShaderManifestEntry['variants'][number][] = [];
|
|
526
|
+
for (const transmissionAvailable of [false, true]) {
|
|
527
|
+
const declared = manifestEntry.variants.find(
|
|
528
|
+
(variant) =>
|
|
529
|
+
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
530
|
+
variant.defines.CLUSTER_FORWARD_AVAILABLE === false &&
|
|
531
|
+
variant.defines.VERTEX_COLOR_AVAILABLE === false &&
|
|
532
|
+
variant.defines.TRANSMISSION_AVAILABLE === transmissionAvailable,
|
|
533
|
+
);
|
|
534
|
+
if (declared === undefined) {
|
|
535
|
+
throw new Error(
|
|
536
|
+
`Standard material shader manifest lacks exact TRANSMISSION_AVAILABLE=${transmissionAvailable} prewarm variant`,
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
const exact = findVariantByKey(manifestEntry, declared.definesKey);
|
|
540
|
+
if (exact === undefined) {
|
|
541
|
+
throw new Error(
|
|
542
|
+
`Standard material shader manifest exact lookup failed for TRANSMISSION_AVAILABLE=${transmissionAvailable}`,
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
selected.push(exact);
|
|
546
|
+
}
|
|
547
|
+
return selected;
|
|
548
|
+
}
|
|
549
|
+
|
|
493
550
|
/**
|
|
494
551
|
* Select the PBR variant that matches a fallback mesh with no COLOR_0 input.
|
|
495
552
|
* Unknown/custom material IDs may still use the legacy PBR fallback, so that
|
|
@@ -501,7 +558,9 @@ export function selectNoColorPbrVariant(
|
|
|
501
558
|
variantSet: string | undefined,
|
|
502
559
|
): MaterialShaderManifestEntry['variants'][number] | undefined {
|
|
503
560
|
const hdrpRequested =
|
|
504
|
-
variantSet === '' ||
|
|
561
|
+
variantSet === '' ||
|
|
562
|
+
// biome-ignore lint/complexity/useOptionalChain: G-14 requires an explicit undefined guard so no variant request becomes false.
|
|
563
|
+
(variantSet !== undefined && variantSet.includes('CLUSTER_FORWARD_AVAILABLE=true'));
|
|
505
564
|
return manifestEntry?.variants.find(
|
|
506
565
|
(variant) =>
|
|
507
566
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
@@ -511,12 +570,7 @@ export function selectNoColorPbrVariant(
|
|
|
511
570
|
);
|
|
512
571
|
}
|
|
513
572
|
|
|
514
|
-
export type MaterialShaderBindingContract =
|
|
515
|
-
| 'group-0'
|
|
516
|
-
| 'group-0-resource'
|
|
517
|
-
| 'view-only'
|
|
518
|
-
| 'view-and-scene-depth'
|
|
519
|
-
| 'render-material';
|
|
573
|
+
export type MaterialShaderBindingContract = RenderFeatureMaterialShaderBindingContract;
|
|
520
574
|
export type MaterialShaderVertexInputContract = 'none' | 'render-material';
|
|
521
575
|
|
|
522
576
|
export function resolveMaterialShaderBindingContract(
|
|
@@ -631,6 +685,13 @@ export function selectPipelineLayoutForVariant(
|
|
|
631
685
|
if (layoutKind === 'unlit-urp') {
|
|
632
686
|
return state.pbrPipelineLayout;
|
|
633
687
|
}
|
|
688
|
+
// Static PBR callers can use the canonical all-true variant key (`''`)
|
|
689
|
+
// without becoming HDRP. Keep the shader variant identity independent from
|
|
690
|
+
// the pipeline-layout identity; shadow-caster static draws use this explicit
|
|
691
|
+
// kind so their ordinary mesh bind group remains compatible with the PSO.
|
|
692
|
+
if (layoutKind === 'pbr') {
|
|
693
|
+
return state.pbrPipelineLayout;
|
|
694
|
+
}
|
|
634
695
|
// HDRP variant matches when:
|
|
635
696
|
// - layoutKind === 'hdrp-pbr' (caller-driven, future-proof), OR
|
|
636
697
|
// - variantSet is the canonical all-true key '' (boot-time HDRP
|
|
@@ -684,7 +745,17 @@ export function resolveMaterialShaderVariantSet(
|
|
|
684
745
|
? backendKind === 'wgpu-webgl2'
|
|
685
746
|
: axis === 'STORAGE_BUFFER_AVAILABLE'
|
|
686
747
|
? backendKind !== 'wgpu-webgl2' && storageBuffer
|
|
687
|
-
:
|
|
748
|
+
: axis === 'SKINNING_DISABLED'
|
|
749
|
+
? (requested[axis] ?? true)
|
|
750
|
+
: axis === 'CLUSTER_FORWARD_AVAILABLE'
|
|
751
|
+
? backendKind !== 'wgpu-webgl2' && (requested[axis] ?? false)
|
|
752
|
+
: axis === 'PER_INSTANCE_REGION'
|
|
753
|
+
? requestedVariantSet === '' || (requested[axis] ?? false)
|
|
754
|
+
: axis === 'VERTEX_COLOR_AVAILABLE'
|
|
755
|
+
? requestedVariantSet === '' || (requested[axis] ?? false)
|
|
756
|
+
: axis === 'TRANSMISSION_AVAILABLE'
|
|
757
|
+
? (requested[axis] ?? false)
|
|
758
|
+
: (requested[axis] ?? true);
|
|
688
759
|
}
|
|
689
760
|
const sorted = Object.entries(filtered).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
690
761
|
return sorted.every(([, value]) => value)
|
|
@@ -694,6 +765,21 @@ export function resolveMaterialShaderVariantSet(
|
|
|
694
765
|
: sorted.map(([name, value]) => `${name}=${value}`).join('+');
|
|
695
766
|
}
|
|
696
767
|
|
|
768
|
+
/**
|
|
769
|
+
* Keep a single-source material on its canonical module identity. A manifest
|
|
770
|
+
* entry with no variants cannot consume a synthesized capability or geometry
|
|
771
|
+
* axis, so carrying one into the pipeline cache only creates an uncached lazy
|
|
772
|
+
* compile beside the prewarmed source module.
|
|
773
|
+
*/
|
|
774
|
+
export function normalizeMaterialShaderVariantSet(
|
|
775
|
+
requestedVariantSet: string | undefined,
|
|
776
|
+
manifestEntry: MaterialShaderManifestEntry | undefined,
|
|
777
|
+
): string | undefined {
|
|
778
|
+
return manifestEntry !== undefined && manifestEntry.variants.length === 0
|
|
779
|
+
? undefined
|
|
780
|
+
: requestedVariantSet;
|
|
781
|
+
}
|
|
782
|
+
|
|
697
783
|
export async function createRenderer(
|
|
698
784
|
canvas: unknown,
|
|
699
785
|
options?: RendererOptions,
|
|
@@ -859,11 +945,32 @@ const COMPRESSION_FEATURES: GPUFeatureName[] = [
|
|
|
859
945
|
'texture-compression-astc',
|
|
860
946
|
];
|
|
861
947
|
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
948
|
+
// The Standard transmission fragment ABI needs 17 sampled textures across its
|
|
949
|
+
// material, IBL, clustered-light, and SSAO bindings. Ordinary Standard draws
|
|
950
|
+
// use the non-transmission variant and remain valid on the WebGPU minimum of
|
|
951
|
+
// 16; only request the elevated limit when the adapter can provide it.
|
|
952
|
+
const STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES = 17;
|
|
953
|
+
|
|
954
|
+
function deviceOptionsForAdapter(adapter: Pick<RhiAdapter, 'features' | 'limits'>):
|
|
955
|
+
| {
|
|
956
|
+
requiredFeatures?: GPUFeatureName[];
|
|
957
|
+
requiredLimits?: GPUDeviceDescriptor['requiredLimits'];
|
|
958
|
+
}
|
|
959
|
+
| undefined {
|
|
865
960
|
const requiredFeatures = COMPRESSION_FEATURES.filter((feature) => adapter.features.has(feature));
|
|
866
|
-
|
|
961
|
+
const supportsTransmission =
|
|
962
|
+
(adapter.limits.maxSampledTexturesPerShaderStage ?? 0) >=
|
|
963
|
+
STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
964
|
+
return {
|
|
965
|
+
...(requiredFeatures.length > 0 ? { requiredFeatures } : {}),
|
|
966
|
+
...(supportsTransmission
|
|
967
|
+
? {
|
|
968
|
+
requiredLimits: {
|
|
969
|
+
maxSampledTexturesPerShaderStage: STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES,
|
|
970
|
+
},
|
|
971
|
+
}
|
|
972
|
+
: {}),
|
|
973
|
+
};
|
|
867
974
|
}
|
|
868
975
|
|
|
869
976
|
/**
|
|
@@ -1081,16 +1188,34 @@ async function makeWebGPURenderer(
|
|
|
1081
1188
|
// compile across both paths (charter P4 consistent abstraction +
|
|
1082
1189
|
// architecture-principles #6 idempotency).
|
|
1083
1190
|
let sharedShaderModuleAdapter: ShaderDeviceAdapterInternal | null = null;
|
|
1191
|
+
// Generated feature programs (currently VFX) opt into the handle-first
|
|
1192
|
+
// adapter. Keep it separate from the validated adapter so ordinary render
|
|
1193
|
+
// pipelines retain the compiler-readiness barrier that prevents first-frame
|
|
1194
|
+
// black output on Dawn.
|
|
1195
|
+
let sharedImmediateShaderModuleAdapter: ShaderDeviceAdapterInternal | null = null;
|
|
1084
1196
|
const getShaderModuleAdapter = (): ShaderDeviceAdapterInternal => {
|
|
1085
1197
|
if (sharedShaderModuleAdapter === null) {
|
|
1086
1198
|
sharedShaderModuleAdapter = makeShaderDeviceAdapter(
|
|
1087
1199
|
internals.device,
|
|
1088
1200
|
internals.errorRegistry,
|
|
1089
1201
|
internals.pack.createShaderModule,
|
|
1202
|
+
internals.pack.createShaderModuleImmediate,
|
|
1090
1203
|
);
|
|
1091
1204
|
}
|
|
1092
1205
|
return sharedShaderModuleAdapter;
|
|
1093
1206
|
};
|
|
1207
|
+
const getImmediateShaderModuleAdapter = (): ShaderDeviceAdapterInternal => {
|
|
1208
|
+
if (sharedImmediateShaderModuleAdapter === null) {
|
|
1209
|
+
sharedImmediateShaderModuleAdapter = makeShaderDeviceAdapter(
|
|
1210
|
+
internals.device,
|
|
1211
|
+
internals.errorRegistry,
|
|
1212
|
+
internals.pack.createShaderModule,
|
|
1213
|
+
internals.pack.createShaderModuleImmediate,
|
|
1214
|
+
'immediate',
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
return sharedImmediateShaderModuleAdapter;
|
|
1218
|
+
};
|
|
1094
1219
|
const getShader = (): ShaderCatalog => {
|
|
1095
1220
|
if (shaderInstance === null) {
|
|
1096
1221
|
// feat-20260608-create-app-param-surface-trim / M2 / D-2 q5-A:
|
|
@@ -1310,13 +1435,13 @@ async function makeWebGPURenderer(
|
|
|
1310
1435
|
>();
|
|
1311
1436
|
const preparedMaterialPipelineLayoutCache = new Map<string, PipelineLayout>();
|
|
1312
1437
|
// feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1): per-shader material
|
|
1313
|
-
// BGL + pipeline layout cache.
|
|
1314
|
-
// user-region
|
|
1315
|
-
//
|
|
1316
|
-
//
|
|
1317
|
-
//
|
|
1318
|
-
//
|
|
1319
|
-
//
|
|
1438
|
+
// BGL + pipeline layout cache. Every authored material shader owns the
|
|
1439
|
+
// user-region binding order emitted by its paramSchema, plus the shared
|
|
1440
|
+
// engine-injection suffix. A custom shader with four-or-fewer textures can
|
|
1441
|
+
// still differ from the standard PBR order (for example, baseColorTexture
|
|
1442
|
+
// followed directly by normalTexture), so it cannot safely reuse the
|
|
1443
|
+
// standard material BGL. Built lazily on first request and cached by
|
|
1444
|
+
// shaderId; built-in shader IDs continue to use the boot-time shared layout.
|
|
1320
1445
|
type PerShaderMaterialLayout = {
|
|
1321
1446
|
materialBgl: BindGroupLayout;
|
|
1322
1447
|
pipelineLayout: PipelineLayout;
|
|
@@ -1327,6 +1452,16 @@ async function makeWebGPURenderer(
|
|
|
1327
1452
|
readonly layout: PerShaderMaterialLayout | null;
|
|
1328
1453
|
};
|
|
1329
1454
|
const perShaderMaterialLayoutCache = new Map<string, PerShaderMaterialLayoutCacheEntry>();
|
|
1455
|
+
const isEngineOwnedMaterialShader = (materialShaderId: string): boolean =>
|
|
1456
|
+
materialShaderId === 'forgeax::default-unlit' ||
|
|
1457
|
+
materialShaderId === 'forgeax::default-shadow-caster' ||
|
|
1458
|
+
materialShaderId === 'forgeax::sprite' ||
|
|
1459
|
+
materialShaderId === 'forgeax::sprite-lit' ||
|
|
1460
|
+
materialShaderId === 'forgeax::default-sprite' ||
|
|
1461
|
+
materialShaderId === 'forgeax::msdf-text' ||
|
|
1462
|
+
materialShaderId === 'forgeax::default-standard-pbr' ||
|
|
1463
|
+
materialShaderId === 'forgeax::pbr-skin' ||
|
|
1464
|
+
materialShaderId === 'forgeax::default-standard-pbr-skin';
|
|
1330
1465
|
// Material binding contracts are derived from WGSL source, but the lookup
|
|
1331
1466
|
// is also used by the per-submesh bind-group path. Cache the derived value
|
|
1332
1467
|
// per renderer so a frame does not re-run the comment stripping and regex
|
|
@@ -1461,7 +1596,9 @@ async function makeWebGPURenderer(
|
|
|
1461
1596
|
if (cached !== undefined) return cached;
|
|
1462
1597
|
if (pipelineState === null) return null;
|
|
1463
1598
|
const materialBgl =
|
|
1464
|
-
|
|
1599
|
+
(isEngineOwnedMaterialShader(materialShaderId)
|
|
1600
|
+
? undefined
|
|
1601
|
+
: getOrBuildPerShaderMaterialLayout(materialShaderId)?.materialBgl) ??
|
|
1465
1602
|
pipelineState.materialBindGroupLayout;
|
|
1466
1603
|
const result = internals.device.createPipelineLayout({
|
|
1467
1604
|
label: `prepared-material-pl-${materialShaderId}`,
|
|
@@ -1474,15 +1611,15 @@ async function makeWebGPURenderer(
|
|
|
1474
1611
|
preparedMaterialPipelineLayoutCache.set(materialShaderId, result.value);
|
|
1475
1612
|
return result.value;
|
|
1476
1613
|
};
|
|
1477
|
-
// Returns the per-shader { materialBgl, pipelineLayout } for
|
|
1478
|
-
//
|
|
1479
|
-
//
|
|
1480
|
-
//
|
|
1481
|
-
//
|
|
1482
|
-
// 'pbr-material-merged' + materialParamSchema) used by buildPbrPipelineLayouts.
|
|
1614
|
+
// Returns the per-shader { materialBgl, pipelineLayout } for an authored
|
|
1615
|
+
// shader, or null when it is unregistered / pipelineState is not ready.
|
|
1616
|
+
// Pure-ish: builds + caches on miss via the same
|
|
1617
|
+
// buildBindGroupLayoutDescriptor SSOT (kind 'pbr-material-merged' +
|
|
1618
|
+
// materialParamSchema) used by buildPbrPipelineLayouts.
|
|
1483
1619
|
const getOrBuildPerShaderMaterialLayout = (
|
|
1484
1620
|
materialShaderId: string,
|
|
1485
1621
|
): PerShaderMaterialLayout | null => {
|
|
1622
|
+
if (isEngineOwnedMaterialShader(materialShaderId)) return null;
|
|
1486
1623
|
const lookup = getShader().findMaterialArtifact(materialShaderId);
|
|
1487
1624
|
if (!lookup.ok) return null;
|
|
1488
1625
|
const cached = perShaderMaterialLayoutCache.get(materialShaderId);
|
|
@@ -1491,16 +1628,6 @@ async function makeWebGPURenderer(
|
|
|
1491
1628
|
}
|
|
1492
1629
|
if (pipelineState === null) return null;
|
|
1493
1630
|
const paramSchema = lookup.value.paramSchema;
|
|
1494
|
-
// 4-or-fewer user-region textures derive to the same shape as the shared
|
|
1495
|
-
// built-in BGL -> reuse it (D-2; no per-shader entry).
|
|
1496
|
-
if (derive(paramSchema).textureFieldNames.size <= 4) {
|
|
1497
|
-
perShaderMaterialLayoutCache.set(materialShaderId, {
|
|
1498
|
-
source: lookup.value.source,
|
|
1499
|
-
paramSchema,
|
|
1500
|
-
layout: null,
|
|
1501
|
-
});
|
|
1502
|
-
return null;
|
|
1503
|
-
}
|
|
1504
1631
|
const spec: PipelineSpec = {
|
|
1505
1632
|
shader: { id: materialShaderId, passKind: 'forward', variantSet: undefined },
|
|
1506
1633
|
attachments: { colorFormats: [], depthFormat: undefined, sampleCount: 1 },
|
|
@@ -1587,9 +1714,11 @@ async function makeWebGPURenderer(
|
|
|
1587
1714
|
// `internals.device` / `internals.pack` are read live, so a rebuild after a
|
|
1588
1715
|
// device swap compiles against the new device.
|
|
1589
1716
|
let tonemapRegistered = false;
|
|
1590
|
-
|
|
1717
|
+
let temporalPostProcessesRegistered = false;
|
|
1718
|
+
const buildPipeline = (scope: DeviceScope = activeDeviceScope): Promise<PipelineState> =>
|
|
1591
1719
|
buildReadyWebGPU(
|
|
1592
1720
|
internals.device,
|
|
1721
|
+
scope,
|
|
1593
1722
|
getShader,
|
|
1594
1723
|
gpuStore,
|
|
1595
1724
|
internals.pack.createShaderModule,
|
|
@@ -1628,13 +1757,31 @@ async function makeWebGPURenderer(
|
|
|
1628
1757
|
// tonemap into this channel each frame (render-system-extract.ts w13).
|
|
1629
1758
|
(source: string) => {
|
|
1630
1759
|
if (tonemapRegistered) return;
|
|
1631
|
-
renderSystem.registerBuiltinPostProcess(
|
|
1760
|
+
renderSystem.registerBuiltinPostProcess(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, {
|
|
1632
1761
|
source,
|
|
1633
1762
|
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
1634
1763
|
reads: ['hdrColor'],
|
|
1635
1764
|
});
|
|
1636
1765
|
tonemapRegistered = true;
|
|
1637
1766
|
},
|
|
1767
|
+
(entries: { readonly motionBlur?: string; readonly taaResolve?: string }) => {
|
|
1768
|
+
if (temporalPostProcessesRegistered) return;
|
|
1769
|
+
if (entries.motionBlur !== undefined) {
|
|
1770
|
+
renderSystem.registerBuiltinPostProcess('forgeax.motion-blur', {
|
|
1771
|
+
source: entries.motionBlur,
|
|
1772
|
+
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
1773
|
+
reads: ['scene-color', 'scene-temporal'],
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
if (entries.taaResolve !== undefined) {
|
|
1777
|
+
renderSystem.registerBuiltinPostProcess('forgeax.taa-resolve', {
|
|
1778
|
+
source: entries.taaResolve,
|
|
1779
|
+
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
1780
|
+
reads: ['scene-color', 'scene-temporal', 'taa-history-color', 'taa-history-temporal'],
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
temporalPostProcessesRegistered = true;
|
|
1784
|
+
},
|
|
1638
1785
|
);
|
|
1639
1786
|
const ready: Promise<Result<void, RhiError>> = buildPipeline().then(
|
|
1640
1787
|
(state): Result<void, RhiError> => {
|
|
@@ -1714,6 +1861,8 @@ async function makeWebGPURenderer(
|
|
|
1714
1861
|
meshAttributes?: VertexAttributeMap,
|
|
1715
1862
|
vertexLayout?: string,
|
|
1716
1863
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
1864
|
+
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
1865
|
+
layoutKindOverride?: LayoutKind,
|
|
1717
1866
|
) => {
|
|
1718
1867
|
// bug-20260611: derive LayoutKind from materialShaderId (caller-driven)
|
|
1719
1868
|
// so the selector body stays free of literal shader-id strings (AC-09).
|
|
@@ -1725,17 +1874,17 @@ async function makeWebGPURenderer(
|
|
|
1725
1874
|
// through the HDRP layout branch of the selector — sprite shaders have
|
|
1726
1875
|
// no HDRP variant, plan-strategy R-1' guard.
|
|
1727
1876
|
const layoutKind: LayoutKind | undefined =
|
|
1728
|
-
|
|
1877
|
+
layoutKindOverride ??
|
|
1878
|
+
(materialShaderId === SKIN_MATERIAL_SHADER_ID ||
|
|
1879
|
+
isSkinnedShadowCasterVariant(materialShaderId, variantSet)
|
|
1729
1880
|
? 'pbr-skin'
|
|
1730
1881
|
: materialShaderId === 'forgeax::sprite' || materialShaderId === 'forgeax::sprite-lit'
|
|
1731
1882
|
? 'sprite-urp'
|
|
1732
1883
|
: materialShaderId === 'forgeax::default-unlit'
|
|
1733
1884
|
? 'unlit-urp'
|
|
1734
|
-
: undefined;
|
|
1885
|
+
: undefined);
|
|
1735
1886
|
const usesSharedBootMaterialLayout =
|
|
1736
|
-
materialShaderId
|
|
1737
|
-
materialShaderId === 'forgeax::default-unlit' ||
|
|
1738
|
-
materialShaderId === SKIN_MATERIAL_SHADER_ID;
|
|
1887
|
+
materialShaderId !== undefined && isEngineOwnedMaterialShader(materialShaderId);
|
|
1739
1888
|
// Built-in PBR/unlit/skin layouts are assembled at boot. Re-deriving a
|
|
1740
1889
|
// per-shader layout from the expanded PBR schema would bypass the HDRP
|
|
1741
1890
|
// group(2) selector and bind the URP mesh BGL to the cluster pipeline.
|
|
@@ -1743,12 +1892,11 @@ async function makeWebGPURenderer(
|
|
|
1743
1892
|
!usesSharedBootMaterialLayout && layoutKind !== 'pbr-skin' && materialShaderId !== undefined
|
|
1744
1893
|
? getOrBuildPerShaderMaterialLayout(materialShaderId)
|
|
1745
1894
|
: null;
|
|
1746
|
-
// feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1):
|
|
1747
|
-
//
|
|
1748
|
-
//
|
|
1749
|
-
//
|
|
1750
|
-
//
|
|
1751
|
-
// (D-2 byte-for-byte).
|
|
1895
|
+
// feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1): an authored
|
|
1896
|
+
// shader owns a per-shader pipeline layout derived from its paramSchema,
|
|
1897
|
+
// because its compact texture order may differ from standard PBR even
|
|
1898
|
+
// when it declares only a few textures. Skin / HDRP keep their dedicated
|
|
1899
|
+
// layouts; built-in material IDs remain on the shared boot-time layout.
|
|
1752
1900
|
const bindingContract =
|
|
1753
1901
|
materialShaderId === undefined
|
|
1754
1902
|
? 'render-material'
|
|
@@ -1884,9 +2032,13 @@ async function makeWebGPURenderer(
|
|
|
1884
2032
|
] satisfies readonly GPUVertexBufferLayout[]);
|
|
1885
2033
|
return {
|
|
1886
2034
|
device: internals.device,
|
|
1887
|
-
shaderModuleFactory:
|
|
2035
|
+
shaderModuleFactory:
|
|
2036
|
+
shaderModuleMode === 'immediate'
|
|
2037
|
+
? getImmediateShaderModuleAdapter()
|
|
2038
|
+
: getShaderModuleAdapter(),
|
|
1888
2039
|
pipelineLayout,
|
|
1889
2040
|
vertexBuffers,
|
|
2041
|
+
...(layoutKind === 'pbr-skin' ? { layoutKind } : {}),
|
|
1890
2042
|
};
|
|
1891
2043
|
};
|
|
1892
2044
|
/**
|
|
@@ -1944,6 +2096,8 @@ async function makeWebGPURenderer(
|
|
|
1944
2096
|
depthFormatOverride?: GPUTextureFormat | null,
|
|
1945
2097
|
vertexLayout?: string,
|
|
1946
2098
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2099
|
+
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
2100
|
+
layoutKindOverride?: LayoutKind,
|
|
1947
2101
|
): RenderPipeline | null => {
|
|
1948
2102
|
const ctx = buildPipelineContext(
|
|
1949
2103
|
variantSet,
|
|
@@ -1951,6 +2105,8 @@ async function makeWebGPURenderer(
|
|
|
1951
2105
|
meshAttributes,
|
|
1952
2106
|
vertexLayout,
|
|
1953
2107
|
vertexLayoutProjection,
|
|
2108
|
+
shaderModuleMode,
|
|
2109
|
+
layoutKindOverride,
|
|
1954
2110
|
);
|
|
1955
2111
|
if (ctx === null) return null;
|
|
1956
2112
|
// bug-20260612: route the LDR color format through pipelineState so the
|
|
@@ -1960,6 +2116,11 @@ async function makeWebGPURenderer(
|
|
|
1960
2116
|
// the explicit null check).
|
|
1961
2117
|
if (pipelineState === null) return null;
|
|
1962
2118
|
const ldrColorFormat = colorFormatOverride ?? pipelineState.colorAttachmentFormat;
|
|
2119
|
+
const fragmentEntry =
|
|
2120
|
+
isHdr &&
|
|
2121
|
+
(materialShaderId === 'forgeax::sprite' || materialShaderId === 'forgeax::sprite-lit')
|
|
2122
|
+
? 'fs_main_hdr'
|
|
2123
|
+
: undefined;
|
|
1963
2124
|
const built = buildPipelineForMaterialShader(
|
|
1964
2125
|
cacheKey,
|
|
1965
2126
|
// The catalog entry is projected into the material-shader entry shape.
|
|
@@ -1993,7 +2154,7 @@ async function makeWebGPURenderer(
|
|
|
1993
2154
|
}
|
|
1994
2155
|
: undefined,
|
|
1995
2156
|
undefined, // vertexEntry — default vs_main
|
|
1996
|
-
|
|
2157
|
+
fragmentEntry,
|
|
1997
2158
|
undefined, // defines — none
|
|
1998
2159
|
passKind,
|
|
1999
2160
|
sampleCount,
|
|
@@ -2052,6 +2213,8 @@ async function makeWebGPURenderer(
|
|
|
2052
2213
|
depthFormatOverride?: GPUTextureFormat | null,
|
|
2053
2214
|
vertexLayout?: string,
|
|
2054
2215
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2216
|
+
shaderModuleMode: RenderFeatureShaderModuleMode = 'validated',
|
|
2217
|
+
layoutKindOverride?: LayoutKind,
|
|
2055
2218
|
): RenderPipeline | null => {
|
|
2056
2219
|
// WebGL2 is a downlevel backend even when the record stage asks for the
|
|
2057
2220
|
// native-capability variant string. Resolve the variant axes at this
|
|
@@ -2066,14 +2229,23 @@ async function makeWebGPURenderer(
|
|
|
2066
2229
|
// (stored in materialShaderUvSetCounts during prepareMaterialShaders).
|
|
2067
2230
|
const resolvedUvSetCount = shaderUvSetCount ?? materialShaderUvSetCounts.get(materialShaderId);
|
|
2068
2231
|
const manifestEntry = findMaterialShaderManifestEntry(materialShaderId);
|
|
2069
|
-
|
|
2232
|
+
// Sprite's omitted request is the boot-selected default artifact. The
|
|
2233
|
+
// explicit empty key remains the PER_INSTANCE_REGION=true request used by
|
|
2234
|
+
// SpriteInstances, so only that path is capability-rewritten here.
|
|
2235
|
+
const resolvedVariantSet =
|
|
2236
|
+
variantSet === undefined &&
|
|
2237
|
+
(materialShaderId === 'forgeax::sprite' || materialShaderId === 'forgeax::sprite-lit')
|
|
2238
|
+
? undefined
|
|
2239
|
+
: resolveCachedMaterialShaderVariantSet(variantSet, manifestEntry);
|
|
2070
2240
|
// The record stage also composes engine capability axes for user shaders.
|
|
2071
2241
|
// Keep only manifest-declared axes for non-engine material IDs: a plain
|
|
2072
2242
|
// custom shader must not collapse that request to '' because '' is the
|
|
2073
2243
|
// canonical HDRP variant key and would pair an HDRP PSO with the URP mesh
|
|
2074
2244
|
// bind group. Builtin forgeax:: shaders keep the compatibility request
|
|
2075
2245
|
// when a reduced test/legacy manifest omits variant metadata.
|
|
2076
|
-
|
|
2246
|
+
// A single-source manifest entry must stay on its canonical module key;
|
|
2247
|
+
// otherwise a synthesized capability axis races the prewarmed module.
|
|
2248
|
+
let effectiveVariantSet = normalizeMaterialShaderVariantSet(resolvedVariantSet, manifestEntry);
|
|
2077
2249
|
if (
|
|
2078
2250
|
vertexLayoutProjection === undefined &&
|
|
2079
2251
|
manifestEntry?.variants.some((variant) => 'VERTEX_COLOR_AVAILABLE' in variant.defines)
|
|
@@ -2209,6 +2381,7 @@ async function makeWebGPURenderer(
|
|
|
2209
2381
|
depthFormatOverride,
|
|
2210
2382
|
vertexLayout,
|
|
2211
2383
|
vertexLayoutProjection,
|
|
2384
|
+
shaderModuleMode,
|
|
2212
2385
|
);
|
|
2213
2386
|
}
|
|
2214
2387
|
const pbrEntry = findStandardPbrEntry(effectiveVariantSet);
|
|
@@ -2262,6 +2435,7 @@ async function makeWebGPURenderer(
|
|
|
2262
2435
|
depthFormatOverride,
|
|
2263
2436
|
vertexLayout,
|
|
2264
2437
|
vertexLayoutProjection,
|
|
2438
|
+
shaderModuleMode,
|
|
2265
2439
|
);
|
|
2266
2440
|
}
|
|
2267
2441
|
// feat-20260609 M4 / w31: resolve variant WGSL from manifest when
|
|
@@ -2352,6 +2526,8 @@ async function makeWebGPURenderer(
|
|
|
2352
2526
|
depthFormatOverride,
|
|
2353
2527
|
vertexLayout,
|
|
2354
2528
|
vertexLayoutProjection,
|
|
2529
|
+
shaderModuleMode,
|
|
2530
|
+
layoutKindOverride,
|
|
2355
2531
|
);
|
|
2356
2532
|
};
|
|
2357
2533
|
const getParamSchema = (materialShaderId: string) => {
|
|
@@ -2360,10 +2536,11 @@ async function makeWebGPURenderer(
|
|
|
2360
2536
|
};
|
|
2361
2537
|
// feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1): expose the per-shader
|
|
2362
2538
|
// material BGL so the record stage creates the material bind group against
|
|
2363
|
-
// the matching layout
|
|
2364
|
-
//
|
|
2365
|
-
//
|
|
2539
|
+
// the matching paramSchema-derived layout. Returns undefined only for
|
|
2540
|
+
// group-0 / view-only contracts or an unavailable shader layout; built-in
|
|
2541
|
+
// material IDs continue to resolve through the shared layout path.
|
|
2366
2542
|
const getMaterialBindGroupLayout = (materialShaderId: string): BindGroupLayout | undefined => {
|
|
2543
|
+
if (isEngineOwnedMaterialShader(materialShaderId)) return undefined;
|
|
2367
2544
|
const contract = getCachedMaterialShaderBindingContract(materialShaderId);
|
|
2368
2545
|
if (contract === 'group-0') {
|
|
2369
2546
|
return getOrBuildGroup0MaterialLayout()?.materialBgl;
|
|
@@ -2440,7 +2617,11 @@ async function makeWebGPURenderer(
|
|
|
2440
2617
|
fragment: {
|
|
2441
2618
|
module: moduleResult.value,
|
|
2442
2619
|
entryPoint: 'fs_main',
|
|
2443
|
-
targets:
|
|
2620
|
+
targets:
|
|
2621
|
+
entry.source.includes('@location(1) temporal') &&
|
|
2622
|
+
entry.source.includes('TaaResolveOutput')
|
|
2623
|
+
? [{ format: colorFormat }, { format: colorFormat }]
|
|
2624
|
+
: [{ format: colorFormat }],
|
|
2444
2625
|
},
|
|
2445
2626
|
primitive: { topology: 'triangle-list', cullMode: 'none', frontFace: 'ccw' },
|
|
2446
2627
|
depthStencil: undefined,
|
|
@@ -2469,6 +2650,10 @@ async function makeWebGPURenderer(
|
|
|
2469
2650
|
shaderModuleFactory: {
|
|
2470
2651
|
createShaderModule: (descriptor) => getShaderModuleAdapter().createShaderModule(descriptor),
|
|
2471
2652
|
},
|
|
2653
|
+
immediateShaderModuleFactory: {
|
|
2654
|
+
createShaderModule: (descriptor) =>
|
|
2655
|
+
getImmediateShaderModuleAdapter().createShaderModule(descriptor),
|
|
2656
|
+
},
|
|
2472
2657
|
profiler: internals.options?.profiler,
|
|
2473
2658
|
// feat-20260622-s5 M3 / w17: device + context read live off `internals` via
|
|
2474
2659
|
// getters so the recover() rebuild (which swaps internals.device /
|
|
@@ -2481,6 +2666,9 @@ async function makeWebGPURenderer(
|
|
|
2481
2666
|
get deviceScope() {
|
|
2482
2667
|
return activeDeviceScope;
|
|
2483
2668
|
},
|
|
2669
|
+
invalidateShaderModule: (label: string) => {
|
|
2670
|
+
sharedShaderModuleAdapter?.invalidateModule(label);
|
|
2671
|
+
},
|
|
2484
2672
|
get context() {
|
|
2485
2673
|
return internals.context;
|
|
2486
2674
|
},
|
|
@@ -2518,6 +2706,18 @@ async function makeWebGPURenderer(
|
|
|
2518
2706
|
return internals.meshSsboState;
|
|
2519
2707
|
},
|
|
2520
2708
|
});
|
|
2709
|
+
if (
|
|
2710
|
+
internals.device.caps.backendKind === 'null' &&
|
|
2711
|
+
Array.from(getShader().entries()).length === 0
|
|
2712
|
+
) {
|
|
2713
|
+
// RhiNull validates graph topology only; keep the built-in identity
|
|
2714
|
+
// registered so structural frames do not fail before graph inspection.
|
|
2715
|
+
renderSystem.registerBuiltinPostProcess(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, {
|
|
2716
|
+
source: '',
|
|
2717
|
+
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
2718
|
+
reads: ['hdrColor'],
|
|
2719
|
+
});
|
|
2720
|
+
}
|
|
2521
2721
|
// The Standard pipeline is the sole built-in graph owner. Feature plans are
|
|
2522
2722
|
// projected into that graph directly; no second post-process registry is
|
|
2523
2723
|
// installed for fullscreen producers.
|
|
@@ -2578,6 +2778,22 @@ async function makeWebGPURenderer(
|
|
|
2578
2778
|
: health.reason === 'internal-fault' || !readySettled
|
|
2579
2779
|
? 'faulted'
|
|
2580
2780
|
: 'alive';
|
|
2781
|
+
const surfaceStorage = pipelineState?.format ?? 'rgba8unorm';
|
|
2782
|
+
const surfaceDisplay = pipelineState?.colorAttachmentFormat ?? 'rgba8unorm';
|
|
2783
|
+
const observationId = `${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}:frame-${frameId}`;
|
|
2784
|
+
const outputInspection = projectRendererOutputInspection({
|
|
2785
|
+
graph: renderSystem.perFrameGraphInfo,
|
|
2786
|
+
surfaceAvailable: !surfaceReleased && pipelineState !== null,
|
|
2787
|
+
surfaceStorage,
|
|
2788
|
+
surfaceDisplay,
|
|
2789
|
+
...(pipelineState?.surfaceProfile === undefined
|
|
2790
|
+
? {}
|
|
2791
|
+
: { surfaceProfile: pipelineState.surfaceProfile }),
|
|
2792
|
+
rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
|
|
2793
|
+
...(internals.context?.presentationProof === undefined
|
|
2794
|
+
? {}
|
|
2795
|
+
: { presentationProof: internals.context.presentationProof }),
|
|
2796
|
+
});
|
|
2581
2797
|
return Object.freeze({
|
|
2582
2798
|
state,
|
|
2583
2799
|
surface: surfaceReleased ? 'released' : 'available',
|
|
@@ -2590,9 +2806,22 @@ async function makeWebGPURenderer(
|
|
|
2590
2806
|
features: Object.freeze(
|
|
2591
2807
|
(internals.featureHost?.features ?? []).map((feature) => feature.identity),
|
|
2592
2808
|
),
|
|
2809
|
+
featureDiagnostics: internals.featureHost?.diagnostics() ?? Object.freeze([]),
|
|
2593
2810
|
frustumStats: Object.freeze({ ...renderSystem.frustumStats }),
|
|
2594
2811
|
visibilityStats: Object.freeze({ ...renderSystem.visibilityStats }),
|
|
2595
2812
|
renderScene: renderSystem.renderScene,
|
|
2813
|
+
environment: renderSystem.environment,
|
|
2814
|
+
temporal: renderSystem.temporal,
|
|
2815
|
+
bloom: renderSystem.bloom,
|
|
2816
|
+
...(renderSystem.temporalTargetInspection === undefined
|
|
2817
|
+
? {}
|
|
2818
|
+
: { temporalTarget: Object.freeze(renderSystem.temporalTargetInspection) }),
|
|
2819
|
+
...(renderSystem.motionBlurInspection === undefined
|
|
2820
|
+
? {}
|
|
2821
|
+
: { motionBlur: Object.freeze(renderSystem.motionBlurInspection) }),
|
|
2822
|
+
...(renderSystem.transmission === undefined
|
|
2823
|
+
? {}
|
|
2824
|
+
: { transmission: renderSystem.transmission }),
|
|
2596
2825
|
meshMaterialBindings: Object.freeze(
|
|
2597
2826
|
renderSystem.meshMaterialBindings.map((observation) =>
|
|
2598
2827
|
Object.freeze({
|
|
@@ -2611,6 +2840,22 @@ async function makeWebGPURenderer(
|
|
|
2611
2840
|
createBindGroup: renderSystem.bindGroupCounts.createBindGroup,
|
|
2612
2841
|
keys: Object.freeze([...renderSystem.bindGroupCounts.keys]),
|
|
2613
2842
|
}),
|
|
2843
|
+
...outputInspection,
|
|
2844
|
+
observation: Object.freeze({
|
|
2845
|
+
observationId,
|
|
2846
|
+
frameId,
|
|
2847
|
+
...(renderSystem.lastSuccessfulCameraAntialias === undefined
|
|
2848
|
+
? {}
|
|
2849
|
+
: { antialias: renderSystem.lastSuccessfulCameraAntialias }),
|
|
2850
|
+
...(outputInspection.surfaceProfile === undefined
|
|
2851
|
+
? {}
|
|
2852
|
+
: { surfaceProfile: outputInspection.surfaceProfile }),
|
|
2853
|
+
rgba16floatRenderable: internals.device.caps.rgba16floatRenderable,
|
|
2854
|
+
passNames: Object.freeze([...outputInspection.graphPassNames]),
|
|
2855
|
+
...(outputInspection.standardOutputColor === undefined
|
|
2856
|
+
? {}
|
|
2857
|
+
: { standardOutputColor: Object.freeze({ ...outputInspection.standardOutputColor }) }),
|
|
2858
|
+
}),
|
|
2614
2859
|
});
|
|
2615
2860
|
},
|
|
2616
2861
|
get device(): RhiDevice {
|
|
@@ -2701,6 +2946,12 @@ async function makeWebGPURenderer(
|
|
|
2701
2946
|
observeCurrentFrame(options: FrameObservationOptions) {
|
|
2702
2947
|
return renderSystem.observeCurrentFrame(options);
|
|
2703
2948
|
},
|
|
2949
|
+
getCurrentGraphTarget(name: string) {
|
|
2950
|
+
return renderSystem.getCurrentGraphTarget(name);
|
|
2951
|
+
},
|
|
2952
|
+
requestGraphTargetCapture(request) {
|
|
2953
|
+
renderSystem.requestGraphTargetCapture(request);
|
|
2954
|
+
},
|
|
2704
2955
|
get frustumStats() {
|
|
2705
2956
|
return renderSystem.frustumStats;
|
|
2706
2957
|
},
|
|
@@ -2972,7 +3223,12 @@ async function makeWebGPURenderer(
|
|
|
2972
3223
|
// Configure context lazily on first draw (D-S1 single-point
|
|
2973
3224
|
// exemption): GPUCanvasContext.configure({device}) needs a raw
|
|
2974
3225
|
// The canvas context is configured through the RHI device scope.
|
|
2975
|
-
ensureContextConfigured(
|
|
3226
|
+
const contextConfigured = ensureContextConfigured(
|
|
3227
|
+
internals,
|
|
3228
|
+
pipelineState,
|
|
3229
|
+
internals.errorRegistry,
|
|
3230
|
+
);
|
|
3231
|
+
if (!contextConfigured.ok) return contextConfigured;
|
|
2976
3232
|
// w24 — facade-level try/catch produces Result.err on unexpected throw
|
|
2977
3233
|
// (D-P6 dual-channel preserved: per-stage RhiError continues to fan out
|
|
2978
3234
|
// through onError separately; the facade Result is the synchronous
|
|
@@ -3140,13 +3396,6 @@ async function makeWebGPURenderer(
|
|
|
3140
3396
|
if (disposed) return ok(undefined);
|
|
3141
3397
|
disposed = true;
|
|
3142
3398
|
const cleanupFailures: RendererOperationCause[] = [];
|
|
3143
|
-
try {
|
|
3144
|
-
activeDeviceScope.dispose();
|
|
3145
|
-
} catch (cause) {
|
|
3146
|
-
const error = wrapDisposeError(cause, 'activeDeviceScope.dispose');
|
|
3147
|
-
cleanupFailures.push(error);
|
|
3148
|
-
internals.errorRegistry.fire(error);
|
|
3149
|
-
}
|
|
3150
3399
|
for (const world of attachedWorlds) {
|
|
3151
3400
|
try {
|
|
3152
3401
|
const derivedSystemName = derivedSystemNames.get(world);
|
|
@@ -3228,6 +3477,18 @@ async function makeWebGPURenderer(
|
|
|
3228
3477
|
cleanupFailures.push(error);
|
|
3229
3478
|
internals.errorRegistry.fire(error);
|
|
3230
3479
|
}
|
|
3480
|
+
// Retire assembly-owned device resources only after the surface and
|
|
3481
|
+
// renderer-owned GPU pools have been released. The wgpu WebGL2 surface
|
|
3482
|
+
// still needs a live device while `unconfigure()` presents its pending
|
|
3483
|
+
// image; disposing the scope first lets wasm finalizers race that
|
|
3484
|
+
// presentation and can surface a parking_lot panic in WebKit.
|
|
3485
|
+
try {
|
|
3486
|
+
activeDeviceScope.dispose();
|
|
3487
|
+
} catch (cause) {
|
|
3488
|
+
const error = wrapDisposeError(cause, 'activeDeviceScope.dispose');
|
|
3489
|
+
cleanupFailures.push(error);
|
|
3490
|
+
internals.errorRegistry.fire(error);
|
|
3491
|
+
}
|
|
3231
3492
|
// Step 5: the IBL cache is keyed by the retired DeviceScope generation.
|
|
3232
3493
|
// It is no longer reachable through the active renderer scope, while
|
|
3233
3494
|
// the device teardown owns the child GPU handles.
|
|
@@ -3307,6 +3568,7 @@ async function makeWebGPURenderer(
|
|
|
3307
3568
|
// buffer (or crash the browser GPU process). Keep the ShaderCatalog's
|
|
3308
3569
|
// CPU material/source catalogue, but rebuild this device-bound adapter.
|
|
3309
3570
|
sharedShaderModuleAdapter = null;
|
|
3571
|
+
sharedImmediateShaderModuleAdapter = null;
|
|
3310
3572
|
// The fullscreen post-process builder keeps one empty group-0 layout
|
|
3311
3573
|
// outside RenderSystem's frame cache. It is also device-bound, so retain
|
|
3312
3574
|
// the source/registration but force the next post-process pass to derive
|
|
@@ -3353,6 +3615,13 @@ async function makeWebGPURenderer(
|
|
|
3353
3615
|
// swap is observed without reconstructing the RenderSystem.
|
|
3354
3616
|
internals.device = device;
|
|
3355
3617
|
internals.context = ctxResult.value;
|
|
3618
|
+
// Build the replacement graph under a fresh renderer-owned scope. The
|
|
3619
|
+
// old committed scope remains authoritative until every rebuild step
|
|
3620
|
+
// succeeds, so a failed recovery cannot publish a half-built scope.
|
|
3621
|
+
const recoveryScope = DeviceScope.create(
|
|
3622
|
+
activeDeviceScope.generation + 1,
|
|
3623
|
+
activeDeviceScope.owner,
|
|
3624
|
+
);
|
|
3356
3625
|
// Re-attach the device.lost fan-out to the new device's lost Promise,
|
|
3357
3626
|
// reusing the SAME registries the host already subscribed to (SSOT
|
|
3358
3627
|
// helper). When this device is lost again, health() flips to
|
|
@@ -3400,10 +3669,13 @@ async function makeWebGPURenderer(
|
|
|
3400
3669
|
// the first post-recovery upload; unlike CPU asset payloads, these
|
|
3401
3670
|
// handles cannot survive a device replacement.
|
|
3402
3671
|
dynamicTextureStore.configureGpuDevice(adaptDynamicTextureDevice(internals.device));
|
|
3672
|
+
let candidatePipelineState: PipelineState | undefined;
|
|
3403
3673
|
const abortDisposedRecovery = (): Result<void, RecoverFailure> | undefined => {
|
|
3404
3674
|
if (!disposed) return undefined;
|
|
3405
3675
|
gpuStore.destroyAll();
|
|
3676
|
+
candidatePipelineState?.perPassResources.drainBloomResources?.();
|
|
3406
3677
|
renderSystem.disposeFrameState();
|
|
3678
|
+
recoveryScope.abandon();
|
|
3407
3679
|
materialShaderPipelineCache.clear();
|
|
3408
3680
|
perShaderMaterialLayoutCache.clear();
|
|
3409
3681
|
group0ResourceLayouts.clear();
|
|
@@ -3422,7 +3694,7 @@ async function makeWebGPURenderer(
|
|
|
3422
3694
|
);
|
|
3423
3695
|
const abortedAfterShaders = abortDisposedRecovery();
|
|
3424
3696
|
if (abortedAfterShaders !== undefined) return abortedAfterShaders;
|
|
3425
|
-
|
|
3697
|
+
candidatePipelineState = await buildPipeline(recoveryScope);
|
|
3426
3698
|
const abortedAfterPipeline = abortDisposedRecovery();
|
|
3427
3699
|
if (abortedAfterPipeline !== undefined) return abortedAfterPipeline;
|
|
3428
3700
|
renderSystem.restorePostProcessResources();
|
|
@@ -3430,6 +3702,8 @@ async function makeWebGPURenderer(
|
|
|
3430
3702
|
// Pipeline rebuild failed against the new device. Treat as a device
|
|
3431
3703
|
// unavailability (the device was acquired but is not usable); health
|
|
3432
3704
|
// stays `device-lost` so the host can retry.
|
|
3705
|
+
candidatePipelineState?.perPassResources.drainBloomResources?.();
|
|
3706
|
+
recoveryScope.abandon();
|
|
3433
3707
|
const aborted = abortDisposedRecovery();
|
|
3434
3708
|
if (aborted !== undefined) return aborted;
|
|
3435
3709
|
return err(new RecoverError('recover-device-unavailable'));
|
|
@@ -3437,27 +3711,18 @@ async function makeWebGPURenderer(
|
|
|
3437
3711
|
const abortedAfterPostProcess = abortDisposedRecovery();
|
|
3438
3712
|
if (abortedAfterPostProcess !== undefined) return abortedAfterPostProcess;
|
|
3439
3713
|
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
)
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
return err(
|
|
3447
|
-
new RenderRecoveryError(
|
|
3448
|
-
disposed ? 'recover-disposed-during-rebuild' : 'recover-lifecycle-failed',
|
|
3449
|
-
{
|
|
3450
|
-
owner: detail.owner,
|
|
3451
|
-
generation: detail.generation,
|
|
3452
|
-
recovery: disposed ? 'stop' : 'recover',
|
|
3453
|
-
receipt: replacementResult.error.receipt,
|
|
3454
|
-
cause: replacementResult.error,
|
|
3455
|
-
},
|
|
3456
|
-
),
|
|
3457
|
-
);
|
|
3714
|
+
// Promote only after the complete replacement build is ready. The old
|
|
3715
|
+
// scope is then retired exactly once; a failed attempt abandons its
|
|
3716
|
+
// candidate in the catch path and can be retried with a new generation.
|
|
3717
|
+
if (candidatePipelineState === undefined) {
|
|
3718
|
+
recoveryScope.abandon();
|
|
3719
|
+
return err(new RecoverError('recover-device-unavailable'));
|
|
3458
3720
|
}
|
|
3459
|
-
|
|
3721
|
+
const previousDeviceScope = activeDeviceScope;
|
|
3722
|
+
activeDeviceScope = recoveryScope;
|
|
3723
|
+
pipelineState = candidatePipelineState;
|
|
3460
3724
|
gpuStore.bindDeviceScope(activeDeviceScope);
|
|
3725
|
+
previousDeviceScope.retire();
|
|
3461
3726
|
|
|
3462
3727
|
// Re-evaluate feature capability gates against the replacement device.
|
|
3463
3728
|
// Feature recovery is isolated from renderer recovery: a feature that is
|
|
@@ -3555,7 +3820,7 @@ export function exposeRenderer(renderer: RendererHostImplementation): Renderer {
|
|
|
3555
3820
|
);
|
|
3556
3821
|
},
|
|
3557
3822
|
setProfile: (profile) => {
|
|
3558
|
-
if (currentState
|
|
3823
|
+
if (!isRendererStateOperational(currentState)) {
|
|
3559
3824
|
return err(
|
|
3560
3825
|
new RendererOperationError('renderer-state-invalid', {
|
|
3561
3826
|
operation: 'set-profile',
|
|
@@ -3569,6 +3834,13 @@ export function exposeRenderer(renderer: RendererHostImplementation): Renderer {
|
|
|
3569
3834
|
draw: (request) => {
|
|
3570
3835
|
const result = renderer.drawFrame(request);
|
|
3571
3836
|
if (!result.ok) return err(drawError(result.error));
|
|
3837
|
+
emit(
|
|
3838
|
+
Object.freeze({
|
|
3839
|
+
kind: 'frame-submitted',
|
|
3840
|
+
frameId: result.value.frameId,
|
|
3841
|
+
deviceGeneration: result.value.deviceGeneration,
|
|
3842
|
+
}),
|
|
3843
|
+
);
|
|
3572
3844
|
return ok(result.value);
|
|
3573
3845
|
},
|
|
3574
3846
|
inspect: () => renderer.inspect(),
|
|
@@ -3621,7 +3893,7 @@ export function exposeRenderer(renderer: RendererHostImplementation): Renderer {
|
|
|
3621
3893
|
const error = new RendererOperationError('recovery-failed', {
|
|
3622
3894
|
operation: 'recover',
|
|
3623
3895
|
oldGeneration,
|
|
3624
|
-
cause: result.error,
|
|
3896
|
+
cause: structuredRendererCause(result.error, 'recover'),
|
|
3625
3897
|
});
|
|
3626
3898
|
emit(Object.freeze({ kind: 'error', error }));
|
|
3627
3899
|
return err(error);
|
|
@@ -3664,13 +3936,24 @@ function structuredRendererCause(cause: unknown, operation: string): RendererOpe
|
|
|
3664
3936
|
typeof candidate.expected === 'string' &&
|
|
3665
3937
|
typeof candidate.hint === 'string'
|
|
3666
3938
|
) {
|
|
3667
|
-
return
|
|
3939
|
+
return {
|
|
3940
|
+
code: candidate.code,
|
|
3941
|
+
expected: candidate.expected,
|
|
3942
|
+
hint: candidate.hint,
|
|
3943
|
+
...(candidate.detail === undefined ? {} : { detail: candidate.detail }),
|
|
3944
|
+
};
|
|
3668
3945
|
}
|
|
3669
3946
|
}
|
|
3670
|
-
|
|
3947
|
+
const contractFailure = new RendererContractFailureError(
|
|
3671
3948
|
'draw',
|
|
3672
3949
|
`${operation}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
3673
3950
|
);
|
|
3951
|
+
return {
|
|
3952
|
+
code: contractFailure.code,
|
|
3953
|
+
expected: contractFailure.expected,
|
|
3954
|
+
hint: contractFailure.hint,
|
|
3955
|
+
detail: contractFailure.detail,
|
|
3956
|
+
};
|
|
3674
3957
|
}
|
|
3675
3958
|
|
|
3676
3959
|
const RENDER_PROFILE_KEYS = new Set([
|
|
@@ -3681,10 +3964,6 @@ const RENDER_PROFILE_KEYS = new Set([
|
|
|
3681
3964
|
'pbr',
|
|
3682
3965
|
'ibl',
|
|
3683
3966
|
'ssao',
|
|
3684
|
-
'bloom',
|
|
3685
|
-
'tone',
|
|
3686
|
-
'antialias',
|
|
3687
|
-
'sky',
|
|
3688
3967
|
'fallback',
|
|
3689
3968
|
'postStages',
|
|
3690
3969
|
]);
|
|
@@ -3705,32 +3984,13 @@ function validateRenderProfile(profile: RenderProfile): string | undefined {
|
|
|
3705
3984
|
if (profile.shadows !== 'off' && profile.shadows !== 'hard' && profile.shadows !== 'filtered') {
|
|
3706
3985
|
return "RenderProfile.shadows must be 'off', 'hard', or 'filtered'";
|
|
3707
3986
|
}
|
|
3708
|
-
if (
|
|
3709
|
-
profile.tone !== 'none' &&
|
|
3710
|
-
profile.tone !== 'aces-filmic' &&
|
|
3711
|
-
profile.tone !== 'agx' &&
|
|
3712
|
-
profile.tone !== 'cineon' &&
|
|
3713
|
-
profile.tone !== 'linear' &&
|
|
3714
|
-
profile.tone !== 'neutral' &&
|
|
3715
|
-
profile.tone !== 'reinhard' &&
|
|
3716
|
-
profile.tone !== 'reinhard-extended'
|
|
3717
|
-
) {
|
|
3718
|
-
return 'RenderProfile.tone is not a supported Standard tone';
|
|
3719
|
-
}
|
|
3720
|
-
if (
|
|
3721
|
-
profile.antialias !== 'none' &&
|
|
3722
|
-
profile.antialias !== 'fxaa' &&
|
|
3723
|
-
profile.antialias !== 'msaa'
|
|
3724
|
-
) {
|
|
3725
|
-
return "RenderProfile.antialias must be 'none', 'fxaa', or 'msaa'";
|
|
3726
|
-
}
|
|
3727
3987
|
if (profile.fallback !== 'native' && profile.fallback !== 'cpu-webgl2') {
|
|
3728
3988
|
return "RenderProfile.fallback must be 'native' or 'cpu-webgl2'";
|
|
3729
3989
|
}
|
|
3730
|
-
for (const field of ['pbr', 'ibl', 'ssao'
|
|
3990
|
+
for (const field of ['pbr', 'ibl', 'ssao'] as const) {
|
|
3731
3991
|
if (typeof profile[field] !== 'boolean') return `RenderProfile.${field} must be boolean`;
|
|
3732
3992
|
}
|
|
3733
|
-
const stages =
|
|
3993
|
+
const stages = STANDARD_POST_STAGE_NAMES;
|
|
3734
3994
|
if (
|
|
3735
3995
|
!Array.isArray(profile.postStages) ||
|
|
3736
3996
|
profile.postStages.length !== stages.length ||
|
|
@@ -3781,8 +4041,11 @@ interface ShaderDeviceAdapterInternal extends ShaderCatalogDevice {
|
|
|
3781
4041
|
* `module-forgeax::default-shadow-caster` returns OK on frame 1.
|
|
3782
4042
|
*/
|
|
3783
4043
|
seedModule(label: string, module: ShaderModule): void;
|
|
4044
|
+
invalidateModule(label: string): void;
|
|
3784
4045
|
}
|
|
3785
4046
|
|
|
4047
|
+
type ShaderModuleAdapterMode = 'validated' | 'immediate';
|
|
4048
|
+
|
|
3786
4049
|
function makeShaderDeviceAdapter(
|
|
3787
4050
|
rhiDevice: RhiDevice,
|
|
3788
4051
|
_errorRegistry: RhiErrorListenerRegistry,
|
|
@@ -3792,10 +4055,18 @@ function makeShaderDeviceAdapter(
|
|
|
3792
4055
|
desc: { code: string; label?: string | undefined },
|
|
3793
4056
|
) => Promise<Result<ShaderModule, RhiError>>)
|
|
3794
4057
|
| undefined,
|
|
4058
|
+
immediateCreateShaderModule:
|
|
4059
|
+
| ((
|
|
4060
|
+
device: RhiDevice,
|
|
4061
|
+
desc: { code: string; label?: string | undefined },
|
|
4062
|
+
) => Result<ShaderModule, RhiError>)
|
|
4063
|
+
| undefined,
|
|
4064
|
+
mode: ShaderModuleAdapterMode = 'validated',
|
|
3795
4065
|
): ShaderDeviceAdapterInternal {
|
|
3796
4066
|
const moduleCache = new Map<string, ShaderModule>();
|
|
3797
4067
|
const errorCache = new Map<string, RhiError>();
|
|
3798
|
-
const pending = new
|
|
4068
|
+
const pending = new Map<string, number>();
|
|
4069
|
+
const epochs = new Map<string, number>();
|
|
3799
4070
|
|
|
3800
4071
|
return {
|
|
3801
4072
|
createShaderModule(desc): Result<ShaderModule, RhiError> {
|
|
@@ -3805,18 +4076,39 @@ function makeShaderDeviceAdapter(
|
|
|
3805
4076
|
const cachedError = errorCache.get(key);
|
|
3806
4077
|
if (cachedError !== undefined) return err(cachedError);
|
|
3807
4078
|
|
|
4079
|
+
// WebGPU's raw createShaderModule is synchronous. Only the explicitly
|
|
4080
|
+
// selected immediate adapter uses that entry: generated feature programs
|
|
4081
|
+
// can avoid a slow getCompilationInfo() round trip, while the default
|
|
4082
|
+
// validated adapter keeps the compiler-readiness barrier for ordinary
|
|
4083
|
+
// render pipelines. The public async factory still owns diagnostics;
|
|
4084
|
+
// pipeline creation remains the validation point for the fast path.
|
|
4085
|
+
if (mode === 'immediate' && immediateCreateShaderModule !== undefined) {
|
|
4086
|
+
const immediateResult = immediateCreateShaderModule(rhiDevice, {
|
|
4087
|
+
code: desc.code,
|
|
4088
|
+
...(desc.label === undefined ? {} : { label: desc.label }),
|
|
4089
|
+
});
|
|
4090
|
+
if (immediateResult.ok) {
|
|
4091
|
+
moduleCache.set(key, immediateResult.value);
|
|
4092
|
+
return immediateResult;
|
|
4093
|
+
}
|
|
4094
|
+
errorCache.set(key, immediateResult.error);
|
|
4095
|
+
return immediateResult;
|
|
4096
|
+
}
|
|
4097
|
+
|
|
3808
4098
|
// Fire async creation; first sync call returns pending error (the AI consumer retry pattern).
|
|
3809
4099
|
// M3 D-P4: rhi-webgpu supplies the async factory; rhi-wgpu (no top-level
|
|
3810
4100
|
// async factory) falls back to the synchronous device.createShaderModule
|
|
3811
4101
|
// entry which already returns Result<ShaderModule, RhiError>.
|
|
3812
4102
|
if (!pending.has(key)) {
|
|
3813
|
-
|
|
4103
|
+
const epoch = epochs.get(key) ?? 0;
|
|
4104
|
+
pending.set(key, epoch);
|
|
3814
4105
|
const desc2: { code: string; label?: string | undefined } = { code: desc.code };
|
|
3815
4106
|
if (desc.label !== undefined) desc2.label = desc.label;
|
|
3816
4107
|
const asyncResult: Promise<Result<ShaderModule, RhiError>> = asyncCreateShaderModule
|
|
3817
4108
|
? asyncCreateShaderModule(rhiDevice, desc2)
|
|
3818
4109
|
: invokeDeviceCreateShaderModule(rhiDevice, desc2);
|
|
3819
4110
|
void asyncResult.then((result) => {
|
|
4111
|
+
if (pending.get(key) !== epoch || (epochs.get(key) ?? 0) !== epoch) return;
|
|
3820
4112
|
if (result.ok) {
|
|
3821
4113
|
moduleCache.set(key, result.value);
|
|
3822
4114
|
} else {
|
|
@@ -3831,6 +4123,12 @@ function makeShaderDeviceAdapter(
|
|
|
3831
4123
|
seedModule(label: string, module: ShaderModule): void {
|
|
3832
4124
|
moduleCache.set(label, module);
|
|
3833
4125
|
},
|
|
4126
|
+
invalidateModule(label: string): void {
|
|
4127
|
+
epochs.set(label, (epochs.get(label) ?? 0) + 1);
|
|
4128
|
+
moduleCache.delete(label);
|
|
4129
|
+
errorCache.delete(label);
|
|
4130
|
+
pending.delete(label);
|
|
4131
|
+
},
|
|
3834
4132
|
};
|
|
3835
4133
|
}
|
|
3836
4134
|
|
|
@@ -4076,7 +4374,11 @@ const HDR_COLOR_ATTACHMENT_FORMAT: GPUTextureFormat = 'rgba16float';
|
|
|
4076
4374
|
// so it is intentionally absent here. A `mipmap:true` texture in an
|
|
4077
4375
|
// un-prewarmed format surfaces a structured RhiError at record (never an
|
|
4078
4376
|
// async stall) -- the falsifiable anchor for an incomplete prewarm list.
|
|
4079
|
-
const MIPMAP_PREWARM_FORMATS: readonly GPUTextureFormat[] = [
|
|
4377
|
+
const MIPMAP_PREWARM_FORMATS: readonly GPUTextureFormat[] = [
|
|
4378
|
+
'rgba8unorm-srgb',
|
|
4379
|
+
'rgba8unorm',
|
|
4380
|
+
'rgba16float',
|
|
4381
|
+
];
|
|
4080
4382
|
|
|
4081
4383
|
// feat-20260531-bloom-first-declarative-render-graph-pass / w13:
|
|
4082
4384
|
// UBO sizes for bloom params (std140 layout).
|
|
@@ -4490,6 +4792,13 @@ async function prepareMaterialShaders(
|
|
|
4490
4792
|
const variantDefines: Record<string, boolean> = {
|
|
4491
4793
|
STORAGE_BUFFER_AVAILABLE: storageBufferCapable,
|
|
4492
4794
|
};
|
|
4795
|
+
// User material manifests may declare WEBGL2_COMPAT alongside the shared
|
|
4796
|
+
// mesh-storage axis. The fallback backend must select that authored
|
|
4797
|
+
// branch at boot; otherwise it asks for the impossible bare
|
|
4798
|
+
// `STORAGE_BUFFER_AVAILABLE=false` key and fails before the first frame.
|
|
4799
|
+
if (msEntry.variants.some((v) => 'WEBGL2_COMPAT' in v.defines)) {
|
|
4800
|
+
variantDefines.WEBGL2_COMPAT = webgl2Downlevel;
|
|
4801
|
+
}
|
|
4493
4802
|
// Only include CLUSTER_FORWARD_AVAILABLE when the material shader declares
|
|
4494
4803
|
// the axis (has a variant with this key), preserving backward compat for
|
|
4495
4804
|
// entries without the pragma.
|
|
@@ -4499,6 +4808,12 @@ async function prepareMaterialShaders(
|
|
|
4499
4808
|
if (msEntry.variants.some((v) => 'VERTEX_COLOR_AVAILABLE' in v.defines)) {
|
|
4500
4809
|
variantDefines.VERTEX_COLOR_AVAILABLE = false;
|
|
4501
4810
|
}
|
|
4811
|
+
if (
|
|
4812
|
+
msEntry.identifier === 'forgeax::default-standard-pbr' &&
|
|
4813
|
+
msEntry.variants.some((v) => 'TRANSMISSION_AVAILABLE' in v.defines)
|
|
4814
|
+
) {
|
|
4815
|
+
variantDefines.TRANSMISSION_AVAILABLE = false;
|
|
4816
|
+
}
|
|
4502
4817
|
// bug-20260708 M2 (a): PER_INSTANCE_REGION axis default = false at boot.
|
|
4503
4818
|
// Per-entity spritePH path (`main-pass-sprite-draws.ts:511-513`) expects
|
|
4504
4819
|
// `variantSet=undefined` to resolve to the PIR=false shader source (64B
|
|
@@ -4513,6 +4828,9 @@ async function prepareMaterialShaders(
|
|
|
4513
4828
|
if (msEntry.variants.some((v) => 'PER_INSTANCE_REGION' in v.defines)) {
|
|
4514
4829
|
variantDefines.PER_INSTANCE_REGION = false;
|
|
4515
4830
|
}
|
|
4831
|
+
if (msEntry.variants.some((v) => 'SKINNING_DISABLED' in v.defines)) {
|
|
4832
|
+
variantDefines.SKINNING_DISABLED = true;
|
|
4833
|
+
}
|
|
4516
4834
|
const sortedEntries = Object.entries(variantDefines).sort(([a], [b]) =>
|
|
4517
4835
|
a < b ? -1 : a > b ? 1 : 0,
|
|
4518
4836
|
);
|
|
@@ -4520,6 +4838,13 @@ async function prepareMaterialShaders(
|
|
|
4520
4838
|
? ''
|
|
4521
4839
|
: sortedEntries.map(([k, v]) => `${k}=${v}`).join('+');
|
|
4522
4840
|
const chosen = findVariantByKey(msEntry, definesKey);
|
|
4841
|
+
if (msEntry.variants.length > 0 && chosen === undefined) {
|
|
4842
|
+
throw new RhiError({
|
|
4843
|
+
code: 'shader-compile-failed',
|
|
4844
|
+
expected: `material shader '${msEntry.identifier}' declares the exact boot variant '${definesKey}'`,
|
|
4845
|
+
hint: 'regenerate the shader manifest with the complete variant Cartesian product',
|
|
4846
|
+
});
|
|
4847
|
+
}
|
|
4523
4848
|
const wgsl = chosen?.composedWgsl ?? msEntry.composedWgsl;
|
|
4524
4849
|
if (wgsl.length > 0) {
|
|
4525
4850
|
const existing = registry.findMaterialArtifact(msEntry.identifier);
|
|
@@ -4601,6 +4926,7 @@ async function prepareMaterialShaders(
|
|
|
4601
4926
|
|
|
4602
4927
|
async function buildReadyWebGPU(
|
|
4603
4928
|
rhiDevice: RhiDevice,
|
|
4929
|
+
rendererScope: DeviceScope,
|
|
4604
4930
|
getShader: () => ShaderCatalog,
|
|
4605
4931
|
gpuStore: GpuResidencyCache,
|
|
4606
4932
|
asyncCreateShaderModule:
|
|
@@ -4654,6 +4980,10 @@ async function buildReadyWebGPU(
|
|
|
4654
4980
|
// tonemap source resolves inside this async function (after the manifest-load
|
|
4655
4981
|
// await), by which point the synchronous `renderSystem` const is defined.
|
|
4656
4982
|
registerBuiltinTonemap: (source: string) => void,
|
|
4983
|
+
registerBuiltinTemporalPostProcesses: (entries: {
|
|
4984
|
+
readonly motionBlur?: string;
|
|
4985
|
+
readonly taaResolve?: string;
|
|
4986
|
+
}) => void,
|
|
4657
4987
|
): Promise<PipelineState> {
|
|
4658
4988
|
// bug-20260610: WebGL2 fallback path — read storage-buffer capability up
|
|
4659
4989
|
// front so both Step 2 (shader-module compile, where we patch engine
|
|
@@ -4680,7 +5010,10 @@ async function buildReadyWebGPU(
|
|
|
4680
5010
|
// pipeline target, configure() call, and color-attachment format —
|
|
4681
5011
|
// single SSOT, no scattered branches. See selectSwapChainFormat
|
|
4682
5012
|
// (above the SWAP_CHAIN_*_FORMAT historical constants).
|
|
4683
|
-
const
|
|
5013
|
+
const surfaceViewFormats =
|
|
5014
|
+
(rhiDevice as unknown as { readonly surfaceViewFormats?: boolean }).surfaceViewFormats ??
|
|
5015
|
+
!webgl2Downlevel;
|
|
5016
|
+
const swapChainFormats = selectSwapChainFormat(storageBufferCapable, surfaceViewFormats);
|
|
4684
5017
|
// The 'null' (headless RhiNull) backend has no UA preferred-canvas-format by
|
|
4685
5018
|
// design; the rgba8unorm fallback is its intended steady state, not a
|
|
4686
5019
|
// degraded one — firing 'rhi-not-available' there is territorially wrong
|
|
@@ -4785,6 +5118,7 @@ async function buildReadyWebGPU(
|
|
|
4785
5118
|
let pbrEntry = findEngineManifestEntry('forgeax::default-standard-pbr');
|
|
4786
5119
|
let unlitEntry = findEngineManifestEntry('forgeax::default-unlit');
|
|
4787
5120
|
let tonemapEntry: ManifestEntry | undefined;
|
|
5121
|
+
let motionBlurEntry: ManifestEntry | undefined;
|
|
4788
5122
|
let spriteEntry: ManifestEntry | undefined;
|
|
4789
5123
|
// sprite-lit identification marker is the `spriteLitShadeAccum`
|
|
4790
5124
|
// helper (defined in sprite-lit.wgsl, absent from sprite.wgsl). The
|
|
@@ -4794,6 +5128,7 @@ async function buildReadyWebGPU(
|
|
|
4794
5128
|
// their paramSchema mirror) does not mis-classify sprite-lit as
|
|
4795
5129
|
// sprite.
|
|
4796
5130
|
let spriteLitEntry: ManifestEntry | undefined;
|
|
5131
|
+
let taaResolveEntry: ManifestEntry | undefined;
|
|
4797
5132
|
let iblEquirectEntry: ManifestEntry | undefined;
|
|
4798
5133
|
let iblIrradianceEntry: ManifestEntry | undefined;
|
|
4799
5134
|
let iblPrefilterEntry: ManifestEntry | undefined;
|
|
@@ -4818,6 +5153,14 @@ async function buildReadyWebGPU(
|
|
|
4818
5153
|
tonemapEntry ??= entry;
|
|
4819
5154
|
continue;
|
|
4820
5155
|
}
|
|
5156
|
+
if (entry.wgsl.includes('MotionBlurParams')) {
|
|
5157
|
+
motionBlurEntry ??= entry;
|
|
5158
|
+
continue;
|
|
5159
|
+
}
|
|
5160
|
+
if (entry.wgsl.includes('fs_taa_resolve')) {
|
|
5161
|
+
taaResolveEntry ??= entry;
|
|
5162
|
+
continue;
|
|
5163
|
+
}
|
|
4821
5164
|
if (entry.wgsl.includes('spriteLitShadeAccum')) {
|
|
4822
5165
|
// sprite-lit identification — must precede the `pivotAndSize`
|
|
4823
5166
|
// marker since sprite-lit also carries that field (paramSchema
|
|
@@ -4959,10 +5302,10 @@ async function buildReadyWebGPU(
|
|
|
4959
5302
|
(!('CLUSTER_FORWARD_AVAILABLE' in v.defines) ||
|
|
4960
5303
|
v.defines.CLUSTER_FORWARD_AVAILABLE === false) &&
|
|
4961
5304
|
(!('VERTEX_COLOR_AVAILABLE' in v.defines) ||
|
|
4962
|
-
v.defines.VERTEX_COLOR_AVAILABLE === false)
|
|
5305
|
+
v.defines.VERTEX_COLOR_AVAILABLE === false) &&
|
|
5306
|
+
(!('SKINNING_DISABLED' in v.defines) || v.defines.SKINNING_DISABLED === true),
|
|
4963
5307
|
);
|
|
4964
|
-
const v =
|
|
4965
|
-
exact ?? ms.variants.find((cand) => cand.defines.STORAGE_BUFFER_AVAILABLE === false);
|
|
5308
|
+
const v = exact === undefined ? undefined : findVariantByKey(ms, exact.definesKey);
|
|
4966
5309
|
if (v !== undefined) {
|
|
4967
5310
|
idToVariantWgsl.set(ms.identifier, v.composedWgsl);
|
|
4968
5311
|
}
|
|
@@ -5002,13 +5345,13 @@ async function buildReadyWebGPU(
|
|
|
5002
5345
|
(!('CLUSTER_FORWARD_AVAILABLE' in variant.defines) ||
|
|
5003
5346
|
variant.defines.CLUSTER_FORWARD_AVAILABLE === false) &&
|
|
5004
5347
|
(!('VERTEX_COLOR_AVAILABLE' in variant.defines) ||
|
|
5005
|
-
variant.defines.VERTEX_COLOR_AVAILABLE === false)
|
|
5348
|
+
variant.defines.VERTEX_COLOR_AVAILABLE === false) &&
|
|
5349
|
+
(!('SKINNING_DISABLED' in variant.defines) ||
|
|
5350
|
+
variant.defines.SKINNING_DISABLED === true) &&
|
|
5351
|
+
(!('TRANSMISSION_AVAILABLE' in variant.defines) ||
|
|
5352
|
+
variant.defines.TRANSMISSION_AVAILABLE === false),
|
|
5006
5353
|
);
|
|
5007
|
-
const variant =
|
|
5008
|
-
exact ??
|
|
5009
|
-
ms.variants.find(
|
|
5010
|
-
(candidate) => candidate.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable,
|
|
5011
|
-
);
|
|
5354
|
+
const variant = exact === undefined ? undefined : findVariantByKey(ms, exact.definesKey);
|
|
5012
5355
|
if (variant !== undefined) {
|
|
5013
5356
|
idToRuntimeVariantWgsl.set(ms.identifier, variant.composedWgsl);
|
|
5014
5357
|
}
|
|
@@ -5082,6 +5425,34 @@ async function buildReadyWebGPU(
|
|
|
5082
5425
|
brdfLut: iblBrdfLutEntry.wgsl,
|
|
5083
5426
|
});
|
|
5084
5427
|
}
|
|
5428
|
+
// Points/Lines are an engine-owned record path rather than a producer
|
|
5429
|
+
// RenderFeature, so they cannot declare `requiredMaterialShaders`. Their
|
|
5430
|
+
// material pipeline still goes through the same lazy shader-module adapter
|
|
5431
|
+
// as producer features. Prewarm the optional manifest artifact here so a
|
|
5432
|
+
// first frame cannot race async module compilation and silently skip its
|
|
5433
|
+
// draw while the adapter is still pending. Older/minimal manifests may not
|
|
5434
|
+
// carry the optional entry; those hosts retain their existing behavior.
|
|
5435
|
+
const pointsLinesLookup = registry.findMaterialArtifact(POINTS_LINES_MATERIAL_SHADER_ID);
|
|
5436
|
+
if (pointsLinesLookup.ok) {
|
|
5437
|
+
const pointsLinesLabel = `module-${POINTS_LINES_MATERIAL_SHADER_ID}`;
|
|
5438
|
+
const pointsLinesShaderResult = await runShimStep(
|
|
5439
|
+
() =>
|
|
5440
|
+
asyncCreateShaderModule
|
|
5441
|
+
? asyncCreateShaderModule(rhiDevice, {
|
|
5442
|
+
code: pointsLinesLookup.value.source,
|
|
5443
|
+
label: pointsLinesLabel,
|
|
5444
|
+
})
|
|
5445
|
+
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5446
|
+
code: pointsLinesLookup.value.source,
|
|
5447
|
+
label: pointsLinesLabel,
|
|
5448
|
+
}),
|
|
5449
|
+
'shader-compile-failed',
|
|
5450
|
+
`engine material shader '${POINTS_LINES_MATERIAL_SHADER_ID}' compiled`,
|
|
5451
|
+
`inspect the composed WGSL for '${POINTS_LINES_MATERIAL_SHADER_ID}' and check device.features`,
|
|
5452
|
+
);
|
|
5453
|
+
if (!pointsLinesShaderResult.ok) throw pointsLinesShaderResult.error;
|
|
5454
|
+
seedShaderModule(pointsLinesLabel, pointsLinesShaderResult.value);
|
|
5455
|
+
}
|
|
5085
5456
|
// Producer-declared material shader modules use the same source and module
|
|
5086
5457
|
// label as getMaterialShaderPipeline. Prewarming them here keeps the
|
|
5087
5458
|
// renderer.initialization barrier meaningful for prepared graphics: the first draw
|
|
@@ -5136,6 +5507,12 @@ async function buildReadyWebGPU(
|
|
|
5136
5507
|
if (pbrVariant !== undefined) {
|
|
5137
5508
|
seedShaderModule(`module-forgeax::default-standard-pbr#${pbrVariant.definesKey}`, pbrModule);
|
|
5138
5509
|
}
|
|
5510
|
+
const transmissionCapable =
|
|
5511
|
+
(rhiDevice.limits.maxSampledTexturesPerShaderStage ?? 0) >=
|
|
5512
|
+
STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
5513
|
+
const transmissionVariants = transmissionCapable
|
|
5514
|
+
? selectStandardPbrTransmissionPrewarmVariants(pbrManifestEntry, storageBufferCapable)
|
|
5515
|
+
: [];
|
|
5139
5516
|
|
|
5140
5517
|
// HDRP uses the canonical all-true variant key (`''`) and the lazy
|
|
5141
5518
|
// material pipeline adapter therefore requests a distinct module label.
|
|
@@ -5143,9 +5520,35 @@ async function buildReadyWebGPU(
|
|
|
5143
5520
|
// label. Color availability is part of the geometry-owned variant axis, so
|
|
5144
5521
|
// both colored and no-color HDRP sources must be retained.
|
|
5145
5522
|
const prewarmedHdrpModules = new Map<string, ShaderModule>([[pbrEntry.wgsl, pbrModule]]);
|
|
5523
|
+
for (const transmissionVariant of transmissionVariants) {
|
|
5524
|
+
const moduleLabel = `module-forgeax::default-standard-pbr#${transmissionVariant.definesKey}`;
|
|
5525
|
+
let transmissionModule = prewarmedHdrpModules.get(transmissionVariant.composedWgsl);
|
|
5526
|
+
if (transmissionModule === undefined) {
|
|
5527
|
+
const transmissionModuleResult = await runShimStep(
|
|
5528
|
+
() =>
|
|
5529
|
+
asyncCreateShaderModule
|
|
5530
|
+
? asyncCreateShaderModule(rhiDevice, {
|
|
5531
|
+
code: transmissionVariant.composedWgsl,
|
|
5532
|
+
label: moduleLabel,
|
|
5533
|
+
})
|
|
5534
|
+
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5535
|
+
code: transmissionVariant.composedWgsl,
|
|
5536
|
+
label: moduleLabel,
|
|
5537
|
+
}),
|
|
5538
|
+
'shader-compile-failed',
|
|
5539
|
+
'Standard transmission shader module compiled',
|
|
5540
|
+
'inspect the composed Standard transmission variant WGSL and check device.features',
|
|
5541
|
+
);
|
|
5542
|
+
if (!transmissionModuleResult.ok) throw transmissionModuleResult.error;
|
|
5543
|
+
transmissionModule = transmissionModuleResult.value;
|
|
5544
|
+
prewarmedHdrpModules.set(transmissionVariant.composedWgsl, transmissionModule);
|
|
5545
|
+
}
|
|
5546
|
+
seedShaderModule(moduleLabel, transmissionModule);
|
|
5547
|
+
}
|
|
5146
5548
|
for (const hdrpVariant of selectHdrpPbrPrewarmVariants(
|
|
5147
5549
|
pbrManifestEntry,
|
|
5148
5550
|
storageBufferCapable,
|
|
5551
|
+
transmissionCapable,
|
|
5149
5552
|
)) {
|
|
5150
5553
|
const moduleLabel = `module-forgeax::default-standard-pbr#${hdrpVariant.definesKey}`;
|
|
5151
5554
|
let hdrpModule = prewarmedHdrpModules.get(hdrpVariant.composedWgsl);
|
|
@@ -5281,6 +5684,49 @@ async function buildReadyWebGPU(
|
|
|
5281
5684
|
// uses so the lazy build's createShaderModule call hits OK on
|
|
5282
5685
|
// frame 1.
|
|
5283
5686
|
seedShaderModule('module-forgeax::default-shadow-caster', shadowCasterShaderResult.value);
|
|
5687
|
+
|
|
5688
|
+
// The shadow caster now has independent storage and skinning axes. The
|
|
5689
|
+
// canonical all-true entry remains the ordinary static source through
|
|
5690
|
+
// SKINNING_DISABLED=true; skinned meshes request the explicit false
|
|
5691
|
+
// variant. Prewarm both for the active storage capability so neither
|
|
5692
|
+
// path loses its first shadow frame to the lazy module retry contract.
|
|
5693
|
+
const shadowCasterManifestEntry = Array.from(registry.materialShaderManifestEntries()).find(
|
|
5694
|
+
(candidate) => candidate.identifier === 'forgeax::default-shadow-caster',
|
|
5695
|
+
);
|
|
5696
|
+
const activeShadowVariants = shadowCasterManifestEntry?.variants.filter(
|
|
5697
|
+
(variant) =>
|
|
5698
|
+
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
5699
|
+
typeof variant.defines.SKINNING_DISABLED === 'boolean',
|
|
5700
|
+
);
|
|
5701
|
+
for (const variant of activeShadowVariants ?? []) {
|
|
5702
|
+
if (variant.definesKey === '') {
|
|
5703
|
+
seedShaderModule(
|
|
5704
|
+
'module-forgeax::default-shadow-caster#',
|
|
5705
|
+
shadowCasterShaderResult.value,
|
|
5706
|
+
);
|
|
5707
|
+
continue;
|
|
5708
|
+
}
|
|
5709
|
+
const variantShaderResult = await runShimStep(
|
|
5710
|
+
() =>
|
|
5711
|
+
asyncCreateShaderModule
|
|
5712
|
+
? asyncCreateShaderModule(rhiDevice, {
|
|
5713
|
+
code: variant.composedWgsl,
|
|
5714
|
+
label: `shadow_caster#${variant.definesKey}`,
|
|
5715
|
+
})
|
|
5716
|
+
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5717
|
+
code: variant.composedWgsl,
|
|
5718
|
+
label: `shadow_caster#${variant.definesKey}`,
|
|
5719
|
+
}),
|
|
5720
|
+
'shader-compile-failed',
|
|
5721
|
+
`shadow_caster variant ${variant.definesKey || '<default>'} compiled`,
|
|
5722
|
+
'inspect the selected shadow_caster storage/skinning variant and device.features',
|
|
5723
|
+
);
|
|
5724
|
+
if (!variantShaderResult.ok) throw variantShaderResult.error;
|
|
5725
|
+
seedShaderModule(
|
|
5726
|
+
`module-forgeax::default-shadow-caster#${variant.definesKey}`,
|
|
5727
|
+
variantShaderResult.value,
|
|
5728
|
+
);
|
|
5729
|
+
}
|
|
5284
5730
|
}
|
|
5285
5731
|
|
|
5286
5732
|
// feat-20260621 M-A3 (D-5): register the built-in tonemap onto the unified
|
|
@@ -5300,27 +5746,62 @@ async function buildReadyWebGPU(
|
|
|
5300
5746
|
// forever. Mirror the shadow_caster prewarm: await-compile the tonemap module
|
|
5301
5747
|
// here and `seedShaderModule` it under the exact label
|
|
5302
5748
|
// `buildPostProcessPipeline` requests (`post-process-${id}-module`), so the
|
|
5303
|
-
// first `getPostProcessPipeline(
|
|
5749
|
+
// first `getPostProcessPipeline(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, …)` hits the module cache
|
|
5304
5750
|
// synchronously and builds the pipeline on frame 1.
|
|
5305
5751
|
const tonemapPrewarm = await runShimStep(
|
|
5306
5752
|
() =>
|
|
5307
5753
|
asyncCreateShaderModule
|
|
5308
5754
|
? asyncCreateShaderModule(rhiDevice, {
|
|
5309
5755
|
code: tonemapEntry.wgsl,
|
|
5310
|
-
label: `post-process-${
|
|
5756
|
+
label: `post-process-${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}-module`,
|
|
5311
5757
|
})
|
|
5312
5758
|
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5313
5759
|
code: tonemapEntry.wgsl,
|
|
5314
|
-
label: `post-process-${
|
|
5760
|
+
label: `post-process-${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}-module`,
|
|
5315
5761
|
}),
|
|
5316
5762
|
'shader-compile-failed',
|
|
5317
5763
|
'tonemap shader module compiled (unified post-process prewarm)',
|
|
5318
5764
|
'inspect manifest tonemap entry composed wgsl; check device.features',
|
|
5319
5765
|
);
|
|
5320
5766
|
if (!tonemapPrewarm.ok) throw tonemapPrewarm.error;
|
|
5321
|
-
seedShaderModule(
|
|
5767
|
+
seedShaderModule(
|
|
5768
|
+
`post-process-${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}-module`,
|
|
5769
|
+
tonemapPrewarm.value,
|
|
5770
|
+
);
|
|
5322
5771
|
registerBuiltinTonemap(tonemapEntry.wgsl);
|
|
5323
5772
|
|
|
5773
|
+
// Temporal fullscreen passes execute synchronously from the compiled graph,
|
|
5774
|
+
// so their shader modules must be warmed before the initialization barrier
|
|
5775
|
+
// resolves. Otherwise the first tight draw loop sees an asynchronous
|
|
5776
|
+
// `rhi-not-available` result and leaves the temporal targets cleared even
|
|
5777
|
+
// though the graph topology is present.
|
|
5778
|
+
const prewarmTemporal = async (entry: ManifestEntry | undefined, id: string) => {
|
|
5779
|
+
if (entry === undefined) return;
|
|
5780
|
+
const prewarm = await runShimStep(
|
|
5781
|
+
() =>
|
|
5782
|
+
asyncCreateShaderModule
|
|
5783
|
+
? asyncCreateShaderModule(rhiDevice, {
|
|
5784
|
+
code: entry.wgsl,
|
|
5785
|
+
label: `post-process-${id}-module`,
|
|
5786
|
+
})
|
|
5787
|
+
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5788
|
+
code: entry.wgsl,
|
|
5789
|
+
label: `post-process-${id}-module`,
|
|
5790
|
+
}),
|
|
5791
|
+
'shader-compile-failed',
|
|
5792
|
+
`${id} shader module compiled (unified post-process prewarm)`,
|
|
5793
|
+
`inspect manifest ${id} entry composed wgsl; check device.features`,
|
|
5794
|
+
);
|
|
5795
|
+
if (!prewarm.ok) throw prewarm.error;
|
|
5796
|
+
seedShaderModule(`post-process-${id}-module`, prewarm.value);
|
|
5797
|
+
};
|
|
5798
|
+
await prewarmTemporal(motionBlurEntry, 'forgeax.motion-blur');
|
|
5799
|
+
await prewarmTemporal(taaResolveEntry, 'forgeax.taa-resolve');
|
|
5800
|
+
registerBuiltinTemporalPostProcesses({
|
|
5801
|
+
...(motionBlurEntry === undefined ? {} : { motionBlur: motionBlurEntry.wgsl }),
|
|
5802
|
+
...(taaResolveEntry === undefined ? {} : { taaResolve: taaResolveEntry.wgsl }),
|
|
5803
|
+
});
|
|
5804
|
+
|
|
5324
5805
|
// feat-20260520-2d-sprite-layer-mvp M-3 / w24: sprite shader module
|
|
5325
5806
|
// is optional in the 3-tuple legacy manifest (back-compat for apps
|
|
5326
5807
|
// that locked their manifest URL before this feat). With the
|
|
@@ -5356,6 +5837,36 @@ async function buildReadyWebGPU(
|
|
|
5356
5837
|
// shares the generic per-MaterialShader lazy build path, which now
|
|
5357
5838
|
// needs the explicit seed to preserve frame-1 readiness.
|
|
5358
5839
|
seedShaderModule('module-forgeax::sprite', spriteShaderResult.value);
|
|
5840
|
+
const spriteManifestEntry = Array.from(registry.materialShaderManifestEntries()).find(
|
|
5841
|
+
(entry) => entry.identifier === 'forgeax::sprite',
|
|
5842
|
+
);
|
|
5843
|
+
const regionVariant = spriteManifestEntry?.variants.find(
|
|
5844
|
+
(variant) =>
|
|
5845
|
+
variant.defines.PER_INSTANCE_REGION === true &&
|
|
5846
|
+
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable,
|
|
5847
|
+
);
|
|
5848
|
+
if (regionVariant !== undefined) {
|
|
5849
|
+
const regionShaderResult = await runShimStep(
|
|
5850
|
+
() =>
|
|
5851
|
+
asyncCreateShaderModule
|
|
5852
|
+
? asyncCreateShaderModule(rhiDevice, {
|
|
5853
|
+
code: regionVariant.composedWgsl,
|
|
5854
|
+
label: `sprite#${regionVariant.definesKey}`,
|
|
5855
|
+
})
|
|
5856
|
+
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5857
|
+
code: regionVariant.composedWgsl,
|
|
5858
|
+
label: `sprite#${regionVariant.definesKey}`,
|
|
5859
|
+
}),
|
|
5860
|
+
'shader-compile-failed',
|
|
5861
|
+
`sprite variant ${regionVariant.definesKey || '<default>'} compiled`,
|
|
5862
|
+
'inspect the selected sprite PER_INSTANCE_REGION variant and device.features',
|
|
5863
|
+
);
|
|
5864
|
+
if (!regionShaderResult.ok) throw regionShaderResult.error;
|
|
5865
|
+
seedShaderModule(
|
|
5866
|
+
`module-forgeax::sprite#${regionVariant.definesKey}`,
|
|
5867
|
+
regionShaderResult.value,
|
|
5868
|
+
);
|
|
5869
|
+
}
|
|
5359
5870
|
}
|
|
5360
5871
|
|
|
5361
5872
|
// feat-20260624-sprite-lit-shading-model-pure-2d-lighting M1' / t7:
|
|
@@ -5428,15 +5939,12 @@ async function buildReadyWebGPU(
|
|
|
5428
5939
|
skyboxModule = skyboxShaderResult.value;
|
|
5429
5940
|
}
|
|
5430
5941
|
|
|
5431
|
-
//
|
|
5432
|
-
//
|
|
5433
|
-
//
|
|
5434
|
-
//
|
|
5435
|
-
// (see triage above; struct names survive naga_oil composition). When
|
|
5436
|
-
// present, each module is compiled here; pipeline construction happens
|
|
5437
|
-
// alongside the FXAA / tonemap pipelines below.
|
|
5942
|
+
// Bloom shader modules remain available to the lazy Bloom pipeline owner.
|
|
5943
|
+
// Their construction is still part of renderer readiness because the
|
|
5944
|
+
// backend may expose only an asynchronous module factory; the expensive
|
|
5945
|
+
// pipeline, binding, and parameter resources stay deferred until admission.
|
|
5438
5946
|
if (bloomBrightEntry !== undefined) {
|
|
5439
|
-
const
|
|
5947
|
+
const result = await runShimStep(
|
|
5440
5948
|
() =>
|
|
5441
5949
|
asyncCreateShaderModule
|
|
5442
5950
|
? asyncCreateShaderModule(rhiDevice, {
|
|
@@ -5451,17 +5959,14 @@ async function buildReadyWebGPU(
|
|
|
5451
5959
|
'bloom-bright shader module compiled',
|
|
5452
5960
|
'inspect manifest bloom-bright entry composed wgsl; check device.features',
|
|
5453
5961
|
);
|
|
5454
|
-
if (!
|
|
5455
|
-
bloomBrightModule =
|
|
5962
|
+
if (!result.ok) throw result.error;
|
|
5963
|
+
bloomBrightModule = result.value;
|
|
5456
5964
|
}
|
|
5457
5965
|
if (bloomBlurEntry !== undefined) {
|
|
5458
|
-
const
|
|
5966
|
+
const result = await runShimStep(
|
|
5459
5967
|
() =>
|
|
5460
5968
|
asyncCreateShaderModule
|
|
5461
|
-
? asyncCreateShaderModule(rhiDevice, {
|
|
5462
|
-
code: bloomBlurEntry.wgsl,
|
|
5463
|
-
label: 'bloom-blur',
|
|
5464
|
-
})
|
|
5969
|
+
? asyncCreateShaderModule(rhiDevice, { code: bloomBlurEntry.wgsl, label: 'bloom-blur' })
|
|
5465
5970
|
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5466
5971
|
code: bloomBlurEntry.wgsl,
|
|
5467
5972
|
label: 'bloom-blur',
|
|
@@ -5470,11 +5975,11 @@ async function buildReadyWebGPU(
|
|
|
5470
5975
|
'bloom-blur shader module compiled',
|
|
5471
5976
|
'inspect manifest bloom-blur entry composed wgsl; check device.features',
|
|
5472
5977
|
);
|
|
5473
|
-
if (!
|
|
5474
|
-
bloomBlurModule =
|
|
5978
|
+
if (!result.ok) throw result.error;
|
|
5979
|
+
bloomBlurModule = result.value;
|
|
5475
5980
|
}
|
|
5476
5981
|
if (bloomCompositeEntry !== undefined) {
|
|
5477
|
-
const
|
|
5982
|
+
const result = await runShimStep(
|
|
5478
5983
|
() =>
|
|
5479
5984
|
asyncCreateShaderModule
|
|
5480
5985
|
? asyncCreateShaderModule(rhiDevice, {
|
|
@@ -5489,8 +5994,8 @@ async function buildReadyWebGPU(
|
|
|
5489
5994
|
'bloom-composite shader module compiled',
|
|
5490
5995
|
'inspect manifest bloom-composite entry composed wgsl; check device.features',
|
|
5491
5996
|
);
|
|
5492
|
-
if (!
|
|
5493
|
-
bloomCompositeModule =
|
|
5997
|
+
if (!result.ok) throw result.error;
|
|
5998
|
+
bloomCompositeModule = result.value;
|
|
5494
5999
|
}
|
|
5495
6000
|
// feat-20260612-hdrp-ssao M6 / w27: SSAO shader module compilation.
|
|
5496
6001
|
// Optional manifest entry — absent on legacy manifests (zero-overhead
|
|
@@ -5522,11 +6027,11 @@ async function buildReadyWebGPU(
|
|
|
5522
6027
|
// The factory builds 4 BindGroupLayouts (view + material + mesh-array +
|
|
5523
6028
|
// instances) and the 4-slot pipeline layout in one call, throwing on each
|
|
5524
6029
|
// device.createBindGroupLayout / device.createPipelineLayout failure.
|
|
5525
|
-
// material BGL now carries
|
|
6030
|
+
// material BGL now carries 24 entries (material 0..16 + Skylight 17..23 via
|
|
5526
6031
|
// mergeSkylightIntoMaterialBgl) -- pipeline layout itself stays at 4 slots,
|
|
5527
6032
|
// no @group(4) allocated (D-5 round-4 fix for the round-2 maxBindGroups=4
|
|
5528
6033
|
// BLOCKER). The unlit pipeline currently shares this 14-entry layout
|
|
5529
|
-
// (carrying fallback identity at binding
|
|
6034
|
+
// (carrying fallback identity at binding 17..23 from the record stage);
|
|
5530
6035
|
// future split via `buildUnlitMaterialBgl` is exposed for tests.
|
|
5531
6036
|
//
|
|
5532
6037
|
// feat-20260520-directional-light-shadow-mapping merge: view BGL entries
|
|
@@ -5560,7 +6065,10 @@ async function buildReadyWebGPU(
|
|
|
5560
6065
|
// (URP layout) so the manifest entry's URP variant WGSL still builds a
|
|
5561
6066
|
// valid PSO instead of hard-disabling the entire HDRP-variant build path.
|
|
5562
6067
|
let hdrpPbrPipelineLayoutHandle: PipelineLayout | null = null;
|
|
5563
|
-
|
|
6068
|
+
// The cluster layout is storage-buffer backed. On the WebGL2/fallback
|
|
6069
|
+
// capability tier its uniform-buffer projection exceeds the backend's
|
|
6070
|
+
// fragment-stage limit, and HDRP is not a valid route there anyway.
|
|
6071
|
+
if (storageBufferCapable) {
|
|
5564
6072
|
const hdrpUnifiedBglDesc = createHdrpBindGroupLayoutDescriptor(storageBufferCapable);
|
|
5565
6073
|
const hdrpUnifiedBglRes = rhiDevice.createBindGroupLayout(hdrpUnifiedBglDesc);
|
|
5566
6074
|
if (hdrpUnifiedBglRes.ok) {
|
|
@@ -5899,6 +6407,20 @@ async function buildReadyWebGPU(
|
|
|
5899
6407
|
);
|
|
5900
6408
|
if (!viewUboResult.ok) throw viewUboResult.error;
|
|
5901
6409
|
|
|
6410
|
+
const pointsLinesViewBufferResult = runShimSyncStep(
|
|
6411
|
+
() =>
|
|
6412
|
+
rhiDevice.createBuffer({
|
|
6413
|
+
label: 'points-lines-view-ubo',
|
|
6414
|
+
size: POINTS_LINES_VIEW_BUFFER_SIZE,
|
|
6415
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
6416
|
+
mappedAtCreation: false,
|
|
6417
|
+
}),
|
|
6418
|
+
'webgpu-runtime-error',
|
|
6419
|
+
'createBuffer (points-lines view ubo) succeeded',
|
|
6420
|
+
'check device.limits.maxUniformBufferBindingSize',
|
|
6421
|
+
);
|
|
6422
|
+
if (!pointsLinesViewBufferResult.ok) throw pointsLinesViewBufferResult.error;
|
|
6423
|
+
|
|
5902
6424
|
// feat-20260613-csm-cascaded-shadow-maps M5 / w28: per-pass cascade-index
|
|
5903
6425
|
// UBO consumed by shadow_caster.wgsl. Stable singleton; the record stage
|
|
5904
6426
|
// queue.writeBuffer-overwrites the fields immediately before each pass's
|
|
@@ -6854,6 +7376,59 @@ async function buildReadyWebGPU(
|
|
|
6854
7376
|
let bloomBlurHParamsBufferHandle: Buffer | null = null;
|
|
6855
7377
|
let bloomBlurVParamsBufferHandle: Buffer | null = null;
|
|
6856
7378
|
let bloomCompositeParamsBufferHandle: Buffer | null = null;
|
|
7379
|
+
let bloomResourcesReady = false;
|
|
7380
|
+
let ensureBloomResources: (() => void) | undefined;
|
|
7381
|
+
let bloomActiveBundle: BloomPersistentBundle | undefined;
|
|
7382
|
+
let bloomCandidateBundle: BloomPersistentBundle | undefined;
|
|
7383
|
+
const retiringBloomBundles = new Set<BloomPersistentBundle>();
|
|
7384
|
+
let bloomGeneration = 0;
|
|
7385
|
+
let lastSuccessfulBloomReceipts:
|
|
7386
|
+
| import('../record/frame-snapshot').BloomFrameReceipts
|
|
7387
|
+
| undefined;
|
|
7388
|
+
const getBloomResourcesStable = (): BloomPersistentBundle | null =>
|
|
7389
|
+
bloomCandidateBundle ?? bloomActiveBundle ?? null;
|
|
7390
|
+
let commitBloomResources: (() => void) | undefined;
|
|
7391
|
+
let discardBloomResources: (() => void) | undefined;
|
|
7392
|
+
let retireBloomResources: ((completion: Promise<unknown>) => void) | undefined;
|
|
7393
|
+
let drainBloomResources: (() => void) | undefined;
|
|
7394
|
+
let inspectBloomResources:
|
|
7395
|
+
| ((graph?: import('@forgeax/engine-render-graph').CompiledRenderGraphInfo) => BloomInspection)
|
|
7396
|
+
| undefined;
|
|
7397
|
+
const ensureBloomResourcesStable = (): void => {
|
|
7398
|
+
ensureBloomResources?.();
|
|
7399
|
+
};
|
|
7400
|
+
const retireBloomResourcesStable = (completion: Promise<unknown>): void => {
|
|
7401
|
+
retireBloomResources?.(completion);
|
|
7402
|
+
};
|
|
7403
|
+
const commitBloomResourcesStable = (): void => {
|
|
7404
|
+
commitBloomResources?.();
|
|
7405
|
+
};
|
|
7406
|
+
const discardBloomResourcesStable = (): void => {
|
|
7407
|
+
discardBloomResources?.();
|
|
7408
|
+
};
|
|
7409
|
+
const drainBloomResourcesStable = (): void => {
|
|
7410
|
+
drainBloomResources?.();
|
|
7411
|
+
};
|
|
7412
|
+
const inspectBloomResourcesStable = (
|
|
7413
|
+
graph?: import('@forgeax/engine-render-graph').CompiledRenderGraphInfo,
|
|
7414
|
+
): BloomInspection => {
|
|
7415
|
+
return (
|
|
7416
|
+
inspectBloomResources?.(graph) ?? {
|
|
7417
|
+
graphStatus: 'empty',
|
|
7418
|
+
enabled: false,
|
|
7419
|
+
targetCount: 0,
|
|
7420
|
+
targetBytes: 0,
|
|
7421
|
+
resourceCount: 0,
|
|
7422
|
+
passCount: 0,
|
|
7423
|
+
encodeCount: 0,
|
|
7424
|
+
bindGroupCount: 0,
|
|
7425
|
+
uploadCount: 0,
|
|
7426
|
+
residentChildBytes: 0,
|
|
7427
|
+
generation: 0,
|
|
7428
|
+
state: 'off',
|
|
7429
|
+
}
|
|
7430
|
+
);
|
|
7431
|
+
};
|
|
6857
7432
|
// feat-20260612-hdrp-ssao M6 / w26 + w43: SSAO pipeline handles (D-A).
|
|
6858
7433
|
// calc + blur RenderPipeline pair sharing a dedicated 6-entry BGL.
|
|
6859
7434
|
// Optional — null when manifest lacks hdrp-ssao entry.
|
|
@@ -7154,361 +7729,560 @@ async function buildReadyWebGPU(
|
|
|
7154
7729
|
// bloom-blur-v : same BGL, same module as blur-h, V-axis texelSize
|
|
7155
7730
|
// bloom-composite: 2-tex + sampler + UBO@3 BGL, rgba16float target
|
|
7156
7731
|
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
const
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7732
|
+
ensureBloomResources = (): void => {
|
|
7733
|
+
if (bloomResourcesReady) return;
|
|
7734
|
+
const candidateScope = rendererScope.createChild(`${rendererScope.owner}:standard-bloom`);
|
|
7735
|
+
const adopt = <T>(
|
|
7736
|
+
kind: Parameters<DeviceScope['_adopt']>[0],
|
|
7737
|
+
value: T,
|
|
7738
|
+
cleanup: (value: T) => void,
|
|
7739
|
+
): T => {
|
|
7740
|
+
candidateScope._adopt(kind, value, cleanup);
|
|
7741
|
+
return value;
|
|
7742
|
+
};
|
|
7743
|
+
try {
|
|
7744
|
+
// Shared bloom sampler: linear filter + clamp-to-edge (all 4 passes
|
|
7745
|
+
// sample from textures using fullscreen triangle UVs).
|
|
7746
|
+
if (
|
|
7747
|
+
bloomBrightModule !== null ||
|
|
7748
|
+
bloomBlurModule !== null ||
|
|
7749
|
+
bloomCompositeModule !== null
|
|
7750
|
+
) {
|
|
7751
|
+
const bloomSamplerResult = runShimSyncStep(
|
|
7752
|
+
() =>
|
|
7753
|
+
rhiDevice.createSampler({
|
|
7754
|
+
label: 'bloom-sampler',
|
|
7755
|
+
magFilter: 'linear',
|
|
7756
|
+
minFilter: 'linear',
|
|
7757
|
+
mipmapFilter: 'linear',
|
|
7758
|
+
addressModeU: 'clamp-to-edge',
|
|
7759
|
+
addressModeV: 'clamp-to-edge',
|
|
7760
|
+
}),
|
|
7761
|
+
'webgpu-runtime-error',
|
|
7762
|
+
'createSampler (bloom) succeeded',
|
|
7763
|
+
'check device.limits.maxSamplersPerShaderStage',
|
|
7764
|
+
);
|
|
7765
|
+
if (!bloomSamplerResult.ok) throw bloomSamplerResult.error;
|
|
7766
|
+
bloomSamplerHandle = adopt('binding', bloomSamplerResult.value, () => undefined);
|
|
7767
|
+
}
|
|
7177
7768
|
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7769
|
+
// Bloom bright: 1-tex + sampler + UBO@2 BGL (D-4).
|
|
7770
|
+
if (bloomBrightModule !== null && bloomSamplerHandle !== null) {
|
|
7771
|
+
const brightBglResult = runShimSyncStep(
|
|
7772
|
+
() =>
|
|
7773
|
+
rhiDevice.createBindGroupLayout({
|
|
7774
|
+
label: 'bloom-bright-bgl',
|
|
7775
|
+
entries: [
|
|
7776
|
+
{
|
|
7777
|
+
binding: 0,
|
|
7778
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
7779
|
+
texture: { sampleType: 'float', viewDimension: '2d' },
|
|
7780
|
+
},
|
|
7781
|
+
{
|
|
7782
|
+
binding: 1,
|
|
7783
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
7784
|
+
sampler: { type: 'filtering' },
|
|
7785
|
+
},
|
|
7786
|
+
{
|
|
7787
|
+
binding: 2,
|
|
7788
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
7789
|
+
buffer: { type: 'uniform' },
|
|
7790
|
+
},
|
|
7791
|
+
],
|
|
7792
|
+
}),
|
|
7793
|
+
'webgpu-runtime-error',
|
|
7794
|
+
'createBindGroupLayout(bloom-bright) succeeded',
|
|
7795
|
+
'check device.limits.maxBindGroupsPerPipelineLayout',
|
|
7796
|
+
);
|
|
7797
|
+
if (!brightBglResult.ok) throw brightBglResult.error;
|
|
7798
|
+
bloomBrightBglHandle = adopt('binding', brightBglResult.value, () => undefined);
|
|
7799
|
+
|
|
7800
|
+
const brightPlResult = runShimSyncStep(
|
|
7801
|
+
() =>
|
|
7802
|
+
rhiDevice.createPipelineLayout({
|
|
7803
|
+
label: 'bloom-bright-pl',
|
|
7804
|
+
bindGroupLayouts: [brightBglResult.value],
|
|
7805
|
+
}),
|
|
7806
|
+
'webgpu-runtime-error',
|
|
7807
|
+
'createPipelineLayout(bloom-bright) succeeded',
|
|
7808
|
+
'verify bloom-bright BindGroupLayout matches shader @group(0) bindings',
|
|
7809
|
+
);
|
|
7810
|
+
if (!brightPlResult.ok) throw brightPlResult.error;
|
|
7811
|
+
adopt('binding', brightPlResult.value, () => undefined);
|
|
7812
|
+
|
|
7813
|
+
// M2-T4: bloom-bright pipeline via getOrBuildPipeline (lazy-build).
|
|
7814
|
+
{
|
|
7815
|
+
const brightSpec: PipelineSpec = {
|
|
7816
|
+
shader: {
|
|
7817
|
+
id: 'forgeax::post::bloom-bright',
|
|
7818
|
+
passKind: 'post-process',
|
|
7819
|
+
variantSet: undefined,
|
|
7189
7820
|
},
|
|
7190
|
-
{
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7821
|
+
attachments: {
|
|
7822
|
+
colorFormats: [HDR_COLOR_ATTACHMENT_FORMAT],
|
|
7823
|
+
depthFormat: undefined,
|
|
7824
|
+
sampleCount: 1,
|
|
7194
7825
|
},
|
|
7195
|
-
{
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7826
|
+
geometry: {
|
|
7827
|
+
topology: 'triangle-list',
|
|
7828
|
+
stripIndexFormat: undefined,
|
|
7829
|
+
vertexLayout: {},
|
|
7199
7830
|
},
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7831
|
+
renderState: { cullMode: 'none' },
|
|
7832
|
+
};
|
|
7833
|
+
const modules = {
|
|
7834
|
+
vertex: bloomBrightModule,
|
|
7835
|
+
fragment: bloomBrightModule,
|
|
7836
|
+
layout: brightPlResult.value,
|
|
7837
|
+
};
|
|
7838
|
+
try {
|
|
7839
|
+
bloomBrightPipelineHandle = getOrBuildPipeline(
|
|
7840
|
+
brightSpec,
|
|
7841
|
+
pipelineDeviceProvider,
|
|
7842
|
+
pipelineCache,
|
|
7843
|
+
modules,
|
|
7844
|
+
) as RenderPipeline;
|
|
7845
|
+
bloomBrightPipelineHandle = adopt(
|
|
7846
|
+
'pipeline',
|
|
7847
|
+
bloomBrightPipelineHandle,
|
|
7848
|
+
() => undefined,
|
|
7849
|
+
);
|
|
7850
|
+
} catch (err) {
|
|
7851
|
+
if (err instanceof PipelineSpecError) throw err;
|
|
7852
|
+
throw new PipelineSpecError({
|
|
7853
|
+
code: 'pipeline-build-failed',
|
|
7854
|
+
detail: { cause: err },
|
|
7855
|
+
hint: 'createRenderPipeline (bloom-bright fullscreen) failed; inspect gpuMessage',
|
|
7856
|
+
});
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7220
7859
|
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
vertexLayout: {},
|
|
7238
|
-
},
|
|
7239
|
-
renderState: { cullMode: 'none' },
|
|
7240
|
-
};
|
|
7241
|
-
const modules = {
|
|
7242
|
-
vertex: bloomBrightModule,
|
|
7243
|
-
fragment: bloomBrightModule,
|
|
7244
|
-
layout: brightPlResult.value,
|
|
7245
|
-
};
|
|
7246
|
-
try {
|
|
7247
|
-
bloomBrightPipelineHandle = getOrBuildPipeline(
|
|
7248
|
-
brightSpec,
|
|
7249
|
-
pipelineDeviceProvider,
|
|
7250
|
-
pipelineCache,
|
|
7251
|
-
modules,
|
|
7252
|
-
) as RenderPipeline;
|
|
7253
|
-
} catch (err) {
|
|
7254
|
-
if (err instanceof PipelineSpecError) throw err;
|
|
7255
|
-
throw new PipelineSpecError({
|
|
7256
|
-
code: 'pipeline-build-failed',
|
|
7257
|
-
detail: { cause: err },
|
|
7258
|
-
hint: 'createRenderPipeline (bloom-bright fullscreen) failed; inspect gpuMessage',
|
|
7860
|
+
// Bright params UBO: 16 B std140 (threshold f32 + 12 B pad).
|
|
7861
|
+
const brightParamsResult = runShimSyncStep(
|
|
7862
|
+
() =>
|
|
7863
|
+
rhiDevice.createBuffer({
|
|
7864
|
+
label: 'bloom-bright-params-ubo',
|
|
7865
|
+
size: BRIGHT_PARAMS_BYTES,
|
|
7866
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
7867
|
+
mappedAtCreation: false,
|
|
7868
|
+
}),
|
|
7869
|
+
'webgpu-runtime-error',
|
|
7870
|
+
'createBuffer (bloom-bright params UBO) succeeded',
|
|
7871
|
+
'check device.limits.maxUniformBufferBindingSize',
|
|
7872
|
+
);
|
|
7873
|
+
if (!brightParamsResult.ok) throw brightParamsResult.error;
|
|
7874
|
+
bloomBrightParamsBufferHandle = adopt('buffer', brightParamsResult.value, (value) => {
|
|
7875
|
+
rhiDevice.destroyBuffer(value);
|
|
7259
7876
|
});
|
|
7260
7877
|
}
|
|
7261
|
-
}
|
|
7262
7878
|
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
()
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7879
|
+
// Bloom blur H/V: same BGL (1-tex + sampler + UBO@2), same module,
|
|
7880
|
+
// two separate pipelines with per-axis texelSize (D-1, D-4).
|
|
7881
|
+
if (bloomBlurModule !== null && bloomSamplerHandle !== null) {
|
|
7882
|
+
const blurBglResult = runShimSyncStep(
|
|
7883
|
+
() =>
|
|
7884
|
+
rhiDevice.createBindGroupLayout({
|
|
7885
|
+
label: 'bloom-blur-bgl',
|
|
7886
|
+
entries: [
|
|
7887
|
+
{
|
|
7888
|
+
binding: 0,
|
|
7889
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
7890
|
+
texture: { sampleType: 'float', viewDimension: '2d' },
|
|
7891
|
+
},
|
|
7892
|
+
{
|
|
7893
|
+
binding: 1,
|
|
7894
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
7895
|
+
sampler: { type: 'filtering' },
|
|
7896
|
+
},
|
|
7897
|
+
{
|
|
7898
|
+
binding: 2,
|
|
7899
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
7900
|
+
buffer: { type: 'uniform' },
|
|
7901
|
+
},
|
|
7902
|
+
],
|
|
7903
|
+
}),
|
|
7904
|
+
'webgpu-runtime-error',
|
|
7905
|
+
'createBindGroupLayout(bloom-blur) succeeded',
|
|
7906
|
+
'check device.limits.maxBindGroupsPerPipelineLayout',
|
|
7907
|
+
);
|
|
7908
|
+
if (!blurBglResult.ok) throw blurBglResult.error;
|
|
7909
|
+
bloomBlurBglHandle = adopt('binding', blurBglResult.value, () => undefined);
|
|
7910
|
+
|
|
7911
|
+
const blurPlResult = runShimSyncStep(
|
|
7912
|
+
() =>
|
|
7913
|
+
rhiDevice.createPipelineLayout({
|
|
7914
|
+
label: 'bloom-blur-pl',
|
|
7915
|
+
bindGroupLayouts: [blurBglResult.value],
|
|
7916
|
+
}),
|
|
7917
|
+
'webgpu-runtime-error',
|
|
7918
|
+
'createPipelineLayout(bloom-blur) succeeded',
|
|
7919
|
+
'verify bloom-blur BindGroupLayout matches shader @group(0) bindings',
|
|
7920
|
+
);
|
|
7921
|
+
if (!blurPlResult.ok) throw blurPlResult.error;
|
|
7922
|
+
adopt('binding', blurPlResult.value, () => undefined);
|
|
7923
|
+
|
|
7924
|
+
// M2-T4: bloom-blur H/V pipelines via getOrBuildPipeline (lazy-build).
|
|
7925
|
+
// H and V share the same PSO descriptor — only per-axis texelSize UBO
|
|
7926
|
+
// distinguishes them at record time. getOrBuildPipeline cache-hit on the
|
|
7927
|
+
// second call returns the same handle (identical spec, identical PSO).
|
|
7928
|
+
{
|
|
7929
|
+
const blurSpec: PipelineSpec = {
|
|
7930
|
+
shader: {
|
|
7931
|
+
id: 'forgeax::post::bloom-blur',
|
|
7932
|
+
passKind: 'post-process',
|
|
7933
|
+
variantSet: undefined,
|
|
7292
7934
|
},
|
|
7293
|
-
{
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7935
|
+
attachments: {
|
|
7936
|
+
colorFormats: [HDR_COLOR_ATTACHMENT_FORMAT],
|
|
7937
|
+
depthFormat: undefined,
|
|
7938
|
+
sampleCount: 1,
|
|
7297
7939
|
},
|
|
7298
|
-
{
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7940
|
+
geometry: {
|
|
7941
|
+
topology: 'triangle-list',
|
|
7942
|
+
stripIndexFormat: undefined,
|
|
7943
|
+
vertexLayout: {},
|
|
7302
7944
|
},
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7945
|
+
renderState: { cullMode: 'none' },
|
|
7946
|
+
};
|
|
7947
|
+
const modules = {
|
|
7948
|
+
vertex: bloomBlurModule,
|
|
7949
|
+
fragment: bloomBlurModule,
|
|
7950
|
+
layout: blurPlResult.value,
|
|
7951
|
+
label: 'bloom-blur-h-pipeline',
|
|
7952
|
+
};
|
|
7953
|
+
try {
|
|
7954
|
+
bloomBlurHPipelineHandle = getOrBuildPipeline(
|
|
7955
|
+
blurSpec,
|
|
7956
|
+
pipelineDeviceProvider,
|
|
7957
|
+
pipelineCache,
|
|
7958
|
+
modules,
|
|
7959
|
+
) as RenderPipeline;
|
|
7960
|
+
bloomBlurHPipelineHandle = adopt(
|
|
7961
|
+
'pipeline',
|
|
7962
|
+
bloomBlurHPipelineHandle,
|
|
7963
|
+
() => undefined,
|
|
7964
|
+
);
|
|
7965
|
+
} catch (err) {
|
|
7966
|
+
if (err instanceof PipelineSpecError) throw err;
|
|
7967
|
+
throw new PipelineSpecError({
|
|
7968
|
+
code: 'pipeline-build-failed',
|
|
7969
|
+
detail: { cause: err },
|
|
7970
|
+
hint: 'createRenderPipeline (bloom-blur-h fullscreen) failed; inspect gpuMessage',
|
|
7971
|
+
});
|
|
7972
|
+
}
|
|
7973
|
+
// V is cache-hit on the same spec (identical PSO; per-axis UBO differentiates at record time).
|
|
7974
|
+
bloomBlurVPipelineHandle = bloomBlurHPipelineHandle;
|
|
7975
|
+
}
|
|
7311
7976
|
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7977
|
+
// Blur params UBOs: 16 B std140 (texelSize.xy + radius + pad) each.
|
|
7978
|
+
// bug-20260625: one per axis -- H and V must not share a buffer (the
|
|
7979
|
+
// shared-buffer writeBuffer race made both passes blur vertically).
|
|
7980
|
+
const blurHParamsResult = runShimSyncStep(
|
|
7981
|
+
() =>
|
|
7982
|
+
rhiDevice.createBuffer({
|
|
7983
|
+
label: 'bloom-blur-h-params-ubo',
|
|
7984
|
+
size: BLUR_PARAMS_BYTES,
|
|
7985
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
7986
|
+
mappedAtCreation: false,
|
|
7987
|
+
}),
|
|
7988
|
+
'webgpu-runtime-error',
|
|
7989
|
+
'createBuffer (bloom-blur-h params UBO) succeeded',
|
|
7990
|
+
'check device.limits.maxUniformBufferBindingSize',
|
|
7991
|
+
);
|
|
7992
|
+
if (!blurHParamsResult.ok) throw blurHParamsResult.error;
|
|
7993
|
+
bloomBlurHParamsBufferHandle = adopt('buffer', blurHParamsResult.value, (value) => {
|
|
7994
|
+
rhiDevice.destroyBuffer(value);
|
|
7995
|
+
});
|
|
7323
7996
|
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
},
|
|
7340
|
-
geometry: {
|
|
7341
|
-
topology: 'triangle-list',
|
|
7342
|
-
stripIndexFormat: undefined,
|
|
7343
|
-
vertexLayout: {},
|
|
7344
|
-
},
|
|
7345
|
-
renderState: { cullMode: 'none' },
|
|
7346
|
-
};
|
|
7347
|
-
const modules = {
|
|
7348
|
-
vertex: bloomBlurModule,
|
|
7349
|
-
fragment: bloomBlurModule,
|
|
7350
|
-
layout: blurPlResult.value,
|
|
7351
|
-
label: 'bloom-blur-h-pipeline',
|
|
7352
|
-
};
|
|
7353
|
-
try {
|
|
7354
|
-
bloomBlurHPipelineHandle = getOrBuildPipeline(
|
|
7355
|
-
blurSpec,
|
|
7356
|
-
pipelineDeviceProvider,
|
|
7357
|
-
pipelineCache,
|
|
7358
|
-
modules,
|
|
7359
|
-
) as RenderPipeline;
|
|
7360
|
-
} catch (err) {
|
|
7361
|
-
if (err instanceof PipelineSpecError) throw err;
|
|
7362
|
-
throw new PipelineSpecError({
|
|
7363
|
-
code: 'pipeline-build-failed',
|
|
7364
|
-
detail: { cause: err },
|
|
7365
|
-
hint: 'createRenderPipeline (bloom-blur-h fullscreen) failed; inspect gpuMessage',
|
|
7997
|
+
const blurVParamsResult = runShimSyncStep(
|
|
7998
|
+
() =>
|
|
7999
|
+
rhiDevice.createBuffer({
|
|
8000
|
+
label: 'bloom-blur-v-params-ubo',
|
|
8001
|
+
size: BLUR_PARAMS_BYTES,
|
|
8002
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
8003
|
+
mappedAtCreation: false,
|
|
8004
|
+
}),
|
|
8005
|
+
'webgpu-runtime-error',
|
|
8006
|
+
'createBuffer (bloom-blur-v params UBO) succeeded',
|
|
8007
|
+
'check device.limits.maxUniformBufferBindingSize',
|
|
8008
|
+
);
|
|
8009
|
+
if (!blurVParamsResult.ok) throw blurVParamsResult.error;
|
|
8010
|
+
bloomBlurVParamsBufferHandle = adopt('buffer', blurVParamsResult.value, (value) => {
|
|
8011
|
+
rhiDevice.destroyBuffer(value);
|
|
7366
8012
|
});
|
|
7367
8013
|
}
|
|
7368
|
-
// V is cache-hit on the same spec (identical PSO; per-axis UBO differentiates at record time).
|
|
7369
|
-
bloomBlurVPipelineHandle = bloomBlurHPipelineHandle;
|
|
7370
|
-
}
|
|
7371
|
-
|
|
7372
|
-
// Blur params UBOs: 16 B std140 (texelSize.xy + radius + pad) each.
|
|
7373
|
-
// bug-20260625: one per axis -- H and V must not share a buffer (the
|
|
7374
|
-
// shared-buffer writeBuffer race made both passes blur vertically).
|
|
7375
|
-
const blurHParamsResult = runShimSyncStep(
|
|
7376
|
-
() =>
|
|
7377
|
-
rhiDevice.createBuffer({
|
|
7378
|
-
label: 'bloom-blur-h-params-ubo',
|
|
7379
|
-
size: BLUR_PARAMS_BYTES,
|
|
7380
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
7381
|
-
mappedAtCreation: false,
|
|
7382
|
-
}),
|
|
7383
|
-
'webgpu-runtime-error',
|
|
7384
|
-
'createBuffer (bloom-blur-h params UBO) succeeded',
|
|
7385
|
-
'check device.limits.maxUniformBufferBindingSize',
|
|
7386
|
-
);
|
|
7387
|
-
if (!blurHParamsResult.ok) throw blurHParamsResult.error;
|
|
7388
|
-
bloomBlurHParamsBufferHandle = blurHParamsResult.value;
|
|
7389
|
-
|
|
7390
|
-
const blurVParamsResult = runShimSyncStep(
|
|
7391
|
-
() =>
|
|
7392
|
-
rhiDevice.createBuffer({
|
|
7393
|
-
label: 'bloom-blur-v-params-ubo',
|
|
7394
|
-
size: BLUR_PARAMS_BYTES,
|
|
7395
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
7396
|
-
mappedAtCreation: false,
|
|
7397
|
-
}),
|
|
7398
|
-
'webgpu-runtime-error',
|
|
7399
|
-
'createBuffer (bloom-blur-v params UBO) succeeded',
|
|
7400
|
-
'check device.limits.maxUniformBufferBindingSize',
|
|
7401
|
-
);
|
|
7402
|
-
if (!blurVParamsResult.ok) throw blurVParamsResult.error;
|
|
7403
|
-
bloomBlurVParamsBufferHandle = blurVParamsResult.value;
|
|
7404
|
-
}
|
|
7405
8014
|
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7419
|
-
|
|
7420
|
-
|
|
7421
|
-
|
|
8015
|
+
// Bloom composite: 2-tex + sampler + UBO@3 BGL (D-4, D-5).
|
|
8016
|
+
if (bloomCompositeModule !== null && bloomSamplerHandle !== null) {
|
|
8017
|
+
const compositeBglResult = runShimSyncStep(
|
|
8018
|
+
() =>
|
|
8019
|
+
rhiDevice.createBindGroupLayout({
|
|
8020
|
+
label: 'bloom-composite-bgl',
|
|
8021
|
+
entries: [
|
|
8022
|
+
{
|
|
8023
|
+
binding: 0,
|
|
8024
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
8025
|
+
texture: { sampleType: 'float', viewDimension: '2d' },
|
|
8026
|
+
},
|
|
8027
|
+
{
|
|
8028
|
+
binding: 1,
|
|
8029
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
8030
|
+
texture: { sampleType: 'float', viewDimension: '2d' },
|
|
8031
|
+
},
|
|
8032
|
+
{
|
|
8033
|
+
binding: 2,
|
|
8034
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
8035
|
+
sampler: { type: 'filtering' },
|
|
8036
|
+
},
|
|
8037
|
+
{
|
|
8038
|
+
binding: 3,
|
|
8039
|
+
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
8040
|
+
buffer: { type: 'uniform' },
|
|
8041
|
+
},
|
|
8042
|
+
],
|
|
8043
|
+
}),
|
|
8044
|
+
'webgpu-runtime-error',
|
|
8045
|
+
'createBindGroupLayout(bloom-composite) succeeded',
|
|
8046
|
+
'check device.limits.maxBindGroupsPerPipelineLayout',
|
|
8047
|
+
);
|
|
8048
|
+
if (!compositeBglResult.ok) throw compositeBglResult.error;
|
|
8049
|
+
bloomCompositeBglHandle = adopt('binding', compositeBglResult.value, () => undefined);
|
|
8050
|
+
|
|
8051
|
+
const compositePlResult = runShimSyncStep(
|
|
8052
|
+
() =>
|
|
8053
|
+
rhiDevice.createPipelineLayout({
|
|
8054
|
+
label: 'bloom-composite-pl',
|
|
8055
|
+
bindGroupLayouts: [compositeBglResult.value],
|
|
8056
|
+
}),
|
|
8057
|
+
'webgpu-runtime-error',
|
|
8058
|
+
'createPipelineLayout(bloom-composite) succeeded',
|
|
8059
|
+
'verify bloom-composite BindGroupLayout matches shader @group(0) bindings',
|
|
8060
|
+
);
|
|
8061
|
+
if (!compositePlResult.ok) throw compositePlResult.error;
|
|
8062
|
+
adopt('binding', compositePlResult.value, () => undefined);
|
|
8063
|
+
|
|
8064
|
+
// M2-T4: bloom-composite pipeline via getOrBuildPipeline (lazy-build).
|
|
8065
|
+
{
|
|
8066
|
+
const compositeSpec: PipelineSpec = {
|
|
8067
|
+
shader: {
|
|
8068
|
+
id: 'forgeax::post::bloom-composite',
|
|
8069
|
+
passKind: 'post-process',
|
|
8070
|
+
variantSet: undefined,
|
|
7422
8071
|
},
|
|
7423
|
-
{
|
|
7424
|
-
|
|
7425
|
-
|
|
7426
|
-
|
|
8072
|
+
attachments: {
|
|
8073
|
+
colorFormats: [HDR_COLOR_ATTACHMENT_FORMAT],
|
|
8074
|
+
depthFormat: undefined,
|
|
8075
|
+
sampleCount: 1,
|
|
7427
8076
|
},
|
|
7428
|
-
{
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
8077
|
+
geometry: {
|
|
8078
|
+
topology: 'triangle-list',
|
|
8079
|
+
stripIndexFormat: undefined,
|
|
8080
|
+
vertexLayout: {},
|
|
7432
8081
|
},
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
8082
|
+
renderState: { cullMode: 'none' },
|
|
8083
|
+
};
|
|
8084
|
+
const modules = {
|
|
8085
|
+
vertex: bloomCompositeModule,
|
|
8086
|
+
fragment: bloomCompositeModule,
|
|
8087
|
+
layout: compositePlResult.value,
|
|
8088
|
+
};
|
|
8089
|
+
try {
|
|
8090
|
+
bloomCompositePipelineHandle = getOrBuildPipeline(
|
|
8091
|
+
compositeSpec,
|
|
8092
|
+
pipelineDeviceProvider,
|
|
8093
|
+
pipelineCache,
|
|
8094
|
+
modules,
|
|
8095
|
+
) as RenderPipeline;
|
|
8096
|
+
bloomCompositePipelineHandle = adopt(
|
|
8097
|
+
'pipeline',
|
|
8098
|
+
bloomCompositePipelineHandle,
|
|
8099
|
+
() => undefined,
|
|
8100
|
+
);
|
|
8101
|
+
} catch (err) {
|
|
8102
|
+
if (err instanceof PipelineSpecError) throw err;
|
|
8103
|
+
throw new PipelineSpecError({
|
|
8104
|
+
code: 'pipeline-build-failed',
|
|
8105
|
+
detail: { cause: err },
|
|
8106
|
+
hint: 'createRenderPipeline (bloom-composite fullscreen) failed; inspect gpuMessage',
|
|
8107
|
+
});
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
7441
8110
|
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
8111
|
+
// Composite params UBO: 16 B std140 (intensity f32 + 12 B pad).
|
|
8112
|
+
const compositeParamsResult = runShimSyncStep(
|
|
8113
|
+
() =>
|
|
8114
|
+
rhiDevice.createBuffer({
|
|
8115
|
+
label: 'bloom-composite-params-ubo',
|
|
8116
|
+
size: COMPOSITE_PARAMS_BYTES,
|
|
8117
|
+
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
8118
|
+
mappedAtCreation: false,
|
|
8119
|
+
}),
|
|
8120
|
+
'webgpu-runtime-error',
|
|
8121
|
+
'createBuffer (bloom-composite params UBO) succeeded',
|
|
8122
|
+
'check device.limits.maxUniformBufferBindingSize',
|
|
8123
|
+
);
|
|
8124
|
+
if (!compositeParamsResult.ok) throw compositeParamsResult.error;
|
|
8125
|
+
bloomCompositeParamsBufferHandle = adopt(
|
|
8126
|
+
'buffer',
|
|
8127
|
+
compositeParamsResult.value,
|
|
8128
|
+
(value) => {
|
|
8129
|
+
rhiDevice.destroyBuffer(value);
|
|
8130
|
+
},
|
|
8131
|
+
);
|
|
8132
|
+
}
|
|
7453
8133
|
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
stripIndexFormat: undefined,
|
|
7470
|
-
vertexLayout: {},
|
|
7471
|
-
},
|
|
7472
|
-
renderState: { cullMode: 'none' },
|
|
8134
|
+
bloomCandidateBundle = {
|
|
8135
|
+
scope: candidateScope,
|
|
8136
|
+
generation: bloomGeneration + 1,
|
|
8137
|
+
bloomBrightPipeline: bloomBrightPipelineHandle,
|
|
8138
|
+
bloomBlurHPipeline: bloomBlurHPipelineHandle,
|
|
8139
|
+
bloomBlurVPipeline: bloomBlurVPipelineHandle,
|
|
8140
|
+
bloomCompositePipeline: bloomCompositePipelineHandle,
|
|
8141
|
+
bloomBrightBindGroupLayout: bloomBrightBglHandle,
|
|
8142
|
+
bloomBlurBindGroupLayout: bloomBlurBglHandle,
|
|
8143
|
+
bloomCompositeBindGroupLayout: bloomCompositeBglHandle,
|
|
8144
|
+
bloomSampler: bloomSamplerHandle,
|
|
8145
|
+
bloomBrightParamsBuffer: bloomBrightParamsBufferHandle,
|
|
8146
|
+
bloomBlurHParamsBuffer: bloomBlurHParamsBufferHandle,
|
|
8147
|
+
bloomBlurVParamsBuffer: bloomBlurVParamsBufferHandle,
|
|
8148
|
+
bloomCompositeParamsBuffer: bloomCompositeParamsBufferHandle,
|
|
7473
8149
|
};
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
8150
|
+
inspectBloomResources = (graph): BloomInspection => {
|
|
8151
|
+
const retiring = [...retiringBloomBundles].sort(
|
|
8152
|
+
(left, right) => left.generation - right.generation,
|
|
8153
|
+
);
|
|
8154
|
+
const active = bloomActiveBundle?.scope.isAlive() ? bloomActiveBundle : undefined;
|
|
8155
|
+
const liveRetiring = retiring.filter((bundle) => bundle.scope.state === 'retiring');
|
|
8156
|
+
const selected = active ?? liveRetiring[liveRetiring.length - 1];
|
|
8157
|
+
if (selected === undefined) {
|
|
8158
|
+
return {
|
|
8159
|
+
graphStatus: 'empty',
|
|
8160
|
+
enabled: false,
|
|
8161
|
+
targetCount: 0,
|
|
8162
|
+
targetBytes: 0,
|
|
8163
|
+
resourceCount: 0,
|
|
8164
|
+
passCount: 0,
|
|
8165
|
+
encodeCount: 0,
|
|
8166
|
+
bindGroupCount: 0,
|
|
8167
|
+
uploadCount: 0,
|
|
8168
|
+
residentChildBytes: 0,
|
|
8169
|
+
generation: 0,
|
|
8170
|
+
state: 'off',
|
|
8171
|
+
};
|
|
8172
|
+
}
|
|
8173
|
+
const state = active === undefined ? 'retiring' : 'active';
|
|
8174
|
+
const ownedBundles = [...(active === undefined ? [] : [active]), ...liveRetiring];
|
|
8175
|
+
const graphInspection = inspectStandardBloomGraph(graph);
|
|
8176
|
+
const receipts = lastSuccessfulBloomReceipts ?? {
|
|
8177
|
+
uploadCount: 0,
|
|
8178
|
+
bindGroupCount: 0,
|
|
8179
|
+
encodeCount: 0,
|
|
8180
|
+
};
|
|
8181
|
+
return {
|
|
8182
|
+
graphStatus: active === undefined ? 'empty' : graphInspection.status,
|
|
8183
|
+
enabled: active !== undefined,
|
|
8184
|
+
targetCount: active === undefined ? 0 : graphInspection.targetCount,
|
|
8185
|
+
targetBytes: active === undefined ? 0 : graphInspection.targetBytes,
|
|
8186
|
+
resourceCount: ownedBundles.reduce(
|
|
8187
|
+
(count, bundle) => count + bundle.scope.resourceDelta(),
|
|
8188
|
+
0,
|
|
8189
|
+
),
|
|
8190
|
+
passCount: active === undefined ? 0 : graphInspection.passCount,
|
|
8191
|
+
encodeCount: active === undefined ? 0 : receipts.encodeCount,
|
|
8192
|
+
bindGroupCount: active === undefined ? 0 : receipts.bindGroupCount,
|
|
8193
|
+
uploadCount: active === undefined ? 0 : receipts.uploadCount,
|
|
8194
|
+
residentChildBytes: 0,
|
|
8195
|
+
generation: selected?.generation ?? 0,
|
|
8196
|
+
state,
|
|
8197
|
+
};
|
|
7478
8198
|
};
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
if (
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
8199
|
+
commitBloomResources = () => {
|
|
8200
|
+
if (bloomCandidateBundle?.scope !== candidateScope) return;
|
|
8201
|
+
bloomActiveBundle = bloomCandidateBundle;
|
|
8202
|
+
bloomCandidateBundle = undefined;
|
|
8203
|
+
bloomGeneration += 1;
|
|
8204
|
+
bloomResourcesReady = true;
|
|
8205
|
+
};
|
|
8206
|
+
discardBloomResources = () => {
|
|
8207
|
+
if (bloomCandidateBundle?.scope !== candidateScope) return;
|
|
8208
|
+
candidateScope.abandon();
|
|
8209
|
+
bloomCandidateBundle = undefined;
|
|
8210
|
+
bloomResourcesReady = false;
|
|
8211
|
+
};
|
|
8212
|
+
const reportBloomFenceFailure = (cause: unknown): void => {
|
|
8213
|
+
if (cause instanceof RhiError) {
|
|
8214
|
+
errorRegistry.fire(cause);
|
|
8215
|
+
return;
|
|
8216
|
+
}
|
|
8217
|
+
const detailError = {
|
|
8218
|
+
code: 'bloom-fence-rejected',
|
|
8219
|
+
message: String(cause),
|
|
8220
|
+
...(cause instanceof Error ? { name: cause.name } : {}),
|
|
8221
|
+
};
|
|
8222
|
+
errorRegistry.fire(
|
|
8223
|
+
new RhiError({
|
|
8224
|
+
code: 'webgpu-runtime-error',
|
|
8225
|
+
expected: 'Bloom queue completion resolves after the submitted frame retires',
|
|
8226
|
+
hint: 'inspect renderer errors for the queue completion cause',
|
|
8227
|
+
detail: { error: detailError },
|
|
8228
|
+
}),
|
|
8229
|
+
);
|
|
8230
|
+
};
|
|
8231
|
+
retireBloomResources = (completion) => {
|
|
8232
|
+
const retiring = bloomActiveBundle;
|
|
8233
|
+
if (retiring === undefined) return;
|
|
8234
|
+
bloomActiveBundle = undefined;
|
|
8235
|
+
bloomResourcesReady = false;
|
|
8236
|
+
retiringBloomBundles.add(retiring);
|
|
8237
|
+
retiring.scope.beginRetire();
|
|
8238
|
+
void completion.then(
|
|
8239
|
+
() => {
|
|
8240
|
+
if (!retiringBloomBundles.has(retiring)) return;
|
|
8241
|
+
retiring.scope.retire();
|
|
8242
|
+
retiringBloomBundles.delete(retiring);
|
|
8243
|
+
},
|
|
8244
|
+
(cause) => {
|
|
8245
|
+
if (!retiringBloomBundles.has(retiring)) return;
|
|
8246
|
+
reportBloomFenceFailure(cause);
|
|
8247
|
+
retiring.scope.retire();
|
|
8248
|
+
retiringBloomBundles.delete(retiring);
|
|
8249
|
+
},
|
|
8250
|
+
);
|
|
8251
|
+
};
|
|
8252
|
+
drainBloomResources = () => {
|
|
8253
|
+
if (bloomCandidateBundle !== undefined) {
|
|
8254
|
+
bloomCandidateBundle.scope.abandon();
|
|
8255
|
+
bloomCandidateBundle = undefined;
|
|
8256
|
+
}
|
|
8257
|
+
if (bloomActiveBundle !== undefined) {
|
|
8258
|
+
bloomActiveBundle.scope.retire();
|
|
8259
|
+
bloomActiveBundle = undefined;
|
|
8260
|
+
}
|
|
8261
|
+
for (const retiring of retiringBloomBundles) retiring.scope.retire();
|
|
8262
|
+
retiringBloomBundles.clear();
|
|
8263
|
+
bloomResourcesReady = false;
|
|
8264
|
+
};
|
|
8265
|
+
} catch (cause) {
|
|
8266
|
+
candidateScope.abandon();
|
|
8267
|
+
if (bloomCandidateBundle?.scope === candidateScope) bloomCandidateBundle = undefined;
|
|
8268
|
+
commitBloomResources = undefined;
|
|
8269
|
+
discardBloomResources = undefined;
|
|
8270
|
+
drainBloomResources = undefined;
|
|
8271
|
+
bloomBrightPipelineHandle = null;
|
|
8272
|
+
bloomBlurHPipelineHandle = null;
|
|
8273
|
+
bloomBlurVPipelineHandle = null;
|
|
8274
|
+
bloomCompositePipelineHandle = null;
|
|
8275
|
+
bloomBrightBglHandle = null;
|
|
8276
|
+
bloomBlurBglHandle = null;
|
|
8277
|
+
bloomCompositeBglHandle = null;
|
|
8278
|
+
bloomSamplerHandle = null;
|
|
8279
|
+
bloomBrightParamsBufferHandle = null;
|
|
8280
|
+
bloomBlurHParamsBufferHandle = null;
|
|
8281
|
+
bloomBlurVParamsBufferHandle = null;
|
|
8282
|
+
bloomCompositeParamsBufferHandle = null;
|
|
8283
|
+
throw cause;
|
|
7494
8284
|
}
|
|
7495
|
-
|
|
7496
|
-
// Composite params UBO: 16 B std140 (intensity f32 + 12 B pad).
|
|
7497
|
-
const compositeParamsResult = runShimSyncStep(
|
|
7498
|
-
() =>
|
|
7499
|
-
rhiDevice.createBuffer({
|
|
7500
|
-
label: 'bloom-composite-params-ubo',
|
|
7501
|
-
size: COMPOSITE_PARAMS_BYTES,
|
|
7502
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
7503
|
-
mappedAtCreation: false,
|
|
7504
|
-
}),
|
|
7505
|
-
'webgpu-runtime-error',
|
|
7506
|
-
'createBuffer (bloom-composite params UBO) succeeded',
|
|
7507
|
-
'check device.limits.maxUniformBufferBindingSize',
|
|
7508
|
-
);
|
|
7509
|
-
if (!compositeParamsResult.ok) throw compositeParamsResult.error;
|
|
7510
|
-
bloomCompositeParamsBufferHandle = compositeParamsResult.value;
|
|
7511
|
-
}
|
|
8285
|
+
};
|
|
7512
8286
|
|
|
7513
8287
|
// ── feat-20260612-hdrp-ssao M6 / w26 + w43 + M8 / w37 ───────────────────
|
|
7514
8288
|
//
|
|
@@ -7730,7 +8504,7 @@ async function buildReadyWebGPU(
|
|
|
7730
8504
|
if (!prewarmRes.ok) throw prewarmRes.error;
|
|
7731
8505
|
}
|
|
7732
8506
|
|
|
7733
|
-
|
|
8507
|
+
const pipelineState: PipelineState = {
|
|
7734
8508
|
// feat-20260518-pbr-direct-lighting-mvp M5 / w22.10 (AC-06 dual->triple
|
|
7735
8509
|
// pipeline + D-2 + D-10): three distinct GPU render-pipeline handles
|
|
7736
8510
|
// backed by 2 distinct shader modules (unlit + pbr) x 2 vertex stride
|
|
@@ -7813,10 +8587,12 @@ async function buildReadyWebGPU(
|
|
|
7813
8587
|
meshes: meshHandles,
|
|
7814
8588
|
format: swapChainFormats.storage,
|
|
7815
8589
|
colorAttachmentFormat: swapChainFormats.view,
|
|
8590
|
+
surfaceProfile: surfaceViewFormats ? 'dual-view' : 'raw-only',
|
|
7816
8591
|
viewBindGroupLayout: viewBglResult.value,
|
|
7817
8592
|
materialBindGroupLayout: materialBglResult.value,
|
|
7818
8593
|
meshBindGroupLayout: meshArrayBglResult.value,
|
|
7819
8594
|
viewUniformBuffer: viewUboResult.value,
|
|
8595
|
+
pointsLinesViewBuffer: pointsLinesViewBufferResult.value,
|
|
7820
8596
|
shadowCasterCascadeBuffer: shadowCasterCascadeUboResult.value,
|
|
7821
8597
|
// feat-20260608-mesh-ssbo-dynamic-grow-l1-lift-1024-entity-cap M2 /
|
|
7822
8598
|
// T-M2-05: PipelineState now references the wrapper objects owned by
|
|
@@ -7921,18 +8697,28 @@ async function buildReadyWebGPU(
|
|
|
7921
8697
|
// (ensureLazyTexture, slot width/height tracking for size-drift rebuild).
|
|
7922
8698
|
// BindGroup caches survive until the intermediate view is invalidated
|
|
7923
8699
|
// by a resize (width/height drift forces null).
|
|
7924
|
-
bloomBrightPipeline:
|
|
7925
|
-
bloomBlurHPipeline:
|
|
7926
|
-
bloomBlurVPipeline:
|
|
7927
|
-
bloomCompositePipeline:
|
|
7928
|
-
bloomBrightBindGroupLayout:
|
|
7929
|
-
bloomBlurBindGroupLayout:
|
|
7930
|
-
bloomCompositeBindGroupLayout:
|
|
7931
|
-
bloomSampler:
|
|
7932
|
-
bloomBrightParamsBuffer:
|
|
7933
|
-
bloomBlurHParamsBuffer:
|
|
7934
|
-
bloomBlurVParamsBuffer:
|
|
7935
|
-
bloomCompositeParamsBuffer:
|
|
8700
|
+
bloomBrightPipeline: null,
|
|
8701
|
+
bloomBlurHPipeline: null,
|
|
8702
|
+
bloomBlurVPipeline: null,
|
|
8703
|
+
bloomCompositePipeline: null,
|
|
8704
|
+
bloomBrightBindGroupLayout: null,
|
|
8705
|
+
bloomBlurBindGroupLayout: null,
|
|
8706
|
+
bloomCompositeBindGroupLayout: null,
|
|
8707
|
+
bloomSampler: null,
|
|
8708
|
+
bloomBrightParamsBuffer: null,
|
|
8709
|
+
bloomBlurHParamsBuffer: null,
|
|
8710
|
+
bloomBlurVParamsBuffer: null,
|
|
8711
|
+
bloomCompositeParamsBuffer: null,
|
|
8712
|
+
ensureBloomResources: ensureBloomResourcesStable,
|
|
8713
|
+
getBloomResources: getBloomResourcesStable,
|
|
8714
|
+
commitBloomResources: commitBloomResourcesStable,
|
|
8715
|
+
commitBloomFrameReceipts: (receipts) => {
|
|
8716
|
+
lastSuccessfulBloomReceipts = receipts;
|
|
8717
|
+
},
|
|
8718
|
+
discardBloomResources: discardBloomResourcesStable,
|
|
8719
|
+
retireBloomResources: retireBloomResourcesStable,
|
|
8720
|
+
drainBloomResources: drainBloomResourcesStable,
|
|
8721
|
+
inspectBloomResources: inspectBloomResourcesStable,
|
|
7936
8722
|
bloomBrightTexture: null,
|
|
7937
8723
|
bloomBrightView: null,
|
|
7938
8724
|
bloomBrightWidth: 0,
|
|
@@ -7957,6 +8743,7 @@ async function buildReadyWebGPU(
|
|
|
7957
8743
|
ssaoFallbackRawView: null,
|
|
7958
8744
|
},
|
|
7959
8745
|
};
|
|
8746
|
+
return pipelineState;
|
|
7960
8747
|
}
|
|
7961
8748
|
|
|
7962
8749
|
/**
|
|
@@ -7981,8 +8768,8 @@ function ensureContextConfigured(
|
|
|
7981
8768
|
internals: WebGPURendererInternals,
|
|
7982
8769
|
state: PipelineState,
|
|
7983
8770
|
errorRegistry: RhiErrorListenerRegistry,
|
|
7984
|
-
): void {
|
|
7985
|
-
if (state.perPassResources.configured) return;
|
|
8771
|
+
): Result<void, RhiError> {
|
|
8772
|
+
if (state.perPassResources.configured) return ok(undefined);
|
|
7986
8773
|
// M6 / w41 (feat-20260510-rhi-resource-creation): configure goes through
|
|
7987
8774
|
// the forgeax `RhiCanvasContext` surface; the shim resolves the forgeax
|
|
7988
8775
|
// RhiDevice back to the underlying raw GPUDevice via RAW_DEVICE_MAP
|
|
@@ -7995,7 +8782,7 @@ function ensureContextConfigured(
|
|
|
7995
8782
|
const configuredDevice = internals.pack.instrumentation?.resolveSurfaceDevice?.(internals.device);
|
|
7996
8783
|
if (configuredDevice !== undefined && !configuredDevice.ok) {
|
|
7997
8784
|
errorRegistry.fire(configuredDevice.error);
|
|
7998
|
-
return;
|
|
8785
|
+
return err(configuredDevice.error);
|
|
7999
8786
|
}
|
|
8000
8787
|
const cfgResult = configureSurface(
|
|
8001
8788
|
internals.context,
|
|
@@ -8005,7 +8792,7 @@ function ensureContextConfigured(
|
|
|
8005
8792
|
);
|
|
8006
8793
|
if (!cfgResult.ok) {
|
|
8007
8794
|
errorRegistry.fire(cfgResult.error);
|
|
8008
|
-
return;
|
|
8795
|
+
return err(cfgResult.error);
|
|
8009
8796
|
}
|
|
8010
8797
|
state.perPassResources.configured = true;
|
|
8011
8798
|
// bug-20260612: write the actually-configured swap-chain storage format
|
|
@@ -8018,6 +8805,7 @@ function ensureContextConfigured(
|
|
|
8018
8805
|
// `${storage}-srgb` partner — derived, not duplicated.
|
|
8019
8806
|
(globalThis as { __forgeaxSwapChainFormat?: GPUTextureFormat }).__forgeaxSwapChainFormat =
|
|
8020
8807
|
state.format;
|
|
8808
|
+
return ok(undefined);
|
|
8021
8809
|
}
|
|
8022
8810
|
|
|
8023
8811
|
/**
|