@forgeax/engine-render 0.1.23 → 0.1.24
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 +131 -9
- package/dist/assembly/bundler-contract.d.ts +9 -0
- package/dist/assembly/bundler-contract.d.ts.map +1 -0
- package/dist/assembly/device-feature-admission.d.ts +8 -0
- package/dist/assembly/device-feature-admission.d.ts.map +1 -1
- package/dist/assembly/extended-lighting-fallback.d.ts +13 -0
- package/dist/assembly/extended-lighting-fallback.d.ts.map +1 -0
- package/dist/assembly/factory.d.ts +7 -243
- package/dist/assembly/factory.d.ts.map +1 -1
- package/dist/assembly/material/assembly.d.ts +3 -2
- package/dist/assembly/material/assembly.d.ts.map +1 -1
- package/dist/assembly/material/standard-layer-projection.d.ts +10 -0
- package/dist/assembly/material/standard-layer-projection.d.ts.map +1 -0
- package/dist/assembly/material-shader-policy.d.ts +168 -0
- package/dist/assembly/material-shader-policy.d.ts.map +1 -0
- package/dist/assembly/material-shader-prewarm.d.ts +21 -0
- package/dist/assembly/material-shader-prewarm.d.ts.map +1 -0
- package/dist/assembly/mesh-ssbo-grow.d.ts +88 -0
- package/dist/assembly/mesh-ssbo-grow.d.ts.map +1 -0
- package/dist/assembly/recovery/device-loss-fanout.d.ts +14 -0
- package/dist/assembly/recovery/device-loss-fanout.d.ts.map +1 -0
- package/dist/assembly/recovery/generation.d.ts +46 -0
- package/dist/assembly/recovery/generation.d.ts.map +1 -0
- package/dist/assembly/recovery/recovery-attempt.d.ts +63 -0
- package/dist/assembly/recovery/recovery-attempt.d.ts.map +1 -0
- package/dist/assembly/recovery/renderer-recover.d.ts +115 -0
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts +10 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -0
- package/dist/assembly/renderer-frame-transaction.d.ts +17 -0
- package/dist/assembly/renderer-frame-transaction.d.ts.map +1 -1
- package/dist/assembly/renderer-helpers.d.ts +19 -0
- package/dist/assembly/renderer-helpers.d.ts.map +1 -0
- package/dist/assembly/renderer-lifecycle.d.ts +34 -0
- package/dist/assembly/renderer-lifecycle.d.ts.map +1 -1
- package/dist/assembly/shader-prewarm-policy.d.ts +15 -0
- package/dist/assembly/shader-prewarm-policy.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-contract.d.ts +9 -0
- package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready.d.ts +55 -0
- package/dist/assembly/webgpu-ready.d.ts.map +1 -0
- package/dist/assembly/webgpu-renderer.d.ts +152 -0
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -0
- package/dist/authoring.mjs +1 -1
- package/dist/{chunk-4L3P6DT5.mjs → chunk-GE3SDD72.mjs} +37 -5
- package/dist/chunk-GE3SDD72.mjs.map +1 -0
- package/dist/{chunk-HZUPSYW7.mjs → chunk-KZFTK6DO.mjs} +102 -15
- package/dist/chunk-KZFTK6DO.mjs.map +1 -0
- package/dist/{chunk-FF7K3FSO.mjs → chunk-Q752SHI5.mjs} +89 -89
- package/dist/{chunk-FF7K3FSO.mjs.map → chunk-Q752SHI5.mjs.map} +1 -1
- package/dist/{chunk-QODA5UKU.mjs → chunk-RR5L4SRS.mjs} +2 -2
- package/dist/chunk-RR5L4SRS.mjs.map +1 -0
- package/dist/{chunk-YALPS44A.mjs → chunk-TFH5I7JJ.mjs} +47 -47
- package/dist/chunk-TFH5I7JJ.mjs.map +1 -0
- package/dist/{chunk-FBC4R44J.mjs → chunk-UQYS7FRM.mjs} +57 -57
- package/dist/chunk-UQYS7FRM.mjs.map +1 -0
- package/dist/{chunk-4NPUASZR.mjs → chunk-YKB5DQV7.mjs} +161 -19
- package/dist/chunk-YKB5DQV7.mjs.map +1 -0
- package/dist/{chunk-ZUWKAZ2I.mjs → chunk-YRUYGA4A.mjs} +615 -237
- package/dist/chunk-YRUYGA4A.mjs.map +1 -0
- package/dist/{chunk-DIYW3M5D.mjs → chunk-Z3YYH2MB.mjs} +2210 -2213
- package/dist/chunk-Z3YYH2MB.mjs.map +1 -0
- package/dist/construct-renderer.mjs +19841 -16190
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts +2 -0
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +24 -4
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/environment/frame.d.ts +5 -0
- package/dist/environment/frame.d.ts.map +1 -1
- package/dist/environment/inspection.d.ts +7 -0
- package/dist/environment/inspection.d.ts.map +1 -1
- package/dist/environment/lifecycle.d.ts +20 -2
- package/dist/environment/lifecycle.d.ts.map +1 -1
- package/dist/errors/recover.d.ts +58 -2
- package/dist/errors/recover.d.ts.map +1 -1
- package/dist/errors/render.d.ts +13 -0
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +899 -0
- package/dist/extract/contracts.d.ts.map +1 -0
- package/dist/features/host.d.ts +2 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +3 -0
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/prepared-graphics-store.d.ts +2 -0
- package/dist/features/prepared-graphics-store.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +28 -0
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +1 -0
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/ibl/IblPipelineCache.d.ts +7 -6
- package/dist/ibl/IblPipelineCache.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +7 -1
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +6 -6
- package/dist/internal.mjs +6 -6
- package/dist/light-buffer-layout.d.ts +3 -0
- package/dist/light-buffer-layout.d.ts.map +1 -1
- package/dist/materials.d.ts +34 -10
- package/dist/materials.d.ts.map +1 -1
- package/dist/mesh-material-bindings.d.ts +56 -0
- package/dist/mesh-material-bindings.d.ts.map +1 -1
- package/dist/occlusion-proxy.d.ts +4 -0
- package/dist/occlusion-proxy.d.ts.map +1 -0
- package/dist/pbr-pipeline.d.ts +32 -9
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/points-lines/prepare.d.ts +3 -0
- package/dist/points-lines/prepare.d.ts.map +1 -1
- package/dist/points-lines/standard-owner.d.ts +33 -0
- package/dist/points-lines/standard-owner.d.ts.map +1 -0
- package/dist/record/frame-snapshot.d.ts +5 -0
- 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 +14 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +14 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +7 -17
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +11 -3
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/recovery-pipeline.d.ts +30 -0
- package/dist/record/recovery-pipeline.d.ts.map +1 -0
- package/dist/record/render-context.d.ts +26 -4
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +1 -1
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +65 -0
- package/dist/recovery/render-system-candidate.d.ts.map +1 -0
- package/dist/render-contract.d.ts +64 -7
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts +12 -0
- package/dist/render-system-extract-tail.d.ts.map +1 -0
- package/dist/render-system-extract.d.ts +156 -22
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +128 -4
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +19 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -6
- package/dist/temporal/taa-history-store.d.ts +15 -1
- package/dist/temporal/taa-history-store.d.ts.map +1 -1
- package/dist/temporal/transaction.d.ts +5 -0
- package/dist/temporal/transaction.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/device-feature-admission.unit.test.ts +6 -0
- package/src/__tests__/device-loss-fanout.unit.test.ts +107 -0
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +21 -0
- package/src/__tests__/environment-generation.integration.test.ts +23 -0
- package/src/__tests__/expose-renderer-errors.unit.test.ts +34 -0
- package/src/__tests__/factory-contract.integration.test.ts +122 -7
- package/src/__tests__/fallback-row-stride-surface.unit.test.ts +4 -1
- package/src/__tests__/ibl/face-uniforms.unit.test.ts +2 -0
- package/src/__tests__/ibl-residency-publication.unit.test.ts +113 -0
- package/src/__tests__/material-contract-inventory.unit.test.ts +24 -9
- package/src/__tests__/material-cooked-projection.unit.test.ts +19 -1
- package/src/__tests__/material-static-dynamic-route.unit.test.ts +29 -0
- package/src/__tests__/material-stride-surface.unit.test.ts +7 -3
- package/src/__tests__/materials-standard-contract.unit.test.ts +104 -0
- package/src/__tests__/mesh-buffer-usage-surface.unit.test.ts +1 -1
- package/src/__tests__/physical-clearcoat.integration.test.ts +89 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +1 -1
- package/src/__tests__/prepared-graphics-pipeline-warmup.unit.test.ts +1 -1
- package/src/__tests__/recover-error-policy-owner.unit.test.ts +6 -3
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +140 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +85 -18
- package/src/__tests__/renderer-frame-generation-fence.integration.test.ts +47 -0
- package/src/__tests__/renderer-generation-assembly.unit.test.ts +77 -0
- package/src/__tests__/renderer-generation-failure.unit.test.ts +108 -0
- package/src/__tests__/renderer-producer-root-matrix.integration.test.ts +33 -0
- package/src/__tests__/renderer-recovery-capability-matrix.unit.test.ts +59 -0
- package/src/__tests__/renderer-recovery-contract.unit.test.ts +73 -0
- package/src/__tests__/renderer-recovery-lifecycle.unit.test.ts +216 -0
- package/src/__tests__/renderer-target-history-recovery.integration.test.ts +80 -0
- package/src/__tests__/shader-manifest-fixture.ts +13 -16
- package/src/__tests__/shader-stage-surface.unit.test.ts +1 -1
- package/src/__tests__/standard-layer-projection.unit.test.ts +23 -0
- package/src/__tests__/transmission-prewarm.unit.test.ts +95 -85
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +3 -1
- package/src/assembly/bundler-contract.ts +9 -0
- package/src/assembly/device-feature-admission.ts +15 -5
- package/src/assembly/extended-lighting-fallback.ts +216 -0
- package/src/assembly/factory.ts +81 -10088
- package/src/assembly/material/assembly.ts +18 -2
- package/src/assembly/material/standard-layer-projection.ts +16 -0
- package/src/assembly/material-shader-policy.ts +854 -0
- package/src/assembly/material-shader-prewarm.ts +88 -0
- package/src/assembly/mesh-ssbo-grow.ts +270 -0
- package/src/assembly/recovery/device-loss-fanout.ts +89 -0
- package/src/assembly/recovery/generation.ts +141 -0
- package/src/assembly/recovery/recovery-attempt.ts +800 -0
- package/src/assembly/recovery/renderer-recover.ts +906 -0
- package/src/assembly/renderer-facade.ts +342 -0
- package/src/assembly/renderer-frame-transaction.ts +45 -0
- package/src/assembly/renderer-helpers.ts +99 -0
- package/src/assembly/renderer-lifecycle.ts +159 -0
- package/src/assembly/shader-prewarm-policy.ts +157 -0
- package/src/assembly/webgpu-ready-contract.ts +14 -0
- package/src/assembly/webgpu-ready.ts +4079 -0
- package/src/assembly/webgpu-renderer.ts +4095 -0
- package/src/components/camera.ts +1 -1
- package/src/debug-draw-glue.ts +1 -1
- package/src/device/device-scope.ts +35 -6
- package/src/device/gpu-residency.ts +194 -26
- package/src/environment/frame.ts +6 -0
- package/src/environment/inspection.ts +10 -0
- package/src/environment/lifecycle.ts +114 -8
- package/src/errors/recover.ts +76 -5
- package/src/errors/render.ts +17 -0
- package/src/extract/contracts.ts +974 -0
- package/src/features/host.ts +16 -0
- package/src/features/prepared-gpu-work.ts +20 -2
- package/src/features/prepared-graphics-store.ts +17 -0
- package/src/gpu-driven/production-raster.ts +97 -2
- package/src/gpu-scene.ts +3 -2
- package/src/ibl/IblPipelineCache.ts +134 -50
- package/src/ibl/skylight-bind-group.ts +26 -0
- package/src/index.ts +9 -1
- package/src/light-buffer-layout.ts +3 -0
- package/src/materials.ts +262 -45
- package/src/mesh-material-bindings.ts +59 -0
- package/src/occlusion-proxy.ts +64 -0
- package/src/pbr-pipeline.ts +139 -15
- package/src/points-lines/__tests__/cache-recovery.unit.test.ts +22 -0
- package/src/points-lines/prepare.ts +9 -0
- package/src/points-lines/standard-owner.ts +403 -0
- package/src/record/__tests__/frame-targets-recovery.unit.test.ts +60 -0
- package/src/record/__tests__/main-pass-geometry.unit.test.ts +26 -1
- package/src/record/__tests__/mesh-ssbo.unit.test.ts +38 -0
- package/src/record/__tests__/recovery-pipeline.unit.test.ts +124 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +28 -13
- package/src/record/__tests__/standard-pbr-ubo-layout.unit.test.ts +47 -4
- package/src/record/__tests__/temporal-payload.unit.test.ts +22 -43
- package/src/record/__tests__/typed-frame-graph-plan.unit.test.ts +1 -1
- package/src/record/frame-snapshot.ts +15 -0
- package/src/record/frame-targets.ts +11 -1
- package/src/record/frame.ts +6 -2
- package/src/record/helpers.ts +1 -1
- package/src/record/main-pass-geometry.ts +230 -5
- package/src/record/main-pass-material.ts +316 -209
- package/src/record/main-pass-sprite-draws.ts +6 -7
- package/src/record/main-pass.ts +94 -6
- package/src/record/mesh-ssbo.ts +32 -10
- package/src/record/recovery-pipeline.ts +695 -0
- package/src/record/render-context.ts +35 -4
- package/src/record/typed-frame-graph.ts +45 -1
- package/src/record/view-ubo.ts +3 -3
- package/src/recovery/render-system-candidate.ts +839 -0
- package/src/render-contract.ts +65 -7
- package/src/render-system-extract-tail.ts +2983 -0
- package/src/render-system-extract.ts +124 -2977
- package/src/render-system.ts +868 -659
- package/src/scene/render-scene.ts +124 -1
- package/src/temporal/frame-coordinator.ts +4 -1
- package/src/temporal/taa-history-store.ts +46 -2
- package/src/temporal/temporal-view.ts +1 -1
- package/src/temporal/transaction.ts +9 -0
- package/src/tilemap-chunk-extract-system.ts +1 -1
- package/src/transmission/__tests__/standard-transmission.dawn.test.ts +19 -12
- package/dist/chunk-4L3P6DT5.mjs.map +0 -1
- package/dist/chunk-4NPUASZR.mjs.map +0 -1
- package/dist/chunk-DIYW3M5D.mjs.map +0 -1
- package/dist/chunk-FBC4R44J.mjs.map +0 -1
- package/dist/chunk-HZUPSYW7.mjs.map +0 -1
- package/dist/chunk-QODA5UKU.mjs.map +0 -1
- package/dist/chunk-YALPS44A.mjs.map +0 -1
- package/dist/chunk-ZUWKAZ2I.mjs.map +0 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { RhiCanvasContext, RhiDevice, Texture, TextureView } from '@forgeax/engine-rhi';
|
|
2
|
+
import { err, ok, RhiError } from '@forgeax/engine-rhi';
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { acquireSwapChainTarget } from '../frame-targets';
|
|
5
|
+
import type { PipelineState, RenderSystemInternals } from '../render-context';
|
|
6
|
+
|
|
7
|
+
function device(): RhiDevice {
|
|
8
|
+
return {
|
|
9
|
+
caps: { backendKind: 'webgpu', storageBuffer: true },
|
|
10
|
+
limits: {},
|
|
11
|
+
createTextureView: vi.fn(() => ok({} as TextureView)),
|
|
12
|
+
} as unknown as RhiDevice;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function unavailableSurfaceError(): RhiError {
|
|
16
|
+
return new RhiError({
|
|
17
|
+
code: 'webgpu-runtime-error',
|
|
18
|
+
expected: 'the surface has a current texture',
|
|
19
|
+
hint: 'configure the surface and retry',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('acquireSwapChainTarget recovery', () => {
|
|
24
|
+
it('resolves the recorder device before reconfiguring a recovered surface', () => {
|
|
25
|
+
const recorderDevice = device();
|
|
26
|
+
const configuredDevice = device();
|
|
27
|
+
const texture = {} as Texture;
|
|
28
|
+
const getCurrentTexture = vi
|
|
29
|
+
.fn()
|
|
30
|
+
.mockReturnValueOnce(err(unavailableSurfaceError()))
|
|
31
|
+
.mockReturnValueOnce(ok(texture));
|
|
32
|
+
const configure = vi.fn(() => ok(undefined));
|
|
33
|
+
const context = { configure, getCurrentTexture } as unknown as RhiCanvasContext;
|
|
34
|
+
const resolveSurfaceDevice = vi.fn(() => ok(configuredDevice));
|
|
35
|
+
const internals = {
|
|
36
|
+
context,
|
|
37
|
+
device: recorderDevice,
|
|
38
|
+
canvas: { width: 800, height: 600 },
|
|
39
|
+
resolveSurfaceDevice,
|
|
40
|
+
errorRegistry: { fire: vi.fn() },
|
|
41
|
+
healthRegistry: { fire: vi.fn() },
|
|
42
|
+
} as unknown as RenderSystemInternals;
|
|
43
|
+
const pipelineState = {
|
|
44
|
+
device: recorderDevice,
|
|
45
|
+
format: 'bgra8unorm',
|
|
46
|
+
colorAttachmentFormat: 'bgra8unorm',
|
|
47
|
+
perPassResources: { configured: false },
|
|
48
|
+
} as unknown as PipelineState;
|
|
49
|
+
|
|
50
|
+
const result = acquireSwapChainTarget(internals, pipelineState);
|
|
51
|
+
|
|
52
|
+
expect(result).toMatchObject({ currentTexture: texture, targetW: 800, targetH: 600 });
|
|
53
|
+
expect(resolveSurfaceDevice).toHaveBeenCalledWith(recorderDevice);
|
|
54
|
+
expect(configure).toHaveBeenCalledWith(
|
|
55
|
+
expect.objectContaining({ device: configuredDevice, format: 'bgra8unorm' }),
|
|
56
|
+
);
|
|
57
|
+
expect(getCurrentTexture).toHaveBeenCalledTimes(2);
|
|
58
|
+
expect(pipelineState.perPassResources.configured).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
geometryRenderStateForPass,
|
|
4
|
+
requestsStandardTransmissionVariant,
|
|
5
|
+
} from '../main-pass-geometry';
|
|
3
6
|
|
|
4
7
|
describe('geometry pass render state', () => {
|
|
5
8
|
it('keeps the main depth authority while admitting equal temporal depth', () => {
|
|
@@ -45,3 +48,25 @@ describe('geometry pass render state', () => {
|
|
|
45
48
|
expect(accepted).toEqual([visibleDepth]);
|
|
46
49
|
});
|
|
47
50
|
});
|
|
51
|
+
|
|
52
|
+
describe('Standard transmission variant selection', () => {
|
|
53
|
+
it('uses authored transmission presence instead of the canonical reserved schema', () => {
|
|
54
|
+
const baseOnly = {
|
|
55
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
56
|
+
paramSnapshot: { alphaCutoff: 0.25 },
|
|
57
|
+
} as const;
|
|
58
|
+
const transmission = {
|
|
59
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
60
|
+
paramSnapshot: { transmission: 0 },
|
|
61
|
+
} as const;
|
|
62
|
+
|
|
63
|
+
expect(requestsStandardTransmissionVariant(baseOnly)).toBe(false);
|
|
64
|
+
expect(requestsStandardTransmissionVariant(transmission)).toBe(true);
|
|
65
|
+
expect(
|
|
66
|
+
requestsStandardTransmissionVariant({
|
|
67
|
+
materialShaderId: 'custom::standard',
|
|
68
|
+
paramSnapshot: { transmission: 1 },
|
|
69
|
+
}),
|
|
70
|
+
).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { getOrCreateFromChain } from '../mesh-ssbo';
|
|
3
|
+
|
|
4
|
+
describe('mesh bind-group cache chains', () => {
|
|
5
|
+
it('keeps variable-depth material resource chains isolated', () => {
|
|
6
|
+
const root = new WeakMap<object, unknown>();
|
|
7
|
+
const shared = { shared: true };
|
|
8
|
+
const coatSampler = { coatSampler: true };
|
|
9
|
+
const coatTexture = { coatTexture: true };
|
|
10
|
+
const counts = { createBindGroup: 0, keys: [] as string[] };
|
|
11
|
+
const long = getOrCreateFromChain(
|
|
12
|
+
root,
|
|
13
|
+
[shared, coatSampler, coatTexture],
|
|
14
|
+
'material-shared',
|
|
15
|
+
() => ({ id: 'long' }) as never,
|
|
16
|
+
counts,
|
|
17
|
+
);
|
|
18
|
+
const short = getOrCreateFromChain(
|
|
19
|
+
root,
|
|
20
|
+
[shared, coatSampler],
|
|
21
|
+
'material-shared',
|
|
22
|
+
() => ({ id: 'short' }) as never,
|
|
23
|
+
counts,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(
|
|
27
|
+
getOrCreateFromChain(
|
|
28
|
+
root,
|
|
29
|
+
[shared, coatSampler],
|
|
30
|
+
'material-shared',
|
|
31
|
+
() => ({ id: 'unexpected' }) as never,
|
|
32
|
+
counts,
|
|
33
|
+
),
|
|
34
|
+
).toBe(short);
|
|
35
|
+
expect(long).not.toBe(short);
|
|
36
|
+
expect(counts.createBindGroup).toBe(2);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { makeZeroCameraFallbackSnapshot, type ValidatedRenderable } from '../frame-snapshot';
|
|
4
|
+
import {
|
|
5
|
+
createRecoveryColdWorkGuard,
|
|
6
|
+
prepareRecoveryPipelineReadiness,
|
|
7
|
+
} from '../recovery-pipeline';
|
|
8
|
+
import type { PipelineState, RenderSystemInternals } from '../render-context';
|
|
9
|
+
|
|
10
|
+
function probeRenderable(probe: boolean): ValidatedRenderable {
|
|
11
|
+
const material = {
|
|
12
|
+
baseColor: [1, 1, 1],
|
|
13
|
+
metallic: 0,
|
|
14
|
+
roughness: 1,
|
|
15
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
16
|
+
} as never;
|
|
17
|
+
return {
|
|
18
|
+
source: {
|
|
19
|
+
material,
|
|
20
|
+
materials: [material],
|
|
21
|
+
...(probe ? { probeBlendRecord: {} } : {}),
|
|
22
|
+
},
|
|
23
|
+
mesh: {
|
|
24
|
+
layoutProjection: deriveVertexLayoutProjection({
|
|
25
|
+
position: new Float32Array(0),
|
|
26
|
+
normal: new Float32Array(0),
|
|
27
|
+
uv: new Float32Array(0),
|
|
28
|
+
tangent: new Float32Array(0),
|
|
29
|
+
}),
|
|
30
|
+
indexFormat: 'uint32',
|
|
31
|
+
submeshes: [
|
|
32
|
+
{
|
|
33
|
+
indexOffset: 0,
|
|
34
|
+
indexCount: 3,
|
|
35
|
+
vertexCount: 3,
|
|
36
|
+
topology: 'triangle-list',
|
|
37
|
+
materialSlot: 0,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
} as never;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function recoveryReadinessFor(
|
|
45
|
+
renderable: ValidatedRenderable,
|
|
46
|
+
reflectionFallbackAvailable = false,
|
|
47
|
+
) {
|
|
48
|
+
const pipeline = {} as never;
|
|
49
|
+
const internals = {
|
|
50
|
+
device: { caps: { backendKind: 'webgpu', storageBuffer: true } },
|
|
51
|
+
getMaterialShaderPipelineEntry: (..._args: unknown[]) => ({
|
|
52
|
+
pipeline,
|
|
53
|
+
group2Contract: 'cluster' as const,
|
|
54
|
+
}),
|
|
55
|
+
} as unknown as RenderSystemInternals;
|
|
56
|
+
const pipelineState = {
|
|
57
|
+
colorAttachmentFormat: 'bgra8unorm',
|
|
58
|
+
} as unknown as PipelineState;
|
|
59
|
+
return prepareRecoveryPipelineReadiness({
|
|
60
|
+
internals,
|
|
61
|
+
pipelineState,
|
|
62
|
+
camera: makeZeroCameraFallbackSnapshot(),
|
|
63
|
+
standardLighting: { kind: 'no-local-lights' } as never,
|
|
64
|
+
validated: [renderable],
|
|
65
|
+
dispatch: [],
|
|
66
|
+
shadowCastersActive: false,
|
|
67
|
+
splitLdrSprite: false,
|
|
68
|
+
reflectionFallbackAvailable,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('recovery first-frame cold-work guard', () => {
|
|
73
|
+
it('prepares the probe-enabled Standard PBR variant used by the live record path', () => {
|
|
74
|
+
const readiness = recoveryReadinessFor(probeRenderable(true));
|
|
75
|
+
const standardPbr = readiness.pipelineSpecs.find(
|
|
76
|
+
(spec) => spec.shader.id === 'forgeax::default-standard-pbr',
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(standardPbr?.shader.variantSet).toContain('PROBE_BLEND_AVAILABLE=true');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('does not add the probe ABI to a Standard PBR renderable without a record', () => {
|
|
83
|
+
const readiness = recoveryReadinessFor(probeRenderable(false));
|
|
84
|
+
const standardPbr = readiness.pipelineSpecs.find(
|
|
85
|
+
(spec) => spec.shader.id === 'forgeax::default-standard-pbr',
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
expect(standardPbr?.shader.variantSet).not.toContain('PROBE_BLEND_AVAILABLE=true');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('prepares the Standard PBR reflection fallback MRT variant', () => {
|
|
92
|
+
const readiness = recoveryReadinessFor(probeRenderable(false), true);
|
|
93
|
+
const standardPbr = readiness.pipelineSpecs.find(
|
|
94
|
+
(spec) => spec.shader.id === 'forgeax::default-standard-pbr',
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(standardPbr?.shader.variantSet).toContain('REFLECTION_FALLBACK_AVAILABLE=true');
|
|
98
|
+
expect(standardPbr?.attachments.colorFormats).toEqual(['rgba16float', 'rgba16float']);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('accepts a prepared candidate with no first-frame cold work', () => {
|
|
102
|
+
const guard = createRecoveryColdWorkGuard();
|
|
103
|
+
|
|
104
|
+
guard.arm();
|
|
105
|
+
expect(() => guard.finish()).not.toThrow();
|
|
106
|
+
expect(() => guard.finish()).not.toThrow();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it.each([
|
|
110
|
+
[
|
|
111
|
+
'pipeline',
|
|
112
|
+
(guard: ReturnType<typeof createRecoveryColdWorkGuard>) => guard.notePipelineColdWork(),
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
'upload',
|
|
116
|
+
(guard: ReturnType<typeof createRecoveryColdWorkGuard>) => guard.noteUploadColdWork(),
|
|
117
|
+
],
|
|
118
|
+
] as const)('hard-fails a first-frame %s miss before submission', (_kind, note) => {
|
|
119
|
+
const guard = createRecoveryColdWorkGuard();
|
|
120
|
+
|
|
121
|
+
guard.arm();
|
|
122
|
+
expect(() => note(guard)).toThrow(/recovery first frame performed cold/);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import type { Buffer, Sampler, TextureView } from '@forgeax/engine-rhi';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import { assembleMaterialWithSkylightEntries } from '../../ibl/skylight-bind-group';
|
|
4
|
+
import { defaultViewForUserRegionField } from '../main-pass-material';
|
|
4
5
|
import { buildSpritePassBaseMaterialEntries } from '../main-pass-sprite-draws';
|
|
5
6
|
import type { PipelineState } from '../render-context';
|
|
6
7
|
|
|
7
8
|
describe('sprite Standard material ABI', () => {
|
|
9
|
+
it('uses the dedicated neutral anisotropy fallback instead of white', () => {
|
|
10
|
+
const white = {} as TextureView;
|
|
11
|
+
const anisotropy = {} as TextureView;
|
|
12
|
+
const pipelineState = {
|
|
13
|
+
defaultWhiteTextureView: white,
|
|
14
|
+
defaultAnisotropyTextureView: anisotropy,
|
|
15
|
+
defaultNormalTextureView: {} as TextureView,
|
|
16
|
+
fallbackTextureView: {} as TextureView,
|
|
17
|
+
} as PipelineState;
|
|
18
|
+
|
|
19
|
+
expect(defaultViewForUserRegionField('anisotropyTexture', pipelineState)).toBe(anisotropy);
|
|
20
|
+
expect(defaultViewForUserRegionField('baseColorTexture', pipelineState)).toBe(
|
|
21
|
+
pipelineState.fallbackTextureView,
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
8
25
|
it('covers transmission and thickness before IBL injection', () => {
|
|
9
26
|
const sampler = {} as Sampler;
|
|
10
27
|
const white = {} as TextureView;
|
|
@@ -21,14 +38,12 @@ describe('sprite Standard material ABI', () => {
|
|
|
21
38
|
const entries = buildSpritePassBaseMaterialEntries(pipelineState, spriteTexture);
|
|
22
39
|
|
|
23
40
|
expect(entries.map((entry) => entry.binding)).toEqual([
|
|
24
|
-
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
|
41
|
+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
|
25
42
|
]);
|
|
26
43
|
expect(entries[13]).toMatchObject({ binding: 13, resource: { kind: 'sampler' } });
|
|
27
44
|
expect(entries[14]).toMatchObject({ binding: 14, resource: { kind: 'textureView' } });
|
|
28
|
-
expect(entries[15]).toMatchObject({ binding: 15, resource: { kind: 'sampler' } });
|
|
29
|
-
expect(entries[16]).toMatchObject({ binding: 16, resource: { kind: 'textureView' } });
|
|
30
45
|
expect(entries[2]?.resource).toMatchObject({ kind: 'textureView', value: spriteTexture });
|
|
31
|
-
expect(entries).toHaveLength(
|
|
46
|
+
expect(entries).toHaveLength(15);
|
|
32
47
|
|
|
33
48
|
const merged = assembleMaterialWithSkylightEntries(entries, {
|
|
34
49
|
irradianceView: {} as TextureView,
|
|
@@ -39,19 +54,19 @@ describe('sprite Standard material ABI', () => {
|
|
|
39
54
|
brdfLutSampler: sampler,
|
|
40
55
|
intensityBuffer: {} as Buffer,
|
|
41
56
|
});
|
|
42
|
-
expect(merged[
|
|
43
|
-
binding:
|
|
57
|
+
expect(merged[15]).toMatchObject({
|
|
58
|
+
binding: 15,
|
|
44
59
|
resource: { kind: 'textureView' },
|
|
45
60
|
});
|
|
46
|
-
expect(merged[
|
|
47
|
-
binding:
|
|
61
|
+
expect(merged[22]).toMatchObject({
|
|
62
|
+
binding: 22,
|
|
48
63
|
resource: { kind: 'sampler', value: sampler },
|
|
49
64
|
});
|
|
50
|
-
expect(merged[
|
|
51
|
-
binding:
|
|
65
|
+
expect(merged[23]).toMatchObject({
|
|
66
|
+
binding: 23,
|
|
52
67
|
resource: { kind: 'textureView' },
|
|
53
68
|
});
|
|
54
|
-
expect(merged).toHaveLength(
|
|
69
|
+
expect(merged).toHaveLength(24);
|
|
55
70
|
|
|
56
71
|
const backdrop = {} as TextureView;
|
|
57
72
|
const active = assembleMaterialWithSkylightEntries(
|
|
@@ -67,8 +82,8 @@ describe('sprite Standard material ABI', () => {
|
|
|
67
82
|
},
|
|
68
83
|
{ sampler, backdropView: backdrop },
|
|
69
84
|
);
|
|
70
|
-
expect(active[
|
|
71
|
-
binding:
|
|
85
|
+
expect(active[23]).toMatchObject({
|
|
86
|
+
binding: 23,
|
|
72
87
|
resource: { kind: 'textureView', value: backdrop },
|
|
73
88
|
});
|
|
74
89
|
});
|
|
@@ -14,7 +14,7 @@ describe('Standard PBR UBO layout', () => {
|
|
|
14
14
|
derived.coordinateRecords.map((record) => [record.parameter, record.offset]),
|
|
15
15
|
);
|
|
16
16
|
|
|
17
|
-
expect(derived.totalBytes).toBe(
|
|
17
|
+
expect(derived.totalBytes).toBe(736);
|
|
18
18
|
expect(numeric.get('normalScale')).toBe(92);
|
|
19
19
|
expect(numeric.get('transmission')).toBe(96);
|
|
20
20
|
expect(numeric.get('ior')).toBe(100);
|
|
@@ -26,6 +26,26 @@ describe('Standard PBR UBO layout', () => {
|
|
|
26
26
|
expect(coordinates.get('thicknessTexture')).toBe(352);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
+
it('reserves the physical-layer tail required by the clearcoat shader ABI', () => {
|
|
30
|
+
const material = {
|
|
31
|
+
baseColor: vec3.create(0.7, 0.7, 0.7),
|
|
32
|
+
metallic: 0,
|
|
33
|
+
roughness: 0.4,
|
|
34
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
35
|
+
materialParamSchema: DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
|
|
36
|
+
paramSnapshot: {
|
|
37
|
+
clearcoat: 0.8,
|
|
38
|
+
clearcoatRoughness: 0.2,
|
|
39
|
+
clearcoatNormalScale: 0.75,
|
|
40
|
+
},
|
|
41
|
+
} satisfies MaterialSnapshot;
|
|
42
|
+
const payload = buildPbrMaterialUboPayload(material);
|
|
43
|
+
const f32 = new Float32Array(payload.buffer);
|
|
44
|
+
|
|
45
|
+
expect(payload.byteLength).toBe(736);
|
|
46
|
+
expect(f32.slice(108, 111)).toEqual(new Float32Array([0.8, 0.2, 0.75]));
|
|
47
|
+
});
|
|
48
|
+
|
|
29
49
|
it('writes transmission scalars and defaults at the reflected offsets', () => {
|
|
30
50
|
const material = {
|
|
31
51
|
baseColor: vec3.create(0.7, 0.7, 0.7),
|
|
@@ -33,6 +53,7 @@ describe('Standard PBR UBO layout', () => {
|
|
|
33
53
|
roughness: 0.4,
|
|
34
54
|
normalScale: 0.25,
|
|
35
55
|
materialShaderId: 'forgeax::default-standard-pbr',
|
|
56
|
+
materialParamSchema: DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
|
|
36
57
|
paramSnapshot: {
|
|
37
58
|
transmission: 0.75,
|
|
38
59
|
ior: 1.6,
|
|
@@ -48,7 +69,7 @@ describe('Standard PBR UBO layout', () => {
|
|
|
48
69
|
);
|
|
49
70
|
});
|
|
50
71
|
|
|
51
|
-
it('uses all
|
|
72
|
+
it('uses all seven user-region identity coordinate records in the no-snapshot fallback', () => {
|
|
52
73
|
const material = {
|
|
53
74
|
baseColor: vec3.create(0.7, 0.7, 0.7),
|
|
54
75
|
metallic: 0,
|
|
@@ -60,8 +81,30 @@ describe('Standard PBR UBO layout', () => {
|
|
|
60
81
|
applyMaterialTextureUvScales(payload, material, new World());
|
|
61
82
|
|
|
62
83
|
const f32 = new Float32Array(payload.buffer);
|
|
63
|
-
expect(f32.slice(32,
|
|
64
|
-
new Float32Array(Array(
|
|
84
|
+
expect(f32.slice(32, 88)).toEqual(
|
|
85
|
+
new Float32Array(Array(7).fill([0, 0, 1, 1, 0, 0, 1, 1]).flat()),
|
|
65
86
|
);
|
|
66
87
|
});
|
|
88
|
+
|
|
89
|
+
it('keeps the physical tail intact when standard coordinates are projected', () => {
|
|
90
|
+
const material = {
|
|
91
|
+
baseColor: vec3.create(0.7, 0.7, 0.7),
|
|
92
|
+
metallic: 0,
|
|
93
|
+
roughness: 0.4,
|
|
94
|
+
materialShaderId: 'forgeax::default-standard-pbr',
|
|
95
|
+
materialParamSchema: DEFAULT_STANDARD_PBR_PARAM_SCHEMA,
|
|
96
|
+
paramSnapshot: {
|
|
97
|
+
clearcoat: 1,
|
|
98
|
+
clearcoatRoughness: 0.18,
|
|
99
|
+
clearcoatNormalScale: 0.75,
|
|
100
|
+
},
|
|
101
|
+
} satisfies MaterialSnapshot;
|
|
102
|
+
const payload = buildPbrMaterialUboPayload(material);
|
|
103
|
+
|
|
104
|
+
applyMaterialTextureUvScales(payload, material, new World());
|
|
105
|
+
|
|
106
|
+
const f32 = new Float32Array(payload.buffer);
|
|
107
|
+
expect(f32.slice(108, 111)).toEqual(new Float32Array([1, 0.18, 0.75]));
|
|
108
|
+
expect(f32.slice(32, 40)).toEqual(new Float32Array([0, 0, 1, 1, 0, 0, 1, 1]));
|
|
109
|
+
});
|
|
67
110
|
});
|
|
@@ -2,7 +2,7 @@ import { vec3 } from '@forgeax/engine-math';
|
|
|
2
2
|
import type { Buffer, RhiQueue } from '@forgeax/engine-rhi';
|
|
3
3
|
import { describe, expect, it } from 'vitest';
|
|
4
4
|
import type { CameraSnapshot } from '../../render-contract';
|
|
5
|
-
import type { RenderableSnapshot
|
|
5
|
+
import type { RenderableSnapshot } from '../../render-system-extract';
|
|
6
6
|
import type { TemporalView } from '../../temporal/temporal-view';
|
|
7
7
|
import type { ValidatedRenderable } from '../frame-snapshot';
|
|
8
8
|
import { uploadMeshSsboBatch } from '../mesh-ssbo';
|
|
@@ -98,27 +98,8 @@ function temporalView(): TemporalView {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
describe('temporal GPU payloads', () => {
|
|
101
|
-
it('
|
|
101
|
+
it('keeps the zero-directional fallback direction finite and unscaled', () => {
|
|
102
102
|
const { queue, uploads } = recordingQueue();
|
|
103
|
-
const projectorMatrix = translatedMatrix(9);
|
|
104
|
-
const projector: SpotLightSnapshot = {
|
|
105
|
-
kind: 'spot',
|
|
106
|
-
entity: 4,
|
|
107
|
-
position: vec3.create(0, 2, 0),
|
|
108
|
-
direction: vec3.create(0, -1, 0),
|
|
109
|
-
color: vec3.create(1, 1, 1),
|
|
110
|
-
intensity: 1,
|
|
111
|
-
invRangeSquared: 0.04,
|
|
112
|
-
cosInner: 0.95,
|
|
113
|
-
cosOuter: 0.8,
|
|
114
|
-
castShadow: false,
|
|
115
|
-
lightViewProj: projectorMatrix,
|
|
116
|
-
mapSize: 512,
|
|
117
|
-
nearPlane: 0.1,
|
|
118
|
-
farPlane: 25,
|
|
119
|
-
shadowAtlasTile: -1,
|
|
120
|
-
projectorAsset: { kind: 'texture' } as never,
|
|
121
|
-
};
|
|
122
103
|
writeViewUbo(
|
|
123
104
|
queue,
|
|
124
105
|
{} as Buffer,
|
|
@@ -126,19 +107,20 @@ describe('temporal GPU payloads', () => {
|
|
|
126
107
|
{
|
|
127
108
|
kind: 'directional',
|
|
128
109
|
direction: vec3.create(0, -1, 0),
|
|
129
|
-
color: vec3.create(
|
|
130
|
-
intensity:
|
|
110
|
+
color: vec3.create(0, 0, 0),
|
|
111
|
+
intensity: 0,
|
|
131
112
|
},
|
|
132
|
-
{ point: [], spot: [
|
|
133
|
-
[
|
|
134
|
-
undefined,
|
|
135
|
-
0,
|
|
113
|
+
{ point: [], spot: [] } as never,
|
|
114
|
+
[],
|
|
136
115
|
);
|
|
116
|
+
|
|
137
117
|
const payload = new Float32Array(uploads[0]?.buffer ?? new ArrayBuffer(0));
|
|
138
|
-
expect(payload[
|
|
118
|
+
expect(Array.from(payload.slice(16, 19))).toEqual([0, -1, 0]);
|
|
119
|
+
expect(Array.from(payload.slice(20, 23))).toEqual([0, 0, 0]);
|
|
120
|
+
expect(Array.from(payload.slice(16, 23)).every(Number.isFinite)).toBe(true);
|
|
139
121
|
});
|
|
140
122
|
|
|
141
|
-
it('
|
|
123
|
+
it('keeps directional orientation independent from light intensity', () => {
|
|
142
124
|
const { queue, uploads } = recordingQueue();
|
|
143
125
|
writeViewUbo(
|
|
144
126
|
queue,
|
|
@@ -146,28 +128,20 @@ describe('temporal GPU payloads', () => {
|
|
|
146
128
|
camera(),
|
|
147
129
|
{
|
|
148
130
|
kind: 'directional',
|
|
149
|
-
direction: vec3.create(0, -
|
|
131
|
+
direction: vec3.create(0.25, -0.5, 0.75),
|
|
150
132
|
color: vec3.create(1, 1, 1),
|
|
151
|
-
intensity:
|
|
133
|
+
intensity: 4,
|
|
152
134
|
},
|
|
153
135
|
{ point: [], spot: [] } as never,
|
|
154
136
|
[],
|
|
155
|
-
temporalView(),
|
|
156
137
|
);
|
|
157
138
|
|
|
158
139
|
const payload = new Float32Array(uploads[0]?.buffer ?? new ArrayBuffer(0));
|
|
159
|
-
expect(payload.
|
|
160
|
-
expect(payload[196]).toBeCloseTo(Math.sqrt(3));
|
|
161
|
-
expect(payload[212]).toBe(3);
|
|
162
|
-
expect(payload[228]).toBeCloseTo(0.1);
|
|
163
|
-
expect(payload[229]).toBe(100);
|
|
164
|
-
expect(payload[230]).toBe(0);
|
|
140
|
+
expect(Array.from(payload.slice(16, 19))).toEqual([0.25, -0.5, 0.75]);
|
|
165
141
|
});
|
|
166
142
|
|
|
167
|
-
it('
|
|
143
|
+
it('writes temporal current/previous projection after the spot-light lanes', () => {
|
|
168
144
|
const { queue, uploads } = recordingQueue();
|
|
169
|
-
const acceptedCamera = [7, 8, 9] as const;
|
|
170
|
-
const temporal = { ...temporalView(), previousCameraPosition: acceptedCamera };
|
|
171
145
|
writeViewUbo(
|
|
172
146
|
queue,
|
|
173
147
|
{} as Buffer,
|
|
@@ -180,11 +154,16 @@ describe('temporal GPU payloads', () => {
|
|
|
180
154
|
},
|
|
181
155
|
{ point: [], spot: [] } as never,
|
|
182
156
|
[],
|
|
183
|
-
|
|
157
|
+
temporalView(),
|
|
184
158
|
);
|
|
185
159
|
|
|
186
160
|
const payload = new Float32Array(uploads[0]?.buffer ?? new ArrayBuffer(0));
|
|
187
|
-
expect(
|
|
161
|
+
expect(payload.length).toBe(240);
|
|
162
|
+
expect(payload[196]).toBeCloseTo(Math.sqrt(3));
|
|
163
|
+
expect(payload[212]).toBe(3);
|
|
164
|
+
expect(payload[228]).toBeCloseTo(0.1);
|
|
165
|
+
expect(payload[229]).toBe(100);
|
|
166
|
+
expect(payload[230]).toBe(0);
|
|
188
167
|
});
|
|
189
168
|
|
|
190
169
|
it('seeds previousWorld on the first frame and uses the submitted snapshot later', () => {
|
|
@@ -18,6 +18,7 @@ import type { DirectionalShadowQuality } from '../components/directional-shadow-
|
|
|
18
18
|
import type { EnvironmentGeneration } from '../environment/generation';
|
|
19
19
|
import type { EnvironmentLifecycle } from '../environment/lifecycle';
|
|
20
20
|
import type { GpuBuffer } from '../gpu-resource';
|
|
21
|
+
import type { IblBindingInspection } from '../mesh-material-bindings';
|
|
21
22
|
import type { StandardLightingInspection } from '../pipeline/standard-lighting/inspection';
|
|
22
23
|
import type { PointShadowInspection } from '../point-shadow-inspection';
|
|
23
24
|
import type { TemporalGpuState } from '../temporal/gpu';
|
|
@@ -138,6 +139,18 @@ export interface CurrentGraphTarget {
|
|
|
138
139
|
const textureIdentities = new WeakMap<object, number>();
|
|
139
140
|
let nextTextureIdentity = 1;
|
|
140
141
|
|
|
142
|
+
const opaqueResourceIdentities = new WeakMap<object, number>();
|
|
143
|
+
let nextOpaqueResourceIdentity = 1;
|
|
144
|
+
|
|
145
|
+
/** Stable detached identity for a renderer-owned opaque resource handle. */
|
|
146
|
+
export function getOpaqueResourceIdentity(resource: object): number {
|
|
147
|
+
const existing = opaqueResourceIdentities.get(resource);
|
|
148
|
+
if (existing !== undefined) return existing;
|
|
149
|
+
const identity = nextOpaqueResourceIdentity++;
|
|
150
|
+
opaqueResourceIdentities.set(resource, identity);
|
|
151
|
+
return identity;
|
|
152
|
+
}
|
|
153
|
+
|
|
141
154
|
/**
|
|
142
155
|
* Project an opaque RHI texture handle to a stable diagnostic identity.
|
|
143
156
|
* RHI handles intentionally expose no serializable fields; object identity is
|
|
@@ -289,6 +302,8 @@ export function makeZeroCameraFallbackSnapshot(): CameraSnapshot {
|
|
|
289
302
|
*/
|
|
290
303
|
export interface RenderFrameState {
|
|
291
304
|
frameNumber: number;
|
|
305
|
+
/** Final-submit Standard IBL binding-chain receipt for this frame. */
|
|
306
|
+
iblBindingInspection?: IblBindingInspection;
|
|
292
307
|
/** Demand for the frame-local Standard fallback MRT. */
|
|
293
308
|
reflectionFallbackDemand?: boolean;
|
|
294
309
|
/** Monotonic compiled-graph identity used by diagnostic readbacks. */
|
|
@@ -75,9 +75,19 @@ export function acquireSwapChainTarget(
|
|
|
75
75
|
|
|
76
76
|
let currentTextureResult = canvasContext.getCurrentTexture();
|
|
77
77
|
if (!currentTextureResult.ok) {
|
|
78
|
+
const configuredDevice = internals.resolveSurfaceDevice?.(internals.device);
|
|
79
|
+
if (configuredDevice !== undefined && !configuredDevice.ok) {
|
|
80
|
+
internals.errorRegistry.fire(configuredDevice.error);
|
|
81
|
+
internals.healthRegistry.fire({
|
|
82
|
+
reason: 'internal-fault',
|
|
83
|
+
detail: { message: 'surface device resolution failed; current frame skipped' },
|
|
84
|
+
recoverable: true,
|
|
85
|
+
});
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
78
88
|
const configured = configureSurface(
|
|
79
89
|
canvasContext,
|
|
80
|
-
internals.device,
|
|
90
|
+
configuredDevice?.value ?? internals.device,
|
|
81
91
|
pipelineState.format,
|
|
82
92
|
pipelineState.colorAttachmentFormat,
|
|
83
93
|
);
|
package/src/record/frame.ts
CHANGED
|
@@ -2484,7 +2484,7 @@ function prepareDirectionalShadowCache(
|
|
|
2484
2484
|
*
|
|
2485
2485
|
* @internal
|
|
2486
2486
|
*/
|
|
2487
|
-
function validateRenderables(
|
|
2487
|
+
export function validateRenderables(
|
|
2488
2488
|
internals: RenderSystemInternals,
|
|
2489
2489
|
world: World,
|
|
2490
2490
|
// feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): the full
|
|
@@ -2714,8 +2714,12 @@ function prepareMorphMesh(
|
|
|
2714
2714
|
frameState.morphBuffers.delete(cacheKey);
|
|
2715
2715
|
return undefined;
|
|
2716
2716
|
}
|
|
2717
|
+
const buffer = new GpuBuffer(internals.device, created.value);
|
|
2718
|
+
internals.deviceScope._adopt('buffer', buffer, (owned) => {
|
|
2719
|
+
if (!owned.isDestroyed) owned.destroy();
|
|
2720
|
+
});
|
|
2717
2721
|
cached = {
|
|
2718
|
-
buffer
|
|
2722
|
+
buffer,
|
|
2719
2723
|
byteLength: vertices.byteLength,
|
|
2720
2724
|
};
|
|
2721
2725
|
frameState.morphBuffers.set(cacheKey, cached);
|
package/src/record/helpers.ts
CHANGED