@forgeax/engine-render 0.1.34 → 0.1.36
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 +69 -3
- package/dist/assembly/device-feature-admission.d.ts +2 -2
- package/dist/assembly/host-contract.d.ts +3 -2
- package/dist/assembly/host-contract.d.ts.map +1 -1
- package/dist/assembly/recovery/renderer-recover.d.ts.map +1 -1
- package/dist/assembly/render-target-host.d.ts +5 -0
- package/dist/assembly/render-target-host.d.ts.map +1 -1
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts +1 -0
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/assembly/webgpu-vertex-layouts.d.ts.map +1 -1
- package/dist/authoring.mjs +2 -4
- package/dist/authoring.mjs.map +1 -1
- package/dist/chunk-2OZAS3WU.mjs +3489 -0
- package/dist/chunk-2OZAS3WU.mjs.map +1 -0
- package/dist/{chunk-UI5SFODX.mjs → chunk-BZQ4CDRF.mjs} +2338 -1991
- package/dist/chunk-BZQ4CDRF.mjs.map +1 -0
- package/dist/{chunk-MKDIT2EM.mjs → chunk-CJOV2XLO.mjs} +216 -261
- package/dist/chunk-CJOV2XLO.mjs.map +1 -0
- package/dist/chunk-D6WSBXJ2.mjs +1842 -0
- package/dist/chunk-D6WSBXJ2.mjs.map +1 -0
- package/dist/{chunk-HEB564WF.mjs → chunk-DRF6OULX.mjs} +4 -4
- package/dist/{chunk-HEB564WF.mjs.map → chunk-DRF6OULX.mjs.map} +1 -1
- package/dist/{chunk-OCDLEI6I.mjs → chunk-OB5WAFYH.mjs} +117 -5
- package/dist/chunk-OB5WAFYH.mjs.map +1 -0
- package/dist/{chunk-ODZWR6JQ.mjs → chunk-OEYKKMXG.mjs} +2042 -27
- package/dist/chunk-OEYKKMXG.mjs.map +1 -0
- package/dist/chunk-RIPAEGI6.mjs +48 -0
- package/dist/chunk-RIPAEGI6.mjs.map +1 -0
- package/dist/{chunk-ELJFJ2BU.mjs → chunk-X2KA6WHM.mjs} +29 -4
- package/dist/chunk-X2KA6WHM.mjs.map +1 -0
- package/dist/{chunk-BLXGONR2.mjs → chunk-XSFB7XXQ.mjs} +51 -4
- package/dist/chunk-XSFB7XXQ.mjs.map +1 -0
- package/dist/cloud/frame-context.d.ts +2 -2
- package/dist/cloud/frame-context.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +936 -2140
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/device-scope.d.ts.map +1 -1
- package/dist/device/gpu-residency.d.ts +19 -19
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/errors/render.d.ts +3 -2
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/material-context.d.ts +7 -0
- package/dist/extract/material-context.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/particle-mesh-layout.d.ts +19 -0
- package/dist/features/particle-mesh-layout.d.ts.map +1 -0
- package/dist/features/plan.d.ts +2 -0
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/types.d.ts +8 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/glyph-text-layout-system.d.ts +1 -1
- package/dist/glyph-text-layout-system.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +1 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-ownership.d.ts +3 -3
- package/dist/gpu-driven/shadow-ownership.d.ts.map +1 -1
- package/dist/gpu-driven/shadow-views.d.ts +1 -1
- package/dist/gpu-driven/shadow-views.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +1 -1
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/ibl/frame-resources.d.ts.map +1 -1
- package/dist/ibl/skylight-bind-group.d.ts +4 -4
- package/dist/ibl/skylight-bind-group.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +542 -9
- package/dist/index.mjs.map +1 -1
- package/dist/instances.d.ts +3 -4
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +7 -8
- package/dist/internal.mjs.map +1 -1
- package/dist/pbr-pipeline.d.ts +3 -3
- package/dist/pbr-pipeline.d.ts.map +1 -1
- package/dist/pipeline/standard-output/lut-gpu.d.ts +2 -2
- package/dist/pipeline/standard-output/lut-gpu.d.ts.map +1 -1
- package/dist/pipeline/standard-output/resources.d.ts +2 -2
- package/dist/pipeline/standard-output/resources.d.ts.map +1 -1
- package/dist/publication/contract.d.ts +77 -0
- package/dist/publication/contract.d.ts.map +1 -0
- package/dist/publication/dependencies.d.ts +7 -0
- package/dist/publication/dependencies.d.ts.map +1 -0
- package/dist/publication/prepare-geometry.d.ts +9 -0
- package/dist/publication/prepare-geometry.d.ts.map +1 -0
- package/dist/publication/programs.d.ts +5 -0
- package/dist/publication/programs.d.ts.map +1 -0
- package/dist/publication/publisher.d.ts +34 -0
- package/dist/publication/publisher.d.ts.map +1 -0
- package/dist/publication/receiver.d.ts +29 -0
- package/dist/publication/receiver.d.ts.map +1 -0
- package/dist/publication/resource-scope.d.ts +18 -0
- package/dist/publication/resource-scope.d.ts.map +1 -0
- package/dist/publication/targets.d.ts +33 -0
- package/dist/publication/targets.d.ts.map +1 -0
- package/dist/publication/video.d.ts +5 -0
- package/dist/publication/video.d.ts.map +1 -0
- package/dist/record/fold-buckets.d.ts +3 -3
- package/dist/record/fold-buckets.d.ts.map +1 -1
- package/dist/record/frame-snapshot.d.ts +6 -6
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame.d.ts +5 -4
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/helpers.d.ts +2 -2
- package/dist/record/helpers.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts +2 -2
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/main-pass-material.d.ts +7 -9
- package/dist/record/main-pass-material.d.ts.map +1 -1
- package/dist/record/main-pass-sprite-draws.d.ts +3 -16
- package/dist/record/main-pass-sprite-draws.d.ts.map +1 -1
- package/dist/record/main-pass.d.ts.map +1 -1
- package/dist/record/prepared-material-bindings.d.ts +2 -3
- package/dist/record/prepared-material-bindings.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +4 -2
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts +2 -2
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/sprite-instance-buffer.d.ts +31 -0
- package/dist/record/sprite-instance-buffer.d.ts.map +1 -0
- package/dist/record/target-capture-graph.d.ts +19 -0
- package/dist/record/target-capture-graph.d.ts.map +1 -0
- package/dist/record/typed-frame-graph.d.ts +2 -13
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/record/view-ubo.d.ts.map +1 -1
- package/dist/record/volume-params.d.ts +2 -2
- package/dist/record/volume-params.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts +5 -12
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/render-contract.d.ts +4 -1
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +2 -0
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system-presentation.d.ts +2 -2
- package/dist/render-system-presentation.d.ts.map +1 -1
- package/dist/render-system-projections.d.ts +3 -2
- package/dist/render-system-projections.d.ts.map +1 -1
- package/dist/render-system-residency.d.ts +2 -2
- package/dist/render-system-residency.d.ts.map +1 -1
- package/dist/render-system.d.ts +3 -7
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +8 -4
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/scene/render-source.d.ts +42 -0
- package/dist/scene/render-source.d.ts.map +1 -0
- package/dist/scene/source-systems.d.ts +6 -0
- package/dist/scene/source-systems.d.ts.map +1 -0
- package/dist/systems/skin-palette-allocator.d.ts.map +1 -1
- package/dist/systems/skin-palette-types.d.ts +10 -0
- package/dist/systems/skin-palette-types.d.ts.map +1 -1
- package/dist/systems/transparent-dispatch.d.ts +2 -2
- package/dist/systems/transparent-dispatch.d.ts.map +1 -1
- package/dist/systems/transparent-sort-config.d.ts +2 -1
- package/dist/systems/transparent-sort-config.d.ts.map +1 -1
- package/dist/temporal/index.mjs +6 -7
- package/dist/typed-shadow-passes.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/auto-exposure-transaction.integration.test.ts +4 -9
- package/src/__tests__/device-feature-admission.unit.test.ts +3 -2
- package/src/__tests__/device-scope-lifecycle.unit.test.ts +26 -0
- package/src/__tests__/gpu-driven-pbr.dawn.test.ts +3 -4
- package/src/__tests__/gpu-driven-production.integration.test.ts +18 -1
- package/src/__tests__/gpu-driven-shadow-views.unit.test.ts +23 -0
- package/src/__tests__/ktx2-basis-gpu-consumer.dawn.test.ts +4 -1
- package/src/__tests__/main-pass-material-medium-selection.unit.test.ts +0 -1
- package/src/__tests__/material-vertex-texture.browser.test.ts +7 -0
- package/src/__tests__/material-vertex-texture.dawn.test.ts +7 -0
- package/src/__tests__/material-vertex-texture.fixture.ts +31 -0
- package/src/__tests__/materials-standard-contract.unit.test.ts +0 -1
- package/src/__tests__/physical-clearcoat.integration.test.ts +23 -23
- package/src/__tests__/physical-material-budget.unit.test.ts +40 -0
- package/src/__tests__/recovery-candidate-prepare.contract.test.ts +5 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +1 -0
- package/src/__tests__/renderer-factory-material-contract.unit.test.ts +107 -5
- package/src/__tests__/runtime-material-program.integration.test.ts +79 -0
- package/src/__tests__/vertex-layouts-ssot.unit.test.ts +23 -3
- package/src/assembly/device-feature-admission.ts +2 -2
- package/src/assembly/host-contract.ts +5 -2
- package/src/assembly/recovery/renderer-recover.ts +9 -0
- package/src/assembly/render-target-host.ts +57 -47
- package/src/assembly/renderer-facade.ts +3 -1
- package/src/assembly/webgpu-ready.ts +1 -1
- package/src/assembly/webgpu-renderer.ts +101 -70
- package/src/assembly/webgpu-vertex-layouts.ts +2 -9
- package/src/cloud/frame-context.ts +2 -2
- package/src/device/device-scope.ts +3 -0
- package/src/device/gpu-residency.ts +26 -25
- package/src/errors/render.ts +3 -1
- package/src/extract/material-context.ts +21 -0
- package/src/features/host.ts +13 -5
- package/src/features/particle-mesh-layout.ts +25 -0
- package/src/features/plan.ts +2 -0
- package/src/features/types.ts +8 -0
- package/src/glyph-text-layout-system.ts +2 -2
- package/src/gpu-driven/production-raster.ts +9 -3
- package/src/gpu-driven/shadow-ownership.ts +3 -3
- package/src/gpu-driven/shadow-views.ts +27 -2
- package/src/gpu-driven/view-gpu.ts +5 -0
- package/src/ibl/frame-resources.ts +0 -1
- package/src/ibl/skylight-bind-group.ts +13 -25
- package/src/index.ts +9 -0
- package/src/instances.ts +9 -6
- package/src/pbr-pipeline.ts +15 -18
- package/src/pipeline/standard-output/lut-gpu.ts +3 -3
- package/src/pipeline/standard-output/resources.ts +2 -2
- package/src/publication/__tests__/derived-geometry.integration.test.ts +177 -0
- package/src/publication/__tests__/features.integration.test.ts +173 -0
- package/src/publication/__tests__/programs.unit.test.ts +25 -0
- package/src/publication/__tests__/publication.integration.test.ts +415 -0
- package/src/publication/__tests__/targets.integration.test.ts +120 -0
- package/src/publication/__tests__/video.browser.test.ts +50 -0
- package/src/publication/contract.ts +104 -0
- package/src/publication/dependencies.ts +54 -0
- package/src/publication/prepare-geometry.ts +94 -0
- package/src/publication/programs.ts +45 -0
- package/src/publication/publisher.ts +645 -0
- package/src/publication/receiver.ts +297 -0
- package/src/publication/resource-scope.ts +31 -0
- package/src/publication/targets.ts +182 -0
- package/src/publication/video.ts +33 -0
- package/src/record/__tests__/material-bg-cache.unit.test.ts +15 -1
- package/src/record/__tests__/prepared-runtime-material.integration.test.ts +134 -0
- package/src/record/__tests__/sprite-material-abi.unit.test.ts +8 -10
- package/src/record/__tests__/video-texture-view.unit.test.ts +9 -9
- package/src/record/fold-buckets.ts +8 -5
- package/src/record/frame-snapshot.ts +6 -6
- package/src/record/frame.ts +87 -28
- package/src/record/helpers.ts +2 -2
- package/src/record/main-pass-geometry.ts +21 -2
- package/src/record/main-pass-material.ts +37 -33
- package/src/record/main-pass-sprite-draws.ts +6 -152
- package/src/record/main-pass.ts +8 -7
- package/src/record/prepared-material-bindings.ts +16 -37
- package/src/record/render-context.ts +4 -2
- package/src/record/shadow-pass.ts +65 -7
- package/src/record/sprite-instance-buffer.ts +155 -0
- package/src/record/target-capture-graph.ts +258 -0
- package/src/record/typed-frame-graph.ts +11 -207
- package/src/record/view-ubo.ts +1 -1
- package/src/record/volume-params.ts +6 -6
- package/src/recovery/render-system-candidate.ts +10 -18
- package/src/render-contract.ts +6 -1
- package/src/render-system-extract-tail.ts +48 -23
- package/src/render-system-extract.ts +11 -2
- package/src/render-system-presentation.ts +2 -2
- package/src/render-system-projections.ts +3 -2
- package/src/render-system-residency.ts +2 -2
- package/src/render-system.ts +82 -54
- package/src/scene/render-scene.ts +110 -100
- package/src/scene/render-source.ts +90 -0
- package/src/scene/source-systems.ts +33 -0
- package/src/systems/skin-palette-allocator.ts +1 -0
- package/src/systems/skin-palette-types.ts +11 -0
- package/src/systems/transparent-dispatch.ts +2 -2
- package/src/systems/transparent-sort-config.ts +3 -1
- package/src/typed-shadow-passes.ts +36 -5
- package/dist/chunk-BLXGONR2.mjs.map +0 -1
- package/dist/chunk-ELJFJ2BU.mjs.map +0 -1
- package/dist/chunk-ILQ5MEZF.mjs +0 -32
- package/dist/chunk-ILQ5MEZF.mjs.map +0 -1
- package/dist/chunk-MKDIT2EM.mjs.map +0 -1
- package/dist/chunk-OCDLEI6I.mjs.map +0 -1
- package/dist/chunk-ODZWR6JQ.mjs.map +0 -1
- package/dist/chunk-RXZ3RGOU.mjs +0 -53
- package/dist/chunk-RXZ3RGOU.mjs.map +0 -1
- package/dist/chunk-SHFUFJ5N.mjs +0 -1781
- package/dist/chunk-SHFUFJ5N.mjs.map +0 -1
- package/dist/chunk-UI5SFODX.mjs.map +0 -1
- package/dist/chunk-UINHLCZA.mjs +0 -2389
- package/dist/chunk-UINHLCZA.mjs.map +0 -1
- package/dist/chunk-WEX45HKH.mjs +0 -1524
- package/dist/chunk-WEX45HKH.mjs.map +0 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { AssetRegistry, RuntimeMaterialValue } from '@forgeax/engine-assets-runtime';
|
|
2
|
+
import { World } from '@forgeax/engine-ecs';
|
|
3
|
+
import type { BindGroupLayout } from '@forgeax/engine-rhi';
|
|
4
|
+
import { ShaderRegistry } from '@forgeax/engine-shader';
|
|
5
|
+
import { type MaterialAsset, ok } from '@forgeax/engine-types';
|
|
6
|
+
import { expect, it } from 'vitest';
|
|
7
|
+
import { renderPublicationTransfers } from '../../publication/contract';
|
|
8
|
+
import { createRenderPublisher } from '../../publication/publisher';
|
|
9
|
+
import { RenderPublicationReceiver } from '../../publication/receiver';
|
|
10
|
+
import { preparedMaterialBindings } from '../prepared-material-bindings';
|
|
11
|
+
import type { RenderSystemInternals } from '../render-context';
|
|
12
|
+
|
|
13
|
+
it('uploads accepted values on World and published-reader paths without mutating the base', () => {
|
|
14
|
+
const world = new World();
|
|
15
|
+
const assets = new AssetRegistry(new ShaderRegistry({ manifestUrl: undefined }));
|
|
16
|
+
const guid = '12345678-1234-1234-1234-123456789abc';
|
|
17
|
+
const base: MaterialAsset = { kind: 'material', values: { roughness: 0.8 } };
|
|
18
|
+
assets.catalog(guid, base);
|
|
19
|
+
const handle = world.internSharedRef('MaterialAsset', base);
|
|
20
|
+
const content = world
|
|
21
|
+
.spawn({
|
|
22
|
+
component: RuntimeMaterialValue,
|
|
23
|
+
data: { asset: handle, parameter: 'roughness', kind: 0, value: [0.25] },
|
|
24
|
+
})
|
|
25
|
+
.unwrap();
|
|
26
|
+
const uploads: number[] = [];
|
|
27
|
+
let destroys = 0;
|
|
28
|
+
const runtime = {
|
|
29
|
+
assets,
|
|
30
|
+
getParamSchema: () => [{ name: 'roughness', type: 'f32' }],
|
|
31
|
+
getPipelineState: () => ({
|
|
32
|
+
defaultSampler: {},
|
|
33
|
+
skylightFallback: {
|
|
34
|
+
irradianceView: {},
|
|
35
|
+
prefilterView: {},
|
|
36
|
+
brdfLutView: {},
|
|
37
|
+
sampler: {},
|
|
38
|
+
intensityBuffer: {},
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
device: {
|
|
42
|
+
caps: { storageBuffer: false },
|
|
43
|
+
limits: { maxSampledTexturesPerShaderStage: 16 },
|
|
44
|
+
createBuffer: () => ok({}),
|
|
45
|
+
destroyBuffer: () => {
|
|
46
|
+
destroys++;
|
|
47
|
+
return ok(undefined);
|
|
48
|
+
},
|
|
49
|
+
createBindGroup: () => ok({}),
|
|
50
|
+
queue: {
|
|
51
|
+
writeBuffer: (_buffer: unknown, _offset: number, bytes: Uint8Array) => {
|
|
52
|
+
uploads.push(new Float32Array(bytes.buffer, bytes.byteOffset, 1)[0] ?? NaN);
|
|
53
|
+
return ok(undefined);
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
} as unknown as RenderSystemInternals;
|
|
58
|
+
const identity = { source: 'material-values', epoch: 1 };
|
|
59
|
+
const publisher = createRenderPublisher(world, assets, identity, undefined, [
|
|
60
|
+
{
|
|
61
|
+
identity: 'material-consumer',
|
|
62
|
+
extract: () => ok({}),
|
|
63
|
+
assetDependencies: () => [guid],
|
|
64
|
+
plan: () => ok({ resources: [], passes: [] }),
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
const receiver = new RenderPublicationReceiver(identity);
|
|
68
|
+
const publish = () => {
|
|
69
|
+
const candidate = publisher.prepare(0).unwrap();
|
|
70
|
+
const packet = structuredClone(candidate.packet);
|
|
71
|
+
candidate.accept();
|
|
72
|
+
const resources = receiver.accept(packet).unwrap().resources;
|
|
73
|
+
publisher.recycle(packet.revision, renderPublicationTransfers(packet)).unwrap();
|
|
74
|
+
return resources;
|
|
75
|
+
};
|
|
76
|
+
const reader = publish();
|
|
77
|
+
for (const source of [world, reader]) {
|
|
78
|
+
const bindings = preparedMaterialBindings(
|
|
79
|
+
runtime,
|
|
80
|
+
[source],
|
|
81
|
+
'shader',
|
|
82
|
+
0,
|
|
83
|
+
guid,
|
|
84
|
+
{} as BindGroupLayout,
|
|
85
|
+
).unwrap();
|
|
86
|
+
if ('release' in bindings) bindings.release?.();
|
|
87
|
+
}
|
|
88
|
+
expect(uploads).toEqual([0.25, 0.25]);
|
|
89
|
+
expect(base.values?.roughness).toBe(0.8);
|
|
90
|
+
const other = new World();
|
|
91
|
+
const otherHandle = other.internSharedRef('MaterialAsset', base);
|
|
92
|
+
const otherContent = other
|
|
93
|
+
.spawn({
|
|
94
|
+
component: RuntimeMaterialValue,
|
|
95
|
+
data: {
|
|
96
|
+
asset: otherHandle,
|
|
97
|
+
parameter: 'roughness',
|
|
98
|
+
kind: 0,
|
|
99
|
+
value: [0.6],
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
.unwrap();
|
|
103
|
+
for (const [owner, expected] of [
|
|
104
|
+
[1, 0.6],
|
|
105
|
+
[0, 0.25],
|
|
106
|
+
] as const) {
|
|
107
|
+
const bindings = preparedMaterialBindings(
|
|
108
|
+
runtime,
|
|
109
|
+
[world, other],
|
|
110
|
+
'shader',
|
|
111
|
+
owner,
|
|
112
|
+
guid,
|
|
113
|
+
{} as BindGroupLayout,
|
|
114
|
+
).unwrap();
|
|
115
|
+
expect(uploads.at(-1)).toBeCloseTo(expected);
|
|
116
|
+
if ('release' in bindings) bindings.release?.();
|
|
117
|
+
}
|
|
118
|
+
other.despawn(otherContent).unwrap();
|
|
119
|
+
world.despawn(content).unwrap();
|
|
120
|
+
for (const source of [world, publish()]) {
|
|
121
|
+
const restored = preparedMaterialBindings(
|
|
122
|
+
runtime,
|
|
123
|
+
[source],
|
|
124
|
+
'shader',
|
|
125
|
+
0,
|
|
126
|
+
guid,
|
|
127
|
+
{} as BindGroupLayout,
|
|
128
|
+
).unwrap();
|
|
129
|
+
expect(uploads.at(-1)).toBeCloseTo(0.8);
|
|
130
|
+
if ('release' in restored) restored.release?.();
|
|
131
|
+
}
|
|
132
|
+
expect(destroys).toBe(6);
|
|
133
|
+
publisher.dispose();
|
|
134
|
+
});
|
|
@@ -72,22 +72,21 @@ describe('sprite Standard material ABI', () => {
|
|
|
72
72
|
prefilterView: {} as TextureView,
|
|
73
73
|
prefilterSampler: sampler,
|
|
74
74
|
brdfLutView: {} as TextureView,
|
|
75
|
-
brdfLutSampler: sampler,
|
|
76
75
|
intensityBuffer: {} as Buffer,
|
|
77
76
|
});
|
|
78
77
|
expect(merged[15]).toMatchObject({
|
|
79
78
|
binding: 15,
|
|
80
79
|
resource: { kind: 'textureView' },
|
|
81
80
|
});
|
|
82
|
-
expect(merged[
|
|
83
|
-
binding:
|
|
81
|
+
expect(merged[21]).toMatchObject({
|
|
82
|
+
binding: 21,
|
|
84
83
|
resource: { kind: 'sampler', value: sampler },
|
|
85
84
|
});
|
|
86
|
-
expect(merged[
|
|
87
|
-
binding:
|
|
85
|
+
expect(merged[22]).toMatchObject({
|
|
86
|
+
binding: 22,
|
|
88
87
|
resource: { kind: 'textureView' },
|
|
89
88
|
});
|
|
90
|
-
expect(merged).toHaveLength(
|
|
89
|
+
expect(merged).toHaveLength(24);
|
|
91
90
|
expect(merged.find((entry) => entry.binding === 47)?.resource.kind).toBe('textureView');
|
|
92
91
|
|
|
93
92
|
const complete = [
|
|
@@ -97,7 +96,7 @@ describe('sprite Standard material ABI', () => {
|
|
|
97
96
|
resource: { kind: 'buffer' as const, value: { buffer: {} as Buffer } },
|
|
98
97
|
},
|
|
99
98
|
];
|
|
100
|
-
expect(complete).toHaveLength(
|
|
99
|
+
expect(complete).toHaveLength(25);
|
|
101
100
|
expect(complete.find((entry) => entry.binding === 46)).toMatchObject({
|
|
102
101
|
binding: 46,
|
|
103
102
|
resource: { kind: 'buffer' },
|
|
@@ -112,13 +111,12 @@ describe('sprite Standard material ABI', () => {
|
|
|
112
111
|
prefilterView: {} as TextureView,
|
|
113
112
|
prefilterSampler: sampler,
|
|
114
113
|
brdfLutView: {} as TextureView,
|
|
115
|
-
brdfLutSampler: sampler,
|
|
116
114
|
intensityBuffer: {} as Buffer,
|
|
117
115
|
},
|
|
118
116
|
{ sampler, backdropView: backdrop },
|
|
119
117
|
);
|
|
120
|
-
expect(active[
|
|
121
|
-
binding:
|
|
118
|
+
expect(active[22]).toMatchObject({
|
|
119
|
+
binding: 22,
|
|
122
120
|
resource: { kind: 'textureView', value: backdrop },
|
|
123
121
|
});
|
|
124
122
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type DynamicTextureDevice, DynamicTextureStore } from '@forgeax/engine-assets-runtime';
|
|
2
2
|
import { World } from '@forgeax/engine-ecs';
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
type
|
|
4
|
+
VIDEO_SOURCE_PROVIDER_KEY,
|
|
5
|
+
type VideoSourceProvider,
|
|
6
6
|
} from '@forgeax/engine-graphics-extras';
|
|
7
7
|
import type { Result, RhiError, Texture, TextureView } from '@forgeax/engine-rhi';
|
|
8
8
|
import { ok } from '@forgeax/engine-rhi';
|
|
@@ -53,16 +53,16 @@ describe('videoTextureView provider-loss recovery', () => {
|
|
|
53
53
|
const store = makeStore(probe);
|
|
54
54
|
const errors: { readonly code: string }[] = [];
|
|
55
55
|
const runtime = runtimeWithErrors((error) => errors.push(error));
|
|
56
|
-
const provider:
|
|
57
|
-
|
|
56
|
+
const provider: VideoSourceProvider = {
|
|
57
|
+
getSource: vi.fn(() => VIDEO),
|
|
58
58
|
};
|
|
59
|
-
world.insertResource(
|
|
59
|
+
world.insertResource(VIDEO_SOURCE_PROVIDER_KEY, provider);
|
|
60
60
|
|
|
61
61
|
const lkg = videoTextureView(world, store, runtime, ENTITY, CLIP, false);
|
|
62
62
|
expect(lkg).toBeDefined();
|
|
63
63
|
expect(probe.copies).toBe(1);
|
|
64
64
|
|
|
65
|
-
world.removeResource(
|
|
65
|
+
world.removeResource(VIDEO_SOURCE_PROVIDER_KEY);
|
|
66
66
|
expect(videoTextureView(world, store, runtime, ENTITY, CLIP, false)).toBe(lkg);
|
|
67
67
|
expect(videoTextureView(world, store, runtime, ENTITY, CLIP, false)).toBe(lkg);
|
|
68
68
|
expect(videoTextureView(world, store, runtime, ENTITY, CLIP, false)).toBe(lkg);
|
|
@@ -70,19 +70,19 @@ describe('videoTextureView provider-loss recovery', () => {
|
|
|
70
70
|
expect(errors[0]?.code).toBe('video-upload-unsupported');
|
|
71
71
|
expect(probe.copies).toBe(1);
|
|
72
72
|
|
|
73
|
-
world.insertResource(
|
|
73
|
+
world.insertResource(VIDEO_SOURCE_PROVIDER_KEY, provider);
|
|
74
74
|
expect(videoTextureView(world, store, runtime, ENTITY, CLIP, false)).toBe(lkg);
|
|
75
75
|
expect(videoTextureView(world, store, runtime, ENTITY, CLIP, false)).toBe(lkg);
|
|
76
76
|
expect(errors).toHaveLength(1);
|
|
77
77
|
expect(probe.copies).toBe(3);
|
|
78
78
|
|
|
79
|
-
world.removeResource(
|
|
79
|
+
world.removeResource(VIDEO_SOURCE_PROVIDER_KEY);
|
|
80
80
|
expect(videoTextureView(world, store, runtime, ENTITY, CLIP, false)).toBe(lkg);
|
|
81
81
|
expect(videoTextureView(world, store, runtime, ENTITY, CLIP, false)).toBe(lkg);
|
|
82
82
|
expect(errors).toHaveLength(2);
|
|
83
83
|
expect(probe.copies).toBe(3);
|
|
84
84
|
|
|
85
|
-
world.insertResource(
|
|
85
|
+
world.insertResource(VIDEO_SOURCE_PROVIDER_KEY, provider);
|
|
86
86
|
expect(videoTextureView(world, store, runtime, ENTITY, CLIP, false)).toBe(lkg);
|
|
87
87
|
expect(errors).toHaveLength(2);
|
|
88
88
|
expect(probe.copies).toBe(4);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { HANDLE_NINESLICE_QUAD, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
2
|
-
import type { World } from '@forgeax/engine-ecs';
|
|
3
2
|
import { deriveVertexCount } from '@forgeax/engine-geometry';
|
|
4
3
|
import { RhiError } from '@forgeax/engine-rhi';
|
|
5
4
|
import type { MaterialRenderState, MeshAsset } from '@forgeax/engine-types';
|
|
@@ -9,6 +8,8 @@ import { materialBindingKey } from '../gpu-driven/material-bindings';
|
|
|
9
8
|
import { GpuBuffer } from '../gpu-resource';
|
|
10
9
|
import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_VERTEX } from '../gpu-usage';
|
|
11
10
|
import { disposeTransientInstanceBuffers } from '../instance-buffer-cache';
|
|
11
|
+
import type { RenderResourceScope } from '../publication/resource-scope';
|
|
12
|
+
import { renderAssetByGuid } from '../publication/resource-scope';
|
|
12
13
|
import type { DispatchEntry, MaterialSnapshot, RenderableSnapshot } from '../render-system-extract';
|
|
13
14
|
import {
|
|
14
15
|
getTransparentSortConfig,
|
|
@@ -151,14 +152,14 @@ function limitRowsToMeshSsboCapacity<T>(rows: readonly T[], availableSlots: numb
|
|
|
151
152
|
*/
|
|
152
153
|
export function validateRenderables(
|
|
153
154
|
internals: RenderSystemInternals,
|
|
154
|
-
world:
|
|
155
|
+
world: RenderResourceScope,
|
|
155
156
|
// feat-20260709-editor-world-partition ENGINE-fix-round2 (defect 2): the full
|
|
156
157
|
// worlds[] list. Each renderable's mesh handle is a user-tier slot in its OWN
|
|
157
158
|
// world's sharedRefs; resolving against a foreign world either misses
|
|
158
159
|
// (asset-not-registered) or resolves the wrong slot payload. `world` (the
|
|
159
160
|
// resource-owner) is retained as the fallback for renderables whose worldId
|
|
160
161
|
// is out of range (defensive; extractFrames always stamps a valid index).
|
|
161
|
-
worlds: readonly
|
|
162
|
+
worlds: readonly RenderResourceScope[],
|
|
162
163
|
pipelineState: PipelineState,
|
|
163
164
|
frameState: RenderFrameState,
|
|
164
165
|
renderables: readonly RenderableSnapshot[],
|
|
@@ -226,7 +227,9 @@ export function validateRenderables(
|
|
|
226
227
|
// frames hit the O(1) cache.
|
|
227
228
|
const meshAssetHandle = toShared<'MeshAsset'>(r.assetHandle);
|
|
228
229
|
let meshHandles = internals.gpuStore.getMeshGpuHandles(meshAssetHandle, renderableWorld);
|
|
229
|
-
const lodMeshes = r.lods?.map((lod) =>
|
|
230
|
+
const lodMeshes = r.lods?.map((lod) =>
|
|
231
|
+
renderAssetByGuid<MeshAsset>(renderableWorld, internals.assets, lod.mesh),
|
|
232
|
+
);
|
|
230
233
|
const resolvedLodMeshes =
|
|
231
234
|
lodMeshes?.every((lod): lod is MeshAsset => lod?.kind === 'mesh') === true
|
|
232
235
|
? lodMeshes
|
|
@@ -793,7 +796,7 @@ export function cleanPerFrameCaches(
|
|
|
793
796
|
* @internal
|
|
794
797
|
*/
|
|
795
798
|
export function computeFoldBuckets(
|
|
796
|
-
world:
|
|
799
|
+
world: RenderResourceScope,
|
|
797
800
|
frameState: RenderFrameState,
|
|
798
801
|
transparentDispatch: readonly DispatchEntry[],
|
|
799
802
|
renderables: readonly RenderableSnapshot[],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { RenderResourceScope } from '../publication/resource-scope';
|
|
1
2
|
// @forgeax/engine-runtime - RenderSystem record stage: frame-snapshot.
|
|
2
3
|
// Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
|
|
3
4
|
|
|
4
|
-
import type { World } from '@forgeax/engine-ecs';
|
|
5
5
|
import type { RenderReadVersion } from '@forgeax/engine-ecs/projection';
|
|
6
6
|
import { vec3 } from '@forgeax/engine-math';
|
|
7
7
|
import type {
|
|
@@ -867,8 +867,8 @@ export interface BloomFrameReceipts {
|
|
|
867
867
|
*/
|
|
868
868
|
export interface ValidatedRenderable {
|
|
869
869
|
readonly source: RenderableSnapshot;
|
|
870
|
-
/**
|
|
871
|
-
readonly world?:
|
|
870
|
+
/** RenderResourceScope that owns the renderable's asset handles and material snapshots. */
|
|
871
|
+
readonly world?: RenderResourceScope;
|
|
872
872
|
readonly mesh: MeshGpuHandles;
|
|
873
873
|
readonly renderableIndex: number;
|
|
874
874
|
readonly renderState: MaterialRenderState | undefined;
|
|
@@ -900,7 +900,7 @@ export interface ValidatedRenderable {
|
|
|
900
900
|
* @internal
|
|
901
901
|
*/
|
|
902
902
|
export interface DirectionalShadowCache {
|
|
903
|
-
readonly worlds: readonly
|
|
903
|
+
readonly worlds: readonly RenderResourceScope[];
|
|
904
904
|
readonly worldStateTokens: readonly DirectionalShadowWorldState[];
|
|
905
905
|
readonly assetCatalogEpoch: number;
|
|
906
906
|
readonly pipelineHandle: number;
|
|
@@ -916,8 +916,8 @@ export interface DirectionalShadowCache {
|
|
|
916
916
|
* ECS state that can change the contents of a directional shadow map.
|
|
917
917
|
*
|
|
918
918
|
* The ECS-owned RenderReadLease is the only render-facing mutation boundary.
|
|
919
|
-
* Its version is intentionally conservative: any published
|
|
920
|
-
* invalidates the cached atlas, while render never reaches through
|
|
919
|
+
* Its version is intentionally conservative: any published RenderResourceScope change
|
|
920
|
+
* invalidates the cached atlas, while render never reaches through RenderResourceScope for
|
|
921
921
|
* component-clock storage.
|
|
922
922
|
*/
|
|
923
923
|
export interface DirectionalShadowWorldState {
|
package/src/record/frame.ts
CHANGED
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
requiresProbeBlendRecord,
|
|
6
6
|
} from '../gpu-driven/material-artifacts';
|
|
7
7
|
import { projectMaterialBindingClasses } from '../gpu-driven/material-bindings';
|
|
8
|
+
import type { RenderResourceScope } from '../publication/resource-scope';
|
|
9
|
+
import { renderAssetByGuid, renderTime } from '../publication/resource-scope';
|
|
8
10
|
import {
|
|
9
11
|
buildDispatchPlan,
|
|
10
12
|
cleanPerFrameCaches,
|
|
@@ -16,7 +18,6 @@ import { uploadMaterialUniforms } from './material-uniforms';
|
|
|
16
18
|
// Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
|
|
17
19
|
|
|
18
20
|
import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
19
|
-
import { Time, type World } from '@forgeax/engine-ecs';
|
|
20
21
|
import type { RenderReadLease } from '@forgeax/engine-ecs/projection';
|
|
21
22
|
import { frustum, mat4, vec3 } from '@forgeax/engine-math';
|
|
22
23
|
import {
|
|
@@ -57,7 +58,7 @@ import {
|
|
|
57
58
|
resolveDepthOfFieldFrameParams,
|
|
58
59
|
} from '../features/depth-of-field/depth-of-field-params';
|
|
59
60
|
import type { GpuDrivenProduction, PreparedGpuDrivenFrame } from '../gpu-driven/production-raster';
|
|
60
|
-
import type
|
|
61
|
+
import { type ShadowViewIdentity, shadowViewIdentityKey } from '../gpu-driven/shadow-views';
|
|
61
62
|
import type { GpuDrivenLodSubmitIdentity } from '../gpu-driven/view-gpu';
|
|
62
63
|
import { GPU_TEXTURE_USAGE_COPY_SRC } from '../gpu-texture-usage';
|
|
63
64
|
import { GPU_BUFFER_USAGE_COPY_DST, GPU_BUFFER_USAGE_MAP_READ } from '../gpu-usage';
|
|
@@ -144,11 +145,14 @@ export type FrameObservationReadback = (
|
|
|
144
145
|
) => Promise<Result<Uint8Array, Error>>;
|
|
145
146
|
|
|
146
147
|
/**
|
|
147
|
-
* Fold
|
|
148
|
+
* Fold RenderResourceScope identity into the renderer-owned validation token. The GPU
|
|
148
149
|
* production owner receives only this opaque key; ECS identity and ordering
|
|
149
|
-
* stay in the record owner where the
|
|
150
|
+
* stay in the record owner where the RenderResourceScope list is authoritative.
|
|
150
151
|
*/
|
|
151
|
-
function residencyValidationCacheKey(
|
|
152
|
+
function residencyValidationCacheKey(
|
|
153
|
+
world: RenderResourceScope,
|
|
154
|
+
worlds: readonly RenderResourceScope[],
|
|
155
|
+
): string {
|
|
152
156
|
return [world.identity, ...worlds.map((candidate) => candidate.identity)].join('\u001f');
|
|
153
157
|
}
|
|
154
158
|
function renderableIdentity(snapshot: Pick<RenderableSnapshot, 'worldId' | 'entityKey'>): string {
|
|
@@ -327,7 +331,7 @@ import {
|
|
|
327
331
|
/**
|
|
328
332
|
* Resolve the GPU mesh view used by the optional production raster before its
|
|
329
333
|
* filtering pass runs. The result is keyed by the retained RenderScene slot,
|
|
330
|
-
* which preserves the
|
|
334
|
+
* which preserves the RenderResourceScope namespace already used by gpuStore. User meshes
|
|
331
335
|
* are pull-resident during record; relying only on the boot-time builtin map
|
|
332
336
|
* would make the first frame classify every imported mesh as a CPU fallback
|
|
333
337
|
* and would hide its LOD ranges from the GPU cull. Builtins are the only
|
|
@@ -336,7 +340,7 @@ import {
|
|
|
336
340
|
export function gpuDrivenMeshesForFrame(
|
|
337
341
|
internals: RenderSystemInternals,
|
|
338
342
|
pipelineState: PipelineState,
|
|
339
|
-
worlds: readonly
|
|
343
|
+
worlds: readonly RenderResourceScope[],
|
|
340
344
|
scene: PersistentGpuDrivenState | undefined,
|
|
341
345
|
): ReadonlyMap<number, MeshGpuHandles> {
|
|
342
346
|
const meshes = new Map<number, MeshGpuHandles>();
|
|
@@ -357,7 +361,7 @@ export function gpuDrivenMeshesForFrame(
|
|
|
357
361
|
const asset = resolveAssetHandle<MeshAsset>(world, sharedHandle);
|
|
358
362
|
if (asset.ok) {
|
|
359
363
|
const lodMeshes = slot.snapshot.lods?.map((lod) =>
|
|
360
|
-
|
|
364
|
+
renderAssetByGuid<MeshAsset>(world, internals.assets, lod.mesh),
|
|
361
365
|
);
|
|
362
366
|
const resolvedLodMeshes =
|
|
363
367
|
lodMeshes?.every((lod): lod is MeshAsset => lod?.kind === 'mesh') === true
|
|
@@ -382,6 +386,7 @@ export type RecordProfileRunner = <T>(phase: RenderRecordPhase, action: () => T)
|
|
|
382
386
|
export interface CubeCaptureFrameInput {
|
|
383
387
|
readonly captureDispatch?: readonly DispatchEntry[];
|
|
384
388
|
readonly snapshots: readonly CubeCameraSnapshot[];
|
|
389
|
+
readonly auxiliaryCameras: readonly CameraSnapshot[];
|
|
385
390
|
readonly scheduler: import('../capture/scheduler').CubeCaptureScheduler;
|
|
386
391
|
readonly state: CubeCaptureGraphState;
|
|
387
392
|
readonly reflectionProbes?: import('./render-context').ReflectionProbeRecordState;
|
|
@@ -437,6 +442,18 @@ function prepareCubeCapture(
|
|
|
437
442
|
(CUBE_CAPTURE_VIEW_SLOT_BASE + physicalWork.length) * VIEW_UNIFORM_SLOT_STRIDE,
|
|
438
443
|
});
|
|
439
444
|
}
|
|
445
|
+
for (const camera of input.auxiliaryCameras) {
|
|
446
|
+
if (camera.target === undefined) continue;
|
|
447
|
+
const physical = internals.getRenderTargetPhysical?.(camera.target);
|
|
448
|
+
if (physical?.descriptor.shape !== '2d') continue;
|
|
449
|
+
physicalWork.push({
|
|
450
|
+
target: camera.target,
|
|
451
|
+
faceIndex: 0,
|
|
452
|
+
physical,
|
|
453
|
+
faceCamera: camera,
|
|
454
|
+
viewBindGroupDynamicOffset: (CUBE_CAPTURE_VIEW_SLOT_BASE + 6) * VIEW_UNIFORM_SLOT_STRIDE,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
440
457
|
input.state.work = physicalWork;
|
|
441
458
|
return physicalWork;
|
|
442
459
|
}
|
|
@@ -467,8 +484,42 @@ function shadowPlanes(matrix: Float32Array): Float32Array {
|
|
|
467
484
|
return frustum.fromViewProjection(frustum.create(), matrix as never);
|
|
468
485
|
}
|
|
469
486
|
|
|
470
|
-
function
|
|
471
|
-
|
|
487
|
+
function shadowViewPlanesByView(lights: ExtractedLights): ReadonlyMap<string, Float32Array> {
|
|
488
|
+
const views = new Map<string, Float32Array>();
|
|
489
|
+
for (const [index, matrix] of (lights.lightViewProj ?? []).entries()) {
|
|
490
|
+
views.set(shadowViewIdentityKey({ kind: 'directional', index }), shadowPlanes(matrix));
|
|
491
|
+
}
|
|
492
|
+
for (const [index, point] of lights.pointShadow.entries()) {
|
|
493
|
+
for (let face = 0; face < 6; face += 1) {
|
|
494
|
+
views.set(
|
|
495
|
+
shadowViewIdentityKey({ kind: 'point', index, face }),
|
|
496
|
+
shadowPlanes(point.shadowMatrices.subarray(face * 16, face * 16 + 16)),
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
let spotIndex = 0;
|
|
501
|
+
for (const spot of lights.spot) {
|
|
502
|
+
if (spot.lightViewProj === undefined) continue;
|
|
503
|
+
views.set(
|
|
504
|
+
shadowViewIdentityKey({ kind: 'spot', index: spotIndex }),
|
|
505
|
+
shadowPlanes(spot.lightViewProj),
|
|
506
|
+
);
|
|
507
|
+
spotIndex += 1;
|
|
508
|
+
}
|
|
509
|
+
return views;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function shadowViewToken(
|
|
513
|
+
matrix: Float32Array,
|
|
514
|
+
targetSize: number | undefined,
|
|
515
|
+
graphGeneration: number,
|
|
516
|
+
): string {
|
|
517
|
+
// A newly accepted graph imports a new resource projection and must refresh
|
|
518
|
+
// the shadow compute inputs once. The graph-generation component is a
|
|
519
|
+
// one-shot resource fence; it is deliberately excluded from the GPU
|
|
520
|
+
// producer's topology signature so that this refresh cannot rebuild the
|
|
521
|
+
// graph indefinitely.
|
|
522
|
+
return `${Array.from(matrix).join(',')}|target:${targetSize}|graph:${graphGeneration}`;
|
|
472
523
|
}
|
|
473
524
|
|
|
474
525
|
function halfFloatToNumber(bits: number): number {
|
|
@@ -840,8 +891,8 @@ function resolveVolumetricFogHistoryContext(
|
|
|
840
891
|
*/
|
|
841
892
|
function prepareSpotLightProjectorFrame(
|
|
842
893
|
internals: RenderSystemInternals,
|
|
843
|
-
world:
|
|
844
|
-
worlds: readonly
|
|
894
|
+
world: RenderResourceScope,
|
|
895
|
+
worlds: readonly RenderResourceScope[],
|
|
845
896
|
lights: ExtractedLights,
|
|
846
897
|
fog: ExtractedVolumetricFog | undefined,
|
|
847
898
|
): SpotLightProjectorFrameContext | undefined {
|
|
@@ -982,15 +1033,15 @@ interface FrameClockSample {
|
|
|
982
1033
|
|
|
983
1034
|
/** Read the render sample clock from the camera's composition world once per frame. */
|
|
984
1035
|
function frameClockSample(
|
|
985
|
-
world:
|
|
986
|
-
worlds: readonly
|
|
1036
|
+
world: RenderResourceScope,
|
|
1037
|
+
worlds: readonly RenderResourceScope[],
|
|
987
1038
|
camera: CameraSnapshot,
|
|
988
1039
|
sampleTimeSeconds?: number,
|
|
989
1040
|
): FrameClockSample {
|
|
990
1041
|
const owner = worlds[camera.worldId ?? 0] ?? world;
|
|
991
1042
|
const invalidSampleTime = sampleTimeSeconds !== undefined && !Number.isFinite(sampleTimeSeconds);
|
|
992
1043
|
try {
|
|
993
|
-
const clock = owner
|
|
1044
|
+
const clock = renderTime(owner);
|
|
994
1045
|
return {
|
|
995
1046
|
deltaSeconds: Number.isFinite(clock.delta) ? Math.max(0, clock.delta) : 0,
|
|
996
1047
|
// Keep the raw host sample even when ECS accepted only a clamped
|
|
@@ -1007,7 +1058,7 @@ function frameClockSample(
|
|
|
1007
1058
|
invalidSampleTime,
|
|
1008
1059
|
};
|
|
1009
1060
|
} catch {
|
|
1010
|
-
// Test and recovery callers may provide a minimal
|
|
1061
|
+
// Test and recovery callers may provide a minimal RenderResourceScope-shaped owner. An
|
|
1011
1062
|
// absent clock is an invalid temporal pair and therefore safely resets.
|
|
1012
1063
|
return {
|
|
1013
1064
|
deltaSeconds: 0,
|
|
@@ -1372,7 +1423,7 @@ function updateVolumetricFogInspection(
|
|
|
1372
1423
|
|
|
1373
1424
|
export function recordFrame(
|
|
1374
1425
|
internals: RenderSystemInternals,
|
|
1375
|
-
world:
|
|
1426
|
+
world: RenderResourceScope,
|
|
1376
1427
|
cameras: CameraSnapshot[],
|
|
1377
1428
|
lights: ExtractedLights,
|
|
1378
1429
|
renderables: readonly RenderableSnapshot[],
|
|
@@ -1392,7 +1443,7 @@ export function recordFrame(
|
|
|
1392
1443
|
// the per-world extract stage). Optional + defaulting to `[world]` keeps the
|
|
1393
1444
|
// pre-split single-world identity path (worldId always 0) byte-for-byte and
|
|
1394
1445
|
// the existing unit-test callers (which pass a single mock world) valid.
|
|
1395
|
-
worlds: readonly
|
|
1446
|
+
worlds: readonly RenderResourceScope[] = [world],
|
|
1396
1447
|
profilePhase?: RecordProfileRunner,
|
|
1397
1448
|
gpuDriven?: {
|
|
1398
1449
|
readonly owner: GpuDrivenProduction;
|
|
@@ -1931,7 +1982,7 @@ export function recordFrame(
|
|
|
1931
1982
|
shadowViews.push({
|
|
1932
1983
|
identity: { kind: 'directional', index },
|
|
1933
1984
|
planes: shadowPlanes(matrix),
|
|
1934
|
-
cacheToken:
|
|
1985
|
+
cacheToken: shadowViewToken(matrix, graphShadowMapSize, frameState.graphGeneration),
|
|
1935
1986
|
});
|
|
1936
1987
|
}
|
|
1937
1988
|
for (const [index, point] of lights.pointShadow.entries()) {
|
|
@@ -1940,7 +1991,7 @@ export function recordFrame(
|
|
|
1940
1991
|
shadowViews.push({
|
|
1941
1992
|
identity: { kind: 'point', index, face },
|
|
1942
1993
|
planes: shadowPlanes(matrix),
|
|
1943
|
-
cacheToken:
|
|
1994
|
+
cacheToken: shadowViewToken(matrix, graphShadowMapSize, frameState.graphGeneration),
|
|
1944
1995
|
});
|
|
1945
1996
|
}
|
|
1946
1997
|
}
|
|
@@ -1950,7 +2001,11 @@ export function recordFrame(
|
|
|
1950
2001
|
shadowViews.push({
|
|
1951
2002
|
identity: { kind: 'spot', index: shadowSpotIndex },
|
|
1952
2003
|
planes: shadowPlanes(spot.lightViewProj),
|
|
1953
|
-
cacheToken:
|
|
2004
|
+
cacheToken: shadowViewToken(
|
|
2005
|
+
spot.lightViewProj,
|
|
2006
|
+
graphShadowMapSize,
|
|
2007
|
+
frameState.graphGeneration,
|
|
2008
|
+
),
|
|
1954
2009
|
});
|
|
1955
2010
|
shadowSpotIndex += 1;
|
|
1956
2011
|
}
|
|
@@ -2270,7 +2325,7 @@ export function recordFrame(
|
|
|
2270
2325
|
activeVolumetricFogSignature = resolvedHistory.signature;
|
|
2271
2326
|
const previousTime = frameState.volumetricFogHistorySignature?.worldTimeSeconds;
|
|
2272
2327
|
const currentTime = resolvedHistory.signature.worldTimeSeconds;
|
|
2273
|
-
// Adjacent UBO lanes: validity,
|
|
2328
|
+
// Adjacent UBO lanes: validity, RenderResourceScope time, elapsed time since accepted history.
|
|
2274
2329
|
const historyParams = new Float32Array([
|
|
2275
2330
|
activeVolumetricFogContext.historyValid ? 1 : 0,
|
|
2276
2331
|
currentTime ?? 0,
|
|
@@ -2710,6 +2765,7 @@ export function recordFrame(
|
|
|
2710
2765
|
gpuDrivenShadowDrawKeysByView: rasterGpuDriven.shadowDrawKeysByView,
|
|
2711
2766
|
}),
|
|
2712
2767
|
}),
|
|
2768
|
+
shadowViewPlanesByView: shadowViewPlanesByView(lights),
|
|
2713
2769
|
...(frameGpuDriven?.scene === undefined
|
|
2714
2770
|
? {}
|
|
2715
2771
|
: { gpuDrivenSceneMaterialBuffer: frameGpuDriven.scene.scene.materialBuffer }),
|
|
@@ -2842,7 +2898,7 @@ export function recordFrame(
|
|
|
2842
2898
|
// the picture authority: an identity frame publishes an identity
|
|
2843
2899
|
// mapping, while a failed enable/disable candidate retains the previous
|
|
2844
2900
|
// mapping together with its old picture. Never pair an LKG graph with
|
|
2845
|
-
// the live
|
|
2901
|
+
// the live RenderResourceScope's newer camera component.
|
|
2846
2902
|
// The exact mapping used to populate the accepted graph's UBO is the
|
|
2847
2903
|
// only mapping promoted with its receipt. This keeps an identity frame,
|
|
2848
2904
|
// a retained barrel LKG and a normal nonzero frame on one context.
|
|
@@ -2855,7 +2911,10 @@ export function recordFrame(
|
|
|
2855
2911
|
// prior tick into the next topology.
|
|
2856
2912
|
acknowledgeRenderFeatureSubmission(getRenderFeatureGraphState(internals).plans);
|
|
2857
2913
|
}
|
|
2858
|
-
if (
|
|
2914
|
+
if (
|
|
2915
|
+
cubeCapture !== undefined &&
|
|
2916
|
+
cubeCaptureWork.some((work) => work.physical.descriptor.shape === 'cube')
|
|
2917
|
+
) {
|
|
2859
2918
|
const completed = cubeCapture.scheduler.completeSubmission(submitted, completion);
|
|
2860
2919
|
if (!completed.ok) internals.errorRegistry.fire(completed.error);
|
|
2861
2920
|
}
|
|
@@ -2980,7 +3039,7 @@ function readWorldState(lease: RenderReadLease): DirectionalShadowWorldState {
|
|
|
2980
3039
|
|
|
2981
3040
|
function sameWorldState(
|
|
2982
3041
|
cached: DirectionalShadowCache,
|
|
2983
|
-
worlds: readonly
|
|
3042
|
+
worlds: readonly RenderResourceScope[],
|
|
2984
3043
|
leases: readonly RenderReadLease[] | undefined,
|
|
2985
3044
|
): boolean {
|
|
2986
3045
|
if (leases === undefined || leases.length !== worlds.length) return false;
|
|
@@ -2999,7 +3058,7 @@ function sameWorldState(
|
|
|
2999
3058
|
changes = lease.readChanges(cachedState.version);
|
|
3000
3059
|
} catch {
|
|
3001
3060
|
// Treat malformed or foreign version evidence as stale and rebuild the
|
|
3002
|
-
// atlas rather than reaching through
|
|
3061
|
+
// atlas rather than reaching through RenderResourceScope for a second mutation clock.
|
|
3003
3062
|
return false;
|
|
3004
3063
|
}
|
|
3005
3064
|
if (
|
|
@@ -3047,7 +3106,7 @@ function sameDirectionalShadowQuality(
|
|
|
3047
3106
|
function prepareDirectionalShadowCache(
|
|
3048
3107
|
internals: RenderSystemInternals,
|
|
3049
3108
|
frameState: RenderFrameState,
|
|
3050
|
-
worlds: readonly
|
|
3109
|
+
worlds: readonly RenderResourceScope[],
|
|
3051
3110
|
leases: readonly RenderReadLease[] | undefined,
|
|
3052
3111
|
lights: ExtractedLights,
|
|
3053
3112
|
shadowMapSize: number | undefined,
|
|
@@ -3102,7 +3161,7 @@ function prepareDirectionalShadowCache(
|
|
|
3102
3161
|
if (leases === undefined || leases.length !== worlds.length) {
|
|
3103
3162
|
// Direct record-stage test harnesses may omit the host-owned leases. They
|
|
3104
3163
|
// still exercise the real shadow encoder, but without the ECS read owner
|
|
3105
|
-
// they must conservatively rebuild rather than inspect
|
|
3164
|
+
// they must conservatively rebuild rather than inspect RenderResourceScope internals.
|
|
3106
3165
|
return { reuse: false, next: null };
|
|
3107
3166
|
}
|
|
3108
3167
|
const worldStateTokens = leases.map(readWorldState);
|
package/src/record/helpers.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { RenderResourceScope } from '../publication/resource-scope';
|
|
1
2
|
// @forgeax/engine-runtime - RenderSystem record stage: helpers.
|
|
2
3
|
// Extracted from render-system-record.ts (feat-20260704 M3/w17, pure move).
|
|
3
4
|
|
|
4
5
|
import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
5
|
-
import type { World } from '@forgeax/engine-ecs';
|
|
6
6
|
import { type Mat4, mat4 } from '@forgeax/engine-math';
|
|
7
7
|
import type { EquirectAsset } from '@forgeax/engine-types';
|
|
8
8
|
import { toShared } from '@forgeax/engine-types';
|
|
@@ -140,7 +140,7 @@ export function warnMultiSkybox(
|
|
|
140
140
|
*/
|
|
141
141
|
export function driveLazyEquirectProjection(
|
|
142
142
|
internals: RenderSystemInternals,
|
|
143
|
-
world:
|
|
143
|
+
world: RenderResourceScope,
|
|
144
144
|
frameState: Pick<RenderFrameState, 'firedEquirectProjectionFailedHandles'>,
|
|
145
145
|
equirectHandle: number,
|
|
146
146
|
): void {
|