@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
|
@@ -24,9 +24,11 @@ import type { MeshGpuHandles } from '../device/gpu-residency';
|
|
|
24
24
|
import { GpuDrivenPreparationError } from '../errors/gpu-driven';
|
|
25
25
|
import {
|
|
26
26
|
gpuDrivenDrawKey,
|
|
27
|
+
gpuDrivenMaterialArtifactKey,
|
|
27
28
|
gpuDrivenShadowDrawKey,
|
|
28
29
|
gpuDrivenSourceDrawItemIndex,
|
|
29
30
|
} from '../extract/gpu-driven';
|
|
31
|
+
import { GPU_SCENE_LAYOUTS } from '../gpu-scene-schema';
|
|
30
32
|
import {
|
|
31
33
|
GPU_BUFFER_USAGE_COPY_DST,
|
|
32
34
|
GPU_BUFFER_USAGE_INDEX,
|
|
@@ -40,14 +42,20 @@ import type {
|
|
|
40
42
|
GpuDrivenProductionInspection,
|
|
41
43
|
GpuDrivenStructureMetrics,
|
|
42
44
|
} from '../inspection-types';
|
|
45
|
+
import { isStandardPbrMaterialShader, resolvePipelineGroup2Contract } from '../pbr-pipeline';
|
|
43
46
|
import type { PipelineBuilderShaderModuleFactory } from '../pipeline-builder';
|
|
44
47
|
import type { ValidatedRenderable } from '../record/frame-snapshot';
|
|
45
48
|
import { worldEntityKey } from '../record/frame-snapshot';
|
|
46
49
|
import { geometryRenderStateForTopology } from '../record/main-pass-material';
|
|
47
50
|
import { MESH_SSBO_BYTES, packInstanceStorageBuffer } from '../record/mesh-ssbo';
|
|
48
|
-
import
|
|
51
|
+
import {
|
|
52
|
+
MATERIAL_PER_ENTITY_STRIDE,
|
|
53
|
+
type PipelineState,
|
|
54
|
+
type RenderSystemInternals,
|
|
55
|
+
} from '../record/render-context';
|
|
49
56
|
import type { CameraSnapshot } from '../render-contract';
|
|
50
57
|
import type {
|
|
58
|
+
GpuDrivenSceneMeshBinding,
|
|
51
59
|
GpuDrivenStandardPbrFrameResources,
|
|
52
60
|
RenderPipelineFrame,
|
|
53
61
|
RenderPipelineGpuDrivenProjection,
|
|
@@ -67,6 +75,7 @@ import {
|
|
|
67
75
|
projectedHeightForCandidate,
|
|
68
76
|
type SubmissionPlan,
|
|
69
77
|
} from './batch-topology';
|
|
78
|
+
import { materialBindingKey } from './material-bindings';
|
|
70
79
|
import { type GpuDrivenPbrProgram, resolveStandardPbrProgram } from './pbr-program';
|
|
71
80
|
import {
|
|
72
81
|
type ShadowViewIdentity,
|
|
@@ -99,10 +108,17 @@ export interface GpuDrivenShadowBatchProjection {
|
|
|
99
108
|
readonly meshBindGroup: BindGroup;
|
|
100
109
|
readonly visibleBindGroup: BindGroup;
|
|
101
110
|
readonly deformation: 'rigid' | 'skin';
|
|
111
|
+
/** Actual producer-selected shadow-pass artifact for this batch. */
|
|
112
|
+
readonly shadowArtifact: MaterialShaderArtifact;
|
|
113
|
+
/** Pass entry points selected by the authored ShadowCaster dispatch. */
|
|
114
|
+
readonly shadowVertexEntry?: string;
|
|
115
|
+
readonly shadowFragmentEntry?: string;
|
|
102
116
|
/** Material snapshot used to assemble the receipt-owned alpha-mask BG. */
|
|
103
117
|
readonly material: MaterialSnapshot;
|
|
104
118
|
/** Composite entity identity used only for video-resource routing. */
|
|
105
119
|
readonly materialEntityKey: number;
|
|
120
|
+
/** Geometry-owned COLOR_0 fact shared by main and shadow ABI variants. */
|
|
121
|
+
readonly vertexColorAvailable: boolean;
|
|
106
122
|
}
|
|
107
123
|
|
|
108
124
|
/**
|
|
@@ -227,6 +243,8 @@ export function adaptStandardPbrFrameResources(input: {
|
|
|
227
243
|
interface PreparedBatch {
|
|
228
244
|
readonly batch: GpuDrivenBatch;
|
|
229
245
|
readonly mesh: MeshGpuHandles;
|
|
246
|
+
/** Exact producer artifact selected by every draw in this homogeneous batch. */
|
|
247
|
+
readonly artifact: MaterialShaderArtifact;
|
|
230
248
|
readonly renderState: MaterialRenderState | undefined;
|
|
231
249
|
/** Stable World attachment key used to join same-submit GPU counters. */
|
|
232
250
|
readonly worldKey: number;
|
|
@@ -298,12 +316,8 @@ function hasFiniteOrderedBounds(bounds: Float32Array | undefined): boolean {
|
|
|
298
316
|
|
|
299
317
|
function skinLaneReason(
|
|
300
318
|
snapshot: RenderableSnapshot,
|
|
301
|
-
material: MaterialSnapshot,
|
|
302
319
|
): 'skin-bounds-missing' | 'skin-address-missing' | undefined {
|
|
303
|
-
|
|
304
|
-
material.materialShaderId === 'forgeax::pbr-skin' ||
|
|
305
|
-
material.materialShaderId === 'forgeax::default-standard-pbr-skin';
|
|
306
|
-
if (!isSkinnedPbr || snapshot.skin === undefined) return undefined;
|
|
320
|
+
if (snapshot.skin === undefined) return undefined;
|
|
307
321
|
if (!hasFiniteOrderedBounds(snapshot.skin.bounds)) return 'skin-bounds-missing';
|
|
308
322
|
if (
|
|
309
323
|
snapshot.skin.storageOrUniform !== 'storage' ||
|
|
@@ -347,15 +361,19 @@ function preparedPbrCandidateEligible(
|
|
|
347
361
|
snapshot: RenderableSnapshot,
|
|
348
362
|
batch: GpuDrivenBatch,
|
|
349
363
|
candidate: GpuDrivenBatch['candidates'][number],
|
|
350
|
-
|
|
364
|
+
artifact: MaterialShaderArtifact | undefined,
|
|
351
365
|
): boolean {
|
|
352
366
|
const material = snapshot.materials[batch.key.materialSlot] ?? snapshot.material;
|
|
353
367
|
const prepared = candidate.prepared;
|
|
354
368
|
if (prepared === undefined) return false;
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
prepared.identity.material
|
|
369
|
+
const receipt = artifact?.receipt;
|
|
370
|
+
const publishedAbiMatches =
|
|
371
|
+
receipt !== undefined &&
|
|
372
|
+
artifact?.material === prepared.identity.material &&
|
|
373
|
+
(prepared.receiptIdentity === undefined ||
|
|
374
|
+
(prepared.receiptIdentity === receipt.receiptIdentity &&
|
|
375
|
+
prepared.receiptGeneration === receipt.generation)) &&
|
|
376
|
+
receipt.reflection.layoutIdentity === artifact?.layoutIdentity;
|
|
359
377
|
const alphaBlend = material.transparent === true || material.renderState?.blend !== undefined;
|
|
360
378
|
const cpuOnlyMaterialResources =
|
|
361
379
|
(material.textureSources?.size ?? 0) > 0 || (material.videoTextureFields?.size ?? 0) > 0;
|
|
@@ -363,18 +381,19 @@ function preparedPbrCandidateEligible(
|
|
|
363
381
|
// GPU-owned candidate set so the residual transmission draw is not swallowed
|
|
364
382
|
// by the generic Standard PBR lane.
|
|
365
383
|
const transmission =
|
|
384
|
+
isStandardPbrMaterialShader(prepared.identity.material) &&
|
|
366
385
|
typeof material.paramSnapshot?.transmission === 'number' &&
|
|
367
386
|
material.paramSnapshot.transmission > 0;
|
|
368
387
|
const skinReady =
|
|
369
388
|
prepared.identity.deformation !== 'skin' ||
|
|
370
|
-
(
|
|
389
|
+
(artifact?.receipt?.skinPaletteAddress !== undefined &&
|
|
371
390
|
snapshot.skin?.storageOrUniform === 'storage' &&
|
|
372
391
|
snapshot.skin.customDataStart >= 0 &&
|
|
373
392
|
Number.isInteger(snapshot.skin.customDataStart) &&
|
|
374
393
|
snapshot.skin.buffer !== undefined &&
|
|
375
394
|
hasFiniteOrderedBounds(snapshot.skin.bounds));
|
|
376
395
|
return (
|
|
377
|
-
|
|
396
|
+
publishedAbiMatches &&
|
|
378
397
|
!alphaBlend &&
|
|
379
398
|
!cpuOnlyMaterialResources &&
|
|
380
399
|
!transmission &&
|
|
@@ -432,7 +451,13 @@ function gpuDrivenProjectionKey(
|
|
|
432
451
|
deformation: 'rigid' | 'skin',
|
|
433
452
|
artifact: MaterialShaderArtifact,
|
|
434
453
|
): string {
|
|
435
|
-
|
|
454
|
+
const receipt = artifact.receipt;
|
|
455
|
+
return `${batch.batchId}:${batch.generation}:${gpuDrivenMaterialArtifactKey({
|
|
456
|
+
material: artifact.material,
|
|
457
|
+
deformation,
|
|
458
|
+
receiptIdentity: receipt?.receiptIdentity,
|
|
459
|
+
receiptGeneration: receipt?.generation ?? 0,
|
|
460
|
+
})}:${artifact.variantSet ?? ''}:${materialArtifactProgramIdentity(artifact)}:${artifact.vertexEntry ?? receipt?.sceneIndexEntry ?? ''}:${artifact.fragmentEntry ?? ''}`;
|
|
436
461
|
}
|
|
437
462
|
|
|
438
463
|
function gpuDrivenShadowProjectionKey(
|
|
@@ -454,6 +479,26 @@ function sourceDrawForCandidate(
|
|
|
454
479
|
);
|
|
455
480
|
}
|
|
456
481
|
|
|
482
|
+
const materialArtifactProgramIdentities = new WeakMap<
|
|
483
|
+
MaterialShaderArtifact,
|
|
484
|
+
{ readonly source: string; readonly identity: string }
|
|
485
|
+
>();
|
|
486
|
+
|
|
487
|
+
/** Hash a publication once; replacement source invalidates the same artifact key. */
|
|
488
|
+
function materialArtifactProgramIdentity(artifact: MaterialShaderArtifact): string {
|
|
489
|
+
const source = artifact.specializationKey ?? artifact.wgsl;
|
|
490
|
+
const cached = materialArtifactProgramIdentities.get(artifact);
|
|
491
|
+
if (cached?.source === source) return cached.identity;
|
|
492
|
+
let hash = 2166136261;
|
|
493
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
494
|
+
hash ^= source.charCodeAt(index);
|
|
495
|
+
hash = Math.imul(hash, 16777619);
|
|
496
|
+
}
|
|
497
|
+
const identity = `${source.length}:${hash >>> 0}`;
|
|
498
|
+
materialArtifactProgramIdentities.set(artifact, { source, identity });
|
|
499
|
+
return identity;
|
|
500
|
+
}
|
|
501
|
+
|
|
457
502
|
function shadowKeysForCandidate(
|
|
458
503
|
slot: PersistentGpuDrivenState['slots'][number],
|
|
459
504
|
candidate: GpuDrivenBatch['candidates'][number],
|
|
@@ -524,8 +569,10 @@ function shadowMembershipIsGpuCompatible(
|
|
|
524
569
|
const membership = matching[0];
|
|
525
570
|
if (membership?.cpuReason !== undefined) return false;
|
|
526
571
|
if (membership?.renderState !== undefined) return false;
|
|
527
|
-
|
|
528
|
-
|
|
572
|
+
// Older synthetic membership fixtures omit the producer flag and remain
|
|
573
|
+
// compatible; extracted records always carry it. A false flag is an
|
|
574
|
+
// authored/producer decision and must keep the draw on its CPU lane.
|
|
575
|
+
return membership?.gpuDrivenEligible !== false;
|
|
529
576
|
}
|
|
530
577
|
|
|
531
578
|
function selectedLodLevel(candidate: GpuDrivenCandidate): number {
|
|
@@ -578,6 +625,9 @@ function filteredPlan(
|
|
|
578
625
|
camera?: CameraSnapshot,
|
|
579
626
|
activeEntityKeys?: ReadonlySet<number>,
|
|
580
627
|
worldKeys?: readonly number[],
|
|
628
|
+
materialArtifact?: MaterialShaderArtifact,
|
|
629
|
+
materialArtifacts?: ReadonlyMap<string, MaterialShaderArtifact>,
|
|
630
|
+
materialBindingClasses?: ReadonlyMap<string, string>,
|
|
581
631
|
): {
|
|
582
632
|
readonly plan: SubmissionPlan;
|
|
583
633
|
readonly batches: readonly PreparedBatch[];
|
|
@@ -601,6 +651,7 @@ function filteredPlan(
|
|
|
601
651
|
}
|
|
602
652
|
}
|
|
603
653
|
const eligibleMainCandidates = new Set<string>();
|
|
654
|
+
const eligibleArtifacts = new Map<string, MaterialShaderArtifact>();
|
|
604
655
|
const drawKeys = new Set<string>();
|
|
605
656
|
const eligibleShadowCandidates = new Set<string>();
|
|
606
657
|
const shadowDrawKeys = new Set<string>();
|
|
@@ -617,13 +668,23 @@ function filteredPlan(
|
|
|
617
668
|
for (const candidate of batch.candidates) {
|
|
618
669
|
if (candidate.primitiveIndex !== slot.slot) continue;
|
|
619
670
|
const draw = sourceDrawForCandidate(slot, candidate);
|
|
671
|
+
const selectedArtifact = artifactForPreparedCandidate(
|
|
672
|
+
candidate,
|
|
673
|
+
materialArtifacts,
|
|
674
|
+
materialArtifact,
|
|
675
|
+
skinArtifact,
|
|
676
|
+
);
|
|
620
677
|
const mainEligible =
|
|
621
678
|
draw !== undefined &&
|
|
622
|
-
preparedPbrCandidateEligible(slot.snapshot, batch, candidate,
|
|
679
|
+
preparedPbrCandidateEligible(slot.snapshot, batch, candidate, selectedArtifact) &&
|
|
623
680
|
mesh !== undefined &&
|
|
624
681
|
meshSupportsBatch(mesh, batch);
|
|
682
|
+
const candidateKey = shadowCandidateKey(candidate);
|
|
683
|
+
if (mainEligible && selectedArtifact !== undefined) {
|
|
684
|
+
eligibleArtifacts.set(candidateKey, selectedArtifact);
|
|
685
|
+
}
|
|
625
686
|
if (!shadowSelectionProvided && mainEligible && draw !== undefined) {
|
|
626
|
-
eligibleMainCandidates.add(
|
|
687
|
+
eligibleMainCandidates.add(candidateKey);
|
|
627
688
|
const material = slot.snapshot.materials[draw.materialSlot] ?? slot.snapshot.material;
|
|
628
689
|
drawKeys.add(
|
|
629
690
|
gpuDrivenDrawKey(
|
|
@@ -661,6 +722,7 @@ function filteredPlan(
|
|
|
661
722
|
{
|
|
662
723
|
readonly source: GpuDrivenBatch;
|
|
663
724
|
readonly mesh: MeshGpuHandles;
|
|
725
|
+
readonly artifact: MaterialShaderArtifact;
|
|
664
726
|
readonly renderState: MaterialRenderState | undefined;
|
|
665
727
|
readonly candidates: GpuDrivenCandidate[];
|
|
666
728
|
}
|
|
@@ -684,6 +746,8 @@ function filteredPlan(
|
|
|
684
746
|
if (slot === undefined) continue;
|
|
685
747
|
const mesh = meshBySlot.get(slot.slot);
|
|
686
748
|
if (mesh === undefined) continue;
|
|
749
|
+
const artifact = eligibleArtifacts.get(shadowCandidateKey(candidate));
|
|
750
|
+
if (artifact === undefined) continue;
|
|
687
751
|
const physicalLodRanges = mesh.lodRanges
|
|
688
752
|
?.slice(1)
|
|
689
753
|
.map((ranges) => ranges[candidate.drawItemIndex]);
|
|
@@ -701,7 +765,23 @@ function filteredPlan(
|
|
|
701
765
|
const selectedRange = selectedLodRange(batch, projected);
|
|
702
766
|
const groupKey = JSON.stringify({
|
|
703
767
|
key: batch.key,
|
|
768
|
+
artifact: [
|
|
769
|
+
artifact.material,
|
|
770
|
+
artifact.layoutIdentity,
|
|
771
|
+
artifact.receipt?.receiptIdentity ?? '',
|
|
772
|
+
artifact.receipt?.generation ?? 0,
|
|
773
|
+
artifact.variantSet ?? '',
|
|
774
|
+
materialArtifactProgramIdentity(artifact),
|
|
775
|
+
],
|
|
704
776
|
worldKey: worldKeyFor(slot),
|
|
777
|
+
bindingClass:
|
|
778
|
+
materialBindingClasses?.get(
|
|
779
|
+
materialBindingKey(
|
|
780
|
+
slot.snapshot,
|
|
781
|
+
(slot.snapshot.materials[batch.key.materialSlot] ?? slot.snapshot.material)
|
|
782
|
+
.materialHandle ?? -1,
|
|
783
|
+
),
|
|
784
|
+
) ?? '',
|
|
705
785
|
lodCoverages: projected.lodCoverages ?? null,
|
|
706
786
|
lodRanges: projected.lodRanges ?? null,
|
|
707
787
|
selectedRange,
|
|
@@ -713,6 +793,7 @@ function filteredPlan(
|
|
|
713
793
|
groups.set(groupKey, {
|
|
714
794
|
source: batch,
|
|
715
795
|
mesh,
|
|
796
|
+
artifact,
|
|
716
797
|
renderState: geometryRenderStateForTopology(batch.key.topology, material.renderState),
|
|
717
798
|
candidates: [projected],
|
|
718
799
|
});
|
|
@@ -765,6 +846,7 @@ function filteredPlan(
|
|
|
765
846
|
batches.push({
|
|
766
847
|
batch: filtered,
|
|
767
848
|
mesh: group.mesh,
|
|
849
|
+
artifact: group.artifact,
|
|
768
850
|
renderState: group.renderState,
|
|
769
851
|
worldKey: worldKeyFor(firstSlot),
|
|
770
852
|
primitiveSlot: firstSlot.slot,
|
|
@@ -833,6 +915,7 @@ function shadowCasterMembershipSignature(
|
|
|
833
915
|
entry.materialShaderId ?? '',
|
|
834
916
|
JSON.stringify(entry.renderState ?? null),
|
|
835
917
|
entry.cpuReason ?? '',
|
|
918
|
+
entry.gpuDrivenEligible === undefined ? '' : String(entry.gpuDrivenEligible),
|
|
836
919
|
].join(':'),
|
|
837
920
|
)
|
|
838
921
|
.sort()
|
|
@@ -845,11 +928,8 @@ type FilteredProductionPlan = ReturnType<typeof filteredPlan>;
|
|
|
845
928
|
function sceneRequiresGpuDrivenArtifact(scene: PersistentGpuDrivenState): boolean {
|
|
846
929
|
return scene.plan.batches.some((batch) =>
|
|
847
930
|
batch.candidates.some((candidate) => {
|
|
848
|
-
const material = candidate.prepared?.identity.material;
|
|
849
931
|
return (
|
|
850
|
-
|
|
851
|
-
material === 'forgeax::pbr-skin' ||
|
|
852
|
-
material === 'forgeax::default-standard-pbr-skin') &&
|
|
932
|
+
candidate.prepared?.receiptIdentity !== undefined &&
|
|
853
933
|
(candidate.prepared?.identity.deformation === 'rigid' ||
|
|
854
934
|
candidate.prepared?.identity.deformation === 'skin')
|
|
855
935
|
);
|
|
@@ -857,6 +937,31 @@ function sceneRequiresGpuDrivenArtifact(scene: PersistentGpuDrivenState): boolea
|
|
|
857
937
|
);
|
|
858
938
|
}
|
|
859
939
|
|
|
940
|
+
function missingMaterialArtifact(
|
|
941
|
+
scene: PersistentGpuDrivenState,
|
|
942
|
+
artifacts: ReadonlyMap<string, MaterialShaderArtifact> | undefined,
|
|
943
|
+
rigidFallback: MaterialShaderArtifact | undefined,
|
|
944
|
+
skinFallback: MaterialShaderArtifact | undefined,
|
|
945
|
+
): GpuDrivenPreparationError | undefined {
|
|
946
|
+
for (const batch of scene.plan.batches) {
|
|
947
|
+
for (const candidate of batch.candidates) {
|
|
948
|
+
const prepared = candidate.prepared;
|
|
949
|
+
if (prepared?.receiptIdentity === undefined) continue;
|
|
950
|
+
if (
|
|
951
|
+
artifactForPreparedCandidate(candidate, artifacts, rigidFallback, skinFallback) !==
|
|
952
|
+
undefined
|
|
953
|
+
)
|
|
954
|
+
continue;
|
|
955
|
+
return new GpuDrivenPreparationError('missing-material-receipt', {
|
|
956
|
+
reason: 'material-receipt-missing',
|
|
957
|
+
owner: 'material',
|
|
958
|
+
expected: `published artifact for ${prepared.identity.material} (${prepared.receiptIdentity}@${prepared.receiptGeneration})`,
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return undefined;
|
|
963
|
+
}
|
|
964
|
+
|
|
860
965
|
function nonCpuPreparationFailure(
|
|
861
966
|
scene: PersistentGpuDrivenState,
|
|
862
967
|
): { readonly error: GpuDrivenPreparationError; readonly drawKeys: readonly string[] } | undefined {
|
|
@@ -926,8 +1031,110 @@ function sameEntityKeySet(
|
|
|
926
1031
|
return true;
|
|
927
1032
|
}
|
|
928
1033
|
|
|
1034
|
+
type MaterialPipelineFactory = NonNullable<RenderSystemInternals['getMaterialShaderPipelineEntry']>;
|
|
1035
|
+
|
|
1036
|
+
function artifactForPreparedCandidate(
|
|
1037
|
+
candidate: GpuDrivenBatch['candidates'][number],
|
|
1038
|
+
artifacts: ReadonlyMap<string, MaterialShaderArtifact> | undefined,
|
|
1039
|
+
rigidFallback: MaterialShaderArtifact | undefined,
|
|
1040
|
+
skinFallback: MaterialShaderArtifact | undefined,
|
|
1041
|
+
): MaterialShaderArtifact | undefined {
|
|
1042
|
+
const prepared = candidate.prepared;
|
|
1043
|
+
if (prepared === undefined) return undefined;
|
|
1044
|
+
const exact = artifacts?.get(
|
|
1045
|
+
gpuDrivenMaterialArtifactKey({
|
|
1046
|
+
material: prepared.identity.material,
|
|
1047
|
+
deformation: prepared.identity.deformation,
|
|
1048
|
+
receiptIdentity: prepared.receiptIdentity,
|
|
1049
|
+
receiptGeneration: prepared.receiptGeneration,
|
|
1050
|
+
}),
|
|
1051
|
+
);
|
|
1052
|
+
if (exact !== undefined) return exact;
|
|
1053
|
+
// A map supplied by the real record path is authoritative. A receipt-backed
|
|
1054
|
+
// draw missing from it is a producer/load failure, not permission to pair
|
|
1055
|
+
// the draw with another material's artifact.
|
|
1056
|
+
if (artifacts !== undefined && prepared.receiptIdentity !== undefined) return undefined;
|
|
1057
|
+
const fallback = prepared.identity.deformation === 'skin' ? skinFallback : rigidFallback;
|
|
1058
|
+
return fallback?.material === prepared.identity.material ? fallback : undefined;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
function shadowArtifactForPreparedCandidate(
|
|
1062
|
+
candidate: GpuDrivenBatch['candidates'][number],
|
|
1063
|
+
artifacts: ReadonlyMap<string, MaterialShaderArtifact> | undefined,
|
|
1064
|
+
fallback: MaterialShaderArtifact | undefined,
|
|
1065
|
+
): MaterialShaderArtifact | undefined {
|
|
1066
|
+
const prepared = candidate.prepared;
|
|
1067
|
+
if (prepared === undefined) return undefined;
|
|
1068
|
+
const exact = artifacts?.get(
|
|
1069
|
+
gpuDrivenMaterialArtifactKey({
|
|
1070
|
+
material: prepared.identity.material,
|
|
1071
|
+
deformation: prepared.identity.deformation,
|
|
1072
|
+
receiptIdentity: prepared.receiptIdentity,
|
|
1073
|
+
receiptGeneration: prepared.receiptGeneration,
|
|
1074
|
+
}),
|
|
1075
|
+
);
|
|
1076
|
+
if (exact !== undefined) return exact;
|
|
1077
|
+
// Direct production fixtures predate the separate shadow publication map.
|
|
1078
|
+
// They are allowed to reuse the already validated artifact; a real frame
|
|
1079
|
+
// passes the authoritative map and therefore fails closed on a missing
|
|
1080
|
+
// shadow program instead of silently selecting the forward program.
|
|
1081
|
+
return artifacts === undefined ? fallback : undefined;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
function materialArtifactSignature(
|
|
1085
|
+
artifacts: ReadonlyMap<string, MaterialShaderArtifact> | undefined,
|
|
1086
|
+
rigidFallback: MaterialShaderArtifact | undefined,
|
|
1087
|
+
skinFallback: MaterialShaderArtifact | undefined,
|
|
1088
|
+
): string {
|
|
1089
|
+
const rows =
|
|
1090
|
+
artifacts === undefined
|
|
1091
|
+
? []
|
|
1092
|
+
: [...artifacts.entries()]
|
|
1093
|
+
.map(([key, artifact]) => [
|
|
1094
|
+
key,
|
|
1095
|
+
artifact.material,
|
|
1096
|
+
artifact.layoutIdentity,
|
|
1097
|
+
artifact.receipt?.receiptIdentity ?? '',
|
|
1098
|
+
artifact.receipt?.generation ?? 0,
|
|
1099
|
+
artifact.variantSet ?? '',
|
|
1100
|
+
materialArtifactProgramIdentity(artifact),
|
|
1101
|
+
artifact.vertexEntry ?? artifact.receipt?.sceneIndexEntry ?? '',
|
|
1102
|
+
artifact.fragmentEntry ?? '',
|
|
1103
|
+
])
|
|
1104
|
+
.sort((left, right) => String(left[0] ?? '').localeCompare(String(right[0] ?? '')));
|
|
1105
|
+
return JSON.stringify({
|
|
1106
|
+
rows,
|
|
1107
|
+
rigid:
|
|
1108
|
+
rigidFallback === undefined
|
|
1109
|
+
? undefined
|
|
1110
|
+
: [
|
|
1111
|
+
rigidFallback.material,
|
|
1112
|
+
rigidFallback.layoutIdentity,
|
|
1113
|
+
rigidFallback.receipt?.receiptIdentity ?? '',
|
|
1114
|
+
rigidFallback.receipt?.generation ?? 0,
|
|
1115
|
+
rigidFallback.variantSet ?? '',
|
|
1116
|
+
materialArtifactProgramIdentity(rigidFallback),
|
|
1117
|
+
rigidFallback.vertexEntry ?? rigidFallback.receipt?.sceneIndexEntry ?? '',
|
|
1118
|
+
rigidFallback.fragmentEntry ?? '',
|
|
1119
|
+
],
|
|
1120
|
+
skin:
|
|
1121
|
+
skinFallback === undefined
|
|
1122
|
+
? undefined
|
|
1123
|
+
: [
|
|
1124
|
+
skinFallback.material,
|
|
1125
|
+
skinFallback.layoutIdentity,
|
|
1126
|
+
skinFallback.receipt?.receiptIdentity ?? '',
|
|
1127
|
+
skinFallback.receipt?.generation ?? 0,
|
|
1128
|
+
skinFallback.variantSet ?? '',
|
|
1129
|
+
materialArtifactProgramIdentity(skinFallback),
|
|
1130
|
+
skinFallback.vertexEntry ?? skinFallback.receipt?.sceneIndexEntry ?? '',
|
|
1131
|
+
skinFallback.fragmentEntry ?? '',
|
|
1132
|
+
],
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
|
|
929
1136
|
/** The GPU-driven bridge to the existing four-group Standard PBR pipeline. */
|
|
930
|
-
class
|
|
1137
|
+
class MaterialAbiRasterAdapter {
|
|
931
1138
|
private readonly pipelines = new Map<string, RenderPipeline>();
|
|
932
1139
|
private readonly projectionBuffers = new Set<Buffer>();
|
|
933
1140
|
private readonly projectionBindings = new Map<
|
|
@@ -966,13 +1173,14 @@ class StandardPbrRasterAdapter {
|
|
|
966
1173
|
private readonly pipelineLayout: import('@forgeax/engine-rhi').PipelineLayout,
|
|
967
1174
|
private readonly meshLayout: BindGroupLayout,
|
|
968
1175
|
private readonly instancesBindGroupLayout: BindGroupLayout,
|
|
969
|
-
private readonly programs: ReadonlyMap<string, GpuDrivenPbrProgram
|
|
1176
|
+
private readonly programs: ReadonlyMap<string, GpuDrivenPbrProgram> | undefined,
|
|
970
1177
|
private instancesBindGroup: BindGroup,
|
|
971
1178
|
private readonly instanceBuffer: Buffer,
|
|
972
1179
|
private readonly instanceBufferSize: number,
|
|
973
1180
|
private visibleBuffer: Buffer,
|
|
974
1181
|
private readonly artifact: MaterialShaderArtifact,
|
|
975
1182
|
private readonly deformation: 'rigid' | 'skin',
|
|
1183
|
+
private readonly pipelineFactory: MaterialPipelineFactory | undefined,
|
|
976
1184
|
private paletteBuffer: Buffer | undefined,
|
|
977
1185
|
private paletteBindingWindowBytes: number | undefined,
|
|
978
1186
|
) {}
|
|
@@ -985,8 +1193,51 @@ class StandardPbrRasterAdapter {
|
|
|
985
1193
|
readonly deformation?: 'rigid' | 'skin';
|
|
986
1194
|
readonly paletteBuffer?: Buffer;
|
|
987
1195
|
readonly paletteBindingWindowBytes?: number;
|
|
988
|
-
|
|
1196
|
+
/** Renderer-owned factory for the selected artifact's actual PSO. */
|
|
1197
|
+
readonly pipelineFactory?: MaterialPipelineFactory;
|
|
1198
|
+
}): Result<MaterialAbiRasterAdapter, RhiError | GpuDrivenPreparationError> {
|
|
989
1199
|
const deformation = input.deformation ?? 'rigid';
|
|
1200
|
+
const receipt = input.artifact.receipt;
|
|
1201
|
+
if (receipt === undefined) {
|
|
1202
|
+
return err(
|
|
1203
|
+
new RhiError({
|
|
1204
|
+
code: 'rhi-not-available',
|
|
1205
|
+
expected: 'published MaterialProgramAbi receipt for the selected material artifact',
|
|
1206
|
+
hint: 'recook and publish the material before creating a GPU-driven raster owner',
|
|
1207
|
+
}),
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
const materialRowMatchesSceneTable =
|
|
1211
|
+
receipt.materialRow.byteLength === GPU_SCENE_LAYOUTS.material.stride &&
|
|
1212
|
+
new Set(receipt.materialRow.fields).size === receipt.materialRow.fields.length;
|
|
1213
|
+
if (!materialRowMatchesSceneTable) {
|
|
1214
|
+
return err(
|
|
1215
|
+
new GpuDrivenPreparationError('material-abi-not-executable', {
|
|
1216
|
+
reason: 'material-abi-not-executable',
|
|
1217
|
+
owner: 'material',
|
|
1218
|
+
expected: `scene-index material row ${GPU_SCENE_LAYOUTS.material.stride} bytes with unique producer fields`,
|
|
1219
|
+
actual: `row ${receipt.materialRow.byteLength} bytes with fields ${receipt.materialRow.fields.join(',')}`,
|
|
1220
|
+
}),
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
if (
|
|
1224
|
+
receipt.reflection.layoutIdentity !== input.artifact.layoutIdentity ||
|
|
1225
|
+
receipt.directEntry.length === 0 ||
|
|
1226
|
+
receipt.sceneIndexEntry.length === 0 ||
|
|
1227
|
+
receipt.resourceSlots.some((slot) => slot.group !== 1)
|
|
1228
|
+
) {
|
|
1229
|
+
return err(
|
|
1230
|
+
new RhiError({
|
|
1231
|
+
code: 'rhi-descriptor-invalid',
|
|
1232
|
+
expected: `complete selected MaterialProgramAbi reflection, scene row stride ${GPU_SCENE_LAYOUTS.material.stride}, and resource slots`,
|
|
1233
|
+
hint: 'repair the producer publication or use the owner for its non-scene material layout',
|
|
1234
|
+
}),
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
// Scene-index shaders declare the dedicated slot-3 ABI. Keep the legacy
|
|
1238
|
+
// fields as a compatibility fallback for synthetic fixtures; the real
|
|
1239
|
+
// factory publishes the dedicated handles whenever the capable storage
|
|
1240
|
+
// lane is enabled, so production draws never bind the direct-draw BGL.
|
|
990
1241
|
const pipelineLayout =
|
|
991
1242
|
deformation === 'skin'
|
|
992
1243
|
? input.pipelineState.gpuDrivenPbrSkinPipelineLayout
|
|
@@ -996,25 +1247,26 @@ class StandardPbrRasterAdapter {
|
|
|
996
1247
|
return err(
|
|
997
1248
|
new RhiError({
|
|
998
1249
|
code: 'rhi-not-available',
|
|
999
|
-
expected: `${deformation === 'skin' ? 'skinned ' : ''}
|
|
1000
|
-
hint: 'initialize the producer
|
|
1250
|
+
expected: `${deformation === 'skin' ? 'skinned ' : ''}published material pipeline layout`,
|
|
1251
|
+
hint: 'initialize the producer material pipeline before enabling GPU-driven raster',
|
|
1001
1252
|
}),
|
|
1002
1253
|
);
|
|
1003
1254
|
}
|
|
1004
1255
|
const programs = input.pipelineState.gpuDrivenPbrPrograms;
|
|
1005
1256
|
if (
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1257
|
+
input.pipelineFactory === undefined &&
|
|
1258
|
+
(programs === undefined ||
|
|
1259
|
+
resolveStandardPbrProgram(
|
|
1260
|
+
programs,
|
|
1261
|
+
input.artifact.material,
|
|
1262
|
+
input.artifact.receipt?.vertexInputs.some((attribute) => attribute.semantic === 'color'),
|
|
1263
|
+
) === undefined)
|
|
1012
1264
|
) {
|
|
1013
1265
|
return err(
|
|
1014
1266
|
new RhiError({
|
|
1015
1267
|
code: 'rhi-not-available',
|
|
1016
|
-
expected: `producer-owned ${deformation === 'skin' ? 'skinned ' : ''}
|
|
1017
|
-
hint: 'publish the
|
|
1268
|
+
expected: `producer-owned ${deformation === 'skin' ? 'skinned ' : ''}material shader module for the current device generation`,
|
|
1269
|
+
hint: 'publish the selected material module from buildReadyWebGPU before enabling GPU-driven raster',
|
|
1018
1270
|
}),
|
|
1019
1271
|
);
|
|
1020
1272
|
}
|
|
@@ -1026,7 +1278,7 @@ class StandardPbrRasterAdapter {
|
|
|
1026
1278
|
return err(
|
|
1027
1279
|
new RhiError({
|
|
1028
1280
|
code: 'rhi-not-available',
|
|
1029
|
-
expected: `${deformation === 'skin' ? 'skinned ' : ''}
|
|
1281
|
+
expected: `${deformation === 'skin' ? 'skinned ' : ''}material mesh bind-group layout`,
|
|
1030
1282
|
hint: 'publish the exact mesh-array layout selected by the material receipt',
|
|
1031
1283
|
}),
|
|
1032
1284
|
);
|
|
@@ -1077,7 +1329,7 @@ class StandardPbrRasterAdapter {
|
|
|
1077
1329
|
});
|
|
1078
1330
|
if (!instancesBindGroup.ok) return instancesBindGroup;
|
|
1079
1331
|
return ok(
|
|
1080
|
-
new
|
|
1332
|
+
new MaterialAbiRasterAdapter(
|
|
1081
1333
|
input.device,
|
|
1082
1334
|
pipelineLayout,
|
|
1083
1335
|
meshLayout,
|
|
@@ -1089,6 +1341,7 @@ class StandardPbrRasterAdapter {
|
|
|
1089
1341
|
input.visibleBuffer,
|
|
1090
1342
|
input.artifact,
|
|
1091
1343
|
deformation,
|
|
1344
|
+
input.pipelineFactory,
|
|
1092
1345
|
input.paletteBuffer,
|
|
1093
1346
|
input.paletteBindingWindowBytes,
|
|
1094
1347
|
),
|
|
@@ -1190,6 +1443,8 @@ class StandardPbrRasterAdapter {
|
|
|
1190
1443
|
topology: GpuDrivenBatch['key']['topology'],
|
|
1191
1444
|
stripIndexFormat: 'uint16' | 'uint32' | undefined,
|
|
1192
1445
|
renderState: MaterialRenderState | undefined,
|
|
1446
|
+
selectedArtifact: MaterialShaderArtifact = this.artifact,
|
|
1447
|
+
additionalColorFormats: readonly TextureFormat[] = [],
|
|
1193
1448
|
): Result<RenderPipeline, RhiError> {
|
|
1194
1449
|
const vertexLayout = deriveVertexBufferLayoutFromProjection(layoutProjection, {
|
|
1195
1450
|
shaderUvSetCount,
|
|
@@ -1203,12 +1458,25 @@ class StandardPbrRasterAdapter {
|
|
|
1203
1458
|
}),
|
|
1204
1459
|
);
|
|
1205
1460
|
}
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1461
|
+
const receipt = selectedArtifact.receipt;
|
|
1462
|
+
if (receipt === undefined) {
|
|
1463
|
+
return err(
|
|
1464
|
+
new RhiError({
|
|
1465
|
+
code: 'rhi-not-available',
|
|
1466
|
+
expected: 'selected MaterialProgramAbi receipt for GPU-driven raster',
|
|
1467
|
+
hint: 'keep the draw on the producer recovery path until its ABI is published',
|
|
1468
|
+
}),
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
const program =
|
|
1472
|
+
this.programs === undefined
|
|
1473
|
+
? undefined
|
|
1474
|
+
: resolveStandardPbrProgram(
|
|
1475
|
+
this.programs,
|
|
1476
|
+
this.artifact.material,
|
|
1477
|
+
layoutProjection.attributes.some((attribute) => attribute.key === 'color'),
|
|
1478
|
+
);
|
|
1479
|
+
if (this.pipelineFactory === undefined && program === undefined) {
|
|
1212
1480
|
return err(
|
|
1213
1481
|
new RhiError({
|
|
1214
1482
|
code: 'rhi-not-available',
|
|
@@ -1217,15 +1485,78 @@ class StandardPbrRasterAdapter {
|
|
|
1217
1485
|
}),
|
|
1218
1486
|
);
|
|
1219
1487
|
}
|
|
1220
|
-
const key = `${format}|${sampleCount}|${vertexLayout.arrayStride}|${topology}|${stripIndexFormat ?? ''}|${JSON.stringify(renderState ?? null)}|${
|
|
1488
|
+
const key = `${format}:${additionalColorFormats.join(',')}|${sampleCount}|${vertexLayout.arrayStride}|${topology}|${stripIndexFormat ?? ''}|${JSON.stringify(renderState ?? null)}|${selectedArtifact.material}|${selectedArtifact.layoutIdentity}|${receipt.receiptIdentity}|${receipt.generation}|${selectedArtifact.variantSet ?? ''}|${materialArtifactProgramIdentity(selectedArtifact)}|${selectedArtifact.vertexEntry ?? receipt.sceneIndexEntry}|${selectedArtifact.fragmentEntry ?? ''}|${layoutProjection.digest}|uv${shaderUvSetCount}`;
|
|
1221
1489
|
const cached = this.pipelines.get(key);
|
|
1222
1490
|
if (cached !== undefined) return ok(cached);
|
|
1491
|
+
if (this.pipelineFactory !== undefined) {
|
|
1492
|
+
const pipelineEntry = this.pipelineFactory(
|
|
1493
|
+
selectedArtifact.specializationKey ?? selectedArtifact.material,
|
|
1494
|
+
false,
|
|
1495
|
+
renderState,
|
|
1496
|
+
topology,
|
|
1497
|
+
stripIndexFormat,
|
|
1498
|
+
selectedArtifact.variantSet,
|
|
1499
|
+
'forward',
|
|
1500
|
+
undefined,
|
|
1501
|
+
sampleCount,
|
|
1502
|
+
format,
|
|
1503
|
+
selectedArtifact.uvSetCount ?? shaderUvSetCount,
|
|
1504
|
+
'depth24plus-stencil8',
|
|
1505
|
+
undefined,
|
|
1506
|
+
layoutProjection,
|
|
1507
|
+
// GPU-driven receipts are already producer-validated. Use the
|
|
1508
|
+
// synchronous shader-module adapter for this submission seam so a
|
|
1509
|
+
// newly loaded authored material cannot lose its first draw to the
|
|
1510
|
+
// ordinary async pre-bake warm-up window.
|
|
1511
|
+
'immediate',
|
|
1512
|
+
this.deformation === 'skin' ? 'gpu-driven-skin' : 'gpu-driven-pbr',
|
|
1513
|
+
receipt.sceneIndexEntry,
|
|
1514
|
+
additionalColorFormats,
|
|
1515
|
+
selectedArtifact.vertexEntry,
|
|
1516
|
+
selectedArtifact.fragmentEntry,
|
|
1517
|
+
);
|
|
1518
|
+
if (pipelineEntry === null) {
|
|
1519
|
+
return err(
|
|
1520
|
+
new RhiError({
|
|
1521
|
+
code: 'rhi-not-available',
|
|
1522
|
+
expected: `producer-owned scene-index pipeline for material '${selectedArtifact.material}'`,
|
|
1523
|
+
hint: 'publish a matching program, entry, layout and resource contract before GPU admission',
|
|
1524
|
+
}),
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1527
|
+
const expectedGroup2Contract = resolvePipelineGroup2Contract(selectedArtifact.wgsl);
|
|
1528
|
+
if (pipelineEntry.group2Contract !== expectedGroup2Contract) {
|
|
1529
|
+
return err(
|
|
1530
|
+
new RhiError({
|
|
1531
|
+
code: 'rhi-descriptor-invalid',
|
|
1532
|
+
expected: `GPU-driven ${this.deformation === 'skin' ? 'skinned ' : ''}material pipeline group(2) ${expectedGroup2Contract} contract`,
|
|
1533
|
+
hint: 'custom attachment or incompatible group(2) layouts remain on their specialized lane',
|
|
1534
|
+
}),
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
this.pipelines.set(key, pipelineEntry.pipeline);
|
|
1538
|
+
return ok(pipelineEntry.pipeline);
|
|
1539
|
+
}
|
|
1540
|
+
if (
|
|
1541
|
+
program === undefined ||
|
|
1542
|
+
selectedArtifact.material !== this.artifact.material ||
|
|
1543
|
+
selectedArtifact.layoutIdentity !== this.artifact.layoutIdentity ||
|
|
1544
|
+
selectedArtifact.receipt?.receiptIdentity !== this.artifact.receipt?.receiptIdentity
|
|
1545
|
+
) {
|
|
1546
|
+
return err(
|
|
1547
|
+
new RhiError({
|
|
1548
|
+
code: 'rhi-not-available',
|
|
1549
|
+
expected: 'selected material pipeline factory for the requested ABI artifact',
|
|
1550
|
+
hint: 'do not pair a receipt-backed custom or color variant with the legacy fixed module',
|
|
1551
|
+
}),
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1223
1554
|
const pipeline = this.device.createRenderPipeline({
|
|
1224
1555
|
label: `standard-pbr-gpu-driven.${key}`,
|
|
1225
1556
|
layout: this.pipelineLayout,
|
|
1226
1557
|
vertex: {
|
|
1227
1558
|
module: program.module,
|
|
1228
|
-
entryPoint:
|
|
1559
|
+
entryPoint: selectedArtifact.vertexEntry ?? receipt.sceneIndexEntry,
|
|
1229
1560
|
buffers: [
|
|
1230
1561
|
{
|
|
1231
1562
|
arrayStride: vertexLayout.arrayStride,
|
|
@@ -1241,7 +1572,7 @@ class StandardPbrRasterAdapter {
|
|
|
1241
1572
|
fragment: {
|
|
1242
1573
|
module: program.module,
|
|
1243
1574
|
entryPoint: 'fs_main',
|
|
1244
|
-
targets: [{ format }
|
|
1575
|
+
targets: [format, ...additionalColorFormats].map((format) => ({ format })),
|
|
1245
1576
|
},
|
|
1246
1577
|
primitive: {
|
|
1247
1578
|
topology,
|
|
@@ -1261,6 +1592,15 @@ class StandardPbrRasterAdapter {
|
|
|
1261
1592
|
return pipeline;
|
|
1262
1593
|
}
|
|
1263
1594
|
|
|
1595
|
+
/**
|
|
1596
|
+
* Drop only program-derived caches when a publication switches. Resource
|
|
1597
|
+
* buffers stay alive until their normal fence retirement, while the next
|
|
1598
|
+
* draw must obtain a PSO for the new artifact rather than reuse the old one.
|
|
1599
|
+
*/
|
|
1600
|
+
invalidatePipelineCache(): void {
|
|
1601
|
+
this.pipelines.clear();
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1264
1604
|
projectionBindGroup(
|
|
1265
1605
|
rows: Float32Array,
|
|
1266
1606
|
key?: string,
|
|
@@ -1307,6 +1647,23 @@ class StandardPbrRasterAdapter {
|
|
|
1307
1647
|
return this.writeProjectionRows(key, retained, rows);
|
|
1308
1648
|
}
|
|
1309
1649
|
|
|
1650
|
+
sceneMeshBinding(key: string): GpuDrivenSceneMeshBinding {
|
|
1651
|
+
const binding = this.stagedProjectionBindings.get(key) ?? this.projectionBindings.get(key);
|
|
1652
|
+
if (binding === undefined)
|
|
1653
|
+
throw new RhiError({
|
|
1654
|
+
code: 'rhi-not-available',
|
|
1655
|
+
expected: 'retained scene mesh projection',
|
|
1656
|
+
hint: 'prepare this batch projection before binding its lighting resources',
|
|
1657
|
+
});
|
|
1658
|
+
return {
|
|
1659
|
+
meshBuffer: binding.buffer,
|
|
1660
|
+
meshBytes: binding.capacityBytes,
|
|
1661
|
+
...(this.deformation === 'skin' && this.paletteBuffer !== undefined
|
|
1662
|
+
? { paletteBuffer: this.paletteBuffer }
|
|
1663
|
+
: {}),
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1310
1667
|
/** Stage the projection keys required by the frame being prepared. */
|
|
1311
1668
|
stageProjectionKeys(keys: ReadonlySet<string>): void {
|
|
1312
1669
|
this.stagedProjectionKeys = new Set(keys);
|
|
@@ -1541,6 +1898,7 @@ export interface PreparedGpuDrivenFrame {
|
|
|
1541
1898
|
graph: RenderGraphBuilder<RenderPipelineFrame>,
|
|
1542
1899
|
format: TextureFormat,
|
|
1543
1900
|
sampleCount: 1 | 4,
|
|
1901
|
+
additionalColorFormats?: readonly TextureFormat[],
|
|
1544
1902
|
): Result<RenderPipelineGpuDrivenProjection, RenderGraphError | RhiError>;
|
|
1545
1903
|
}
|
|
1546
1904
|
|
|
@@ -1665,8 +2023,8 @@ export function classifyRecoveryCapability(
|
|
|
1665
2023
|
export class GpuDrivenProduction {
|
|
1666
2024
|
private disposed = false;
|
|
1667
2025
|
private view: GpuDrivenView | undefined;
|
|
1668
|
-
private
|
|
1669
|
-
private skinRaster:
|
|
2026
|
+
private materialRaster: MaterialAbiRasterAdapter | undefined;
|
|
2027
|
+
private skinRaster: MaterialAbiRasterAdapter | undefined;
|
|
1670
2028
|
private readonly sceneIdentities = new WeakMap<object, number>();
|
|
1671
2029
|
private nextSceneIdentity = 1;
|
|
1672
2030
|
private filteredCache:
|
|
@@ -1680,6 +2038,7 @@ export class GpuDrivenProduction {
|
|
|
1680
2038
|
readonly activeEntityRevision?: number;
|
|
1681
2039
|
readonly activeEntityKeys?: ReadonlySet<number>;
|
|
1682
2040
|
readonly skinArtifact?: MaterialShaderArtifact;
|
|
2041
|
+
readonly materialArtifactSignature: string;
|
|
1683
2042
|
readonly value: FilteredProductionPlan;
|
|
1684
2043
|
}
|
|
1685
2044
|
| undefined;
|
|
@@ -1693,6 +2052,7 @@ export class GpuDrivenProduction {
|
|
|
1693
2052
|
readonly meshResidencyEpoch: number;
|
|
1694
2053
|
readonly membership: string;
|
|
1695
2054
|
readonly skinArtifact?: MaterialShaderArtifact;
|
|
2055
|
+
readonly materialArtifactSignature: string;
|
|
1696
2056
|
readonly value: FilteredProductionPlan;
|
|
1697
2057
|
}
|
|
1698
2058
|
| undefined;
|
|
@@ -1743,8 +2103,7 @@ export class GpuDrivenProduction {
|
|
|
1743
2103
|
readonly filtered: FilteredProductionPlan;
|
|
1744
2104
|
readonly scene: PersistentGpuDrivenState['scene'];
|
|
1745
2105
|
readonly contentRevision: number;
|
|
1746
|
-
readonly
|
|
1747
|
-
readonly skinArtifact: MaterialShaderArtifact | undefined;
|
|
2106
|
+
readonly materialArtifactSignature: string;
|
|
1748
2107
|
}
|
|
1749
2108
|
| undefined;
|
|
1750
2109
|
private validatedResidencyCache: GpuDrivenResidencyValidationCache | undefined;
|
|
@@ -1860,12 +2219,23 @@ export class GpuDrivenProduction {
|
|
|
1860
2219
|
readonly meshBySlot: ReadonlyMap<number, MeshGpuHandles>;
|
|
1861
2220
|
readonly viewBindGroupLayout: BindGroupLayout;
|
|
1862
2221
|
readonly meshResidencyEpoch: number;
|
|
1863
|
-
readonly
|
|
2222
|
+
readonly materialBindingClasses?: ReadonlyMap<string, string>;
|
|
1864
2223
|
/** Cooked producer artifact selected from the frame material catalog. */
|
|
2224
|
+
readonly materialArtifact?: MaterialShaderArtifact;
|
|
2225
|
+
/** Compatibility input for callers that have not migrated their name. */
|
|
1865
2226
|
readonly standardPbrArtifact?: MaterialShaderArtifact;
|
|
1866
2227
|
/** Cooked producer artifact for the GPU skinning variant. */
|
|
2228
|
+
readonly materialSkinArtifact?: MaterialShaderArtifact;
|
|
2229
|
+
/** Compatibility input for callers that have not migrated their name. */
|
|
1867
2230
|
readonly standardPbrSkinArtifact?: MaterialShaderArtifact;
|
|
2231
|
+
/** Per-draw producer artifacts keyed by prepared identity and receipt. */
|
|
2232
|
+
readonly materialArtifacts?: ReadonlyMap<string, MaterialShaderArtifact>;
|
|
2233
|
+
/** Per-draw shadow-pass artifacts keyed by the same prepared identity. */
|
|
2234
|
+
readonly shadowMaterialArtifacts?: ReadonlyMap<string, MaterialShaderArtifact>;
|
|
2235
|
+
readonly materialPipelineState?: PipelineState;
|
|
1868
2236
|
readonly standardPbrPipelineState?: PipelineState;
|
|
2237
|
+
/** Existing renderer owner that builds the selected artifact PSO/BGL. */
|
|
2238
|
+
readonly materialPipelineFactory?: MaterialPipelineFactory;
|
|
1869
2239
|
/** Extracted ShadowCaster ownership keyed by concrete draw item and pass. */
|
|
1870
2240
|
readonly shadowCasterDrawKeys?: ReadonlySet<string>;
|
|
1871
2241
|
/** Structured ShadowCaster ownership facts for exact pass admission. */
|
|
@@ -1905,7 +2275,7 @@ export class GpuDrivenProduction {
|
|
|
1905
2275
|
paletteUploadBytes: 0,
|
|
1906
2276
|
};
|
|
1907
2277
|
this.lane = 'cpu-semantic';
|
|
1908
|
-
this.laneReason =
|
|
2278
|
+
this.laneReason = 'capability';
|
|
1909
2279
|
this.submissionState = 'idle';
|
|
1910
2280
|
this.shadowExpectedDrawKeys = new Set();
|
|
1911
2281
|
const capability = classifyRecoveryCapability({
|
|
@@ -1918,7 +2288,6 @@ export class GpuDrivenProduction {
|
|
|
1918
2288
|
});
|
|
1919
2289
|
if (
|
|
1920
2290
|
input.scene === undefined ||
|
|
1921
|
-
input.hdrp ||
|
|
1922
2291
|
selectGpuLodLane(this.device.caps) === 'cpu' ||
|
|
1923
2292
|
capability.status === 'disabled'
|
|
1924
2293
|
) {
|
|
@@ -1953,10 +2322,18 @@ export class GpuDrivenProduction {
|
|
|
1953
2322
|
this.preparationFailure = preparationFailureInspection(preparationFailure.error);
|
|
1954
2323
|
return err(preparationFailure.error);
|
|
1955
2324
|
}
|
|
1956
|
-
const
|
|
1957
|
-
const skinArtifact = input.standardPbrSkinArtifact;
|
|
1958
|
-
const
|
|
1959
|
-
|
|
2325
|
+
const materialArtifact = input.materialArtifact ?? input.standardPbrArtifact;
|
|
2326
|
+
const skinArtifact = input.materialSkinArtifact ?? input.standardPbrSkinArtifact;
|
|
2327
|
+
const materialPipelineState = input.materialPipelineState ?? input.standardPbrPipelineState;
|
|
2328
|
+
const artifactSignature = `${materialArtifactSignature(
|
|
2329
|
+
input.materialArtifacts,
|
|
2330
|
+
materialArtifact,
|
|
2331
|
+
skinArtifact,
|
|
2332
|
+
)}|shadow=${materialArtifactSignature(input.shadowMaterialArtifacts, undefined, undefined)}|bindings=${JSON.stringify([...(input.materialBindingClasses ?? [])])}`;
|
|
2333
|
+
if (
|
|
2334
|
+
(materialArtifact === undefined && input.materialArtifacts === undefined) ||
|
|
2335
|
+
materialPipelineState === undefined
|
|
2336
|
+
) {
|
|
1960
2337
|
this.gpuOwnedEntityCount = 0;
|
|
1961
2338
|
this.laneReason = 'resource-not-ready';
|
|
1962
2339
|
if (sceneRequiresGpuDrivenArtifact(input.scene)) {
|
|
@@ -1967,22 +2344,39 @@ export class GpuDrivenProduction {
|
|
|
1967
2344
|
this.mainTotalDrawItems = Math.max(this.mainTotalDrawItems, this.blockedDrawItems);
|
|
1968
2345
|
this.lane = 'blocked';
|
|
1969
2346
|
const failure =
|
|
1970
|
-
|
|
2347
|
+
materialArtifact === undefined && input.materialArtifacts === undefined
|
|
1971
2348
|
? new GpuDrivenPreparationError('missing-material-receipt', {
|
|
1972
2349
|
reason: 'material-receipt-missing',
|
|
1973
2350
|
owner: 'material',
|
|
1974
|
-
expected: 'producer-owned
|
|
2351
|
+
expected: 'producer-owned MaterialProgramAbi GPU-driven artifact',
|
|
1975
2352
|
})
|
|
1976
2353
|
: new GpuDrivenPreparationError('resource-not-ready', {
|
|
1977
2354
|
reason: 'material-resource-missing',
|
|
1978
2355
|
owner: 'material',
|
|
1979
|
-
expected: 'producer-owned
|
|
2356
|
+
expected: 'producer-owned GPU-driven material pipeline state',
|
|
1980
2357
|
});
|
|
1981
2358
|
this.preparationFailure = preparationFailureInspection(failure);
|
|
1982
2359
|
return err(failure);
|
|
1983
2360
|
}
|
|
1984
2361
|
return ok(undefined);
|
|
1985
2362
|
}
|
|
2363
|
+
const missingArtifact = missingMaterialArtifact(
|
|
2364
|
+
scene,
|
|
2365
|
+
input.materialArtifacts,
|
|
2366
|
+
materialArtifact,
|
|
2367
|
+
skinArtifact,
|
|
2368
|
+
);
|
|
2369
|
+
if (missingArtifact !== undefined) {
|
|
2370
|
+
this.gpuOwnedEntityCount = 0;
|
|
2371
|
+
this.lane = 'blocked';
|
|
2372
|
+
this.laneReason = 'resource-not-ready';
|
|
2373
|
+
this.blockedDrawItems = scene.plan.batches.reduce(
|
|
2374
|
+
(total, batch) => total + batch.candidates.length,
|
|
2375
|
+
0,
|
|
2376
|
+
);
|
|
2377
|
+
this.preparationFailure = preparationFailureInspection(missingArtifact);
|
|
2378
|
+
return err(missingArtifact);
|
|
2379
|
+
}
|
|
1986
2380
|
const projectionFingerprint =
|
|
1987
2381
|
this.filteredCache?.source === scene.plan &&
|
|
1988
2382
|
this.filteredCache.lodProjectionFingerprint === ''
|
|
@@ -2005,7 +2399,8 @@ export class GpuDrivenProduction {
|
|
|
2005
2399
|
this.filteredCache.meshResidencyEpoch !== input.meshResidencyEpoch ||
|
|
2006
2400
|
this.filteredCache.lodProjectionFingerprint !== projectionFingerprint ||
|
|
2007
2401
|
!cachedVisibilityMatches ||
|
|
2008
|
-
this.filteredCache.skinArtifact !== skinArtifact
|
|
2402
|
+
this.filteredCache.skinArtifact !== skinArtifact ||
|
|
2403
|
+
this.filteredCache.materialArtifactSignature !== artifactSignature
|
|
2009
2404
|
) {
|
|
2010
2405
|
filtered = filteredPlan(
|
|
2011
2406
|
scene.plan,
|
|
@@ -2017,6 +2412,9 @@ export class GpuDrivenProduction {
|
|
|
2017
2412
|
input.camera,
|
|
2018
2413
|
input.activeEntityKeys,
|
|
2019
2414
|
scene.worldKeys,
|
|
2415
|
+
materialArtifact,
|
|
2416
|
+
input.materialArtifacts,
|
|
2417
|
+
input.materialBindingClasses,
|
|
2020
2418
|
);
|
|
2021
2419
|
this.filteredCache = {
|
|
2022
2420
|
source: scene.plan,
|
|
@@ -2032,6 +2430,7 @@ export class GpuDrivenProduction {
|
|
|
2032
2430
|
? {}
|
|
2033
2431
|
: { activeEntityKeys: input.activeEntityKeys }),
|
|
2034
2432
|
...(skinArtifact === undefined ? {} : { skinArtifact }),
|
|
2433
|
+
materialArtifactSignature: artifactSignature,
|
|
2035
2434
|
value: filtered,
|
|
2036
2435
|
};
|
|
2037
2436
|
this.gpuOwnedSnapshotsMaterialized = scene.slots.length;
|
|
@@ -2060,7 +2459,8 @@ export class GpuDrivenProduction {
|
|
|
2060
2459
|
this.shadowFilteredCache.lodProjectionFingerprint !== projectionFingerprint ||
|
|
2061
2460
|
this.shadowFilteredCache.meshResidencyEpoch !== input.meshResidencyEpoch ||
|
|
2062
2461
|
this.shadowFilteredCache.membership !== shadowMembership ||
|
|
2063
|
-
this.shadowFilteredCache.skinArtifact !== skinArtifact
|
|
2462
|
+
this.shadowFilteredCache.skinArtifact !== skinArtifact ||
|
|
2463
|
+
this.shadowFilteredCache.materialArtifactSignature !== artifactSignature
|
|
2064
2464
|
) {
|
|
2065
2465
|
const projectedShadowFiltered = filteredPlan(
|
|
2066
2466
|
scene.plan,
|
|
@@ -2072,6 +2472,8 @@ export class GpuDrivenProduction {
|
|
|
2072
2472
|
input.camera,
|
|
2073
2473
|
undefined,
|
|
2074
2474
|
scene.worldKeys,
|
|
2475
|
+
materialArtifact,
|
|
2476
|
+
input.materialArtifacts,
|
|
2075
2477
|
);
|
|
2076
2478
|
shadowFiltered = projectedShadowFiltered;
|
|
2077
2479
|
this.shadowFilteredCache = {
|
|
@@ -2083,6 +2485,7 @@ export class GpuDrivenProduction {
|
|
|
2083
2485
|
meshResidencyEpoch: input.meshResidencyEpoch,
|
|
2084
2486
|
membership: shadowMembership,
|
|
2085
2487
|
...(skinArtifact === undefined ? {} : { skinArtifact }),
|
|
2488
|
+
materialArtifactSignature: artifactSignature,
|
|
2086
2489
|
value: shadowFiltered,
|
|
2087
2490
|
};
|
|
2088
2491
|
}
|
|
@@ -2117,32 +2520,30 @@ export class GpuDrivenProduction {
|
|
|
2117
2520
|
filtered,
|
|
2118
2521
|
scene: scene.scene,
|
|
2119
2522
|
contentRevision: scene.scene.contentRevision,
|
|
2120
|
-
|
|
2121
|
-
skinArtifact,
|
|
2523
|
+
materialArtifactSignature: artifactSignature,
|
|
2122
2524
|
};
|
|
2123
2525
|
const previousSource = this.committedProjectionSource;
|
|
2124
2526
|
const reuseProjectionRows =
|
|
2125
2527
|
previousSource?.filtered === filtered &&
|
|
2126
2528
|
previousSource.scene === scene.scene &&
|
|
2127
2529
|
previousSource.contentRevision === scene.scene.contentRevision &&
|
|
2128
|
-
previousSource.
|
|
2129
|
-
previousSource.skinArtifact === skinArtifact;
|
|
2530
|
+
previousSource.materialArtifactSignature === artifactSignature;
|
|
2130
2531
|
if (!reuseProjectionRows) {
|
|
2131
2532
|
const slotsByPrimitive = new Map(scene.slots.map((slot) => [slot.slot, slot] as const));
|
|
2533
|
+
const representative = filtered.batches[0];
|
|
2534
|
+
if (representative === undefined) return ok(undefined);
|
|
2132
2535
|
const projection = projectStandardPbrScene({
|
|
2133
|
-
artifact:
|
|
2134
|
-
candidates:
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
}),
|
|
2139
|
-
),
|
|
2536
|
+
artifact: representative.artifact,
|
|
2537
|
+
candidates: representative.batch.candidates.flatMap((candidate) => {
|
|
2538
|
+
const slot = slotsByPrimitive.get(candidate.primitiveIndex);
|
|
2539
|
+
return slot === undefined ? [] : [sceneCandidate(slot, candidate)];
|
|
2540
|
+
}),
|
|
2140
2541
|
});
|
|
2141
2542
|
if (!projection.ok) {
|
|
2142
2543
|
return err(
|
|
2143
2544
|
new RhiError({
|
|
2144
2545
|
code: 'shader-compile-failed',
|
|
2145
|
-
expected: 'receipt-derived
|
|
2546
|
+
expected: 'receipt-derived material scene projection',
|
|
2146
2547
|
hint: 'publish one validated MaterialShaderArtifact receipt before entering the GPU lane',
|
|
2147
2548
|
}),
|
|
2148
2549
|
);
|
|
@@ -2171,26 +2572,55 @@ export class GpuDrivenProduction {
|
|
|
2171
2572
|
}),
|
|
2172
2573
|
);
|
|
2173
2574
|
}
|
|
2174
|
-
|
|
2175
|
-
|
|
2575
|
+
const representativeArtifact = materialArtifact ?? filtered.batches[0]?.artifact;
|
|
2576
|
+
if (representativeArtifact === undefined) {
|
|
2577
|
+
return err(
|
|
2578
|
+
new RhiError({
|
|
2579
|
+
code: 'rhi-not-available',
|
|
2580
|
+
expected: 'one selected MaterialProgramAbi artifact for GPU-driven batches',
|
|
2581
|
+
hint: 'keep receipt-backed draws blocked until Catalog/load publishes their artifact',
|
|
2582
|
+
}),
|
|
2583
|
+
);
|
|
2584
|
+
}
|
|
2585
|
+
const committedArtifactChanged =
|
|
2586
|
+
this.committedProjectionSource !== undefined &&
|
|
2587
|
+
this.committedProjectionSource.materialArtifactSignature !== artifactSignature;
|
|
2588
|
+
if (committedArtifactChanged) {
|
|
2589
|
+
// ShadowViewStatePool's cache token is light/view scoped and cannot see
|
|
2590
|
+
// a material publication replacement. Drop the retained projections at
|
|
2591
|
+
// the same boundary as the main PSO cache so a cache hit cannot replay
|
|
2592
|
+
// the previous shadow program or BGL with the new receipt.
|
|
2593
|
+
this.shadowBatchProjections.clear();
|
|
2594
|
+
this.shadowBatchProjectionSource = undefined;
|
|
2595
|
+
this.shadowViews?.invalidate();
|
|
2596
|
+
}
|
|
2597
|
+
if (this.committedProjectionSource?.materialArtifactSignature !== artifactSignature) {
|
|
2598
|
+
this.materialRaster?.invalidatePipelineCache();
|
|
2599
|
+
this.skinRaster?.invalidatePipelineCache();
|
|
2600
|
+
}
|
|
2601
|
+
if (this.materialRaster === undefined) {
|
|
2602
|
+
const created = MaterialAbiRasterAdapter.create({
|
|
2176
2603
|
device: this.device,
|
|
2177
|
-
pipelineState:
|
|
2178
|
-
artifact:
|
|
2604
|
+
pipelineState: materialPipelineState,
|
|
2605
|
+
artifact: representativeArtifact,
|
|
2179
2606
|
visibleBuffer,
|
|
2607
|
+
...(input.materialPipelineFactory === undefined
|
|
2608
|
+
? {}
|
|
2609
|
+
: { pipelineFactory: input.materialPipelineFactory }),
|
|
2180
2610
|
});
|
|
2181
2611
|
if (!created.ok) return created;
|
|
2182
|
-
this.
|
|
2612
|
+
this.materialRaster = created.value;
|
|
2183
2613
|
} else {
|
|
2184
|
-
const rebound = this.
|
|
2614
|
+
const rebound = this.materialRaster.ensureVisibleBuffer(visibleBuffer);
|
|
2185
2615
|
if (!rebound.ok) return rebound;
|
|
2186
2616
|
}
|
|
2187
|
-
const
|
|
2188
|
-
if (
|
|
2617
|
+
const materialRaster = this.materialRaster;
|
|
2618
|
+
if (materialRaster === undefined) {
|
|
2189
2619
|
return err(
|
|
2190
2620
|
new RhiError({
|
|
2191
2621
|
code: 'internal-error',
|
|
2192
|
-
expected: '
|
|
2193
|
-
hint: 'keep
|
|
2622
|
+
expected: 'material ABI raster adapter after prepared artifact admission',
|
|
2623
|
+
hint: 'keep the selected material artifact and PipelineState on one frame identity',
|
|
2194
2624
|
}),
|
|
2195
2625
|
);
|
|
2196
2626
|
}
|
|
@@ -2201,9 +2631,10 @@ export class GpuDrivenProduction {
|
|
|
2201
2631
|
const skinSnapshot = scene.slots.find(
|
|
2202
2632
|
(slot) => slot.slot === skinBatch.batch.candidates[0]?.primitiveIndex,
|
|
2203
2633
|
)?.snapshot.skin;
|
|
2204
|
-
const skinAllocator =
|
|
2634
|
+
const skinAllocator = materialPipelineState.skinPaletteAllocator;
|
|
2635
|
+
const selectedSkinArtifact = skinArtifact ?? skinBatch.artifact;
|
|
2205
2636
|
if (
|
|
2206
|
-
|
|
2637
|
+
selectedSkinArtifact === undefined ||
|
|
2207
2638
|
skinSnapshot === undefined ||
|
|
2208
2639
|
skinAllocator === null ||
|
|
2209
2640
|
skinAllocator === undefined
|
|
@@ -2211,20 +2642,23 @@ export class GpuDrivenProduction {
|
|
|
2211
2642
|
return err(
|
|
2212
2643
|
new RhiError({
|
|
2213
2644
|
code: 'rhi-not-available',
|
|
2214
|
-
expected: 'skinned
|
|
2645
|
+
expected: 'skinned material artifact and persistent palette owner',
|
|
2215
2646
|
hint: 'keep missing skin producer resources on the CPU deformation path',
|
|
2216
2647
|
}),
|
|
2217
2648
|
);
|
|
2218
2649
|
}
|
|
2219
2650
|
if (this.skinRaster === undefined) {
|
|
2220
|
-
const created =
|
|
2651
|
+
const created = MaterialAbiRasterAdapter.create({
|
|
2221
2652
|
device: this.device,
|
|
2222
|
-
pipelineState:
|
|
2223
|
-
artifact:
|
|
2653
|
+
pipelineState: materialPipelineState,
|
|
2654
|
+
artifact: selectedSkinArtifact,
|
|
2224
2655
|
visibleBuffer,
|
|
2225
2656
|
deformation: 'skin',
|
|
2226
2657
|
paletteBuffer: skinSnapshot.buffer,
|
|
2227
2658
|
paletteBindingWindowBytes: skinAllocator.bindingWindowBytes,
|
|
2659
|
+
...(input.materialPipelineFactory === undefined
|
|
2660
|
+
? {}
|
|
2661
|
+
: { pipelineFactory: input.materialPipelineFactory }),
|
|
2228
2662
|
});
|
|
2229
2663
|
if (!created.ok) return created;
|
|
2230
2664
|
this.skinRaster = created.value;
|
|
@@ -2257,11 +2691,11 @@ export class GpuDrivenProduction {
|
|
|
2257
2691
|
}
|
|
2258
2692
|
// A pose can change while conservative bounds, allocation and scene rows
|
|
2259
2693
|
// remain unchanged. Read the palette owner, never optional telemetry.
|
|
2260
|
-
const paletteContentRevision =
|
|
2694
|
+
const paletteContentRevision = materialPipelineState.skinPaletteAllocator?.contentRevision;
|
|
2261
2695
|
if (paletteContentRevision !== this.committedPaletteContentRevision)
|
|
2262
2696
|
shadowViewPool.invalidate();
|
|
2263
2697
|
const abortProvisionalResources = (): void => {
|
|
2264
|
-
|
|
2698
|
+
materialRaster.abortProjectionBindings();
|
|
2265
2699
|
skinRaster?.abortProjectionBindings();
|
|
2266
2700
|
shadowViewPool._abortResourceReplacement();
|
|
2267
2701
|
};
|
|
@@ -2275,13 +2709,13 @@ export class GpuDrivenProduction {
|
|
|
2275
2709
|
};
|
|
2276
2710
|
// Start the provisional resource set before any retained row is updated;
|
|
2277
2711
|
// an aborted graph can then restore the active rows from its snapshot.
|
|
2278
|
-
|
|
2712
|
+
materialRaster.stageProjectionKeys(new Set());
|
|
2279
2713
|
skinRaster?.stageProjectionKeys(new Set());
|
|
2280
2714
|
for (const prepared of filtered.batches) {
|
|
2281
2715
|
if (reuseProjectionRows) {
|
|
2282
2716
|
const deformation = prepared.batch.candidates[0]?.prepared?.identity.deformation ?? 'rigid';
|
|
2283
|
-
const raster = deformation === 'skin' ? skinRaster :
|
|
2284
|
-
const artifact =
|
|
2717
|
+
const raster = deformation === 'skin' ? skinRaster : materialRaster;
|
|
2718
|
+
const artifact = prepared.artifact;
|
|
2285
2719
|
if (raster !== undefined && artifact !== undefined)
|
|
2286
2720
|
raster.noteProjectionKey(gpuDrivenProjectionKey(prepared.batch, deformation, artifact));
|
|
2287
2721
|
continue;
|
|
@@ -2293,9 +2727,9 @@ export class GpuDrivenProduction {
|
|
|
2293
2727
|
);
|
|
2294
2728
|
if (admittedCandidates.length === 0) continue;
|
|
2295
2729
|
const deformation = admittedCandidates[0]?.prepared?.identity.deformation ?? 'rigid';
|
|
2296
|
-
const raster = deformation === 'skin' ? skinRaster :
|
|
2297
|
-
const artifact =
|
|
2298
|
-
if (raster === undefined
|
|
2730
|
+
const raster = deformation === 'skin' ? skinRaster : materialRaster;
|
|
2731
|
+
const artifact = prepared.artifact;
|
|
2732
|
+
if (raster === undefined) continue;
|
|
2299
2733
|
const projectionKey = gpuDrivenProjectionKey(prepared.batch, deformation, artifact);
|
|
2300
2734
|
raster.noteProjectionKey(projectionKey);
|
|
2301
2735
|
const candidates = admittedCandidates.flatMap((candidate) => {
|
|
@@ -2308,7 +2742,7 @@ export class GpuDrivenProduction {
|
|
|
2308
2742
|
return err(
|
|
2309
2743
|
new RhiError({
|
|
2310
2744
|
code: 'shader-compile-failed',
|
|
2311
|
-
expected: 'receipt-derived
|
|
2745
|
+
expected: 'receipt-derived material scene projection',
|
|
2312
2746
|
hint: 'keep the retained projection rows on the current material receipt',
|
|
2313
2747
|
}),
|
|
2314
2748
|
);
|
|
@@ -2342,9 +2776,10 @@ export class GpuDrivenProduction {
|
|
|
2342
2776
|
sceneIdentity,
|
|
2343
2777
|
revision: filtered.plan.revision,
|
|
2344
2778
|
meshResidencyEpoch: input.meshResidencyEpoch,
|
|
2779
|
+
materialArtifactSignature: artifactSignature,
|
|
2345
2780
|
standardPbrLayoutIdentity: this.standardPbrProjection?.layoutIdentity,
|
|
2346
2781
|
viewResourceGeneration: viewInspection.resourceGeneration,
|
|
2347
|
-
batches: filtered.batches.map(({ batch, mesh }) => [
|
|
2782
|
+
batches: filtered.batches.map(({ batch, mesh, artifact }) => [
|
|
2348
2783
|
batch.batchId,
|
|
2349
2784
|
batch.generation,
|
|
2350
2785
|
batch.visibleBase,
|
|
@@ -2369,6 +2804,12 @@ export class GpuDrivenProduction {
|
|
|
2369
2804
|
mesh.iboBytes,
|
|
2370
2805
|
mesh.indexFormat,
|
|
2371
2806
|
mesh.uvSetCount,
|
|
2807
|
+
artifact.material,
|
|
2808
|
+
artifact.layoutIdentity,
|
|
2809
|
+
artifact.receipt?.receiptIdentity,
|
|
2810
|
+
artifact.receipt?.generation,
|
|
2811
|
+
artifact.variantSet,
|
|
2812
|
+
artifact.wgsl.length,
|
|
2372
2813
|
]),
|
|
2373
2814
|
capacities: [
|
|
2374
2815
|
viewInspection.candidateCapacity,
|
|
@@ -2409,8 +2850,8 @@ export class GpuDrivenProduction {
|
|
|
2409
2850
|
standardPbrFrameResources: {
|
|
2410
2851
|
materialBindGroups: [],
|
|
2411
2852
|
materialSlotIndicesByEntity: new Map(),
|
|
2412
|
-
instancesBindGroup:
|
|
2413
|
-
materialStride:
|
|
2853
|
+
instancesBindGroup: materialRaster.instancesFrameGroup,
|
|
2854
|
+
materialStride: MATERIAL_PER_ENTITY_STRIDE,
|
|
2414
2855
|
sceneMaterialBuffer: scene.scene.materialBuffer,
|
|
2415
2856
|
},
|
|
2416
2857
|
_commitGpuResourceReplacement: () => {
|
|
@@ -2419,7 +2860,7 @@ export class GpuDrivenProduction {
|
|
|
2419
2860
|
_commitResourceReplacement: () => {
|
|
2420
2861
|
view._commitResourceReplacement();
|
|
2421
2862
|
shadowViewPool._commitResourceReplacement();
|
|
2422
|
-
|
|
2863
|
+
materialRaster.commitProjectionBindings();
|
|
2423
2864
|
skinRaster?.commitProjectionBindings();
|
|
2424
2865
|
this.committedProjectionSource = projectionSource;
|
|
2425
2866
|
this.committedPaletteContentRevision = paletteContentRevision;
|
|
@@ -2428,7 +2869,7 @@ export class GpuDrivenProduction {
|
|
|
2428
2869
|
},
|
|
2429
2870
|
_abortResourceReplacement: () => {
|
|
2430
2871
|
shadowViewPool._abortResourceReplacement();
|
|
2431
|
-
|
|
2872
|
+
materialRaster.abortProjectionBindings();
|
|
2432
2873
|
skinRaster?.abortProjectionBindings();
|
|
2433
2874
|
this.committedProjectionSource = undefined;
|
|
2434
2875
|
this.submissionState = 'aborted';
|
|
@@ -2443,8 +2884,10 @@ export class GpuDrivenProduction {
|
|
|
2443
2884
|
shadowFiltered.plan.batches.length > 0
|
|
2444
2885
|
? this.updateShadowViews(
|
|
2445
2886
|
views,
|
|
2446
|
-
|
|
2887
|
+
materialArtifact,
|
|
2447
2888
|
skinArtifact,
|
|
2889
|
+
input.materialArtifacts,
|
|
2890
|
+
input.shadowMaterialArtifacts,
|
|
2448
2891
|
shadowFiltered,
|
|
2449
2892
|
scene,
|
|
2450
2893
|
input.shadowCasterDrawKeys,
|
|
@@ -2459,21 +2902,25 @@ export class GpuDrivenProduction {
|
|
|
2459
2902
|
),
|
|
2460
2903
|
);
|
|
2461
2904
|
},
|
|
2462
|
-
project: (graph, format, sampleCount) => {
|
|
2905
|
+
project: (graph, format, sampleCount, additionalColorFormats) => {
|
|
2463
2906
|
this.indirectDrawCount = admittedRasterBatchCount(filtered.plan);
|
|
2464
2907
|
const outputs = view.addPasses(graph);
|
|
2465
2908
|
if (!outputs.ok) return outputs;
|
|
2466
|
-
if (
|
|
2909
|
+
if (materialRaster !== undefined && filtered.batches.length > 0) {
|
|
2467
2910
|
const standardProjected: Array<{
|
|
2468
2911
|
readonly batch: GpuDrivenBatch;
|
|
2469
2912
|
readonly mesh: MeshGpuHandles;
|
|
2470
2913
|
readonly materialEntityKey: number;
|
|
2471
2914
|
readonly vertex: GraphBuffer;
|
|
2472
2915
|
readonly index?: GraphBuffer;
|
|
2473
|
-
readonly
|
|
2916
|
+
readonly pipelineFor: (
|
|
2917
|
+
formats: readonly TextureFormat[],
|
|
2918
|
+
) => Result<RenderPipeline, RhiError>;
|
|
2474
2919
|
readonly meshBindGroup: BindGroup;
|
|
2475
|
-
readonly raster:
|
|
2920
|
+
readonly raster: MaterialAbiRasterAdapter;
|
|
2476
2921
|
readonly deformation: 'rigid' | 'skin';
|
|
2922
|
+
readonly clustered: boolean;
|
|
2923
|
+
readonly projectionKey: string;
|
|
2477
2924
|
}> = [];
|
|
2478
2925
|
const standardAccesses: GraphAccess[] = [
|
|
2479
2926
|
{ resource: outputs.value.primitive, usage: 'storage-read' },
|
|
@@ -2496,14 +2943,14 @@ export class GpuDrivenProduction {
|
|
|
2496
2943
|
);
|
|
2497
2944
|
if (admittedCandidates.length === 0) continue;
|
|
2498
2945
|
const deformation = admittedCandidates[0]?.prepared?.identity.deformation ?? 'rigid';
|
|
2499
|
-
const raster = deformation === 'skin' ? skinRaster :
|
|
2500
|
-
const artifact =
|
|
2501
|
-
if (raster === undefined
|
|
2946
|
+
const raster = deformation === 'skin' ? skinRaster : materialRaster;
|
|
2947
|
+
const artifact = prepared.artifact;
|
|
2948
|
+
if (raster === undefined) {
|
|
2502
2949
|
return err(
|
|
2503
2950
|
new RhiError({
|
|
2504
2951
|
code: 'rhi-not-available',
|
|
2505
|
-
expected: `${deformation === 'skin' ? 'skinned ' : ''}
|
|
2506
|
-
hint: '
|
|
2952
|
+
expected: `${deformation === 'skin' ? 'skinned ' : ''}published material ABI raster owner`,
|
|
2953
|
+
hint: 'publish the selected material artifact before entering the GPU lane',
|
|
2507
2954
|
}),
|
|
2508
2955
|
);
|
|
2509
2956
|
}
|
|
@@ -2524,7 +2971,7 @@ export class GpuDrivenProduction {
|
|
|
2524
2971
|
return err(
|
|
2525
2972
|
new RhiError({
|
|
2526
2973
|
code: 'shader-compile-failed',
|
|
2527
|
-
expected: 'receipt-derived
|
|
2974
|
+
expected: 'receipt-derived material scene projection',
|
|
2528
2975
|
hint: 'keep the frame artifact receipt and projected rows on one identity',
|
|
2529
2976
|
}),
|
|
2530
2977
|
);
|
|
@@ -2577,20 +3024,31 @@ export class GpuDrivenProduction {
|
|
|
2577
3024
|
standardAccesses.push({ resource: buffers.index, usage: 'index-read' });
|
|
2578
3025
|
}
|
|
2579
3026
|
}
|
|
2580
|
-
const
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
3027
|
+
const pipelineFor = (
|
|
3028
|
+
formats: readonly TextureFormat[],
|
|
3029
|
+
): Result<RenderPipeline, RhiError> => {
|
|
3030
|
+
return raster.pipeline(
|
|
3031
|
+
formats[0] ?? format,
|
|
3032
|
+
sampleCount,
|
|
3033
|
+
prepared.mesh.layoutProjection,
|
|
3034
|
+
// The producer receipt owns the selected shader's UV ABI. The
|
|
3035
|
+
// Standard count is only a fallback for legacy synthetic
|
|
3036
|
+
// fixtures; using it for a custom program would add aliases
|
|
3037
|
+
// the shader never declares (or omit a declared custom set).
|
|
3038
|
+
artifact.uvSetCount ?? materialPipelineState.standardPbrShaderUvSetCount,
|
|
3039
|
+
prepared.batch.key.topology,
|
|
3040
|
+
prepared.batch.key.drawKind === 'indexed' &&
|
|
3041
|
+
(prepared.batch.key.topology === 'line-strip' ||
|
|
3042
|
+
prepared.batch.key.topology === 'triangle-strip')
|
|
3043
|
+
? prepared.mesh.indexFormat
|
|
3044
|
+
: undefined,
|
|
3045
|
+
prepared.renderState,
|
|
3046
|
+
artifact,
|
|
3047
|
+
formats.slice(1),
|
|
3048
|
+
);
|
|
3049
|
+
};
|
|
3050
|
+
const initialPipeline = pipelineFor([format, ...(additionalColorFormats ?? [])]);
|
|
3051
|
+
if (!initialPipeline.ok) return initialPipeline;
|
|
2594
3052
|
standardProjected.push({
|
|
2595
3053
|
batch: prepared.batch,
|
|
2596
3054
|
mesh: prepared.mesh,
|
|
@@ -2599,10 +3057,14 @@ export class GpuDrivenProduction {
|
|
|
2599
3057
|
materialSlot.snapshot.entityKey,
|
|
2600
3058
|
),
|
|
2601
3059
|
...buffers,
|
|
2602
|
-
|
|
3060
|
+
pipelineFor,
|
|
2603
3061
|
meshBindGroup: meshBindGroup.value,
|
|
2604
3062
|
raster,
|
|
2605
3063
|
deformation,
|
|
3064
|
+
clustered: ['cluster', 'skin-cluster'].includes(
|
|
3065
|
+
resolvePipelineGroup2Contract(artifact.wgsl),
|
|
3066
|
+
),
|
|
3067
|
+
projectionKey: gpuDrivenProjectionKey(prepared.batch, deformation, artifact),
|
|
2606
3068
|
});
|
|
2607
3069
|
}
|
|
2608
3070
|
if (
|
|
@@ -2612,7 +3074,7 @@ export class GpuDrivenProduction {
|
|
|
2612
3074
|
return err(
|
|
2613
3075
|
new RhiError({
|
|
2614
3076
|
code: 'internal-error',
|
|
2615
|
-
expected: 'one actual
|
|
3077
|
+
expected: 'one actual material ABI raster dispatch for each eligible batch',
|
|
2616
3078
|
hint: 'repair the receipt-backed batch projection before encoding the frame',
|
|
2617
3079
|
}),
|
|
2618
3080
|
);
|
|
@@ -2636,10 +3098,6 @@ export class GpuDrivenProduction {
|
|
|
2636
3098
|
let currentVertex: GraphBuffer | undefined;
|
|
2637
3099
|
let currentIndex: GraphBuffer | undefined;
|
|
2638
3100
|
for (const batch of rasterDispatches) {
|
|
2639
|
-
if (currentPipeline !== batch.pipeline) {
|
|
2640
|
-
pass.setPipeline(batch.pipeline);
|
|
2641
|
-
currentPipeline = batch.pipeline;
|
|
2642
|
-
}
|
|
2643
3101
|
const globalMaterialSlots = frameResources.materialSlotIndicesByEntity.get(
|
|
2644
3102
|
batch.materialEntityKey,
|
|
2645
3103
|
);
|
|
@@ -2655,6 +3113,20 @@ export class GpuDrivenProduction {
|
|
|
2655
3113
|
hint: 'populate materialSlotIndicesByEntity from the main-pass material producer',
|
|
2656
3114
|
});
|
|
2657
3115
|
}
|
|
3116
|
+
if (
|
|
3117
|
+
frameResources.selectedMaterialSlots !== undefined &&
|
|
3118
|
+
!frameResources.selectedMaterialSlots.has(globalMaterialSlot)
|
|
3119
|
+
)
|
|
3120
|
+
continue;
|
|
3121
|
+
const selectedPipeline = batch.pipelineFor(
|
|
3122
|
+
frameResources.colorFormats ?? [format, ...(additionalColorFormats ?? [])],
|
|
3123
|
+
);
|
|
3124
|
+
if (!selectedPipeline.ok) throw selectedPipeline.error;
|
|
3125
|
+
const pipeline = selectedPipeline.value;
|
|
3126
|
+
if (currentPipeline !== pipeline) {
|
|
3127
|
+
pass.setPipeline(pipeline);
|
|
3128
|
+
currentPipeline = pipeline;
|
|
3129
|
+
}
|
|
2658
3130
|
const materialBindGroup = frameResources.materialBindGroups[globalMaterialSlot];
|
|
2659
3131
|
if (materialBindGroup === undefined) {
|
|
2660
3132
|
throw new RhiError({
|
|
@@ -2666,11 +3138,18 @@ export class GpuDrivenProduction {
|
|
|
2666
3138
|
pass.setBindGroup(1, materialBindGroup, [
|
|
2667
3139
|
globalMaterialSlot * frameResources.materialStride,
|
|
2668
3140
|
]);
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
3141
|
+
const meshBindGroup = batch.clustered
|
|
3142
|
+
? frameResources.resolveClusteredMeshBindGroup?.(
|
|
3143
|
+
batch.raster.sceneMeshBinding(batch.projectionKey),
|
|
3144
|
+
)
|
|
3145
|
+
: batch.meshBindGroup;
|
|
3146
|
+
if (meshBindGroup === undefined)
|
|
3147
|
+
throw new RhiError({
|
|
3148
|
+
code: 'rhi-not-available',
|
|
3149
|
+
expected: 'current scene-index lighting binding',
|
|
3150
|
+
hint: 'resolve clustered surface resources during the material pass',
|
|
3151
|
+
});
|
|
3152
|
+
pass.setBindGroup(2, meshBindGroup, batch.deformation === 'skin' ? [0, 0, 0] : [0]);
|
|
2674
3153
|
const visibleWindow = batch.raster.visibleWindowBindGroup(
|
|
2675
3154
|
batch.batch.visibleBase,
|
|
2676
3155
|
batch.batch.visibleCapacity,
|
|
@@ -2704,8 +3183,8 @@ export class GpuDrivenProduction {
|
|
|
2704
3183
|
return err(
|
|
2705
3184
|
new RhiError({
|
|
2706
3185
|
code: 'internal-error',
|
|
2707
|
-
expected: '
|
|
2708
|
-
hint: 'preserve the artifact
|
|
3186
|
+
expected: 'material ABI raster projection branch',
|
|
3187
|
+
hint: 'preserve the selected-artifact branch as the only raster owner',
|
|
2709
3188
|
}),
|
|
2710
3189
|
);
|
|
2711
3190
|
},
|
|
@@ -2714,8 +3193,10 @@ export class GpuDrivenProduction {
|
|
|
2714
3193
|
|
|
2715
3194
|
updateShadowViews(
|
|
2716
3195
|
views: readonly Omit<ShadowViewUpdateInput, 'sourcePlan' | 'scene'>[],
|
|
2717
|
-
artifact: MaterialShaderArtifact,
|
|
3196
|
+
artifact: MaterialShaderArtifact | undefined,
|
|
2718
3197
|
skinArtifact: MaterialShaderArtifact | undefined,
|
|
3198
|
+
materialArtifacts: ReadonlyMap<string, MaterialShaderArtifact> | undefined,
|
|
3199
|
+
shadowMaterialArtifacts: ReadonlyMap<string, MaterialShaderArtifact> | undefined,
|
|
2719
3200
|
filtered: FilteredProductionPlan,
|
|
2720
3201
|
scene: PersistentGpuDrivenState,
|
|
2721
3202
|
shadowCasterDrawKeys: ReadonlySet<string> | undefined,
|
|
@@ -2730,13 +3211,13 @@ export class GpuDrivenProduction {
|
|
|
2730
3211
|
}
|
|
2731
3212
|
const nextProjections = new Map<string, ReadonlyMap<number, GpuDrivenShadowBatchProjection>>();
|
|
2732
3213
|
const nextShadowDrawKeysByView = new Map<string, ReadonlySet<string>>();
|
|
2733
|
-
const
|
|
2734
|
-
if (
|
|
3214
|
+
const materialRaster = this.materialRaster;
|
|
3215
|
+
if (materialRaster === undefined) {
|
|
2735
3216
|
return err(
|
|
2736
3217
|
new RhiError({
|
|
2737
3218
|
code: 'internal-error',
|
|
2738
|
-
expected: '
|
|
2739
|
-
hint: 'prepare the artifact
|
|
3219
|
+
expected: 'material ABI raster adapter for shadow projection',
|
|
3220
|
+
hint: 'prepare the selected-artifact raster owner before updating shadow views',
|
|
2740
3221
|
}),
|
|
2741
3222
|
);
|
|
2742
3223
|
}
|
|
@@ -2760,11 +3241,23 @@ export class GpuDrivenProduction {
|
|
|
2760
3241
|
nextShadowDrawKeysByView.set(identityKey, previousClaims);
|
|
2761
3242
|
for (const batch of updated.value.plan.batches) {
|
|
2762
3243
|
const deformation = batch.candidates[0]?.prepared?.identity.deformation ?? 'rigid';
|
|
2763
|
-
const batchArtifact =
|
|
2764
|
-
|
|
2765
|
-
|
|
3244
|
+
const batchArtifact =
|
|
3245
|
+
preparedByBatch.get(batch.batchId)?.artifact ??
|
|
3246
|
+
artifactForPreparedCandidate(
|
|
3247
|
+
batch.candidates[0] as GpuDrivenBatch['candidates'][number],
|
|
3248
|
+
materialArtifacts,
|
|
3249
|
+
artifact,
|
|
3250
|
+
skinArtifact,
|
|
3251
|
+
);
|
|
3252
|
+
const shadowArtifact = shadowArtifactForPreparedCandidate(
|
|
3253
|
+
batch.candidates[0] as GpuDrivenBatch['candidates'][number],
|
|
3254
|
+
shadowMaterialArtifacts,
|
|
3255
|
+
batchArtifact,
|
|
3256
|
+
);
|
|
3257
|
+
const raster = deformation === 'skin' ? this.skinRaster : materialRaster;
|
|
3258
|
+
if (shadowArtifact !== undefined && raster !== undefined) {
|
|
2766
3259
|
raster.noteProjectionKey(
|
|
2767
|
-
gpuDrivenShadowProjectionKey(identityKey, batch, deformation,
|
|
3260
|
+
gpuDrivenShadowProjectionKey(identityKey, batch, deformation, shadowArtifact),
|
|
2768
3261
|
);
|
|
2769
3262
|
}
|
|
2770
3263
|
}
|
|
@@ -2822,14 +3315,19 @@ export class GpuDrivenProduction {
|
|
|
2822
3315
|
);
|
|
2823
3316
|
}
|
|
2824
3317
|
const deformation = batch.candidates[0]?.prepared?.identity.deformation ?? 'rigid';
|
|
2825
|
-
const raster = deformation === 'skin' ? this.skinRaster :
|
|
2826
|
-
const batchArtifact =
|
|
2827
|
-
|
|
3318
|
+
const raster = deformation === 'skin' ? this.skinRaster : materialRaster;
|
|
3319
|
+
const batchArtifact = prepared.artifact;
|
|
3320
|
+
const shadowArtifact = shadowArtifactForPreparedCandidate(
|
|
3321
|
+
batch.candidates[0] as GpuDrivenBatch['candidates'][number],
|
|
3322
|
+
shadowMaterialArtifacts,
|
|
3323
|
+
batchArtifact,
|
|
3324
|
+
);
|
|
3325
|
+
if (raster === undefined || batchArtifact === undefined || shadowArtifact === undefined) {
|
|
2828
3326
|
return err(
|
|
2829
3327
|
new RhiError({
|
|
2830
3328
|
code: 'rhi-not-available',
|
|
2831
|
-
expected: `${deformation === 'skin' ? 'skinned ' : ''}
|
|
2832
|
-
hint: '
|
|
3329
|
+
expected: `${deformation === 'skin' ? 'skinned ' : ''}selected shadow MaterialProgramAbi artifact`,
|
|
3330
|
+
hint: 'publish a matching shadow-pass program before entering the capable shadow lane',
|
|
2833
3331
|
}),
|
|
2834
3332
|
);
|
|
2835
3333
|
}
|
|
@@ -2842,13 +3340,13 @@ export class GpuDrivenProduction {
|
|
|
2842
3340
|
);
|
|
2843
3341
|
return [sceneCandidate(slot, candidate, materialIndex)];
|
|
2844
3342
|
});
|
|
2845
|
-
const projected = projectStandardPbrScene({ artifact:
|
|
3343
|
+
const projected = projectStandardPbrScene({ artifact: shadowArtifact, candidates });
|
|
2846
3344
|
if (!projected.ok) {
|
|
2847
3345
|
return err(
|
|
2848
3346
|
new RhiError({
|
|
2849
3347
|
code: 'shader-compile-failed',
|
|
2850
3348
|
expected: 'receipt-derived shadow scene-index projection',
|
|
2851
|
-
hint: 'publish one validated
|
|
3349
|
+
hint: 'publish one validated material receipt per shadow batch',
|
|
2852
3350
|
}),
|
|
2853
3351
|
);
|
|
2854
3352
|
}
|
|
@@ -2856,7 +3354,7 @@ export class GpuDrivenProduction {
|
|
|
2856
3354
|
identityKey,
|
|
2857
3355
|
batch,
|
|
2858
3356
|
deformation,
|
|
2859
|
-
|
|
3357
|
+
shadowArtifact,
|
|
2860
3358
|
);
|
|
2861
3359
|
raster.noteProjectionKey(projectionKey);
|
|
2862
3360
|
const meshBindGroup = raster.projectionBindGroup(
|
|
@@ -2887,16 +3385,36 @@ export class GpuDrivenProduction {
|
|
|
2887
3385
|
}),
|
|
2888
3386
|
);
|
|
2889
3387
|
}
|
|
3388
|
+
const firstWorldEntity = worldEntityKey(
|
|
3389
|
+
firstSlot.snapshot.worldId,
|
|
3390
|
+
firstSlot.snapshot.entityKey,
|
|
3391
|
+
);
|
|
3392
|
+
const shadowEntry = shadowCasterMembership?.find(
|
|
3393
|
+
(membership) =>
|
|
3394
|
+
membership.worldEntity === firstWorldEntity &&
|
|
3395
|
+
membership.materialHandle === (material.materialHandle ?? -1) &&
|
|
3396
|
+
membership.drawItemIndex === firstCandidate?.drawItemIndex,
|
|
3397
|
+
);
|
|
2890
3398
|
projections.set(batch.batchId, {
|
|
2891
3399
|
mesh: prepared.mesh,
|
|
2892
3400
|
meshBindGroup: meshBindGroup.value,
|
|
2893
3401
|
visibleBindGroup: visibleBindGroup.value,
|
|
2894
3402
|
deformation,
|
|
3403
|
+
shadowArtifact,
|
|
3404
|
+
...(shadowEntry?.vertexEntry === undefined
|
|
3405
|
+
? {}
|
|
3406
|
+
: { shadowVertexEntry: shadowEntry.vertexEntry }),
|
|
3407
|
+
...(shadowEntry?.fragmentEntry === undefined
|
|
3408
|
+
? {}
|
|
3409
|
+
: { shadowFragmentEntry: shadowEntry.fragmentEntry }),
|
|
2895
3410
|
material,
|
|
2896
3411
|
materialEntityKey: worldEntityKey(
|
|
2897
3412
|
firstSlot.snapshot.worldId,
|
|
2898
3413
|
firstSlot.snapshot.entityKey,
|
|
2899
3414
|
),
|
|
3415
|
+
vertexColorAvailable: prepared.mesh.layoutProjection.attributes.some(
|
|
3416
|
+
(attribute) => attribute.key === 'color',
|
|
3417
|
+
),
|
|
2900
3418
|
});
|
|
2901
3419
|
}
|
|
2902
3420
|
nextProjections.set(identityKey, projections);
|
|
@@ -2919,8 +3437,8 @@ export class GpuDrivenProduction {
|
|
|
2919
3437
|
this.disposed = true;
|
|
2920
3438
|
this.view?.dispose();
|
|
2921
3439
|
this.view = undefined;
|
|
2922
|
-
this.
|
|
2923
|
-
this.
|
|
3440
|
+
this.materialRaster?.dispose();
|
|
3441
|
+
this.materialRaster = undefined;
|
|
2924
3442
|
this.committedProjectionSource = undefined;
|
|
2925
3443
|
this.skinRaster?.dispose();
|
|
2926
3444
|
this.skinRaster = undefined;
|
|
@@ -3222,8 +3740,7 @@ export class GpuDrivenProduction {
|
|
|
3222
3740
|
if (draws.length === 0) {
|
|
3223
3741
|
totalDrawItems += 1;
|
|
3224
3742
|
this.cpuFallbackDrawItems += 1;
|
|
3225
|
-
const
|
|
3226
|
-
const skinReason = skinLaneReason(row.source, material);
|
|
3743
|
+
const skinReason = skinLaneReason(row.source);
|
|
3227
3744
|
if (skinReason !== undefined) {
|
|
3228
3745
|
this.cpuDeformationFallbackDrawItems += 1;
|
|
3229
3746
|
addDeformationReason(skinReason);
|
|
@@ -3248,7 +3765,7 @@ export class GpuDrivenProduction {
|
|
|
3248
3765
|
}
|
|
3249
3766
|
this.cpuFallbackDrawItems += 1;
|
|
3250
3767
|
cpuResidual = true;
|
|
3251
|
-
const skinReason = skinLaneReason(row.source
|
|
3768
|
+
const skinReason = skinLaneReason(row.source);
|
|
3252
3769
|
if (skinReason !== undefined) {
|
|
3253
3770
|
this.cpuDeformationFallbackDrawItems += 1;
|
|
3254
3771
|
addDeformationReason(skinReason);
|