@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
|
@@ -76,26 +76,26 @@ describe('Standard clearcoat root contract', () => {
|
|
|
76
76
|
);
|
|
77
77
|
|
|
78
78
|
// The canonical seven texture pairs occupy bindings 1..14. IBL remains
|
|
79
|
-
// at 15..
|
|
79
|
+
// at 15..20 and transmission at 21..22; authored physical maps append at
|
|
80
80
|
// their canonical fixed slots (26, 27, 30, 31), followed by the
|
|
81
81
|
// scene-index row at binding 46. Compacting the authored root here shifts WGSL IBL bindings and
|
|
82
82
|
// causes Dawn to reject the pipeline before any pixels are submitted.
|
|
83
|
-
expect(descriptor.entries).toHaveLength(
|
|
83
|
+
expect(descriptor.entries).toHaveLength(29);
|
|
84
84
|
expect(descriptor.entries.slice(0, 15).map((entry) => entry.binding)).toEqual(
|
|
85
85
|
Array.from({ length: 15 }, (_, binding) => binding),
|
|
86
86
|
);
|
|
87
87
|
expect(descriptor.entries[15]?.texture).toMatchObject({ viewDimension: 'cube' });
|
|
88
88
|
expect(descriptor.entries[17]?.texture).toMatchObject({ viewDimension: 'cube' });
|
|
89
|
-
expect(descriptor.entries[
|
|
90
|
-
expect(descriptor.entries[
|
|
91
|
-
expect(descriptor.entries.slice(
|
|
89
|
+
expect(descriptor.entries[21]?.sampler).toMatchObject({ type: 'filtering' });
|
|
90
|
+
expect(descriptor.entries[22]?.texture).toMatchObject({ viewDimension: '2d' });
|
|
91
|
+
expect(descriptor.entries.slice(23, 27).map((entry) => entry.binding)).toEqual([
|
|
92
92
|
26, 27, 30, 31,
|
|
93
93
|
]);
|
|
94
|
-
expect(descriptor.entries[
|
|
94
|
+
expect(descriptor.entries[28]).toMatchObject({
|
|
95
95
|
binding: 47,
|
|
96
96
|
texture: { sampleType: 'float', viewDimension: 'cube' },
|
|
97
97
|
});
|
|
98
|
-
expect(descriptor.entries[
|
|
98
|
+
expect(descriptor.entries[27]).toMatchObject({
|
|
99
99
|
binding: 46,
|
|
100
100
|
visibility: GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT,
|
|
101
101
|
buffer: { type: 'read-only-storage', hasDynamicOffset: false },
|
|
@@ -122,7 +122,7 @@ describe('Standard clearcoat root contract', () => {
|
|
|
122
122
|
// PBR layouts keep the producer-owned scene-material row at binding 46,
|
|
123
123
|
// even when an authored schema has no numeric parameters.
|
|
124
124
|
expect(descriptor.entries.map((entry) => entry.binding)).toEqual([
|
|
125
|
-
...Array.from({ length:
|
|
125
|
+
...Array.from({ length: 9 }, (_, binding) => binding),
|
|
126
126
|
46,
|
|
127
127
|
47,
|
|
128
128
|
]);
|
|
@@ -144,43 +144,43 @@ describe('Standard clearcoat root contract', () => {
|
|
|
144
144
|
);
|
|
145
145
|
|
|
146
146
|
expect(descriptor.entries.map((entry) => entry.binding)).toEqual([
|
|
147
|
-
...Array.from({ length:
|
|
147
|
+
...Array.from({ length: 15 }, (_, binding) => binding),
|
|
148
148
|
46,
|
|
149
149
|
]);
|
|
150
|
-
expect(descriptor.entries[
|
|
150
|
+
expect(descriptor.entries[9]).toMatchObject({
|
|
151
151
|
sampler: { type: 'non-filtering' },
|
|
152
152
|
});
|
|
153
|
-
expect(descriptor.entries[
|
|
153
|
+
expect(descriptor.entries[10]).toMatchObject({
|
|
154
154
|
texture: { sampleType: 'unfilterable-float', viewDimension: '2d' },
|
|
155
155
|
});
|
|
156
|
-
expect(descriptor.entries[
|
|
156
|
+
expect(descriptor.entries[11]).toMatchObject({
|
|
157
157
|
sampler: { type: 'filtering' },
|
|
158
158
|
});
|
|
159
|
-
expect(descriptor.entries[
|
|
159
|
+
expect(descriptor.entries[12]).toMatchObject({
|
|
160
160
|
texture: { sampleType: 'float', viewDimension: '2d' },
|
|
161
161
|
});
|
|
162
|
-
expect(descriptor.entries[
|
|
162
|
+
expect(descriptor.entries[13]).toMatchObject({
|
|
163
163
|
sampler: { type: 'non-filtering' },
|
|
164
164
|
});
|
|
165
|
-
expect(descriptor.entries[
|
|
165
|
+
expect(descriptor.entries[14]).toMatchObject({
|
|
166
166
|
texture: { sampleType: 'unfilterable-float', viewDimension: '2d' },
|
|
167
167
|
});
|
|
168
168
|
|
|
169
169
|
// The medium is a project material artifact with an explicit empty
|
|
170
170
|
// paramSchema. Its per-shader user region is therefore the reserved UBO
|
|
171
|
-
// at binding 0, followed by IBL 1..
|
|
172
|
-
// resources
|
|
171
|
+
// at binding 0, followed by IBL 1..6, transmission 7..8, and Surface
|
|
172
|
+
// resources 9..14. Keep this source assertion next to the descriptor
|
|
173
173
|
// assertion so a future injection-order change cannot leave a compiled
|
|
174
174
|
// WGSL binding map out of sync with the runtime BGL.
|
|
175
175
|
const source = readFileSync(
|
|
176
176
|
resolve(import.meta.dirname, '../../../shader/src/single-layer-medium.wgsl'),
|
|
177
177
|
'utf8',
|
|
178
178
|
);
|
|
179
|
-
expect(source).toContain('@group(1) @binding(
|
|
180
|
-
expect(source).toContain('@group(1) @binding(
|
|
181
|
-
expect(source).toContain('@group(1) @binding(
|
|
182
|
-
expect(source).toContain('@group(1) @binding(
|
|
183
|
-
expect(source).toContain('@group(1) @binding(
|
|
184
|
-
expect(source).toContain('@group(1) @binding(
|
|
179
|
+
expect(source).toContain('@group(1) @binding(9) var surfaceRawDepthSampler');
|
|
180
|
+
expect(source).toContain('@group(1) @binding(10) var surfaceRawDepthTexture');
|
|
181
|
+
expect(source).toContain('@group(1) @binding(11) var surfaceNearestLayerSampler');
|
|
182
|
+
expect(source).toContain('@group(1) @binding(12) var surfaceNearestLayerTexture');
|
|
183
|
+
expect(source).toContain('@group(1) @binding(13) var surfaceNearestDepthSampler');
|
|
184
|
+
expect(source).toContain('@group(1) @binding(14) var surfaceNearestDepthTexture');
|
|
185
185
|
});
|
|
186
186
|
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { STANDARD_MATERIAL_PARAM_SCHEMA } from '@forgeax/engine-types';
|
|
2
|
+
import { expect, it } from 'vitest';
|
|
3
|
+
import { STANDARD_PHYSICAL_REQUIRED_SAMPLED_TEXTURES } from '../assembly/device-feature-admission';
|
|
4
|
+
import { buildBindGroupLayoutDescriptor, buildPbrViewBglEntries } from '../pbr-pipeline';
|
|
5
|
+
import type { PipelineSpec } from '../pipeline-spec';
|
|
6
|
+
|
|
7
|
+
it('fits the physical carrier and extended-lighting view within the admitted device budget', () => {
|
|
8
|
+
const textureFields = new Set([
|
|
9
|
+
'baseColorTexture',
|
|
10
|
+
'metallicRoughnessTexture',
|
|
11
|
+
'normalTexture',
|
|
12
|
+
'emissiveTexture',
|
|
13
|
+
'occlusionTexture',
|
|
14
|
+
'clearcoatTexture',
|
|
15
|
+
'clearcoatRoughnessTexture',
|
|
16
|
+
'clearcoatNormalTexture',
|
|
17
|
+
'transmissionTexture',
|
|
18
|
+
'thicknessTexture',
|
|
19
|
+
]);
|
|
20
|
+
const spec: PipelineSpec = {
|
|
21
|
+
shader: { id: 'budget::physical', passKind: 'forward', variantSet: undefined },
|
|
22
|
+
attachments: { colorFormats: [], depthFormat: undefined, sampleCount: 1 },
|
|
23
|
+
geometry: { topology: 'triangle-list', vertexLayout: {} },
|
|
24
|
+
renderState: undefined,
|
|
25
|
+
};
|
|
26
|
+
const entries = [
|
|
27
|
+
...buildPbrViewBglEntries({ storageBuffer: true, extendedLighting: true }),
|
|
28
|
+
...buildBindGroupLayoutDescriptor(spec, {
|
|
29
|
+
kind: 'pbr-material-merged',
|
|
30
|
+
materialParamSchema: STANDARD_MATERIAL_PARAM_SCHEMA.filter(
|
|
31
|
+
(field) => field.type !== 'texture2d' || textureFields.has(field.name),
|
|
32
|
+
),
|
|
33
|
+
caps: { storageBuffer: true, transmissionBackdrop: true },
|
|
34
|
+
}).entries,
|
|
35
|
+
].filter((entry) => (entry.visibility & 2) !== 0);
|
|
36
|
+
expect(entries.filter((entry) => entry.sampler !== undefined).length).toBeLessThanOrEqual(16);
|
|
37
|
+
expect(entries.filter((entry) => entry.texture !== undefined).length).toBeLessThanOrEqual(
|
|
38
|
+
STANDARD_PHYSICAL_REQUIRED_SAMPLED_TEXTURES,
|
|
39
|
+
);
|
|
40
|
+
});
|
|
@@ -139,6 +139,11 @@ describe('recovery candidate preparation contract', () => {
|
|
|
139
139
|
expect(rendererSource).not.toMatch(/\n\s*materialShaderPipelineCache\.(get|set|has)\(/);
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
it('forks the validated shader catalog onto a recovery device', () => {
|
|
143
|
+
expect(rendererSource).toContain('activeCatalog.forkForDevice(getShaderModuleAdapter())');
|
|
144
|
+
expect(rendererSource).toContain('state === candidateShaderState && activeCatalog !== null');
|
|
145
|
+
});
|
|
146
|
+
|
|
142
147
|
it('resolves recovery variants from the candidate device capabilities', () => {
|
|
143
148
|
const start = rendererSource.indexOf('const resolveCachedMaterialShaderVariantSet');
|
|
144
149
|
const end = rendererSource.indexOf('const findMaterialShaderManifestEntry', start);
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
+
import { AssetRegistry } from '@forgeax/engine-assets-runtime';
|
|
3
4
|
import { type RhiDevice, RhiError } from '@forgeax/engine-rhi';
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import {
|
|
6
|
+
createMaterialProgramArtifactReceipt,
|
|
7
|
+
type MaterialShaderManifestEntry,
|
|
8
|
+
type MaterialShaderManifestVariant,
|
|
9
|
+
type ShaderCatalog,
|
|
10
|
+
ShaderRegistry,
|
|
11
|
+
type ShaderRegistryDevice,
|
|
8
12
|
} from '@forgeax/engine-shader';
|
|
9
13
|
import { describe, expect, it, vi } from 'vitest';
|
|
10
14
|
import { deviceOptionsForAdapter } from '../assembly/device-feature-admission';
|
|
@@ -128,6 +132,104 @@ fn cloud_direct_solar_factorX() -> f32 {
|
|
|
128
132
|
});
|
|
129
133
|
});
|
|
130
134
|
|
|
135
|
+
it('selects the replacement device capability variant after a metadata-only fork', async () => {
|
|
136
|
+
const uniformReceipt = createMaterialProgramArtifactReceipt({
|
|
137
|
+
schema: [],
|
|
138
|
+
directEntry: 'vs_uniform',
|
|
139
|
+
sceneIndexEntry: 'vs_scene_uniform',
|
|
140
|
+
vertexInputs: [],
|
|
141
|
+
layoutIdentity: 'uniform',
|
|
142
|
+
});
|
|
143
|
+
const storageReceipt = createMaterialProgramArtifactReceipt({
|
|
144
|
+
schema: [],
|
|
145
|
+
directEntry: 'vs_storage',
|
|
146
|
+
sceneIndexEntry: 'vs_scene_storage',
|
|
147
|
+
vertexInputs: [],
|
|
148
|
+
layoutIdentity: 'storage',
|
|
149
|
+
});
|
|
150
|
+
const entry: MaterialShaderManifestEntry = {
|
|
151
|
+
identifier: 'game::variant',
|
|
152
|
+
sourcePath: 'variant.wgsl',
|
|
153
|
+
composedWgsl: 'fallback',
|
|
154
|
+
paramSchema: '[]',
|
|
155
|
+
uvSetCount: 3,
|
|
156
|
+
variants: [
|
|
157
|
+
{
|
|
158
|
+
definesKey: 'STORAGE_BUFFER_AVAILABLE=false',
|
|
159
|
+
defines: { STORAGE_BUFFER_AVAILABLE: false },
|
|
160
|
+
composedWgsl: 'uniform-variant',
|
|
161
|
+
receipt: uniformReceipt,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
definesKey: '',
|
|
165
|
+
defines: { STORAGE_BUFFER_AVAILABLE: true },
|
|
166
|
+
composedWgsl: 'storage-variant',
|
|
167
|
+
receipt: storageReceipt,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
const manifestUrl = URL.createObjectURL(
|
|
172
|
+
new Blob([JSON.stringify({ entries: [], materialShaders: [entry] })]),
|
|
173
|
+
);
|
|
174
|
+
const registryDevice = (): ShaderRegistryDevice => ({
|
|
175
|
+
createShaderModule: () => ({ ok: true, value: {} as never }),
|
|
176
|
+
});
|
|
177
|
+
const device = (storageBuffer: boolean): RhiDevice =>
|
|
178
|
+
({
|
|
179
|
+
caps: {
|
|
180
|
+
backendKind: 'webgpu',
|
|
181
|
+
storageBuffer,
|
|
182
|
+
rgba16floatRenderable: true,
|
|
183
|
+
samplerAliasing: true,
|
|
184
|
+
},
|
|
185
|
+
limits: {
|
|
186
|
+
maxStorageBuffersPerShaderStage: storageBuffer ? 8 : 0,
|
|
187
|
+
maxSampledTexturesPerShaderStage: 32,
|
|
188
|
+
maxTextureArrayLayers: 64,
|
|
189
|
+
maxUniformBuffersPerShaderStage: 4,
|
|
190
|
+
},
|
|
191
|
+
}) as unknown as RhiDevice;
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const active = new ShaderRegistry({ device: registryDevice(), manifestUrl });
|
|
195
|
+
const activeAssets = new AssetRegistry(active);
|
|
196
|
+
await prepareMaterialShaders(device(false), () => active, activeAssets, new Map());
|
|
197
|
+
active.installMaterialArtifact('game::custom', {
|
|
198
|
+
source: 'custom-variant',
|
|
199
|
+
paramSchema: [],
|
|
200
|
+
receipt: uniformReceipt,
|
|
201
|
+
});
|
|
202
|
+
const activeArtifact = active.findMaterialArtifact('game::variant');
|
|
203
|
+
expect(activeArtifact.ok).toBe(true);
|
|
204
|
+
if (!activeArtifact.ok) return;
|
|
205
|
+
expect(activeArtifact.value.source).toBe('uniform-variant');
|
|
206
|
+
expect(activeArtifact.value.receipt).toEqual(uniformReceipt);
|
|
207
|
+
|
|
208
|
+
URL.revokeObjectURL(manifestUrl);
|
|
209
|
+
const candidate = active.forkForDevice(registryDevice());
|
|
210
|
+
const candidateAssets = new AssetRegistry(candidate);
|
|
211
|
+
const uvSetCounts = new Map<string, number>();
|
|
212
|
+
await prepareMaterialShaders(device(true), () => candidate, candidateAssets, uvSetCounts);
|
|
213
|
+
const candidateArtifact = candidate.findMaterialArtifact('game::variant');
|
|
214
|
+
expect(candidateArtifact.ok).toBe(true);
|
|
215
|
+
if (!candidateArtifact.ok) return;
|
|
216
|
+
expect(candidateArtifact.value.source).toBe('storage-variant');
|
|
217
|
+
expect(candidateArtifact.value.receipt).toEqual(storageReceipt);
|
|
218
|
+
expect(uvSetCounts.get('game::variant')).toBe(3);
|
|
219
|
+
expect(candidate.findMaterialArtifact('game::custom').ok).toBe(false);
|
|
220
|
+
|
|
221
|
+
const customArtifact = active.findMaterialArtifact('game::custom');
|
|
222
|
+
expect(customArtifact.ok).toBe(true);
|
|
223
|
+
if (!customArtifact.ok) return;
|
|
224
|
+
candidate.installMaterialArtifact('game::custom', customArtifact.value);
|
|
225
|
+
const candidateCustom = candidate.findMaterialArtifact('game::custom');
|
|
226
|
+
expect(candidateCustom.ok && candidateCustom.value.source).toBe('custom-variant');
|
|
227
|
+
expect(activeArtifact.value.source).toBe('uniform-variant');
|
|
228
|
+
} finally {
|
|
229
|
+
URL.revokeObjectURL(manifestUrl);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
131
233
|
it('requests the extended-lighting sampled-texture limit when the adapter supports it', () => {
|
|
132
234
|
const features = new Set<GPUFeatureName>();
|
|
133
235
|
expect(
|
|
@@ -137,7 +239,7 @@ fn cloud_direct_solar_factorX() -> f32 {
|
|
|
137
239
|
} as never),
|
|
138
240
|
).toEqual({
|
|
139
241
|
requiredFeatures: [],
|
|
140
|
-
requiredLimits: { maxSampledTexturesPerShaderStage:
|
|
242
|
+
requiredLimits: { maxSampledTexturesPerShaderStage: 23 },
|
|
141
243
|
});
|
|
142
244
|
expect(
|
|
143
245
|
deviceOptionsForAdapter({
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { AssetRegistry, HANDLE_CUBE, RuntimeMaterialValue } from '@forgeax/engine-assets-runtime';
|
|
2
|
+
import { World } from '@forgeax/engine-ecs';
|
|
3
|
+
import { Transform } from '@forgeax/engine-scene';
|
|
4
|
+
import { ShaderRegistry } from '@forgeax/engine-shader';
|
|
5
|
+
import type { MaterialAsset } from '@forgeax/engine-types';
|
|
6
|
+
import { expect, it } from 'vitest';
|
|
7
|
+
import {
|
|
8
|
+
MATERIAL_CONTEXT,
|
|
9
|
+
materialRecordFixture,
|
|
10
|
+
} from '../../../assets-runtime/src/__tests__/fixtures/material-publication';
|
|
11
|
+
import { MeshFilter, MeshRenderer } from '../components';
|
|
12
|
+
import { extractFrames, resolveMaterialSnapshot } from '../render-system-extract';
|
|
13
|
+
|
|
14
|
+
it('keeps cooked Forward and ShadowCaster programs when World values change or a catalogue entry is replaced', () => {
|
|
15
|
+
const assets = new AssetRegistry(new ShaderRegistry({ manifestUrl: undefined }));
|
|
16
|
+
const world = new World();
|
|
17
|
+
const record = materialRecordFixture({
|
|
18
|
+
parameters: [{ name: 'roughness', type: 'f32' }],
|
|
19
|
+
values: { roughness: 0.8 },
|
|
20
|
+
passes: [
|
|
21
|
+
{ name: 'Forward', program: { module: 'game::material' } },
|
|
22
|
+
{ name: 'ShadowCaster', program: { module: 'game::shadow' } },
|
|
23
|
+
],
|
|
24
|
+
});
|
|
25
|
+
const base: MaterialAsset = { kind: 'material', ...record.resolved };
|
|
26
|
+
assets.catalog(record.guid, base).unwrap();
|
|
27
|
+
if (!record.sourceClosure || !record.parameterContract)
|
|
28
|
+
throw new Error('Missing cooked metadata');
|
|
29
|
+
assets.recordMaterialReadiness(record.guid, {
|
|
30
|
+
status: 'Ready',
|
|
31
|
+
guid: record.guid,
|
|
32
|
+
materialGuid: record.guid,
|
|
33
|
+
publicationGeneration: record.publicationGeneration,
|
|
34
|
+
specializationKey: record.specializationKey,
|
|
35
|
+
artifactDigest: record.artifactDigest,
|
|
36
|
+
sourceClosure: record.sourceClosure,
|
|
37
|
+
parameterContract: record.parameterContract,
|
|
38
|
+
record,
|
|
39
|
+
programs: record.programs,
|
|
40
|
+
});
|
|
41
|
+
const handle = world.internSharedRef('MaterialAsset', base);
|
|
42
|
+
world
|
|
43
|
+
.spawn(
|
|
44
|
+
{ component: Transform, data: {} },
|
|
45
|
+
{ component: MeshFilter, data: { assetHandle: HANDLE_CUBE } },
|
|
46
|
+
{ component: MeshRenderer, data: { materials: [handle] } },
|
|
47
|
+
)
|
|
48
|
+
.unwrap();
|
|
49
|
+
world.update(0).unwrap();
|
|
50
|
+
const frameRead = () =>
|
|
51
|
+
extractFrames([world], 0, assets, undefined, undefined, {
|
|
52
|
+
materialContext: MATERIAL_CONTEXT,
|
|
53
|
+
cull: 'none',
|
|
54
|
+
}).renderables[0]?.materials[0];
|
|
55
|
+
const read = () =>
|
|
56
|
+
resolveMaterialSnapshot(handle, world, assets, undefined, undefined, MATERIAL_CONTEXT);
|
|
57
|
+
const before = read();
|
|
58
|
+
expect(frameRead()?.materialProgramKeys).toEqual(before.materialProgramKeys);
|
|
59
|
+
expect(Object.keys(before.materialProgramKeys ?? {})).toEqual(['Forward', 'ShadowCaster']);
|
|
60
|
+
const content = world
|
|
61
|
+
.spawn({
|
|
62
|
+
component: RuntimeMaterialValue,
|
|
63
|
+
data: {
|
|
64
|
+
asset: handle,
|
|
65
|
+
parameter: 'roughness',
|
|
66
|
+
kind: 0,
|
|
67
|
+
value: [0.25],
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
.unwrap();
|
|
71
|
+
expect(read().roughness).toBe(0.25);
|
|
72
|
+
expect(frameRead()?.materialProgramKeys).toEqual(before.materialProgramKeys);
|
|
73
|
+
expect(read().materialProgramKeys).toEqual(before.materialProgramKeys);
|
|
74
|
+
assets.catalog(record.guid, { ...base, values: { roughness: 0.6 } }).unwrap();
|
|
75
|
+
expect(read().materialProgramKeys).toEqual(before.materialProgramKeys);
|
|
76
|
+
world.despawn(content).unwrap();
|
|
77
|
+
expect(read().roughness).toBeCloseTo(0.8);
|
|
78
|
+
expect(read().materialProgramKeys).toEqual(before.materialProgramKeys);
|
|
79
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { deriveVertexBufferLayout } from '@forgeax/engine-geometry';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import {
|
|
4
4
|
BILLBOARD_MATERIAL_INSTANCE_VERTEX_BUFFERS,
|
|
@@ -17,11 +17,31 @@ const FLOAT32_COMPONENT_COUNT = {
|
|
|
17
17
|
describe('prepared feature vertex layout SSOT', () => {
|
|
18
18
|
it('derives the mesh vertex stream from geometry layout ownership', () => {
|
|
19
19
|
const [expected] = toGpuVertexBufferLayouts(
|
|
20
|
-
deriveVertexBufferLayout(
|
|
20
|
+
deriveVertexBufferLayout({
|
|
21
|
+
position: new Float32Array(),
|
|
22
|
+
normal: new Float32Array(),
|
|
23
|
+
uv: new Float32Array(),
|
|
24
|
+
tangent: new Float32Array(),
|
|
25
|
+
uv1: new Float32Array(),
|
|
26
|
+
color: new Float32Array(),
|
|
27
|
+
}),
|
|
21
28
|
);
|
|
22
29
|
const [actual] = MESH_GEOMETRY_MATERIAL_INSTANCE_VERTEX_BUFFERS;
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
if (expected === undefined) throw new Error('Missing geometry descriptor');
|
|
32
|
+
expect(actual).toEqual({
|
|
33
|
+
...expected,
|
|
34
|
+
stepMode: 'vertex',
|
|
35
|
+
attributes: expected.attributes.map((attribute) => ({
|
|
36
|
+
...attribute,
|
|
37
|
+
shaderLocation:
|
|
38
|
+
attribute.shaderLocation === 6
|
|
39
|
+
? 15
|
|
40
|
+
: attribute.shaderLocation === 13
|
|
41
|
+
? 14
|
|
42
|
+
: attribute.shaderLocation,
|
|
43
|
+
})),
|
|
44
|
+
});
|
|
25
45
|
});
|
|
26
46
|
|
|
27
47
|
it.each([
|
|
@@ -21,12 +21,12 @@ export function transmissionBackdropAvailable(limit: number | undefined): boolea
|
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* The complete Standard physical root can declare all optional texture slots.
|
|
24
|
-
* Its material group therefore needs
|
|
24
|
+
* Its material group therefore needs two more sampled-texture entries than the
|
|
25
25
|
* extended-lighting topology alone. Request this ceiling when the adapter can
|
|
26
26
|
* provide it; lower-capability devices remain fail-closed at material
|
|
27
27
|
* pipeline admission instead of receiving an invalid WebGPU layout.
|
|
28
28
|
*/
|
|
29
|
-
export const STANDARD_PHYSICAL_REQUIRED_SAMPLED_TEXTURES =
|
|
29
|
+
export const STANDARD_PHYSICAL_REQUIRED_SAMPLED_TEXTURES = 23;
|
|
30
30
|
|
|
31
31
|
export interface DeviceFeatureAdmissionOptions {
|
|
32
32
|
readonly gpuPassTiming?: object | undefined;
|
|
@@ -14,6 +14,7 @@ import type { ObservationUnavailableError, RenderError } from '../errors/render'
|
|
|
14
14
|
import type { RenderFeature, RenderFeatureDiagnostics } from '../features/types';
|
|
15
15
|
import type { GpuDrivenProductionInspection, LodOcclusionInspection } from '../inspection-types';
|
|
16
16
|
import type { MeshMaterialBindingObservation } from '../mesh-material-bindings';
|
|
17
|
+
import type { PublishedRenderFrameInput } from '../publication/contract';
|
|
17
18
|
import type { FrameObservation, FrameObservationOptions } from '../record/frame';
|
|
18
19
|
import type { CurrentGraphTarget, GraphTargetCaptureRequest } from '../record/frame-snapshot';
|
|
19
20
|
import type {
|
|
@@ -120,7 +121,9 @@ export interface RendererHostImplementation {
|
|
|
120
121
|
readonly lodOcclusion: LodOcclusionInspection | undefined;
|
|
121
122
|
readonly gpuDriven: GpuDrivenProductionInspection;
|
|
122
123
|
};
|
|
123
|
-
drawFrame(
|
|
124
|
+
drawFrame(
|
|
125
|
+
request: RenderFrameInput | PublishedRenderFrameInput,
|
|
126
|
+
): RenderResult<FrameReceipt, RhiError | RenderError>;
|
|
124
127
|
/** Arm one upcoming frame for receipt-bound color observations. */
|
|
125
128
|
requestObservation?: (
|
|
126
129
|
domains: readonly import('../render-contract').FrameObservationDomain[],
|
|
@@ -154,7 +157,7 @@ export interface RendererLegacyHostAdapter {
|
|
|
154
157
|
attachScene(world: World): RenderResult<void, RhiError>;
|
|
155
158
|
detachScene(world: World): void;
|
|
156
159
|
draw(
|
|
157
|
-
worldsOrRequest: readonly World[] | RenderFrameInput,
|
|
160
|
+
worldsOrRequest: readonly World[] | RenderFrameInput | PublishedRenderFrameInput,
|
|
158
161
|
options?: DrawOwnerOptions,
|
|
159
162
|
): RenderResult<void | FrameReceipt, RhiError | RenderError>;
|
|
160
163
|
observeCurrentFrame(
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
|
|
11
11
|
import type { MaterialShaderManifestEntry, ShaderCatalog } from '@forgeax/engine-shader';
|
|
12
12
|
import type { Handle, ParamSchemaEntry } from '@forgeax/engine-types';
|
|
13
|
+
import { AssetError } from '@forgeax/engine-types';
|
|
13
14
|
import { DeviceScope } from '../../device/device-scope';
|
|
14
15
|
import { GpuResidencyCache } from '../../device/gpu-residency';
|
|
15
16
|
import { RecoverError, type RecoverFailure } from '../../errors/recover';
|
|
@@ -399,6 +400,14 @@ export function createRendererRecovery(deps: RendererRecoveryDependencies): Rend
|
|
|
399
400
|
device,
|
|
400
401
|
adaptMipmapShaderModuleFactory(internals.pack.createShaderModule),
|
|
401
402
|
(world, pod) => {
|
|
403
|
+
if ('resolveAsset' in world)
|
|
404
|
+
return err(
|
|
405
|
+
new AssetError({
|
|
406
|
+
code: 'asset-not-found',
|
|
407
|
+
expected: 'source-owned environment asset allocation',
|
|
408
|
+
hint: 'publish the environment asset from its source owner before GPU preparation',
|
|
409
|
+
}),
|
|
410
|
+
);
|
|
402
411
|
const handle: Handle<'EquirectAsset', 'shared'> = world.allocSharedRef(
|
|
403
412
|
'EquirectAsset',
|
|
404
413
|
pod,
|
|
@@ -79,6 +79,11 @@ interface ReadbackRecord {
|
|
|
79
79
|
export interface RenderTargetHost {
|
|
80
80
|
/** The logical owner for every target lifecycle operation. */
|
|
81
81
|
readonly owner: 'renderer';
|
|
82
|
+
/** Logical descriptions for the source publication boundary; no physical state escapes. */
|
|
83
|
+
descriptions(): readonly {
|
|
84
|
+
readonly target: RenderTarget;
|
|
85
|
+
readonly descriptor: RenderTargetDescriptor;
|
|
86
|
+
}[];
|
|
82
87
|
createRenderTarget(descriptor: RenderTargetDescriptor): RenderResult<RenderTarget, RenderError>;
|
|
83
88
|
resizeRenderTarget(
|
|
84
89
|
target: RenderTarget,
|
|
@@ -231,53 +236,51 @@ function makePhysical(
|
|
|
231
236
|
const faceViews: TextureView[] = [];
|
|
232
237
|
const depthTextures: Texture[] = [];
|
|
233
238
|
const depthViews: TextureView[] = [];
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
depthViews.push(depthView.value);
|
|
280
|
-
}
|
|
239
|
+
for (let face = 0; face < layers; face += 1) {
|
|
240
|
+
const depth = device.createTexture({
|
|
241
|
+
label: `render-target.${generation}.depth.${face}`,
|
|
242
|
+
size: {
|
|
243
|
+
width: descriptor.width,
|
|
244
|
+
height: descriptor.height,
|
|
245
|
+
depthOrArrayLayers: descriptor.sampleCount === 4 ? 1 : layers,
|
|
246
|
+
},
|
|
247
|
+
mipLevelCount: 1,
|
|
248
|
+
sampleCount: descriptor.sampleCount,
|
|
249
|
+
dimension: '2d',
|
|
250
|
+
format: 'depth24plus-stencil8',
|
|
251
|
+
usage: GPU_TEXTURE_USAGE_RENDER_ATTACHMENT,
|
|
252
|
+
viewFormats: undefined,
|
|
253
|
+
textureBindingViewDimension: undefined,
|
|
254
|
+
});
|
|
255
|
+
if (!depth.ok)
|
|
256
|
+
return {
|
|
257
|
+
ok: false,
|
|
258
|
+
error: new RenderTargetOperationFailedError({
|
|
259
|
+
operation: 'create',
|
|
260
|
+
stage: 'allocation',
|
|
261
|
+
generation,
|
|
262
|
+
cause: depth.error,
|
|
263
|
+
recovery: 'retry',
|
|
264
|
+
}),
|
|
265
|
+
};
|
|
266
|
+
const depthView = device.createTextureView(depth.value, {
|
|
267
|
+
dimension: '2d',
|
|
268
|
+
baseArrayLayer: descriptor.sampleCount === 4 ? 0 : face,
|
|
269
|
+
arrayLayerCount: 1,
|
|
270
|
+
});
|
|
271
|
+
if (!depthView.ok)
|
|
272
|
+
return {
|
|
273
|
+
ok: false,
|
|
274
|
+
error: new RenderTargetOperationFailedError({
|
|
275
|
+
operation: 'create',
|
|
276
|
+
stage: 'allocation',
|
|
277
|
+
generation,
|
|
278
|
+
cause: depthView.error,
|
|
279
|
+
recovery: 'retry',
|
|
280
|
+
}),
|
|
281
|
+
};
|
|
282
|
+
depthTextures.push(depth.value);
|
|
283
|
+
depthViews.push(depthView.value);
|
|
281
284
|
}
|
|
282
285
|
if (descriptor.sampleCount === 4) {
|
|
283
286
|
for (let face = 0; face < layers; face += 1) {
|
|
@@ -405,6 +408,13 @@ export function createRenderTargetHost(options: RenderTargetHostOptions = {}): R
|
|
|
405
408
|
|
|
406
409
|
return Object.freeze({
|
|
407
410
|
owner: 'renderer' as const,
|
|
411
|
+
descriptions() {
|
|
412
|
+
return [...targets].map((target) => {
|
|
413
|
+
const state = owner.inspect(target);
|
|
414
|
+
if (!state.ok) throw state.error;
|
|
415
|
+
return { target, descriptor: state.value.candidate?.descriptor ?? state.value.descriptor };
|
|
416
|
+
});
|
|
417
|
+
},
|
|
408
418
|
createRenderTarget(
|
|
409
419
|
descriptor: RenderTargetDescriptor,
|
|
410
420
|
): RenderResult<RenderTarget, RenderError> {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RenderPublicationError } from '../publication/contract';
|
|
1
2
|
// Public renderer facade and profile validation.
|
|
2
3
|
// This module narrows the lifetime-heavy host implementation to the stable
|
|
3
4
|
// lease/receipt contract and owns the profile/error projection helpers.
|
|
@@ -64,7 +65,8 @@ export function exposeRenderer(renderer: RendererHostImplementation): Renderer {
|
|
|
64
65
|
cause: structuredRendererCause(cause, 'renderer-event'),
|
|
65
66
|
});
|
|
66
67
|
const drawError = (cause: RhiError | RenderError): RenderError => {
|
|
67
|
-
if (cause instanceof MotionBlurValidationError
|
|
68
|
+
if (cause instanceof MotionBlurValidationError || cause instanceof RenderPublicationError)
|
|
69
|
+
return cause;
|
|
68
70
|
if (cause instanceof RendererOperationError) return cause;
|
|
69
71
|
if (cause instanceof RendererContractFailureError) {
|
|
70
72
|
return new RendererOperationError('device-operation-failed', {
|
|
@@ -1439,7 +1439,7 @@ export async function buildReadyWebGPU(
|
|
|
1439
1439
|
// instances) and the 4-slot pipeline layout in one call, throwing on each
|
|
1440
1440
|
// device.createBindGroupLayout / device.createPipelineLayout failure.
|
|
1441
1441
|
// material BGL now carries 24 entries (canonical material 0..14 + Skylight
|
|
1442
|
-
// 15..
|
|
1442
|
+
// 15..20 + transmission 21..22); authored physical maps append after the
|
|
1443
1443
|
// engine-owned injections. The pipeline layout itself stays at 4 slots, no
|
|
1444
1444
|
// @group(4) allocated (D-5 round-4 fix for the round-2 maxBindGroups=4
|
|
1445
1445
|
// BLOCKER). The standalone unlit BGL is the 15-entry user region exposed by
|