@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
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// @forgeax/engine-render — WebGPU renderer lifecycle/frame interpreter.
|
|
2
2
|
// Runtime selects the backend; structured RHI errors and browser isolation stay intact.
|
|
3
|
-
|
|
4
3
|
import {
|
|
5
4
|
AssetRegistry,
|
|
6
5
|
adaptDynamicTextureDevice,
|
|
@@ -38,6 +37,7 @@ import type {
|
|
|
38
37
|
PrimitiveTopology,
|
|
39
38
|
VertexAttributeMap,
|
|
40
39
|
} from '@forgeax/engine-types';
|
|
40
|
+
import { transmissionBackdropAvailable } from './device-feature-admission';
|
|
41
41
|
|
|
42
42
|
export type { MaterialShaderManifestEntry } from '@forgeax/engine-shader';
|
|
43
43
|
|
|
@@ -61,7 +61,6 @@ import {
|
|
|
61
61
|
postProcessShaderModuleLabel,
|
|
62
62
|
} from '../fullscreen-post-process-pass';
|
|
63
63
|
import { glyphTextLayoutSystem } from '../glyph-text-layout-system';
|
|
64
|
-
import { resolveStandardPbrProgram } from '../gpu-driven/pbr-program';
|
|
65
64
|
import { GPU_SHADER_STAGE_FRAGMENT, GPU_SHADER_STAGE_VERTEX } from '../gpu-stage';
|
|
66
65
|
import type { BloomInspection } from '../inspection-types';
|
|
67
66
|
import { DeviceScope } from '../lifecycle';
|
|
@@ -147,6 +146,13 @@ import type { RhiBackendPack } from './backend-contract';
|
|
|
147
146
|
import type { BundlerOptions } from './bundler-contract';
|
|
148
147
|
import { createRendererDynamicGeometryController } from './dynamic-geometry-host';
|
|
149
148
|
import type { RendererAssemblyImplementation } from './host-contract';
|
|
149
|
+
import {
|
|
150
|
+
isEngineOwnedMaterialShader,
|
|
151
|
+
resolveMaterialPipelineBindGroups,
|
|
152
|
+
resolveMaterialPipelineShaderId,
|
|
153
|
+
resolveMaterialPipelineVertexEntry,
|
|
154
|
+
resolveRendererMaterialShaderArtifact,
|
|
155
|
+
} from './material/pipeline-helpers';
|
|
150
156
|
import {
|
|
151
157
|
allowsUnlitPreparedFallback,
|
|
152
158
|
invokeDeviceCreateShaderModule,
|
|
@@ -243,7 +249,6 @@ export {
|
|
|
243
249
|
selectPipelineLayoutForVariant,
|
|
244
250
|
shouldDeferMissingPreparedMaterialShader,
|
|
245
251
|
} from './material-shader-policy';
|
|
246
|
-
|
|
247
252
|
export {
|
|
248
253
|
createMeshSsboGrowController,
|
|
249
254
|
deriveStorageBufferCeiling,
|
|
@@ -282,6 +287,7 @@ async function makeWebGPURenderer(
|
|
|
282
287
|
internals: WebGPURendererInternals,
|
|
283
288
|
): Promise<RendererAssemblyImplementation> {
|
|
284
289
|
let disposed = false;
|
|
290
|
+
const observeTiming = observeGpuPassTimingDisabled;
|
|
285
291
|
const frameContinuations = new Set<ContinuationTerminator>();
|
|
286
292
|
type RecoveryInspection = RenderInspection['recovery'];
|
|
287
293
|
let recoveryAttempt = 0;
|
|
@@ -571,19 +577,11 @@ async function makeWebGPURenderer(
|
|
|
571
577
|
// ShaderCatalogDevice adapter (shared between the catalog and per-MaterialShader
|
|
572
578
|
// pipeline cache) is already available at this point.
|
|
573
579
|
const shaderCatalog = getShader();
|
|
574
|
-
// feat-20260528-material-shader-registration-unification M3 / w14:
|
|
575
|
-
// placeholder hardcoded installMaterialArtifact calls deleted.
|
|
576
580
|
// bug-20260601-hello-tonemap-material-register M1 (plan-strategy D-1):
|
|
577
581
|
// All material-shader entries are now registered from the manifest
|
|
578
582
|
// BEFORE the renderer is returned, so `register<MaterialAsset>` referencing
|
|
579
583
|
// an engine shader succeeds without waiting for `renderer.initialization`.
|
|
580
|
-
|
|
581
|
-
// feat-20260623-asset-payload-generic-open-registry M3 / w10: host apps
|
|
582
|
-
// that need custom loaders register them on `assets.loaders.register(...)`.
|
|
583
|
-
// feat-20260604-hdr-equirect-cube-importer-loader M4 / w16 (D-3 / AC-05):
|
|
584
|
-
// the host-injected ImportTransport (or undefined for the shipped form) is
|
|
585
|
-
// threaded into the AssetRegistry ctor -- the construction-time-
|
|
586
|
-
// only single injection point (no setter, no illegal intermediate state).
|
|
584
|
+
// Host-injected ImportTransport remains a constructor-only dependency.
|
|
587
585
|
// feat-20260705-runtime-tier2-decomposition M1 / w10 (D-1 / D-2): the sole
|
|
588
586
|
// production assembly point injects (a) the post-spawn hook
|
|
589
587
|
// `postSpawnResolveJoints` (auto-wire Skin.joints; D-1) and the audio
|
|
@@ -691,7 +689,6 @@ async function makeWebGPURenderer(
|
|
|
691
689
|
etc2: internals.device.caps.textureCompressionEtc2,
|
|
692
690
|
astc: internals.device.caps.textureCompressionAstc,
|
|
693
691
|
});
|
|
694
|
-
|
|
695
692
|
// M1 (bug-20260601-hello-tonemap-material-register D-1/D-2): prepare
|
|
696
693
|
// engine-shipped material shaders (cap gate + manifest load + registration)
|
|
697
694
|
// so they are available in ShaderCatalog before `register<MaterialAsset>`.
|
|
@@ -799,14 +796,7 @@ async function makeWebGPURenderer(
|
|
|
799
796
|
{ materialBgl: BindGroupLayout; pipelineLayout: PipelineLayout }
|
|
800
797
|
>();
|
|
801
798
|
const preparedMaterialPipelineLayoutCache = new Map<string, PipelineLayout>();
|
|
802
|
-
//
|
|
803
|
-
// BGL + pipeline layout cache. Every authored material shader owns the
|
|
804
|
-
// user-region binding order emitted by its paramSchema, plus the shared
|
|
805
|
-
// engine-injection suffix. A custom shader with four-or-fewer textures can
|
|
806
|
-
// still differ from the standard PBR order (for example, baseColorTexture
|
|
807
|
-
// followed directly by normalTexture), so it cannot safely reuse the
|
|
808
|
-
// standard material BGL. Built lazily on first request and cached by
|
|
809
|
-
// shaderId; built-in shader IDs continue to use the boot-time shared layout.
|
|
799
|
+
// Cache authored material layouts by their exact mesh and lighting contract.
|
|
810
800
|
type PerShaderMaterialLayout = {
|
|
811
801
|
materialBgl: BindGroupLayout;
|
|
812
802
|
pipelineLayout: PipelineLayout;
|
|
@@ -818,16 +808,6 @@ async function makeWebGPURenderer(
|
|
|
818
808
|
readonly layout: PerShaderMaterialLayout | null;
|
|
819
809
|
};
|
|
820
810
|
const perShaderMaterialLayoutCache = new Map<string, PerShaderMaterialLayoutCacheEntry>();
|
|
821
|
-
const isEngineOwnedMaterialShader = (materialShaderId: string): boolean =>
|
|
822
|
-
materialShaderId === 'forgeax::default-unlit' ||
|
|
823
|
-
materialShaderId === 'forgeax::default-shadow-caster' ||
|
|
824
|
-
materialShaderId === 'forgeax::sprite' ||
|
|
825
|
-
materialShaderId === 'forgeax::sprite-lit' ||
|
|
826
|
-
materialShaderId === 'forgeax::default-sprite' ||
|
|
827
|
-
materialShaderId === 'forgeax::msdf-text' ||
|
|
828
|
-
materialShaderId === 'forgeax::default-standard-pbr' ||
|
|
829
|
-
materialShaderId === 'forgeax::pbr-skin' ||
|
|
830
|
-
materialShaderId === 'forgeax::default-standard-pbr-skin';
|
|
831
811
|
// Material binding contracts are derived from WGSL source, but the lookup
|
|
832
812
|
// is also used by the per-submesh bind-group path. Cache the derived value
|
|
833
813
|
// per renderer so a frame does not re-run the comment stripping and regex
|
|
@@ -1139,16 +1119,18 @@ async function makeWebGPURenderer(
|
|
|
1139
1119
|
materialShaderId: string,
|
|
1140
1120
|
explicitParamSchema?: readonly ParamSchemaEntry[],
|
|
1141
1121
|
layoutKind: LayoutKind = 'pbr',
|
|
1122
|
+
clustered = false,
|
|
1142
1123
|
): PerShaderMaterialLayout | null => {
|
|
1143
1124
|
const cacheState = currentPipelineCacheState();
|
|
1144
1125
|
const currentState = currentPipelineState();
|
|
1145
1126
|
if (currentState === null) return null;
|
|
1146
|
-
if (isEngineOwnedMaterialShader(materialShaderId)) return null;
|
|
1127
|
+
if (isEngineOwnedMaterialShader(materialShaderId) && !clustered) return null;
|
|
1147
1128
|
const lookup = getShader().findMaterialArtifact(materialShaderId);
|
|
1148
1129
|
const source = lookup.ok ? lookup.value.source : '';
|
|
1149
1130
|
const paramSchema = lookup.ok ? lookup.value.paramSchema : explicitParamSchema;
|
|
1150
1131
|
if (paramSchema === undefined) return null;
|
|
1151
|
-
const
|
|
1132
|
+
const cacheKey = `${materialShaderId}:${layoutKind}:${clustered}`;
|
|
1133
|
+
const cached = cacheState.perShaderMaterialLayoutCache.get(cacheKey);
|
|
1152
1134
|
if (
|
|
1153
1135
|
cached?.source === source &&
|
|
1154
1136
|
cached.paramSchema === paramSchema &&
|
|
@@ -1164,8 +1146,8 @@ async function makeWebGPURenderer(
|
|
|
1164
1146
|
isStandardPbrMaterialShader(materialShaderId) ||
|
|
1165
1147
|
standardPhysicalTextureFields(paramSchema).length > 0;
|
|
1166
1148
|
const isStandardMapLayout = requiresStandardMapLayout;
|
|
1167
|
-
if (!isStandardMapLayout && isSharedMaterialUserRegionCompatible(paramSchema)) {
|
|
1168
|
-
cacheState.perShaderMaterialLayoutCache.set(
|
|
1149
|
+
if (!clustered && !isStandardMapLayout && isSharedMaterialUserRegionCompatible(paramSchema)) {
|
|
1150
|
+
cacheState.perShaderMaterialLayoutCache.set(cacheKey, {
|
|
1169
1151
|
source,
|
|
1170
1152
|
paramSchema,
|
|
1171
1153
|
layoutKind,
|
|
@@ -1182,27 +1164,27 @@ async function makeWebGPURenderer(
|
|
|
1182
1164
|
const desc = buildBindGroupLayoutDescriptor(spec, {
|
|
1183
1165
|
kind: 'pbr-material-merged',
|
|
1184
1166
|
materialParamSchema: paramSchema,
|
|
1185
|
-
caps: {
|
|
1167
|
+
caps: {
|
|
1168
|
+
storageBuffer: currentBuildDevice().caps.storageBuffer,
|
|
1169
|
+
transmissionBackdrop: transmissionBackdropAvailable(
|
|
1170
|
+
currentBuildDevice().limits.maxSampledTexturesPerShaderStage,
|
|
1171
|
+
),
|
|
1172
|
+
},
|
|
1186
1173
|
});
|
|
1187
1174
|
const bglRes = currentBuildDevice().createBindGroupLayout(desc);
|
|
1188
1175
|
if (!bglRes.ok) {
|
|
1189
1176
|
internals.errorRegistry.fire(bglRes.error);
|
|
1190
1177
|
return null;
|
|
1191
1178
|
}
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
? currentState.pbrSkinMeshBindGroupLayout
|
|
1195
|
-
: layoutKind === 'hdrp-skin'
|
|
1196
|
-
? currentState.hdrpSkinMeshBindGroupLayout
|
|
1197
|
-
: currentState.meshBindGroupLayout;
|
|
1198
|
-
if (meshLayout === null) return null;
|
|
1179
|
+
const bindGroups = resolveMaterialPipelineBindGroups(layoutKind, currentState, clustered);
|
|
1180
|
+
if (bindGroups === null) return null;
|
|
1199
1181
|
const plRes = currentBuildDevice().createPipelineLayout({
|
|
1200
1182
|
label: `pbr-pl-${materialShaderId}`,
|
|
1201
1183
|
bindGroupLayouts: [
|
|
1202
1184
|
currentState.viewBindGroupLayout,
|
|
1203
1185
|
bglRes.value,
|
|
1204
|
-
meshLayout,
|
|
1205
|
-
|
|
1186
|
+
bindGroups.meshLayout,
|
|
1187
|
+
bindGroups.instancesLayout,
|
|
1206
1188
|
],
|
|
1207
1189
|
});
|
|
1208
1190
|
if (!plRes.ok) {
|
|
@@ -1210,7 +1192,7 @@ async function makeWebGPURenderer(
|
|
|
1210
1192
|
return null;
|
|
1211
1193
|
}
|
|
1212
1194
|
const built = { materialBgl: bglRes.value, pipelineLayout: plRes.value };
|
|
1213
|
-
cacheState.perShaderMaterialLayoutCache.set(
|
|
1195
|
+
cacheState.perShaderMaterialLayoutCache.set(cacheKey, {
|
|
1214
1196
|
source,
|
|
1215
1197
|
paramSchema,
|
|
1216
1198
|
layoutKind,
|
|
@@ -1320,7 +1302,13 @@ async function makeWebGPURenderer(
|
|
|
1320
1302
|
residencyStore,
|
|
1321
1303
|
internals.pack.createShaderModule,
|
|
1322
1304
|
internals.errorRegistry,
|
|
1323
|
-
|
|
1305
|
+
candidateBuildState === undefined
|
|
1306
|
+
? requiredMaterialShaders
|
|
1307
|
+
: [
|
|
1308
|
+
...new Set([...requiredMaterialShaders, ...getShader().materialShaderIdentifiers()]),
|
|
1309
|
+
].filter(
|
|
1310
|
+
(id) => requiredMaterialShaders.includes(id) || !isEngineOwnedMaterialShader(id),
|
|
1311
|
+
),
|
|
1324
1312
|
materialShaderUvSetCounts.get('forgeax::default-standard-pbr') ?? 0,
|
|
1325
1313
|
requiredFullscreenPostProcesses,
|
|
1326
1314
|
// Publish growth and capacity together for the active or candidate device.
|
|
@@ -1333,14 +1321,12 @@ async function makeWebGPURenderer(
|
|
|
1333
1321
|
internals.meshSsboState = state;
|
|
1334
1322
|
}
|
|
1335
1323
|
},
|
|
1336
|
-
//
|
|
1337
|
-
// eagerly-compiled shadow_caster module.
|
|
1324
|
+
// Both module adapters consume the generation's eager shader cache.
|
|
1338
1325
|
(label, module) => {
|
|
1339
1326
|
getShaderModuleAdapter().seedModule(label, module);
|
|
1327
|
+
getImmediateShaderModuleAdapter().seedModule(label, module);
|
|
1340
1328
|
},
|
|
1341
|
-
//
|
|
1342
|
-
// the boot-time SPEC_CONST prewarm. Recovery builds must never put a
|
|
1343
|
-
// candidate-device PSO into the retired generation's cache.
|
|
1329
|
+
// Prewarm only the active build generation's material pipeline cache.
|
|
1344
1330
|
(key, pso, group2Contract) => {
|
|
1345
1331
|
const pipelineCache = currentPipelineCacheState().materialShaderPipelineCache;
|
|
1346
1332
|
if (!pipelineCache.has(key)) {
|
|
@@ -1475,20 +1461,21 @@ async function makeWebGPURenderer(
|
|
|
1475
1461
|
? 'pbr-skin'
|
|
1476
1462
|
: 'pbr';
|
|
1477
1463
|
const layoutKind = layoutKindOverride ?? contractLayoutKind;
|
|
1464
|
+
const gpuClustered =
|
|
1465
|
+
(layoutKind === 'gpu-driven-pbr' || layoutKind === 'gpu-driven-skin') &&
|
|
1466
|
+
(group2Contract === 'cluster' || group2Contract === 'skin-cluster');
|
|
1478
1467
|
const usesSharedBootMaterialLayout =
|
|
1479
|
-
|
|
1468
|
+
!gpuClustered &&
|
|
1469
|
+
materialShaderId !== undefined &&
|
|
1470
|
+
isEngineOwnedMaterialShader(materialShaderId);
|
|
1480
1471
|
// Built-in layouts are assembled at boot; authored layouts must not bypass
|
|
1481
1472
|
// the HDRP group(2) selector or bind the URP mesh BGL to a cluster PSO.
|
|
1482
1473
|
const perShaderLayout =
|
|
1483
|
-
!usesSharedBootMaterialLayout &&
|
|
1484
|
-
|
|
1485
|
-
layoutKind !== 'gpu-driven-pbr' &&
|
|
1486
|
-
layoutKind !== 'gpu-driven-skin' &&
|
|
1487
|
-
materialShaderId !== undefined
|
|
1488
|
-
? getOrBuildPerShaderMaterialLayout(materialShaderId, undefined, layoutKind)
|
|
1474
|
+
!usesSharedBootMaterialLayout && materialShaderId !== undefined
|
|
1475
|
+
? getOrBuildPerShaderMaterialLayout(materialShaderId, undefined, layoutKind, gpuClustered)
|
|
1489
1476
|
: null;
|
|
1490
|
-
// Authored shaders
|
|
1491
|
-
//
|
|
1477
|
+
// Authored shaders retain their own material schema and selected mesh
|
|
1478
|
+
// layout, including skin. Built-in IDs keep their boot-time layout.
|
|
1492
1479
|
const bindingContract =
|
|
1493
1480
|
materialShaderId === undefined
|
|
1494
1481
|
? 'render-material'
|
|
@@ -1634,6 +1621,7 @@ async function makeWebGPURenderer(
|
|
|
1634
1621
|
vertexEntry?: string,
|
|
1635
1622
|
authoredFragmentEntry?: string,
|
|
1636
1623
|
particleInputLanes?: number,
|
|
1624
|
+
coverageOnly = false,
|
|
1637
1625
|
): RenderPipeline | null => {
|
|
1638
1626
|
const declaredGroup2Contract = resolvePipelineGroup2Contract(entry.source);
|
|
1639
1627
|
const group2Contract =
|
|
@@ -1643,6 +1631,11 @@ async function makeWebGPURenderer(
|
|
|
1643
1631
|
(variantSet === '' || variantSet?.includes('CLUSTER_FORWARD_AVAILABLE=true') === true)
|
|
1644
1632
|
? 'cluster'
|
|
1645
1633
|
: declaredGroup2Contract;
|
|
1634
|
+
const resolvedVertexEntry = resolveMaterialPipelineVertexEntry(
|
|
1635
|
+
layoutKindOverride,
|
|
1636
|
+
vertexEntryPoint,
|
|
1637
|
+
vertexEntry,
|
|
1638
|
+
);
|
|
1646
1639
|
const ctx = buildPipelineContext(
|
|
1647
1640
|
variantSet,
|
|
1648
1641
|
materialShaderId,
|
|
@@ -1682,6 +1675,7 @@ async function makeWebGPURenderer(
|
|
|
1682
1675
|
: passKindPolicyTable[passKind]?.shape === 'depth-only'
|
|
1683
1676
|
? (depthFormatOverride ?? 'depth32float')
|
|
1684
1677
|
: (depthFormatOverride ?? DEPTH_TEXTURE_FORMAT),
|
|
1678
|
+
coverageOnly,
|
|
1685
1679
|
label,
|
|
1686
1680
|
// feat-20260604 w16-b: the shader-MODULE cache identity. Stable across
|
|
1687
1681
|
// per variant. See PipelineBuilderContext.moduleLabel.
|
|
@@ -1702,7 +1696,7 @@ async function makeWebGPURenderer(
|
|
|
1702
1696
|
: {}),
|
|
1703
1697
|
}
|
|
1704
1698
|
: undefined,
|
|
1705
|
-
|
|
1699
|
+
resolvedVertexEntry,
|
|
1706
1700
|
fragmentEntry,
|
|
1707
1701
|
undefined, // defines — none
|
|
1708
1702
|
passKind,
|
|
@@ -1766,6 +1760,8 @@ async function makeWebGPURenderer(
|
|
|
1766
1760
|
fragmentEntry?: string,
|
|
1767
1761
|
particleInputLanes?: number,
|
|
1768
1762
|
): RenderPipeline | null => {
|
|
1763
|
+
const coverageOnly =
|
|
1764
|
+
passKind === 'temporal' && variantSet?.split('+').includes('COVERAGE_ONLY=true') === true;
|
|
1769
1765
|
// WebGL2 is a downlevel backend even when the record stage asks for the
|
|
1770
1766
|
// native-capability variant string. Resolve the variant axes at this
|
|
1771
1767
|
// shared PSO seam so the shader source, BGL layout, and cache key agree on
|
|
@@ -1778,10 +1774,11 @@ async function makeWebGPURenderer(
|
|
|
1778
1774
|
(currentState !== null ? currentState.colorAttachmentFormat : 'bgra8unorm-srgb');
|
|
1779
1775
|
// feat-20260629 M4: auto-fill shaderUvSetCount from naga reflection
|
|
1780
1776
|
// (stored in materialShaderUvSetCounts during prepareMaterialShaders).
|
|
1777
|
+
const materialShaderIdForPass = resolveMaterialPipelineShaderId(materialShaderId, passKind);
|
|
1781
1778
|
const pipelineShaderId = resolveMaterialShaderBackendArtifactKey(
|
|
1782
|
-
|
|
1779
|
+
materialShaderIdForPass,
|
|
1783
1780
|
internals.device.caps.backendKind,
|
|
1784
|
-
assets.getMaterialArtifact(
|
|
1781
|
+
assets.getMaterialArtifact(materialShaderIdForPass),
|
|
1785
1782
|
);
|
|
1786
1783
|
const materialShaderLookup = getShader().findMaterialArtifact(pipelineShaderId);
|
|
1787
1784
|
const materialShaderSource = materialShaderLookup.ok
|
|
@@ -1952,6 +1949,10 @@ async function makeWebGPURenderer(
|
|
|
1952
1949
|
layoutKindOverride,
|
|
1953
1950
|
vertexEntryPoint,
|
|
1954
1951
|
additionalColorFormats,
|
|
1952
|
+
undefined,
|
|
1953
|
+
undefined,
|
|
1954
|
+
undefined,
|
|
1955
|
+
coverageOnly,
|
|
1955
1956
|
);
|
|
1956
1957
|
}
|
|
1957
1958
|
const pbrEntry = findStandardPbrEntry(effectiveVariantSet);
|
|
@@ -2009,6 +2010,10 @@ async function makeWebGPURenderer(
|
|
|
2009
2010
|
layoutKindOverride,
|
|
2010
2011
|
vertexEntryPoint,
|
|
2011
2012
|
additionalColorFormats,
|
|
2013
|
+
undefined,
|
|
2014
|
+
undefined,
|
|
2015
|
+
undefined,
|
|
2016
|
+
coverageOnly,
|
|
2012
2017
|
);
|
|
2013
2018
|
}
|
|
2014
2019
|
// feat-20260609 M4 / w31: resolve variant WGSL from manifest when
|
|
@@ -2106,6 +2111,7 @@ async function makeWebGPURenderer(
|
|
|
2106
2111
|
vertexEntry,
|
|
2107
2112
|
fragmentEntry,
|
|
2108
2113
|
particleInputLanes,
|
|
2114
|
+
coverageOnly,
|
|
2109
2115
|
);
|
|
2110
2116
|
};
|
|
2111
2117
|
const getMaterialShaderPipelineEntry = (
|
|
@@ -2241,6 +2247,7 @@ async function makeWebGPURenderer(
|
|
|
2241
2247
|
return internals.bundler?.build;
|
|
2242
2248
|
},
|
|
2243
2249
|
ssrIdentity: internals.options?.ssrIdentity,
|
|
2250
|
+
captureReflectionFallbackReadback: internals.options?.captureReflectionFallbackReadback,
|
|
2244
2251
|
get standardProfile() {
|
|
2245
2252
|
return activeProfile;
|
|
2246
2253
|
},
|
|
@@ -2260,11 +2267,8 @@ async function makeWebGPURenderer(
|
|
|
2260
2267
|
getImmediateShaderModuleAdapter().createShaderModule(descriptor),
|
|
2261
2268
|
},
|
|
2262
2269
|
profiler: internals.options?.profiler,
|
|
2263
|
-
//
|
|
2264
|
-
//
|
|
2265
|
-
// internals.context for a freshly-acquired pair) is observed by the record
|
|
2266
|
-
// stage without reconstructing the RenderSystem (RenderSystemRuntime.device
|
|
2267
|
-
// / RenderSystemInternals.context are read at frame time, not cached).
|
|
2270
|
+
// Keep device/context getters live so recovery swaps are observed by the
|
|
2271
|
+
// record stage without reconstructing the RenderSystem.
|
|
2268
2272
|
get device() {
|
|
2269
2273
|
return internals.device;
|
|
2270
2274
|
},
|
|
@@ -2329,12 +2333,13 @@ async function makeWebGPURenderer(
|
|
|
2329
2333
|
getMaterialShaderPipeline,
|
|
2330
2334
|
getMaterialShaderPipelineEntry,
|
|
2331
2335
|
getMaterialShaderBindingContract: getCachedMaterialShaderBindingContract,
|
|
2332
|
-
getMaterialShaderArtifact: (materialShaderId,
|
|
2333
|
-
|
|
2334
|
-
pipelineState?.gpuDrivenPbrPrograms,
|
|
2336
|
+
getMaterialShaderArtifact: (materialShaderId, options = {}) =>
|
|
2337
|
+
resolveRendererMaterialShaderArtifact(
|
|
2335
2338
|
materialShaderId,
|
|
2336
|
-
|
|
2337
|
-
|
|
2339
|
+
getShader(),
|
|
2340
|
+
pipelineState?.gpuDrivenPbrPrograms,
|
|
2341
|
+
options,
|
|
2342
|
+
),
|
|
2338
2343
|
getParamSchema,
|
|
2339
2344
|
getMaterialBindGroupLayout,
|
|
2340
2345
|
metrics,
|
|
@@ -2469,10 +2474,7 @@ async function makeWebGPURenderer(
|
|
|
2469
2474
|
const transformReleases = new Map<World, () => void>();
|
|
2470
2475
|
const attachmentOwner = {};
|
|
2471
2476
|
let frameId = 0;
|
|
2472
|
-
//
|
|
2473
|
-
// registry avoids invalidating a still-valid frame merely because another
|
|
2474
|
-
// submission completed in the meantime.
|
|
2475
|
-
const currentReceipts = new Set<FrameReceipt>();
|
|
2477
|
+
// Caller-owned receipts remain observable; Renderer does not retain every submitted receipt.
|
|
2476
2478
|
const issuedReceipts = new WeakSet<FrameReceipt>();
|
|
2477
2479
|
const receiptTimings = new WeakMap<FrameReceipt, Promise<VolumeTimingObservation>>();
|
|
2478
2480
|
let latestReceipt: FrameReceipt | undefined;
|
|
@@ -2792,6 +2794,7 @@ async function makeWebGPURenderer(
|
|
|
2792
2794
|
),
|
|
2793
2795
|
dynamicGeometry: dynamicGeometry.inspect(),
|
|
2794
2796
|
renderScene: renderSystem.renderScene,
|
|
2797
|
+
...(renderSystem.iblBinding === undefined ? {} : { iblBinding: renderSystem.iblBinding }),
|
|
2795
2798
|
reflectionProbes: renderSystem.reflectionProbes,
|
|
2796
2799
|
ssrDependencies: renderSystem.ssrDependencies,
|
|
2797
2800
|
ssr: renderSystem.ssr,
|
|
@@ -3530,7 +3533,6 @@ async function makeWebGPURenderer(
|
|
|
3530
3533
|
return { status: 'complete', frame };
|
|
3531
3534
|
};
|
|
3532
3535
|
timingObservationStore?.register(receipt, timingSource);
|
|
3533
|
-
currentReceipts.add(receipt);
|
|
3534
3536
|
if (timingRequested && gpuPassTimingOptions === undefined) {
|
|
3535
3537
|
receiptTimings.set(
|
|
3536
3538
|
receipt,
|
|
@@ -3592,11 +3594,11 @@ async function makeWebGPURenderer(
|
|
|
3592
3594
|
}
|
|
3593
3595
|
if (gpuPassTimingOptions !== undefined && !includesDomain) {
|
|
3594
3596
|
return timingObservationStore === undefined
|
|
3595
|
-
?
|
|
3597
|
+
? observeTiming(receipt, request, () => activeDeviceScope.generation)
|
|
3596
3598
|
: timingObservationStore.observe(receipt, request);
|
|
3597
3599
|
}
|
|
3598
3600
|
if (
|
|
3599
|
-
|
|
3601
|
+
disposed ||
|
|
3600
3602
|
!issuedReceipts.has(receipt) ||
|
|
3601
3603
|
receipt.deviceGeneration !== activeDeviceScope.generation ||
|
|
3602
3604
|
receipt.backendId !== internals.device.caps.backendKind
|
|
@@ -3726,11 +3728,11 @@ async function makeWebGPURenderer(
|
|
|
3726
3728
|
reason: 'timestamp capture was not requested for this frame',
|
|
3727
3729
|
}))
|
|
3728
3730
|
: undefined;
|
|
3731
|
+
const observation = await observeTiming(receipt, request, () => activeDeviceScope.generation);
|
|
3732
|
+
if (!observation.ok) return observation;
|
|
3729
3733
|
return ok(
|
|
3730
3734
|
Object.freeze({
|
|
3731
|
-
|
|
3732
|
-
deviceGeneration: receipt.deviceGeneration,
|
|
3733
|
-
include: Object.freeze([...request.include]),
|
|
3735
|
+
...observation.value,
|
|
3734
3736
|
...(targetReadbacks === undefined
|
|
3735
3737
|
? {}
|
|
3736
3738
|
: { targetReadbacks: Object.freeze(targetReadbacks.value) }),
|
|
@@ -3795,16 +3797,8 @@ async function makeWebGPURenderer(
|
|
|
3795
3797
|
* `disposed` latch flips up-front so a re-entrant dispose (or a draw
|
|
3796
3798
|
* that races with the cascade) short-circuits.
|
|
3797
3799
|
*
|
|
3798
|
-
*
|
|
3799
|
-
*
|
|
3800
|
-
* attempted to evict stale devices from the chromium WebGPU adapter
|
|
3801
|
-
* pool via `_internal_getRawDevice`, but the explicit destroy
|
|
3802
|
-
* accelerated pool poisoning -- after the first `dispose()` the
|
|
3803
|
-
* chromium adapter pool started recycling the just-destroyed device
|
|
3804
|
-
* into the next `requestDevice` so the NEXT test's `Engine.create`
|
|
3805
|
-
* resolved with `ready.error.code: 'device-lost'`. Cross-test isolation
|
|
3806
|
-
* moved to the vitest infra layer (`browser.isolate: true`) instead of
|
|
3807
|
-
* test code self-managing device lifecycle.
|
|
3800
|
+
* Raw-device destruction is owned by backend lifetime, not this facade.
|
|
3801
|
+
* Browser test isolation handles adapter-pool reuse between tests.
|
|
3808
3802
|
*
|
|
3809
3803
|
* Stays in sync with the `Renderer.dispose` row of the README "API index".
|
|
3810
3804
|
*/
|
|
@@ -3816,7 +3810,6 @@ async function makeWebGPURenderer(
|
|
|
3816
3810
|
for (const continuation of frameContinuations) {
|
|
3817
3811
|
continuation.terminate({ code: 'disposed' });
|
|
3818
3812
|
}
|
|
3819
|
-
currentReceipts.clear();
|
|
3820
3813
|
const cleanupFailures: RendererOperationCause[] = [];
|
|
3821
3814
|
const observationCleanupFailure = disposeReceiptObservationCaptures();
|
|
3822
3815
|
if (observationCleanupFailure !== undefined) {
|
|
@@ -4083,6 +4076,13 @@ async function makeWebGPURenderer(
|
|
|
4083
4076
|
buildPipeline,
|
|
4084
4077
|
getMaterialShaderPipeline,
|
|
4085
4078
|
getMaterialShaderPipelineEntry,
|
|
4079
|
+
getMaterialShaderArtifact: (materialShaderId, options = {}) =>
|
|
4080
|
+
resolveRendererMaterialShaderArtifact(
|
|
4081
|
+
materialShaderId,
|
|
4082
|
+
getShader(),
|
|
4083
|
+
pipelineState?.gpuDrivenPbrPrograms,
|
|
4084
|
+
options,
|
|
4085
|
+
),
|
|
4086
4086
|
getCachedMaterialShaderBindingContract,
|
|
4087
4087
|
getParamSchema,
|
|
4088
4088
|
getMaterialBindGroupLayout,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { DispatchEntry, RenderableSnapshot } from '../render-system-extract';
|
|
2
|
+
|
|
3
|
+
/** One retained index domain for auxiliary views and the display's selected draws. */
|
|
4
|
+
export function projectCaptureScene(
|
|
5
|
+
retained: {
|
|
6
|
+
readonly renderables: readonly RenderableSnapshot[];
|
|
7
|
+
readonly dispatch: readonly DispatchEntry[];
|
|
8
|
+
},
|
|
9
|
+
visible: readonly RenderableSnapshot[],
|
|
10
|
+
displayDispatch: readonly DispatchEntry[],
|
|
11
|
+
) {
|
|
12
|
+
const renderables: RenderableSnapshot[] = [];
|
|
13
|
+
const retainedIndices = new Map<number, number>();
|
|
14
|
+
const identityIndices = new Map<string, number>();
|
|
15
|
+
for (const [index, row] of retained.renderables.entries()) {
|
|
16
|
+
if (row.authorVisible === false) continue;
|
|
17
|
+
retainedIndices.set(index, renderables.length);
|
|
18
|
+
identityIndices.set(`${row.worldId}:${row.entityKey}`, renderables.length);
|
|
19
|
+
renderables.push(row);
|
|
20
|
+
}
|
|
21
|
+
const remap = (
|
|
22
|
+
entries: readonly DispatchEntry[],
|
|
23
|
+
indexFor: (index: number) => number | undefined,
|
|
24
|
+
) =>
|
|
25
|
+
entries.flatMap((entry) => {
|
|
26
|
+
const index = indexFor(entry.renderableIndex);
|
|
27
|
+
return index === undefined ? [] : [{ ...entry, renderableIndex: index }];
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
renderables,
|
|
31
|
+
captureDispatch: remap(retained.dispatch, (index) => retainedIndices.get(index)),
|
|
32
|
+
displayDispatch: remap(displayDispatch, (index) => {
|
|
33
|
+
const row = visible[index];
|
|
34
|
+
return row === undefined ? undefined : identityIndices.get(`${row.worldId}:${row.entityKey}`);
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { describe, expect, expectTypeOf, it } from 'vitest';
|
|
3
|
+
import {
|
|
4
|
+
DynamicResolution,
|
|
5
|
+
type DynamicResolutionData,
|
|
6
|
+
validateDynamicResolutionParameters,
|
|
7
|
+
} from '../dynamic-resolution';
|
|
8
|
+
|
|
9
|
+
describe('DynamicResolution authoring schema', () => {
|
|
10
|
+
it('provides deterministic defaults through the real World component path', () => {
|
|
11
|
+
const world = new World();
|
|
12
|
+
const entity = world.spawn({ component: DynamicResolution, data: {} }).unwrap();
|
|
13
|
+
|
|
14
|
+
expect(DynamicResolution.name).toBe('DynamicResolution');
|
|
15
|
+
const defaults = world.get(entity, DynamicResolution).unwrap();
|
|
16
|
+
expect(defaults).toMatchObject({ maxScale: 1 });
|
|
17
|
+
// World stores f32 fields, so the authored decimal is represented by its
|
|
18
|
+
// nearest f32 value rather than the exact JS number.
|
|
19
|
+
expect(defaults.targetGpuMs).toBeCloseTo(16.67);
|
|
20
|
+
expect(defaults.minScale).toBeCloseTo(0.67);
|
|
21
|
+
expect(DynamicResolution.fields).toMatchObject({
|
|
22
|
+
targetGpuMs: { type: 'f32', default: 16.67 },
|
|
23
|
+
minScale: { type: 'f32', default: 0.67 },
|
|
24
|
+
maxScale: { type: 'f32', default: 1 },
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it.each([
|
|
29
|
+
{ targetGpuMs: 0, minScale: 0.67, maxScale: 1, field: 'targetGpuMs' },
|
|
30
|
+
{ targetGpuMs: Number.NaN, minScale: 0.67, maxScale: 1, field: 'targetGpuMs' },
|
|
31
|
+
{ targetGpuMs: 16.67, minScale: 0.49, maxScale: 1, field: 'minScale' },
|
|
32
|
+
{ targetGpuMs: 16.67, minScale: 0.8, maxScale: 1.01, field: 'maxScale' },
|
|
33
|
+
{ targetGpuMs: 16.67, minScale: 0.9, maxScale: 0.8, field: 'minScale' },
|
|
34
|
+
])('rejects invalid $field before graph construction', (input) => {
|
|
35
|
+
const result = validateDynamicResolutionParameters(input);
|
|
36
|
+
|
|
37
|
+
expect(result.ok).toBe(false);
|
|
38
|
+
if (!result.ok) {
|
|
39
|
+
expect(result.error.code).toBe('dynamic-resolution-invalid-parameter');
|
|
40
|
+
expect(result.error.expected).toContain(input.field);
|
|
41
|
+
expect(result.error.hint).toContain(input.field);
|
|
42
|
+
expect(result.error.detail.field).toBe(input.field);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it.each([
|
|
47
|
+
{ targetGpuMs: 1, minScale: 0.5, maxScale: 0.5 },
|
|
48
|
+
{ targetGpuMs: 16.67, minScale: 0.67, maxScale: 1 },
|
|
49
|
+
{ targetGpuMs: 100, minScale: 0.5, maxScale: 1 },
|
|
50
|
+
])('accepts boundary values $minScale..$maxScale', (input) => {
|
|
51
|
+
const result = validateDynamicResolutionParameters(input);
|
|
52
|
+
expect(result).toEqual({ ok: true, value: input });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('keeps the detached authoring POD limited to the three resolution fields', () => {
|
|
56
|
+
expectTypeOf<DynamicResolutionData>().toEqualTypeOf<{
|
|
57
|
+
readonly targetGpuMs: number;
|
|
58
|
+
readonly minScale: number;
|
|
59
|
+
readonly maxScale: number;
|
|
60
|
+
}>();
|
|
61
|
+
expectTypeOf<keyof DynamicResolutionData>().toEqualTypeOf<
|
|
62
|
+
'targetGpuMs' | 'minScale' | 'maxScale'
|
|
63
|
+
>();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createWorldContext, World } from '@forgeax/engine-ecs';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { renderComponentsPlugin } from '../../plugin';
|
|
4
|
+
import { DirectionalLight, DirectionalShadowFilterValue } from '../directional-light';
|
|
5
|
+
import { validateDirectionalLightData } from '../light-helpers';
|
|
6
|
+
|
|
7
|
+
function adjacentFloat32(value: number, direction: -1 | 1): number {
|
|
8
|
+
const floats = new Float32Array([value]);
|
|
9
|
+
const bits = new Uint32Array(floats.buffer);
|
|
10
|
+
bits[0] = (bits[0] ?? 0) + direction;
|
|
11
|
+
return floats[0] ?? NaN;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('PCSS angular radius storage boundary', () => {
|
|
15
|
+
for (const shadowFilter of [
|
|
16
|
+
DirectionalShadowFilterValue.pcssMedium,
|
|
17
|
+
DirectionalShadowFilterValue.pcssHigh,
|
|
18
|
+
]) {
|
|
19
|
+
it(`accepts both authored endpoints after World spawn and set (${shadowFilter})`, async () => {
|
|
20
|
+
const world = new World();
|
|
21
|
+
const context = await createWorldContext(world, [renderComponentsPlugin()]);
|
|
22
|
+
try {
|
|
23
|
+
for (const radius of [0.0001, 0.05]) {
|
|
24
|
+
const data = { direction: [0, -1, 0], shadowFilter, shadowAngularRadius: radius };
|
|
25
|
+
expect(validateDirectionalLightData(data).ok).toBe(true);
|
|
26
|
+
const entity = world.spawn({ component: DirectionalLight, data }).unwrap();
|
|
27
|
+
const stored = world.get(entity, DirectionalLight).unwrap();
|
|
28
|
+
expect(stored.shadowAngularRadius).toBe(Math.fround(radius));
|
|
29
|
+
expect(validateDirectionalLightData(stored).ok).toBe(true);
|
|
30
|
+
world.set(entity, DirectionalLight, { shadowAngularRadius: 0.025 }).unwrap();
|
|
31
|
+
world.set(entity, DirectionalLight, { shadowAngularRadius: radius }).unwrap();
|
|
32
|
+
expect(
|
|
33
|
+
validateDirectionalLightData(world.get(entity, DirectionalLight).unwrap()).ok,
|
|
34
|
+
).toBe(true);
|
|
35
|
+
}
|
|
36
|
+
} finally {
|
|
37
|
+
await context.fiber.dispose();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
it.each([
|
|
43
|
+
adjacentFloat32(0.0001, -1),
|
|
44
|
+
adjacentFloat32(0.05, 1),
|
|
45
|
+
0,
|
|
46
|
+
-1,
|
|
47
|
+
NaN,
|
|
48
|
+
Infinity,
|
|
49
|
+
-Infinity,
|
|
50
|
+
])('rejects a radius outside the float32 endpoints: %s', (shadowAngularRadius) => {
|
|
51
|
+
const result = validateDirectionalLightData({
|
|
52
|
+
direction: [0, -1, 0],
|
|
53
|
+
shadowFilter: DirectionalShadowFilterValue.pcssHigh,
|
|
54
|
+
shadowAngularRadius,
|
|
55
|
+
});
|
|
56
|
+
expect(result.ok).toBe(false);
|
|
57
|
+
if (!result.ok) expect(result.error.code).toBe('shadow-invalid-config');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -60,8 +60,8 @@ export {
|
|
|
60
60
|
* splitLambda ∈ [0,1] — PSSM split weight (default 0.75)
|
|
61
61
|
* cascadeBlend ∈ [0,0.5] — blend width between cascades (default 0.2)
|
|
62
62
|
* mapSize >= 1 — shadow map resolution (default 2048)
|
|
63
|
-
* depthBias —
|
|
64
|
-
* normalBias —
|
|
63
|
+
* depthBias — normalized receiver depth subtraction (default 0.00001)
|
|
64
|
+
* normalBias — world-space receiver normal offset (default 0.05)
|
|
65
65
|
* shadowDistance > 0 — how far shadows reach in front of the camera,
|
|
66
66
|
* in world units (default 200). This is the sole
|
|
67
67
|
* coverage knob: the CSM cascades are PSSM-split
|
|
@@ -123,7 +123,7 @@ export const DirectionalLight = defineComponent('DirectionalLight', {
|
|
|
123
123
|
splitLambda: { type: 'f32', default: 0.75 },
|
|
124
124
|
cascadeBlend: { type: 'f32', default: 0.2 },
|
|
125
125
|
mapSize: { type: 'f32', default: 2048 },
|
|
126
|
-
depthBias: { type: 'f32', default: 0.
|
|
126
|
+
depthBias: { type: 'f32', default: 0.00001 },
|
|
127
127
|
normalBias: { type: 'f32', default: 0.05 },
|
|
128
128
|
// Sole shadow coverage knob (feat replaces nearPlane/farPlane). The PSSM
|
|
129
129
|
// near end derives from the active camera near; shadowDistance is the far
|