@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
package/src/features/host.ts
CHANGED
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
PreparedGraphicsResolver,
|
|
18
18
|
PreparedGraphicsResourceLease,
|
|
19
19
|
} from '../prepare/prepared-graphics-resolver';
|
|
20
|
-
import type
|
|
20
|
+
import { isSceneDataTarget, type SceneDataTarget } from '../temporal/scene-data';
|
|
21
21
|
import { createSceneDataCatalog, type SceneDataCatalog } from '../temporal/scene-data-catalog';
|
|
22
22
|
import {
|
|
23
23
|
cloneRenderFeaturePlanSignatureSnapshot,
|
|
@@ -134,6 +134,8 @@ export interface RenderFeatureFrameInput {
|
|
|
134
134
|
readonly worlds: readonly import('@forgeax/engine-ecs').World[];
|
|
135
135
|
readonly owner: number;
|
|
136
136
|
readonly frameNumber: number;
|
|
137
|
+
/** Physical render extent for bounded feature dispatch sizing. */
|
|
138
|
+
readonly frameSize?: { readonly width: number; readonly height: number };
|
|
137
139
|
readonly visibilitySnapshots?: readonly RenderFeatureWorldVisibilitySnapshot[];
|
|
138
140
|
readonly hiddenEntityReports?: readonly RenderFeatureHiddenEntityReport[];
|
|
139
141
|
/** Active-pipeline logical targets available to producer-owned features. */
|
|
@@ -412,7 +414,7 @@ function asFeatureError(
|
|
|
412
414
|
if (error instanceof Error && typeof (error as Partial<RenderError>).code === 'string') {
|
|
413
415
|
return error as RenderError;
|
|
414
416
|
}
|
|
415
|
-
return new RenderFeatureStageFailedError(identity, order, stage, 'next-frame');
|
|
417
|
+
return new RenderFeatureStageFailedError(identity, order, stage, 'next-frame', error);
|
|
416
418
|
}
|
|
417
419
|
|
|
418
420
|
function featureErrorForSlot(slot: FeatureSlot, error: RenderError): RenderError {
|
|
@@ -431,6 +433,7 @@ function featureErrorForSlot(slot: FeatureSlot, error: RenderError): RenderError
|
|
|
431
433
|
slot.order,
|
|
432
434
|
'plan',
|
|
433
435
|
'next-frame',
|
|
436
|
+
error,
|
|
434
437
|
);
|
|
435
438
|
default:
|
|
436
439
|
return new RenderFeatureStageFailedError(
|
|
@@ -438,6 +441,7 @@ function featureErrorForSlot(slot: FeatureSlot, error: RenderError): RenderError
|
|
|
438
441
|
slot.order,
|
|
439
442
|
'plan',
|
|
440
443
|
'next-frame',
|
|
444
|
+
error,
|
|
441
445
|
);
|
|
442
446
|
}
|
|
443
447
|
}
|
|
@@ -467,6 +471,7 @@ function errorDescriptor(error: RenderError): RenderFeatureErrorDescriptor {
|
|
|
467
471
|
order: -1,
|
|
468
472
|
stage: 'plan',
|
|
469
473
|
recovery: 'next-frame',
|
|
474
|
+
cause: error,
|
|
470
475
|
},
|
|
471
476
|
};
|
|
472
477
|
}
|
|
@@ -606,14 +611,17 @@ function preparePlanResources(
|
|
|
606
611
|
: targetHandle(resource.logicalTarget, targets);
|
|
607
612
|
if (
|
|
608
613
|
resource.logicalTarget !== undefined &&
|
|
609
|
-
(logical === undefined ||
|
|
614
|
+
(logical === undefined ||
|
|
615
|
+
(typeof logical !== 'string' &&
|
|
616
|
+
!isRenderFeatureTargetHandle(logical) &&
|
|
617
|
+
!isSceneDataTarget(logical)))
|
|
610
618
|
) {
|
|
611
619
|
return err(planFailure(slot));
|
|
612
620
|
}
|
|
613
621
|
const prepared = gpu.prepareTextureView(
|
|
614
622
|
resource.name,
|
|
615
|
-
resource.resource.value as TextureView,
|
|
616
|
-
logical === undefined
|
|
623
|
+
resource.resource.value as TextureView | undefined,
|
|
624
|
+
logical === undefined ? undefined : logical,
|
|
617
625
|
);
|
|
618
626
|
if (!prepared.ok) return prepared;
|
|
619
627
|
preparedGpuResources.set(resource.name, {
|
|
@@ -1429,7 +1437,10 @@ export function runRenderFeatureFrame(
|
|
|
1429
1437
|
() =>
|
|
1430
1438
|
feature.plan(extracted.value, {
|
|
1431
1439
|
caps: input.caps,
|
|
1432
|
-
frame: {
|
|
1440
|
+
frame: {
|
|
1441
|
+
frameNumber: input.frameNumber,
|
|
1442
|
+
...(input.frameSize === undefined ? {} : input.frameSize),
|
|
1443
|
+
},
|
|
1433
1444
|
generation: transaction.generation,
|
|
1434
1445
|
targets,
|
|
1435
1446
|
sceneData,
|
|
@@ -1499,9 +1510,9 @@ export function runRenderFeatureFrame(
|
|
|
1499
1510
|
resolver === undefined
|
|
1500
1511
|
? undefined
|
|
1501
1512
|
: (descriptor: import('./prepared-graphics').RenderFeatureGraphicsPassDescriptor) => {
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
return
|
|
1513
|
+
// A pending pass can be skipped while earlier passes remain usable.
|
|
1514
|
+
// Keep their shared resolver leases alive until submission retires them.
|
|
1515
|
+
return resolveGraphicsSnapshot(resolver, descriptor, transaction.generation);
|
|
1505
1516
|
};
|
|
1506
1517
|
const releaseResolver = (): void => {
|
|
1507
1518
|
if (resolver !== undefined) resolver.release();
|
|
@@ -1529,9 +1540,11 @@ export function runRenderFeatureFrame(
|
|
|
1529
1540
|
host.setStatus(identity, 'failed', slot.latestError);
|
|
1530
1541
|
continue;
|
|
1531
1542
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1543
|
+
// Include partial allocations from skipped pending passes, even when no pass
|
|
1544
|
+
// completed. Fatal projection failures release the resolver above instead.
|
|
1545
|
+
const graphicsLeases =
|
|
1546
|
+
resolver?.leases ??
|
|
1547
|
+
projected.value.passes.flatMap((pass) => pass.resolvedGraphics?.leases ?? []);
|
|
1535
1548
|
if (graphicsLeases.length > 0) requiresPreparedResourceKey = true;
|
|
1536
1549
|
const leases = [...new Set(graphicsLeases), ...(gpu?.retireUntouched() ?? [])];
|
|
1537
1550
|
const retained = host.retainPreparedGraphics(identity, leases);
|
|
@@ -1545,7 +1558,7 @@ export function runRenderFeatureFrame(
|
|
|
1545
1558
|
if (feature.onFrameSubmitted !== undefined) {
|
|
1546
1559
|
submissionSensitivePlans.add(plannedFrame);
|
|
1547
1560
|
submissionAcknowledgments.set(plannedFrame, () =>
|
|
1548
|
-
feature.onFrameSubmitted?.(extracted.value),
|
|
1561
|
+
feature.onFrameSubmitted?.(extracted.value, projected.value),
|
|
1549
1562
|
);
|
|
1550
1563
|
}
|
|
1551
1564
|
plans.push(plannedFrame);
|
|
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
|
|
|
2
2
|
import { SCENE_DATA_TEMPORAL_V1_SCHEMA } from '../../../temporal/scene-data';
|
|
3
3
|
import { createSceneDataCatalog } from '../../../temporal/scene-data-catalog';
|
|
4
4
|
import { freezeRenderFeaturePlan } from '../../plan';
|
|
5
|
-
import { planMotionBlur } from '../motion-blur-feature';
|
|
5
|
+
import { MOTION_BLUR_COMPUTE_WGSL, planMotionBlur } from '../motion-blur-feature';
|
|
6
6
|
import { DEFAULT_MOTION_BLUR_PARAMS } from '../motion-blur-params';
|
|
7
7
|
|
|
8
8
|
const caps = {
|
|
@@ -64,4 +64,114 @@ describe('Motion Blur feature plan', () => {
|
|
|
64
64
|
expect(pass.sampledTargets).not.toContain('motion-depth');
|
|
65
65
|
expect(catalog.schema).toBe(SCENE_DATA_TEMPORAL_V1_SCHEMA);
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
it('selects one tile summary and one fused reconstruction on compute-capable targets', () => {
|
|
69
|
+
const computeCaps = {
|
|
70
|
+
rgba16floatRenderable: true,
|
|
71
|
+
compute: true,
|
|
72
|
+
storageBuffer: true,
|
|
73
|
+
storageTexture: true,
|
|
74
|
+
} as never;
|
|
75
|
+
const catalog = createSceneDataCatalog({
|
|
76
|
+
featureIdentity: 'forgeax.motion-blur',
|
|
77
|
+
generation: 4,
|
|
78
|
+
planIdentity: 'forgeax.motion-blur:4',
|
|
79
|
+
rgba16floatRenderable: true,
|
|
80
|
+
});
|
|
81
|
+
const targets = [
|
|
82
|
+
{
|
|
83
|
+
name: 'motion-input',
|
|
84
|
+
kind: 'color' as const,
|
|
85
|
+
format: 'rgba16float' as const,
|
|
86
|
+
sampleCount: 1 as const,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'motion-output',
|
|
90
|
+
kind: 'color' as const,
|
|
91
|
+
format: 'rgba16float' as const,
|
|
92
|
+
sampleCount: 1 as const,
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
const planned = planMotionBlur(
|
|
96
|
+
DEFAULT_MOTION_BLUR_PARAMS,
|
|
97
|
+
{
|
|
98
|
+
caps: computeCaps,
|
|
99
|
+
frame: { frameNumber: 7, width: 1920, height: 1080 },
|
|
100
|
+
generation: 4,
|
|
101
|
+
targets,
|
|
102
|
+
sceneData: catalog,
|
|
103
|
+
},
|
|
104
|
+
{ frameDeltaSeconds: 1 / 60, reset: false },
|
|
105
|
+
);
|
|
106
|
+
expect(planned.ok).toBe(true);
|
|
107
|
+
if (!planned.ok) return;
|
|
108
|
+
const frozen = freezeRenderFeaturePlan('forgeax.motion-blur', planned.value, targets);
|
|
109
|
+
expect(frozen.ok).toBe(true);
|
|
110
|
+
if (!frozen.ok) return;
|
|
111
|
+
|
|
112
|
+
const computeProgram = frozen.value.resources.find(
|
|
113
|
+
(resource) => resource.kind === 'compute-program',
|
|
114
|
+
);
|
|
115
|
+
const tileSummary = frozen.value.resources.find(
|
|
116
|
+
(resource) => resource.kind === 'buffer' && resource.name === 'motion-blur-tile-summary',
|
|
117
|
+
);
|
|
118
|
+
expect(computeProgram?.kind).toBe('compute-program');
|
|
119
|
+
expect(tileSummary).toMatchObject({
|
|
120
|
+
kind: 'buffer',
|
|
121
|
+
size: Math.ceil(1920 / 16) * Math.ceil(1080 / 16) * 32,
|
|
122
|
+
usage: ['storage'],
|
|
123
|
+
});
|
|
124
|
+
expect(frozen.value.passes.map((pass) => pass.name)).toEqual([
|
|
125
|
+
'motion-blur-tile-summary',
|
|
126
|
+
'motion-blur',
|
|
127
|
+
]);
|
|
128
|
+
expect(frozen.value.passes).toHaveLength(2);
|
|
129
|
+
expect(frozen.value.passes[0]).toMatchObject({
|
|
130
|
+
kind: 'compute',
|
|
131
|
+
dispatches: [{ entryPoint: 'tile_summary', workgroups: [120, 68, 1] }],
|
|
132
|
+
});
|
|
133
|
+
expect(frozen.value.passes[1]).toMatchObject({
|
|
134
|
+
kind: 'compute',
|
|
135
|
+
dispatches: [{ entryPoint: 'reconstruct', workgroups: [240, 135, 1] }],
|
|
136
|
+
});
|
|
137
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('2.0 * (f32(index) + 0.5) / denominator - 1.0');
|
|
138
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('let sourcePixel = workgroupBase');
|
|
139
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('halfFilteredColor');
|
|
140
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('workgroupBarrier()');
|
|
141
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('if (!inBounds) {');
|
|
142
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('depthReject');
|
|
143
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('sourceVelocitySegmentCovers');
|
|
144
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('TILE_NEIGHBORHOOD_COUNT');
|
|
145
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('tileIntersectsWorkgroupSupport');
|
|
146
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('tileNeighborhoodOffset');
|
|
147
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('const EDGE_FILL_FACTOR : f32 = 0.99;');
|
|
148
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('let acceptedColor');
|
|
149
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('let acceptedAlpha');
|
|
150
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('let emptyReceiver');
|
|
151
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('let sourceScale');
|
|
152
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('let sourceContribution');
|
|
153
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('let trailScale');
|
|
154
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).toContain('let outputAlpha');
|
|
155
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).not.toContain('TILE_CANDIDATE_OFFSETS');
|
|
156
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).not.toContain('atomic');
|
|
157
|
+
expect(MOTION_BLUR_COMPUTE_WGSL).not.toContain('textureSampleLevel');
|
|
158
|
+
if (computeProgram?.kind === 'compute-program') {
|
|
159
|
+
const entries = computeProgram.program.bindings?.[0]?.entries ?? [];
|
|
160
|
+
expect(entries.find((entry) => entry.binding === 2)?.buffer?.type).toBe('storage');
|
|
161
|
+
expect(entries.find((entry) => entry.binding === 3)?.storageTexture?.access).toBe(
|
|
162
|
+
'write-only',
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('compiles the compute lane WGSL before it reaches a backend', async () => {
|
|
168
|
+
const { compileShader } = await import(
|
|
169
|
+
/* @vite-ignore */
|
|
170
|
+
new URL('../../../../../shader-compiler/dist/index.mjs', import.meta.url).href
|
|
171
|
+
);
|
|
172
|
+
const compiled = await compileShader(MOTION_BLUR_COMPUTE_WGSL, {
|
|
173
|
+
id: 'forgeax::motion-blur-compute',
|
|
174
|
+
});
|
|
175
|
+
expect(compiled.ok, compiled.ok ? undefined : String(compiled.error)).toBe(true);
|
|
176
|
+
});
|
|
67
177
|
});
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_MOTION_BLUR_PARAMS,
|
|
4
|
+
effectiveMotionBlurSampleCount,
|
|
5
|
+
isMotionBlurIntervalValid,
|
|
6
|
+
motionBlurExposureScale,
|
|
7
|
+
motionBlurSampleDelta,
|
|
4
8
|
motionBlurTemporalDemand,
|
|
5
9
|
validateMotionBlurParams,
|
|
6
10
|
} from '../motion-blur-params';
|
|
@@ -12,6 +16,7 @@ describe('Motion Blur parameter validation', () => {
|
|
|
12
16
|
shutterAngle: 180,
|
|
13
17
|
maxRadiusPixels: 32,
|
|
14
18
|
sampleCount: 8,
|
|
19
|
+
targetFps: 60,
|
|
15
20
|
});
|
|
16
21
|
});
|
|
17
22
|
|
|
@@ -32,6 +37,9 @@ describe('Motion Blur parameter validation', () => {
|
|
|
32
37
|
['sampleCount', 3],
|
|
33
38
|
['sampleCount', 17],
|
|
34
39
|
['sampleCount', 8.5],
|
|
40
|
+
['targetFps', -1],
|
|
41
|
+
['targetFps', 240.5],
|
|
42
|
+
['targetFps', 59.5],
|
|
35
43
|
] as const)('returns structured failure for %s=%s', (field, value) => {
|
|
36
44
|
const result = validateMotionBlurParams({ [field]: value });
|
|
37
45
|
expect(result.ok).toBe(false);
|
|
@@ -45,9 +53,56 @@ describe('Motion Blur parameter validation', () => {
|
|
|
45
53
|
|
|
46
54
|
it('does no temporal work when omitted or shutter is zero', () => {
|
|
47
55
|
expect(motionBlurTemporalDemand(undefined)).toBe(false);
|
|
48
|
-
expect(
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
expect(
|
|
57
|
+
motionBlurTemporalDemand({
|
|
58
|
+
shutterAngle: 0,
|
|
59
|
+
maxRadiusPixels: 32,
|
|
60
|
+
sampleCount: 8,
|
|
61
|
+
targetFps: 60,
|
|
62
|
+
}),
|
|
63
|
+
).toBe(false);
|
|
64
|
+
expect(
|
|
65
|
+
motionBlurTemporalDemand({
|
|
66
|
+
shutterAngle: 180,
|
|
67
|
+
maxRadiusPixels: 0,
|
|
68
|
+
sampleCount: 8,
|
|
69
|
+
targetFps: 60,
|
|
70
|
+
}),
|
|
71
|
+
).toBe(false);
|
|
51
72
|
expect(motionBlurTemporalDemand(DEFAULT_MOTION_BLUR_PARAMS)).toBe(true);
|
|
52
73
|
});
|
|
74
|
+
|
|
75
|
+
it('rounds authored sample budgets down to the fixed shader tiers', () => {
|
|
76
|
+
expect(effectiveMotionBlurSampleCount(3)).toBe(0);
|
|
77
|
+
expect(effectiveMotionBlurSampleCount(4)).toBe(4);
|
|
78
|
+
expect(effectiveMotionBlurSampleCount(7)).toBe(4);
|
|
79
|
+
expect(effectiveMotionBlurSampleCount(8)).toBe(8);
|
|
80
|
+
expect(effectiveMotionBlurSampleCount(15)).toBe(8);
|
|
81
|
+
expect(effectiveMotionBlurSampleCount(16)).toBe(16);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('rejects unstable intervals and scales displacement to the target rate', () => {
|
|
85
|
+
expect(isMotionBlurIntervalValid(1 / 60)).toBe(true);
|
|
86
|
+
expect(isMotionBlurIntervalValid(0)).toBe(false);
|
|
87
|
+
expect(isMotionBlurIntervalValid(0.101)).toBe(false);
|
|
88
|
+
expect(motionBlurExposureScale(1 / 60, 60)).toBeCloseTo(1);
|
|
89
|
+
expect(motionBlurExposureScale(1 / 120, 60)).toBeCloseTo(2);
|
|
90
|
+
expect(motionBlurExposureScale(1 / 60, 0)).toBe(1);
|
|
91
|
+
expect(motionBlurExposureScale(0, 60)).toBe(0);
|
|
92
|
+
expect(motionBlurExposureScale(1 / 60, 60.5)).toBe(0);
|
|
93
|
+
expect(motionBlurExposureScale(1 / 60, 241)).toBe(0);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('keeps raw render hitches visible before ECS clamp policy', () => {
|
|
97
|
+
expect(motionBlurSampleDelta(1.2, 1, 0.1)).toBeCloseTo(0.2);
|
|
98
|
+
expect(motionBlurSampleDelta(1 / 30, 0, 0.1)).toBeCloseTo(1 / 30);
|
|
99
|
+
expect(isMotionBlurIntervalValid(motionBlurSampleDelta(1.2, 1, 0.1))).toBe(false);
|
|
100
|
+
expect(isMotionBlurIntervalValid(motionBlurSampleDelta(1 / 30, 0, 0.1))).toBe(true);
|
|
101
|
+
expect(motionBlurSampleDelta(undefined, undefined, 0.1)).toBe(0.1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('fails closed for an explicitly invalid host/replay sample', () => {
|
|
105
|
+
expect(Number.isNaN(motionBlurSampleDelta(Number.NaN, 1, 1 / 60))).toBe(true);
|
|
106
|
+
expect(Number.isNaN(motionBlurSampleDelta(1, Number.POSITIVE_INFINITY, 1 / 60))).toBe(true);
|
|
107
|
+
});
|
|
53
108
|
});
|