@forgeax/engine-render 0.1.31 → 0.1.33
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 +447 -34
- package/dist/assembly/analytic-fog-registration.d.ts +3 -0
- package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
- package/dist/assembly/fxaa-registration.d.ts +4 -0
- package/dist/assembly/fxaa-registration.d.ts.map +1 -0
- package/dist/assembly/lod-observation.d.ts +4 -0
- package/dist/assembly/lod-observation.d.ts.map +1 -0
- package/dist/assembly/motion-blur-registration.d.ts +3 -0
- package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
- package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
- package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready.d.ts +3 -2
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/bloom-admission.d.ts +13 -0
- package/dist/bloom-admission.d.ts.map +1 -0
- package/dist/{chunk-FUUO4UND.mjs → chunk-3RTCJTH4.mjs} +12 -9
- package/dist/chunk-3RTCJTH4.mjs.map +1 -0
- package/dist/{chunk-NO4WZCAH.mjs → chunk-LEAXOMYJ.mjs} +3 -3
- package/dist/{chunk-NO4WZCAH.mjs.map → chunk-LEAXOMYJ.mjs.map} +1 -1
- package/dist/{chunk-JRBDO2KB.mjs → chunk-NB2S7ZRX.mjs} +166 -10
- package/dist/chunk-NB2S7ZRX.mjs.map +1 -0
- package/dist/{chunk-7MQ3XBSP.mjs → chunk-NNQL7CB3.mjs} +4 -4
- package/dist/{chunk-7MQ3XBSP.mjs.map → chunk-NNQL7CB3.mjs.map} +1 -1
- package/dist/{chunk-C3DDSXYK.mjs → chunk-OO3DRYK7.mjs} +171 -230
- package/dist/chunk-OO3DRYK7.mjs.map +1 -0
- package/dist/{chunk-S5UE2T5P.mjs → chunk-PLAXOOX5.mjs} +608 -7
- package/dist/chunk-PLAXOOX5.mjs.map +1 -0
- package/dist/{chunk-PSEQ6QPH.mjs → chunk-VOZWDM3L.mjs} +295 -198
- package/dist/chunk-VOZWDM3L.mjs.map +1 -0
- package/dist/{chunk-MDQJPY3T.mjs → chunk-ZDQWGQW7.mjs} +10 -4
- package/dist/chunk-ZDQWGQW7.mjs.map +1 -0
- package/dist/components/atmosphere.d.ts +2 -0
- package/dist/components/atmosphere.d.ts.map +1 -1
- package/dist/components/camera.d.ts +20 -4
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/depth-of-field.d.ts +28 -0
- package/dist/components/depth-of-field.d.ts.map +1 -0
- package/dist/components/directional-light.d.ts +2 -1
- package/dist/components/directional-light.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/mesh-filter.d.ts +6 -5
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/motion-blur.d.ts +1 -0
- package/dist/components/motion-blur.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +3746 -837
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +6 -0
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/environment/analytic-fog.d.ts +6 -0
- package/dist/environment/analytic-fog.d.ts.map +1 -0
- package/dist/errors/render.d.ts +6 -2
- 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/environment.d.ts +2 -0
- package/dist/extract/environment.d.ts.map +1 -1
- package/dist/features/depth-of-field/depth-of-field-assembly.d.ts +19 -0
- package/dist/features/depth-of-field/depth-of-field-assembly.d.ts.map +1 -0
- package/dist/features/depth-of-field/depth-of-field-feature.d.ts +43 -0
- package/dist/features/depth-of-field/depth-of-field-feature.d.ts.map +1 -0
- package/dist/features/depth-of-field/depth-of-field-inspection.d.ts +34 -0
- package/dist/features/depth-of-field/depth-of-field-inspection.d.ts.map +1 -0
- package/dist/features/depth-of-field/depth-of-field-params.d.ts +83 -0
- package/dist/features/depth-of-field/depth-of-field-params.d.ts.map +1 -0
- package/dist/features/error-reporting.d.ts +13 -0
- package/dist/features/error-reporting.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/motion-blur/motion-blur-feature.d.ts +20 -2
- package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
- package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
- package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
- package/dist/features/plan.d.ts +7 -4
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +9 -5
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/features/types.d.ts +19 -1
- package/dist/features/types.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +7 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +3 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +9 -0
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +6 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +6 -5
- package/dist/index.mjs.map +1 -1
- package/dist/inspection-types.d.ts +78 -2
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances.d.ts +15 -1
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +6 -6
- package/dist/mesh-material-bindings.d.ts +3 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/pipeline/standard-output/graph.d.ts.map +1 -1
- package/dist/pipeline/standard-output/types.d.ts +4 -2
- package/dist/pipeline/standard-output/types.d.ts.map +1 -1
- package/dist/pipeline/standard-post.d.ts +28 -19
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +24 -1
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +5 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +6 -5
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +12 -28
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +5 -3
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +8 -1
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/render-contract.d.ts +32 -5
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-graph-primitives.d.ts +2 -0
- package/dist/render-graph-primitives.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +15 -1
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +26 -5
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +9 -2
- 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/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +7 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/view.d.ts +6 -1
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +10 -7
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +0 -1
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/passes.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +3 -1
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
- package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
- package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
- package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
- package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
- package/src/__tests__/environment-selection.unit.test.ts +12 -0
- package/src/__tests__/factory-contract.integration.test.ts +331 -68
- package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
- package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
- package/src/__tests__/gpu-driven-baseline.characterization.test.ts +12 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
- package/src/__tests__/gpu-driven-production.integration.test.ts +27 -1
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +1 -1
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +11 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
- package/src/__tests__/gpu-scene.unit.test.ts +91 -0
- package/src/__tests__/instances-store.unit.test.ts +90 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
- package/src/__tests__/motion-blur-compute.dawn.test.ts +583 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +49 -0
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
- package/src/__tests__/public-surface.test-d.ts +8 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +2 -0
- package/src/__tests__/render-feature-error-reporting.unit.test.ts +24 -0
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +31 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +7 -1
- package/src/__tests__/shader-manifest-fixture.ts +4 -4
- package/src/__tests__/skinned-shadow-caster.test.ts +2 -0
- package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
- package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
- package/src/__tests__/standard-pipeline.integration.test.ts +18 -8
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
- package/src/__tests__/temporal-view.unit.test.ts +2 -1
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
- package/src/__tests__/volume-optics-oracle.unit.test.ts +3 -1
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +177 -13
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +447 -61
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +19 -3
- package/src/assembly/analytic-fog-registration.ts +14 -0
- package/src/assembly/fxaa-registration.ts +17 -0
- package/src/assembly/lod-observation.ts +12 -0
- package/src/assembly/motion-blur-registration.ts +17 -0
- package/src/assembly/renderer-facade.ts +2 -0
- package/src/assembly/temporal-post-process-prewarm.ts +40 -0
- package/src/assembly/webgpu-ready-contract.ts +8 -3
- package/src/assembly/webgpu-ready.ts +186 -250
- package/src/assembly/webgpu-renderer.ts +30 -45
- package/src/bloom-admission.ts +21 -0
- package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
- package/src/components/atmosphere.ts +4 -0
- package/src/components/camera.ts +66 -10
- package/src/components/depth-of-field.ts +45 -0
- package/src/components/directional-light.ts +2 -1
- package/src/components/index.ts +1 -0
- package/src/components/mesh-filter.ts +11 -9
- package/src/components/motion-blur.ts +1 -0
- package/src/device/gpu-residency.ts +56 -12
- package/src/environment/analytic-fog.ts +44 -0
- package/src/environment/background.ts +2 -2
- package/src/errors/render.ts +14 -1
- package/src/extract/camera.ts +42 -9
- package/src/extract/contracts.ts +7 -0
- package/src/extract/environment.ts +4 -0
- package/src/features/depth-of-field/__tests__/depth-of-field-params.unit.test.ts +943 -0
- package/src/features/depth-of-field/depth-of-field-assembly.ts +100 -0
- package/src/features/depth-of-field/depth-of-field-feature.ts +295 -0
- package/src/features/depth-of-field/depth-of-field-inspection.ts +237 -0
- package/src/features/depth-of-field/depth-of-field-params.ts +556 -0
- package/src/features/error-reporting.ts +78 -0
- package/src/features/host.ts +26 -13
- package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
- package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
- package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
- package/src/features/motion-blur/motion-blur-params.ts +70 -2
- package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
- package/src/features/plan.ts +25 -8
- package/src/features/prepared-gpu-work.ts +86 -35
- package/src/features/render-graph-compute.ts +13 -5
- package/src/features/render-graph-contribution.ts +2 -0
- package/src/features/types.ts +18 -1
- package/src/gpu-driven/material-artifacts.ts +2 -7
- package/src/gpu-driven/prepared-draw.ts +15 -10
- package/src/gpu-driven/production-raster.ts +83 -7
- package/src/gpu-driven/view-gpu.ts +17 -0
- package/src/gpu-scene.ts +188 -21
- package/src/index.ts +64 -0
- package/src/inspection-types.ts +98 -2
- package/src/instances.ts +93 -0
- package/src/mesh-material-bindings.ts +4 -0
- package/src/pipeline/standard-output/graph.ts +3 -0
- package/src/pipeline/standard-output/types.ts +4 -1
- package/src/pipeline/standard-post.ts +324 -76
- package/src/pipeline-spec.ts +5 -5
- package/src/plugin.ts +2 -0
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
- package/src/record/__tests__/temporal-payload.unit.test.ts +2 -1
- package/src/record/frame-snapshot.ts +31 -2
- package/src/record/frame-targets.ts +10 -6
- package/src/record/frame.ts +202 -28
- package/src/record/main-pass-geometry.ts +30 -1
- package/src/record/main-pass-sprite-draws.ts +6 -1
- package/src/record/main-pass.ts +1 -5
- package/src/record/mesh-ssbo.ts +43 -6
- package/src/record/render-context.ts +12 -28
- package/src/record/shadow-pass.ts +6 -1
- package/src/record/skybox-post-pass.ts +122 -196
- package/src/record/typed-frame-graph.ts +259 -15
- package/src/recovery/render-system-candidate.ts +28 -10
- package/src/render-contract.ts +34 -4
- package/src/render-graph-primitives.ts +8 -1
- package/src/render-pipeline.ts +22 -1
- package/src/render-system-extract-tail.ts +103 -13
- package/src/render-system-extract.ts +148 -38
- package/src/render-system.ts +332 -119
- package/src/scene/render-scene.ts +85 -4
- package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
- package/src/temporal/frame-coordinator.ts +8 -1
- package/src/temporal/frame.ts +20 -0
- package/src/temporal/view.ts +13 -2
- package/src/typed-render-graph-primitives.ts +124 -48
- package/src/volume/component.ts +0 -1
- package/src/volume/passes.ts +8 -4
- package/src/volume/resources.ts +1 -1
- package/src/volume/temporal.ts +11 -0
- package/dist/chunk-C3DDSXYK.mjs.map +0 -1
- package/dist/chunk-FUUO4UND.mjs.map +0 -1
- package/dist/chunk-JRBDO2KB.mjs.map +0 -1
- package/dist/chunk-MDQJPY3T.mjs.map +0 -1
- package/dist/chunk-PSEQ6QPH.mjs.map +0 -1
- package/dist/chunk-S5UE2T5P.mjs.map +0 -1
|
@@ -5,8 +5,10 @@ import type {
|
|
|
5
5
|
} from '@forgeax/engine-render-graph';
|
|
6
6
|
import { RenderGraphError } from '@forgeax/engine-render-graph';
|
|
7
7
|
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
8
|
+
import { standardBloomAdmitted } from '../bloom-admission';
|
|
8
9
|
import { addTypedDebugOverlayPass } from '../debug-draw-glue';
|
|
9
10
|
import { SceneDataUnavailableError } from '../errors/render';
|
|
11
|
+
import { addDepthOfFieldPasses } from '../features/depth-of-field/depth-of-field-feature';
|
|
10
12
|
import { addMotionBlurPass } from '../features/motion-blur/motion-blur-feature';
|
|
11
13
|
import { GPU_TEXTURE_USAGE_COPY_SRC } from '../gpu-texture-usage';
|
|
12
14
|
import type {
|
|
@@ -29,6 +31,7 @@ import {
|
|
|
29
31
|
TYPED_BLOOM_PASS_NAMES,
|
|
30
32
|
type TypedTemporalResolveTargets,
|
|
31
33
|
} from '../typed-render-graph-primitives';
|
|
34
|
+
import { renderExtentSize } from './render-extent';
|
|
32
35
|
import {
|
|
33
36
|
addAutoExposureExposurePass,
|
|
34
37
|
addAutoExposureGraphPasses,
|
|
@@ -41,22 +44,81 @@ interface StandardPostSurface {
|
|
|
41
44
|
readonly storage: RenderPipelineTarget;
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
export const STANDARD_BLOOM_MAX_LEVELS = 5;
|
|
48
|
+
|
|
44
49
|
/** One authored Bloom contract drives declaration and compiled-graph validation. */
|
|
45
50
|
export const STANDARD_BLOOM_TARGET_SPEC = {
|
|
46
|
-
composited: { label: 'bloom-composited', format: 'rgba16float'
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
composited: { label: 'bloom-composited', format: 'rgba16float' },
|
|
52
|
+
downsamplePrefix: 'bloom-downsample-',
|
|
53
|
+
upsamplePrefix: 'bloom-upsample-',
|
|
54
|
+
} as const;
|
|
55
|
+
|
|
56
|
+
export interface StandardBloomLevelDimensions {
|
|
57
|
+
readonly width: number;
|
|
58
|
+
readonly height: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Derive the bounded ceil-halving output pyramid, including D0. */
|
|
62
|
+
export function deriveStandardBloomLevels(
|
|
63
|
+
outputWidth: number,
|
|
64
|
+
outputHeight: number,
|
|
65
|
+
): readonly StandardBloomLevelDimensions[] {
|
|
66
|
+
const levels: StandardBloomLevelDimensions[] = [];
|
|
67
|
+
let width = Math.max(1, Math.ceil(outputWidth / 2));
|
|
68
|
+
let height = Math.max(1, Math.ceil(outputHeight / 2));
|
|
69
|
+
for (let level = 0; level < STANDARD_BLOOM_MAX_LEVELS; level += 1) {
|
|
70
|
+
levels.push({ width, height });
|
|
71
|
+
if (width === 1 && height === 1) break;
|
|
72
|
+
width = Math.max(1, Math.ceil(width / 2));
|
|
73
|
+
height = Math.max(1, Math.ceil(height / 2));
|
|
74
|
+
}
|
|
75
|
+
return levels;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface StandardBloomTargetSpecs {
|
|
79
|
+
readonly composited: GraphTextureDescriptor & { readonly label: string };
|
|
80
|
+
readonly downsample: readonly (GraphTextureDescriptor & { readonly label: string })[];
|
|
81
|
+
readonly upsample: readonly (GraphTextureDescriptor & { readonly label: string })[];
|
|
82
|
+
readonly levels: readonly StandardBloomLevelDimensions[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function standardBloomTargetSpecs(
|
|
86
|
+
outputWidth: number,
|
|
87
|
+
outputHeight: number,
|
|
88
|
+
): StandardBloomTargetSpecs {
|
|
89
|
+
const levels = deriveStandardBloomLevels(outputWidth, outputHeight);
|
|
90
|
+
return {
|
|
91
|
+
composited: {
|
|
92
|
+
label: STANDARD_BLOOM_TARGET_SPEC.composited.label,
|
|
93
|
+
format: STANDARD_BLOOM_TARGET_SPEC.composited.format,
|
|
94
|
+
size: { width: outputWidth, height: outputHeight },
|
|
95
|
+
domain: 'linear-hdr',
|
|
96
|
+
},
|
|
97
|
+
downsample: levels.map((size, level) => ({
|
|
98
|
+
label: `${STANDARD_BLOOM_TARGET_SPEC.downsamplePrefix}${level}`,
|
|
99
|
+
format: 'rgba16float' as const,
|
|
100
|
+
size,
|
|
101
|
+
domain: 'linear-hdr' as const,
|
|
102
|
+
})),
|
|
103
|
+
upsample: levels.slice(0, -1).map((size, level) => ({
|
|
104
|
+
label: `${STANDARD_BLOOM_TARGET_SPEC.upsamplePrefix}${level}`,
|
|
105
|
+
format: 'rgba16float' as const,
|
|
106
|
+
size,
|
|
107
|
+
domain: 'linear-hdr' as const,
|
|
108
|
+
})),
|
|
109
|
+
levels,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
54
112
|
|
|
55
113
|
export interface StandardBloomGraphInspection {
|
|
56
114
|
readonly status: 'empty' | 'valid' | 'invalid';
|
|
57
115
|
readonly targetCount: number;
|
|
58
116
|
readonly targetBytes: number;
|
|
59
117
|
readonly passCount: number;
|
|
118
|
+
readonly levelCount: number;
|
|
119
|
+
readonly levelDimensions: readonly StandardBloomLevelDimensions[];
|
|
120
|
+
readonly downsamplePassCount: number;
|
|
121
|
+
readonly upsamplePassCount: number;
|
|
60
122
|
}
|
|
61
123
|
|
|
62
124
|
const emptyBloomGraphInspection = (): StandardBloomGraphInspection => ({
|
|
@@ -64,6 +126,10 @@ const emptyBloomGraphInspection = (): StandardBloomGraphInspection => ({
|
|
|
64
126
|
targetCount: 0,
|
|
65
127
|
targetBytes: 0,
|
|
66
128
|
passCount: 0,
|
|
129
|
+
levelCount: 0,
|
|
130
|
+
levelDimensions: [],
|
|
131
|
+
downsamplePassCount: 0,
|
|
132
|
+
upsamplePassCount: 0,
|
|
67
133
|
});
|
|
68
134
|
|
|
69
135
|
const invalidBloomGraphInspection = (): StandardBloomGraphInspection => ({
|
|
@@ -71,21 +137,23 @@ const invalidBloomGraphInspection = (): StandardBloomGraphInspection => ({
|
|
|
71
137
|
targetCount: 0,
|
|
72
138
|
targetBytes: 0,
|
|
73
139
|
passCount: 0,
|
|
140
|
+
levelCount: 0,
|
|
141
|
+
levelDimensions: [],
|
|
142
|
+
downsamplePassCount: 0,
|
|
143
|
+
upsamplePassCount: 0,
|
|
74
144
|
});
|
|
75
145
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
): number {
|
|
146
|
+
type CompiledBloomResource = CompiledRenderGraphInfo['resources'][number];
|
|
147
|
+
|
|
148
|
+
function textureBytes(resource: CompiledBloomResource): number | undefined {
|
|
80
149
|
const descriptor = resource.descriptor;
|
|
81
150
|
if (
|
|
151
|
+
resource.kind !== 'texture' ||
|
|
82
152
|
descriptor === undefined ||
|
|
83
153
|
descriptor.kind !== 'texture' ||
|
|
84
|
-
descriptor.format !==
|
|
85
|
-
!('size' in descriptor) ||
|
|
86
|
-
descriptor.size !== expected.size
|
|
154
|
+
descriptor.format !== 'rgba16float'
|
|
87
155
|
)
|
|
88
|
-
return
|
|
156
|
+
return undefined;
|
|
89
157
|
const texelBytes = 8;
|
|
90
158
|
let bytes = 0;
|
|
91
159
|
for (let level = 0; level < descriptor.mipLevelCount; level += 1) {
|
|
@@ -95,7 +163,56 @@ function textureBytes(
|
|
|
95
163
|
descriptor.depthOrArrayLayers *
|
|
96
164
|
texelBytes;
|
|
97
165
|
}
|
|
98
|
-
|
|
166
|
+
const descriptorBytes = bytes * descriptor.sampleCount;
|
|
167
|
+
return resource.byteSize === descriptorBytes && descriptorBytes > 0 ? descriptorBytes : undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function resourceSize(
|
|
171
|
+
resource: CompiledBloomResource,
|
|
172
|
+
): { readonly width: number; readonly height: number } | undefined {
|
|
173
|
+
const descriptor = resource.descriptor;
|
|
174
|
+
if (resource.kind !== 'texture' || descriptor.kind !== 'texture') return undefined;
|
|
175
|
+
if (typeof descriptor.size === 'string') return undefined;
|
|
176
|
+
if (
|
|
177
|
+
resource.extent === undefined ||
|
|
178
|
+
resource.extent.width !== descriptor.width ||
|
|
179
|
+
resource.extent.height !== descriptor.height ||
|
|
180
|
+
resource.extent.depthOrArrayLayers !== descriptor.depthOrArrayLayers
|
|
181
|
+
) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
if (
|
|
185
|
+
descriptor.format !== 'rgba16float' ||
|
|
186
|
+
descriptor.domain !== 'linear-hdr' ||
|
|
187
|
+
descriptor.width <= 0 ||
|
|
188
|
+
descriptor.height <= 0 ||
|
|
189
|
+
descriptor.depthOrArrayLayers !== 1 ||
|
|
190
|
+
descriptor.mipLevelCount !== 1 ||
|
|
191
|
+
descriptor.sampleCount !== 1 ||
|
|
192
|
+
resource.dimension !== '2d' ||
|
|
193
|
+
descriptor.size.width !== descriptor.width ||
|
|
194
|
+
descriptor.size.height !== descriptor.height ||
|
|
195
|
+
textureBytes(resource) === undefined
|
|
196
|
+
) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
return { width: descriptor.width, height: descriptor.height };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function hasPassAccesses(
|
|
203
|
+
pass: CompiledRenderGraphInfo['passes'][number] | undefined,
|
|
204
|
+
accesses: readonly { readonly resource: string; readonly usage: string }[],
|
|
205
|
+
): boolean {
|
|
206
|
+
return (
|
|
207
|
+
pass !== undefined &&
|
|
208
|
+
pass.kind === 'raster' &&
|
|
209
|
+
pass.accesses.length === accesses.length &&
|
|
210
|
+
accesses.every(
|
|
211
|
+
(access, index) =>
|
|
212
|
+
pass.accesses[index]?.resource === access.resource &&
|
|
213
|
+
pass.accesses[index]?.usage === access.usage,
|
|
214
|
+
)
|
|
215
|
+
);
|
|
99
216
|
}
|
|
100
217
|
|
|
101
218
|
/** Derive Bloom target/pass facts solely from the compiled Standard graph. */
|
|
@@ -103,33 +220,125 @@ export function inspectStandardBloomGraph(
|
|
|
103
220
|
graph: CompiledRenderGraphInfo | undefined,
|
|
104
221
|
): StandardBloomGraphInspection {
|
|
105
222
|
if (graph === undefined) return emptyBloomGraphInspection();
|
|
106
|
-
|
|
107
|
-
(
|
|
108
|
-
const targetSpec = new Map<string, BloomTargetSpec>(
|
|
109
|
-
Object.values(STANDARD_BLOOM_TARGET_SPEC).map((entry) => [entry.label, entry] as const),
|
|
223
|
+
const composited = graph.resources.filter(
|
|
224
|
+
(resource) => resource.label === STANDARD_BLOOM_TARGET_SPEC.composited.label,
|
|
110
225
|
);
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
226
|
+
const downsample = graph.resources
|
|
227
|
+
.filter((resource) => resource.label.startsWith(STANDARD_BLOOM_TARGET_SPEC.downsamplePrefix))
|
|
228
|
+
.sort((a, b) => a.label.localeCompare(b.label, undefined, { numeric: true }));
|
|
229
|
+
const upsample = graph.resources
|
|
230
|
+
.filter((resource) => resource.label.startsWith(STANDARD_BLOOM_TARGET_SPEC.upsamplePrefix))
|
|
231
|
+
.sort((a, b) => a.label.localeCompare(b.label, undefined, { numeric: true }));
|
|
232
|
+
const targets = [...composited, ...downsample, ...upsample];
|
|
233
|
+
const passes = graph.passes
|
|
234
|
+
.filter(
|
|
235
|
+
(pass) =>
|
|
236
|
+
pass.name === TYPED_BLOOM_PASS_NAMES.composite ||
|
|
237
|
+
pass.name.startsWith(`${TYPED_BLOOM_PASS_NAMES.downsample}-`) ||
|
|
238
|
+
pass.name.startsWith(`${TYPED_BLOOM_PASS_NAMES.upsample}-`),
|
|
239
|
+
)
|
|
240
|
+
.sort((left, right) => left.executionIndex - right.executionIndex);
|
|
117
241
|
if (targets.length === 0 && passes.length === 0) return emptyBloomGraphInspection();
|
|
242
|
+
if (composited.length !== 1) return invalidBloomGraphInspection();
|
|
243
|
+
const compositedResource = composited[0];
|
|
244
|
+
if (compositedResource === undefined) return invalidBloomGraphInspection();
|
|
245
|
+
const compositedSize = resourceSize(compositedResource);
|
|
246
|
+
if (compositedSize === undefined) return invalidBloomGraphInspection();
|
|
247
|
+
|
|
248
|
+
const expected = standardBloomTargetSpecs(compositedSize.width, compositedSize.height);
|
|
249
|
+
const expectedDownsampleLabels = expected.downsample.map((entry) => entry.label);
|
|
250
|
+
const expectedUpsampleLabels = expected.upsample.map((entry) => entry.label);
|
|
251
|
+
const orderedDownsample = downsample.sort((left, right) =>
|
|
252
|
+
left.label.localeCompare(right.label, undefined, { numeric: true }),
|
|
253
|
+
);
|
|
254
|
+
const orderedUpsample = upsample.sort((left, right) =>
|
|
255
|
+
left.label.localeCompare(right.label, undefined, { numeric: true }),
|
|
256
|
+
);
|
|
118
257
|
const completeTargetSet =
|
|
119
|
-
targets.length ===
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
258
|
+
targets.length === 1 + expectedDownsampleLabels.length + expectedUpsampleLabels.length &&
|
|
259
|
+
orderedDownsample.length === expectedDownsampleLabels.length &&
|
|
260
|
+
orderedUpsample.length === expectedUpsampleLabels.length &&
|
|
261
|
+
orderedDownsample.every(
|
|
262
|
+
(resource, level) => resource.label === expectedDownsampleLabels[level],
|
|
263
|
+
) &&
|
|
264
|
+
orderedUpsample.every((resource, level) => resource.label === expectedUpsampleLabels[level]) &&
|
|
265
|
+
orderedDownsample.every(
|
|
266
|
+
(resource, level) =>
|
|
267
|
+
resourceSize(resource)?.width === expected.levels[level]?.width &&
|
|
268
|
+
resourceSize(resource)?.height === expected.levels[level]?.height,
|
|
269
|
+
) &&
|
|
270
|
+
orderedUpsample.every(
|
|
271
|
+
(resource, level) =>
|
|
272
|
+
resourceSize(resource)?.width === expected.levels[level]?.width &&
|
|
273
|
+
resourceSize(resource)?.height === expected.levels[level]?.height,
|
|
274
|
+
);
|
|
275
|
+
const expectedPassNames = [
|
|
276
|
+
...expectedDownsampleLabels,
|
|
277
|
+
...[...expectedUpsampleLabels].reverse(),
|
|
278
|
+
TYPED_BLOOM_PASS_NAMES.composite,
|
|
279
|
+
];
|
|
280
|
+
const completePassSet =
|
|
281
|
+
passes.length === expected.levels.length + expected.upsample.length + 1 &&
|
|
282
|
+
passes.every((pass, index) => pass.name === expectedPassNames[index]);
|
|
283
|
+
const sceneLabel = passes[0]?.accesses[0]?.resource;
|
|
284
|
+
const passTopology =
|
|
285
|
+
completePassSet &&
|
|
286
|
+
sceneLabel !== undefined &&
|
|
287
|
+
!sceneLabel.startsWith(STANDARD_BLOOM_TARGET_SPEC.downsamplePrefix) &&
|
|
288
|
+
!sceneLabel.startsWith(STANDARD_BLOOM_TARGET_SPEC.upsamplePrefix) &&
|
|
289
|
+
sceneLabel !== STANDARD_BLOOM_TARGET_SPEC.composited.label &&
|
|
290
|
+
graph.resources.some((resource) => resource.label === sceneLabel) &&
|
|
291
|
+
orderedDownsample.every((_, level) =>
|
|
292
|
+
hasPassAccesses(passes[level], [
|
|
293
|
+
{
|
|
294
|
+
resource: level === 0 ? sceneLabel : `bloom-downsample-${level - 1}`,
|
|
295
|
+
usage: 'sampled-read',
|
|
296
|
+
},
|
|
297
|
+
{ resource: `bloom-downsample-${level}`, usage: 'color-attachment' },
|
|
298
|
+
]),
|
|
299
|
+
) &&
|
|
300
|
+
orderedUpsample.every((_, level) => {
|
|
301
|
+
const passIndex = expected.levels.length + expected.upsample.length - 1 - level;
|
|
302
|
+
return hasPassAccesses(passes[passIndex], [
|
|
303
|
+
{ resource: `bloom-downsample-${level}`, usage: 'sampled-read' },
|
|
304
|
+
{
|
|
305
|
+
resource:
|
|
306
|
+
level === expected.levels.length - 2
|
|
307
|
+
? `bloom-downsample-${level + 1}`
|
|
308
|
+
: `bloom-upsample-${level + 1}`,
|
|
309
|
+
usage: 'sampled-read',
|
|
310
|
+
},
|
|
311
|
+
{ resource: `bloom-upsample-${level}`, usage: 'color-attachment' },
|
|
312
|
+
]);
|
|
313
|
+
}) &&
|
|
314
|
+
hasPassAccesses(passes.at(-1), [
|
|
315
|
+
{ resource: sceneLabel, usage: 'sampled-read' },
|
|
316
|
+
{
|
|
317
|
+
resource: expected.levels.length === 1 ? 'bloom-downsample-0' : 'bloom-upsample-0',
|
|
318
|
+
usage: 'sampled-read',
|
|
319
|
+
},
|
|
320
|
+
{ resource: STANDARD_BLOOM_TARGET_SPEC.composited.label, usage: 'color-attachment' },
|
|
321
|
+
]);
|
|
322
|
+
if (!completeTargetSet || !passTopology) {
|
|
323
|
+
return invalidBloomGraphInspection();
|
|
324
|
+
}
|
|
325
|
+
const levelDimensions = orderedDownsample.flatMap((resource) => {
|
|
326
|
+
const size = resourceSize(resource);
|
|
327
|
+
return size === undefined ? [] : [size];
|
|
328
|
+
});
|
|
329
|
+
if (levelDimensions.length !== orderedDownsample.length) {
|
|
123
330
|
return invalidBloomGraphInspection();
|
|
124
331
|
}
|
|
332
|
+
const targetBytes = targets.reduce((bytes, resource) => bytes + (textureBytes(resource) ?? 0), 0);
|
|
125
333
|
return {
|
|
126
334
|
status: 'valid',
|
|
127
335
|
targetCount: targets.length,
|
|
128
|
-
targetBytes
|
|
129
|
-
(bytes, target) => bytes + textureBytes(target.resource, target.expected),
|
|
130
|
-
0,
|
|
131
|
-
),
|
|
336
|
+
targetBytes,
|
|
132
337
|
passCount: passes.length,
|
|
338
|
+
levelCount: orderedDownsample.length,
|
|
339
|
+
levelDimensions,
|
|
340
|
+
downsamplePassCount: orderedDownsample.length,
|
|
341
|
+
upsamplePassCount: orderedUpsample.length,
|
|
133
342
|
};
|
|
134
343
|
}
|
|
135
344
|
|
|
@@ -159,8 +368,8 @@ function missingTemporalContributor(
|
|
|
159
368
|
/**
|
|
160
369
|
* Build the one Standard post chain shared by direct and clustered lighting.
|
|
161
370
|
* Bloom is admitted before any Bloom target is declared, keeping the disabled
|
|
162
|
-
* path exact-zero while
|
|
163
|
-
*
|
|
371
|
+
* path exact-zero while building one bounded five-level HDR
|
|
372
|
+
* downsample/tent-upsample pyramid for the enabled path.
|
|
164
373
|
*/
|
|
165
374
|
export function addStandardPost(
|
|
166
375
|
context: RenderPipelineBuildContext<RenderPipelineFrame>,
|
|
@@ -178,7 +387,8 @@ export function addStandardPost(
|
|
|
178
387
|
lane: topology.standardProfile?.renderPath === 'deferred' ? 'clustered' : 'direct',
|
|
179
388
|
temporal: !clearOnly && topology.camera.antialias === 'taa',
|
|
180
389
|
meter: topology.output?.autoExposure === true,
|
|
181
|
-
bloom: !clearOnly && topology.camera
|
|
390
|
+
bloom: !clearOnly && standardBloomAdmitted(topology.camera),
|
|
391
|
+
dof: !clearOnly && topology.camera.depthOfField !== undefined,
|
|
182
392
|
exposure: topology.output?.autoExposure === true,
|
|
183
393
|
whiteBalance: topology.output?.whiteBalance === true,
|
|
184
394
|
lut: topology.output?.colorLut === true,
|
|
@@ -218,6 +428,33 @@ export function addStandardPost(
|
|
|
218
428
|
const finalPresent = surface.storage;
|
|
219
429
|
let postInput = scene;
|
|
220
430
|
let postTemporal = sceneTemporal;
|
|
431
|
+
if (!clearOnly && topology.analyticFog === true) {
|
|
432
|
+
if (depth.sampleCount !== 1)
|
|
433
|
+
return err(
|
|
434
|
+
new RenderGraphError({
|
|
435
|
+
code: 'resource-resolution-failed',
|
|
436
|
+
expected: 'analytic fog receives single-sample scene depth',
|
|
437
|
+
hint: 'select FXAA, TAA or no AA for analytic fog',
|
|
438
|
+
detail: { resourceLabel: 'scene-depth' },
|
|
439
|
+
}),
|
|
440
|
+
);
|
|
441
|
+
const fogged = target(context, 'analytic-fog-color', {
|
|
442
|
+
format: 'rgba16float',
|
|
443
|
+
size:
|
|
444
|
+
topology.extent === undefined ? 'surface' : renderExtentSize(topology.extent, 'internal'),
|
|
445
|
+
domain: hdr ? 'linear-hdr' : 'linear-ldr',
|
|
446
|
+
});
|
|
447
|
+
if (!fogged.ok) return fogged;
|
|
448
|
+
const fog = addTypedFullscreenPass(context.graph, {
|
|
449
|
+
name: 'analytic-fog',
|
|
450
|
+
shader: 'forgeax.analytic-fog',
|
|
451
|
+
input: postInput,
|
|
452
|
+
depth,
|
|
453
|
+
output: fogged.value,
|
|
454
|
+
});
|
|
455
|
+
if (!fog.ok) return fog;
|
|
456
|
+
postInput = fogged.value;
|
|
457
|
+
}
|
|
221
458
|
|
|
222
459
|
const autoExposure = context.standardOutput?.autoExposure;
|
|
223
460
|
if (outputPlan.features.exposure && autoExposure === undefined) {
|
|
@@ -239,7 +476,7 @@ export function addStandardPost(
|
|
|
239
476
|
return err(missingTemporalContributor(topology, 'forgeax::standard::taa-history'));
|
|
240
477
|
}
|
|
241
478
|
const resolveTargets: TypedTemporalResolveTargets = {
|
|
242
|
-
scene,
|
|
479
|
+
scene: postInput,
|
|
243
480
|
depth,
|
|
244
481
|
currentTemporal: sceneTemporal,
|
|
245
482
|
...(coverage === undefined ? {} : { coverage }),
|
|
@@ -260,6 +497,20 @@ export function addStandardPost(
|
|
|
260
497
|
postTemporal = history.currentTemporal;
|
|
261
498
|
}
|
|
262
499
|
|
|
500
|
+
// Meter the temporally resolved color before lens-domain filters. Motion
|
|
501
|
+
// Blur and Bloom must consume a single exposure decision for this frame;
|
|
502
|
+
// running the meter after either filter makes the measured luminance depend
|
|
503
|
+
// on the authored streak radius and changes the exposure feedback loop.
|
|
504
|
+
if (outputPlan.features.exposure && autoExposure !== undefined) {
|
|
505
|
+
const added = addAutoExposureGraphPasses(context, {
|
|
506
|
+
source: postInput,
|
|
507
|
+
resources: autoExposure,
|
|
508
|
+
width: topology.surface.width,
|
|
509
|
+
height: topology.surface.height,
|
|
510
|
+
});
|
|
511
|
+
if (!added.ok) return added;
|
|
512
|
+
}
|
|
513
|
+
|
|
263
514
|
// Feature projection and the built-in fallback share one target contract.
|
|
264
515
|
// The projection runs after TAA so Motion Blur consumes the resolved color,
|
|
265
516
|
// then both paths feed the same linear float target into Bloom/Output.
|
|
@@ -313,49 +564,46 @@ export function addStandardPost(
|
|
|
313
564
|
}
|
|
314
565
|
}
|
|
315
566
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
567
|
+
if (outputPlan.features.dof) {
|
|
568
|
+
const dof = addDepthOfFieldPasses(
|
|
569
|
+
{
|
|
570
|
+
graph: context.graph,
|
|
571
|
+
camera: context.camera ?? {},
|
|
572
|
+
temporalTaa: topology.temporal?.taa === true,
|
|
573
|
+
msaaActive: depth.sampleCount === 4,
|
|
574
|
+
...(topology.extent === undefined ? {} : { extent: topology.extent }),
|
|
575
|
+
},
|
|
576
|
+
postInput,
|
|
577
|
+
depth,
|
|
578
|
+
topology.temporal?.taa === true ? postTemporal : undefined,
|
|
579
|
+
);
|
|
580
|
+
if (!dof.ok) return dof;
|
|
581
|
+
postInput = dof.value;
|
|
326
582
|
}
|
|
327
|
-
|
|
328
583
|
if (bloomActive) {
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
);
|
|
584
|
+
const outputWidth = topology.extent?.outputWidth ?? topology.surface.width;
|
|
585
|
+
const outputHeight = topology.extent?.outputHeight ?? topology.surface.height;
|
|
586
|
+
const bloomSpecs = standardBloomTargetSpecs(outputWidth, outputHeight);
|
|
587
|
+
const composited = target(context, bloomSpecs.composited.label, bloomSpecs.composited);
|
|
334
588
|
if (!composited.ok) return composited;
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const blurV = target(
|
|
348
|
-
context,
|
|
349
|
-
STANDARD_BLOOM_TARGET_SPEC.blurV.label,
|
|
350
|
-
STANDARD_BLOOM_TARGET_SPEC.blurV,
|
|
351
|
-
);
|
|
352
|
-
if (!blurV.ok) return blurV;
|
|
589
|
+
const downsample: RenderPipelineTarget[] = [];
|
|
590
|
+
for (const spec of bloomSpecs.downsample) {
|
|
591
|
+
const level = target(context, spec.label, spec);
|
|
592
|
+
if (!level.ok) return level;
|
|
593
|
+
downsample.push(level.value);
|
|
594
|
+
}
|
|
595
|
+
const upsample: RenderPipelineTarget[] = [];
|
|
596
|
+
for (const spec of bloomSpecs.upsample) {
|
|
597
|
+
const level = target(context, spec.label, spec);
|
|
598
|
+
if (!level.ok) return level;
|
|
599
|
+
upsample.push(level.value);
|
|
600
|
+
}
|
|
353
601
|
const bloom = addTypedBloomPasses(context.graph, {
|
|
354
602
|
scene: postInput,
|
|
355
603
|
composited: composited.value,
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
604
|
+
downsample,
|
|
605
|
+
upsample,
|
|
606
|
+
levelDimensions: bloomSpecs.levels,
|
|
359
607
|
});
|
|
360
608
|
if (!bloom.ok) return bloom;
|
|
361
609
|
postInput = composited.value;
|
package/src/pipeline-spec.ts
CHANGED
|
@@ -562,8 +562,8 @@ export interface PassKindAttachmentPolicy {
|
|
|
562
562
|
* 4. `'point-shadow-caster'` — HDRP point-shadow caster: depth-only
|
|
563
563
|
* 5. `'skybox'` — fullscreen skybox: color-only clear/store
|
|
564
564
|
* 6. `'tonemap'` — HDR→LDR tonemap fullscreen: color-only clear/store
|
|
565
|
-
* 7. `'bloom-
|
|
566
|
-
* clear/store
|
|
565
|
+
* 7. `'bloom-downsample'` / `'bloom-upsample'` — multiscale Bloom pyramid:
|
|
566
|
+
* color-only clear/store
|
|
567
567
|
* 8. `'bloom-composite'` — bloom add-back: color-only load/store (NOT clear)
|
|
568
568
|
* 9. `'fxaa'` — fullscreen FXAA: color-only clear/store
|
|
569
569
|
* 10. `'post-process'` — generic fullscreen primitive (SSAO, render-graph
|
|
@@ -633,7 +633,7 @@ export const passKindPolicyTable: Readonly<Record<string, PassKindAttachmentPoli
|
|
|
633
633
|
},
|
|
634
634
|
defaultDepthOps: undefined,
|
|
635
635
|
},
|
|
636
|
-
'bloom-
|
|
636
|
+
'bloom-downsample': {
|
|
637
637
|
shape: 'color-only',
|
|
638
638
|
defaultColorOps: {
|
|
639
639
|
loadOp: 'clear',
|
|
@@ -642,7 +642,7 @@ export const passKindPolicyTable: Readonly<Record<string, PassKindAttachmentPoli
|
|
|
642
642
|
},
|
|
643
643
|
defaultDepthOps: undefined,
|
|
644
644
|
},
|
|
645
|
-
'bloom-
|
|
645
|
+
'bloom-upsample': {
|
|
646
646
|
shape: 'color-only',
|
|
647
647
|
defaultColorOps: {
|
|
648
648
|
loadOp: 'clear',
|
|
@@ -1220,7 +1220,7 @@ export function buildSpecConstTable(
|
|
|
1220
1220
|
// has sampleCount=4. Both use empty vertexLayout (fullscreen triangle, no
|
|
1221
1221
|
// attributes) and cullMode='none' (forward cull for fullscreen-quad passthrough).
|
|
1222
1222
|
//
|
|
1223
|
-
// Shadow-probe / fxaa /
|
|
1223
|
+
// Shadow-probe / fxaa / Bloom 10-pass / SSAO 2-stage are lazy-build — not in
|
|
1224
1224
|
// SPEC_CONST_TABLE (R-D4 decision: only entries that are always-present on
|
|
1225
1225
|
// every boot).
|
|
1226
1226
|
|
package/src/plugin.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Component, World } from '@forgeax/engine-ecs';
|
|
|
2
2
|
import type { Plugin } from '@forgeax/engine-plugin';
|
|
3
3
|
import { Atmosphere } from './components/atmosphere';
|
|
4
4
|
import { Camera } from './components/camera';
|
|
5
|
+
import { DepthOfField } from './components/depth-of-field';
|
|
5
6
|
import { DirectionalLight } from './components/directional-light';
|
|
6
7
|
import { DynamicResolution } from './components/dynamic-resolution';
|
|
7
8
|
import { Instances } from './components/instances';
|
|
@@ -33,6 +34,7 @@ const RENDER_COMPONENTS: readonly Component[] = [
|
|
|
33
34
|
Camera,
|
|
34
35
|
DirectionalLight,
|
|
35
36
|
DynamicResolution,
|
|
37
|
+
DepthOfField,
|
|
36
38
|
Instances,
|
|
37
39
|
Layer,
|
|
38
40
|
LightProbe,
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
import type { World } from '@forgeax/engine-ecs';
|
|
5
5
|
import type { RenderReadVersion } from '@forgeax/engine-ecs/projection';
|
|
6
6
|
import { vec3 } from '@forgeax/engine-math';
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
CompiledRenderGraphInfo,
|
|
9
|
+
ResolvedColorTargetDescriptor,
|
|
10
|
+
} from '@forgeax/engine-render-graph';
|
|
8
11
|
import type {
|
|
9
12
|
BindGroup,
|
|
10
13
|
BindGroupLayout,
|
|
@@ -17,7 +20,9 @@ import type { ClusterBinScratch } from '../cluster-binner';
|
|
|
17
20
|
import type { DirectionalShadowQuality } from '../components/directional-shadow-filter';
|
|
18
21
|
import type { EnvironmentGeneration } from '../environment/generation';
|
|
19
22
|
import type { EnvironmentLifecycle } from '../environment/lifecycle';
|
|
23
|
+
import type { DepthOfFieldParams } from '../features/depth-of-field/depth-of-field-params';
|
|
20
24
|
import type { GpuBuffer } from '../gpu-resource';
|
|
25
|
+
import type { MotionBlurExecutionReceipt } from '../inspection-types';
|
|
21
26
|
import type { InstanceCollectionStore } from '../instances';
|
|
22
27
|
import type { IblBindingInspection } from '../mesh-material-bindings';
|
|
23
28
|
import type { StandardLightingInspection } from '../pipeline/standard-lighting/inspection';
|
|
@@ -378,7 +383,8 @@ export function makeZeroCameraFallbackSnapshot(): CameraSnapshot {
|
|
|
378
383
|
bloom: 'off',
|
|
379
384
|
bloomThreshold: 1.0,
|
|
380
385
|
bloomIntensity: 1.0,
|
|
381
|
-
|
|
386
|
+
bloomSoftKnee: 0.5,
|
|
387
|
+
bloomScatter: 0.7,
|
|
382
388
|
clearColor: ZERO_CAMERA_CLEAR_FALLBACK,
|
|
383
389
|
};
|
|
384
390
|
}
|
|
@@ -403,6 +409,8 @@ export function makeZeroCameraFallbackSnapshot(): CameraSnapshot {
|
|
|
403
409
|
*/
|
|
404
410
|
export interface RenderFrameState {
|
|
405
411
|
frameNumber: number;
|
|
412
|
+
/** Last successful Motion Blur compute execution; failed submits retain the prior receipt. */
|
|
413
|
+
motionBlurExecution?: MotionBlurExecutionReceipt;
|
|
406
414
|
/** Final-submit Standard IBL binding-chain receipt for this frame. */
|
|
407
415
|
iblBindingInspection?: IblBindingInspection;
|
|
408
416
|
/** Demand for the frame-local Standard fallback MRT. */
|
|
@@ -452,6 +460,27 @@ export interface RenderFrameState {
|
|
|
452
460
|
compiledFrameGraphTopologyKey: string | null;
|
|
453
461
|
/** Monotonic generation of successfully promoted compiled graphs. */
|
|
454
462
|
compiledFrameGraphGeneration: number;
|
|
463
|
+
/**
|
|
464
|
+
* DoF graph candidate staged until the surrounding frame transaction
|
|
465
|
+
* submits. The previous graph, access projection, generation, and lighting
|
|
466
|
+
* signature are restored together when encoding or submission aborts.
|
|
467
|
+
*/
|
|
468
|
+
depthOfFieldPreviousGraph: CompiledRenderGraph<RenderPipelineFrame> | null;
|
|
469
|
+
depthOfFieldPreviousGraphKey: string | null;
|
|
470
|
+
depthOfFieldPreviousPerFrameGraph?: RenderFrameState['perFrameGraph'];
|
|
471
|
+
depthOfFieldPreviousGraphGeneration: number;
|
|
472
|
+
depthOfFieldPreviousStandardLightingGraphSignature: string;
|
|
473
|
+
depthOfFieldCandidateGraph: CompiledRenderGraph<RenderPipelineFrame> | null;
|
|
474
|
+
/** DoF graph/params detached at the same successful-submit boundary. */
|
|
475
|
+
depthOfFieldAccepted?:
|
|
476
|
+
| {
|
|
477
|
+
readonly params: DepthOfFieldParams;
|
|
478
|
+
readonly graph: CompiledRenderGraphInfo;
|
|
479
|
+
readonly deviceGeneration: number;
|
|
480
|
+
}
|
|
481
|
+
| undefined;
|
|
482
|
+
/** A DoF candidate was present when the frame failed to submit. */
|
|
483
|
+
depthOfFieldLastSubmitFailed?: boolean | undefined;
|
|
455
484
|
/**
|
|
456
485
|
* Last-known-good graph held while a volume topology candidate is tested.
|
|
457
486
|
* The candidate is promoted only after its command buffer is submitted;
|