@forgeax/engine-render 0.1.29 → 0.1.31
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 +248 -21
- package/dist/assembly/atmosphere-shader-prewarm.d.ts +1 -1
- package/dist/assembly/atmosphere-shader-prewarm.d.ts.map +1 -1
- package/dist/assembly/device-feature-admission.d.ts +4 -3
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/dynamic-geometry-host.d.ts +6 -0
- package/dist/assembly/dynamic-geometry-host.d.ts.map +1 -1
- package/dist/assembly/host-contract.d.ts +6 -0
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +26 -3
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/pipeline-helpers.d.ts +24 -0
- package/dist/assembly/material/pipeline-helpers.d.ts.map +1 -0
- package/dist/assembly/material-shader-policy.d.ts.map +1 -1
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -1
- package/dist/assembly/recovery/recovery-attempt.d.ts +1 -6
- package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready.d.ts +1 -1
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/capture/scene-projection.d.ts +41 -0
- package/dist/capture/scene-projection.d.ts.map +1 -0
- package/dist/{chunk-BQVQOQJH.mjs → chunk-7MQ3XBSP.mjs} +20 -14
- package/dist/chunk-7MQ3XBSP.mjs.map +1 -0
- package/dist/{chunk-S4DNKQYQ.mjs → chunk-C3DDSXYK.mjs} +86 -33
- package/dist/chunk-C3DDSXYK.mjs.map +1 -0
- package/dist/{chunk-BTWH7BP4.mjs → chunk-FUUO4UND.mjs} +73 -29
- package/dist/chunk-FUUO4UND.mjs.map +1 -0
- package/dist/{chunk-NRNXBLIJ.mjs → chunk-JRBDO2KB.mjs} +46 -6
- package/dist/chunk-JRBDO2KB.mjs.map +1 -0
- package/dist/{chunk-NICXSHAT.mjs → chunk-MDQJPY3T.mjs} +8 -6
- package/dist/chunk-MDQJPY3T.mjs.map +1 -0
- package/dist/{chunk-JQTSEI3Z.mjs → chunk-NO4WZCAH.mjs} +3 -3
- package/dist/chunk-NO4WZCAH.mjs.map +1 -0
- package/dist/{chunk-FUODTTIA.mjs → chunk-PSEQ6QPH.mjs} +621 -108
- package/dist/chunk-PSEQ6QPH.mjs.map +1 -0
- package/dist/{chunk-33XZFTQU.mjs → chunk-S5UE2T5P.mjs} +48 -6
- package/dist/chunk-S5UE2T5P.mjs.map +1 -0
- package/dist/components/directional-light.d.ts +2 -2
- package/dist/components/dynamic-resolution.d.ts +15 -0
- package/dist/components/dynamic-resolution.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/light-helpers.d.ts.map +1 -1
- package/dist/components/reflection-probe.d.ts +1 -0
- package/dist/components/reflection-probe.d.ts.map +1 -1
- package/dist/components/skylight.d.ts +4 -1
- package/dist/components/skylight.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +2172 -796
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts +2 -0
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +2 -2
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/dynamic-geometry.d.ts.map +1 -1
- package/dist/environment/background.d.ts +3 -2
- package/dist/environment/background.d.ts.map +1 -1
- package/dist/environment/ibl.d.ts +11 -0
- package/dist/environment/ibl.d.ts.map +1 -0
- package/dist/environment/storage.d.ts +14 -0
- package/dist/environment/storage.d.ts.map +1 -0
- package/dist/errors/gpu-driven.d.ts +2 -2
- package/dist/errors/gpu-driven.d.ts.map +1 -1
- package/dist/errors/render.d.ts +35 -1
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +7 -0
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/gpu-driven.d.ts +18 -1
- package/dist/extract/gpu-driven.d.ts.map +1 -1
- package/dist/gpu-driven/batch-topology.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts +24 -0
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -0
- package/dist/gpu-driven/material-bindings.d.ts +7 -0
- package/dist/gpu-driven/material-bindings.d.ts.map +1 -0
- package/dist/gpu-driven/prepared-draw.d.ts +2 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +27 -6
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-resource.d.ts +5 -2
- package/dist/gpu-resource.d.ts.map +1 -1
- package/dist/gpu-scene-schema.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/hdrp-buffers.d.ts +3 -0
- package/dist/hdrp-buffers.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +2 -0
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +3 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +83 -81
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +7 -5
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +16 -1
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/internal.mjs +6 -6
- package/dist/material-row.d.ts +10 -0
- package/dist/material-row.d.ts.map +1 -1
- package/dist/pbr-pipeline.d.ts +3 -1
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/render-extent.d.ts +51 -0
- package/dist/pipeline/render-extent.d.ts.map +1 -0
- package/dist/pipeline/standard-build-context.d.ts +7 -0
- package/dist/pipeline/standard-build-context.d.ts.map +1 -0
- package/dist/pipeline/standard-forward-lane.d.ts +3 -2
- package/dist/pipeline/standard-forward-lane.d.ts.map +1 -1
- package/dist/pipeline/standard-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +1 -1
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline/standard-profile.d.ts +2 -1
- package/dist/pipeline/standard-profile.d.ts.map +1 -1
- package/dist/pipeline-builder.d.ts +3 -1
- package/dist/pipeline-builder.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/post-process-errors.d.ts +9 -2
- package/dist/post-process-errors.d.ts.map +1 -1
- package/dist/prepare/extended-lighting/resources.d.ts +3 -3
- package/dist/record/frame-lighting.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +3 -8
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +3 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +7 -2
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +6 -3
- package/dist/record/main-pass-material.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/prepared-material-bindings.d.ts.map +1 -1
- package/dist/record/recovery-pipeline.d.ts +9 -1
- package/dist/record/recovery-pipeline.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +34 -3
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -2
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +5 -2
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/reflection/background.d.ts +14 -0
- package/dist/reflection/background.d.ts.map +1 -0
- package/dist/reflection/filter.d.ts.map +1 -1
- package/dist/reflection/projection.d.ts +1 -0
- package/dist/reflection/projection.d.ts.map +1 -1
- package/dist/reflection/record-owner.d.ts +4 -2
- package/dist/reflection/record-owner.d.ts.map +1 -1
- package/dist/render-contract.d.ts +47 -32
- 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 +17 -1
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +9 -2
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +29 -2
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +2 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/ssao-config.d.ts +8 -6
- package/dist/ssao-config.d.ts.map +1 -1
- package/dist/ssr/graph.d.ts.map +1 -1
- package/dist/temporal/gpu.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/standard-scene-data.d.ts +2 -1
- package/dist/temporal/standard-scene-data.d.ts.map +1 -1
- package/dist/temporal/target-coverage-attachment.d.ts +15 -0
- package/dist/temporal/target-coverage-attachment.d.ts.map +1 -0
- package/dist/temporal/temporal-view.d.ts +6 -2
- package/dist/temporal/temporal-view.d.ts.map +1 -1
- package/dist/temporal/view.d.ts +6 -0
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +6 -0
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +1 -0
- package/dist/volume/component.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/capture-scene-projection.unit.test.ts +24 -0
- package/src/__tests__/device-feature-admission.unit.test.ts +15 -24
- package/src/__tests__/device-loss-fanout.unit.test.ts +43 -0
- package/src/__tests__/dynamic-geometry-batch.unit.test.ts +157 -0
- package/src/__tests__/dynamic-geometry.unit.test.ts +36 -0
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +81 -0
- package/src/__tests__/factory-contract.integration.test.ts +164 -1
- package/src/__tests__/frame-observation.contract.test.ts +21 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +1 -1
- package/src/__tests__/gpu-driven-batch-topology-pbr.unit.test.ts +20 -1
- package/src/__tests__/gpu-driven-lod-cache.unit.test.ts +0 -3
- package/src/__tests__/gpu-driven-mesh-world-identity.unit.test.ts +0 -3
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +11 -6
- package/src/__tests__/gpu-driven-production.integration.test.ts +13 -25
- package/src/__tests__/gpu-driven-view.unit.test.ts +1 -0
- package/src/__tests__/gpu-pass-timing-browser-runner.ts +15 -0
- package/src/__tests__/gpu-pass-timing.browser.test.ts +14 -1
- package/src/__tests__/gpu-resource-scope.unit.test.ts +71 -0
- package/src/__tests__/gpu-scene.unit.test.ts +55 -1
- package/src/__tests__/materials-standard-contract.unit.test.ts +5 -1
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +8 -0
- package/src/__tests__/morph-neutral-admission.unit.test.ts +19 -0
- package/src/__tests__/physical-clearcoat.integration.test.ts +6 -1
- package/src/__tests__/post-process-error-policy-owner.unit.test.ts +6 -5
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +216 -5
- package/src/__tests__/reflection-fallback-async-publication.unit.test.ts +68 -1
- package/src/__tests__/reflection-probe-authoring.unit.test.ts +12 -10
- package/src/__tests__/reflection-probe-box.dawn.test.ts +71 -0
- package/src/__tests__/reflection-probe-filter.unit.test.ts +9 -0
- package/src/__tests__/reflection-probe-orientation.dawn.test.ts +294 -0
- package/src/__tests__/reflection-probe-record-owner.characterization.unit.test.ts +5 -15
- package/src/__tests__/reflection-probe-updates.unit.test.ts +129 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +3 -0
- package/src/__tests__/render-error-detail-ssot.test-d.ts +5 -1
- package/src/__tests__/render-extent.unit.test.ts +63 -0
- package/src/__tests__/render-scene-projection.unit.test.ts +3 -2
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +173 -9
- package/src/__tests__/shader-manifest-fixture.ts +6 -0
- package/src/__tests__/skinned-shadow-caster.test.ts +8 -1
- package/src/__tests__/specular-environment-scale.dawn.test.ts +4 -2
- package/src/__tests__/ssao-config.unit.test.ts +7 -0
- package/src/__tests__/ssao-surface.dawn.test.ts +252 -0
- package/src/__tests__/ssr-cube-silhouette.dawn.test.ts +270 -0
- package/src/__tests__/ssr-gpu-dispatch.browser.test.ts +51 -43
- package/src/__tests__/ssr-gpu-dispatch.dawn.test.ts +4 -0
- package/src/__tests__/ssr-gpu-dispatch.ts +10 -5
- package/src/__tests__/ssr-graph-topology.unit.test.ts +2 -2
- package/src/__tests__/ssr-hiz.browser.test.ts +1 -1
- package/src/__tests__/ssr-hiz.dawn.test.ts +1 -1
- package/src/__tests__/ssr-spatial.integration.test.ts +1 -1
- package/src/__tests__/standard-cluster-variant-resolution.unit.test.ts +40 -0
- package/src/__tests__/standard-pbr-artifact-assembly.unit.test.ts +238 -1
- package/src/__tests__/standard-profile-oracle.unit.test.ts +27 -0
- package/src/__tests__/taa-coverage-edge.dawn.test.ts +6 -0
- package/src/__tests__/taa-neighborhood-equivalence.dawn.test.ts +5 -1
- package/src/__tests__/taa-stability-feedback.ts +1 -0
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +2 -2
- package/src/__tests__/temporal-view.unit.test.ts +17 -0
- package/src/__tests__/transmission-prewarm.unit.test.ts +57 -0
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +62 -13
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +7 -7
- package/src/assembly/atmosphere-shader-prewarm.ts +3 -2
- package/src/assembly/device-feature-admission.ts +9 -4
- package/src/assembly/dynamic-geometry-host.ts +236 -163
- package/src/assembly/host-contract.ts +9 -0
- package/src/assembly/material/assembly.ts +350 -6
- package/src/assembly/material/pipeline-helpers.ts +100 -0
- package/src/assembly/material-shader-policy.ts +39 -18
- package/src/assembly/recovery/device-loss-fanout.ts +5 -0
- package/src/assembly/recovery/recovery-attempt.ts +2 -29
- package/src/assembly/recovery/renderer-recover.ts +6 -9
- package/src/assembly/renderer-facade.ts +22 -1
- package/src/assembly/shader-prewarm-policy.ts +14 -1
- package/src/assembly/webgpu-ready.ts +28 -19
- package/src/assembly/webgpu-renderer.ts +95 -95
- package/src/capture/scene-projection.ts +37 -0
- package/src/components/__tests__/dynamic-resolution-schema.unit.test.ts +65 -0
- package/src/components/__tests__/light-radius.integration.test.ts +59 -0
- package/src/components/directional-light.ts +3 -3
- package/src/components/dynamic-resolution.ts +67 -0
- package/src/components/index.ts +1 -0
- package/src/components/light-helpers.ts +6 -1
- package/src/components/reflection-probe.ts +1 -0
- package/src/components/skylight.ts +4 -1
- package/src/device/device-scope.ts +13 -7
- package/src/device/gpu-residency.ts +11 -11
- package/src/dynamic-geometry.ts +13 -8
- package/src/environment/background.ts +30 -12
- package/src/environment/ibl.ts +211 -0
- package/src/environment/storage.ts +63 -0
- package/src/errors/gpu-driven.ts +10 -3
- package/src/errors/render.ts +64 -0
- package/src/extract/camera.ts +12 -1
- package/src/extract/contracts.ts +10 -0
- package/src/extract/gpu-driven.ts +72 -17
- package/src/gpu-driven/batch-topology.ts +33 -11
- package/src/gpu-driven/material-artifacts.ts +179 -0
- package/src/gpu-driven/material-bindings.ts +41 -0
- package/src/gpu-driven/prepared-draw.ts +13 -2
- package/src/gpu-driven/production-raster.ts +681 -164
- package/src/gpu-resource.ts +21 -2
- package/src/gpu-scene-schema.ts +11 -2
- package/src/gpu-scene.ts +14 -3
- package/src/hdrp-buffers.ts +69 -0
- package/src/ibl/IblPipelineCache.ts +5 -0
- package/src/ibl/skylight-bind-group.ts +27 -17
- package/src/index.ts +92 -81
- package/src/inspection-types.ts +16 -1
- package/src/material-row.ts +60 -11
- package/src/pbr-pipeline.ts +13 -2
- package/src/pipeline/render-extent.ts +111 -0
- package/src/pipeline/standard-build-context.ts +7 -0
- package/src/pipeline/standard-forward-lane.ts +41 -24
- package/src/pipeline/standard-pipeline.ts +47 -18
- package/src/pipeline/standard-post.ts +4 -0
- package/src/pipeline/standard-profile.ts +2 -1
- package/src/pipeline-builder.ts +12 -3
- package/src/plugin.ts +4 -0
- package/src/post-process-errors.ts +26 -18
- package/src/prepare/__tests__/extended-lighting-capability.unit.test.ts +2 -2
- package/src/prepare/extended-lighting/resources.ts +3 -3
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +68 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +45 -0
- package/src/record/__tests__/material-slot-plan.unit.test.ts +9 -0
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +47 -1
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +7 -3
- package/src/record/__tests__/temporal-payload.unit.test.ts +2 -0
- package/src/record/frame-lighting.ts +3 -1
- package/src/record/frame-snapshot.ts +13 -11
- package/src/record/frame.ts +150 -74
- package/src/record/main-pass-geometry.ts +60 -9
- package/src/record/main-pass-material.ts +47 -19
- package/src/record/main-pass.ts +63 -4
- package/src/record/prepared-material-bindings.ts +4 -0
- package/src/record/recovery-pipeline.ts +56 -0
- package/src/record/render-context.ts +34 -3
- package/src/record/shadow-pass.ts +187 -50
- package/src/record/typed-frame-graph.ts +60 -3
- package/src/record/view-ubo.ts +1 -1
- package/src/recovery/render-system-candidate.ts +54 -11
- package/src/reflection/background.ts +151 -0
- package/src/reflection/filter.ts +3 -0
- package/src/reflection/projection.ts +16 -6
- package/src/reflection/record-owner.ts +282 -86
- package/src/render-contract.ts +50 -32
- package/src/render-graph-primitives.ts +28 -3
- package/src/render-pipeline.ts +20 -1
- package/src/render-system-extract-tail.ts +23 -3
- package/src/render-system-extract.ts +127 -42
- package/src/render-system.ts +35 -7
- package/src/scene/render-scene.ts +8 -0
- package/src/ssao-config.ts +25 -8
- package/src/ssr/graph.ts +78 -58
- package/src/temporal/gpu.ts +5 -0
- package/src/temporal/standard-scene-data.ts +7 -1
- package/src/temporal/target-coverage-attachment.ts +72 -0
- package/src/temporal/temporal-view.ts +10 -4
- package/src/temporal/view.ts +12 -2
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +24 -9
- package/src/typed-render-graph-primitives.ts +38 -0
- package/src/volume/component.ts +1 -0
- package/src/volume/passes.ts +3 -3
- package/dist/chunk-33XZFTQU.mjs.map +0 -1
- package/dist/chunk-BQVQOQJH.mjs.map +0 -1
- package/dist/chunk-BTWH7BP4.mjs.map +0 -1
- package/dist/chunk-FUODTTIA.mjs.map +0 -1
- package/dist/chunk-JQTSEI3Z.mjs.map +0 -1
- package/dist/chunk-NICXSHAT.mjs.map +0 -1
- package/dist/chunk-NRNXBLIJ.mjs.map +0 -1
- package/dist/chunk-S4DNKQYQ.mjs.map +0 -1
|
@@ -9,8 +9,13 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
createStandardPbrArtifactReceipt,
|
|
11
11
|
type MaterialShaderArtifact,
|
|
12
|
+
type MaterialShaderArtifactReceipt,
|
|
13
|
+
type MaterialShaderEntry,
|
|
14
|
+
type MaterialShaderManifestEntry,
|
|
15
|
+
type ShaderRegistry,
|
|
12
16
|
} from '@forgeax/engine-shader';
|
|
13
|
-
import type { StandardLayerPlan } from '@forgeax/engine-types';
|
|
17
|
+
import type { MaterialProgramAddress, StandardLayerPlan } from '@forgeax/engine-types';
|
|
18
|
+
import { resolveMaterialShaderUvSetCount } from '../material-shader-policy';
|
|
14
19
|
import { projectStandardLayerPlan } from './standard-layer-projection.js';
|
|
15
20
|
|
|
16
21
|
export type { MaterialRenderPassProjection } from '@forgeax/engine-assets-runtime';
|
|
@@ -36,28 +41,142 @@ export function assembleMaterialProjection(record: CookedMaterialRecord): Materi
|
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
/**
|
|
39
|
-
* Assemble the one built-in Standard PBR artifact from the
|
|
44
|
+
* Assemble the one built-in Standard PBR artifact from the registry-owned entry.
|
|
40
45
|
* Both rigid and skinned identifiers use this producer; unsupported identifiers
|
|
41
46
|
* stay outside the GPU-driven Standard PBR lane.
|
|
42
47
|
*/
|
|
43
48
|
export function assembleStandardPbrArtifact(
|
|
44
49
|
materialShaderId: string,
|
|
45
50
|
wgsl: string,
|
|
46
|
-
vertexColorAvailable
|
|
51
|
+
vertexColorAvailable?: boolean,
|
|
52
|
+
): MaterialShaderArtifact | undefined;
|
|
53
|
+
export function assembleStandardPbrArtifact(
|
|
54
|
+
materialShaderId: string,
|
|
55
|
+
shaderEntry: MaterialShaderEntry,
|
|
56
|
+
options?: MaterialShaderArtifactRequest,
|
|
57
|
+
): MaterialShaderArtifact | undefined;
|
|
58
|
+
export function assembleStandardPbrArtifact(
|
|
59
|
+
materialShaderId: string,
|
|
60
|
+
shaderEntryOrWgsl: MaterialShaderEntry | string,
|
|
61
|
+
optionsOrVertexColor: MaterialShaderArtifactRequest | boolean = {},
|
|
47
62
|
): MaterialShaderArtifact | undefined {
|
|
48
63
|
const skinned =
|
|
49
64
|
materialShaderId === 'forgeax::pbr-skin' ||
|
|
50
65
|
materialShaderId === 'forgeax::default-standard-pbr-skin';
|
|
51
66
|
if (materialShaderId !== 'forgeax::default-standard-pbr' && !skinned) return undefined;
|
|
52
67
|
|
|
53
|
-
const
|
|
68
|
+
const options =
|
|
69
|
+
typeof optionsOrVertexColor === 'boolean'
|
|
70
|
+
? { vertexColorAvailable: optionsOrVertexColor }
|
|
71
|
+
: optionsOrVertexColor;
|
|
72
|
+
const shaderEntry: MaterialShaderEntry =
|
|
73
|
+
typeof shaderEntryOrWgsl === 'string'
|
|
74
|
+
? {
|
|
75
|
+
source: shaderEntryOrWgsl,
|
|
76
|
+
paramSchema: [],
|
|
77
|
+
receipt: createStandardPbrArtifactReceipt(skinned, options.vertexColorAvailable ?? false),
|
|
78
|
+
}
|
|
79
|
+
: shaderEntryOrWgsl;
|
|
80
|
+
|
|
81
|
+
const publishedReceipt = shaderEntry.receipt;
|
|
82
|
+
// The legacy no-request call remains source-compatible for the built-in
|
|
83
|
+
// Standard lane. Once a geometry fact is requested, however, the receipt
|
|
84
|
+
// must come from the producer-selected artifact; synthesising a different
|
|
85
|
+
// color layout here would let source, pipeline, and vertex ABI diverge.
|
|
86
|
+
if (
|
|
87
|
+
options.vertexColorAvailable !== undefined &&
|
|
88
|
+
(publishedReceipt === undefined ||
|
|
89
|
+
receiptHasVertexColor(publishedReceipt) !== options.vertexColorAvailable)
|
|
90
|
+
) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const receipt = publishedReceipt ?? createStandardPbrArtifactReceipt(skinned);
|
|
94
|
+
return {
|
|
95
|
+
material: materialShaderId,
|
|
96
|
+
pass: options.pass ?? 'forward',
|
|
97
|
+
wgsl: shaderEntry.source,
|
|
98
|
+
layoutIdentity: receipt.reflection.layoutIdentity,
|
|
99
|
+
bindings: [],
|
|
100
|
+
deps: [],
|
|
101
|
+
...(options.variantSet === undefined ? {} : { variantSet: options.variantSet }),
|
|
102
|
+
...(options.address === undefined
|
|
103
|
+
? {}
|
|
104
|
+
: {
|
|
105
|
+
vertexEntry:
|
|
106
|
+
options.address === 'scene-index' ? receipt.sceneIndexEntry : receipt.directEntry,
|
|
107
|
+
}),
|
|
108
|
+
vertexInputs: receipt.vertexInputs.map(
|
|
109
|
+
(input): Readonly<Record<string, unknown>> => ({
|
|
110
|
+
semantic: input.semantic,
|
|
111
|
+
location: input.location,
|
|
112
|
+
format: input.format,
|
|
113
|
+
}),
|
|
114
|
+
),
|
|
115
|
+
receipt,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface MaterialShaderArtifactRequest {
|
|
120
|
+
/** Geometry-owned COLOR_0 fact used to select the matching published variant. */
|
|
121
|
+
readonly vertexColorAvailable?: boolean;
|
|
122
|
+
/** Deformation lane used to select a skin-compatible shadow variant. */
|
|
123
|
+
readonly deformation?: 'rigid' | 'skin';
|
|
124
|
+
readonly clustered?: boolean;
|
|
125
|
+
readonly reflectionFallback?: boolean;
|
|
126
|
+
readonly variantSet?: string;
|
|
127
|
+
/** Submission address requested by the consumer; GPU-driven uses scene-index. */
|
|
128
|
+
readonly address?: MaterialProgramAddress;
|
|
129
|
+
/** Concrete Pass whose entry metadata is being assembled. */
|
|
130
|
+
readonly pass?: 'forward' | 'shadow' | 'depth';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function receiptHasVertexColor(receipt: MaterialShaderArtifactReceipt): boolean {
|
|
134
|
+
return receipt.vertexInputs.some((input) => input.semantic === 'color');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function receiptUvSetCount(receipt: MaterialShaderArtifactReceipt): number {
|
|
138
|
+
let count = 0;
|
|
139
|
+
for (const input of receipt.vertexInputs) {
|
|
140
|
+
if (input.semantic === 'uv') count = Math.max(count, 1);
|
|
141
|
+
else if (/^uv([1-7])$/.test(input.semantic)) {
|
|
142
|
+
count = Math.max(count, Number(input.semantic.slice(2)) + 1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return count;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function assemblePublishedArtifact(
|
|
149
|
+
materialShaderId: string,
|
|
150
|
+
shaderEntry: MaterialShaderEntry,
|
|
151
|
+
options: MaterialShaderArtifactRequest,
|
|
152
|
+
reflectedUvSetCount?: number,
|
|
153
|
+
): MaterialShaderArtifact | undefined {
|
|
154
|
+
const receipt = shaderEntry.receipt;
|
|
155
|
+
if (receipt === undefined) return undefined;
|
|
156
|
+
if (
|
|
157
|
+
options.vertexColorAvailable !== undefined &&
|
|
158
|
+
receiptHasVertexColor(receipt) !== options.vertexColorAvailable
|
|
159
|
+
) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
54
162
|
return {
|
|
55
163
|
material: materialShaderId,
|
|
56
|
-
pass: 'forward',
|
|
57
|
-
wgsl,
|
|
164
|
+
pass: options.pass ?? 'forward',
|
|
165
|
+
wgsl: shaderEntry.source,
|
|
58
166
|
layoutIdentity: receipt.reflection.layoutIdentity,
|
|
59
167
|
bindings: [],
|
|
60
168
|
deps: [],
|
|
169
|
+
uvSetCount:
|
|
170
|
+
resolveMaterialShaderUvSetCount(shaderEntry.source, reflectedUvSetCount) ??
|
|
171
|
+
receiptUvSetCount(receipt),
|
|
172
|
+
...(options.variantSet === undefined ? {} : { variantSet: options.variantSet }),
|
|
173
|
+
...(options.address === undefined
|
|
174
|
+
? {}
|
|
175
|
+
: {
|
|
176
|
+
vertexEntry:
|
|
177
|
+
options.address === 'scene-index' ? receipt.sceneIndexEntry : receipt.directEntry,
|
|
178
|
+
}),
|
|
179
|
+
...(options.pass === 'shadow' ? { fragmentEntry: 'fs_shadow' } : {}),
|
|
61
180
|
vertexInputs: receipt.vertexInputs.map(
|
|
62
181
|
(input): Readonly<Record<string, unknown>> => ({
|
|
63
182
|
semantic: input.semantic,
|
|
@@ -68,3 +187,228 @@ export function assembleStandardPbrArtifact(
|
|
|
68
187
|
receipt,
|
|
69
188
|
};
|
|
70
189
|
}
|
|
190
|
+
|
|
191
|
+
function selectedMaterialShaderEntry(
|
|
192
|
+
entry: Pick<MaterialShaderEntry, 'paramSchema'>,
|
|
193
|
+
source: string,
|
|
194
|
+
receipt: MaterialShaderArtifactReceipt | undefined,
|
|
195
|
+
): MaterialShaderEntry {
|
|
196
|
+
return {
|
|
197
|
+
source,
|
|
198
|
+
paramSchema: entry.paramSchema,
|
|
199
|
+
...(receipt === undefined ? {} : { receipt }),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function selectPublishedVariant(
|
|
204
|
+
entry: {
|
|
205
|
+
readonly variants: readonly {
|
|
206
|
+
readonly definesKey: string;
|
|
207
|
+
readonly defines: Readonly<Record<string, boolean>>;
|
|
208
|
+
readonly composedWgsl: string;
|
|
209
|
+
readonly receipt?: MaterialShaderArtifactReceipt;
|
|
210
|
+
}[];
|
|
211
|
+
},
|
|
212
|
+
options: MaterialShaderArtifactRequest,
|
|
213
|
+
): (typeof entry.variants)[number] | undefined {
|
|
214
|
+
const desiredColor = options.vertexColorAvailable;
|
|
215
|
+
const declaresColorAxis = entry.variants.some((variant) =>
|
|
216
|
+
Object.hasOwn(variant.defines, 'VERTEX_COLOR_AVAILABLE'),
|
|
217
|
+
);
|
|
218
|
+
let withColor =
|
|
219
|
+
desiredColor === undefined || !declaresColorAxis
|
|
220
|
+
? [...entry.variants]
|
|
221
|
+
: entry.variants.filter((variant) => variant.defines.VERTEX_COLOR_AVAILABLE === desiredColor);
|
|
222
|
+
const declaresSkinningAxis = entry.variants.some((variant) =>
|
|
223
|
+
Object.hasOwn(variant.defines, 'SKINNING_DISABLED'),
|
|
224
|
+
);
|
|
225
|
+
if (options.deformation !== undefined && declaresSkinningAxis) {
|
|
226
|
+
const skinningDisabled = options.deformation !== 'skin';
|
|
227
|
+
withColor = withColor.filter(
|
|
228
|
+
(variant) => variant.defines.SKINNING_DISABLED === skinningDisabled,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
for (const [axis, requested] of [
|
|
232
|
+
['CLUSTER_FORWARD_AVAILABLE', options.clustered],
|
|
233
|
+
['REFLECTION_FALLBACK_AVAILABLE', options.reflectionFallback],
|
|
234
|
+
] as const) {
|
|
235
|
+
if (
|
|
236
|
+
requested !== undefined &&
|
|
237
|
+
entry.variants.some((variant) => Object.hasOwn(variant.defines, axis))
|
|
238
|
+
) {
|
|
239
|
+
withColor = withColor.filter((variant) => variant.defines[axis] === requested);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (options.variantSet === undefined) {
|
|
243
|
+
// Coverage-only is an explicit temporal producer variant. Ordinary
|
|
244
|
+
// material publication must stay on the color-producing shader.
|
|
245
|
+
withColor = withColor.filter((variant) => variant.defines.COVERAGE_ONLY !== true);
|
|
246
|
+
}
|
|
247
|
+
if (options.variantSet !== undefined) {
|
|
248
|
+
return withColor.find((variant) => variant.definesKey === options.variantSet);
|
|
249
|
+
}
|
|
250
|
+
// GPU-driven Standard owns the storage-backed scene-index variant. Prefer that exact family, then fall back to the closest variant
|
|
251
|
+
// carrying the requested COLOR_0 fact. This keeps the selection deterministic
|
|
252
|
+
// without making capability names an admission whitelist.
|
|
253
|
+
const ranked = withColor
|
|
254
|
+
.map((variant) => ({
|
|
255
|
+
variant,
|
|
256
|
+
score:
|
|
257
|
+
(variant.defines.STORAGE_BUFFER_AVAILABLE === true ? 8 : 0) +
|
|
258
|
+
(variant.defines.GPU_DRIVEN_SCENE_INDEX_AVAILABLE === true ? 4 : 0) +
|
|
259
|
+
(variant.defines.CLUSTER_FORWARD_AVAILABLE === false ? 2 : 0) +
|
|
260
|
+
(variant.defines.PROBE_BLEND_AVAILABLE === false ? 1 : 0) +
|
|
261
|
+
(variant.defines.TRANSMISSION_AVAILABLE === false ? 1 : 0),
|
|
262
|
+
}))
|
|
263
|
+
.sort(
|
|
264
|
+
(left, right) =>
|
|
265
|
+
right.score - left.score || left.variant.definesKey.localeCompare(right.variant.definesKey),
|
|
266
|
+
);
|
|
267
|
+
return ranked[0]?.variant;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function materialShaderLookupIds(materialShaderId: string): readonly string[] {
|
|
271
|
+
return materialShaderId === 'forgeax::pbr-skin'
|
|
272
|
+
? ['forgeax::pbr-skin', 'forgeax::default-standard-pbr-skin']
|
|
273
|
+
: materialShaderId === 'forgeax::default-standard-pbr-skin'
|
|
274
|
+
? ['forgeax::default-standard-pbr-skin', 'forgeax::pbr-skin']
|
|
275
|
+
: [materialShaderId];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const STANDARD_SHADOW_CASTER_ID = 'forgeax::default-shadow-caster';
|
|
279
|
+
|
|
280
|
+
function isBuiltinStandardPbrMaterialShader(materialShaderId: string): boolean {
|
|
281
|
+
return (
|
|
282
|
+
materialShaderId === 'forgeax::default-standard-pbr' ||
|
|
283
|
+
materialShaderId === 'forgeax::default-standard-pbr-skin' ||
|
|
284
|
+
materialShaderId === 'forgeax::pbr-skin'
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Resolve the registry-owned Standard PBR artifact, including the historical skin alias. */
|
|
289
|
+
export function resolveStandardPbrArtifact(
|
|
290
|
+
materialShaderId: string,
|
|
291
|
+
shader: Pick<ShaderRegistry, 'findMaterialArtifact'> &
|
|
292
|
+
Partial<Pick<ShaderRegistry, 'materialShaderManifestEntries'>>,
|
|
293
|
+
options: MaterialShaderArtifactRequest = {},
|
|
294
|
+
): MaterialShaderArtifact | undefined {
|
|
295
|
+
const lookupIds = materialShaderLookupIds(materialShaderId);
|
|
296
|
+
for (const lookupId of lookupIds) {
|
|
297
|
+
const lookup = shader.findMaterialArtifact(lookupId);
|
|
298
|
+
if (!lookup.ok) continue;
|
|
299
|
+
const manifestEntry =
|
|
300
|
+
shader.materialShaderManifestEntries === undefined
|
|
301
|
+
? undefined
|
|
302
|
+
: Array.from(shader.materialShaderManifestEntries()).find(
|
|
303
|
+
(entry) => entry.identifier === lookupId,
|
|
304
|
+
);
|
|
305
|
+
const selected =
|
|
306
|
+
manifestEntry === undefined || manifestEntry.variants.length === 0
|
|
307
|
+
? undefined
|
|
308
|
+
: selectPublishedVariant(manifestEntry, options);
|
|
309
|
+
const selectedEntry =
|
|
310
|
+
selected === undefined
|
|
311
|
+
? lookup.value
|
|
312
|
+
: selectedMaterialShaderEntry(lookup.value, selected.composedWgsl, selected.receipt);
|
|
313
|
+
const artifact = assembleStandardPbrArtifact(materialShaderId, selectedEntry, options);
|
|
314
|
+
if (artifact === undefined) return undefined;
|
|
315
|
+
// A variant source without producer receipt is not safe to admit on a
|
|
316
|
+
// geometry-specific path. The assembly helper keeps its legacy fallback
|
|
317
|
+
// only for callers that did not provide a geometry fact.
|
|
318
|
+
if (
|
|
319
|
+
options.vertexColorAvailable !== undefined &&
|
|
320
|
+
artifact.receipt !== undefined &&
|
|
321
|
+
receiptHasVertexColor(artifact.receipt) !== options.vertexColorAvailable
|
|
322
|
+
) {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
return selected === undefined ? artifact : { ...artifact, variantSet: selected.definesKey };
|
|
326
|
+
}
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Registry entries and manifest publications are immutable. Replace the projection
|
|
331
|
+
// when either owner identity changes; old registrations remain weakly held.
|
|
332
|
+
const publishedArtifacts = new WeakMap<
|
|
333
|
+
MaterialShaderEntry,
|
|
334
|
+
{
|
|
335
|
+
readonly manifest: MaterialShaderManifestEntry | undefined;
|
|
336
|
+
readonly requests: Map<string, MaterialShaderArtifact>;
|
|
337
|
+
}
|
|
338
|
+
>();
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Resolve any producer-published material artifact through the same manifest
|
|
342
|
+
* variant relation as Standard. Unlike the Standard compatibility helper this
|
|
343
|
+
* never fabricates a receipt: a custom shader without producer ABI remains
|
|
344
|
+
* outside the GPU-driven lane with a structured preparation error.
|
|
345
|
+
*/
|
|
346
|
+
export function resolveMaterialShaderArtifact(
|
|
347
|
+
materialShaderId: string,
|
|
348
|
+
shader: Pick<ShaderRegistry, 'findMaterialArtifact'> &
|
|
349
|
+
Partial<Pick<ShaderRegistry, 'materialShaderManifestEntries'>>,
|
|
350
|
+
options: MaterialShaderArtifactRequest = {},
|
|
351
|
+
): MaterialShaderArtifact | undefined {
|
|
352
|
+
const lookupIds =
|
|
353
|
+
options.pass === 'shadow' && isBuiltinStandardPbrMaterialShader(materialShaderId)
|
|
354
|
+
? [STANDARD_SHADOW_CASTER_ID]
|
|
355
|
+
: materialShaderLookupIds(materialShaderId);
|
|
356
|
+
for (const lookupId of lookupIds) {
|
|
357
|
+
const lookup = shader.findMaterialArtifact(lookupId);
|
|
358
|
+
if (!lookup.ok) continue;
|
|
359
|
+
const manifestEntry =
|
|
360
|
+
shader.materialShaderManifestEntries === undefined
|
|
361
|
+
? undefined
|
|
362
|
+
: Array.from(shader.materialShaderManifestEntries()).find(
|
|
363
|
+
(entry) => entry.identifier === lookupId,
|
|
364
|
+
);
|
|
365
|
+
let cache = publishedArtifacts.get(lookup.value);
|
|
366
|
+
if (cache === undefined || cache.manifest !== manifestEntry) {
|
|
367
|
+
cache = { manifest: manifestEntry, requests: new Map() };
|
|
368
|
+
publishedArtifacts.set(lookup.value, cache);
|
|
369
|
+
}
|
|
370
|
+
const requestKey = JSON.stringify([
|
|
371
|
+
materialShaderId,
|
|
372
|
+
options.vertexColorAvailable,
|
|
373
|
+
options.deformation,
|
|
374
|
+
options.clustered,
|
|
375
|
+
options.reflectionFallback,
|
|
376
|
+
options.variantSet,
|
|
377
|
+
options.address,
|
|
378
|
+
options.pass,
|
|
379
|
+
]);
|
|
380
|
+
const cached = cache.requests.get(requestKey);
|
|
381
|
+
if (cached !== undefined) return cached;
|
|
382
|
+
const selected =
|
|
383
|
+
manifestEntry === undefined || manifestEntry.variants.length === 0
|
|
384
|
+
? undefined
|
|
385
|
+
: selectPublishedVariant(manifestEntry, options);
|
|
386
|
+
// A published variant set is an explicit producer contract. Falling back
|
|
387
|
+
// to the primary entry when the requested axes have no variant would pair
|
|
388
|
+
// one source with another artifact's receipt.
|
|
389
|
+
if (
|
|
390
|
+
manifestEntry !== undefined &&
|
|
391
|
+
manifestEntry.variants.length > 0 &&
|
|
392
|
+
selected === undefined
|
|
393
|
+
) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
const selectedEntry =
|
|
397
|
+
selected === undefined
|
|
398
|
+
? lookup.value
|
|
399
|
+
: selectedMaterialShaderEntry(lookup.value, selected.composedWgsl, selected.receipt);
|
|
400
|
+
const artifact = assemblePublishedArtifact(
|
|
401
|
+
lookupId === STANDARD_SHADOW_CASTER_ID ? lookupId : materialShaderId,
|
|
402
|
+
selectedEntry,
|
|
403
|
+
options,
|
|
404
|
+
manifestEntry?.uvSetCount,
|
|
405
|
+
);
|
|
406
|
+
if (artifact !== undefined) {
|
|
407
|
+
const published =
|
|
408
|
+
selected === undefined ? artifact : { ...artifact, variantSet: selected.definesKey };
|
|
409
|
+
cache.requests.set(requestKey, published);
|
|
410
|
+
return published;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { BindGroupLayout } from '@forgeax/engine-rhi';
|
|
2
|
+
import type { MaterialShaderArtifact, ShaderRegistry } from '@forgeax/engine-shader';
|
|
3
|
+
import { type GpuDrivenPbrProgram, resolveStandardPbrProgram } from '../../gpu-driven/pbr-program';
|
|
4
|
+
import type { LayoutKind } from '../material-shader-policy';
|
|
5
|
+
import { type MaterialShaderArtifactRequest, resolveMaterialShaderArtifact } from './assembly';
|
|
6
|
+
|
|
7
|
+
type MaterialArtifactRegistry = Pick<ShaderRegistry, 'findMaterialArtifact'> &
|
|
8
|
+
Partial<Pick<ShaderRegistry, 'materialShaderManifestEntries'>>;
|
|
9
|
+
|
|
10
|
+
export function resolveRendererMaterialShaderArtifact(
|
|
11
|
+
materialShaderId: string,
|
|
12
|
+
shader: MaterialArtifactRegistry,
|
|
13
|
+
programs: ReadonlyMap<string, GpuDrivenPbrProgram> | undefined,
|
|
14
|
+
options: MaterialShaderArtifactRequest | boolean = {},
|
|
15
|
+
): MaterialShaderArtifact | undefined {
|
|
16
|
+
if (typeof options === 'boolean') {
|
|
17
|
+
return resolveStandardPbrProgram(programs, materialShaderId, options)?.artifact;
|
|
18
|
+
}
|
|
19
|
+
const resolved = resolveMaterialShaderArtifact(materialShaderId, shader, options);
|
|
20
|
+
if (resolved !== undefined) return resolved;
|
|
21
|
+
// Legacy callers without geometry facts may reuse a prewarmed Standard
|
|
22
|
+
// program; geometry-specific requests remain fail-closed.
|
|
23
|
+
return Object.keys(options).length === 0
|
|
24
|
+
? resolveStandardPbrProgram(programs, materialShaderId, false)?.artifact
|
|
25
|
+
: undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resolveMaterialPipelineShaderId(
|
|
29
|
+
materialShaderId: string,
|
|
30
|
+
passKind: string,
|
|
31
|
+
): string {
|
|
32
|
+
return passKind === 'shadow-caster' &&
|
|
33
|
+
(materialShaderId === 'forgeax::default-standard-pbr' ||
|
|
34
|
+
materialShaderId === 'forgeax::default-standard-pbr-skin' ||
|
|
35
|
+
materialShaderId === 'forgeax::pbr-skin')
|
|
36
|
+
? 'forgeax::default-shadow-caster'
|
|
37
|
+
: materialShaderId;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function resolveMaterialPipelineVertexEntry(
|
|
41
|
+
layoutKind: LayoutKind | undefined,
|
|
42
|
+
vertexEntryPoint: string | undefined,
|
|
43
|
+
authoredVertexEntry: string | undefined,
|
|
44
|
+
): string | undefined {
|
|
45
|
+
const sceneIndex = layoutKind === 'gpu-driven-pbr' || layoutKind === 'gpu-driven-skin';
|
|
46
|
+
// Scene-index is a renderer submission ABI. An authored/direct entry may
|
|
47
|
+
// accompany the artifact for diagnostics, but it must not replace the
|
|
48
|
+
// receipt entry when the GPU-driven layout is selected.
|
|
49
|
+
return sceneIndex
|
|
50
|
+
? (vertexEntryPoint ?? authoredVertexEntry)
|
|
51
|
+
: (authoredVertexEntry ?? vertexEntryPoint);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface MaterialPipelineBindGroupState {
|
|
55
|
+
readonly meshBindGroupLayout: BindGroupLayout;
|
|
56
|
+
readonly pbrSkinMeshBindGroupLayout: BindGroupLayout | null;
|
|
57
|
+
readonly hdrpSkinMeshBindGroupLayout: BindGroupLayout | null;
|
|
58
|
+
readonly hdrpMeshBindGroupLayout?: BindGroupLayout | null;
|
|
59
|
+
readonly instancesBindGroupLayout: BindGroupLayout;
|
|
60
|
+
readonly gpuDrivenInstancesBindGroupLayout?: BindGroupLayout | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function resolveMaterialPipelineBindGroups(
|
|
64
|
+
layoutKind: LayoutKind,
|
|
65
|
+
state: MaterialPipelineBindGroupState,
|
|
66
|
+
clustered = false,
|
|
67
|
+
): { readonly meshLayout: BindGroupLayout; readonly instancesLayout: BindGroupLayout } | null {
|
|
68
|
+
const meshLayout =
|
|
69
|
+
clustered && layoutKind === 'gpu-driven-pbr'
|
|
70
|
+
? state.hdrpMeshBindGroupLayout
|
|
71
|
+
: clustered && layoutKind === 'gpu-driven-skin'
|
|
72
|
+
? state.hdrpSkinMeshBindGroupLayout
|
|
73
|
+
: layoutKind === 'pbr-skin' || layoutKind === 'gpu-driven-skin'
|
|
74
|
+
? state.pbrSkinMeshBindGroupLayout
|
|
75
|
+
: layoutKind === 'hdrp-skin'
|
|
76
|
+
? state.hdrpSkinMeshBindGroupLayout
|
|
77
|
+
: state.meshBindGroupLayout;
|
|
78
|
+
if (meshLayout == null) return null;
|
|
79
|
+
const instancesLayout =
|
|
80
|
+
layoutKind === 'gpu-driven-pbr' || layoutKind === 'gpu-driven-skin'
|
|
81
|
+
? state.gpuDrivenInstancesBindGroupLayout
|
|
82
|
+
: state.instancesBindGroupLayout;
|
|
83
|
+
return instancesLayout === null || instancesLayout === undefined
|
|
84
|
+
? null
|
|
85
|
+
: { meshLayout, instancesLayout };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function isEngineOwnedMaterialShader(materialShaderId: string): boolean {
|
|
89
|
+
return (
|
|
90
|
+
materialShaderId === 'forgeax::default-unlit' ||
|
|
91
|
+
materialShaderId === 'forgeax::default-shadow-caster' ||
|
|
92
|
+
materialShaderId === 'forgeax::sprite' ||
|
|
93
|
+
materialShaderId === 'forgeax::sprite-lit' ||
|
|
94
|
+
materialShaderId === 'forgeax::default-sprite' ||
|
|
95
|
+
materialShaderId === 'forgeax::msdf-text' ||
|
|
96
|
+
materialShaderId === 'forgeax::default-standard-pbr' ||
|
|
97
|
+
materialShaderId === 'forgeax::pbr-skin' ||
|
|
98
|
+
materialShaderId === 'forgeax::default-standard-pbr-skin'
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -280,8 +280,7 @@ export async function prepareMaterialShaders(
|
|
|
280
280
|
]);
|
|
281
281
|
|
|
282
282
|
// ── Step 0: storage-buffer cap gate ───────────────────────────────────────
|
|
283
|
-
const capValue =
|
|
284
|
-
.maxStorageBuffersPerShaderStage;
|
|
283
|
+
const capValue = rhiDevice.limits.maxStorageBuffersPerShaderStage;
|
|
285
284
|
const webgl2Downlevel = rhiDevice.caps.backendKind === 'wgpu-webgl2';
|
|
286
285
|
let storageBufferCapable = !webgl2Downlevel && rhiDevice.caps.storageBuffer;
|
|
287
286
|
if (typeof capValue === 'number') {
|
|
@@ -350,6 +349,12 @@ export async function prepareMaterialShaders(
|
|
|
350
349
|
if (msEntry.variants.some((v) => 'VERTEX_COLOR_AVAILABLE' in v.defines)) {
|
|
351
350
|
variantDefines.VERTEX_COLOR_AVAILABLE = false;
|
|
352
351
|
}
|
|
352
|
+
if (msEntry.variants.some((v) => 'COVERAGE_ONLY' in v.defines)) {
|
|
353
|
+
// Coverage-only is a producer-owned opt-in for sprite/temporal paths.
|
|
354
|
+
// Keep the ordinary boot material on the opaque variant; record-time
|
|
355
|
+
// coverage batches request the true variant explicitly.
|
|
356
|
+
variantDefines.COVERAGE_ONLY = false;
|
|
357
|
+
}
|
|
353
358
|
if (msEntry.variants.some((v) => 'PROBE_BLEND_AVAILABLE' in v.defines)) {
|
|
354
359
|
variantDefines.PROBE_BLEND_AVAILABLE = false;
|
|
355
360
|
}
|
|
@@ -422,6 +427,9 @@ export async function prepareMaterialShaders(
|
|
|
422
427
|
registry.installMaterialArtifact(msEntry.identifier, {
|
|
423
428
|
source: wgsl,
|
|
424
429
|
paramSchema,
|
|
430
|
+
...(chosen?.receipt === undefined && msEntry.receipt === undefined
|
|
431
|
+
? {}
|
|
432
|
+
: { receipt: chosen?.receipt ?? msEntry.receipt }),
|
|
425
433
|
});
|
|
426
434
|
// feat-20260629 M4: store uvSetCount from naga reflection for
|
|
427
435
|
// auto-fill in getMaterialShaderPipeline clamp-to-last alias.
|
|
@@ -470,6 +478,7 @@ export async function prepareMaterialShaders(
|
|
|
470
478
|
registry.installMaterialArtifact(shadowCasterIdentifier, {
|
|
471
479
|
source: manifestEntry.composedWgsl,
|
|
472
480
|
paramSchema: STANDARD_PIPELINE_PARAM_SCHEMA,
|
|
481
|
+
...(manifestEntry.receipt === undefined ? {} : { receipt: manifestEntry.receipt }),
|
|
473
482
|
});
|
|
474
483
|
const shaderGuid = ENGINE_SHADER_GUIDS.get(shadowCasterIdentifier);
|
|
475
484
|
if (shaderGuid !== undefined) {
|
|
@@ -604,9 +613,12 @@ export function selectNoColorPbrVariant(
|
|
|
604
613
|
): MaterialShaderManifestEntry['variants'][number] | undefined {
|
|
605
614
|
const hdrpRequested = variantSet === '' || requestsClusterVariant(variantSet);
|
|
606
615
|
const probeBlendRequested = requestsProbeBlendVariant(variantSet);
|
|
616
|
+
const coverageOnlyRequested = variantSet?.includes('COVERAGE_ONLY=true') === true;
|
|
607
617
|
return manifestEntry?.variants.find(
|
|
608
618
|
(variant) =>
|
|
609
619
|
variant.defines.STORAGE_BUFFER_AVAILABLE === storageBufferCapable &&
|
|
620
|
+
(!('COVERAGE_ONLY' in variant.defines) ||
|
|
621
|
+
variant.defines.COVERAGE_ONLY === coverageOnlyRequested) &&
|
|
610
622
|
(!('EXTENDED_LIGHTING_AVAILABLE' in variant.defines) ||
|
|
611
623
|
variant.defines.EXTENDED_LIGHTING_AVAILABLE === extendedLightingShaderAvailable) &&
|
|
612
624
|
variant.defines.VERTEX_COLOR_AVAILABLE === false &&
|
|
@@ -896,6 +908,7 @@ export function resolveMaterialShaderVariantSet(
|
|
|
896
908
|
): string | undefined {
|
|
897
909
|
const declaredAxes = new Set(variants.flatMap((variant) => Object.keys(variant.defines)));
|
|
898
910
|
const declaresCapabilityVariants =
|
|
911
|
+
declaredAxes.has('COVERAGE_ONLY') ||
|
|
899
912
|
declaredAxes.has('WEBGL2_COMPAT') ||
|
|
900
913
|
declaredAxes.has('STORAGE_BUFFER_AVAILABLE') ||
|
|
901
914
|
declaredAxes.has('EXTENDED_LIGHTING_AVAILABLE') ||
|
|
@@ -933,23 +946,31 @@ export function resolveMaterialShaderVariantSet(
|
|
|
933
946
|
? backendKind !== 'wgpu-webgl2' && storageBuffer
|
|
934
947
|
: axis === 'SKINNING_DISABLED'
|
|
935
948
|
? (requested[axis] ?? true)
|
|
936
|
-
: axis === '
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
949
|
+
: axis === 'ALPHA_MASK' ||
|
|
950
|
+
axis === 'GPU_DRIVEN_SCENE_INDEX_AVAILABLE' ||
|
|
951
|
+
axis === 'GPU_DRIVEN_SCENE_INDEX_EXPLICIT' ||
|
|
952
|
+
axis === 'COVERAGE_ONLY'
|
|
953
|
+
? (requested[axis] ?? false)
|
|
954
|
+
: axis === 'CLUSTER_FORWARD_AVAILABLE'
|
|
955
|
+
? storageBuffer && backendKind !== 'wgpu-webgl2' && (requested[axis] ?? false)
|
|
956
|
+
: axis === 'PER_INSTANCE_REGION'
|
|
941
957
|
? requestedVariantSet === '' || (requested[axis] ?? false)
|
|
942
|
-
: axis === '
|
|
943
|
-
?
|
|
944
|
-
: axis === '
|
|
945
|
-
?
|
|
946
|
-
: axis === '
|
|
947
|
-
? (requested[axis] ?? false)
|
|
948
|
-
: axis === '
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
958
|
+
: axis === 'VERTEX_COLOR_AVAILABLE'
|
|
959
|
+
? requestedVariantSet === '' || (requested[axis] ?? false)
|
|
960
|
+
: axis === 'EXTENDED_LIGHTING_AVAILABLE'
|
|
961
|
+
? extendedLightingAvailable
|
|
962
|
+
: axis === 'PROBE_BLEND_AVAILABLE'
|
|
963
|
+
? storageBuffer && (requested[axis] ?? false)
|
|
964
|
+
: axis === 'TRANSMISSION_AVAILABLE' ||
|
|
965
|
+
axis === 'REFLECTION_FALLBACK_AVAILABLE'
|
|
966
|
+
? (requested[axis] ?? false)
|
|
967
|
+
: axis === 'DIRECTIONAL_PCSS_AVAILABLE'
|
|
968
|
+
? directionalPcssCapable(backendKind)
|
|
969
|
+
: axis === 'PROJECTOR_AVAILABLE'
|
|
970
|
+
? projectorAvailable
|
|
971
|
+
: // Authored axes keep the manifest's all-true default.
|
|
972
|
+
// Only Engine-owned admission axes default to disabled.
|
|
973
|
+
(requested[axis] ?? true);
|
|
953
974
|
}
|
|
954
975
|
const sorted = Object.entries(filtered).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
955
976
|
return sorted.every(([, value]) => value)
|
|
@@ -50,6 +50,11 @@ export function attachDeviceLostFanout(
|
|
|
50
50
|
}
|
|
51
51
|
onDeviceLost?.(detail);
|
|
52
52
|
lostRegistry.fire(safe);
|
|
53
|
+
// A `destroyed` loss is the terminal signal from an intentional
|
|
54
|
+
// GPUDevice.destroy() call. It is observable through the lost channel,
|
|
55
|
+
// but must not put the renderer into recoverable device-lost health or
|
|
56
|
+
// emit an error during normal teardown. Only an unexpected loss keeps the
|
|
57
|
+
// recovery/error fan-out active.
|
|
53
58
|
if (safe.reason !== 'destroyed') {
|
|
54
59
|
pack.instrumentation?.onDeviceLost?.();
|
|
55
60
|
healthRegistry.fire({
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from '@forgeax/engine-rhi';
|
|
13
13
|
import { ok, type Result } from '@forgeax/engine-types';
|
|
14
14
|
import type { RhiBackendPack } from '../backend-contract';
|
|
15
|
+
import { deviceOptionsForAdapter } from '../device-feature-admission';
|
|
15
16
|
import {
|
|
16
17
|
RECOVERY_ADAPTER_DEADLINE_MS,
|
|
17
18
|
RECOVERY_DEVICE_DEADLINE_MS,
|
|
@@ -20,13 +21,7 @@ import {
|
|
|
20
21
|
type RecoveryPhase,
|
|
21
22
|
} from '../renderer-lifecycle';
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
'texture-compression-bc',
|
|
25
|
-
'texture-compression-etc2',
|
|
26
|
-
'texture-compression-astc',
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
export const STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES = 17;
|
|
24
|
+
export { deviceOptionsForAdapter } from '../device-feature-admission';
|
|
30
25
|
|
|
31
26
|
const PROBE_TEXTURE_USAGE_COPY_SRC = 0x01;
|
|
32
27
|
const PROBE_TEXTURE_USAGE_RENDER_ATTACHMENT = 0x10;
|
|
@@ -573,28 +568,6 @@ export async function probeCandidateGraphExecution(
|
|
|
573
568
|
}
|
|
574
569
|
}
|
|
575
570
|
|
|
576
|
-
export function deviceOptionsForAdapter(adapter: Pick<RhiAdapter, 'features' | 'limits'>):
|
|
577
|
-
| {
|
|
578
|
-
requiredFeatures?: GPUFeatureName[];
|
|
579
|
-
requiredLimits?: GPUDeviceDescriptor['requiredLimits'];
|
|
580
|
-
}
|
|
581
|
-
| undefined {
|
|
582
|
-
const requiredFeatures = COMPRESSION_FEATURES.filter((feature) => adapter.features.has(feature));
|
|
583
|
-
const supportsTransmission =
|
|
584
|
-
(adapter.limits.maxSampledTexturesPerShaderStage ?? 0) >=
|
|
585
|
-
STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES;
|
|
586
|
-
return {
|
|
587
|
-
...(requiredFeatures.length > 0 ? { requiredFeatures } : {}),
|
|
588
|
-
...(supportsTransmission
|
|
589
|
-
? {
|
|
590
|
-
requiredLimits: {
|
|
591
|
-
maxSampledTexturesPerShaderStage: STANDARD_PBR_REQUIRED_SAMPLED_TEXTURES,
|
|
592
|
-
},
|
|
593
|
-
}
|
|
594
|
-
: {}),
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
|
|
598
571
|
export interface AcquiredDeviceGeneration {
|
|
599
572
|
readonly adapter: RhiAdapter;
|
|
600
573
|
readonly device: RhiDevice;
|