@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
|
@@ -18,6 +18,12 @@ export function graphExecutionPhase(passName: string): RenderGraphExecutionPhase
|
|
|
18
18
|
return 'record/graph-execute/ssr-reflection-mip';
|
|
19
19
|
}
|
|
20
20
|
if (passName === 'ssr-compose') return 'record/graph-execute/ssr-compose';
|
|
21
|
+
if (passName.startsWith('bloom-downsample-')) {
|
|
22
|
+
return 'record/graph-execute/bloom-downsample';
|
|
23
|
+
}
|
|
24
|
+
if (passName.startsWith('bloom-upsample-')) {
|
|
25
|
+
return 'record/graph-execute/bloom-upsample';
|
|
26
|
+
}
|
|
21
27
|
switch (passName) {
|
|
22
28
|
case 'cluster-binner-upload':
|
|
23
29
|
case 'cluster-membership-producer':
|
|
@@ -47,12 +53,10 @@ export function graphExecutionPhase(passName: string): RenderGraphExecutionPhase
|
|
|
47
53
|
return 'record/graph-execute/main';
|
|
48
54
|
case 'fxaa':
|
|
49
55
|
return 'record/graph-execute/fxaa';
|
|
50
|
-
case 'bloom-
|
|
51
|
-
return 'record/graph-execute/bloom-
|
|
52
|
-
case 'bloom-
|
|
53
|
-
return 'record/graph-execute/bloom-
|
|
54
|
-
case 'bloom-blur-v':
|
|
55
|
-
return 'record/graph-execute/bloom-blur-v';
|
|
56
|
+
case 'bloom-downsample':
|
|
57
|
+
return 'record/graph-execute/bloom-downsample';
|
|
58
|
+
case 'bloom-upsample':
|
|
59
|
+
return 'record/graph-execute/bloom-upsample';
|
|
56
60
|
case 'bloom-composite':
|
|
57
61
|
return 'record/graph-execute/bloom-composite';
|
|
58
62
|
default:
|
package/src/record/frame.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { encodeAnalyticFogParams } from '../environment/analytic-fog';
|
|
2
|
+
import type { FogFrame } from '../extract/environment';
|
|
1
3
|
import { collectGpuDrivenMaterialArtifacts } from '../gpu-driven/material-artifacts';
|
|
2
4
|
import { materialBindingKey, projectMaterialBindingClasses } from '../gpu-driven/material-bindings';
|
|
3
5
|
import { uploadMaterialUniforms } from './material-uniforms';
|
|
@@ -5,7 +7,7 @@ import { uploadMaterialUniforms } from './material-uniforms';
|
|
|
5
7
|
// Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
|
|
6
8
|
|
|
7
9
|
import { HANDLE_NINESLICE_QUAD, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
8
|
-
import type
|
|
10
|
+
import { Time, type World } from '@forgeax/engine-ecs';
|
|
9
11
|
import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
10
12
|
import { deriveVertexCount } from '@forgeax/engine-geometry';
|
|
11
13
|
import { frustum, mat4, vec3 } from '@forgeax/engine-math';
|
|
@@ -34,8 +36,17 @@ import {
|
|
|
34
36
|
RendererOperationError,
|
|
35
37
|
} from '../errors/render';
|
|
36
38
|
import { gpuDrivenDrawKey, gpuDrivenSourceDrawItemIndex } from '../extract/gpu-driven';
|
|
39
|
+
import {
|
|
40
|
+
DEPTH_OF_FIELD_MSAA_POST_PROCESS_IDS,
|
|
41
|
+
DEPTH_OF_FIELD_POST_PROCESS_IDS,
|
|
42
|
+
} from '../features/depth-of-field/depth-of-field-feature';
|
|
43
|
+
import {
|
|
44
|
+
packDepthOfFieldParams,
|
|
45
|
+
resolveDepthOfFieldFrameParams,
|
|
46
|
+
} from '../features/depth-of-field/depth-of-field-params';
|
|
37
47
|
import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
|
|
38
48
|
import type { ShadowViewIdentity } from '../gpu-driven/shadow-views';
|
|
49
|
+
import type { GpuDrivenLodSubmitIdentity } from '../gpu-driven/view-gpu';
|
|
39
50
|
import { GpuBuffer } from '../gpu-resource';
|
|
40
51
|
import { GPU_TEXTURE_USAGE_COPY_SRC } from '../gpu-texture-usage';
|
|
41
52
|
import {
|
|
@@ -296,6 +307,14 @@ export async function observeCurrentFrame(
|
|
|
296
307
|
}
|
|
297
308
|
|
|
298
309
|
import { acknowledgeRenderFeatureSubmission } from '../features/host';
|
|
310
|
+
import {
|
|
311
|
+
effectiveMotionBlurSampleCount,
|
|
312
|
+
isMotionBlurIntervalValid,
|
|
313
|
+
MOTION_BLUR_PARAMS_BYTE_SIZE,
|
|
314
|
+
motionBlurExposureScale,
|
|
315
|
+
motionBlurSampleDelta,
|
|
316
|
+
motionBlurTemporalDemand,
|
|
317
|
+
} from '../features/motion-blur/motion-blur-params';
|
|
299
318
|
import { createTemporalView, resolveTemporalReset, type TemporalView } from '../temporal/view';
|
|
300
319
|
import { inspectVolumetricFog } from '../volume/inspection';
|
|
301
320
|
import {
|
|
@@ -323,6 +342,7 @@ import {
|
|
|
323
342
|
executeCompiledFrameGraph,
|
|
324
343
|
getRenderFeatureGraphState,
|
|
325
344
|
type RenderFeatureGraphCandidate,
|
|
345
|
+
settleDepthOfFieldGraphCandidate,
|
|
326
346
|
settleVolumetricFogGraphCandidate,
|
|
327
347
|
} from './typed-frame-graph';
|
|
328
348
|
import {
|
|
@@ -895,6 +915,7 @@ function volumeTemporalSignature(
|
|
|
895
915
|
densityGeneration: prepared.densityGeneration,
|
|
896
916
|
width,
|
|
897
917
|
height,
|
|
918
|
+
...(fog.worldTimeSeconds === undefined ? {} : { worldTimeSeconds: fog.worldTimeSeconds }),
|
|
898
919
|
...(fog.lightKind === undefined ? {} : { lightKind: fog.lightKind }),
|
|
899
920
|
...(fog.lightEntity === undefined ? {} : { lightEntity: fog.lightEntity }),
|
|
900
921
|
...(fog.pointLightEntity === undefined ? {} : { pointLightEntity: fog.pointLightEntity }),
|
|
@@ -999,7 +1020,7 @@ function prepareVolumetricFogFrame(
|
|
|
999
1020
|
}
|
|
1000
1021
|
return undefined;
|
|
1001
1022
|
}
|
|
1002
|
-
const params = new Float32Array(
|
|
1023
|
+
const params = new Float32Array(36);
|
|
1003
1024
|
params.set(fog.fog.bounds.min, 0);
|
|
1004
1025
|
params.set(fog.fog.bounds.max, 4);
|
|
1005
1026
|
params.set(fog.fog.extinction, 8);
|
|
@@ -1020,10 +1041,8 @@ function prepareVolumetricFogFrame(
|
|
|
1020
1041
|
const lightColor = selectedSpot?.color ?? lights.directional?.color ?? vec3.create(0, 0, 0);
|
|
1021
1042
|
if (lightDirection === undefined || lightColor === undefined) return undefined;
|
|
1022
1043
|
params.set(lightDirection, 20);
|
|
1023
|
-
//
|
|
1024
|
-
|
|
1025
|
-
// public fog component or allocating another uniform buffer.
|
|
1026
|
-
params[23] = frameState.frameNumber % 1024;
|
|
1044
|
+
// Reserved padding: render frame identity must not move volume samples.
|
|
1045
|
+
params[23] = 0;
|
|
1027
1046
|
const selectedPointIndex =
|
|
1028
1047
|
selectedPoint === undefined
|
|
1029
1048
|
? -1
|
|
@@ -1056,6 +1075,10 @@ function prepareVolumetricFogFrame(
|
|
|
1056
1075
|
frameState.temporalFrame !== undefined && frameState.temporalFrame.resetReason === 'none'
|
|
1057
1076
|
? 1
|
|
1058
1077
|
: 0;
|
|
1078
|
+
// World elapsed time is the sole authored-medium clock. The adjacent
|
|
1079
|
+
// frame phase above remains the deterministic sub-froxel jitter identity;
|
|
1080
|
+
// it never advances density motion.
|
|
1081
|
+
params[32] = fog.worldTimeSeconds ?? 0;
|
|
1059
1082
|
|
|
1060
1083
|
const paramsBuffer = stageVolumetricFogParams(internals, frameState, params);
|
|
1061
1084
|
if (paramsBuffer === undefined) return undefined;
|
|
@@ -1210,6 +1233,8 @@ function mapSsrHistoryResetReason(
|
|
|
1210
1233
|
case 'environment-change':
|
|
1211
1234
|
case 'fog-change':
|
|
1212
1235
|
return 'reflection-generation';
|
|
1236
|
+
case 'time-discontinuity':
|
|
1237
|
+
return 'camera-cut';
|
|
1213
1238
|
case 'device-recover':
|
|
1214
1239
|
return 'device-recovery';
|
|
1215
1240
|
case 'submit-failure':
|
|
@@ -1218,6 +1243,50 @@ function mapSsrHistoryResetReason(
|
|
|
1218
1243
|
}
|
|
1219
1244
|
}
|
|
1220
1245
|
|
|
1246
|
+
interface FrameClockSample {
|
|
1247
|
+
readonly deltaSeconds: number;
|
|
1248
|
+
readonly elapsedSeconds: number | undefined;
|
|
1249
|
+
/** Explicit host/replay clock was present but invalid. */
|
|
1250
|
+
readonly invalidSampleTime: boolean;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/** Read the render sample clock from the camera's composition world once per frame. */
|
|
1254
|
+
function frameClockSample(
|
|
1255
|
+
world: World,
|
|
1256
|
+
worlds: readonly World[],
|
|
1257
|
+
camera: CameraSnapshot,
|
|
1258
|
+
sampleTimeSeconds?: number,
|
|
1259
|
+
): FrameClockSample {
|
|
1260
|
+
const owner = worlds[camera.worldId ?? 0] ?? world;
|
|
1261
|
+
const invalidSampleTime = sampleTimeSeconds !== undefined && !Number.isFinite(sampleTimeSeconds);
|
|
1262
|
+
try {
|
|
1263
|
+
const clock = owner.getResource(Time);
|
|
1264
|
+
return {
|
|
1265
|
+
deltaSeconds: Number.isFinite(clock.delta) ? Math.max(0, clock.delta) : 0,
|
|
1266
|
+
// Keep the raw host sample even when ECS accepted only a clamped
|
|
1267
|
+
// simulation delta. Temporal admission compares this value with the
|
|
1268
|
+
// last successfully submitted sample and therefore sees real hitches.
|
|
1269
|
+
elapsedSeconds:
|
|
1270
|
+
sampleTimeSeconds === undefined
|
|
1271
|
+
? Number.isFinite(clock.elapsed)
|
|
1272
|
+
? clock.elapsed
|
|
1273
|
+
: undefined
|
|
1274
|
+
: Number.isFinite(sampleTimeSeconds)
|
|
1275
|
+
? sampleTimeSeconds
|
|
1276
|
+
: undefined,
|
|
1277
|
+
invalidSampleTime,
|
|
1278
|
+
};
|
|
1279
|
+
} catch {
|
|
1280
|
+
// Test and recovery callers may provide a minimal World-shaped owner. An
|
|
1281
|
+
// absent clock is an invalid temporal pair and therefore safely resets.
|
|
1282
|
+
return {
|
|
1283
|
+
deltaSeconds: 0,
|
|
1284
|
+
elapsedSeconds: Number.isFinite(sampleTimeSeconds) ? sampleTimeSeconds : undefined,
|
|
1285
|
+
invalidSampleTime,
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1221
1290
|
function stageSsrHistoryCandidate(
|
|
1222
1291
|
internals: RenderSystemInternals,
|
|
1223
1292
|
frameState: RenderFrameState,
|
|
@@ -1602,6 +1671,7 @@ export function recordFrame(
|
|
|
1602
1671
|
readonly activeEntityKeys?: ReadonlySet<number>;
|
|
1603
1672
|
readonly activeEntityRevision?: number;
|
|
1604
1673
|
readonly telemetryCandidateCount?: number;
|
|
1674
|
+
readonly telemetrySubmit?: GpuDrivenLodSubmitIdentity;
|
|
1605
1675
|
readonly occlusion?: OcclusionFrameProjection;
|
|
1606
1676
|
},
|
|
1607
1677
|
renderReadLeases?: readonly RenderReadLease[],
|
|
@@ -1622,6 +1692,9 @@ export function recordFrame(
|
|
|
1622
1692
|
preparedLighting?: ReturnType<typeof prepareFrameLighting>,
|
|
1623
1693
|
ssrDependencies?: SsrDependenciesInspection,
|
|
1624
1694
|
onRenderableDraw?: (entry: ValidatedRenderable) => void,
|
|
1695
|
+
analyticFog?: FogFrame,
|
|
1696
|
+
sampleTimeSeconds?: number,
|
|
1697
|
+
temporalReset = false,
|
|
1625
1698
|
): boolean {
|
|
1626
1699
|
void shadowCasterEntityKeys;
|
|
1627
1700
|
frameState.reflectionFallbackObservationSource = undefined;
|
|
@@ -1671,8 +1744,17 @@ export function recordFrame(
|
|
|
1671
1744
|
}),
|
|
1672
1745
|
);
|
|
1673
1746
|
}
|
|
1674
|
-
const
|
|
1675
|
-
if (!
|
|
1747
|
+
const requestedCamera = activeCameras[0];
|
|
1748
|
+
if (!requestedCamera) return false;
|
|
1749
|
+
const frameDepthOfField = resolveDepthOfFieldFrameParams(
|
|
1750
|
+
requestedCamera.depthOfField,
|
|
1751
|
+
requestedCamera.depthOfFieldError,
|
|
1752
|
+
frameState.depthOfFieldAccepted?.params,
|
|
1753
|
+
);
|
|
1754
|
+
const camera: CameraSnapshot =
|
|
1755
|
+
frameDepthOfField === undefined || frameDepthOfField === requestedCamera.depthOfField
|
|
1756
|
+
? requestedCamera
|
|
1757
|
+
: { ...requestedCamera, depthOfField: frameDepthOfField };
|
|
1676
1758
|
frameState.ssrRequested = camera.screenSpaceReflection !== undefined;
|
|
1677
1759
|
const pipelineState = internals.getPipelineState();
|
|
1678
1760
|
if (pipelineState === null) return false;
|
|
@@ -2028,6 +2110,9 @@ export function recordFrame(
|
|
|
2028
2110
|
...(frameGpuDriven.telemetryCandidateCount === undefined
|
|
2029
2111
|
? {}
|
|
2030
2112
|
: { telemetryCandidateCount: frameGpuDriven.telemetryCandidateCount }),
|
|
2113
|
+
...(frameGpuDriven.telemetrySubmit === undefined
|
|
2114
|
+
? {}
|
|
2115
|
+
: { telemetrySubmit: frameGpuDriven.telemetrySubmit }),
|
|
2031
2116
|
...(frameGpuDriven.occlusion === undefined ? {} : { occlusion: frameGpuDriven.occlusion }),
|
|
2032
2117
|
...(frameGpuDriven.scene?.structureMetrics === undefined
|
|
2033
2118
|
? {}
|
|
@@ -2172,6 +2257,7 @@ export function recordFrame(
|
|
|
2172
2257
|
undefined,
|
|
2173
2258
|
ssrAdmission,
|
|
2174
2259
|
renderExtent,
|
|
2260
|
+
analyticFog !== undefined && analyticFog.density > 0 && analyticFog.maxOpacity > 0,
|
|
2175
2261
|
),
|
|
2176
2262
|
);
|
|
2177
2263
|
if (preflightGraph === null) {
|
|
@@ -2250,6 +2336,38 @@ export function recordFrame(
|
|
|
2250
2336
|
const targetH = swapTarget.targetH;
|
|
2251
2337
|
|
|
2252
2338
|
const previousTemporalView = frameState.lastSuccessfulTemporalView;
|
|
2339
|
+
const motionBlurDemand =
|
|
2340
|
+
camera.motionBlur === undefined
|
|
2341
|
+
? false
|
|
2342
|
+
: motionBlurTemporalDemand({
|
|
2343
|
+
shutterAngle: camera.motionBlur.shutterAngle,
|
|
2344
|
+
maxRadiusPixels: camera.motionBlur.maxRadiusPixels,
|
|
2345
|
+
sampleCount: camera.motionBlur.sampleCount,
|
|
2346
|
+
targetFps: camera.motionBlur.targetFps ?? 60,
|
|
2347
|
+
});
|
|
2348
|
+
const temporalDemand = camera.antialias === 'taa' || motionBlurDemand;
|
|
2349
|
+
const clock = frameClockSample(world, worlds, camera, sampleTimeSeconds);
|
|
2350
|
+
const deltaSeconds =
|
|
2351
|
+
temporalReset || clock.invalidSampleTime
|
|
2352
|
+
? Number.NaN
|
|
2353
|
+
: motionBlurSampleDelta(
|
|
2354
|
+
clock.elapsedSeconds,
|
|
2355
|
+
previousTemporalView?.sampleTimeSeconds,
|
|
2356
|
+
clock.deltaSeconds,
|
|
2357
|
+
);
|
|
2358
|
+
const intervalValid = isMotionBlurIntervalValid(deltaSeconds);
|
|
2359
|
+
// An off MB-only frame still carries an unjittered temporal view so the
|
|
2360
|
+
// accepted sample clock remains observable. Its history flag is false,
|
|
2361
|
+
// however, and must form a new baseline when the effect is enabled again.
|
|
2362
|
+
// A reset frame already has an explicit reason and is the valid baseline
|
|
2363
|
+
// for the following pair, so it is deliberately excluded here.
|
|
2364
|
+
const motionBlurFirstFrame =
|
|
2365
|
+
motionBlurDemand &&
|
|
2366
|
+
camera.antialias !== 'taa' &&
|
|
2367
|
+
(previousTemporalView === undefined ||
|
|
2368
|
+
(previousTemporalView.mode === 'off' &&
|
|
2369
|
+
!previousTemporalView.historyValid &&
|
|
2370
|
+
previousTemporalView.resetReason === undefined));
|
|
2253
2371
|
const currentUnjitteredViewProjection = mat4.multiply(
|
|
2254
2372
|
mat4.create(),
|
|
2255
2373
|
computeProjectionMatrix(camera),
|
|
@@ -2276,19 +2394,32 @@ export function recordFrame(
|
|
|
2276
2394
|
internalHeight: renderExtent.internalHeight,
|
|
2277
2395
|
}),
|
|
2278
2396
|
...(camera.historyVersion === undefined ? {} : { historyVersion: camera.historyVersion }),
|
|
2397
|
+
...(clock.elapsedSeconds === undefined ? {} : { sampleTimeSeconds: clock.elapsedSeconds }),
|
|
2279
2398
|
historyValid: false,
|
|
2280
2399
|
currentUnjitteredViewProjection,
|
|
2281
2400
|
currentCameraPosition,
|
|
2282
2401
|
});
|
|
2283
2402
|
const resetReason =
|
|
2284
|
-
|
|
2285
|
-
? resolveTemporalReset(previousTemporalView, temporalCandidate)
|
|
2403
|
+
temporalDemand && environmentReady
|
|
2404
|
+
? (resolveTemporalReset(previousTemporalView, temporalCandidate) ??
|
|
2405
|
+
(temporalReset || (motionBlurDemand && !intervalValid)
|
|
2406
|
+
? 'time-discontinuity'
|
|
2407
|
+
: motionBlurFirstFrame
|
|
2408
|
+
? 'first-frame'
|
|
2409
|
+
: undefined))
|
|
2286
2410
|
: undefined;
|
|
2287
2411
|
const canReusePrevious = previousTemporalView !== undefined && resetReason === undefined;
|
|
2288
2412
|
const temporalView = createTemporalView({
|
|
2289
2413
|
...temporalCandidate.input,
|
|
2290
2414
|
frameIndex: resetReason === undefined ? temporalCandidate.input.frameIndex : 0,
|
|
2291
|
-
|
|
2415
|
+
// Motion Blur needs a finite positive pair before it can read the
|
|
2416
|
+
// baseline. TAA-only frames retain their existing temporal contract and
|
|
2417
|
+
// do not depend on the simulation clock being present in a test host.
|
|
2418
|
+
historyValid:
|
|
2419
|
+
temporalDemand &&
|
|
2420
|
+
environmentReady &&
|
|
2421
|
+
canReusePrevious &&
|
|
2422
|
+
(!motionBlurDemand || intervalValid),
|
|
2292
2423
|
resetReason,
|
|
2293
2424
|
...(canReusePrevious && previousTemporalView.currentUnjitteredViewProjection !== undefined
|
|
2294
2425
|
? {
|
|
@@ -2339,17 +2470,26 @@ export function recordFrame(
|
|
|
2339
2470
|
);
|
|
2340
2471
|
activeVolumetricFogContext = resolvedHistory.context;
|
|
2341
2472
|
activeVolumetricFogSignature = resolvedHistory.signature;
|
|
2342
|
-
const
|
|
2473
|
+
const previousTime = frameState.volumetricFogHistorySignature?.worldTimeSeconds;
|
|
2474
|
+
const currentTime = resolvedHistory.signature.worldTimeSeconds;
|
|
2475
|
+
// Adjacent UBO lanes: validity, World time, elapsed time since accepted history.
|
|
2476
|
+
const historyParams = new Float32Array([
|
|
2477
|
+
activeVolumetricFogContext.historyValid ? 1 : 0,
|
|
2478
|
+
currentTime ?? 0,
|
|
2479
|
+
previousTime === undefined || currentTime === undefined
|
|
2480
|
+
? 0
|
|
2481
|
+
: Math.max(0, currentTime - previousTime),
|
|
2482
|
+
]);
|
|
2343
2483
|
if (frameState.volumetricFogPendingParams !== undefined) {
|
|
2344
|
-
frameState.volumetricFogPendingParams
|
|
2484
|
+
frameState.volumetricFogPendingParams.set(historyParams, 31);
|
|
2345
2485
|
}
|
|
2346
|
-
const
|
|
2486
|
+
const historyParamsWritten = internals.device.queue.writeBuffer(
|
|
2347
2487
|
activeVolumetricFogContext.paramsBuffer,
|
|
2348
2488
|
31 * Float32Array.BYTES_PER_ELEMENT,
|
|
2349
|
-
|
|
2489
|
+
historyParams,
|
|
2350
2490
|
);
|
|
2351
|
-
if (!
|
|
2352
|
-
internals.errorRegistry.fire(
|
|
2491
|
+
if (!historyParamsWritten.ok) {
|
|
2492
|
+
internals.errorRegistry.fire(historyParamsWritten.error);
|
|
2353
2493
|
abortPreparedGpuDriven();
|
|
2354
2494
|
return false;
|
|
2355
2495
|
}
|
|
@@ -2634,14 +2774,47 @@ export function recordFrame(
|
|
|
2634
2774
|
// builtin motion-blur pass receives its all-zero registration default and
|
|
2635
2775
|
// becomes a no-op even though the temporal scene target is populated.
|
|
2636
2776
|
const framePostProcessParams = new Map(postProcessParams);
|
|
2777
|
+
if (analyticFog !== undefined)
|
|
2778
|
+
framePostProcessParams.set(
|
|
2779
|
+
'forgeax.analytic-fog',
|
|
2780
|
+
encodeAnalyticFogParams(analyticFog, recordCamera),
|
|
2781
|
+
);
|
|
2637
2782
|
if (camera.motionBlur !== undefined) {
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2783
|
+
if (motionBlurDemand) {
|
|
2784
|
+
const motionBlurParams = new Uint8Array(MOTION_BLUR_PARAMS_BYTE_SIZE);
|
|
2785
|
+
const motionBlurView = new DataView(motionBlurParams.buffer);
|
|
2786
|
+
const uniformDeltaSeconds = Number.isFinite(deltaSeconds) ? deltaSeconds : 0;
|
|
2787
|
+
motionBlurView.setFloat32(0, camera.motionBlur.shutterAngle, true);
|
|
2788
|
+
motionBlurView.setFloat32(4, camera.motionBlur.maxRadiusPixels, true);
|
|
2789
|
+
motionBlurView.setUint32(
|
|
2790
|
+
8,
|
|
2791
|
+
effectiveMotionBlurSampleCount(camera.motionBlur.sampleCount),
|
|
2792
|
+
true,
|
|
2793
|
+
);
|
|
2794
|
+
motionBlurView.setUint32(12, temporalView?.historyValid === true ? 0 : 1, true);
|
|
2795
|
+
motionBlurView.setFloat32(16, camera.motionBlur.targetFps ?? 60, true);
|
|
2796
|
+
motionBlurView.setFloat32(
|
|
2797
|
+
20,
|
|
2798
|
+
motionBlurExposureScale(uniformDeltaSeconds, camera.motionBlur.targetFps ?? 60),
|
|
2799
|
+
true,
|
|
2800
|
+
);
|
|
2801
|
+
motionBlurView.setFloat32(24, uniformDeltaSeconds, true);
|
|
2802
|
+
framePostProcessParams.set('forgeax.motion-blur', motionBlurParams);
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
if (camera.depthOfField !== undefined) {
|
|
2806
|
+
const dofParams = packDepthOfFieldParams(camera.depthOfField, {
|
|
2807
|
+
outputHeight: targetH,
|
|
2808
|
+
near: camera.near,
|
|
2809
|
+
far: camera.far,
|
|
2810
|
+
useTemporalDepth: camera.antialias === 'taa' && temporalView !== undefined,
|
|
2811
|
+
});
|
|
2812
|
+
for (const id of [
|
|
2813
|
+
...DEPTH_OF_FIELD_POST_PROCESS_IDS,
|
|
2814
|
+
...DEPTH_OF_FIELD_MSAA_POST_PROCESS_IDS,
|
|
2815
|
+
]) {
|
|
2816
|
+
framePostProcessParams.set(id, dofParams);
|
|
2817
|
+
}
|
|
2645
2818
|
}
|
|
2646
2819
|
|
|
2647
2820
|
const passCtx: _InternalRenderPipelineContext = {
|
|
@@ -2925,11 +3098,12 @@ export function recordFrame(
|
|
|
2925
3098
|
frameState.ssrHistoryOwner?.abortFrame(frameState.ssrHistoryCandidate, 'submit');
|
|
2926
3099
|
frameState.ssrHistoryCandidate = undefined;
|
|
2927
3100
|
}
|
|
2928
|
-
//
|
|
2929
|
-
// succeeds. This runs for every early-return path as well, so a
|
|
2930
|
-
// encoder, or graph failure cannot leave a non-submitted
|
|
2931
|
-
// next frame's accepted graph.
|
|
3101
|
+
// DoF and volume topology replacements are provisional until the queue
|
|
3102
|
+
// submission succeeds. This runs for every early-return path as well, so a
|
|
3103
|
+
// swapchain, encoder, or graph failure cannot leave a non-submitted
|
|
3104
|
+
// candidate as the next frame's accepted graph.
|
|
2932
3105
|
settleVolumetricFogGraphCandidate(frameState, submitted);
|
|
3106
|
+
settleDepthOfFieldGraphCandidate(frameState, submitted);
|
|
2933
3107
|
if (volumeInspectionReady && (submitted || volumeSubmissionAttempted)) {
|
|
2934
3108
|
updateVolumetricFogInspection(
|
|
2935
3109
|
frameState,
|
|
@@ -1481,6 +1481,7 @@ function uploadInstanceRanges(
|
|
|
1481
1481
|
ranges: readonly { readonly start: number; readonly end: number }[],
|
|
1482
1482
|
storageBacked: boolean,
|
|
1483
1483
|
sourceOffsetInstances = 0,
|
|
1484
|
+
previousInst: NonNullable<RenderableSnapshot['temporal']>['previousInstances'] = undefined,
|
|
1484
1485
|
): InstanceUploadResult {
|
|
1485
1486
|
const strideBytes =
|
|
1486
1487
|
(storageBacked ? INSTANCE_STORAGE_STRIDE_FLOATS : 16) * Float32Array.BYTES_PER_ELEMENT;
|
|
@@ -1494,7 +1495,31 @@ function uploadInstanceRanges(
|
|
|
1494
1495
|
(sourceOffsetInstances + start) * 16,
|
|
1495
1496
|
(sourceOffsetInstances + end) * 16,
|
|
1496
1497
|
);
|
|
1497
|
-
|
|
1498
|
+
// A generation-bearing previous collection is kept whole so a compacted
|
|
1499
|
+
// or reordered current range can resolve its real prior ordinal. Without
|
|
1500
|
+
// identity proof, keep the old range-local shape and let the packer seed
|
|
1501
|
+
// on count mismatch rather than pairing an unrelated slot.
|
|
1502
|
+
const hasIdentityProof =
|
|
1503
|
+
inst.generations !== undefined && previousInst?.generations !== undefined;
|
|
1504
|
+
const previousSource = hasIdentityProof
|
|
1505
|
+
? previousInst?.transforms
|
|
1506
|
+
: previousInst?.transforms.subarray(
|
|
1507
|
+
(sourceOffsetInstances + start) * 16,
|
|
1508
|
+
(sourceOffsetInstances + end) * 16,
|
|
1509
|
+
);
|
|
1510
|
+
const currentGenerations = inst.generations?.subarray(
|
|
1511
|
+
sourceOffsetInstances + start,
|
|
1512
|
+
sourceOffsetInstances + end,
|
|
1513
|
+
);
|
|
1514
|
+
const previousGenerations = hasIdentityProof
|
|
1515
|
+
? previousInst?.generations
|
|
1516
|
+
: previousInst?.generations?.subarray(
|
|
1517
|
+
sourceOffsetInstances + start,
|
|
1518
|
+
sourceOffsetInstances + end,
|
|
1519
|
+
);
|
|
1520
|
+
const payload = storageBacked
|
|
1521
|
+
? packInstanceStorageBuffer(source, previousSource, currentGenerations, previousGenerations)
|
|
1522
|
+
: source;
|
|
1498
1523
|
const written = c.runtime.device.queue.writeBuffer(buffer, start * strideBytes, payload);
|
|
1499
1524
|
if (!written.ok) {
|
|
1500
1525
|
c.runtime.errorRegistry.fire(written.error);
|
|
@@ -1673,6 +1698,7 @@ export function resolveStorageInstanceChunks(
|
|
|
1673
1698
|
rangesToUpload,
|
|
1674
1699
|
true,
|
|
1675
1700
|
start,
|
|
1701
|
+
entry.source.temporal?.previousInstances,
|
|
1676
1702
|
);
|
|
1677
1703
|
for (const range of upload.ranges) {
|
|
1678
1704
|
uploadedRanges.push({ start: range.start + start, end: range.end + start });
|
|
@@ -1858,6 +1884,7 @@ function resolveGeometryInstanceBuffer(
|
|
|
1858
1884
|
rangesToUpload,
|
|
1859
1885
|
false,
|
|
1860
1886
|
start,
|
|
1887
|
+
entry.source.temporal?.previousInstances,
|
|
1861
1888
|
);
|
|
1862
1889
|
for (const range of upload.ranges) {
|
|
1863
1890
|
uploadedRanges.push({ start: range.start + start, end: range.end + start });
|
|
@@ -1997,6 +2024,8 @@ function resolveGeometryInstanceBuffer(
|
|
|
1997
2024
|
active.buffer.handle,
|
|
1998
2025
|
ranges,
|
|
1999
2026
|
!uniformFallback,
|
|
2027
|
+
0,
|
|
2028
|
+
entry.source.temporal?.previousInstances,
|
|
2000
2029
|
);
|
|
2001
2030
|
uploadRanges = upload.ranges;
|
|
2002
2031
|
uploadedBytes = upload.bytes;
|
|
@@ -690,7 +690,12 @@ function recordSpriteEntityDraws(
|
|
|
690
690
|
{
|
|
691
691
|
const instancePayload = uniformFallback
|
|
692
692
|
? spriteInst.transforms
|
|
693
|
-
: packInstanceStorageBuffer(
|
|
693
|
+
: packInstanceStorageBuffer(
|
|
694
|
+
spriteInst.transforms,
|
|
695
|
+
spriteEntry.source.temporal?.previousInstances?.transforms,
|
|
696
|
+
spriteInst.generations,
|
|
697
|
+
spriteEntry.source.temporal?.previousInstances?.generations,
|
|
698
|
+
);
|
|
694
699
|
const requestedBytes = instancePayload.byteLength;
|
|
695
700
|
const cap = runtime.device.limits.maxStorageBufferBindingSize;
|
|
696
701
|
if (typeof cap === 'number' && requestedBytes > cap) {
|
package/src/record/main-pass.ts
CHANGED
|
@@ -676,11 +676,7 @@ export function recordMainPass(
|
|
|
676
676
|
hint: 'publish the current frame view/light/shadow bind group before GPU raster',
|
|
677
677
|
});
|
|
678
678
|
}
|
|
679
|
-
if (
|
|
680
|
-
gpuDrivenStandardPbrFrameResources === undefined ||
|
|
681
|
-
gpuDrivenStandardPbrFrameResources.materialBindGroups.length === 0 ||
|
|
682
|
-
gpuDrivenStandardPbrFrameResources.materialBindGroups.some((group) => group === undefined)
|
|
683
|
-
) {
|
|
679
|
+
if (gpuDrivenStandardPbrFrameResources === undefined) {
|
|
684
680
|
throw new RhiError({
|
|
685
681
|
code: 'rhi-not-available',
|
|
686
682
|
expected: 'complete main-pass Standard PBR material bind-group receipt',
|
package/src/record/mesh-ssbo.ts
CHANGED
|
@@ -43,23 +43,58 @@ export const MAX_UNIFORM_INSTANCES = 128;
|
|
|
43
43
|
/** The storage-backed InstanceData layout is current + previous mat4. */
|
|
44
44
|
export const INSTANCE_STORAGE_STRIDE_FLOATS = 32;
|
|
45
45
|
|
|
46
|
+
function hasStableGenerationColumn(generations: Uint32Array | undefined, count: number): boolean {
|
|
47
|
+
if (generations === undefined || generations.length !== count) return false;
|
|
48
|
+
const seen = new Set<number>();
|
|
49
|
+
for (const generation of generations) {
|
|
50
|
+
if (generation === 0 || seen.has(generation)) return false;
|
|
51
|
+
seen.add(generation);
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
/**
|
|
47
57
|
* Project the ECS transform snapshot into the storage-backed InstanceData
|
|
48
|
-
* layout.
|
|
49
|
-
* the
|
|
50
|
-
* previous
|
|
51
|
-
*
|
|
58
|
+
* layout. Unique identity generations select the previous matrix only when
|
|
59
|
+
* the producer proves that the instance survived. Replaced slots seed
|
|
60
|
+
* previous=current; reordered slots map back to the real prior ordinal so a
|
|
61
|
+
* compaction cannot exchange velocities. Uniform-backed variants keep the
|
|
62
|
+
* compact 16-float payload and do not use this projection.
|
|
52
63
|
*/
|
|
53
|
-
export function packInstanceStorageBuffer(
|
|
64
|
+
export function packInstanceStorageBuffer(
|
|
65
|
+
transforms: Float32Array,
|
|
66
|
+
previousTransforms: Float32Array = transforms,
|
|
67
|
+
generations?: Uint32Array,
|
|
68
|
+
previousGenerations?: Uint32Array,
|
|
69
|
+
): Float32Array {
|
|
54
70
|
const count = Math.floor(transforms.length / 16);
|
|
55
71
|
const out = new Float32Array(count * INSTANCE_STORAGE_STRIDE_FLOATS);
|
|
72
|
+
const previousCount = Math.floor(previousTransforms.length / 16);
|
|
73
|
+
const generationInput = generations !== undefined || previousGenerations !== undefined;
|
|
74
|
+
const identityProof =
|
|
75
|
+
hasStableGenerationColumn(generations, count) &&
|
|
76
|
+
hasStableGenerationColumn(previousGenerations, previousCount);
|
|
77
|
+
const previousIndexByGeneration = identityProof
|
|
78
|
+
? new Map(
|
|
79
|
+
Array.from(previousGenerations ?? [], (generation, index) => [generation, index] as const),
|
|
80
|
+
)
|
|
81
|
+
: undefined;
|
|
56
82
|
for (let i = 0; i < count; i++) {
|
|
57
83
|
const sourceBase = i * 16;
|
|
58
84
|
const targetBase = i * INSTANCE_STORAGE_STRIDE_FLOATS;
|
|
85
|
+
const previousIndex = identityProof
|
|
86
|
+
? previousIndexByGeneration?.get(generations?.[i] ?? 0)
|
|
87
|
+
: !generationInput && previousCount === count
|
|
88
|
+
? i
|
|
89
|
+
: undefined;
|
|
90
|
+
const sameIdentity = previousIndex !== undefined && previousIndex < previousCount;
|
|
91
|
+
const previousBase = (previousIndex ?? i) * 16;
|
|
59
92
|
for (let k = 0; k < 16; k++) {
|
|
60
93
|
const value = transforms[sourceBase + k] ?? 0;
|
|
61
94
|
out[targetBase + k] = value;
|
|
62
|
-
out[targetBase + 16 + k] =
|
|
95
|
+
out[targetBase + 16 + k] = sameIdentity
|
|
96
|
+
? (previousTransforms[previousBase + k] ?? value)
|
|
97
|
+
: value;
|
|
63
98
|
}
|
|
64
99
|
}
|
|
65
100
|
return out;
|
|
@@ -451,6 +486,7 @@ export function uploadMeshSsboBatch(
|
|
|
451
486
|
_meshSsboScratchFloats[slot + 38] = 1;
|
|
452
487
|
_meshSsboScratchFloats[slot + 43] = 1;
|
|
453
488
|
_meshSsboScratchFloats[slot + 44] = 1;
|
|
489
|
+
_meshSsboScratchFloats[slot + 45] = 1;
|
|
454
490
|
} else {
|
|
455
491
|
const worldFromLocal = entry.source.transform.world;
|
|
456
492
|
for (let k = 0; k < 16; k++) _meshSsboScratchFloats[slot + k] = worldFromLocal[k] ?? 0;
|
|
@@ -483,6 +519,7 @@ export function uploadMeshSsboBatch(
|
|
|
483
519
|
}
|
|
484
520
|
_meshSsboScratchFloats[slot + 44] =
|
|
485
521
|
entry.source.temporal?.reactive === true || reactiveMaterial ? 1 : 0;
|
|
522
|
+
_meshSsboScratchFloats[slot + 45] = entry.source.temporal?.motionValid === false ? 0 : 1;
|
|
486
523
|
}
|
|
487
524
|
}
|
|
488
525
|
if (neededBytes > 0) {
|
|
@@ -337,17 +337,15 @@ export interface PerPassResources {
|
|
|
337
337
|
readonly skyboxBindGroupLayout: BindGroupLayout | null;
|
|
338
338
|
readonly skyboxSampler: Sampler | null;
|
|
339
339
|
readonly skyboxRotationBuffer: Buffer | null;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
bloomBlurVPipeline: RhiRenderPipeline | null;
|
|
340
|
+
bloomDownsamplePipeline: RhiRenderPipeline | null;
|
|
341
|
+
bloomUpsamplePipeline: RhiRenderPipeline | null;
|
|
343
342
|
bloomCompositePipeline: RhiRenderPipeline | null;
|
|
344
|
-
|
|
345
|
-
|
|
343
|
+
bloomDownsampleBindGroupLayout: BindGroupLayout | null;
|
|
344
|
+
bloomUpsampleBindGroupLayout: BindGroupLayout | null;
|
|
346
345
|
bloomCompositeBindGroupLayout: BindGroupLayout | null;
|
|
347
346
|
bloomSampler: Sampler | null;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
bloomBlurVParamsBuffer: Buffer | null;
|
|
347
|
+
bloomDownsampleParamsBuffer: Buffer | null;
|
|
348
|
+
bloomUpsampleParamsBuffer: Buffer | null;
|
|
351
349
|
bloomCompositeParamsBuffer: Buffer | null;
|
|
352
350
|
ensureBloomResources?: () => void;
|
|
353
351
|
getBloomResources?: () => BloomPersistentBundle | null;
|
|
@@ -358,18 +356,6 @@ export interface PerPassResources {
|
|
|
358
356
|
/** Drain candidate, active, and fence-retiring Bloom generations. */
|
|
359
357
|
drainBloomResources?: () => void;
|
|
360
358
|
inspectBloomResources?: (graph?: CompiledRenderGraphInfo) => BloomInspection;
|
|
361
|
-
bloomBrightTexture: Texture | null;
|
|
362
|
-
bloomBrightView: TextureView | null;
|
|
363
|
-
bloomBrightWidth: number;
|
|
364
|
-
bloomBrightHeight: number;
|
|
365
|
-
bloomBlurHTexture: Texture | null;
|
|
366
|
-
bloomBlurHView: TextureView | null;
|
|
367
|
-
bloomBlurHWidth: number;
|
|
368
|
-
bloomBlurHHeight: number;
|
|
369
|
-
bloomBlurVTexture: Texture | null;
|
|
370
|
-
bloomBlurVView: TextureView | null;
|
|
371
|
-
bloomBlurVWidth: number;
|
|
372
|
-
bloomBlurVHeight: number;
|
|
373
359
|
ssaoCalcPipeline: RhiRenderPipeline | null;
|
|
374
360
|
ssaoBlurPipeline: RhiRenderPipeline | null;
|
|
375
361
|
ssaoBgl: BindGroupLayout | null;
|
|
@@ -392,17 +378,15 @@ export interface PerPassResources {
|
|
|
392
378
|
export interface BloomPersistentBundle {
|
|
393
379
|
readonly scope: DeviceScope;
|
|
394
380
|
readonly generation: number;
|
|
395
|
-
readonly
|
|
396
|
-
readonly
|
|
397
|
-
readonly bloomBlurVPipeline: RhiRenderPipeline | null;
|
|
381
|
+
readonly bloomDownsamplePipeline: RhiRenderPipeline | null;
|
|
382
|
+
readonly bloomUpsamplePipeline: RhiRenderPipeline | null;
|
|
398
383
|
readonly bloomCompositePipeline: RhiRenderPipeline | null;
|
|
399
|
-
readonly
|
|
400
|
-
readonly
|
|
384
|
+
readonly bloomDownsampleBindGroupLayout: BindGroupLayout | null;
|
|
385
|
+
readonly bloomUpsampleBindGroupLayout: BindGroupLayout | null;
|
|
401
386
|
readonly bloomCompositeBindGroupLayout: BindGroupLayout | null;
|
|
402
387
|
readonly bloomSampler: Sampler | null;
|
|
403
|
-
readonly
|
|
404
|
-
readonly
|
|
405
|
-
readonly bloomBlurVParamsBuffer: Buffer | null;
|
|
388
|
+
readonly bloomDownsampleParamsBuffer: Buffer | null;
|
|
389
|
+
readonly bloomUpsampleParamsBuffer: Buffer | null;
|
|
406
390
|
readonly bloomCompositeParamsBuffer: Buffer | null;
|
|
407
391
|
}
|
|
408
392
|
|