@forgeax/engine-render 0.1.7 → 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 +140 -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 +12 -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/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.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-YEJIUW7D.mjs → chunk-LOSO4CTV.mjs} +296 -547
- 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-RXZ3RGOU.mjs +53 -0
- 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-YTQ5C7YQ.mjs +584 -0
- 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 +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/motion-blur.d.ts +7 -0
- package/dist/components/motion-blur.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 +10914 -16392
- 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 +136 -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 +2 -1
- 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-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 +17 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +6 -4
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +43 -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 +10 -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/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 +11 -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 +76 -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 +4 -1
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/render-contract.d.ts +92 -8
- 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 +29 -5
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +23 -4
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +36 -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__/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-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 -146
- 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 +9 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +11 -2
- package/src/__tests__/render-error-exhaustive.test-d.ts +3 -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 +395 -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 +1224 -505
- package/src/assembly/host-contract.ts +4 -0
- 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/components/__tests__/motion-blur.unit.test.ts +14 -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 +3 -0
- package/src/components/motion-blur.ts +8 -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 +285 -0
- 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 +2 -1
- 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-scene.ts +24 -0
- package/src/ibl/skylight-bind-group.ts +30 -38
- package/src/index.ts +66 -6
- package/src/inspection-types.ts +48 -0
- package/src/internal.ts +5 -0
- package/src/materials.ts +119 -24
- package/src/pbr-pipeline.ts +58 -19
- 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 +2 -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-snapshot.ts +129 -5
- package/src/record/frame-targets.ts +15 -6
- package/src/record/frame.ts +107 -3
- package/src/record/main-pass-geometry.ts +86 -40
- package/src/record/main-pass-material.ts +114 -66
- package/src/record/main-pass-sprite-draws.ts +78 -101
- package/src/record/main-pass.ts +77 -4
- package/src/record/mesh-ssbo.ts +22 -3
- package/src/record/render-context.ts +189 -23
- package/src/record/shadow-pass.ts +113 -23
- package/src/record/skybox-post-pass.ts +97 -41
- package/src/record/typed-frame-graph.ts +565 -26
- package/src/record/view-ubo.ts +52 -4
- package/src/render-contract.ts +109 -6
- package/src/render-graph-primitives.ts +18 -1
- package/src/render-pipeline.ts +100 -27
- package/src/render-system-extract.ts +193 -69
- package/src/render-system.ts +482 -41
- package/src/scene/render-scene.ts +250 -7
- 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-HCOGPHKS.mjs +0 -93
- package/dist/chunk-HCOGPHKS.mjs.map +0 -1
- package/dist/chunk-YEJIUW7D.mjs.map +0 -1
- package/dist/components/__tests__/points-lines-admission.unit.test.d.ts +0 -2
- package/dist/components/__tests__/points-lines-admission.unit.test.d.ts.map +0 -1
- package/dist/components/__tests__/points-lines-schema.unit.test.d.ts +0 -2
- package/dist/components/__tests__/points-lines-schema.unit.test.d.ts.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/points-lines/__tests__/bounds.unit.test.d.ts +0 -2
- package/dist/points-lines/__tests__/bounds.unit.test.d.ts.map +0 -1
- package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts +0 -2
- package/dist/points-lines/__tests__/cache-recovery.unit.test.d.ts.map +0 -1
- package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts +0 -2
- package/dist/points-lines/__tests__/inspection-lanes.integration.test.d.ts.map +0 -1
- package/dist/points-lines/__tests__/inspection.unit.test.d.ts +0 -2
- package/dist/points-lines/__tests__/inspection.unit.test.d.ts.map +0 -1
- package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts +0 -2
- package/dist/points-lines/__tests__/lane-structural.integration.test.d.ts.map +0 -1
- package/dist/points-lines/__tests__/points-lines.bench.d.ts +0 -2
- package/dist/points-lines/__tests__/points-lines.bench.d.ts.map +0 -1
- package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts +0 -2
- package/dist/points-lines/__tests__/raster-lane.integration.test.d.ts.map +0 -1
- package/dist/points-lines/__tests__/snapshot.unit.test.d.ts +0 -2
- package/dist/points-lines/__tests__/snapshot.unit.test.d.ts.map +0 -1
- package/dist/points-lines/__tests__/view-ubo.unit.test.d.ts +0 -2
- package/dist/points-lines/__tests__/view-ubo.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 {
|
|
@@ -155,6 +163,7 @@ import {
|
|
|
155
163
|
} from '../pipeline-spec';
|
|
156
164
|
import { POINTS_LINES_MATERIAL_SHADER_ID } from '../points-lines/record';
|
|
157
165
|
import type { FrameObservationOptions } from '../record/frame';
|
|
166
|
+
import type { BloomPersistentBundle } from '../record/render-context';
|
|
158
167
|
import { SHADOW_CASTER_BUFFER_SIZE } from '../record/shadow-pass';
|
|
159
168
|
import { POINTS_LINES_VIEW_BUFFER_SIZE, VIEW_UNIFORM_BUFFER_SIZE } from '../record/view-ubo';
|
|
160
169
|
import type {
|
|
@@ -177,7 +186,7 @@ import type {
|
|
|
177
186
|
RenderResult,
|
|
178
187
|
RenderWorldLease,
|
|
179
188
|
} from '../render-contract';
|
|
180
|
-
import {
|
|
189
|
+
import { STANDARD_OUTPUT_TRANSFORM_FEATURE_ID } from '../render-contract';
|
|
181
190
|
import {
|
|
182
191
|
configureSurface,
|
|
183
192
|
createRenderSystem,
|
|
@@ -194,6 +203,8 @@ import {
|
|
|
194
203
|
import { tilemapChunkExtractSystem } from '../tilemap-chunk-extract-system';
|
|
195
204
|
import { EngineEnvironmentError, type RhiBackendPack } from './backend-contract';
|
|
196
205
|
import type { RendererAssemblyImplementation, RendererHostImplementation } from './host-contract';
|
|
206
|
+
import { projectRendererOutputInspection } from './renderer-inspection';
|
|
207
|
+
import { isRendererStateOperational } from './renderer-lifecycle';
|
|
197
208
|
|
|
198
209
|
export { assembleMaterialProjection } from './material/assembly';
|
|
199
210
|
export {
|
|
@@ -232,6 +243,9 @@ function loadRhiPack(
|
|
|
232
243
|
const createShaderModule = mod.createShaderModule as
|
|
233
244
|
| RhiBackendPack['createShaderModule']
|
|
234
245
|
| undefined;
|
|
246
|
+
const createShaderModuleImmediate = mod.createShaderModuleImmediate as
|
|
247
|
+
| RhiBackendPack['createShaderModuleImmediate']
|
|
248
|
+
| undefined;
|
|
235
249
|
const translateErrorEventToRhiError = mod.translateErrorEventToRhiError as
|
|
236
250
|
| RhiBackendPack['translateErrorEventToRhiError']
|
|
237
251
|
| undefined;
|
|
@@ -241,6 +255,7 @@ function loadRhiPack(
|
|
|
241
255
|
return {
|
|
242
256
|
rhi,
|
|
243
257
|
...(createShaderModule === undefined ? {} : { createShaderModule }),
|
|
258
|
+
...(createShaderModuleImmediate === undefined ? {} : { createShaderModuleImmediate }),
|
|
244
259
|
...(translateErrorEventToRhiError === undefined ? {} : { translateErrorEventToRhiError }),
|
|
245
260
|
...(rawDeviceAccessor === undefined ? {} : { _internal_getRawDevice: rawDeviceAccessor }),
|
|
246
261
|
...(instrumentation === undefined ? {} : { instrumentation }),
|
|
@@ -477,20 +492,61 @@ export type LayoutKind = 'pbr' | 'pbr-skin' | 'hdrp-pbr' | 'sprite-urp' | 'unlit
|
|
|
477
492
|
* Return every device-capability HDRP PBR variant that needs a lazy module
|
|
478
493
|
* label seeded during renderer boot. Color availability is intentionally not
|
|
479
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.
|
|
480
497
|
*/
|
|
481
498
|
export function selectHdrpPbrPrewarmVariants(
|
|
482
499
|
manifestEntry: MaterialShaderManifestEntry | undefined,
|
|
483
500
|
storageBufferCapable: boolean,
|
|
501
|
+
transmissionCapable: boolean,
|
|
484
502
|
): readonly MaterialShaderManifestEntry['variants'][number][] {
|
|
485
503
|
return (
|
|
486
504
|
manifestEntry?.variants.filter(
|
|
487
505
|
(variant) =>
|
|
488
506
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
489
|
-
variant.defines.CLUSTER_FORWARD_AVAILABLE === true
|
|
507
|
+
variant.defines.CLUSTER_FORWARD_AVAILABLE === true &&
|
|
508
|
+
(transmissionCapable || variant.defines.TRANSMISSION_AVAILABLE !== true),
|
|
490
509
|
) ?? []
|
|
491
510
|
);
|
|
492
511
|
}
|
|
493
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
|
+
|
|
494
550
|
/**
|
|
495
551
|
* Select the PBR variant that matches a fallback mesh with no COLOR_0 input.
|
|
496
552
|
* Unknown/custom material IDs may still use the legacy PBR fallback, so that
|
|
@@ -502,7 +558,9 @@ export function selectNoColorPbrVariant(
|
|
|
502
558
|
variantSet: string | undefined,
|
|
503
559
|
): MaterialShaderManifestEntry['variants'][number] | undefined {
|
|
504
560
|
const hdrpRequested =
|
|
505
|
-
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'));
|
|
506
564
|
return manifestEntry?.variants.find(
|
|
507
565
|
(variant) =>
|
|
508
566
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
@@ -512,12 +570,7 @@ export function selectNoColorPbrVariant(
|
|
|
512
570
|
);
|
|
513
571
|
}
|
|
514
572
|
|
|
515
|
-
export type MaterialShaderBindingContract =
|
|
516
|
-
| 'group-0'
|
|
517
|
-
| 'group-0-resource'
|
|
518
|
-
| 'view-only'
|
|
519
|
-
| 'view-and-scene-depth'
|
|
520
|
-
| 'render-material';
|
|
573
|
+
export type MaterialShaderBindingContract = RenderFeatureMaterialShaderBindingContract;
|
|
521
574
|
export type MaterialShaderVertexInputContract = 'none' | 'render-material';
|
|
522
575
|
|
|
523
576
|
export function resolveMaterialShaderBindingContract(
|
|
@@ -632,6 +685,13 @@ export function selectPipelineLayoutForVariant(
|
|
|
632
685
|
if (layoutKind === 'unlit-urp') {
|
|
633
686
|
return state.pbrPipelineLayout;
|
|
634
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
|
+
}
|
|
635
695
|
// HDRP variant matches when:
|
|
636
696
|
// - layoutKind === 'hdrp-pbr' (caller-driven, future-proof), OR
|
|
637
697
|
// - variantSet is the canonical all-true key '' (boot-time HDRP
|
|
@@ -685,7 +745,17 @@ export function resolveMaterialShaderVariantSet(
|
|
|
685
745
|
? backendKind === 'wgpu-webgl2'
|
|
686
746
|
: axis === 'STORAGE_BUFFER_AVAILABLE'
|
|
687
747
|
? backendKind !== 'wgpu-webgl2' && storageBuffer
|
|
688
|
-
:
|
|
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);
|
|
689
759
|
}
|
|
690
760
|
const sorted = Object.entries(filtered).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
691
761
|
return sorted.every(([, value]) => value)
|
|
@@ -875,11 +945,32 @@ const COMPRESSION_FEATURES: GPUFeatureName[] = [
|
|
|
875
945
|
'texture-compression-astc',
|
|
876
946
|
];
|
|
877
947
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
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 {
|
|
881
960
|
const requiredFeatures = COMPRESSION_FEATURES.filter((feature) => adapter.features.has(feature));
|
|
882
|
-
|
|
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
|
+
};
|
|
883
974
|
}
|
|
884
975
|
|
|
885
976
|
/**
|
|
@@ -1097,16 +1188,34 @@ async function makeWebGPURenderer(
|
|
|
1097
1188
|
// compile across both paths (charter P4 consistent abstraction +
|
|
1098
1189
|
// architecture-principles #6 idempotency).
|
|
1099
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;
|
|
1100
1196
|
const getShaderModuleAdapter = (): ShaderDeviceAdapterInternal => {
|
|
1101
1197
|
if (sharedShaderModuleAdapter === null) {
|
|
1102
1198
|
sharedShaderModuleAdapter = makeShaderDeviceAdapter(
|
|
1103
1199
|
internals.device,
|
|
1104
1200
|
internals.errorRegistry,
|
|
1105
1201
|
internals.pack.createShaderModule,
|
|
1202
|
+
internals.pack.createShaderModuleImmediate,
|
|
1106
1203
|
);
|
|
1107
1204
|
}
|
|
1108
1205
|
return sharedShaderModuleAdapter;
|
|
1109
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
|
+
};
|
|
1110
1219
|
const getShader = (): ShaderCatalog => {
|
|
1111
1220
|
if (shaderInstance === null) {
|
|
1112
1221
|
// feat-20260608-create-app-param-surface-trim / M2 / D-2 q5-A:
|
|
@@ -1326,13 +1435,13 @@ async function makeWebGPURenderer(
|
|
|
1326
1435
|
>();
|
|
1327
1436
|
const preparedMaterialPipelineLayoutCache = new Map<string, PipelineLayout>();
|
|
1328
1437
|
// feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1): per-shader material
|
|
1329
|
-
// BGL + pipeline layout cache.
|
|
1330
|
-
// user-region
|
|
1331
|
-
//
|
|
1332
|
-
//
|
|
1333
|
-
//
|
|
1334
|
-
//
|
|
1335
|
-
//
|
|
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.
|
|
1336
1445
|
type PerShaderMaterialLayout = {
|
|
1337
1446
|
materialBgl: BindGroupLayout;
|
|
1338
1447
|
pipelineLayout: PipelineLayout;
|
|
@@ -1343,6 +1452,16 @@ async function makeWebGPURenderer(
|
|
|
1343
1452
|
readonly layout: PerShaderMaterialLayout | null;
|
|
1344
1453
|
};
|
|
1345
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';
|
|
1346
1465
|
// Material binding contracts are derived from WGSL source, but the lookup
|
|
1347
1466
|
// is also used by the per-submesh bind-group path. Cache the derived value
|
|
1348
1467
|
// per renderer so a frame does not re-run the comment stripping and regex
|
|
@@ -1477,7 +1596,9 @@ async function makeWebGPURenderer(
|
|
|
1477
1596
|
if (cached !== undefined) return cached;
|
|
1478
1597
|
if (pipelineState === null) return null;
|
|
1479
1598
|
const materialBgl =
|
|
1480
|
-
|
|
1599
|
+
(isEngineOwnedMaterialShader(materialShaderId)
|
|
1600
|
+
? undefined
|
|
1601
|
+
: getOrBuildPerShaderMaterialLayout(materialShaderId)?.materialBgl) ??
|
|
1481
1602
|
pipelineState.materialBindGroupLayout;
|
|
1482
1603
|
const result = internals.device.createPipelineLayout({
|
|
1483
1604
|
label: `prepared-material-pl-${materialShaderId}`,
|
|
@@ -1490,15 +1611,15 @@ async function makeWebGPURenderer(
|
|
|
1490
1611
|
preparedMaterialPipelineLayoutCache.set(materialShaderId, result.value);
|
|
1491
1612
|
return result.value;
|
|
1492
1613
|
};
|
|
1493
|
-
// Returns the per-shader { materialBgl, pipelineLayout } for
|
|
1494
|
-
//
|
|
1495
|
-
//
|
|
1496
|
-
//
|
|
1497
|
-
//
|
|
1498
|
-
// '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.
|
|
1499
1619
|
const getOrBuildPerShaderMaterialLayout = (
|
|
1500
1620
|
materialShaderId: string,
|
|
1501
1621
|
): PerShaderMaterialLayout | null => {
|
|
1622
|
+
if (isEngineOwnedMaterialShader(materialShaderId)) return null;
|
|
1502
1623
|
const lookup = getShader().findMaterialArtifact(materialShaderId);
|
|
1503
1624
|
if (!lookup.ok) return null;
|
|
1504
1625
|
const cached = perShaderMaterialLayoutCache.get(materialShaderId);
|
|
@@ -1507,16 +1628,6 @@ async function makeWebGPURenderer(
|
|
|
1507
1628
|
}
|
|
1508
1629
|
if (pipelineState === null) return null;
|
|
1509
1630
|
const paramSchema = lookup.value.paramSchema;
|
|
1510
|
-
// 4-or-fewer user-region textures derive to the same shape as the shared
|
|
1511
|
-
// built-in BGL -> reuse it (D-2; no per-shader entry).
|
|
1512
|
-
if (derive(paramSchema).textureFieldNames.size <= 4) {
|
|
1513
|
-
perShaderMaterialLayoutCache.set(materialShaderId, {
|
|
1514
|
-
source: lookup.value.source,
|
|
1515
|
-
paramSchema,
|
|
1516
|
-
layout: null,
|
|
1517
|
-
});
|
|
1518
|
-
return null;
|
|
1519
|
-
}
|
|
1520
1631
|
const spec: PipelineSpec = {
|
|
1521
1632
|
shader: { id: materialShaderId, passKind: 'forward', variantSet: undefined },
|
|
1522
1633
|
attachments: { colorFormats: [], depthFormat: undefined, sampleCount: 1 },
|
|
@@ -1603,9 +1714,11 @@ async function makeWebGPURenderer(
|
|
|
1603
1714
|
// `internals.device` / `internals.pack` are read live, so a rebuild after a
|
|
1604
1715
|
// device swap compiles against the new device.
|
|
1605
1716
|
let tonemapRegistered = false;
|
|
1606
|
-
|
|
1717
|
+
let temporalPostProcessesRegistered = false;
|
|
1718
|
+
const buildPipeline = (scope: DeviceScope = activeDeviceScope): Promise<PipelineState> =>
|
|
1607
1719
|
buildReadyWebGPU(
|
|
1608
1720
|
internals.device,
|
|
1721
|
+
scope,
|
|
1609
1722
|
getShader,
|
|
1610
1723
|
gpuStore,
|
|
1611
1724
|
internals.pack.createShaderModule,
|
|
@@ -1644,13 +1757,31 @@ async function makeWebGPURenderer(
|
|
|
1644
1757
|
// tonemap into this channel each frame (render-system-extract.ts w13).
|
|
1645
1758
|
(source: string) => {
|
|
1646
1759
|
if (tonemapRegistered) return;
|
|
1647
|
-
renderSystem.registerBuiltinPostProcess(
|
|
1760
|
+
renderSystem.registerBuiltinPostProcess(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, {
|
|
1648
1761
|
source,
|
|
1649
1762
|
params: { byteSize: 16, defaultValue: new Uint8Array(16) },
|
|
1650
1763
|
reads: ['hdrColor'],
|
|
1651
1764
|
});
|
|
1652
1765
|
tonemapRegistered = true;
|
|
1653
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
|
+
},
|
|
1654
1785
|
);
|
|
1655
1786
|
const ready: Promise<Result<void, RhiError>> = buildPipeline().then(
|
|
1656
1787
|
(state): Result<void, RhiError> => {
|
|
@@ -1730,6 +1861,8 @@ async function makeWebGPURenderer(
|
|
|
1730
1861
|
meshAttributes?: VertexAttributeMap,
|
|
1731
1862
|
vertexLayout?: string,
|
|
1732
1863
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
1864
|
+
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
1865
|
+
layoutKindOverride?: LayoutKind,
|
|
1733
1866
|
) => {
|
|
1734
1867
|
// bug-20260611: derive LayoutKind from materialShaderId (caller-driven)
|
|
1735
1868
|
// so the selector body stays free of literal shader-id strings (AC-09).
|
|
@@ -1741,17 +1874,17 @@ async function makeWebGPURenderer(
|
|
|
1741
1874
|
// through the HDRP layout branch of the selector — sprite shaders have
|
|
1742
1875
|
// no HDRP variant, plan-strategy R-1' guard.
|
|
1743
1876
|
const layoutKind: LayoutKind | undefined =
|
|
1744
|
-
|
|
1877
|
+
layoutKindOverride ??
|
|
1878
|
+
(materialShaderId === SKIN_MATERIAL_SHADER_ID ||
|
|
1879
|
+
isSkinnedShadowCasterVariant(materialShaderId, variantSet)
|
|
1745
1880
|
? 'pbr-skin'
|
|
1746
1881
|
: materialShaderId === 'forgeax::sprite' || materialShaderId === 'forgeax::sprite-lit'
|
|
1747
1882
|
? 'sprite-urp'
|
|
1748
1883
|
: materialShaderId === 'forgeax::default-unlit'
|
|
1749
1884
|
? 'unlit-urp'
|
|
1750
|
-
: undefined;
|
|
1885
|
+
: undefined);
|
|
1751
1886
|
const usesSharedBootMaterialLayout =
|
|
1752
|
-
materialShaderId
|
|
1753
|
-
materialShaderId === 'forgeax::default-unlit' ||
|
|
1754
|
-
materialShaderId === SKIN_MATERIAL_SHADER_ID;
|
|
1887
|
+
materialShaderId !== undefined && isEngineOwnedMaterialShader(materialShaderId);
|
|
1755
1888
|
// Built-in PBR/unlit/skin layouts are assembled at boot. Re-deriving a
|
|
1756
1889
|
// per-shader layout from the expanded PBR schema would bypass the HDRP
|
|
1757
1890
|
// group(2) selector and bind the URP mesh BGL to the cluster pipeline.
|
|
@@ -1759,12 +1892,11 @@ async function makeWebGPURenderer(
|
|
|
1759
1892
|
!usesSharedBootMaterialLayout && layoutKind !== 'pbr-skin' && materialShaderId !== undefined
|
|
1760
1893
|
? getOrBuildPerShaderMaterialLayout(materialShaderId)
|
|
1761
1894
|
: null;
|
|
1762
|
-
// feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1):
|
|
1763
|
-
//
|
|
1764
|
-
//
|
|
1765
|
-
//
|
|
1766
|
-
//
|
|
1767
|
-
// (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.
|
|
1768
1900
|
const bindingContract =
|
|
1769
1901
|
materialShaderId === undefined
|
|
1770
1902
|
? 'render-material'
|
|
@@ -1900,9 +2032,13 @@ async function makeWebGPURenderer(
|
|
|
1900
2032
|
] satisfies readonly GPUVertexBufferLayout[]);
|
|
1901
2033
|
return {
|
|
1902
2034
|
device: internals.device,
|
|
1903
|
-
shaderModuleFactory:
|
|
2035
|
+
shaderModuleFactory:
|
|
2036
|
+
shaderModuleMode === 'immediate'
|
|
2037
|
+
? getImmediateShaderModuleAdapter()
|
|
2038
|
+
: getShaderModuleAdapter(),
|
|
1904
2039
|
pipelineLayout,
|
|
1905
2040
|
vertexBuffers,
|
|
2041
|
+
...(layoutKind === 'pbr-skin' ? { layoutKind } : {}),
|
|
1906
2042
|
};
|
|
1907
2043
|
};
|
|
1908
2044
|
/**
|
|
@@ -1960,6 +2096,8 @@ async function makeWebGPURenderer(
|
|
|
1960
2096
|
depthFormatOverride?: GPUTextureFormat | null,
|
|
1961
2097
|
vertexLayout?: string,
|
|
1962
2098
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2099
|
+
shaderModuleMode?: RenderFeatureShaderModuleMode,
|
|
2100
|
+
layoutKindOverride?: LayoutKind,
|
|
1963
2101
|
): RenderPipeline | null => {
|
|
1964
2102
|
const ctx = buildPipelineContext(
|
|
1965
2103
|
variantSet,
|
|
@@ -1967,6 +2105,8 @@ async function makeWebGPURenderer(
|
|
|
1967
2105
|
meshAttributes,
|
|
1968
2106
|
vertexLayout,
|
|
1969
2107
|
vertexLayoutProjection,
|
|
2108
|
+
shaderModuleMode,
|
|
2109
|
+
layoutKindOverride,
|
|
1970
2110
|
);
|
|
1971
2111
|
if (ctx === null) return null;
|
|
1972
2112
|
// bug-20260612: route the LDR color format through pipelineState so the
|
|
@@ -1976,6 +2116,11 @@ async function makeWebGPURenderer(
|
|
|
1976
2116
|
// the explicit null check).
|
|
1977
2117
|
if (pipelineState === null) return null;
|
|
1978
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;
|
|
1979
2124
|
const built = buildPipelineForMaterialShader(
|
|
1980
2125
|
cacheKey,
|
|
1981
2126
|
// The catalog entry is projected into the material-shader entry shape.
|
|
@@ -2009,7 +2154,7 @@ async function makeWebGPURenderer(
|
|
|
2009
2154
|
}
|
|
2010
2155
|
: undefined,
|
|
2011
2156
|
undefined, // vertexEntry — default vs_main
|
|
2012
|
-
|
|
2157
|
+
fragmentEntry,
|
|
2013
2158
|
undefined, // defines — none
|
|
2014
2159
|
passKind,
|
|
2015
2160
|
sampleCount,
|
|
@@ -2068,6 +2213,8 @@ async function makeWebGPURenderer(
|
|
|
2068
2213
|
depthFormatOverride?: GPUTextureFormat | null,
|
|
2069
2214
|
vertexLayout?: string,
|
|
2070
2215
|
vertexLayoutProjection?: VertexLayoutProjection,
|
|
2216
|
+
shaderModuleMode: RenderFeatureShaderModuleMode = 'validated',
|
|
2217
|
+
layoutKindOverride?: LayoutKind,
|
|
2071
2218
|
): RenderPipeline | null => {
|
|
2072
2219
|
// WebGL2 is a downlevel backend even when the record stage asks for the
|
|
2073
2220
|
// native-capability variant string. Resolve the variant axes at this
|
|
@@ -2082,7 +2229,14 @@ async function makeWebGPURenderer(
|
|
|
2082
2229
|
// (stored in materialShaderUvSetCounts during prepareMaterialShaders).
|
|
2083
2230
|
const resolvedUvSetCount = shaderUvSetCount ?? materialShaderUvSetCounts.get(materialShaderId);
|
|
2084
2231
|
const manifestEntry = findMaterialShaderManifestEntry(materialShaderId);
|
|
2085
|
-
|
|
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);
|
|
2086
2240
|
// The record stage also composes engine capability axes for user shaders.
|
|
2087
2241
|
// Keep only manifest-declared axes for non-engine material IDs: a plain
|
|
2088
2242
|
// custom shader must not collapse that request to '' because '' is the
|
|
@@ -2227,6 +2381,7 @@ async function makeWebGPURenderer(
|
|
|
2227
2381
|
depthFormatOverride,
|
|
2228
2382
|
vertexLayout,
|
|
2229
2383
|
vertexLayoutProjection,
|
|
2384
|
+
shaderModuleMode,
|
|
2230
2385
|
);
|
|
2231
2386
|
}
|
|
2232
2387
|
const pbrEntry = findStandardPbrEntry(effectiveVariantSet);
|
|
@@ -2280,6 +2435,7 @@ async function makeWebGPURenderer(
|
|
|
2280
2435
|
depthFormatOverride,
|
|
2281
2436
|
vertexLayout,
|
|
2282
2437
|
vertexLayoutProjection,
|
|
2438
|
+
shaderModuleMode,
|
|
2283
2439
|
);
|
|
2284
2440
|
}
|
|
2285
2441
|
// feat-20260609 M4 / w31: resolve variant WGSL from manifest when
|
|
@@ -2370,6 +2526,8 @@ async function makeWebGPURenderer(
|
|
|
2370
2526
|
depthFormatOverride,
|
|
2371
2527
|
vertexLayout,
|
|
2372
2528
|
vertexLayoutProjection,
|
|
2529
|
+
shaderModuleMode,
|
|
2530
|
+
layoutKindOverride,
|
|
2373
2531
|
);
|
|
2374
2532
|
};
|
|
2375
2533
|
const getParamSchema = (materialShaderId: string) => {
|
|
@@ -2378,10 +2536,11 @@ async function makeWebGPURenderer(
|
|
|
2378
2536
|
};
|
|
2379
2537
|
// feat-20260621-learn-render-5-5-parallax M2 / w6 (D-1): expose the per-shader
|
|
2380
2538
|
// material BGL so the record stage creates the material bind group against
|
|
2381
|
-
// the matching layout
|
|
2382
|
-
//
|
|
2383
|
-
//
|
|
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.
|
|
2384
2542
|
const getMaterialBindGroupLayout = (materialShaderId: string): BindGroupLayout | undefined => {
|
|
2543
|
+
if (isEngineOwnedMaterialShader(materialShaderId)) return undefined;
|
|
2385
2544
|
const contract = getCachedMaterialShaderBindingContract(materialShaderId);
|
|
2386
2545
|
if (contract === 'group-0') {
|
|
2387
2546
|
return getOrBuildGroup0MaterialLayout()?.materialBgl;
|
|
@@ -2458,7 +2617,11 @@ async function makeWebGPURenderer(
|
|
|
2458
2617
|
fragment: {
|
|
2459
2618
|
module: moduleResult.value,
|
|
2460
2619
|
entryPoint: 'fs_main',
|
|
2461
|
-
targets:
|
|
2620
|
+
targets:
|
|
2621
|
+
entry.source.includes('@location(1) temporal') &&
|
|
2622
|
+
entry.source.includes('TaaResolveOutput')
|
|
2623
|
+
? [{ format: colorFormat }, { format: colorFormat }]
|
|
2624
|
+
: [{ format: colorFormat }],
|
|
2462
2625
|
},
|
|
2463
2626
|
primitive: { topology: 'triangle-list', cullMode: 'none', frontFace: 'ccw' },
|
|
2464
2627
|
depthStencil: undefined,
|
|
@@ -2487,6 +2650,10 @@ async function makeWebGPURenderer(
|
|
|
2487
2650
|
shaderModuleFactory: {
|
|
2488
2651
|
createShaderModule: (descriptor) => getShaderModuleAdapter().createShaderModule(descriptor),
|
|
2489
2652
|
},
|
|
2653
|
+
immediateShaderModuleFactory: {
|
|
2654
|
+
createShaderModule: (descriptor) =>
|
|
2655
|
+
getImmediateShaderModuleAdapter().createShaderModule(descriptor),
|
|
2656
|
+
},
|
|
2490
2657
|
profiler: internals.options?.profiler,
|
|
2491
2658
|
// feat-20260622-s5 M3 / w17: device + context read live off `internals` via
|
|
2492
2659
|
// getters so the recover() rebuild (which swaps internals.device /
|
|
@@ -2499,6 +2666,9 @@ async function makeWebGPURenderer(
|
|
|
2499
2666
|
get deviceScope() {
|
|
2500
2667
|
return activeDeviceScope;
|
|
2501
2668
|
},
|
|
2669
|
+
invalidateShaderModule: (label: string) => {
|
|
2670
|
+
sharedShaderModuleAdapter?.invalidateModule(label);
|
|
2671
|
+
},
|
|
2502
2672
|
get context() {
|
|
2503
2673
|
return internals.context;
|
|
2504
2674
|
},
|
|
@@ -2536,6 +2706,18 @@ async function makeWebGPURenderer(
|
|
|
2536
2706
|
return internals.meshSsboState;
|
|
2537
2707
|
},
|
|
2538
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
|
+
}
|
|
2539
2721
|
// The Standard pipeline is the sole built-in graph owner. Feature plans are
|
|
2540
2722
|
// projected into that graph directly; no second post-process registry is
|
|
2541
2723
|
// installed for fullscreen producers.
|
|
@@ -2596,6 +2778,22 @@ async function makeWebGPURenderer(
|
|
|
2596
2778
|
: health.reason === 'internal-fault' || !readySettled
|
|
2597
2779
|
? 'faulted'
|
|
2598
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
|
+
});
|
|
2599
2797
|
return Object.freeze({
|
|
2600
2798
|
state,
|
|
2601
2799
|
surface: surfaceReleased ? 'released' : 'available',
|
|
@@ -2612,6 +2810,18 @@ async function makeWebGPURenderer(
|
|
|
2612
2810
|
frustumStats: Object.freeze({ ...renderSystem.frustumStats }),
|
|
2613
2811
|
visibilityStats: Object.freeze({ ...renderSystem.visibilityStats }),
|
|
2614
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 }),
|
|
2615
2825
|
meshMaterialBindings: Object.freeze(
|
|
2616
2826
|
renderSystem.meshMaterialBindings.map((observation) =>
|
|
2617
2827
|
Object.freeze({
|
|
@@ -2630,6 +2840,22 @@ async function makeWebGPURenderer(
|
|
|
2630
2840
|
createBindGroup: renderSystem.bindGroupCounts.createBindGroup,
|
|
2631
2841
|
keys: Object.freeze([...renderSystem.bindGroupCounts.keys]),
|
|
2632
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
|
+
}),
|
|
2633
2859
|
});
|
|
2634
2860
|
},
|
|
2635
2861
|
get device(): RhiDevice {
|
|
@@ -2720,6 +2946,12 @@ async function makeWebGPURenderer(
|
|
|
2720
2946
|
observeCurrentFrame(options: FrameObservationOptions) {
|
|
2721
2947
|
return renderSystem.observeCurrentFrame(options);
|
|
2722
2948
|
},
|
|
2949
|
+
getCurrentGraphTarget(name: string) {
|
|
2950
|
+
return renderSystem.getCurrentGraphTarget(name);
|
|
2951
|
+
},
|
|
2952
|
+
requestGraphTargetCapture(request) {
|
|
2953
|
+
renderSystem.requestGraphTargetCapture(request);
|
|
2954
|
+
},
|
|
2723
2955
|
get frustumStats() {
|
|
2724
2956
|
return renderSystem.frustumStats;
|
|
2725
2957
|
},
|
|
@@ -2991,7 +3223,12 @@ async function makeWebGPURenderer(
|
|
|
2991
3223
|
// Configure context lazily on first draw (D-S1 single-point
|
|
2992
3224
|
// exemption): GPUCanvasContext.configure({device}) needs a raw
|
|
2993
3225
|
// The canvas context is configured through the RHI device scope.
|
|
2994
|
-
ensureContextConfigured(
|
|
3226
|
+
const contextConfigured = ensureContextConfigured(
|
|
3227
|
+
internals,
|
|
3228
|
+
pipelineState,
|
|
3229
|
+
internals.errorRegistry,
|
|
3230
|
+
);
|
|
3231
|
+
if (!contextConfigured.ok) return contextConfigured;
|
|
2995
3232
|
// w24 — facade-level try/catch produces Result.err on unexpected throw
|
|
2996
3233
|
// (D-P6 dual-channel preserved: per-stage RhiError continues to fan out
|
|
2997
3234
|
// through onError separately; the facade Result is the synchronous
|
|
@@ -3159,13 +3396,6 @@ async function makeWebGPURenderer(
|
|
|
3159
3396
|
if (disposed) return ok(undefined);
|
|
3160
3397
|
disposed = true;
|
|
3161
3398
|
const cleanupFailures: RendererOperationCause[] = [];
|
|
3162
|
-
try {
|
|
3163
|
-
activeDeviceScope.dispose();
|
|
3164
|
-
} catch (cause) {
|
|
3165
|
-
const error = wrapDisposeError(cause, 'activeDeviceScope.dispose');
|
|
3166
|
-
cleanupFailures.push(error);
|
|
3167
|
-
internals.errorRegistry.fire(error);
|
|
3168
|
-
}
|
|
3169
3399
|
for (const world of attachedWorlds) {
|
|
3170
3400
|
try {
|
|
3171
3401
|
const derivedSystemName = derivedSystemNames.get(world);
|
|
@@ -3247,6 +3477,18 @@ async function makeWebGPURenderer(
|
|
|
3247
3477
|
cleanupFailures.push(error);
|
|
3248
3478
|
internals.errorRegistry.fire(error);
|
|
3249
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
|
+
}
|
|
3250
3492
|
// Step 5: the IBL cache is keyed by the retired DeviceScope generation.
|
|
3251
3493
|
// It is no longer reachable through the active renderer scope, while
|
|
3252
3494
|
// the device teardown owns the child GPU handles.
|
|
@@ -3326,6 +3568,7 @@ async function makeWebGPURenderer(
|
|
|
3326
3568
|
// buffer (or crash the browser GPU process). Keep the ShaderCatalog's
|
|
3327
3569
|
// CPU material/source catalogue, but rebuild this device-bound adapter.
|
|
3328
3570
|
sharedShaderModuleAdapter = null;
|
|
3571
|
+
sharedImmediateShaderModuleAdapter = null;
|
|
3329
3572
|
// The fullscreen post-process builder keeps one empty group-0 layout
|
|
3330
3573
|
// outside RenderSystem's frame cache. It is also device-bound, so retain
|
|
3331
3574
|
// the source/registration but force the next post-process pass to derive
|
|
@@ -3372,6 +3615,13 @@ async function makeWebGPURenderer(
|
|
|
3372
3615
|
// swap is observed without reconstructing the RenderSystem.
|
|
3373
3616
|
internals.device = device;
|
|
3374
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
|
+
);
|
|
3375
3625
|
// Re-attach the device.lost fan-out to the new device's lost Promise,
|
|
3376
3626
|
// reusing the SAME registries the host already subscribed to (SSOT
|
|
3377
3627
|
// helper). When this device is lost again, health() flips to
|
|
@@ -3419,10 +3669,13 @@ async function makeWebGPURenderer(
|
|
|
3419
3669
|
// the first post-recovery upload; unlike CPU asset payloads, these
|
|
3420
3670
|
// handles cannot survive a device replacement.
|
|
3421
3671
|
dynamicTextureStore.configureGpuDevice(adaptDynamicTextureDevice(internals.device));
|
|
3672
|
+
let candidatePipelineState: PipelineState | undefined;
|
|
3422
3673
|
const abortDisposedRecovery = (): Result<void, RecoverFailure> | undefined => {
|
|
3423
3674
|
if (!disposed) return undefined;
|
|
3424
3675
|
gpuStore.destroyAll();
|
|
3676
|
+
candidatePipelineState?.perPassResources.drainBloomResources?.();
|
|
3425
3677
|
renderSystem.disposeFrameState();
|
|
3678
|
+
recoveryScope.abandon();
|
|
3426
3679
|
materialShaderPipelineCache.clear();
|
|
3427
3680
|
perShaderMaterialLayoutCache.clear();
|
|
3428
3681
|
group0ResourceLayouts.clear();
|
|
@@ -3441,7 +3694,7 @@ async function makeWebGPURenderer(
|
|
|
3441
3694
|
);
|
|
3442
3695
|
const abortedAfterShaders = abortDisposedRecovery();
|
|
3443
3696
|
if (abortedAfterShaders !== undefined) return abortedAfterShaders;
|
|
3444
|
-
|
|
3697
|
+
candidatePipelineState = await buildPipeline(recoveryScope);
|
|
3445
3698
|
const abortedAfterPipeline = abortDisposedRecovery();
|
|
3446
3699
|
if (abortedAfterPipeline !== undefined) return abortedAfterPipeline;
|
|
3447
3700
|
renderSystem.restorePostProcessResources();
|
|
@@ -3449,6 +3702,8 @@ async function makeWebGPURenderer(
|
|
|
3449
3702
|
// Pipeline rebuild failed against the new device. Treat as a device
|
|
3450
3703
|
// unavailability (the device was acquired but is not usable); health
|
|
3451
3704
|
// stays `device-lost` so the host can retry.
|
|
3705
|
+
candidatePipelineState?.perPassResources.drainBloomResources?.();
|
|
3706
|
+
recoveryScope.abandon();
|
|
3452
3707
|
const aborted = abortDisposedRecovery();
|
|
3453
3708
|
if (aborted !== undefined) return aborted;
|
|
3454
3709
|
return err(new RecoverError('recover-device-unavailable'));
|
|
@@ -3456,27 +3711,18 @@ async function makeWebGPURenderer(
|
|
|
3456
3711
|
const abortedAfterPostProcess = abortDisposedRecovery();
|
|
3457
3712
|
if (abortedAfterPostProcess !== undefined) return abortedAfterPostProcess;
|
|
3458
3713
|
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
)
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
return err(
|
|
3466
|
-
new RenderRecoveryError(
|
|
3467
|
-
disposed ? 'recover-disposed-during-rebuild' : 'recover-lifecycle-failed',
|
|
3468
|
-
{
|
|
3469
|
-
owner: detail.owner,
|
|
3470
|
-
generation: detail.generation,
|
|
3471
|
-
recovery: disposed ? 'stop' : 'recover',
|
|
3472
|
-
receipt: replacementResult.error.receipt,
|
|
3473
|
-
cause: replacementResult.error,
|
|
3474
|
-
},
|
|
3475
|
-
),
|
|
3476
|
-
);
|
|
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'));
|
|
3477
3720
|
}
|
|
3478
|
-
|
|
3721
|
+
const previousDeviceScope = activeDeviceScope;
|
|
3722
|
+
activeDeviceScope = recoveryScope;
|
|
3723
|
+
pipelineState = candidatePipelineState;
|
|
3479
3724
|
gpuStore.bindDeviceScope(activeDeviceScope);
|
|
3725
|
+
previousDeviceScope.retire();
|
|
3480
3726
|
|
|
3481
3727
|
// Re-evaluate feature capability gates against the replacement device.
|
|
3482
3728
|
// Feature recovery is isolated from renderer recovery: a feature that is
|
|
@@ -3574,7 +3820,7 @@ export function exposeRenderer(renderer: RendererHostImplementation): Renderer {
|
|
|
3574
3820
|
);
|
|
3575
3821
|
},
|
|
3576
3822
|
setProfile: (profile) => {
|
|
3577
|
-
if (currentState
|
|
3823
|
+
if (!isRendererStateOperational(currentState)) {
|
|
3578
3824
|
return err(
|
|
3579
3825
|
new RendererOperationError('renderer-state-invalid', {
|
|
3580
3826
|
operation: 'set-profile',
|
|
@@ -3647,7 +3893,7 @@ export function exposeRenderer(renderer: RendererHostImplementation): Renderer {
|
|
|
3647
3893
|
const error = new RendererOperationError('recovery-failed', {
|
|
3648
3894
|
operation: 'recover',
|
|
3649
3895
|
oldGeneration,
|
|
3650
|
-
cause: result.error,
|
|
3896
|
+
cause: structuredRendererCause(result.error, 'recover'),
|
|
3651
3897
|
});
|
|
3652
3898
|
emit(Object.freeze({ kind: 'error', error }));
|
|
3653
3899
|
return err(error);
|
|
@@ -3690,13 +3936,24 @@ function structuredRendererCause(cause: unknown, operation: string): RendererOpe
|
|
|
3690
3936
|
typeof candidate.expected === 'string' &&
|
|
3691
3937
|
typeof candidate.hint === 'string'
|
|
3692
3938
|
) {
|
|
3693
|
-
return
|
|
3939
|
+
return {
|
|
3940
|
+
code: candidate.code,
|
|
3941
|
+
expected: candidate.expected,
|
|
3942
|
+
hint: candidate.hint,
|
|
3943
|
+
...(candidate.detail === undefined ? {} : { detail: candidate.detail }),
|
|
3944
|
+
};
|
|
3694
3945
|
}
|
|
3695
3946
|
}
|
|
3696
|
-
|
|
3947
|
+
const contractFailure = new RendererContractFailureError(
|
|
3697
3948
|
'draw',
|
|
3698
3949
|
`${operation}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
3699
3950
|
);
|
|
3951
|
+
return {
|
|
3952
|
+
code: contractFailure.code,
|
|
3953
|
+
expected: contractFailure.expected,
|
|
3954
|
+
hint: contractFailure.hint,
|
|
3955
|
+
detail: contractFailure.detail,
|
|
3956
|
+
};
|
|
3700
3957
|
}
|
|
3701
3958
|
|
|
3702
3959
|
const RENDER_PROFILE_KEYS = new Set([
|
|
@@ -3707,10 +3964,6 @@ const RENDER_PROFILE_KEYS = new Set([
|
|
|
3707
3964
|
'pbr',
|
|
3708
3965
|
'ibl',
|
|
3709
3966
|
'ssao',
|
|
3710
|
-
'bloom',
|
|
3711
|
-
'tone',
|
|
3712
|
-
'antialias',
|
|
3713
|
-
'sky',
|
|
3714
3967
|
'fallback',
|
|
3715
3968
|
'postStages',
|
|
3716
3969
|
]);
|
|
@@ -3731,32 +3984,13 @@ function validateRenderProfile(profile: RenderProfile): string | undefined {
|
|
|
3731
3984
|
if (profile.shadows !== 'off' && profile.shadows !== 'hard' && profile.shadows !== 'filtered') {
|
|
3732
3985
|
return "RenderProfile.shadows must be 'off', 'hard', or 'filtered'";
|
|
3733
3986
|
}
|
|
3734
|
-
if (
|
|
3735
|
-
profile.tone !== 'none' &&
|
|
3736
|
-
profile.tone !== 'aces-filmic' &&
|
|
3737
|
-
profile.tone !== 'agx' &&
|
|
3738
|
-
profile.tone !== 'cineon' &&
|
|
3739
|
-
profile.tone !== 'linear' &&
|
|
3740
|
-
profile.tone !== 'neutral' &&
|
|
3741
|
-
profile.tone !== 'reinhard' &&
|
|
3742
|
-
profile.tone !== 'reinhard-extended'
|
|
3743
|
-
) {
|
|
3744
|
-
return 'RenderProfile.tone is not a supported Standard tone';
|
|
3745
|
-
}
|
|
3746
|
-
if (
|
|
3747
|
-
profile.antialias !== 'none' &&
|
|
3748
|
-
profile.antialias !== 'fxaa' &&
|
|
3749
|
-
profile.antialias !== 'msaa'
|
|
3750
|
-
) {
|
|
3751
|
-
return "RenderProfile.antialias must be 'none', 'fxaa', or 'msaa'";
|
|
3752
|
-
}
|
|
3753
3987
|
if (profile.fallback !== 'native' && profile.fallback !== 'cpu-webgl2') {
|
|
3754
3988
|
return "RenderProfile.fallback must be 'native' or 'cpu-webgl2'";
|
|
3755
3989
|
}
|
|
3756
|
-
for (const field of ['pbr', 'ibl', 'ssao'
|
|
3990
|
+
for (const field of ['pbr', 'ibl', 'ssao'] as const) {
|
|
3757
3991
|
if (typeof profile[field] !== 'boolean') return `RenderProfile.${field} must be boolean`;
|
|
3758
3992
|
}
|
|
3759
|
-
const stages =
|
|
3993
|
+
const stages = STANDARD_POST_STAGE_NAMES;
|
|
3760
3994
|
if (
|
|
3761
3995
|
!Array.isArray(profile.postStages) ||
|
|
3762
3996
|
profile.postStages.length !== stages.length ||
|
|
@@ -3807,8 +4041,11 @@ interface ShaderDeviceAdapterInternal extends ShaderCatalogDevice {
|
|
|
3807
4041
|
* `module-forgeax::default-shadow-caster` returns OK on frame 1.
|
|
3808
4042
|
*/
|
|
3809
4043
|
seedModule(label: string, module: ShaderModule): void;
|
|
4044
|
+
invalidateModule(label: string): void;
|
|
3810
4045
|
}
|
|
3811
4046
|
|
|
4047
|
+
type ShaderModuleAdapterMode = 'validated' | 'immediate';
|
|
4048
|
+
|
|
3812
4049
|
function makeShaderDeviceAdapter(
|
|
3813
4050
|
rhiDevice: RhiDevice,
|
|
3814
4051
|
_errorRegistry: RhiErrorListenerRegistry,
|
|
@@ -3818,10 +4055,18 @@ function makeShaderDeviceAdapter(
|
|
|
3818
4055
|
desc: { code: string; label?: string | undefined },
|
|
3819
4056
|
) => Promise<Result<ShaderModule, RhiError>>)
|
|
3820
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',
|
|
3821
4065
|
): ShaderDeviceAdapterInternal {
|
|
3822
4066
|
const moduleCache = new Map<string, ShaderModule>();
|
|
3823
4067
|
const errorCache = new Map<string, RhiError>();
|
|
3824
|
-
const pending = new
|
|
4068
|
+
const pending = new Map<string, number>();
|
|
4069
|
+
const epochs = new Map<string, number>();
|
|
3825
4070
|
|
|
3826
4071
|
return {
|
|
3827
4072
|
createShaderModule(desc): Result<ShaderModule, RhiError> {
|
|
@@ -3831,18 +4076,39 @@ function makeShaderDeviceAdapter(
|
|
|
3831
4076
|
const cachedError = errorCache.get(key);
|
|
3832
4077
|
if (cachedError !== undefined) return err(cachedError);
|
|
3833
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
|
+
|
|
3834
4098
|
// Fire async creation; first sync call returns pending error (the AI consumer retry pattern).
|
|
3835
4099
|
// M3 D-P4: rhi-webgpu supplies the async factory; rhi-wgpu (no top-level
|
|
3836
4100
|
// async factory) falls back to the synchronous device.createShaderModule
|
|
3837
4101
|
// entry which already returns Result<ShaderModule, RhiError>.
|
|
3838
4102
|
if (!pending.has(key)) {
|
|
3839
|
-
|
|
4103
|
+
const epoch = epochs.get(key) ?? 0;
|
|
4104
|
+
pending.set(key, epoch);
|
|
3840
4105
|
const desc2: { code: string; label?: string | undefined } = { code: desc.code };
|
|
3841
4106
|
if (desc.label !== undefined) desc2.label = desc.label;
|
|
3842
4107
|
const asyncResult: Promise<Result<ShaderModule, RhiError>> = asyncCreateShaderModule
|
|
3843
4108
|
? asyncCreateShaderModule(rhiDevice, desc2)
|
|
3844
4109
|
: invokeDeviceCreateShaderModule(rhiDevice, desc2);
|
|
3845
4110
|
void asyncResult.then((result) => {
|
|
4111
|
+
if (pending.get(key) !== epoch || (epochs.get(key) ?? 0) !== epoch) return;
|
|
3846
4112
|
if (result.ok) {
|
|
3847
4113
|
moduleCache.set(key, result.value);
|
|
3848
4114
|
} else {
|
|
@@ -3857,6 +4123,12 @@ function makeShaderDeviceAdapter(
|
|
|
3857
4123
|
seedModule(label: string, module: ShaderModule): void {
|
|
3858
4124
|
moduleCache.set(label, module);
|
|
3859
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
|
+
},
|
|
3860
4132
|
};
|
|
3861
4133
|
}
|
|
3862
4134
|
|
|
@@ -4102,7 +4374,11 @@ const HDR_COLOR_ATTACHMENT_FORMAT: GPUTextureFormat = 'rgba16float';
|
|
|
4102
4374
|
// so it is intentionally absent here. A `mipmap:true` texture in an
|
|
4103
4375
|
// un-prewarmed format surfaces a structured RhiError at record (never an
|
|
4104
4376
|
// async stall) -- the falsifiable anchor for an incomplete prewarm list.
|
|
4105
|
-
const MIPMAP_PREWARM_FORMATS: readonly GPUTextureFormat[] = [
|
|
4377
|
+
const MIPMAP_PREWARM_FORMATS: readonly GPUTextureFormat[] = [
|
|
4378
|
+
'rgba8unorm-srgb',
|
|
4379
|
+
'rgba8unorm',
|
|
4380
|
+
'rgba16float',
|
|
4381
|
+
];
|
|
4106
4382
|
|
|
4107
4383
|
// feat-20260531-bloom-first-declarative-render-graph-pass / w13:
|
|
4108
4384
|
// UBO sizes for bloom params (std140 layout).
|
|
@@ -4516,6 +4792,13 @@ async function prepareMaterialShaders(
|
|
|
4516
4792
|
const variantDefines: Record<string, boolean> = {
|
|
4517
4793
|
STORAGE_BUFFER_AVAILABLE: storageBufferCapable,
|
|
4518
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
|
+
}
|
|
4519
4802
|
// Only include CLUSTER_FORWARD_AVAILABLE when the material shader declares
|
|
4520
4803
|
// the axis (has a variant with this key), preserving backward compat for
|
|
4521
4804
|
// entries without the pragma.
|
|
@@ -4525,6 +4808,12 @@ async function prepareMaterialShaders(
|
|
|
4525
4808
|
if (msEntry.variants.some((v) => 'VERTEX_COLOR_AVAILABLE' in v.defines)) {
|
|
4526
4809
|
variantDefines.VERTEX_COLOR_AVAILABLE = false;
|
|
4527
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
|
+
}
|
|
4528
4817
|
// bug-20260708 M2 (a): PER_INSTANCE_REGION axis default = false at boot.
|
|
4529
4818
|
// Per-entity spritePH path (`main-pass-sprite-draws.ts:511-513`) expects
|
|
4530
4819
|
// `variantSet=undefined` to resolve to the PIR=false shader source (64B
|
|
@@ -4539,6 +4828,9 @@ async function prepareMaterialShaders(
|
|
|
4539
4828
|
if (msEntry.variants.some((v) => 'PER_INSTANCE_REGION' in v.defines)) {
|
|
4540
4829
|
variantDefines.PER_INSTANCE_REGION = false;
|
|
4541
4830
|
}
|
|
4831
|
+
if (msEntry.variants.some((v) => 'SKINNING_DISABLED' in v.defines)) {
|
|
4832
|
+
variantDefines.SKINNING_DISABLED = true;
|
|
4833
|
+
}
|
|
4542
4834
|
const sortedEntries = Object.entries(variantDefines).sort(([a], [b]) =>
|
|
4543
4835
|
a < b ? -1 : a > b ? 1 : 0,
|
|
4544
4836
|
);
|
|
@@ -4546,6 +4838,13 @@ async function prepareMaterialShaders(
|
|
|
4546
4838
|
? ''
|
|
4547
4839
|
: sortedEntries.map(([k, v]) => `${k}=${v}`).join('+');
|
|
4548
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
|
+
}
|
|
4549
4848
|
const wgsl = chosen?.composedWgsl ?? msEntry.composedWgsl;
|
|
4550
4849
|
if (wgsl.length > 0) {
|
|
4551
4850
|
const existing = registry.findMaterialArtifact(msEntry.identifier);
|
|
@@ -4627,6 +4926,7 @@ async function prepareMaterialShaders(
|
|
|
4627
4926
|
|
|
4628
4927
|
async function buildReadyWebGPU(
|
|
4629
4928
|
rhiDevice: RhiDevice,
|
|
4929
|
+
rendererScope: DeviceScope,
|
|
4630
4930
|
getShader: () => ShaderCatalog,
|
|
4631
4931
|
gpuStore: GpuResidencyCache,
|
|
4632
4932
|
asyncCreateShaderModule:
|
|
@@ -4680,6 +4980,10 @@ async function buildReadyWebGPU(
|
|
|
4680
4980
|
// tonemap source resolves inside this async function (after the manifest-load
|
|
4681
4981
|
// await), by which point the synchronous `renderSystem` const is defined.
|
|
4682
4982
|
registerBuiltinTonemap: (source: string) => void,
|
|
4983
|
+
registerBuiltinTemporalPostProcesses: (entries: {
|
|
4984
|
+
readonly motionBlur?: string;
|
|
4985
|
+
readonly taaResolve?: string;
|
|
4986
|
+
}) => void,
|
|
4683
4987
|
): Promise<PipelineState> {
|
|
4684
4988
|
// bug-20260610: WebGL2 fallback path — read storage-buffer capability up
|
|
4685
4989
|
// front so both Step 2 (shader-module compile, where we patch engine
|
|
@@ -4706,7 +5010,10 @@ async function buildReadyWebGPU(
|
|
|
4706
5010
|
// pipeline target, configure() call, and color-attachment format —
|
|
4707
5011
|
// single SSOT, no scattered branches. See selectSwapChainFormat
|
|
4708
5012
|
// (above the SWAP_CHAIN_*_FORMAT historical constants).
|
|
4709
|
-
const
|
|
5013
|
+
const surfaceViewFormats =
|
|
5014
|
+
(rhiDevice as unknown as { readonly surfaceViewFormats?: boolean }).surfaceViewFormats ??
|
|
5015
|
+
!webgl2Downlevel;
|
|
5016
|
+
const swapChainFormats = selectSwapChainFormat(storageBufferCapable, surfaceViewFormats);
|
|
4710
5017
|
// The 'null' (headless RhiNull) backend has no UA preferred-canvas-format by
|
|
4711
5018
|
// design; the rgba8unorm fallback is its intended steady state, not a
|
|
4712
5019
|
// degraded one — firing 'rhi-not-available' there is territorially wrong
|
|
@@ -4811,6 +5118,7 @@ async function buildReadyWebGPU(
|
|
|
4811
5118
|
let pbrEntry = findEngineManifestEntry('forgeax::default-standard-pbr');
|
|
4812
5119
|
let unlitEntry = findEngineManifestEntry('forgeax::default-unlit');
|
|
4813
5120
|
let tonemapEntry: ManifestEntry | undefined;
|
|
5121
|
+
let motionBlurEntry: ManifestEntry | undefined;
|
|
4814
5122
|
let spriteEntry: ManifestEntry | undefined;
|
|
4815
5123
|
// sprite-lit identification marker is the `spriteLitShadeAccum`
|
|
4816
5124
|
// helper (defined in sprite-lit.wgsl, absent from sprite.wgsl). The
|
|
@@ -4820,6 +5128,7 @@ async function buildReadyWebGPU(
|
|
|
4820
5128
|
// their paramSchema mirror) does not mis-classify sprite-lit as
|
|
4821
5129
|
// sprite.
|
|
4822
5130
|
let spriteLitEntry: ManifestEntry | undefined;
|
|
5131
|
+
let taaResolveEntry: ManifestEntry | undefined;
|
|
4823
5132
|
let iblEquirectEntry: ManifestEntry | undefined;
|
|
4824
5133
|
let iblIrradianceEntry: ManifestEntry | undefined;
|
|
4825
5134
|
let iblPrefilterEntry: ManifestEntry | undefined;
|
|
@@ -4844,6 +5153,14 @@ async function buildReadyWebGPU(
|
|
|
4844
5153
|
tonemapEntry ??= entry;
|
|
4845
5154
|
continue;
|
|
4846
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
|
+
}
|
|
4847
5164
|
if (entry.wgsl.includes('spriteLitShadeAccum')) {
|
|
4848
5165
|
// sprite-lit identification — must precede the `pivotAndSize`
|
|
4849
5166
|
// marker since sprite-lit also carries that field (paramSchema
|
|
@@ -4985,10 +5302,10 @@ async function buildReadyWebGPU(
|
|
|
4985
5302
|
(!('CLUSTER_FORWARD_AVAILABLE' in v.defines) ||
|
|
4986
5303
|
v.defines.CLUSTER_FORWARD_AVAILABLE === false) &&
|
|
4987
5304
|
(!('VERTEX_COLOR_AVAILABLE' in v.defines) ||
|
|
4988
|
-
v.defines.VERTEX_COLOR_AVAILABLE === false)
|
|
5305
|
+
v.defines.VERTEX_COLOR_AVAILABLE === false) &&
|
|
5306
|
+
(!('SKINNING_DISABLED' in v.defines) || v.defines.SKINNING_DISABLED === true),
|
|
4989
5307
|
);
|
|
4990
|
-
const v =
|
|
4991
|
-
exact ?? ms.variants.find((cand) => cand.defines.STORAGE_BUFFER_AVAILABLE === false);
|
|
5308
|
+
const v = exact === undefined ? undefined : findVariantByKey(ms, exact.definesKey);
|
|
4992
5309
|
if (v !== undefined) {
|
|
4993
5310
|
idToVariantWgsl.set(ms.identifier, v.composedWgsl);
|
|
4994
5311
|
}
|
|
@@ -5028,13 +5345,13 @@ async function buildReadyWebGPU(
|
|
|
5028
5345
|
(!('CLUSTER_FORWARD_AVAILABLE' in variant.defines) ||
|
|
5029
5346
|
variant.defines.CLUSTER_FORWARD_AVAILABLE === false) &&
|
|
5030
5347
|
(!('VERTEX_COLOR_AVAILABLE' in variant.defines) ||
|
|
5031
|
-
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),
|
|
5032
5353
|
);
|
|
5033
|
-
const variant =
|
|
5034
|
-
exact ??
|
|
5035
|
-
ms.variants.find(
|
|
5036
|
-
(candidate) => candidate.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable,
|
|
5037
|
-
);
|
|
5354
|
+
const variant = exact === undefined ? undefined : findVariantByKey(ms, exact.definesKey);
|
|
5038
5355
|
if (variant !== undefined) {
|
|
5039
5356
|
idToRuntimeVariantWgsl.set(ms.identifier, variant.composedWgsl);
|
|
5040
5357
|
}
|
|
@@ -5190,6 +5507,12 @@ async function buildReadyWebGPU(
|
|
|
5190
5507
|
if (pbrVariant !== undefined) {
|
|
5191
5508
|
seedShaderModule(`module-forgeax::default-standard-pbr#${pbrVariant.definesKey}`, pbrModule);
|
|
5192
5509
|
}
|
|
5510
|
+
const transmissionCapable =
|
|
5511
|
+
(rhiDevice.limits.maxSampledTexturesPerShaderStage ?? 0) >=
|
|
5512
|
+
STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
5513
|
+
const transmissionVariants = transmissionCapable
|
|
5514
|
+
? selectStandardPbrTransmissionPrewarmVariants(pbrManifestEntry, storageBufferCapable)
|
|
5515
|
+
: [];
|
|
5193
5516
|
|
|
5194
5517
|
// HDRP uses the canonical all-true variant key (`''`) and the lazy
|
|
5195
5518
|
// material pipeline adapter therefore requests a distinct module label.
|
|
@@ -5197,9 +5520,35 @@ async function buildReadyWebGPU(
|
|
|
5197
5520
|
// label. Color availability is part of the geometry-owned variant axis, so
|
|
5198
5521
|
// both colored and no-color HDRP sources must be retained.
|
|
5199
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
|
+
}
|
|
5200
5548
|
for (const hdrpVariant of selectHdrpPbrPrewarmVariants(
|
|
5201
5549
|
pbrManifestEntry,
|
|
5202
5550
|
storageBufferCapable,
|
|
5551
|
+
transmissionCapable,
|
|
5203
5552
|
)) {
|
|
5204
5553
|
const moduleLabel = `module-forgeax::default-standard-pbr#${hdrpVariant.definesKey}`;
|
|
5205
5554
|
let hdrpModule = prewarmedHdrpModules.get(hdrpVariant.composedWgsl);
|
|
@@ -5335,6 +5684,49 @@ async function buildReadyWebGPU(
|
|
|
5335
5684
|
// uses so the lazy build's createShaderModule call hits OK on
|
|
5336
5685
|
// frame 1.
|
|
5337
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
|
+
}
|
|
5338
5730
|
}
|
|
5339
5731
|
|
|
5340
5732
|
// feat-20260621 M-A3 (D-5): register the built-in tonemap onto the unified
|
|
@@ -5354,27 +5746,62 @@ async function buildReadyWebGPU(
|
|
|
5354
5746
|
// forever. Mirror the shadow_caster prewarm: await-compile the tonemap module
|
|
5355
5747
|
// here and `seedShaderModule` it under the exact label
|
|
5356
5748
|
// `buildPostProcessPipeline` requests (`post-process-${id}-module`), so the
|
|
5357
|
-
// first `getPostProcessPipeline(
|
|
5749
|
+
// first `getPostProcessPipeline(STANDARD_OUTPUT_TRANSFORM_FEATURE_ID, …)` hits the module cache
|
|
5358
5750
|
// synchronously and builds the pipeline on frame 1.
|
|
5359
5751
|
const tonemapPrewarm = await runShimStep(
|
|
5360
5752
|
() =>
|
|
5361
5753
|
asyncCreateShaderModule
|
|
5362
5754
|
? asyncCreateShaderModule(rhiDevice, {
|
|
5363
5755
|
code: tonemapEntry.wgsl,
|
|
5364
|
-
label: `post-process-${
|
|
5756
|
+
label: `post-process-${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}-module`,
|
|
5365
5757
|
})
|
|
5366
5758
|
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5367
5759
|
code: tonemapEntry.wgsl,
|
|
5368
|
-
label: `post-process-${
|
|
5760
|
+
label: `post-process-${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}-module`,
|
|
5369
5761
|
}),
|
|
5370
5762
|
'shader-compile-failed',
|
|
5371
5763
|
'tonemap shader module compiled (unified post-process prewarm)',
|
|
5372
5764
|
'inspect manifest tonemap entry composed wgsl; check device.features',
|
|
5373
5765
|
);
|
|
5374
5766
|
if (!tonemapPrewarm.ok) throw tonemapPrewarm.error;
|
|
5375
|
-
seedShaderModule(
|
|
5767
|
+
seedShaderModule(
|
|
5768
|
+
`post-process-${STANDARD_OUTPUT_TRANSFORM_FEATURE_ID}-module`,
|
|
5769
|
+
tonemapPrewarm.value,
|
|
5770
|
+
);
|
|
5376
5771
|
registerBuiltinTonemap(tonemapEntry.wgsl);
|
|
5377
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
|
+
|
|
5378
5805
|
// feat-20260520-2d-sprite-layer-mvp M-3 / w24: sprite shader module
|
|
5379
5806
|
// is optional in the 3-tuple legacy manifest (back-compat for apps
|
|
5380
5807
|
// that locked their manifest URL before this feat). With the
|
|
@@ -5410,6 +5837,36 @@ async function buildReadyWebGPU(
|
|
|
5410
5837
|
// shares the generic per-MaterialShader lazy build path, which now
|
|
5411
5838
|
// needs the explicit seed to preserve frame-1 readiness.
|
|
5412
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
|
+
}
|
|
5413
5870
|
}
|
|
5414
5871
|
|
|
5415
5872
|
// feat-20260624-sprite-lit-shading-model-pure-2d-lighting M1' / t7:
|
|
@@ -5482,15 +5939,12 @@ async function buildReadyWebGPU(
|
|
|
5482
5939
|
skyboxModule = skyboxShaderResult.value;
|
|
5483
5940
|
}
|
|
5484
5941
|
|
|
5485
|
-
//
|
|
5486
|
-
//
|
|
5487
|
-
//
|
|
5488
|
-
//
|
|
5489
|
-
// (see triage above; struct names survive naga_oil composition). When
|
|
5490
|
-
// present, each module is compiled here; pipeline construction happens
|
|
5491
|
-
// 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.
|
|
5492
5946
|
if (bloomBrightEntry !== undefined) {
|
|
5493
|
-
const
|
|
5947
|
+
const result = await runShimStep(
|
|
5494
5948
|
() =>
|
|
5495
5949
|
asyncCreateShaderModule
|
|
5496
5950
|
? asyncCreateShaderModule(rhiDevice, {
|
|
@@ -5505,17 +5959,14 @@ async function buildReadyWebGPU(
|
|
|
5505
5959
|
'bloom-bright shader module compiled',
|
|
5506
5960
|
'inspect manifest bloom-bright entry composed wgsl; check device.features',
|
|
5507
5961
|
);
|
|
5508
|
-
if (!
|
|
5509
|
-
bloomBrightModule =
|
|
5962
|
+
if (!result.ok) throw result.error;
|
|
5963
|
+
bloomBrightModule = result.value;
|
|
5510
5964
|
}
|
|
5511
5965
|
if (bloomBlurEntry !== undefined) {
|
|
5512
|
-
const
|
|
5966
|
+
const result = await runShimStep(
|
|
5513
5967
|
() =>
|
|
5514
5968
|
asyncCreateShaderModule
|
|
5515
|
-
? asyncCreateShaderModule(rhiDevice, {
|
|
5516
|
-
code: bloomBlurEntry.wgsl,
|
|
5517
|
-
label: 'bloom-blur',
|
|
5518
|
-
})
|
|
5969
|
+
? asyncCreateShaderModule(rhiDevice, { code: bloomBlurEntry.wgsl, label: 'bloom-blur' })
|
|
5519
5970
|
: invokeDeviceCreateShaderModule(rhiDevice, {
|
|
5520
5971
|
code: bloomBlurEntry.wgsl,
|
|
5521
5972
|
label: 'bloom-blur',
|
|
@@ -5524,11 +5975,11 @@ async function buildReadyWebGPU(
|
|
|
5524
5975
|
'bloom-blur shader module compiled',
|
|
5525
5976
|
'inspect manifest bloom-blur entry composed wgsl; check device.features',
|
|
5526
5977
|
);
|
|
5527
|
-
if (!
|
|
5528
|
-
bloomBlurModule =
|
|
5978
|
+
if (!result.ok) throw result.error;
|
|
5979
|
+
bloomBlurModule = result.value;
|
|
5529
5980
|
}
|
|
5530
5981
|
if (bloomCompositeEntry !== undefined) {
|
|
5531
|
-
const
|
|
5982
|
+
const result = await runShimStep(
|
|
5532
5983
|
() =>
|
|
5533
5984
|
asyncCreateShaderModule
|
|
5534
5985
|
? asyncCreateShaderModule(rhiDevice, {
|
|
@@ -5543,8 +5994,8 @@ async function buildReadyWebGPU(
|
|
|
5543
5994
|
'bloom-composite shader module compiled',
|
|
5544
5995
|
'inspect manifest bloom-composite entry composed wgsl; check device.features',
|
|
5545
5996
|
);
|
|
5546
|
-
if (!
|
|
5547
|
-
bloomCompositeModule =
|
|
5997
|
+
if (!result.ok) throw result.error;
|
|
5998
|
+
bloomCompositeModule = result.value;
|
|
5548
5999
|
}
|
|
5549
6000
|
// feat-20260612-hdrp-ssao M6 / w27: SSAO shader module compilation.
|
|
5550
6001
|
// Optional manifest entry — absent on legacy manifests (zero-overhead
|
|
@@ -5576,11 +6027,11 @@ async function buildReadyWebGPU(
|
|
|
5576
6027
|
// The factory builds 4 BindGroupLayouts (view + material + mesh-array +
|
|
5577
6028
|
// instances) and the 4-slot pipeline layout in one call, throwing on each
|
|
5578
6029
|
// device.createBindGroupLayout / device.createPipelineLayout failure.
|
|
5579
|
-
// material BGL now carries
|
|
6030
|
+
// material BGL now carries 24 entries (material 0..16 + Skylight 17..23 via
|
|
5580
6031
|
// mergeSkylightIntoMaterialBgl) -- pipeline layout itself stays at 4 slots,
|
|
5581
6032
|
// no @group(4) allocated (D-5 round-4 fix for the round-2 maxBindGroups=4
|
|
5582
6033
|
// BLOCKER). The unlit pipeline currently shares this 14-entry layout
|
|
5583
|
-
// (carrying fallback identity at binding
|
|
6034
|
+
// (carrying fallback identity at binding 17..23 from the record stage);
|
|
5584
6035
|
// future split via `buildUnlitMaterialBgl` is exposed for tests.
|
|
5585
6036
|
//
|
|
5586
6037
|
// feat-20260520-directional-light-shadow-mapping merge: view BGL entries
|
|
@@ -5614,7 +6065,10 @@ async function buildReadyWebGPU(
|
|
|
5614
6065
|
// (URP layout) so the manifest entry's URP variant WGSL still builds a
|
|
5615
6066
|
// valid PSO instead of hard-disabling the entire HDRP-variant build path.
|
|
5616
6067
|
let hdrpPbrPipelineLayoutHandle: PipelineLayout | null = null;
|
|
5617
|
-
|
|
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) {
|
|
5618
6072
|
const hdrpUnifiedBglDesc = createHdrpBindGroupLayoutDescriptor(storageBufferCapable);
|
|
5619
6073
|
const hdrpUnifiedBglRes = rhiDevice.createBindGroupLayout(hdrpUnifiedBglDesc);
|
|
5620
6074
|
if (hdrpUnifiedBglRes.ok) {
|
|
@@ -6922,6 +7376,59 @@ async function buildReadyWebGPU(
|
|
|
6922
7376
|
let bloomBlurHParamsBufferHandle: Buffer | null = null;
|
|
6923
7377
|
let bloomBlurVParamsBufferHandle: Buffer | null = null;
|
|
6924
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
|
+
};
|
|
6925
7432
|
// feat-20260612-hdrp-ssao M6 / w26 + w43: SSAO pipeline handles (D-A).
|
|
6926
7433
|
// calc + blur RenderPipeline pair sharing a dedicated 6-entry BGL.
|
|
6927
7434
|
// Optional — null when manifest lacks hdrp-ssao entry.
|
|
@@ -7222,361 +7729,560 @@ async function buildReadyWebGPU(
|
|
|
7222
7729
|
// bloom-blur-v : same BGL, same module as blur-h, V-axis texelSize
|
|
7223
7730
|
// bloom-composite: 2-tex + sampler + UBO@3 BGL, rgba16float target
|
|
7224
7731
|
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
const
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
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
|
+
}
|
|
7245
7768
|
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
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,
|
|
7257
7820
|
},
|
|
7258
|
-
{
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7821
|
+
attachments: {
|
|
7822
|
+
colorFormats: [HDR_COLOR_ATTACHMENT_FORMAT],
|
|
7823
|
+
depthFormat: undefined,
|
|
7824
|
+
sampleCount: 1,
|
|
7262
7825
|
},
|
|
7263
|
-
{
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7826
|
+
geometry: {
|
|
7827
|
+
topology: 'triangle-list',
|
|
7828
|
+
stripIndexFormat: undefined,
|
|
7829
|
+
vertexLayout: {},
|
|
7267
7830
|
},
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
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
|
+
}
|
|
7288
7859
|
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
vertexLayout: {},
|
|
7306
|
-
},
|
|
7307
|
-
renderState: { cullMode: 'none' },
|
|
7308
|
-
};
|
|
7309
|
-
const modules = {
|
|
7310
|
-
vertex: bloomBrightModule,
|
|
7311
|
-
fragment: bloomBrightModule,
|
|
7312
|
-
layout: brightPlResult.value,
|
|
7313
|
-
};
|
|
7314
|
-
try {
|
|
7315
|
-
bloomBrightPipelineHandle = getOrBuildPipeline(
|
|
7316
|
-
brightSpec,
|
|
7317
|
-
pipelineDeviceProvider,
|
|
7318
|
-
pipelineCache,
|
|
7319
|
-
modules,
|
|
7320
|
-
) as RenderPipeline;
|
|
7321
|
-
} catch (err) {
|
|
7322
|
-
if (err instanceof PipelineSpecError) throw err;
|
|
7323
|
-
throw new PipelineSpecError({
|
|
7324
|
-
code: 'pipeline-build-failed',
|
|
7325
|
-
detail: { cause: err },
|
|
7326
|
-
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);
|
|
7327
7876
|
});
|
|
7328
7877
|
}
|
|
7329
|
-
}
|
|
7330
|
-
|
|
7331
|
-
// Bright params UBO: 16 B std140 (threshold f32 + 12 B pad).
|
|
7332
|
-
const brightParamsResult = runShimSyncStep(
|
|
7333
|
-
() =>
|
|
7334
|
-
rhiDevice.createBuffer({
|
|
7335
|
-
label: 'bloom-bright-params-ubo',
|
|
7336
|
-
size: BRIGHT_PARAMS_BYTES,
|
|
7337
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
7338
|
-
mappedAtCreation: false,
|
|
7339
|
-
}),
|
|
7340
|
-
'webgpu-runtime-error',
|
|
7341
|
-
'createBuffer (bloom-bright params UBO) succeeded',
|
|
7342
|
-
'check device.limits.maxUniformBufferBindingSize',
|
|
7343
|
-
);
|
|
7344
|
-
if (!brightParamsResult.ok) throw brightParamsResult.error;
|
|
7345
|
-
bloomBrightParamsBufferHandle = brightParamsResult.value;
|
|
7346
|
-
}
|
|
7347
7878
|
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
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,
|
|
7360
7934
|
},
|
|
7361
|
-
{
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7935
|
+
attachments: {
|
|
7936
|
+
colorFormats: [HDR_COLOR_ATTACHMENT_FORMAT],
|
|
7937
|
+
depthFormat: undefined,
|
|
7938
|
+
sampleCount: 1,
|
|
7365
7939
|
},
|
|
7366
|
-
{
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7940
|
+
geometry: {
|
|
7941
|
+
topology: 'triangle-list',
|
|
7942
|
+
stripIndexFormat: undefined,
|
|
7943
|
+
vertexLayout: {},
|
|
7370
7944
|
},
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
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
|
+
}
|
|
7379
7976
|
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
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
|
+
});
|
|
7391
7996
|
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
},
|
|
7408
|
-
geometry: {
|
|
7409
|
-
topology: 'triangle-list',
|
|
7410
|
-
stripIndexFormat: undefined,
|
|
7411
|
-
vertexLayout: {},
|
|
7412
|
-
},
|
|
7413
|
-
renderState: { cullMode: 'none' },
|
|
7414
|
-
};
|
|
7415
|
-
const modules = {
|
|
7416
|
-
vertex: bloomBlurModule,
|
|
7417
|
-
fragment: bloomBlurModule,
|
|
7418
|
-
layout: blurPlResult.value,
|
|
7419
|
-
label: 'bloom-blur-h-pipeline',
|
|
7420
|
-
};
|
|
7421
|
-
try {
|
|
7422
|
-
bloomBlurHPipelineHandle = getOrBuildPipeline(
|
|
7423
|
-
blurSpec,
|
|
7424
|
-
pipelineDeviceProvider,
|
|
7425
|
-
pipelineCache,
|
|
7426
|
-
modules,
|
|
7427
|
-
) as RenderPipeline;
|
|
7428
|
-
} catch (err) {
|
|
7429
|
-
if (err instanceof PipelineSpecError) throw err;
|
|
7430
|
-
throw new PipelineSpecError({
|
|
7431
|
-
code: 'pipeline-build-failed',
|
|
7432
|
-
detail: { cause: err },
|
|
7433
|
-
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);
|
|
7434
8012
|
});
|
|
7435
8013
|
}
|
|
7436
|
-
// V is cache-hit on the same spec (identical PSO; per-axis UBO differentiates at record time).
|
|
7437
|
-
bloomBlurVPipelineHandle = bloomBlurHPipelineHandle;
|
|
7438
|
-
}
|
|
7439
|
-
|
|
7440
|
-
// Blur params UBOs: 16 B std140 (texelSize.xy + radius + pad) each.
|
|
7441
|
-
// bug-20260625: one per axis -- H and V must not share a buffer (the
|
|
7442
|
-
// shared-buffer writeBuffer race made both passes blur vertically).
|
|
7443
|
-
const blurHParamsResult = runShimSyncStep(
|
|
7444
|
-
() =>
|
|
7445
|
-
rhiDevice.createBuffer({
|
|
7446
|
-
label: 'bloom-blur-h-params-ubo',
|
|
7447
|
-
size: BLUR_PARAMS_BYTES,
|
|
7448
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
7449
|
-
mappedAtCreation: false,
|
|
7450
|
-
}),
|
|
7451
|
-
'webgpu-runtime-error',
|
|
7452
|
-
'createBuffer (bloom-blur-h params UBO) succeeded',
|
|
7453
|
-
'check device.limits.maxUniformBufferBindingSize',
|
|
7454
|
-
);
|
|
7455
|
-
if (!blurHParamsResult.ok) throw blurHParamsResult.error;
|
|
7456
|
-
bloomBlurHParamsBufferHandle = blurHParamsResult.value;
|
|
7457
8014
|
|
|
7458
|
-
|
|
7459
|
-
()
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
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,
|
|
7490
8071
|
},
|
|
7491
|
-
{
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
8072
|
+
attachments: {
|
|
8073
|
+
colorFormats: [HDR_COLOR_ATTACHMENT_FORMAT],
|
|
8074
|
+
depthFormat: undefined,
|
|
8075
|
+
sampleCount: 1,
|
|
7495
8076
|
},
|
|
7496
|
-
{
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
8077
|
+
geometry: {
|
|
8078
|
+
topology: 'triangle-list',
|
|
8079
|
+
stripIndexFormat: undefined,
|
|
8080
|
+
vertexLayout: {},
|
|
7500
8081
|
},
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
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
|
+
}
|
|
7509
8110
|
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
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
|
+
}
|
|
7521
8133
|
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
stripIndexFormat: undefined,
|
|
7538
|
-
vertexLayout: {},
|
|
7539
|
-
},
|
|
7540
|
-
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,
|
|
7541
8149
|
};
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
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
|
+
};
|
|
7546
8198
|
};
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
if (
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
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;
|
|
7562
8284
|
}
|
|
7563
|
-
|
|
7564
|
-
// Composite params UBO: 16 B std140 (intensity f32 + 12 B pad).
|
|
7565
|
-
const compositeParamsResult = runShimSyncStep(
|
|
7566
|
-
() =>
|
|
7567
|
-
rhiDevice.createBuffer({
|
|
7568
|
-
label: 'bloom-composite-params-ubo',
|
|
7569
|
-
size: COMPOSITE_PARAMS_BYTES,
|
|
7570
|
-
usage: GPU_BUFFER_USAGE_UNIFORM | GPU_BUFFER_USAGE_COPY_DST,
|
|
7571
|
-
mappedAtCreation: false,
|
|
7572
|
-
}),
|
|
7573
|
-
'webgpu-runtime-error',
|
|
7574
|
-
'createBuffer (bloom-composite params UBO) succeeded',
|
|
7575
|
-
'check device.limits.maxUniformBufferBindingSize',
|
|
7576
|
-
);
|
|
7577
|
-
if (!compositeParamsResult.ok) throw compositeParamsResult.error;
|
|
7578
|
-
bloomCompositeParamsBufferHandle = compositeParamsResult.value;
|
|
7579
|
-
}
|
|
8285
|
+
};
|
|
7580
8286
|
|
|
7581
8287
|
// ── feat-20260612-hdrp-ssao M6 / w26 + w43 + M8 / w37 ───────────────────
|
|
7582
8288
|
//
|
|
@@ -7798,7 +8504,7 @@ async function buildReadyWebGPU(
|
|
|
7798
8504
|
if (!prewarmRes.ok) throw prewarmRes.error;
|
|
7799
8505
|
}
|
|
7800
8506
|
|
|
7801
|
-
|
|
8507
|
+
const pipelineState: PipelineState = {
|
|
7802
8508
|
// feat-20260518-pbr-direct-lighting-mvp M5 / w22.10 (AC-06 dual->triple
|
|
7803
8509
|
// pipeline + D-2 + D-10): three distinct GPU render-pipeline handles
|
|
7804
8510
|
// backed by 2 distinct shader modules (unlit + pbr) x 2 vertex stride
|
|
@@ -7881,6 +8587,7 @@ async function buildReadyWebGPU(
|
|
|
7881
8587
|
meshes: meshHandles,
|
|
7882
8588
|
format: swapChainFormats.storage,
|
|
7883
8589
|
colorAttachmentFormat: swapChainFormats.view,
|
|
8590
|
+
surfaceProfile: surfaceViewFormats ? 'dual-view' : 'raw-only',
|
|
7884
8591
|
viewBindGroupLayout: viewBglResult.value,
|
|
7885
8592
|
materialBindGroupLayout: materialBglResult.value,
|
|
7886
8593
|
meshBindGroupLayout: meshArrayBglResult.value,
|
|
@@ -7990,18 +8697,28 @@ async function buildReadyWebGPU(
|
|
|
7990
8697
|
// (ensureLazyTexture, slot width/height tracking for size-drift rebuild).
|
|
7991
8698
|
// BindGroup caches survive until the intermediate view is invalidated
|
|
7992
8699
|
// by a resize (width/height drift forces null).
|
|
7993
|
-
bloomBrightPipeline:
|
|
7994
|
-
bloomBlurHPipeline:
|
|
7995
|
-
bloomBlurVPipeline:
|
|
7996
|
-
bloomCompositePipeline:
|
|
7997
|
-
bloomBrightBindGroupLayout:
|
|
7998
|
-
bloomBlurBindGroupLayout:
|
|
7999
|
-
bloomCompositeBindGroupLayout:
|
|
8000
|
-
bloomSampler:
|
|
8001
|
-
bloomBrightParamsBuffer:
|
|
8002
|
-
bloomBlurHParamsBuffer:
|
|
8003
|
-
bloomBlurVParamsBuffer:
|
|
8004
|
-
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,
|
|
8005
8722
|
bloomBrightTexture: null,
|
|
8006
8723
|
bloomBrightView: null,
|
|
8007
8724
|
bloomBrightWidth: 0,
|
|
@@ -8026,6 +8743,7 @@ async function buildReadyWebGPU(
|
|
|
8026
8743
|
ssaoFallbackRawView: null,
|
|
8027
8744
|
},
|
|
8028
8745
|
};
|
|
8746
|
+
return pipelineState;
|
|
8029
8747
|
}
|
|
8030
8748
|
|
|
8031
8749
|
/**
|
|
@@ -8050,8 +8768,8 @@ function ensureContextConfigured(
|
|
|
8050
8768
|
internals: WebGPURendererInternals,
|
|
8051
8769
|
state: PipelineState,
|
|
8052
8770
|
errorRegistry: RhiErrorListenerRegistry,
|
|
8053
|
-
): void {
|
|
8054
|
-
if (state.perPassResources.configured) return;
|
|
8771
|
+
): Result<void, RhiError> {
|
|
8772
|
+
if (state.perPassResources.configured) return ok(undefined);
|
|
8055
8773
|
// M6 / w41 (feat-20260510-rhi-resource-creation): configure goes through
|
|
8056
8774
|
// the forgeax `RhiCanvasContext` surface; the shim resolves the forgeax
|
|
8057
8775
|
// RhiDevice back to the underlying raw GPUDevice via RAW_DEVICE_MAP
|
|
@@ -8064,7 +8782,7 @@ function ensureContextConfigured(
|
|
|
8064
8782
|
const configuredDevice = internals.pack.instrumentation?.resolveSurfaceDevice?.(internals.device);
|
|
8065
8783
|
if (configuredDevice !== undefined && !configuredDevice.ok) {
|
|
8066
8784
|
errorRegistry.fire(configuredDevice.error);
|
|
8067
|
-
return;
|
|
8785
|
+
return err(configuredDevice.error);
|
|
8068
8786
|
}
|
|
8069
8787
|
const cfgResult = configureSurface(
|
|
8070
8788
|
internals.context,
|
|
@@ -8074,7 +8792,7 @@ function ensureContextConfigured(
|
|
|
8074
8792
|
);
|
|
8075
8793
|
if (!cfgResult.ok) {
|
|
8076
8794
|
errorRegistry.fire(cfgResult.error);
|
|
8077
|
-
return;
|
|
8795
|
+
return err(cfgResult.error);
|
|
8078
8796
|
}
|
|
8079
8797
|
state.perPassResources.configured = true;
|
|
8080
8798
|
// bug-20260612: write the actually-configured swap-chain storage format
|
|
@@ -8087,6 +8805,7 @@ function ensureContextConfigured(
|
|
|
8087
8805
|
// `${storage}-srgb` partner — derived, not duplicated.
|
|
8088
8806
|
(globalThis as { __forgeaxSwapChainFormat?: GPUTextureFormat }).__forgeaxSwapChainFormat =
|
|
8089
8807
|
state.format;
|
|
8808
|
+
return ok(undefined);
|
|
8090
8809
|
}
|
|
8091
8810
|
|
|
8092
8811
|
/**
|