@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RhiCaps } from '@forgeax/engine-rhi';
|
|
2
2
|
import { err, ok } from '@forgeax/engine-types';
|
|
3
3
|
import { describe, expect, it } from 'vitest';
|
|
4
|
+
import { RenderFeaturePreparationFailedError } from '../errors/render';
|
|
4
5
|
import {
|
|
5
6
|
createRenderFeatureHost,
|
|
6
7
|
runRenderFeatureFrame,
|
|
@@ -136,3 +137,88 @@ describe('prepared graphics lifecycle ownership', () => {
|
|
|
136
137
|
expect(releases).toBe(2);
|
|
137
138
|
});
|
|
138
139
|
});
|
|
140
|
+
|
|
141
|
+
for (const readyFirst of [true, false]) {
|
|
142
|
+
it(`retains partial resolver allocations through pending pipelines (ready pass: ${readyFirst})`, () => {
|
|
143
|
+
let releases = 0;
|
|
144
|
+
const lease = {
|
|
145
|
+
release: () => {
|
|
146
|
+
releases += 1;
|
|
147
|
+
return ok(undefined);
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
const feature: RenderFeature<undefined> = {
|
|
151
|
+
identity: 'pending.material',
|
|
152
|
+
extract: () => ok(undefined),
|
|
153
|
+
plan: () =>
|
|
154
|
+
ok({
|
|
155
|
+
resources: ['ready', 'pending'].flatMap((name) => [
|
|
156
|
+
{
|
|
157
|
+
kind: 'graphics-program' as const,
|
|
158
|
+
name,
|
|
159
|
+
program: {
|
|
160
|
+
shader: name,
|
|
161
|
+
vertexLayout: 'none',
|
|
162
|
+
colorFormats: ['rgba8unorm' as const],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
kind: 'graphics-bindings' as const,
|
|
167
|
+
name: `${name}-bindings`,
|
|
168
|
+
program: name,
|
|
169
|
+
values: {},
|
|
170
|
+
},
|
|
171
|
+
]),
|
|
172
|
+
passes: (readyFirst ? ['ready', 'pending'] : ['pending']).map((name) => ({
|
|
173
|
+
kind: 'raster' as const,
|
|
174
|
+
name,
|
|
175
|
+
colorAttachments: [
|
|
176
|
+
{ target: 'swapchain', loadOp: 'load' as const, storeOp: 'store' as const },
|
|
177
|
+
],
|
|
178
|
+
draws: [
|
|
179
|
+
{
|
|
180
|
+
vertexLayout: 'none' as const,
|
|
181
|
+
vertexData: [],
|
|
182
|
+
program: name,
|
|
183
|
+
bindings: [`${name}-bindings`],
|
|
184
|
+
draw: { kind: 'draw' as const, vertexCount: 3, instanceCount: 1 },
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
})),
|
|
188
|
+
}),
|
|
189
|
+
};
|
|
190
|
+
const host = createRenderFeatureHost([feature]).unwrap();
|
|
191
|
+
const result = runRenderFeatureFrame(host, {
|
|
192
|
+
worlds: [],
|
|
193
|
+
owner: 0,
|
|
194
|
+
frameNumber: 1,
|
|
195
|
+
caps,
|
|
196
|
+
createPreparedGraphicsResolver: (input) => ({
|
|
197
|
+
leases: [lease],
|
|
198
|
+
release: lease.release,
|
|
199
|
+
resolve: (ref) => {
|
|
200
|
+
const item = input.lookup(ref);
|
|
201
|
+
if (item?.name === 'pending')
|
|
202
|
+
return err(
|
|
203
|
+
new RenderFeaturePreparationFailedError(
|
|
204
|
+
feature.identity,
|
|
205
|
+
0,
|
|
206
|
+
'prepare',
|
|
207
|
+
'pipeline',
|
|
208
|
+
'pending',
|
|
209
|
+
'pipeline-pending',
|
|
210
|
+
'next-frame',
|
|
211
|
+
),
|
|
212
|
+
);
|
|
213
|
+
return ok({ kind: ref.kind, reference: ref, handle: {} } as never);
|
|
214
|
+
},
|
|
215
|
+
}),
|
|
216
|
+
});
|
|
217
|
+
expect(result.errors, JSON.stringify(result.errors)).toEqual([]);
|
|
218
|
+
expect(releases).toBe(0);
|
|
219
|
+
expect(result.preparedResourceBatches).toHaveLength(1);
|
|
220
|
+
host.markPreparedGraphicsSubmitted(result.preparedResourceBatches);
|
|
221
|
+
host.retirePreparedGraphics(result.preparedResourceBatches);
|
|
222
|
+
expect(releases).toBe(1);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
FrameObservationRequest,
|
|
3
3
|
FrameReceipt,
|
|
4
4
|
RenderError as PublicRenderError,
|
|
5
|
+
SunCardinalityError as PublicSunCardinalityError,
|
|
5
6
|
ReflectionProbeInspection,
|
|
6
7
|
ReflectionProbeSelectionInspection,
|
|
7
8
|
RendererState,
|
|
@@ -65,6 +66,13 @@ const ssrInspection: SsrSpatialInspection = inspection.ssr;
|
|
|
65
66
|
void ssrInspection;
|
|
66
67
|
type IntentError = Extract<PublicRenderError, { readonly code: 'render-intent-invalid' }>;
|
|
67
68
|
expectTypeOf<IntentError['detail']>().toEqualTypeOf<RenderIntentInvalidDetail>();
|
|
69
|
+
type PublicSunCardinalityCode = Extract<PublicRenderError['code'], 'sun-cardinality'>;
|
|
70
|
+
expectTypeOf<PublicSunCardinalityCode>().toEqualTypeOf<'sun-cardinality'>();
|
|
71
|
+
expectTypeOf<PublicSunCardinalityError['code']>().toEqualTypeOf<'sun-cardinality'>();
|
|
72
|
+
expectTypeOf<PublicSunCardinalityError['detail']>().toEqualTypeOf<{
|
|
73
|
+
readonly field: 'sun';
|
|
74
|
+
readonly value: number;
|
|
75
|
+
}>();
|
|
68
76
|
const backendKind: RenderInspection['capabilities']['backendKind'] =
|
|
69
77
|
inspection.capabilities.backendKind;
|
|
70
78
|
void backendKind;
|
|
@@ -39,6 +39,7 @@ const expectedCodes = [
|
|
|
39
39
|
'dynamic-resolution-invalid-parameter',
|
|
40
40
|
'dynamic-resolution-requires-taa',
|
|
41
41
|
'dynamic-resolution-timing-unavailable',
|
|
42
|
+
'motion-blur-invalid-params',
|
|
42
43
|
'shadow-invalid-config',
|
|
43
44
|
'equirect-projection-failed',
|
|
44
45
|
'standard-light-budget-exceeded',
|
|
@@ -63,6 +64,7 @@ const expectedCodes = [
|
|
|
63
64
|
'render-feature-draw-recording-failed',
|
|
64
65
|
'environment-source-conflict',
|
|
65
66
|
'fog-cardinality',
|
|
67
|
+
'sun-cardinality',
|
|
66
68
|
'taa-caps-insufficient',
|
|
67
69
|
'environment-generation-failed',
|
|
68
70
|
'atmosphere-invalid-parameter',
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
PENDING_RENDER_FEATURE_ERROR_REPORT_INTERVAL_FRAMES,
|
|
4
|
+
shouldReportPendingRenderFeatureError,
|
|
5
|
+
} from '../features/error-reporting.js';
|
|
6
|
+
|
|
7
|
+
describe('pending render-feature error reporting', () => {
|
|
8
|
+
it('reports the first failure, suppresses the hot-loop, and reopens the cooldown', () => {
|
|
9
|
+
const lastReportedFrame = new Map<string, number>();
|
|
10
|
+
expect(shouldReportPendingRenderFeatureError(lastReportedFrame, 'feature\0plan', 10)).toBe(
|
|
11
|
+
true,
|
|
12
|
+
);
|
|
13
|
+
expect(shouldReportPendingRenderFeatureError(lastReportedFrame, 'feature\0plan', 11)).toBe(
|
|
14
|
+
false,
|
|
15
|
+
);
|
|
16
|
+
expect(
|
|
17
|
+
shouldReportPendingRenderFeatureError(
|
|
18
|
+
lastReportedFrame,
|
|
19
|
+
'feature\0plan',
|
|
20
|
+
10 + PENDING_RENDER_FEATURE_ERROR_REPORT_INTERVAL_FRAMES,
|
|
21
|
+
),
|
|
22
|
+
).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -89,6 +89,29 @@ function frame(host: RenderFeatureHost, frameNumber: number) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
describe('RenderFeature stage fault recovery lifecycle', () => {
|
|
92
|
+
it('retains the original exception when a feature stage throws', () => {
|
|
93
|
+
const cause = new TypeError('feature plan exploded');
|
|
94
|
+
const host = createRenderFeatureHost(
|
|
95
|
+
[
|
|
96
|
+
{
|
|
97
|
+
identity: 'synthetic.m10.throwing',
|
|
98
|
+
extract: () => ok({ ready: true }),
|
|
99
|
+
plan: () => {
|
|
100
|
+
throw cause;
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
caps,
|
|
105
|
+
).unwrap();
|
|
106
|
+
|
|
107
|
+
const result = frame(host, 1);
|
|
108
|
+
expect(result.errors[0]).toMatchObject({
|
|
109
|
+
code: 'render-feature-stage-failed',
|
|
110
|
+
detail: { featureIdentity: 'synthetic.m10.throwing', cause },
|
|
111
|
+
});
|
|
112
|
+
expect(host.diagnostics()[0]?.latestError).toMatchObject({ detail: { cause } });
|
|
113
|
+
});
|
|
114
|
+
|
|
92
115
|
it.each<Fault>([
|
|
93
116
|
'create',
|
|
94
117
|
'prepared-resource',
|
|
@@ -130,4 +130,35 @@ describe('RenderScene temporal retry boundary', () => {
|
|
|
130
130
|
instanceCollections.dispose();
|
|
131
131
|
}
|
|
132
132
|
});
|
|
133
|
+
|
|
134
|
+
it('keeps instance motion valid when stable generations reorder', () => {
|
|
135
|
+
const instanceCollections = new InstanceCollectionStore();
|
|
136
|
+
const collection = instanceCollections.create({ transforms: matrices(2, 1) }).unwrap();
|
|
137
|
+
const initialInstances = instanceCollections.snapshot(collection.collectionId).unwrap();
|
|
138
|
+
instanceCollections
|
|
139
|
+
.replace(
|
|
140
|
+
collection.collectionId,
|
|
141
|
+
matrices(2, 9),
|
|
142
|
+
new Uint32Array([
|
|
143
|
+
initialInstances.generations[1] ?? 0,
|
|
144
|
+
initialInstances.generations[0] ?? 0,
|
|
145
|
+
]),
|
|
146
|
+
)
|
|
147
|
+
.unwrap();
|
|
148
|
+
const reorderedInstances = instanceCollections.snapshot(collection.collectionId).unwrap();
|
|
149
|
+
const scene = new RenderScene();
|
|
150
|
+
scene.setTemporalTracking(true);
|
|
151
|
+
try {
|
|
152
|
+
scene.apply([updateSnapshot(snapshot(7, 1, initialInstances))]);
|
|
153
|
+
submit(scene, 7);
|
|
154
|
+
scene.apply([updateInstances(snapshot(7, 1, reorderedInstances))]);
|
|
155
|
+
|
|
156
|
+
const temporal = scene.temporalSnapshotBySlot(0);
|
|
157
|
+
expect(temporal?.motionValid).toBe(true);
|
|
158
|
+
expect(temporal?.previousInstances?.generations).toEqual(initialInstances.generations);
|
|
159
|
+
expect(temporal?.previousInstances?.transforms).toEqual(initialInstances.transforms);
|
|
160
|
+
} finally {
|
|
161
|
+
instanceCollections.dispose();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
133
164
|
});
|
|
@@ -87,7 +87,8 @@ function temporalResolveFrame(
|
|
|
87
87
|
bloom: 'off',
|
|
88
88
|
bloomThreshold: 1,
|
|
89
89
|
bloomIntensity: 1,
|
|
90
|
-
|
|
90
|
+
bloomSoftKnee: 0.5,
|
|
91
|
+
bloomScatter: 0.7,
|
|
91
92
|
clearColor: [0, 0, 0, 1],
|
|
92
93
|
temporal: frameState.lastSuccessfulTemporalView,
|
|
93
94
|
},
|
|
@@ -1147,6 +1148,7 @@ describe('renderer successful-submit transaction', () => {
|
|
|
1147
1148
|
height: 4,
|
|
1148
1149
|
frameIndex: 6,
|
|
1149
1150
|
historyValid: true,
|
|
1151
|
+
sampleTimeSeconds: 1,
|
|
1150
1152
|
});
|
|
1151
1153
|
const frameState = {
|
|
1152
1154
|
temporalGpuState: undefined,
|
|
@@ -1160,6 +1162,7 @@ describe('renderer successful-submit transaction', () => {
|
|
|
1160
1162
|
height: 4,
|
|
1161
1163
|
frameIndex: 6,
|
|
1162
1164
|
historyValid: true,
|
|
1165
|
+
sampleTimeSeconds: 1.016,
|
|
1163
1166
|
}),
|
|
1164
1167
|
},
|
|
1165
1168
|
environmentGeneration,
|
|
@@ -1244,6 +1247,7 @@ describe('renderer successful-submit transaction', () => {
|
|
|
1244
1247
|
expect(active.valid).toBe(true);
|
|
1245
1248
|
expect(active.readIndex).toBe(1);
|
|
1246
1249
|
expect(frameState.lastSuccessfulTemporalView).toBe(lastSuccessfulTemporalView);
|
|
1250
|
+
expect(frameState.lastSuccessfulTemporalView.sampleTimeSeconds).toBe(1);
|
|
1247
1251
|
expect(environmentLifecycle.inspect().activeSignature).toBe(activeEnvironment.signature);
|
|
1248
1252
|
expect(environmentLifecycle.inspect().lkgSignature).toBe(activeEnvironment.signature);
|
|
1249
1253
|
frameState.environmentGeneration = environmentLifecycle
|
|
@@ -1257,6 +1261,7 @@ describe('renderer successful-submit transaction', () => {
|
|
|
1257
1261
|
height: 8,
|
|
1258
1262
|
frameIndex: 7,
|
|
1259
1263
|
historyValid: true,
|
|
1264
|
+
sampleTimeSeconds: 1.016,
|
|
1260
1265
|
}),
|
|
1261
1266
|
};
|
|
1262
1267
|
const retryFrame = temporalResolveFrame(device, scope, frameState, {});
|
|
@@ -1273,6 +1278,7 @@ describe('renderer successful-submit transaction', () => {
|
|
|
1273
1278
|
expect(gpuCommitAfterSubmit).toEqual([1]);
|
|
1274
1279
|
expect(frameState.activeTemporalGpuState).not.toBe(active);
|
|
1275
1280
|
expect(frameState.successfulTemporalFrameIndex).toBe(8);
|
|
1281
|
+
expect(frameState.lastSuccessfulTemporalView?.sampleTimeSeconds).toBe(1.016);
|
|
1276
1282
|
expect(frameState.temporalGpuState).toBeUndefined();
|
|
1277
1283
|
expect(frameState.retiringTemporalGpuStates.size).toBe(1);
|
|
1278
1284
|
expect(publishEnvironment).toHaveBeenCalledTimes(1);
|
|
@@ -43,14 +43,14 @@ const entries: readonly FixtureEntry[] = [
|
|
|
43
43
|
bindings: '[]',
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
|
-
hash: 'fixture-bloom-
|
|
47
|
-
wgsl: '// fixture marker:
|
|
46
|
+
hash: 'fixture-bloom-downsample',
|
|
47
|
+
wgsl: '// fixture marker: BloomDownsampleParams\n',
|
|
48
48
|
glsl: '',
|
|
49
49
|
bindings: '[]',
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
|
-
hash: 'fixture-bloom-
|
|
53
|
-
wgsl: '// fixture marker:
|
|
52
|
+
hash: 'fixture-bloom-upsample',
|
|
53
|
+
wgsl: '// fixture marker: BloomUpsampleParams\n',
|
|
54
54
|
glsl: '',
|
|
55
55
|
bindings: '[]',
|
|
56
56
|
},
|
|
@@ -60,6 +60,8 @@ describe('skinned shadow caster', () => {
|
|
|
60
60
|
expect(shader).toContain('discard;');
|
|
61
61
|
expect(shader).toContain('@group(2) @binding(1)');
|
|
62
62
|
expect(shader).toContain('let skinMatrix = palette[');
|
|
63
|
+
expect(shader).toContain('materialIndex = u32(meshes[meshIndex].temporal.w);');
|
|
64
|
+
expect(shader).not.toContain('bitcast<u32>(meshes[meshIndex].temporal.w)');
|
|
63
65
|
expect(shader).toContain('@location(13) color : vec4<f32>');
|
|
64
66
|
expect(shader).toContain('out.color = in.color;');
|
|
65
67
|
expect(record).toContain('entry.source.skin.byteOffset');
|
|
@@ -31,7 +31,7 @@ function topology(): RenderPipelineTopology {
|
|
|
31
31
|
storageFormat: 'bgra8unorm',
|
|
32
32
|
viewFormat: 'bgra8unorm-srgb',
|
|
33
33
|
},
|
|
34
|
-
camera: { tonemap: 'aces-filmic', antialias: 'fxaa', bloom: 'on' },
|
|
34
|
+
camera: { tonemap: 'aces-filmic', antialias: 'fxaa', bloom: 'on', bloomIntensity: 1 },
|
|
35
35
|
shadow: {
|
|
36
36
|
directional: { mapSize: 64, cascadeCount: 1 },
|
|
37
37
|
spotMapSize: 64,
|
|
@@ -35,10 +35,15 @@ const dualViewProfile: SurfaceProfile = {
|
|
|
35
35
|
hasDisplayEndpoint: true,
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
+
type TopologyOverrides = Omit<Partial<RenderPipelineTopology>, 'camera'> & {
|
|
39
|
+
readonly camera?: Partial<RenderPipelineTopology['camera']>;
|
|
40
|
+
};
|
|
41
|
+
|
|
38
42
|
function topology(
|
|
39
43
|
profile: StandardProfile,
|
|
40
|
-
overrides:
|
|
44
|
+
overrides: TopologyOverrides = {},
|
|
41
45
|
): RenderPipelineTopology {
|
|
46
|
+
const { camera: cameraOverride, ...rest } = overrides;
|
|
42
47
|
return {
|
|
43
48
|
pipelineId: STANDARD_PIPELINE_ID,
|
|
44
49
|
standardProfile: profile,
|
|
@@ -49,7 +54,13 @@ function topology(
|
|
|
49
54
|
storageFormat: 'bgra8unorm',
|
|
50
55
|
viewFormat: 'bgra8unorm-srgb',
|
|
51
56
|
},
|
|
52
|
-
camera: {
|
|
57
|
+
camera: {
|
|
58
|
+
tonemap: 'aces-filmic',
|
|
59
|
+
antialias: 'fxaa',
|
|
60
|
+
bloom: 'on',
|
|
61
|
+
bloomIntensity: 1,
|
|
62
|
+
...cameraOverride,
|
|
63
|
+
},
|
|
53
64
|
shadow: {
|
|
54
65
|
directional: { mapSize: 64, cascadeCount: 1 },
|
|
55
66
|
spotMapSize: 64,
|
|
@@ -65,7 +76,7 @@ function topology(
|
|
|
65
76
|
},
|
|
66
77
|
featureTopologySignature: 'none',
|
|
67
78
|
gpuDrivenTopologySignature: '',
|
|
68
|
-
...
|
|
79
|
+
...rest,
|
|
69
80
|
};
|
|
70
81
|
}
|
|
71
82
|
|
|
@@ -109,7 +120,7 @@ function standardLightingFor(resolvedTopology: RenderPipelineTopology) {
|
|
|
109
120
|
return standardLighting;
|
|
110
121
|
}
|
|
111
122
|
|
|
112
|
-
async function build(profile: StandardProfile, overrides?:
|
|
123
|
+
async function build(profile: StandardProfile, overrides?: TopologyOverrides) {
|
|
113
124
|
const graph = new RenderGraphBuilder<RenderPipelineFrame>();
|
|
114
125
|
const resolvedTopology = topology(profile, overrides);
|
|
115
126
|
const histogram = graph.createBuffer('standard-auto-exposure-histogram', { size: 1024 });
|
|
@@ -232,7 +243,7 @@ function importedTaaHistory(graph: RenderGraphBuilder<RenderPipelineFrame>) {
|
|
|
232
243
|
};
|
|
233
244
|
}
|
|
234
245
|
|
|
235
|
-
async function buildTaa(profile: StandardProfile, overrides?:
|
|
246
|
+
async function buildTaa(profile: StandardProfile, overrides?: TopologyOverrides) {
|
|
236
247
|
const graph = new RenderGraphBuilder<RenderPipelineFrame>();
|
|
237
248
|
const history = importedTaaHistory(graph);
|
|
238
249
|
const resolvedTopology = topology(profile, overrides);
|
|
@@ -542,9 +553,8 @@ describe('forgeax::standard graph', () => {
|
|
|
542
553
|
expect(result.ok).toBe(true);
|
|
543
554
|
if (!result.ok) return;
|
|
544
555
|
const names = result.value.inspect().passes.map((pass) => pass.name);
|
|
545
|
-
expect(names).
|
|
546
|
-
expect(names).
|
|
547
|
-
expect(names).not.toContain('bloom-blur-v');
|
|
556
|
+
expect(names.some((name) => name.startsWith('bloom-downsample-'))).toBe(false);
|
|
557
|
+
expect(names.some((name) => name.startsWith('bloom-upsample-'))).toBe(false);
|
|
548
558
|
expect(names).not.toContain('bloom-composite');
|
|
549
559
|
expect(names).toContain('taa-resolve');
|
|
550
560
|
expect(names).toContain('output-transform');
|
|
@@ -37,6 +37,16 @@ describe('TemporalView reset matrix', () => {
|
|
|
37
37
|
expect(resolveTemporalReset(previous, next)).toBeUndefined();
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
it('applies camera reset reasons to motion-blur-only off views', () => {
|
|
41
|
+
const previous = createTemporalView({ ...base().input, antialias: 'none' });
|
|
42
|
+
const next = createTemporalView({
|
|
43
|
+
...previous.input,
|
|
44
|
+
antialias: 'none',
|
|
45
|
+
viewIdentity: 'secondary',
|
|
46
|
+
});
|
|
47
|
+
expect(resolveTemporalReset(previous, next)).toBe('view-switch');
|
|
48
|
+
});
|
|
49
|
+
|
|
40
50
|
it('treats an explicit off commit as a first-frame boundary when TAA resumes', () => {
|
|
41
51
|
const previous = base();
|
|
42
52
|
const off = createTemporalView({
|
|
@@ -130,7 +130,8 @@ describe('TemporalView v1', () => {
|
|
|
130
130
|
bloom: 'disabled',
|
|
131
131
|
bloomThreshold: 1,
|
|
132
132
|
bloomIntensity: 0,
|
|
133
|
-
|
|
133
|
+
bloomSoftKnee: 0.5,
|
|
134
|
+
bloomScatter: 0.7,
|
|
134
135
|
clearColor: [0, 0, 0, 1] as const,
|
|
135
136
|
} as unknown as CameraSnapshot;
|
|
136
137
|
const light = {
|
|
@@ -32,6 +32,10 @@ import { standardTemporalPostOrder } from '../temporal/standard-scene-data';
|
|
|
32
32
|
|
|
33
33
|
let device: RhiDevice;
|
|
34
34
|
|
|
35
|
+
type TopologyOverrides = Omit<Partial<RenderPipelineTopology>, 'camera'> & {
|
|
36
|
+
readonly camera?: Partial<RenderPipelineTopology['camera']>;
|
|
37
|
+
};
|
|
38
|
+
|
|
35
39
|
beforeAll(async () => {
|
|
36
40
|
const adapter = await rhi.requestAdapter();
|
|
37
41
|
if (!adapter.ok) throw adapter.error;
|
|
@@ -42,8 +46,9 @@ beforeAll(async () => {
|
|
|
42
46
|
|
|
43
47
|
function topology(
|
|
44
48
|
pipelineId: 'forgeax::standard',
|
|
45
|
-
overrides:
|
|
49
|
+
overrides: TopologyOverrides = {},
|
|
46
50
|
): RenderPipelineTopology {
|
|
51
|
+
const { camera: cameraOverride, ...rest } = overrides;
|
|
47
52
|
return {
|
|
48
53
|
pipelineId,
|
|
49
54
|
config: undefined,
|
|
@@ -53,7 +58,13 @@ function topology(
|
|
|
53
58
|
storageFormat: 'bgra8unorm',
|
|
54
59
|
viewFormat: 'bgra8unorm-srgb',
|
|
55
60
|
},
|
|
56
|
-
camera: {
|
|
61
|
+
camera: {
|
|
62
|
+
tonemap: 'aces-filmic',
|
|
63
|
+
antialias: 'fxaa',
|
|
64
|
+
bloom: 'on',
|
|
65
|
+
bloomIntensity: 1,
|
|
66
|
+
...cameraOverride,
|
|
67
|
+
},
|
|
57
68
|
shadow: {
|
|
58
69
|
directional: { mapSize: 1024, cascadeCount: 4 },
|
|
59
70
|
spotMapSize: 1024,
|
|
@@ -69,7 +80,7 @@ function topology(
|
|
|
69
80
|
},
|
|
70
81
|
featureTopologySignature: 'none',
|
|
71
82
|
gpuDrivenTopologySignature: '',
|
|
72
|
-
...
|
|
83
|
+
...rest,
|
|
73
84
|
};
|
|
74
85
|
}
|
|
75
86
|
|
|
@@ -877,7 +888,12 @@ describe('typed built-in pipeline topology', () => {
|
|
|
877
888
|
|
|
878
889
|
it('keeps zero-shutter Motion Blur free of temporal target and passes', () => {
|
|
879
890
|
expect(
|
|
880
|
-
isMotionBlurTemporalDemand({
|
|
891
|
+
isMotionBlurTemporalDemand({
|
|
892
|
+
shutterAngle: 0,
|
|
893
|
+
maxRadiusPixels: 32,
|
|
894
|
+
sampleCount: 8,
|
|
895
|
+
targetFps: 60,
|
|
896
|
+
}),
|
|
881
897
|
).toBe(false);
|
|
882
898
|
const compiled = compile(
|
|
883
899
|
standardPipeline,
|
|
@@ -42,7 +42,9 @@ describe('volumetric optics CPU oracle', () => {
|
|
|
42
42
|
|
|
43
43
|
it('converts the shared integrated-light basis once before normalized HG', () => {
|
|
44
44
|
expect(integrateShader).toContain('const VOLUME_LIGHT_PHASE_SCALE : f32 = FOUR_PI;');
|
|
45
|
-
expect(integrateShader
|
|
45
|
+
expect(integrateShader.match(/phase\s*\*\s*VOLUME_LIGHT_PHASE_SCALE\s*\*/g) ?? []).toHaveLength(
|
|
46
|
+
1,
|
|
47
|
+
);
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
it('keeps empty density at identity transmittance', () => {
|