@forgeax/engine-render 0.1.34 → 0.1.36
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 +69 -3
- package/dist/assembly/device-feature-admission.d.ts +2 -2
- package/dist/assembly/host-contract.d.ts +3 -2
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/render-target-host.d.ts +5 -0
- package/dist/assembly/render-target-host.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts +1 -0
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
- package/dist/authoring.mjs +2 -4
- package/dist/authoring.mjs.map +1 -1
- package/dist/chunk-2OZAS3WU.mjs +3489 -0
- package/dist/chunk-2OZAS3WU.mjs.map +1 -0
- package/dist/{chunk-UI5SFODX.mjs → chunk-BZQ4CDRF.mjs} +2338 -1991
- package/dist/chunk-BZQ4CDRF.mjs.map +1 -0
- package/dist/{chunk-MKDIT2EM.mjs → chunk-CJOV2XLO.mjs} +216 -261
- package/dist/chunk-CJOV2XLO.mjs.map +1 -0
- package/dist/chunk-D6WSBXJ2.mjs +1842 -0
- package/dist/chunk-D6WSBXJ2.mjs.map +1 -0
- package/dist/{chunk-HEB564WF.mjs → chunk-DRF6OULX.mjs} +4 -4
- package/dist/{chunk-HEB564WF.mjs.map → chunk-DRF6OULX.mjs.map} +1 -1
- package/dist/{chunk-OCDLEI6I.mjs → chunk-OB5WAFYH.mjs} +117 -5
- package/dist/chunk-OB5WAFYH.mjs.map +1 -0
- package/dist/{chunk-ODZWR6JQ.mjs → chunk-OEYKKMXG.mjs} +2042 -27
- package/dist/chunk-OEYKKMXG.mjs.map +1 -0
- package/dist/chunk-RIPAEGI6.mjs +48 -0
- package/dist/chunk-RIPAEGI6.mjs.map +1 -0
- package/dist/{chunk-ELJFJ2BU.mjs → chunk-X2KA6WHM.mjs} +29 -4
- package/dist/chunk-X2KA6WHM.mjs.map +1 -0
- package/dist/{chunk-BLXGONR2.mjs → chunk-XSFB7XXQ.mjs} +51 -4
- package/dist/chunk-XSFB7XXQ.mjs.map +1 -0
- package/dist/cloud/frame-context.d.ts +2 -2
- package/dist/cloud/frame-context.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +936 -2140
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +19 -19
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/errors/render.d.ts +3 -2
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/material-context.d.ts +7 -0
- package/dist/extract/material-context.d.ts.map +1 -0
- package/dist/features/host.d.ts +5 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/particle-mesh-layout.d.ts +19 -0
- package/dist/features/particle-mesh-layout.d.ts.map +1 -0
- package/dist/features/plan.d.ts +2 -0
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/types.d.ts +8 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/glyph-text-layout-system.d.ts +1 -1
- package/dist/glyph-text-layout-system.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +1 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-ownership.d.ts +3 -3
- package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-views.d.ts +1 -1
- package/dist/gpu-driven/shadow-views.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +1 -1
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/ibl/frame-resources.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +4 -4
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +542 -9
- package/dist/index.mjs.map +1 -1
- package/dist/instances.d.ts +3 -4
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +7 -8
- package/dist/internal.mjs.map +1 -1
- package/dist/pbr-pipeline.d.ts +3 -3
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-output/lut-gpu.d.ts +2 -2
- package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -1
- package/dist/pipeline/standard-output/resources.d.ts +2 -2
- package/dist/pipeline/standard-output/resources.d.ts.map +1 -1
- package/dist/publication/contract.d.ts +77 -0
- package/dist/publication/contract.d.ts.map +1 -0
- package/dist/publication/dependencies.d.ts +7 -0
- package/dist/publication/dependencies.d.ts.map +1 -0
- package/dist/publication/prepare-geometry.d.ts +9 -0
- package/dist/publication/prepare-geometry.d.ts.map +1 -0
- package/dist/publication/programs.d.ts +5 -0
- package/dist/publication/programs.d.ts.map +1 -0
- package/dist/publication/publisher.d.ts +34 -0
- package/dist/publication/publisher.d.ts.map +1 -0
- package/dist/publication/receiver.d.ts +29 -0
- package/dist/publication/receiver.d.ts.map +1 -0
- package/dist/publication/resource-scope.d.ts +18 -0
- package/dist/publication/resource-scope.d.ts.map +1 -0
- package/dist/publication/targets.d.ts +33 -0
- package/dist/publication/targets.d.ts.map +1 -0
- package/dist/publication/video.d.ts +5 -0
- package/dist/publication/video.d.ts.map +1 -0
- package/dist/record/fold-buckets.d.ts +3 -3
- package/dist/record/fold-buckets.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +6 -6
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +5 -4
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +2 -2
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +2 -2
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +7 -9
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +3 -16
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/prepared-material-bindings.d.ts +2 -3
- package/dist/record/prepared-material-bindings.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +4 -2
- 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/sprite-instance-buffer.d.ts +31 -0
- package/dist/record/sprite-instance-buffer.d.ts.map +1 -0
- package/dist/record/target-capture-graph.d.ts +19 -0
- package/dist/record/target-capture-graph.d.ts.map +1 -0
- package/dist/record/typed-frame-graph.d.ts +2 -13
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/record/volume-params.d.ts +2 -2
- package/dist/record/volume-params.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +5 -12
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/render-contract.d.ts +4 -1
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +2 -0
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-presentation.d.ts +2 -2
- package/dist/render-system-presentation.d.ts.map +1 -1
- package/dist/render-system-projections.d.ts +3 -2
- package/dist/render-system-projections.d.ts.map +1 -1
- package/dist/render-system-residency.d.ts +2 -2
- package/dist/render-system-residency.d.ts.map +1 -1
- package/dist/render-system.d.ts +3 -7
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +8 -4
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/render-source.d.ts +42 -0
- package/dist/scene/render-source.d.ts.map +1 -0
- package/dist/scene/source-systems.d.ts +6 -0
- package/dist/scene/source-systems.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +10 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -1
- package/dist/systems/transparent-dispatch.d.ts +2 -2
- package/dist/systems/transparent-dispatch.d.ts.map +1 -1
- package/dist/systems/transparent-sort-config.d.ts +2 -1
- package/dist/systems/transparent-sort-config.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -7
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/auto-exposure-transaction.integration.test.ts +4 -9
- package/src/__tests__/device-feature-admission.unit.test.ts +3 -2
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +26 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +3 -4
- package/src/__tests__/gpu-driven-production.integration.test.ts +18 -1
- package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +23 -0
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +4 -1
- package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +0 -1
- package/src/__tests__/material-vertex-texture.browser.test.ts +7 -0
- package/src/__tests__/material-vertex-texture.dawn.test.ts +7 -0
- package/src/__tests__/material-vertex-texture.fixture.ts +31 -0
- package/src/__tests__/materials-standard-contract.unit.test.ts +0 -1
- package/src/__tests__/physical-clearcoat.integration.test.ts +23 -23
- package/src/__tests__/physical-material-budget.unit.test.ts +40 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +5 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +107 -5
- package/src/__tests__/runtime-material-program.integration.test.ts +79 -0
- package/src/__tests__/vertex-layouts-ssot.unit.test.ts +23 -3
- package/src/assembly/device-feature-admission.ts +2 -2
- package/src/assembly/host-contract.ts +5 -2
- package/src/assembly/recovery/renderer-recover.ts +9 -0
- package/src/assembly/render-target-host.ts +57 -47
- package/src/assembly/renderer-facade.ts +3 -1
- package/src/assembly/webgpu-ready.ts +1 -1
- package/src/assembly/webgpu-renderer.ts +101 -70
- package/src/assembly/webgpu-vertex-layouts.ts +2 -9
- package/src/cloud/frame-context.ts +2 -2
- package/src/device/device-scope.ts +3 -0
- package/src/device/gpu-residency.ts +26 -25
- package/src/errors/render.ts +3 -1
- package/src/extract/material-context.ts +21 -0
- package/src/features/host.ts +13 -5
- package/src/features/particle-mesh-layout.ts +25 -0
- package/src/features/plan.ts +2 -0
- package/src/features/types.ts +8 -0
- package/src/glyph-text-layout-system.ts +2 -2
- package/src/gpu-driven/production-raster.ts +9 -3
- package/src/gpu-driven/shadow-ownership.ts +3 -3
- package/src/gpu-driven/shadow-views.ts +27 -2
- package/src/gpu-driven/view-gpu.ts +5 -0
- package/src/ibl/frame-resources.ts +0 -1
- package/src/ibl/skylight-bind-group.ts +13 -25
- package/src/index.ts +9 -0
- package/src/instances.ts +9 -6
- package/src/pbr-pipeline.ts +15 -18
- package/src/pipeline/standard-output/lut-gpu.ts +3 -3
- package/src/pipeline/standard-output/resources.ts +2 -2
- package/src/publication/__tests__/derived-geometry.integration.test.ts +177 -0
- package/src/publication/__tests__/features.integration.test.ts +173 -0
- package/src/publication/__tests__/programs.unit.test.ts +25 -0
- package/src/publication/__tests__/publication.integration.test.ts +415 -0
- package/src/publication/__tests__/targets.integration.test.ts +120 -0
- package/src/publication/__tests__/video.browser.test.ts +50 -0
- package/src/publication/contract.ts +104 -0
- package/src/publication/dependencies.ts +54 -0
- package/src/publication/prepare-geometry.ts +94 -0
- package/src/publication/programs.ts +45 -0
- package/src/publication/publisher.ts +645 -0
- package/src/publication/receiver.ts +297 -0
- package/src/publication/resource-scope.ts +31 -0
- package/src/publication/targets.ts +182 -0
- package/src/publication/video.ts +33 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +15 -1
- package/src/record/__tests__/prepared-runtime-material.integration.test.ts +134 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +8 -10
- package/src/record/__tests__/video-texture-view.unit.test.ts +9 -9
- package/src/record/fold-buckets.ts +8 -5
- package/src/record/frame-snapshot.ts +6 -6
- package/src/record/frame.ts +87 -28
- package/src/record/helpers.ts +2 -2
- package/src/record/main-pass-geometry.ts +21 -2
- package/src/record/main-pass-material.ts +37 -33
- package/src/record/main-pass-sprite-draws.ts +6 -152
- package/src/record/main-pass.ts +8 -7
- package/src/record/prepared-material-bindings.ts +16 -37
- package/src/record/render-context.ts +4 -2
- package/src/record/shadow-pass.ts +65 -7
- package/src/record/sprite-instance-buffer.ts +155 -0
- package/src/record/target-capture-graph.ts +258 -0
- package/src/record/typed-frame-graph.ts +11 -207
- package/src/record/view-ubo.ts +1 -1
- package/src/record/volume-params.ts +6 -6
- package/src/recovery/render-system-candidate.ts +10 -18
- package/src/render-contract.ts +6 -1
- package/src/render-system-extract-tail.ts +48 -23
- package/src/render-system-extract.ts +11 -2
- package/src/render-system-presentation.ts +2 -2
- package/src/render-system-projections.ts +3 -2
- package/src/render-system-residency.ts +2 -2
- package/src/render-system.ts +82 -54
- package/src/scene/render-scene.ts +110 -100
- package/src/scene/render-source.ts +90 -0
- package/src/scene/source-systems.ts +33 -0
- package/src/systems/skin-palette-allocator.ts +1 -0
- package/src/systems/skin-palette-types.ts +11 -0
- package/src/systems/transparent-dispatch.ts +2 -2
- package/src/systems/transparent-sort-config.ts +3 -1
- package/src/typed-shadow-passes.ts +36 -5
- package/dist/chunk-BLXGONR2.mjs.map +0 -1
- package/dist/chunk-ELJFJ2BU.mjs.map +0 -1
- package/dist/chunk-ILQ5MEZF.mjs +0 -32
- package/dist/chunk-ILQ5MEZF.mjs.map +0 -1
- package/dist/chunk-MKDIT2EM.mjs.map +0 -1
- package/dist/chunk-OCDLEI6I.mjs.map +0 -1
- package/dist/chunk-ODZWR6JQ.mjs.map +0 -1
- package/dist/chunk-RXZ3RGOU.mjs +0 -53
- package/dist/chunk-RXZ3RGOU.mjs.map +0 -1
- package/dist/chunk-SHFUFJ5N.mjs +0 -1781
- package/dist/chunk-SHFUFJ5N.mjs.map +0 -1
- package/dist/chunk-UI5SFODX.mjs.map +0 -1
- package/dist/chunk-UINHLCZA.mjs +0 -2389
- package/dist/chunk-UINHLCZA.mjs.map +0 -1
- package/dist/chunk-WEX45HKH.mjs +0 -1524
- package/dist/chunk-WEX45HKH.mjs.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { probeVideoHighPerfUpload } from '@forgeax/engine-graphics-extras';
|
|
2
|
+
import { box3, frustum } from '@forgeax/engine-math';
|
|
2
3
|
import {
|
|
3
4
|
type BindGroup,
|
|
4
5
|
type BindGroupEntry,
|
|
@@ -24,6 +25,7 @@ import {
|
|
|
24
25
|
import { assembleMaterialWithSkylightEntries } from '../ibl/skylight-bind-group';
|
|
25
26
|
import type { InstanceBufferCacheEntry } from '../instance-buffer-cache';
|
|
26
27
|
import { instanceUploadRangesForResident } from '../instances';
|
|
28
|
+
import { deriveInstancesUnionBounds } from '../instances-derived-bounds';
|
|
27
29
|
import {
|
|
28
30
|
buildPbrMaterialUserRegionEntries,
|
|
29
31
|
createPbrSkinMeshBindGroupEntries,
|
|
@@ -643,6 +645,29 @@ function shadowCasterKeys(c: _InternalRenderPipelineContext): ReadonlySet<string
|
|
|
643
645
|
return c.gpuDrivenShadowDrawKeys ?? new Set<string>();
|
|
644
646
|
}
|
|
645
647
|
|
|
648
|
+
function shadowViewContains(source: RenderableSnapshot, planes: Float32Array | undefined): boolean {
|
|
649
|
+
if (planes === undefined || planes.length === 0 || source.localAabb === undefined) return true;
|
|
650
|
+
// The extracted mesh AABB is bind-pose geometry, not a conservative bound
|
|
651
|
+
// for the current skin palette. Keep skinned residuals until extraction owns
|
|
652
|
+
// a deformation-aware bound.
|
|
653
|
+
if (source.skin !== undefined) return true;
|
|
654
|
+
if (source.spriteInstances !== undefined) return true;
|
|
655
|
+
let worldBounds: Float32Array;
|
|
656
|
+
if (source.instances !== undefined) {
|
|
657
|
+
if (source.instances.instanceCount === 0) return false;
|
|
658
|
+
const derived = deriveInstancesUnionBounds({
|
|
659
|
+
meshAabb: source.localAabb,
|
|
660
|
+
entityWorld: source.transform.world,
|
|
661
|
+
transforms: source.instances.transforms,
|
|
662
|
+
});
|
|
663
|
+
if (derived === undefined) return true;
|
|
664
|
+
worldBounds = derived;
|
|
665
|
+
} else {
|
|
666
|
+
worldBounds = box3.transformBox3(box3.create(), source.localAabb, source.transform.world);
|
|
667
|
+
}
|
|
668
|
+
return frustum.intersectsBox(planes as frustum.Frustum, worldBounds as box3.Box3Like);
|
|
669
|
+
}
|
|
670
|
+
|
|
646
671
|
function validateShadowOwnership(
|
|
647
672
|
c: _InternalRenderPipelineContext,
|
|
648
673
|
identity: ShadowViewIdentity,
|
|
@@ -654,12 +679,39 @@ function validateShadowOwnership(
|
|
|
654
679
|
(c.gpuDrivenShadowDrawKeys === undefined || c.gpuDrivenShadowDrawKeys.size === 0)
|
|
655
680
|
)
|
|
656
681
|
return;
|
|
657
|
-
const
|
|
682
|
+
const viewPlanes = c.shadowViewPlanesByView?.get(shadowViewIdentityKey(identity));
|
|
683
|
+
const visibleRenderableIndices =
|
|
684
|
+
viewPlanes === undefined
|
|
685
|
+
? undefined
|
|
686
|
+
: new Set(
|
|
687
|
+
(c.shadowValidatedOrdered ?? c.validatedOrdered)
|
|
688
|
+
.filter((entry) => shadowViewContains(entry.source, viewPlanes))
|
|
689
|
+
.map((entry) => entry.renderableIndex),
|
|
690
|
+
);
|
|
691
|
+
const allExpectedKeys = shadowCasterKeys(c);
|
|
692
|
+
const visibleExpectedKeys =
|
|
693
|
+
visibleRenderableIndices === undefined || c.shadowCasterMembership === undefined
|
|
694
|
+
? allExpectedKeys
|
|
695
|
+
: new Set(
|
|
696
|
+
c.shadowCasterMembership
|
|
697
|
+
.filter((membership) => visibleRenderableIndices.has(membership.renderableIndex))
|
|
698
|
+
.map((membership) =>
|
|
699
|
+
gpuDrivenShadowDrawKey(
|
|
700
|
+
membership.worldEntity,
|
|
701
|
+
membership.materialHandle,
|
|
702
|
+
membership.drawItemIndex,
|
|
703
|
+
membership.passIndex,
|
|
704
|
+
),
|
|
705
|
+
),
|
|
706
|
+
);
|
|
658
707
|
const claimed = claimedKeys ?? new Set<string>();
|
|
659
708
|
const overlap = [...claimed].filter((key) => recordedKeys.has(key));
|
|
660
709
|
const union = new Set([...claimed, ...recordedKeys]);
|
|
661
|
-
const missing = [...
|
|
662
|
-
|
|
710
|
+
const missing = [...visibleExpectedKeys].filter((key) => !union.has(key));
|
|
711
|
+
// GPU ownership is projected before its own per-view culling. A claim may
|
|
712
|
+
// therefore be outside the CPU residual's conservative visible subset and
|
|
713
|
+
// is still valid as long as it belongs to the extracted caster membership.
|
|
714
|
+
const unexpectedClaims = [...claimed].filter((key) => !allExpectedKeys.has(key));
|
|
663
715
|
// `shadowCasterMembership` describes the concrete draws that the GPU
|
|
664
716
|
// producer is eligible to claim. A renderable that stays on the CPU lane
|
|
665
717
|
// (for example a clustered frame without a compatible GPU-driven PBR
|
|
@@ -674,7 +726,7 @@ function validateShadowOwnership(
|
|
|
674
726
|
throw new RhiError({
|
|
675
727
|
code: 'internal-error',
|
|
676
728
|
expected: `disjoint ShadowCaster ownership for ${shadowViewIdentityKey(identity)}`,
|
|
677
|
-
hint: `claimed=${[...claimed].sort().join(',')} recorded=${[...recordedKeys].sort().join(',')} missing=${missing.sort().join(',')} unexpectedClaims=${unexpectedClaims.sort().join(',')}`,
|
|
729
|
+
hint: `expected=${[...allExpectedKeys].sort().join(',')} visibleExpected=${[...visibleExpectedKeys].sort().join(',')} claimed=${[...claimed].sort().join(',')} recorded=${[...recordedKeys].sort().join(',')} missing=${missing.sort().join(',')} unexpectedClaims=${unexpectedClaims.sort().join(',')}`,
|
|
678
730
|
});
|
|
679
731
|
}
|
|
680
732
|
|
|
@@ -842,9 +894,11 @@ export function encodeDirectionalShadowPass(
|
|
|
842
894
|
cascadeIndex: number,
|
|
843
895
|
viewport: { readonly x: number; readonly y: number; readonly w: number; readonly h: number },
|
|
844
896
|
drawFeatures?: (view: BindGroup) => void,
|
|
897
|
+
forceRaster = false,
|
|
845
898
|
): void {
|
|
846
899
|
const directionalIdentity = { kind: 'directional' as const, index: cascadeIndex };
|
|
847
900
|
if (
|
|
901
|
+
!forceRaster &&
|
|
848
902
|
(c.gpuDrivenShadowViews?.cacheHit(directionalIdentity) === true ||
|
|
849
903
|
(c.gpuDrivenShadowViews === undefined && c.directionalShadowCacheReuse)) &&
|
|
850
904
|
drawFeatures === undefined
|
|
@@ -941,6 +995,8 @@ export function encodeDirectionalShadowPass(
|
|
|
941
995
|
}),
|
|
942
996
|
shadowShaderMap(c),
|
|
943
997
|
claimedKeys,
|
|
998
|
+
false,
|
|
999
|
+
c.shadowViewPlanesByView?.get(shadowViewIdentityKey(directionalIdentity)),
|
|
944
1000
|
);
|
|
945
1001
|
validateShadowOwnership(c, directionalIdentity, claimedKeys, recordedKeys);
|
|
946
1002
|
c.frameState.directionalShadowCacheRecorded =
|
|
@@ -1047,6 +1103,7 @@ export function encodePointShadowPass(
|
|
|
1047
1103
|
shadowShaderMap(c),
|
|
1048
1104
|
claimedKeys,
|
|
1049
1105
|
true,
|
|
1106
|
+
c.shadowViewPlanesByView?.get(shadowViewIdentityKey(pointIdentity)),
|
|
1050
1107
|
);
|
|
1051
1108
|
validateShadowOwnership(c, pointIdentity, claimedKeys, recordedKeys);
|
|
1052
1109
|
}
|
|
@@ -1171,6 +1228,8 @@ export function encodeSpotShadowPass(
|
|
|
1171
1228
|
}),
|
|
1172
1229
|
shadowShaderMap(c),
|
|
1173
1230
|
claimedKeys,
|
|
1231
|
+
false,
|
|
1232
|
+
c.shadowViewPlanesByView?.get(shadowViewIdentityKey(spotIdentity)),
|
|
1174
1233
|
);
|
|
1175
1234
|
validateShadowOwnership(c, spotIdentity, claimedKeys, recordedKeys);
|
|
1176
1235
|
}
|
|
@@ -1303,6 +1362,7 @@ export function recordShadowCasterDraws(
|
|
|
1303
1362
|
shadowDispatchByRenderableIdx: ShadowDispatchMap,
|
|
1304
1363
|
gpuDrivenShadowDrawKeys: ReadonlySet<string> | undefined,
|
|
1305
1364
|
reflected = false,
|
|
1365
|
+
viewPlanes?: Float32Array,
|
|
1306
1366
|
): ReadonlySet<string> {
|
|
1307
1367
|
const {
|
|
1308
1368
|
runtime,
|
|
@@ -1330,6 +1390,7 @@ export function recordShadowCasterDraws(
|
|
|
1330
1390
|
for (let i = 0; i < validatedOrdered.length; i++) {
|
|
1331
1391
|
const entry = validatedOrdered[i];
|
|
1332
1392
|
if (entry === undefined) continue;
|
|
1393
|
+
if (!shadowViewContains(entry.source, viewPlanes)) continue;
|
|
1333
1394
|
|
|
1334
1395
|
// feat-20260609 M2: skip entities that don't match the pass selector.
|
|
1335
1396
|
if (matchedIndices !== null && !matchedIndices.has(entry.renderableIndex)) continue;
|
|
@@ -1874,7 +1935,6 @@ function ensureSpotShadowMaterialBg(c: _InternalRenderPipelineContext): BindGrou
|
|
|
1874
1935
|
prefilterView: fb.prefilterView,
|
|
1875
1936
|
prefilterSampler: fb.sampler,
|
|
1876
1937
|
brdfLutView: fb.brdfLutView,
|
|
1877
|
-
brdfLutSampler: fb.sampler,
|
|
1878
1938
|
intensityBuffer: fb.intensityBuffer,
|
|
1879
1939
|
},
|
|
1880
1940
|
transmissionBackdropAvailable(runtime.device.limits.maxSampledTexturesPerShaderStage)
|
|
@@ -1956,7 +2016,6 @@ function ensureGpuDrivenAlphaMaskMaterialBg(
|
|
|
1956
2016
|
prefilterView: fallback.prefilterView,
|
|
1957
2017
|
prefilterSampler: fallback.sampler,
|
|
1958
2018
|
brdfLutView: fallback.brdfLutView,
|
|
1959
|
-
brdfLutSampler: fallback.sampler,
|
|
1960
2019
|
intensityBuffer: fallback.intensityBuffer,
|
|
1961
2020
|
} satisfies import('../ibl/skylight-bind-group').SkylightBindGroupResources;
|
|
1962
2021
|
const deps: PerSubmeshMaterialBgDeps = {
|
|
@@ -2022,7 +2081,6 @@ function ensureGpuDrivenCustomShadowMaterialBg(
|
|
|
2022
2081
|
prefilterView: fallback.prefilterView,
|
|
2023
2082
|
prefilterSampler: fallback.sampler,
|
|
2024
2083
|
brdfLutView: fallback.brdfLutView,
|
|
2025
|
-
brdfLutSampler: fallback.sampler,
|
|
2026
2084
|
intensityBuffer: fallback.intensityBuffer,
|
|
2027
2085
|
} satisfies import('../ibl/skylight-bind-group').SkylightBindGroupResources;
|
|
2028
2086
|
const deps: PerSubmeshMaterialBgDeps = {
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { type Buffer, RhiError } from '@forgeax/engine-rhi';
|
|
2
|
+
import { GpuBuffer } from '../gpu-resource';
|
|
3
|
+
import {
|
|
4
|
+
GPU_BUFFER_USAGE_COPY_DST,
|
|
5
|
+
GPU_BUFFER_USAGE_STORAGE,
|
|
6
|
+
GPU_BUFFER_USAGE_UNIFORM,
|
|
7
|
+
} from '../gpu-usage';
|
|
8
|
+
import type { InstanceBufferCacheEntry } from '../instance-buffer-cache';
|
|
9
|
+
import type { SpriteInstancesSnapshot } from '../render-system-extract';
|
|
10
|
+
import { worldEntityKey } from './frame-snapshot';
|
|
11
|
+
import type { _InternalRenderPipelineContext } from './render-context';
|
|
12
|
+
|
|
13
|
+
export type { SpriteInstancesSnapshot } from '../render-system-extract';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Build the interleaved sprite instance payload consumed by this record owner.
|
|
17
|
+
* The extract stage validates the two packed arrays before they reach this
|
|
18
|
+
* function, so the record path has one source of count-mismatch errors.
|
|
19
|
+
*/
|
|
20
|
+
export function interleaveSpriteInstanceBuffer(
|
|
21
|
+
transforms: Float32Array,
|
|
22
|
+
regions: Float32Array,
|
|
23
|
+
includePrevious = false,
|
|
24
|
+
): Float32Array {
|
|
25
|
+
const count = transforms.length / 16;
|
|
26
|
+
const stride = includePrevious ? 36 : 20;
|
|
27
|
+
const regionOffset = includePrevious ? 32 : 16;
|
|
28
|
+
const out = new Float32Array(count * stride);
|
|
29
|
+
for (let i = 0; i < count; i++) {
|
|
30
|
+
const dstBase = i * stride;
|
|
31
|
+
const transformBase = i * 16;
|
|
32
|
+
const regionBase = i * 4;
|
|
33
|
+
for (let k = 0; k < 16; k++) out[dstBase + k] = transforms[transformBase + k] ?? 0;
|
|
34
|
+
if (includePrevious) {
|
|
35
|
+
for (let k = 0; k < 16; k++) {
|
|
36
|
+
out[dstBase + 16 + k] = transforms[transformBase + k] ?? 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (let k = 0; k < 4; k++) {
|
|
40
|
+
out[dstBase + regionOffset + k] = regions[regionBase + k] ?? 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Check whether the existing per-entity sprite buffer still matches the
|
|
48
|
+
* extract snapshot and interleaved byte count.
|
|
49
|
+
*/
|
|
50
|
+
export function spriteInstancesCacheHit(
|
|
51
|
+
entry: InstanceBufferCacheEntry | undefined,
|
|
52
|
+
snapshot: SpriteInstancesSnapshot,
|
|
53
|
+
requestedBytes: number,
|
|
54
|
+
): boolean {
|
|
55
|
+
return (
|
|
56
|
+
entry !== undefined &&
|
|
57
|
+
entry.uploadedArchVersion === snapshot.archVersion &&
|
|
58
|
+
entry.uploadedByteLength === requestedBytes
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* feat-20260704 M3/w19: resolve the interleaved SpriteInstances (@group(3))
|
|
64
|
+
* buffer + instanceCount for a sprite entity in the LDR blend sub-pass,
|
|
65
|
+
* extracted verbatim from recordSpriteEntityDraws. Uploads the interleaved
|
|
66
|
+
* mat4 + per-instance UV region transforms (cache-keyed on the snapshot); on
|
|
67
|
+
* over-cap fires the structured limit-exceeded error and leaves the passed-in
|
|
68
|
+
* fallback buffer/count. Returns the resolved (or unchanged) buffer + count.
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export function resolveSpriteInstancesBuffer(
|
|
73
|
+
c: Pick<_InternalRenderPipelineContext, 'runtime' | 'frameState'>,
|
|
74
|
+
spriteEntry: _InternalRenderPipelineContext['validatedOrdered'][number],
|
|
75
|
+
fallbackBuffer: Buffer,
|
|
76
|
+
fallbackCount: number,
|
|
77
|
+
): { buffer: Buffer; count: number } {
|
|
78
|
+
const { runtime, frameState } = c;
|
|
79
|
+
let buffer = fallbackBuffer;
|
|
80
|
+
let count = fallbackCount;
|
|
81
|
+
const spriteInstancesSnap: SpriteInstancesSnapshot | undefined =
|
|
82
|
+
spriteEntry.source.spriteInstances;
|
|
83
|
+
if (spriteInstancesSnap !== undefined) {
|
|
84
|
+
if (spriteInstancesSnap.instanceCount === 0) return { buffer, count: 0 };
|
|
85
|
+
const uniformFallback = runtime.device.caps.storageBuffer === false;
|
|
86
|
+
const interleaved = interleaveSpriteInstanceBuffer(
|
|
87
|
+
spriteInstancesSnap.transforms,
|
|
88
|
+
spriteInstancesSnap.regions,
|
|
89
|
+
!uniformFallback,
|
|
90
|
+
);
|
|
91
|
+
const requestedBytes = interleaved.byteLength;
|
|
92
|
+
const cap = runtime.device.limits.maxStorageBufferBindingSize;
|
|
93
|
+
if (typeof cap === 'number' && requestedBytes > cap) {
|
|
94
|
+
runtime.errorRegistry.fire(
|
|
95
|
+
new RhiError({
|
|
96
|
+
code: 'limit-exceeded',
|
|
97
|
+
expected: `requestedBytes (${requestedBytes}) <= maxStorageBufferBindingSize (${cap})`,
|
|
98
|
+
hint: 'reduce SpriteInstances instance count to fit within device.limits.maxStorageBufferBindingSize (144 bytes per instance: current mat4 64B + previous mat4 64B + region 16B)',
|
|
99
|
+
detail: {
|
|
100
|
+
maxStorageBufferBindingSize: cap,
|
|
101
|
+
requestedBytes,
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
const cachedSpriteInst = frameState.instanceBuffers.get(
|
|
107
|
+
worldEntityKey(spriteEntry.source.worldId, spriteInstancesSnap.cacheKey),
|
|
108
|
+
);
|
|
109
|
+
let activeSpriteInst: InstanceBufferCacheEntry | null = null;
|
|
110
|
+
if (spriteInstancesCacheHit(cachedSpriteInst, spriteInstancesSnap, requestedBytes)) {
|
|
111
|
+
activeSpriteInst = cachedSpriteInst ?? null;
|
|
112
|
+
} else if (requestedBytes > 0) {
|
|
113
|
+
const bufRes = runtime.device.createBuffer({
|
|
114
|
+
size: requestedBytes,
|
|
115
|
+
usage:
|
|
116
|
+
(uniformFallback ? GPU_BUFFER_USAGE_UNIFORM : GPU_BUFFER_USAGE_STORAGE) |
|
|
117
|
+
GPU_BUFFER_USAGE_COPY_DST,
|
|
118
|
+
mappedAtCreation: false,
|
|
119
|
+
});
|
|
120
|
+
if (!bufRes.ok) {
|
|
121
|
+
runtime.errorRegistry.fire(bufRes.error);
|
|
122
|
+
} else {
|
|
123
|
+
if (cachedSpriteInst !== undefined && !cachedSpriteInst.buffer.isDestroyed) {
|
|
124
|
+
const r = cachedSpriteInst.buffer.destroy();
|
|
125
|
+
if (!r.ok) runtime.errorRegistry.fire(r.error);
|
|
126
|
+
}
|
|
127
|
+
const newBuf = new GpuBuffer(runtime.device, bufRes.value);
|
|
128
|
+
activeSpriteInst = {
|
|
129
|
+
buffer: newBuf,
|
|
130
|
+
uploadedArchVersion: spriteInstancesSnap.archVersion,
|
|
131
|
+
uploadedByteLength: requestedBytes,
|
|
132
|
+
};
|
|
133
|
+
frameState.instanceBuffers.set(
|
|
134
|
+
worldEntityKey(spriteEntry.source.worldId, spriteInstancesSnap.cacheKey),
|
|
135
|
+
activeSpriteInst,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (activeSpriteInst !== null && requestedBytes > 0) {
|
|
140
|
+
const writeRes = runtime.device.queue.writeBuffer(
|
|
141
|
+
activeSpriteInst.buffer.handle,
|
|
142
|
+
0,
|
|
143
|
+
interleaved,
|
|
144
|
+
);
|
|
145
|
+
if (!writeRes.ok) {
|
|
146
|
+
runtime.errorRegistry.fire(writeRes.error);
|
|
147
|
+
} else {
|
|
148
|
+
buffer = activeSpriteInst.buffer.handle;
|
|
149
|
+
count = spriteInstancesSnap.instanceCount;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return { buffer, count };
|
|
155
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import type { RenderGraphBuilder, RenderGraphError } from '@forgeax/engine-render-graph';
|
|
2
|
+
import type { Texture, TextureView } from '@forgeax/engine-rhi';
|
|
3
|
+
import { ok, type Result } from '@forgeax/engine-types';
|
|
4
|
+
import type { RenderPipelineFrame } from '../render-pipeline';
|
|
5
|
+
import type { RenderTarget } from '../targets/contracts';
|
|
6
|
+
import { resolveRenderTargetMaterialSource } from '../targets/material-source';
|
|
7
|
+
import type { RenderTargetPhysical } from '../targets/physical';
|
|
8
|
+
import { encodeMainPass } from './main-pass';
|
|
9
|
+
import type { _InternalRenderPipelineContext } from './render-context';
|
|
10
|
+
import type { ReflectionProbeGraphState } from './typed-frame-graph';
|
|
11
|
+
|
|
12
|
+
export interface CubeCaptureGraphWork {
|
|
13
|
+
readonly target: RenderTarget;
|
|
14
|
+
readonly faceIndex: number;
|
|
15
|
+
readonly physical: RenderTargetPhysical;
|
|
16
|
+
readonly faceCamera: import('../render-contract').CameraSnapshot;
|
|
17
|
+
readonly viewBindGroupDynamicOffset: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CubeCaptureGraphState {
|
|
21
|
+
work: readonly CubeCaptureGraphWork[];
|
|
22
|
+
reflectionProbes?: ReflectionProbeGraphState;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function renderTargetMipCount(work: CubeCaptureGraphWork): number {
|
|
26
|
+
const { descriptor } = work.physical;
|
|
27
|
+
return descriptor.mipLevels === 1
|
|
28
|
+
? 1
|
|
29
|
+
: Math.floor(Math.log2(Math.max(descriptor.width, descriptor.height))) + 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function addTargetCaptureGraphPasses(
|
|
33
|
+
builder: RenderGraphBuilder<RenderPipelineFrame>,
|
|
34
|
+
state: CubeCaptureGraphState,
|
|
35
|
+
): Result<void, RenderGraphError> {
|
|
36
|
+
for (let index = 0; index < state.work.length; index += 1) {
|
|
37
|
+
const slot = index;
|
|
38
|
+
const initial = state.work[slot];
|
|
39
|
+
if (initial === undefined) continue;
|
|
40
|
+
const current = (): CubeCaptureGraphWork => state.work[slot] ?? initial;
|
|
41
|
+
const texture = builder.importTexture(
|
|
42
|
+
`cube-capture.${slot}.texture`,
|
|
43
|
+
{
|
|
44
|
+
format: initial.physical.descriptor.format,
|
|
45
|
+
size: {
|
|
46
|
+
width: initial.physical.descriptor.width,
|
|
47
|
+
height: initial.physical.descriptor.height,
|
|
48
|
+
depthOrArrayLayers:
|
|
49
|
+
initial.physical.descriptor.sampleCount === 4 ||
|
|
50
|
+
initial.physical.descriptor.shape === '2d'
|
|
51
|
+
? 1
|
|
52
|
+
: 6,
|
|
53
|
+
},
|
|
54
|
+
mipLevelCount:
|
|
55
|
+
initial.physical.descriptor.sampleCount === 4 ? 1 : renderTargetMipCount(initial),
|
|
56
|
+
sampleCount: initial.physical.descriptor.sampleCount,
|
|
57
|
+
dimension: '2d',
|
|
58
|
+
usage: 0x10 | 0x04 | 0x01,
|
|
59
|
+
},
|
|
60
|
+
() => current().physical.colorTextures[current().faceIndex] ?? initial.physical.texture,
|
|
61
|
+
);
|
|
62
|
+
if (!texture.ok) return texture;
|
|
63
|
+
const color = builder.importView(
|
|
64
|
+
texture.value,
|
|
65
|
+
{
|
|
66
|
+
label: `cube-capture.${slot}.color`,
|
|
67
|
+
dimension: '2d',
|
|
68
|
+
baseMipLevel: 0,
|
|
69
|
+
mipLevelCount: 1,
|
|
70
|
+
baseArrayLayer: initial.physical.descriptor.sampleCount === 4 ? 0 : initial.faceIndex,
|
|
71
|
+
arrayLayerCount: 1,
|
|
72
|
+
},
|
|
73
|
+
() =>
|
|
74
|
+
current().physical.faceViews[current().faceIndex] ??
|
|
75
|
+
initial.physical.faceViews[initial.faceIndex] ??
|
|
76
|
+
initial.physical.view,
|
|
77
|
+
);
|
|
78
|
+
if (!color.ok) return color;
|
|
79
|
+
const depthTexture = builder.importTexture(
|
|
80
|
+
`cube-capture.${slot}.depth-texture`,
|
|
81
|
+
{
|
|
82
|
+
format: 'depth24plus-stencil8',
|
|
83
|
+
size: {
|
|
84
|
+
width: initial.physical.descriptor.width,
|
|
85
|
+
height: initial.physical.descriptor.height,
|
|
86
|
+
depthOrArrayLayers:
|
|
87
|
+
initial.physical.descriptor.sampleCount === 4 ||
|
|
88
|
+
initial.physical.descriptor.shape === '2d'
|
|
89
|
+
? 1
|
|
90
|
+
: 6,
|
|
91
|
+
},
|
|
92
|
+
mipLevelCount: 1,
|
|
93
|
+
sampleCount: initial.physical.descriptor.sampleCount,
|
|
94
|
+
dimension: '2d',
|
|
95
|
+
usage: 0x10,
|
|
96
|
+
},
|
|
97
|
+
() => current().physical.depthTextures[current().faceIndex] as Texture,
|
|
98
|
+
);
|
|
99
|
+
if (!depthTexture.ok) return depthTexture;
|
|
100
|
+
const depth = builder.importView(
|
|
101
|
+
depthTexture.value,
|
|
102
|
+
{
|
|
103
|
+
label: `cube-capture.${slot}.depth`,
|
|
104
|
+
dimension: '2d',
|
|
105
|
+
baseArrayLayer: initial.physical.descriptor.sampleCount === 4 ? 0 : initial.faceIndex,
|
|
106
|
+
arrayLayerCount: 1,
|
|
107
|
+
},
|
|
108
|
+
() => current().physical.depthViews[current().faceIndex] as TextureView,
|
|
109
|
+
);
|
|
110
|
+
if (!depth.ok) return depth;
|
|
111
|
+
const resolveTexture =
|
|
112
|
+
initial.physical.descriptor.sampleCount === 4
|
|
113
|
+
? builder.importTexture(
|
|
114
|
+
`cube-capture.${slot}.resolve-texture`,
|
|
115
|
+
{
|
|
116
|
+
format: initial.physical.descriptor.format,
|
|
117
|
+
size: {
|
|
118
|
+
width: initial.physical.descriptor.width,
|
|
119
|
+
height: initial.physical.descriptor.height,
|
|
120
|
+
depthOrArrayLayers: initial.physical.descriptor.shape === 'cube' ? 6 : 1,
|
|
121
|
+
},
|
|
122
|
+
mipLevelCount: renderTargetMipCount(initial),
|
|
123
|
+
sampleCount: 1,
|
|
124
|
+
dimension: '2d',
|
|
125
|
+
usage: 0x10 | 0x04 | 0x01,
|
|
126
|
+
},
|
|
127
|
+
() =>
|
|
128
|
+
current().physical.resolveTexture ??
|
|
129
|
+
initial.physical.resolveTexture ??
|
|
130
|
+
initial.physical.texture,
|
|
131
|
+
)
|
|
132
|
+
: undefined;
|
|
133
|
+
if (resolveTexture !== undefined && !resolveTexture.ok) return resolveTexture;
|
|
134
|
+
const resolve =
|
|
135
|
+
resolveTexture === undefined
|
|
136
|
+
? undefined
|
|
137
|
+
: builder.importView(
|
|
138
|
+
resolveTexture.value,
|
|
139
|
+
{
|
|
140
|
+
label: `cube-capture.${slot}.resolve`,
|
|
141
|
+
dimension: '2d',
|
|
142
|
+
baseMipLevel: 0,
|
|
143
|
+
mipLevelCount: 1,
|
|
144
|
+
baseArrayLayer: initial.faceIndex,
|
|
145
|
+
arrayLayerCount: 1,
|
|
146
|
+
},
|
|
147
|
+
() =>
|
|
148
|
+
current().physical.resolveFaceViews[current().faceIndex] ??
|
|
149
|
+
initial.physical.resolveFaceViews[initial.faceIndex] ??
|
|
150
|
+
initial.physical.resolveView,
|
|
151
|
+
);
|
|
152
|
+
if (resolve !== undefined && !resolve.ok) return resolve;
|
|
153
|
+
const added = builder.addRasterPass(`cube-capture-face.${slot}`, {
|
|
154
|
+
accesses: [
|
|
155
|
+
{ resource: color.value, usage: 'color-attachment' },
|
|
156
|
+
{ resource: depth.value, usage: 'depth-stencil-write' },
|
|
157
|
+
...(resolve === undefined
|
|
158
|
+
? []
|
|
159
|
+
: [{ resource: resolve.value, usage: 'color-attachment' as const }]),
|
|
160
|
+
],
|
|
161
|
+
colorAttachments: [
|
|
162
|
+
{
|
|
163
|
+
view: color.value,
|
|
164
|
+
...(resolve === undefined ? {} : { resolveTarget: resolve.value }),
|
|
165
|
+
loadOp: 'clear',
|
|
166
|
+
storeOp: 'store',
|
|
167
|
+
clearValue: () => {
|
|
168
|
+
const clear =
|
|
169
|
+
current().physical.descriptor.shape === '2d'
|
|
170
|
+
? current().faceCamera.clearColor
|
|
171
|
+
: [0, 0, 0, 1];
|
|
172
|
+
return { r: clear[0] ?? 0, g: clear[1] ?? 0, b: clear[2] ?? 0, a: clear[3] ?? 1 };
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
depthStencilAttachment: {
|
|
177
|
+
view: depth.value,
|
|
178
|
+
depthClearValue: 1,
|
|
179
|
+
depthLoadOp: 'clear',
|
|
180
|
+
depthStoreOp: 'store',
|
|
181
|
+
stencilClearValue: 0,
|
|
182
|
+
stencilLoadOp: 'clear',
|
|
183
|
+
stencilStoreOp: 'store',
|
|
184
|
+
},
|
|
185
|
+
encode: ({ pass, frame, resources }) => {
|
|
186
|
+
const internal = frame as _InternalRenderPipelineContext;
|
|
187
|
+
const captureColor = resources.textureView(color.value);
|
|
188
|
+
if (!captureColor.ok) return;
|
|
189
|
+
const captureDepth = resources.textureView(depth.value);
|
|
190
|
+
if (!captureDepth.ok) return;
|
|
191
|
+
const captureResolve =
|
|
192
|
+
resolve === undefined ? undefined : resources.textureView(resolve.value);
|
|
193
|
+
if (resolve !== undefined && (captureResolve === undefined || !captureResolve.ok)) return;
|
|
194
|
+
const captureResolveView = captureResolve?.ok ? captureResolve.value : null;
|
|
195
|
+
const selfSampling = new Map<number, Set<number>>();
|
|
196
|
+
for (const row of internal.validatedOrdered) {
|
|
197
|
+
for (const material of row.source.materials) {
|
|
198
|
+
if (
|
|
199
|
+
![...(material.textureSources?.values() ?? [])].some(
|
|
200
|
+
(source) => resolveRenderTargetMaterialSource(source)?.target === current().target,
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
continue;
|
|
204
|
+
let handles = selfSampling.get(row.renderableIndex);
|
|
205
|
+
if (handles === undefined) {
|
|
206
|
+
handles = new Set();
|
|
207
|
+
selfSampling.set(row.renderableIndex, handles);
|
|
208
|
+
}
|
|
209
|
+
handles.add(material.materialHandle ?? 0);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const captureContext = {
|
|
213
|
+
...internal,
|
|
214
|
+
camera: current().faceCamera,
|
|
215
|
+
targetW: current().physical.descriptor.width,
|
|
216
|
+
targetH: current().physical.descriptor.height,
|
|
217
|
+
dispatch: (internal.captureDispatch ?? internal.dispatch).filter(
|
|
218
|
+
(entry) => !selfSampling.get(entry.renderableIndex)?.has(entry.materialHandle),
|
|
219
|
+
),
|
|
220
|
+
foldDispatchPlan: null,
|
|
221
|
+
|
|
222
|
+
// Cube faces are raw scene captures, not display-camera HDR output.
|
|
223
|
+
// Select the PSO against the actual target attachment so an LDR
|
|
224
|
+
// cube target is rendered by a compatible forward pipeline.
|
|
225
|
+
tonemapActive: false,
|
|
226
|
+
skyboxActive: false,
|
|
227
|
+
transparentColorFormat: initial.physical.descriptor.format as GPUTextureFormat,
|
|
228
|
+
msaaActive: initial.physical.descriptor.sampleCount === 4,
|
|
229
|
+
viewBindGroupDynamicOffset: current().viewBindGroupDynamicOffset,
|
|
230
|
+
geometryColorView: captureColor.value,
|
|
231
|
+
geometryDepthView: captureDepth.value,
|
|
232
|
+
geometryColorResolveView: captureResolveView,
|
|
233
|
+
splitLdrSprite: false,
|
|
234
|
+
ldrSpritePassView: null,
|
|
235
|
+
};
|
|
236
|
+
delete captureContext.gpuDrivenDrawKeys;
|
|
237
|
+
delete captureContext.gpuDrivenStandardPbrFrameResources;
|
|
238
|
+
encodeMainPass(
|
|
239
|
+
captureContext,
|
|
240
|
+
pass,
|
|
241
|
+
{ LightMode: ['Forward'] },
|
|
242
|
+
{
|
|
243
|
+
colorViews: [captureColor.value],
|
|
244
|
+
colorFormats: [initial.physical.descriptor.format as GPUTextureFormat],
|
|
245
|
+
depthView: captureDepth.value,
|
|
246
|
+
passKind: 'forward',
|
|
247
|
+
clearColor:
|
|
248
|
+
current().physical.descriptor.shape === '2d'
|
|
249
|
+
? current().faceCamera.clearColor
|
|
250
|
+
: [0, 0, 0, 1],
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
if (!added.ok) return added;
|
|
256
|
+
}
|
|
257
|
+
return ok(undefined);
|
|
258
|
+
}
|