@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
|
@@ -22,12 +22,16 @@ import type {
|
|
|
22
22
|
import { RhiError } from '@forgeax/engine-rhi';
|
|
23
23
|
import { err, ok, type Result } from '@forgeax/engine-types';
|
|
24
24
|
import { executeRendererFrameTransaction } from '../assembly/renderer-frame-transaction';
|
|
25
|
+
import { standardBloomAdmitted } from '../bloom-admission';
|
|
25
26
|
import { type RenderError, RenderFeatureStageFailedError } from '../errors/render';
|
|
27
|
+
import { depthOfFieldTopology } from '../features/depth-of-field/depth-of-field-feature';
|
|
28
|
+
import { resolveDepthOfFieldFrameParams } from '../features/depth-of-field/depth-of-field-params';
|
|
26
29
|
import {
|
|
27
30
|
getRenderFeaturePlanExecutionProjection,
|
|
28
31
|
hasSubmissionSensitiveFeatures,
|
|
29
32
|
type RenderFeaturePlanExecution,
|
|
30
33
|
} from '../features/host';
|
|
34
|
+
import { motionBlurTemporalDemand } from '../features/motion-blur/motion-blur-params';
|
|
31
35
|
import {
|
|
32
36
|
type RenderFeaturePlannedFrame,
|
|
33
37
|
renderFeaturePlanSignature,
|
|
@@ -60,7 +64,11 @@ import {
|
|
|
60
64
|
} from '../gpu-texture-usage';
|
|
61
65
|
import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_UNIFORM } from '../gpu-usage';
|
|
62
66
|
import { createStandardSurfaceLightingBindGroup } from '../hdrp-buffers';
|
|
63
|
-
import type {
|
|
67
|
+
import type {
|
|
68
|
+
MotionBlurExecutionPass,
|
|
69
|
+
MotionBlurExecutionReceipt,
|
|
70
|
+
RenderFeatureGraphInspection,
|
|
71
|
+
} from '../inspection-types';
|
|
64
72
|
import type { RenderExtent } from '../pipeline/render-extent';
|
|
65
73
|
import {
|
|
66
74
|
type StandardTopologyInputValue,
|
|
@@ -433,7 +441,15 @@ export function shouldUseTemporalFrameTransaction(
|
|
|
433
441
|
export function isMotionBlurTemporalDemand(
|
|
434
442
|
motionBlur: CameraSnapshot['motionBlur'] | undefined,
|
|
435
443
|
): boolean {
|
|
436
|
-
return
|
|
444
|
+
return (
|
|
445
|
+
motionBlur !== undefined &&
|
|
446
|
+
motionBlurTemporalDemand({
|
|
447
|
+
shutterAngle: motionBlur.shutterAngle,
|
|
448
|
+
maxRadiusPixels: motionBlur.maxRadiusPixels,
|
|
449
|
+
sampleCount: motionBlur.sampleCount,
|
|
450
|
+
targetFps: motionBlur.targetFps ?? 60,
|
|
451
|
+
})
|
|
452
|
+
);
|
|
437
453
|
}
|
|
438
454
|
|
|
439
455
|
/**
|
|
@@ -1362,6 +1378,7 @@ function topologyOf(
|
|
|
1362
1378
|
standardLighting: StandardTopologyInputValue | undefined,
|
|
1363
1379
|
ssr: SsrSpatialAdmission | undefined,
|
|
1364
1380
|
renderExtent: RenderExtent | undefined,
|
|
1381
|
+
analyticFog: boolean,
|
|
1365
1382
|
): Result<RenderPipelineTopology, RhiError> {
|
|
1366
1383
|
const cascadeCount = Math.max(1, Math.min(4, Math.round(lights.cascadeCount ?? 1))) as
|
|
1367
1384
|
| 1
|
|
@@ -1419,6 +1436,7 @@ function topologyOf(
|
|
|
1419
1436
|
: undefined;
|
|
1420
1437
|
const preparedLut =
|
|
1421
1438
|
frameState.pendingStandardLutGpuResources ?? frameState.standardLutGpuResources;
|
|
1439
|
+
const depthOfField = depthOfFieldTopology(camera.depthOfField);
|
|
1422
1440
|
return ok({
|
|
1423
1441
|
pipelineId: STANDARD_PIPELINE_ID,
|
|
1424
1442
|
standardProfile: internals.standardProfile,
|
|
@@ -1427,6 +1445,7 @@ function topologyOf(
|
|
|
1427
1445
|
reflectionFallback: { enabled: frameState.reflectionFallbackDemand === true },
|
|
1428
1446
|
ssr,
|
|
1429
1447
|
...(renderExtent === undefined ? {} : { extent: renderExtent }),
|
|
1448
|
+
analyticFog: !clearOnly && analyticFog,
|
|
1430
1449
|
surface: {
|
|
1431
1450
|
width,
|
|
1432
1451
|
height,
|
|
@@ -1437,7 +1456,12 @@ function topologyOf(
|
|
|
1437
1456
|
camera: {
|
|
1438
1457
|
tonemap: camera.tonemap,
|
|
1439
1458
|
antialias: camera.antialias,
|
|
1440
|
-
|
|
1459
|
+
// Bloom intensity is a frame parameter, not a graph identity. Fold
|
|
1460
|
+
// only its zero/non-zero admission into the closed topology switch so
|
|
1461
|
+
// positive intensity changes update the recorder UBO in place.
|
|
1462
|
+
bloom: standardBloomAdmitted(camera) ? 'on' : 'off',
|
|
1463
|
+
bloomIntensity: camera.bloomIntensity,
|
|
1464
|
+
...(depthOfField === undefined ? {} : { depthOfField }),
|
|
1441
1465
|
},
|
|
1442
1466
|
atmosphere: !clearOnly && frameState.environmentFrame?.source.kind === 'atmosphere',
|
|
1443
1467
|
output: {
|
|
@@ -1754,18 +1778,66 @@ export function settleVolumetricFogGraphCandidate(
|
|
|
1754
1778
|
if (candidate === null) return;
|
|
1755
1779
|
const previous = frameState.volumetricFogPreviousGraph ?? null;
|
|
1756
1780
|
const previousKey = frameState.volumetricFogPreviousGraphKey ?? null;
|
|
1781
|
+
// A frame can replace both the volume and DoF topology at once. DoF owns
|
|
1782
|
+
// the shared compiled graph's full projection (per-frame resolver,
|
|
1783
|
+
// generation, and lighting signature), so let it settle the graph exactly
|
|
1784
|
+
// once while volume clears only its own candidate marker.
|
|
1785
|
+
const sharedWithDepthOfField = frameState.depthOfFieldCandidateGraph === candidate;
|
|
1757
1786
|
if (submitted) {
|
|
1758
|
-
if (previous !== null && previous !== candidate)
|
|
1787
|
+
if (!sharedWithDepthOfField && previous !== null && previous !== candidate) {
|
|
1788
|
+
retire(frameState, previous);
|
|
1789
|
+
}
|
|
1759
1790
|
} else {
|
|
1760
|
-
if (
|
|
1761
|
-
|
|
1762
|
-
|
|
1791
|
+
if (!sharedWithDepthOfField) {
|
|
1792
|
+
if (candidate !== previous) retire(frameState, candidate);
|
|
1793
|
+
frameState.compiledFrameGraph = previous;
|
|
1794
|
+
frameState.compiledFrameGraphTopologyKey = previousKey;
|
|
1795
|
+
}
|
|
1763
1796
|
}
|
|
1764
1797
|
frameState.volumetricFogPreviousGraph = null;
|
|
1765
1798
|
frameState.volumetricFogPreviousGraphKey = null;
|
|
1766
1799
|
frameState.volumetricFogCandidateGraph = null;
|
|
1767
1800
|
}
|
|
1768
1801
|
|
|
1802
|
+
/**
|
|
1803
|
+
* Settle a DoF graph replacement at the same submit barrier as the frame.
|
|
1804
|
+
* Graph compilation publishes a candidate for the current record pass, but a
|
|
1805
|
+
* failed encode/finish/submit must restore every graph-owned LKG projection
|
|
1806
|
+
* before the next frame can inspect or reuse it.
|
|
1807
|
+
*/
|
|
1808
|
+
export function settleDepthOfFieldGraphCandidate(
|
|
1809
|
+
frameState: RenderFrameState,
|
|
1810
|
+
submitted: boolean,
|
|
1811
|
+
): void {
|
|
1812
|
+
const candidate = frameState.depthOfFieldCandidateGraph;
|
|
1813
|
+
if (candidate === null || candidate === undefined) return;
|
|
1814
|
+
// Sparse synthetic frame states used by graph tests predate the explicit
|
|
1815
|
+
// transaction fields. Treat omitted pending values as the empty LKG rather
|
|
1816
|
+
// than passing undefined into retirement or restoring an invalid key.
|
|
1817
|
+
const previous = frameState.depthOfFieldPreviousGraph ?? null;
|
|
1818
|
+
if (submitted) {
|
|
1819
|
+
if (previous !== null && previous !== candidate) retire(frameState, previous);
|
|
1820
|
+
} else {
|
|
1821
|
+
if (candidate !== previous) retire(frameState, candidate);
|
|
1822
|
+
frameState.compiledFrameGraph = previous;
|
|
1823
|
+
frameState.compiledFrameGraphTopologyKey = frameState.depthOfFieldPreviousGraphKey ?? null;
|
|
1824
|
+
if (frameState.depthOfFieldPreviousPerFrameGraph === undefined) {
|
|
1825
|
+
delete frameState.perFrameGraph;
|
|
1826
|
+
} else {
|
|
1827
|
+
frameState.perFrameGraph = frameState.depthOfFieldPreviousPerFrameGraph;
|
|
1828
|
+
}
|
|
1829
|
+
frameState.graphGeneration = frameState.depthOfFieldPreviousGraphGeneration ?? 0;
|
|
1830
|
+
frameState.standardLightingGraphSignature =
|
|
1831
|
+
frameState.depthOfFieldPreviousStandardLightingGraphSignature ?? '';
|
|
1832
|
+
}
|
|
1833
|
+
frameState.depthOfFieldPreviousGraph = null;
|
|
1834
|
+
frameState.depthOfFieldPreviousGraphKey = null;
|
|
1835
|
+
frameState.depthOfFieldPreviousPerFrameGraph = undefined;
|
|
1836
|
+
frameState.depthOfFieldPreviousGraphGeneration = 0;
|
|
1837
|
+
frameState.depthOfFieldPreviousStandardLightingGraphSignature = '';
|
|
1838
|
+
frameState.depthOfFieldCandidateGraph = null;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1769
1841
|
export function ensureCompiledFrameGraph(
|
|
1770
1842
|
internals: RenderSystemInternals,
|
|
1771
1843
|
frameState: RenderFrameState,
|
|
@@ -1787,10 +1859,23 @@ export function ensureCompiledFrameGraph(
|
|
|
1787
1859
|
onRecoveryError?: (error: unknown) => void,
|
|
1788
1860
|
ssr?: SsrSpatialAdmission,
|
|
1789
1861
|
renderExtent?: RenderExtent,
|
|
1862
|
+
analyticFog = false,
|
|
1790
1863
|
): CompiledRenderGraph<RenderPipelineFrame> | null {
|
|
1791
1864
|
const featureMetrics = mutableFeatureGraphInspection(internals);
|
|
1792
1865
|
featureMetrics.ensureCalls += 1;
|
|
1793
1866
|
const requestedStandardLightingSignature = standardLightingTopologySignature(standardLighting);
|
|
1867
|
+
const frameDepthOfField = resolveDepthOfFieldFrameParams(
|
|
1868
|
+
camera.depthOfField,
|
|
1869
|
+
camera.depthOfFieldError,
|
|
1870
|
+
frameState.depthOfFieldAccepted?.params,
|
|
1871
|
+
);
|
|
1872
|
+
// A rejected authoring request remains visible on the source camera, while
|
|
1873
|
+
// an accepted graph continues to receive its own params until a valid
|
|
1874
|
+
// replacement or explicit component removal is submitted.
|
|
1875
|
+
const graphCamera: CameraSnapshot =
|
|
1876
|
+
frameDepthOfField === undefined || frameDepthOfField === camera.depthOfField
|
|
1877
|
+
? camera
|
|
1878
|
+
: { ...camera, depthOfField: frameDepthOfField };
|
|
1794
1879
|
const lastKnownGood = (): CompiledRenderGraph<RenderPipelineFrame> | null =>
|
|
1795
1880
|
frameState.compiledFrameGraph !== null &&
|
|
1796
1881
|
!hasSubmissionSensitiveFeatures(getRenderFeatureGraphState(internals).plans) &&
|
|
@@ -1825,7 +1910,7 @@ export function ensureCompiledFrameGraph(
|
|
|
1825
1910
|
internals,
|
|
1826
1911
|
frameState,
|
|
1827
1912
|
pipelineState,
|
|
1828
|
-
|
|
1913
|
+
graphCamera,
|
|
1829
1914
|
clearOnly,
|
|
1830
1915
|
lights,
|
|
1831
1916
|
surfaceWidth,
|
|
@@ -1840,6 +1925,7 @@ export function ensureCompiledFrameGraph(
|
|
|
1840
1925
|
standardLighting,
|
|
1841
1926
|
ssr,
|
|
1842
1927
|
renderExtent,
|
|
1928
|
+
analyticFog,
|
|
1843
1929
|
);
|
|
1844
1930
|
if (!topologyResult.ok) {
|
|
1845
1931
|
featureMetrics.buildFailures += 1;
|
|
@@ -1854,6 +1940,16 @@ export function ensureCompiledFrameGraph(
|
|
|
1854
1940
|
const featurePostProcessEntries =
|
|
1855
1941
|
featureGraphCandidate?.fullscreenEffects ??
|
|
1856
1942
|
getRenderFeatureGraphState(internals).fullscreenEffects;
|
|
1943
|
+
// Motion Blur's compute plan intentionally has no raster fullscreen shader
|
|
1944
|
+
// declaration, but it is still a post chain consumer: Standard Post owns
|
|
1945
|
+
// the named `motion-input`/`motion-output` targets and must project it after
|
|
1946
|
+
// TAA. Keep the identity in this deferred set alongside declared fullscreen
|
|
1947
|
+
// effects so an early forward/deferred contribution cannot resolve its
|
|
1948
|
+
// scene-data target against an incomplete target map.
|
|
1949
|
+
const fullscreenFeatureIds = new Set([
|
|
1950
|
+
...featurePostProcessEntries.keys(),
|
|
1951
|
+
'forgeax.motion-blur',
|
|
1952
|
+
]);
|
|
1857
1953
|
const postProcessAdmission = resolvePostProcessChainAdmission(
|
|
1858
1954
|
requestedPostEffects,
|
|
1859
1955
|
(identity) => {
|
|
@@ -1901,8 +1997,19 @@ export function ensureCompiledFrameGraph(
|
|
|
1901
1997
|
},
|
|
1902
1998
|
}
|
|
1903
1999
|
: topologyResult.value;
|
|
2000
|
+
// Keep Bloom's positive parameter values out of the compiled identity. A
|
|
2001
|
+
// crossing of zero has already changed topology.camera.bloom above; the
|
|
2002
|
+
// remaining values are uploaded by the active recorder without rebuilding
|
|
2003
|
+
// targets or pipelines.
|
|
2004
|
+
const topologyIdentity = {
|
|
2005
|
+
...topology,
|
|
2006
|
+
camera: {
|
|
2007
|
+
...topology.camera,
|
|
2008
|
+
bloomIntensity: undefined,
|
|
2009
|
+
},
|
|
2010
|
+
};
|
|
1904
2011
|
const key = JSON.stringify({
|
|
1905
|
-
topology,
|
|
2012
|
+
topology: topologyIdentity,
|
|
1906
2013
|
// Observation domains are an explicit per-frame graph demand. They are
|
|
1907
2014
|
// part of the cache identity so a request made after the ordinary graph
|
|
1908
2015
|
// was compiled can admit the matching tone/output capture boundaries on
|
|
@@ -2023,7 +2130,6 @@ export function ensureCompiledFrameGraph(
|
|
|
2023
2130
|
return lastKnownGood();
|
|
2024
2131
|
}
|
|
2025
2132
|
const ssrHistory = ssr?.status === 'admitted' ? importSsrHistoryTargets(builder) : undefined;
|
|
2026
|
-
let projectedFeatures = false;
|
|
2027
2133
|
const featureProjection = createRenderFeatureProjectionState();
|
|
2028
2134
|
let featureShadows:
|
|
2029
2135
|
| ReturnType<typeof projectRenderFeatureShadows<RenderPipelineFrame>>
|
|
@@ -2032,6 +2138,7 @@ export function ensureCompiledFrameGraph(
|
|
|
2032
2138
|
const built = frameState.activePipeline.build(
|
|
2033
2139
|
{
|
|
2034
2140
|
graph: builder,
|
|
2141
|
+
camera: { depthOfField: graphCamera.depthOfField },
|
|
2035
2142
|
observationCaptureDomains: internals.observationCaptureDomains,
|
|
2036
2143
|
...(autoExposureResources?.ok === true
|
|
2037
2144
|
? { standardOutput: { autoExposure: autoExposureResources.value } }
|
|
@@ -2092,11 +2199,20 @@ export function ensureCompiledFrameGraph(
|
|
|
2092
2199
|
namedTargets = {},
|
|
2093
2200
|
standardSurfaceAccesses = [],
|
|
2094
2201
|
) => {
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2202
|
+
// Forward/deferred scene lanes contribute feature plans before the
|
|
2203
|
+
// Standard post chain has created its named input/output targets.
|
|
2204
|
+
// Fullscreen effects (Motion Blur included) must wait for that later
|
|
2205
|
+
// call so their temporal input is resolved after TAA. Other feature
|
|
2206
|
+
// plans can still be projected at the forward contribution point.
|
|
2207
|
+
const plans =
|
|
2208
|
+
Object.keys(namedTargets).length === 0
|
|
2209
|
+
? projectedFeaturePlans.value.filter(
|
|
2210
|
+
(execution) => !fullscreenFeatureIds.has(execution.featureIdentity),
|
|
2211
|
+
)
|
|
2212
|
+
: projectedFeaturePlans.value;
|
|
2213
|
+
if (plans.length === 0) return ok(undefined);
|
|
2098
2214
|
const resolveTarget = targetResolver(targets, semanticTargets, namedTargets);
|
|
2099
|
-
const projected = projectPlanExecutions(builder,
|
|
2215
|
+
const projected = projectPlanExecutions(builder, plans, {
|
|
2100
2216
|
state: featureProjection,
|
|
2101
2217
|
resolveTarget,
|
|
2102
2218
|
standardSurfaceAccesses,
|
|
@@ -2172,10 +2288,32 @@ export function ensureCompiledFrameGraph(
|
|
|
2172
2288
|
return lastKnownGood();
|
|
2173
2289
|
}
|
|
2174
2290
|
const previous = frameState.compiledFrameGraph;
|
|
2291
|
+
const depthOfFieldReplacement =
|
|
2292
|
+
(frameState.depthOfFieldCandidateGraph === null ||
|
|
2293
|
+
frameState.depthOfFieldCandidateGraph === undefined) &&
|
|
2294
|
+
(topologyResult.value.camera.depthOfField !== undefined ||
|
|
2295
|
+
frameState.depthOfFieldAccepted !== undefined);
|
|
2175
2296
|
if (volumeTopologyCandidate) {
|
|
2176
2297
|
frameState.volumetricFogPreviousGraph = previous;
|
|
2177
2298
|
frameState.volumetricFogPreviousGraphKey = frameState.compiledFrameGraphTopologyKey;
|
|
2178
2299
|
frameState.volumetricFogCandidateGraph = compiled.value;
|
|
2300
|
+
if (depthOfFieldReplacement) {
|
|
2301
|
+
frameState.depthOfFieldPreviousGraph = previous;
|
|
2302
|
+
frameState.depthOfFieldPreviousGraphKey = frameState.compiledFrameGraphTopologyKey;
|
|
2303
|
+
frameState.depthOfFieldPreviousPerFrameGraph = frameState.perFrameGraph;
|
|
2304
|
+
frameState.depthOfFieldPreviousGraphGeneration = frameState.graphGeneration;
|
|
2305
|
+
frameState.depthOfFieldPreviousStandardLightingGraphSignature =
|
|
2306
|
+
frameState.standardLightingGraphSignature;
|
|
2307
|
+
frameState.depthOfFieldCandidateGraph = compiled.value;
|
|
2308
|
+
}
|
|
2309
|
+
} else if (depthOfFieldReplacement) {
|
|
2310
|
+
frameState.depthOfFieldPreviousGraph = previous;
|
|
2311
|
+
frameState.depthOfFieldPreviousGraphKey = frameState.compiledFrameGraphTopologyKey;
|
|
2312
|
+
frameState.depthOfFieldPreviousPerFrameGraph = frameState.perFrameGraph;
|
|
2313
|
+
frameState.depthOfFieldPreviousGraphGeneration = frameState.graphGeneration;
|
|
2314
|
+
frameState.depthOfFieldPreviousStandardLightingGraphSignature =
|
|
2315
|
+
frameState.standardLightingGraphSignature;
|
|
2316
|
+
frameState.depthOfFieldCandidateGraph = compiled.value;
|
|
2179
2317
|
}
|
|
2180
2318
|
frameState.compiledFrameGraph = compiled.value;
|
|
2181
2319
|
frameState.compiledFrameGraphTopologyKey = key;
|
|
@@ -2209,10 +2347,99 @@ export function ensureCompiledFrameGraph(
|
|
|
2209
2347
|
} else if (featureGraphCandidate !== undefined) {
|
|
2210
2348
|
recordRenderFeatureCandidateEvent(internals, featureGraphCandidate, 'abandoned');
|
|
2211
2349
|
}
|
|
2212
|
-
if (!volumeTopologyCandidate && previous !== null)
|
|
2350
|
+
if (!volumeTopologyCandidate && !depthOfFieldReplacement && previous !== null) {
|
|
2351
|
+
retire(frameState, previous);
|
|
2352
|
+
}
|
|
2213
2353
|
return compiled.value;
|
|
2214
2354
|
}
|
|
2215
2355
|
|
|
2356
|
+
/**
|
|
2357
|
+
* Project one accepted Motion Blur compute execution from the same graph and
|
|
2358
|
+
* resolved dispatch objects used by the encoder. A plan/capability candidate
|
|
2359
|
+
* without both executed passes deliberately returns no receipt.
|
|
2360
|
+
*/
|
|
2361
|
+
function deriveMotionBlurExecutionReceipt(
|
|
2362
|
+
internals: RenderSystemInternals,
|
|
2363
|
+
frameState: RenderFrameState,
|
|
2364
|
+
): MotionBlurExecutionReceipt | undefined {
|
|
2365
|
+
const graph = frameState.compiledFrameGraph;
|
|
2366
|
+
const deviceGeneration = internals.deviceScope?.generation;
|
|
2367
|
+
if (graph === null || deviceGeneration === undefined) return undefined;
|
|
2368
|
+
const info = graph.inspect();
|
|
2369
|
+
const graphPasses = new Map(info.passes.map((pass) => [pass.name, pass]));
|
|
2370
|
+
const summaryGraphPass = graphPasses.get('motion-blur-tile-summary');
|
|
2371
|
+
const reconstructGraphPass = graphPasses.get('motion-blur');
|
|
2372
|
+
if (summaryGraphPass?.kind !== 'compute' || reconstructGraphPass?.kind !== 'compute') {
|
|
2373
|
+
return undefined;
|
|
2374
|
+
}
|
|
2375
|
+
const planned = getRenderFeatureGraphState(internals).plans.find(
|
|
2376
|
+
(candidate) => candidate.featureIdentity === 'forgeax.motion-blur',
|
|
2377
|
+
);
|
|
2378
|
+
const execution =
|
|
2379
|
+
planned === undefined ? undefined : getRenderFeaturePlanExecutionProjection(planned);
|
|
2380
|
+
if (execution === undefined || execution.featureIdentity !== 'forgeax.motion-blur') {
|
|
2381
|
+
return undefined;
|
|
2382
|
+
}
|
|
2383
|
+
const executionPass = (name: 'motion-blur-tile-summary' | 'motion-blur') =>
|
|
2384
|
+
execution.passes.find((pass) => pass.name === name);
|
|
2385
|
+
const summary = executionPass('motion-blur-tile-summary');
|
|
2386
|
+
const reconstruct = executionPass('motion-blur');
|
|
2387
|
+
if (
|
|
2388
|
+
summary?.gpuCompute === undefined ||
|
|
2389
|
+
reconstruct?.gpuCompute === undefined ||
|
|
2390
|
+
summary.resolvedGpuCompute === undefined ||
|
|
2391
|
+
reconstruct.resolvedGpuCompute === undefined
|
|
2392
|
+
) {
|
|
2393
|
+
return undefined;
|
|
2394
|
+
}
|
|
2395
|
+
const directWorkgroups = (
|
|
2396
|
+
pass: NonNullable<typeof summary.resolvedGpuCompute>,
|
|
2397
|
+
): readonly [number, number, number] | undefined => {
|
|
2398
|
+
const dispatch = pass.dispatches[0];
|
|
2399
|
+
if (dispatch === undefined || !('workgroups' in dispatch)) return undefined;
|
|
2400
|
+
return dispatch.workgroups;
|
|
2401
|
+
};
|
|
2402
|
+
const summaryWorkgroups = directWorkgroups(summary.resolvedGpuCompute);
|
|
2403
|
+
const reconstructWorkgroups = directWorkgroups(reconstruct.resolvedGpuCompute);
|
|
2404
|
+
if (summaryWorkgroups === undefined || reconstructWorkgroups === undefined) return undefined;
|
|
2405
|
+
const outputResource = info.resources.find(
|
|
2406
|
+
(resource) =>
|
|
2407
|
+
resource.label === 'motion-blurred-color' ||
|
|
2408
|
+
reconstructGraphPass.accesses.some(
|
|
2409
|
+
(access) => access.resource === resource.label && access.usage === 'storage-write',
|
|
2410
|
+
),
|
|
2411
|
+
);
|
|
2412
|
+
if (
|
|
2413
|
+
outputResource?.descriptor.kind !== 'texture' ||
|
|
2414
|
+
outputResource.descriptor.format !== 'rgba16float'
|
|
2415
|
+
) {
|
|
2416
|
+
return undefined;
|
|
2417
|
+
}
|
|
2418
|
+
const summaryReceipt: MotionBlurExecutionPass = {
|
|
2419
|
+
name: 'motion-blur-tile-summary',
|
|
2420
|
+
kind: 'compute',
|
|
2421
|
+
entryPoint: 'tile_summary',
|
|
2422
|
+
workgroups: summaryWorkgroups,
|
|
2423
|
+
};
|
|
2424
|
+
const reconstructReceipt: MotionBlurExecutionPass = {
|
|
2425
|
+
name: 'motion-blur',
|
|
2426
|
+
kind: 'compute',
|
|
2427
|
+
entryPoint: 'reconstruct',
|
|
2428
|
+
workgroups: reconstructWorkgroups,
|
|
2429
|
+
};
|
|
2430
|
+
return Object.freeze({
|
|
2431
|
+
frameId: frameState.frameNumber,
|
|
2432
|
+
deviceGeneration,
|
|
2433
|
+
// RenderGraphBuilder generations are process-global. The renderer's
|
|
2434
|
+
// frame-state generation is the identity used by inspection and by the
|
|
2435
|
+
// same-frame admission check below, so retain that owner-scoped value.
|
|
2436
|
+
graphGeneration: frameState.graphGeneration,
|
|
2437
|
+
program: 'motion-blur-compute' as const,
|
|
2438
|
+
outputFormat: 'rgba16float' as const,
|
|
2439
|
+
passes: [summaryReceipt, reconstructReceipt] as const,
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2216
2443
|
export function executeCompiledFrameGraph(
|
|
2217
2444
|
internals: RenderSystemInternals,
|
|
2218
2445
|
frameState: RenderFrameState,
|
|
@@ -2245,9 +2472,11 @@ export function executeCompiledFrameGraph(
|
|
|
2245
2472
|
frameState.pendingAutoExposureState = undefined;
|
|
2246
2473
|
frameState.pendingStandardLutState = undefined;
|
|
2247
2474
|
settleVolumetricFogGraphCandidate(frameState, false);
|
|
2475
|
+
settleDepthOfFieldGraphCandidate(frameState, false);
|
|
2248
2476
|
rejectTemporalFrame();
|
|
2249
2477
|
return false;
|
|
2250
2478
|
}
|
|
2479
|
+
const motionBlurExecution = deriveMotionBlurExecutionReceipt(internals, frameState);
|
|
2251
2480
|
const legacyTimingInstrumentation =
|
|
2252
2481
|
timingCapture === undefined ? undefined : createTimingInstrumentation(timingCapture);
|
|
2253
2482
|
const timingHost = internals as RenderSystemInternals & {
|
|
@@ -2308,6 +2537,8 @@ export function executeCompiledFrameGraph(
|
|
|
2308
2537
|
},
|
|
2309
2538
|
}),
|
|
2310
2539
|
);
|
|
2540
|
+
settleVolumetricFogGraphCandidate(frameState, false);
|
|
2541
|
+
settleDepthOfFieldGraphCandidate(frameState, false);
|
|
2311
2542
|
return false;
|
|
2312
2543
|
}
|
|
2313
2544
|
const capturedGeneration = deviceScope?.generation;
|
|
@@ -2868,6 +3099,11 @@ export function executeCompiledFrameGraph(
|
|
|
2868
3099
|
commit: () => {
|
|
2869
3100
|
resolvedFrameHooks?.onSubmitted?.();
|
|
2870
3101
|
legacySubmitCommit?.();
|
|
3102
|
+
if (motionBlurExecution === undefined) {
|
|
3103
|
+
delete frameState.motionBlurExecution;
|
|
3104
|
+
} else {
|
|
3105
|
+
frameState.motionBlurExecution = motionBlurExecution;
|
|
3106
|
+
}
|
|
2871
3107
|
if (frameState.temporalFrameInput !== undefined) {
|
|
2872
3108
|
const temporal = frameState.temporalFrameTransaction.commit({ accepted: true });
|
|
2873
3109
|
if (temporal.ok) {
|
|
@@ -3000,6 +3236,8 @@ export function executeCompiledFrameGraph(
|
|
|
3000
3236
|
frameState.pendingAtmospherePublish?.();
|
|
3001
3237
|
frameState.pendingAtmospherePublish = undefined;
|
|
3002
3238
|
onSubmitted?.(internals.device.queue.onSubmittedWorkDone());
|
|
3239
|
+
settleVolumetricFogGraphCandidate(frameState, true);
|
|
3240
|
+
settleDepthOfFieldGraphCandidate(frameState, true);
|
|
3003
3241
|
},
|
|
3004
3242
|
abort: (failure) => {
|
|
3005
3243
|
frameState.pendingAtmospherePublish = undefined;
|
|
@@ -3038,9 +3276,15 @@ export function executeCompiledFrameGraph(
|
|
|
3038
3276
|
frameState.environmentLifecycle?.discard(frameState.environmentGeneration);
|
|
3039
3277
|
frameState.environmentGeneration = undefined;
|
|
3040
3278
|
}
|
|
3279
|
+
settleVolumetricFogGraphCandidate(frameState, false);
|
|
3280
|
+
settleDepthOfFieldGraphCandidate(frameState, false);
|
|
3041
3281
|
},
|
|
3042
3282
|
});
|
|
3283
|
+
// The callbacks above settle the normal transaction path. Keep this guard
|
|
3284
|
+
// for synthetic callers and any future early transaction result that does
|
|
3285
|
+
// not enter a callback.
|
|
3043
3286
|
settleVolumetricFogGraphCandidate(frameState, transaction.ok);
|
|
3287
|
+
settleDepthOfFieldGraphCandidate(frameState, transaction.ok);
|
|
3044
3288
|
if (!transaction.ok) rejectTemporalFrame();
|
|
3045
3289
|
return transaction.ok;
|
|
3046
3290
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from '@forgeax/engine-rhi';
|
|
12
12
|
import type { Handle, MeshAsset, SamplerAsset, TextureAsset } from '@forgeax/engine-types';
|
|
13
13
|
import { toShared } from '@forgeax/engine-types';
|
|
14
|
+
import { standardBloomAdmitted } from '../bloom-admission';
|
|
14
15
|
import { createClusterBinScratch } from '../cluster-binner';
|
|
15
16
|
import type { EnvironmentLifecycle } from '../environment/lifecycle';
|
|
16
17
|
import {
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
type RenderFeaturePreparedGraphicsResolverInput,
|
|
20
21
|
runRenderFeatureFrame,
|
|
21
22
|
} from '../features/host';
|
|
23
|
+
import { motionBlurTemporalDemand } from '../features/motion-blur/motion-blur-params';
|
|
22
24
|
import type { RenderFeatureGpuWorkOwner } from '../features/prepared-gpu-work';
|
|
23
25
|
import { resolveStandardRenderFeatureTargets } from '../features/targets';
|
|
24
26
|
import {
|
|
@@ -232,6 +234,12 @@ export function createRenderSystemRecovery(
|
|
|
232
234
|
directionalShadowCacheRecorded: false,
|
|
233
235
|
compiledFrameGraph: null,
|
|
234
236
|
compiledFrameGraphTopologyKey: null,
|
|
237
|
+
depthOfFieldPreviousGraph: null,
|
|
238
|
+
depthOfFieldPreviousGraphKey: null,
|
|
239
|
+
depthOfFieldPreviousPerFrameGraph: undefined,
|
|
240
|
+
depthOfFieldPreviousGraphGeneration: 0,
|
|
241
|
+
depthOfFieldPreviousStandardLightingGraphSignature: '',
|
|
242
|
+
depthOfFieldCandidateGraph: null,
|
|
235
243
|
volumetricFogPreviousGraph: null,
|
|
236
244
|
volumetricFogPreviousGraphKey: null,
|
|
237
245
|
volumetricFogCandidateGraph: null,
|
|
@@ -526,6 +534,10 @@ export function createRenderSystemRecovery(
|
|
|
526
534
|
worlds: seed.worlds,
|
|
527
535
|
owner: seed.resourceOwner,
|
|
528
536
|
frameNumber: candidateFrameState.frameNumber,
|
|
537
|
+
frameSize: {
|
|
538
|
+
width: runtime.internals.canvas.width,
|
|
539
|
+
height: runtime.internals.canvas.height,
|
|
540
|
+
},
|
|
529
541
|
visibilitySnapshots: seed.frame.featureVisibilitySnapshots,
|
|
530
542
|
hiddenEntityReports: seed.frame.hiddenEntityReports,
|
|
531
543
|
targets: featureTargets,
|
|
@@ -788,29 +800,35 @@ export function createRenderSystemRecovery(
|
|
|
788
800
|
throw new Error('recovery TAA pipeline is unavailable');
|
|
789
801
|
}
|
|
790
802
|
}
|
|
791
|
-
if (
|
|
803
|
+
if (
|
|
804
|
+
camera.motionBlur !== undefined &&
|
|
805
|
+
motionBlurTemporalDemand({
|
|
806
|
+
shutterAngle: camera.motionBlur.shutterAngle,
|
|
807
|
+
maxRadiusPixels: camera.motionBlur.maxRadiusPixels,
|
|
808
|
+
sampleCount: camera.motionBlur.sampleCount,
|
|
809
|
+
targetFps: camera.motionBlur.targetFps ?? 60,
|
|
810
|
+
})
|
|
811
|
+
) {
|
|
792
812
|
warmPostProcessPipeline('forgeax.motion-blur', 'rgba16float');
|
|
793
813
|
}
|
|
794
814
|
for (const id of candidatePostProcessIds) {
|
|
795
815
|
warmPostProcessPipeline(id, runtime.pipelineState.format as GPUTextureFormat);
|
|
796
816
|
}
|
|
797
|
-
if (camera
|
|
817
|
+
if (standardBloomAdmitted(camera)) {
|
|
798
818
|
runtime.pipelineState.perPassResources.ensureBloomResources?.();
|
|
799
819
|
const bloom = runtime.pipelineState.perPassResources.getBloomResources?.();
|
|
800
820
|
if (
|
|
801
821
|
bloom === null ||
|
|
802
822
|
bloom === undefined ||
|
|
803
|
-
bloom.
|
|
804
|
-
bloom.
|
|
805
|
-
bloom.bloomBlurVPipeline === null ||
|
|
823
|
+
bloom.bloomDownsamplePipeline === null ||
|
|
824
|
+
bloom.bloomUpsamplePipeline === null ||
|
|
806
825
|
bloom.bloomCompositePipeline === null ||
|
|
807
|
-
bloom.
|
|
808
|
-
bloom.
|
|
826
|
+
bloom.bloomDownsampleBindGroupLayout === null ||
|
|
827
|
+
bloom.bloomUpsampleBindGroupLayout === null ||
|
|
809
828
|
bloom.bloomCompositeBindGroupLayout === null ||
|
|
810
829
|
bloom.bloomSampler === null ||
|
|
811
|
-
bloom.
|
|
812
|
-
bloom.
|
|
813
|
-
bloom.bloomBlurVParamsBuffer === null ||
|
|
830
|
+
bloom.bloomDownsampleParamsBuffer === null ||
|
|
831
|
+
bloom.bloomUpsampleParamsBuffer === null ||
|
|
814
832
|
bloom.bloomCompositeParamsBuffer === null
|
|
815
833
|
) {
|
|
816
834
|
throw new Error('recovery Bloom resources are incomplete');
|
package/src/render-contract.ts
CHANGED
|
@@ -31,10 +31,15 @@ import type { EnvironmentInspection } from './environment/inspection.js';
|
|
|
31
31
|
import type { GpuDrivenPreparationError } from './errors/gpu-driven.js';
|
|
32
32
|
import type { RecoveryOutcome, RecoveryPhase } from './errors/recover.js';
|
|
33
33
|
import type { RenderError } from './errors/render.js';
|
|
34
|
+
import type {
|
|
35
|
+
DepthOfFieldParams,
|
|
36
|
+
DepthOfFieldRequestFailure,
|
|
37
|
+
} from './features/depth-of-field/depth-of-field-params.js';
|
|
34
38
|
import type { RenderFeature, RenderFeatureDiagnostics } from './features/types.js';
|
|
35
39
|
import type { PostProcessShaderEntry } from './fullscreen-post-process-pass.js';
|
|
36
40
|
import type {
|
|
37
41
|
BloomInspection,
|
|
42
|
+
DepthOfFieldInspection,
|
|
38
43
|
DirectionalShadowInspection,
|
|
39
44
|
LightInspection,
|
|
40
45
|
LodOcclusionInspection,
|
|
@@ -165,17 +170,24 @@ export interface CameraSnapshot {
|
|
|
165
170
|
readonly bloom: BloomEnabled;
|
|
166
171
|
readonly bloomThreshold: number;
|
|
167
172
|
readonly bloomIntensity: number;
|
|
168
|
-
readonly
|
|
173
|
+
readonly bloomSoftKnee: number;
|
|
174
|
+
readonly bloomScatter: number;
|
|
169
175
|
readonly clearColor: readonly [number, number, number, number];
|
|
170
176
|
/** Optional active-camera SSR authoring fact, copied from the ECS schema. */
|
|
171
177
|
readonly screenSpaceReflection?: ScreenSpaceReflectionData;
|
|
172
178
|
/** Renderer-owned temporal projection used by Standard's TAA producer. */
|
|
173
179
|
readonly temporal?: TemporalView;
|
|
180
|
+
/** Active-camera depth-of-field parameters, present only when demanded. */
|
|
181
|
+
readonly depthOfField?: DepthOfFieldParams;
|
|
182
|
+
/** Structured DoF validation facts; invalid requests remain inspectable. */
|
|
183
|
+
readonly depthOfFieldError?: DepthOfFieldRequestFailure;
|
|
174
184
|
/** Active-camera motion blur parameters, present only when demanded. */
|
|
175
185
|
readonly motionBlur?: {
|
|
176
186
|
readonly shutterAngle: number;
|
|
177
187
|
readonly maxRadiusPixels: number;
|
|
178
188
|
readonly sampleCount: number;
|
|
189
|
+
/** Target presentation rate; zero follows the accepted frame interval. */
|
|
190
|
+
readonly targetFps?: number;
|
|
179
191
|
};
|
|
180
192
|
}
|
|
181
193
|
|
|
@@ -557,6 +569,19 @@ export interface RenderFrameInput {
|
|
|
557
569
|
readonly leases: readonly RenderWorldLease[];
|
|
558
570
|
readonly camera: FrameCamera;
|
|
559
571
|
readonly environment: FrameEnvironment;
|
|
572
|
+
/**
|
|
573
|
+
* Monotonic host render-sample time in seconds. This is captured before ECS
|
|
574
|
+
* applies its simulation delta clamp, then accepted with frame submission.
|
|
575
|
+
* Omitting it keeps direct renderer callers on the ECS-clock fallback.
|
|
576
|
+
*/
|
|
577
|
+
readonly sampleTimeSeconds?: number;
|
|
578
|
+
/**
|
|
579
|
+
* Mark the next submitted frame as a fresh temporal baseline. App sets this
|
|
580
|
+
* on resume so a short wall-clock pause cannot be mistaken for a valid
|
|
581
|
+
* motion pair; direct replay callers may use the same explicit lifecycle
|
|
582
|
+
* boundary.
|
|
583
|
+
*/
|
|
584
|
+
readonly temporalReset?: boolean;
|
|
560
585
|
/** Fixed-step publication consumed by dynamic geometry candidates. */
|
|
561
586
|
readonly fixedStep?: number;
|
|
562
587
|
/** Optional profiler correlation token owned by App and consumed by Render. */
|
|
@@ -776,6 +801,8 @@ export interface RenderInspection {
|
|
|
776
801
|
readonly temporalTarget?: TemporalTargetInspection;
|
|
777
802
|
/** Renderer-owned Motion Blur facts; absent when no component is active. */
|
|
778
803
|
readonly motionBlur?: MotionBlurInspection;
|
|
804
|
+
/** Renderer-owned Depth of Field facts; absent when no component is active. */
|
|
805
|
+
readonly depthOfField?: DepthOfFieldInspection;
|
|
779
806
|
/** Bounded renderer-owned LOD/occlusion facts; absent before a candidate frame. */
|
|
780
807
|
readonly lodOcclusion?: LodOcclusionInspection;
|
|
781
808
|
}
|
|
@@ -800,6 +827,10 @@ export type DrawOwnerOptions = {
|
|
|
800
827
|
readonly resourceOwner: number;
|
|
801
828
|
/** Optional FrameCamera entity selection; absent preserves ActiveCamera semantics. */
|
|
802
829
|
readonly cameraEntityKey?: number;
|
|
830
|
+
/** Monotonic host render-sample time in seconds, before the ECS delta clamp. */
|
|
831
|
+
readonly sampleTimeSeconds?: number;
|
|
832
|
+
/** Explicit pause/resume or replay lifecycle boundary for temporal owners. */
|
|
833
|
+
readonly temporalReset?: boolean;
|
|
803
834
|
readonly profileFrame?: ProfileFrameToken;
|
|
804
835
|
};
|
|
805
836
|
|
|
@@ -898,9 +929,8 @@ export const RENDER_GRAPH_EXECUTION_PHASE_CATALOG = [
|
|
|
898
929
|
'record/graph-execute/skybox',
|
|
899
930
|
'record/graph-execute/main',
|
|
900
931
|
'record/graph-execute/fxaa',
|
|
901
|
-
'record/graph-execute/bloom-
|
|
902
|
-
'record/graph-execute/bloom-
|
|
903
|
-
'record/graph-execute/bloom-blur-v',
|
|
932
|
+
'record/graph-execute/bloom-downsample',
|
|
933
|
+
'record/graph-execute/bloom-upsample',
|
|
904
934
|
'record/graph-execute/bloom-composite',
|
|
905
935
|
'record/graph-execute/ssr-hiz',
|
|
906
936
|
'record/graph-execute/ssr-trace',
|
|
@@ -577,6 +577,7 @@ function dispatchFullscreenPass(
|
|
|
577
577
|
graphDepthView?: TextureView,
|
|
578
578
|
paramsOverride?: Uint8Array,
|
|
579
579
|
fragmentEntryPoint?: string,
|
|
580
|
+
msaaActive = false,
|
|
580
581
|
): void {
|
|
581
582
|
if (shader === 'fxaa') {
|
|
582
583
|
if (resolveCtx === undefined) {
|
|
@@ -643,7 +644,7 @@ function dispatchFullscreenPass(
|
|
|
643
644
|
const built = buildFullscreenPostProcessPass(
|
|
644
645
|
{ device: ctx.runtime.device, errorRegistry: ctx.runtime.errorRegistry },
|
|
645
646
|
entry,
|
|
646
|
-
|
|
647
|
+
msaaActive,
|
|
647
648
|
);
|
|
648
649
|
if (built === null) return;
|
|
649
650
|
|
|
@@ -842,6 +843,8 @@ export function encodeFullscreenPass(
|
|
|
842
843
|
readonly depthView?: TextureView | undefined;
|
|
843
844
|
readonly paramsOverride?: Uint8Array | undefined;
|
|
844
845
|
readonly fragmentEntryPoint?: string | undefined;
|
|
846
|
+
/** The depth target's actual sample count, when this typed pass reads depth. */
|
|
847
|
+
readonly msaaActive?: boolean | undefined;
|
|
845
848
|
},
|
|
846
849
|
): void {
|
|
847
850
|
dispatchFullscreenPass(
|
|
@@ -858,5 +861,9 @@ export function encodeFullscreenPass(
|
|
|
858
861
|
input.depthView,
|
|
859
862
|
input.paramsOverride,
|
|
860
863
|
input.fragmentEntryPoint,
|
|
864
|
+
// Legacy callers rely on the frame context's MSAA state. Typed callers
|
|
865
|
+
// pass the actual depth target sample count explicitly so a single-sample
|
|
866
|
+
// view can never select the multisampled shader by camera policy alone.
|
|
867
|
+
input.msaaActive ?? ctx.msaaActive,
|
|
861
868
|
);
|
|
862
869
|
}
|