@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
package/src/features/host.ts
CHANGED
|
@@ -133,6 +133,8 @@ export interface RenderFeatureStageEvent {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
export interface RenderFeatureFrameInput {
|
|
136
|
+
readonly publishedFeatures?: readonly { readonly identity: string; readonly data: unknown }[];
|
|
137
|
+
readonly onFeatureSourceSubmitted?: (identity: string, feedback: unknown) => void;
|
|
136
138
|
readonly worlds: readonly import('@forgeax/engine-ecs').World[];
|
|
137
139
|
readonly owner: number;
|
|
138
140
|
readonly frameNumber: number;
|
|
@@ -1454,7 +1456,10 @@ export function runRenderFeatureFrame(
|
|
|
1454
1456
|
const extracted = invokeStage(
|
|
1455
1457
|
slot,
|
|
1456
1458
|
'extract',
|
|
1457
|
-
() =>
|
|
1459
|
+
() => {
|
|
1460
|
+
const published = input.publishedFeatures?.find((row) => row.identity === identity);
|
|
1461
|
+
return published === undefined ? slot.feature.extract(extractContext) : ok(published.data);
|
|
1462
|
+
},
|
|
1458
1463
|
stageEvents,
|
|
1459
1464
|
errors,
|
|
1460
1465
|
);
|
|
@@ -1655,11 +1660,14 @@ export function runRenderFeatureFrame(
|
|
|
1655
1660
|
continue;
|
|
1656
1661
|
}
|
|
1657
1662
|
planExecutionProjections.set(plannedFrame, projected.value);
|
|
1658
|
-
if (feature.onFrameSubmitted !== undefined) {
|
|
1663
|
+
if (feature.onFrameSubmitted !== undefined || feature.onSourceFrameSubmitted !== undefined) {
|
|
1659
1664
|
submissionSensitivePlans.add(plannedFrame);
|
|
1660
|
-
submissionAcknowledgments.set(plannedFrame, () =>
|
|
1661
|
-
feature.onFrameSubmitted?.(extracted.value, projected.value)
|
|
1662
|
-
|
|
1665
|
+
submissionAcknowledgments.set(plannedFrame, () => {
|
|
1666
|
+
feature.onFrameSubmitted?.(extracted.value, projected.value);
|
|
1667
|
+
if (input.publishedFeatures?.some((row) => row.identity === identity))
|
|
1668
|
+
input.onFeatureSourceSubmitted?.(identity, declared.value.sourceFeedback);
|
|
1669
|
+
else feature.onSourceFrameSubmitted?.(extracted.value, declared.value.sourceFeedback);
|
|
1670
|
+
});
|
|
1663
1671
|
}
|
|
1664
1672
|
plans.push(plannedFrame);
|
|
1665
1673
|
if (feature.onFrameAborted !== undefined) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { deriveVertexLayoutProjection } from '@forgeax/engine-geometry';
|
|
2
|
+
|
|
3
|
+
// Mesh attributes keep geometry's canonical packing. Only locations change:
|
|
4
|
+
// the particle instance occupies 4..9 and custom material inputs occupy 10..13.
|
|
5
|
+
export const PARTICLE_MESH_DEFAULTS = {
|
|
6
|
+
position: [0, 0, 0],
|
|
7
|
+
normal: [0, 0, 1],
|
|
8
|
+
uv: [0, 0],
|
|
9
|
+
tangent: [1, 0, 0, 1],
|
|
10
|
+
uv1: [0, 0],
|
|
11
|
+
color: [1, 1, 1, 1],
|
|
12
|
+
} as const;
|
|
13
|
+
export const PARTICLE_MESH_GEOMETRY = deriveVertexLayoutProjection(
|
|
14
|
+
Object.fromEntries(Object.keys(PARTICLE_MESH_DEFAULTS).map((key) => [key, new Float32Array()])),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const PARTICLE_MESH_VERTEX_BUFFER = {
|
|
18
|
+
arrayStride: PARTICLE_MESH_GEOMETRY.arrayStride,
|
|
19
|
+
stepMode: 'vertex' as const,
|
|
20
|
+
attributes: PARTICLE_MESH_GEOMETRY.attributes.map(({ key, shaderLocation, offset, format }) => ({
|
|
21
|
+
shaderLocation: key === 'color' ? 14 : key === 'uv1' ? 15 : shaderLocation,
|
|
22
|
+
offset,
|
|
23
|
+
format,
|
|
24
|
+
})),
|
|
25
|
+
};
|
package/src/features/plan.ts
CHANGED
|
@@ -237,6 +237,8 @@ export type RenderFeaturePassDeclaration =
|
|
|
237
237
|
* access is derived from these roles rather than duplicated as reads/writes.
|
|
238
238
|
*/
|
|
239
239
|
export interface RenderFeaturePlan {
|
|
240
|
+
/** Source acknowledgment facts; never part of GPU resource or graph identity. */
|
|
241
|
+
readonly sourceFeedback?: unknown;
|
|
240
242
|
readonly resources: readonly RenderFeatureResourceDeclaration[];
|
|
241
243
|
readonly passes: readonly RenderFeaturePassDeclaration[];
|
|
242
244
|
}
|
package/src/features/types.ts
CHANGED
|
@@ -171,6 +171,8 @@ export interface RenderFeature<FrameData> {
|
|
|
171
171
|
readonly source: string;
|
|
172
172
|
}[];
|
|
173
173
|
extract(context: RenderFeatureExtractContext): Result<FrameData, RenderError>;
|
|
174
|
+
/** GUID roots needed by the declarative plan; the publisher carries their asset closure. */
|
|
175
|
+
assetDependencies?(data: FrameData): readonly string[];
|
|
174
176
|
plan(data: FrameData, context: RenderFeaturePlanContext): Result<RenderFeaturePlan, RenderError>;
|
|
175
177
|
/**
|
|
176
178
|
* Consume frame-owned producer state only after the compiled graph reached
|
|
@@ -178,6 +180,12 @@ export interface RenderFeature<FrameData> {
|
|
|
178
180
|
* extracted frame and never during declarative planning.
|
|
179
181
|
*/
|
|
180
182
|
onFrameSubmitted?(data: FrameData, submission?: RenderFeatureSubmission): void;
|
|
183
|
+
/**
|
|
184
|
+
* Consume source-owned intents after submission; feedback must be structured-cloneable.
|
|
185
|
+
* A sealed successor may have been extracted before this acknowledgment. Ordered
|
|
186
|
+
* intent producers must identify already-submitted work instead of replaying it.
|
|
187
|
+
*/
|
|
188
|
+
onSourceFrameSubmitted?(data: FrameData, feedback: unknown): void;
|
|
181
189
|
/**
|
|
182
190
|
* Discard frame-owned producer state when graph admission or submission is
|
|
183
191
|
* rejected. This is the symmetric recovery path for onFrameSubmitted.
|
|
@@ -158,7 +158,7 @@ interface GlyphTextData {
|
|
|
158
158
|
*/
|
|
159
159
|
export function glyphTextLayoutSystem(
|
|
160
160
|
world: World,
|
|
161
|
-
gpuStore: GpuResidencyCache,
|
|
161
|
+
gpuStore: Pick<GpuResidencyCache, 'updateMesh'>,
|
|
162
162
|
): Result<void, TextError> {
|
|
163
163
|
resetFontConcurrency();
|
|
164
164
|
|
|
@@ -200,7 +200,7 @@ function collectGlyphEntities(world: World): EntityHandle[] {
|
|
|
200
200
|
*/
|
|
201
201
|
function processEntity(
|
|
202
202
|
world: World,
|
|
203
|
-
gpuStore: GpuResidencyCache,
|
|
203
|
+
gpuStore: Pick<GpuResidencyCache, 'updateMesh'>,
|
|
204
204
|
entity: EntityHandle,
|
|
205
205
|
): TextError | null {
|
|
206
206
|
const gtRes = world.get(entity, GlyphText);
|
|
@@ -1359,6 +1359,7 @@ export interface PreparedGpuDrivenFrame {
|
|
|
1359
1359
|
readonly projectShadow?: (
|
|
1360
1360
|
graph: RenderGraphBuilder<RenderPipelineFrame>,
|
|
1361
1361
|
identity: ShadowViewIdentity,
|
|
1362
|
+
forceCompute?: boolean,
|
|
1362
1363
|
) => Result<ShadowViewProjection | undefined, RenderGraphError>;
|
|
1363
1364
|
readonly updateShadowViews?: (
|
|
1364
1365
|
views: readonly Omit<ShadowViewUpdateInput, 'sourcePlan' | 'scene'>[],
|
|
@@ -2572,7 +2573,12 @@ export class GpuDrivenProduction {
|
|
|
2572
2573
|
?.inspect()
|
|
2573
2574
|
.map(
|
|
2574
2575
|
(entry) =>
|
|
2575
|
-
|
|
2576
|
+
// Cache decisions and logical update generations are frame-local
|
|
2577
|
+
// producer state. They must not feed the compiled graph key: a
|
|
2578
|
+
// graph replacement may intentionally invalidate a shadow view
|
|
2579
|
+
// once without creating a graph-rebuild feedback loop. Resource
|
|
2580
|
+
// and source-plan generations remain real graph dependencies.
|
|
2581
|
+
`${shadowViewIdentityKey(entry.identity)}:${entry.sourceRevision}:${entry.resourceGeneration}`,
|
|
2576
2582
|
)
|
|
2577
2583
|
.join('|') ?? '';
|
|
2578
2584
|
const production = this;
|
|
@@ -2689,10 +2695,10 @@ export class GpuDrivenProduction {
|
|
|
2689
2695
|
frameNumber,
|
|
2690
2696
|
),
|
|
2691
2697
|
}),
|
|
2692
|
-
projectShadow: (graph, identity) => {
|
|
2698
|
+
projectShadow: (graph, identity, forceCompute = false) => {
|
|
2693
2699
|
if (!shadowViewPool.isActive(identity) || shadowViewPool.submission(identity) === undefined)
|
|
2694
2700
|
return ok(undefined);
|
|
2695
|
-
return shadowViewPool.project(graph, identity);
|
|
2701
|
+
return shadowViewPool.project(graph, identity, forceCompute);
|
|
2696
2702
|
},
|
|
2697
2703
|
updateShadowViews: (views) =>
|
|
2698
2704
|
shadowFiltered.plan.batches.length > 0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { World } from '@forgeax/engine-ecs';
|
|
2
1
|
import { gpuDrivenShadowDrawKey } from '../extract/gpu-driven';
|
|
2
|
+
import type { RenderResourceScope } from '../publication/resource-scope';
|
|
3
3
|
import { worldEntityKey } from '../record/frame-snapshot';
|
|
4
4
|
import type { ShadowCasterMembership } from '../render-system-extract';
|
|
5
5
|
|
|
@@ -7,12 +7,12 @@ const WORLD_ENTITY_KEY_STRIDE = 4294967296;
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Extract emits world/entity keys in the transient `worlds[]` index domain,
|
|
10
|
-
* while PersistentRenderScene owns stable renderer-local
|
|
10
|
+
* while PersistentRenderScene owns stable renderer-local RenderResourceScope keys for the
|
|
11
11
|
* retained GPU scene. Rebase ShadowCaster ownership at this orchestration
|
|
12
12
|
* boundary before both GPU projection and CPU residual recording consume it.
|
|
13
13
|
*/
|
|
14
14
|
export function rebaseShadowCasterOwnership(
|
|
15
|
-
worlds: readonly
|
|
15
|
+
worlds: readonly RenderResourceScope[],
|
|
16
16
|
stableWorldKeys: readonly number[],
|
|
17
17
|
entityKeys: ReadonlySet<number>,
|
|
18
18
|
drawKeys: ReadonlySet<string>,
|
|
@@ -395,14 +395,39 @@ export class ShadowViewStatePool {
|
|
|
395
395
|
project<FrameCtx extends RenderGraphFrame>(
|
|
396
396
|
builder: RenderGraphBuilder<FrameCtx>,
|
|
397
397
|
identity: ShadowViewIdentity,
|
|
398
|
+
forceCompute = false,
|
|
398
399
|
): Result<ShadowViewProjection, RenderGraphError> {
|
|
399
400
|
const record = this.records.get(identityKey(identity));
|
|
400
401
|
if (record === undefined) return err(graphStateError(identity));
|
|
401
402
|
const cacheHit = record.cache === 'hit' && !record.invalidated;
|
|
403
|
+
// A graph replacement must carry the shadow compute passes even when the
|
|
404
|
+
// view itself is a cache hit. The graph owns the pass list, so omitting
|
|
405
|
+
// compute work while importing a replacement graph would leave its
|
|
406
|
+
// indirect arguments stale. The normal pool contract still skips work on
|
|
407
|
+
// a cache hit; graph projection opts in explicitly at that boundary.
|
|
408
|
+
const includeCompute = forceCompute || !cacheHit;
|
|
409
|
+
let forceComputePending = forceCompute;
|
|
410
|
+
let lastFrame: FrameCtx | undefined;
|
|
411
|
+
let executeForFrame = false;
|
|
412
|
+
const executeCompute = (frame: FrameCtx): boolean => {
|
|
413
|
+
if (frame !== lastFrame) {
|
|
414
|
+
lastFrame = frame;
|
|
415
|
+
const currentRecord = this.records.get(identityKey(record.identity));
|
|
416
|
+
executeForFrame =
|
|
417
|
+
forceComputePending ||
|
|
418
|
+
currentRecord === undefined ||
|
|
419
|
+
currentRecord.cache !== 'hit' ||
|
|
420
|
+
currentRecord.invalidated;
|
|
421
|
+
forceComputePending = false;
|
|
422
|
+
}
|
|
423
|
+
return executeForFrame;
|
|
424
|
+
};
|
|
402
425
|
const projected = record.view.addPasses(
|
|
403
426
|
builder,
|
|
404
427
|
shadowViewLabelPrefix(record.identity),
|
|
405
|
-
|
|
428
|
+
includeCompute,
|
|
429
|
+
undefined,
|
|
430
|
+
includeCompute ? executeCompute : undefined,
|
|
406
431
|
);
|
|
407
432
|
if (!projected.ok) return projected;
|
|
408
433
|
return ok({
|
|
@@ -411,7 +436,7 @@ export class ShadowViewStatePool {
|
|
|
411
436
|
generation: record.generation,
|
|
412
437
|
plan: record.plan,
|
|
413
438
|
view: record.view,
|
|
414
|
-
passNames:
|
|
439
|
+
passNames: includeCompute ? passNames(record.identity) : Object.freeze([]),
|
|
415
440
|
graphResources: projected.value,
|
|
416
441
|
});
|
|
417
442
|
}
|
|
@@ -982,6 +982,7 @@ export class GpuDrivenView {
|
|
|
982
982
|
labelPrefix = 'gpu-driven',
|
|
983
983
|
includeCompute = true,
|
|
984
984
|
surfaceSubmissionObservation?: () => SurfaceSubmissionCandidate | undefined,
|
|
985
|
+
executeIf?: (frame: FrameCtx) => boolean,
|
|
985
986
|
): Result<GpuDrivenViewGraphResources, RenderGraphError> {
|
|
986
987
|
const buffers = this.buffers;
|
|
987
988
|
const bindGroup = this.bindGroup;
|
|
@@ -1099,6 +1100,7 @@ export class GpuDrivenView {
|
|
|
1099
1100
|
{ resource: view.value, usage: 'uniform-read' },
|
|
1100
1101
|
{ resource: counters.value, usage: 'storage-read-write' },
|
|
1101
1102
|
],
|
|
1103
|
+
...(executeIf === undefined ? {} : { executeIf }),
|
|
1102
1104
|
encode: ({ pass }) => {
|
|
1103
1105
|
pass.setPipeline(this.resetPipeline);
|
|
1104
1106
|
pass.setBindGroup(0, bindGroup);
|
|
@@ -1116,6 +1118,7 @@ export class GpuDrivenView {
|
|
|
1116
1118
|
{ resource: counters.value, usage: 'storage-read-write' },
|
|
1117
1119
|
{ resource: visible.value, usage: 'storage-read-write' },
|
|
1118
1120
|
],
|
|
1121
|
+
...(executeIf === undefined ? {} : { executeIf }),
|
|
1119
1122
|
encode: ({ pass }) => {
|
|
1120
1123
|
pass.setPipeline(this.cullPipeline);
|
|
1121
1124
|
pass.setBindGroup(0, bindGroup);
|
|
@@ -1130,6 +1133,7 @@ export class GpuDrivenView {
|
|
|
1130
1133
|
{ resource: counters.value, usage: 'storage-read' },
|
|
1131
1134
|
{ resource: indirect.value, usage: 'storage-read-write' },
|
|
1132
1135
|
],
|
|
1136
|
+
...(executeIf === undefined ? {} : { executeIf }),
|
|
1133
1137
|
encode: ({ pass }) => {
|
|
1134
1138
|
pass.setPipeline(this.finalizePipeline);
|
|
1135
1139
|
pass.setBindGroup(0, bindGroup);
|
|
@@ -1148,6 +1152,7 @@ export class GpuDrivenView {
|
|
|
1148
1152
|
{ resource: indirect.value, usage: 'copy-src' },
|
|
1149
1153
|
{ resource: lodReadback.value, usage: 'copy-dst' },
|
|
1150
1154
|
],
|
|
1155
|
+
...(executeIf === undefined ? {} : { executeIf }),
|
|
1151
1156
|
encode: ({ encoder, resources }) => {
|
|
1152
1157
|
// A cached graph may run while an observer is awaiting mapAsync on the
|
|
1153
1158
|
// previous copy. WebGPU forbids submitting a copy into a
|
|
@@ -32,7 +32,6 @@ export function resolveFrameSkylightResources(
|
|
|
32
32
|
prefilterView: resident?.prefilterView ?? fallback.prefilterView,
|
|
33
33
|
prefilterSampler: fallback.sampler,
|
|
34
34
|
brdfLutView: resident?.brdfLutView ?? fallback.brdfLutView,
|
|
35
|
-
brdfLutSampler: fallback.sampler,
|
|
36
35
|
intensityBuffer: fallback.intensityBuffer,
|
|
37
36
|
});
|
|
38
37
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
// skylight-bind-group.ts -- Skylight resources merged into @group(1) PBR
|
|
2
|
-
// material BGL (binding 7..
|
|
2
|
+
// material BGL (binding 7..12) + fallback identity resource bundle. The
|
|
3
3
|
// complete Standard material group appends transmission entries.
|
|
4
4
|
//
|
|
5
5
|
// Plan-strategy D-5 (round-4 REVISED): the round-2 stand-alone @group(4)
|
|
6
6
|
// Skylight BindGroupLayout collided with WebGPU's default maxBindGroups=4
|
|
7
7
|
// limit, blocking pbr-pl pipeline-layout creation in chrome-beta. Round-4
|
|
8
|
-
// rewrites the contract: Skylight
|
|
9
|
-
// existing PBR material BindGroupLayout at bindings 7..
|
|
8
|
+
// rewrites the contract: Skylight 6 entries are **appended** to the
|
|
9
|
+
// existing PBR material BindGroupLayout at bindings 7..12; pipeline layout
|
|
10
10
|
// stays at 4 slots `[view, material, mesh, instances]`; unlit pipeline is
|
|
11
11
|
// untouched (it uses its own material BGL).
|
|
12
12
|
//
|
|
13
13
|
// Surface (M2 round-4 / t40 amend):
|
|
14
14
|
// - mergeSkylightIntoMaterialBgl(materialBglEntries): given the existing
|
|
15
15
|
// 7 PBR material BindGroupLayout entries (binding 0..6), returns the
|
|
16
|
-
// merged
|
|
17
|
-
// [irrTex, irrSampler, prefTex, prefSampler, brdfTex,
|
|
16
|
+
// merged 13-entry array with Skylight resources at binding 7..12
|
|
17
|
+
// [irrTex, irrSampler, prefTex, prefSampler, brdfTex,
|
|
18
18
|
// uniform { intensity: f32 }]. The merge is a pure function -- caller
|
|
19
19
|
// (createRenderer) is responsible for passing the result to
|
|
20
20
|
// `device.createBindGroupLayout`.
|
|
@@ -124,13 +124,13 @@ export interface SkylightQueue {
|
|
|
124
124
|
*/
|
|
125
125
|
export interface SkylightBindGroupResources {
|
|
126
126
|
readonly irradianceView: TextureView;
|
|
127
|
+
/** Shared by diffuse irradiance and the BRDF lookup table. */
|
|
127
128
|
readonly irradianceSampler: Sampler;
|
|
128
129
|
readonly prefilterView: TextureView;
|
|
129
130
|
/** Present only when the per-draw prefilter slot was replaced by a local probe. */
|
|
130
131
|
readonly skylightPrefilterView?: TextureView;
|
|
131
132
|
readonly prefilterSampler: Sampler;
|
|
132
133
|
readonly brdfLutView: TextureView;
|
|
133
|
-
readonly brdfLutSampler: Sampler;
|
|
134
134
|
readonly intensityBuffer: Buffer;
|
|
135
135
|
}
|
|
136
136
|
|
|
@@ -143,7 +143,7 @@ export interface SkylightBindGroupResources {
|
|
|
143
143
|
*
|
|
144
144
|
* No stand-alone `bindGroup` field: the round-2 stand-alone Skylight BG
|
|
145
145
|
* is gone (D-5 round-4); the fallback resources flow into the PBR
|
|
146
|
-
* material BG at binding 7..
|
|
146
|
+
* material BG at binding 7..12.
|
|
147
147
|
*/
|
|
148
148
|
export interface SkylightFallback {
|
|
149
149
|
readonly irradianceTexture: Texture;
|
|
@@ -159,7 +159,7 @@ export interface SkylightFallback {
|
|
|
159
159
|
// ─── Pure merger: BindGroupLayout entries (D-5 round-4) ─────────────────────
|
|
160
160
|
|
|
161
161
|
/**
|
|
162
|
-
* Append the
|
|
162
|
+
* Append the 6 Skylight BindGroupLayout entries after the existing PBR
|
|
163
163
|
* user-region entries. Pure function -- the
|
|
164
164
|
* caller passes the result to `device.createBindGroupLayout`. Charter P4
|
|
165
165
|
* consistent abstraction: one BGL holds material + Skylight together,
|
|
@@ -208,16 +208,10 @@ export function mergeSkylightIntoMaterialBgl(
|
|
|
208
208
|
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
209
209
|
texture: { sampleType: 'float', viewDimension: '2d' },
|
|
210
210
|
} as BglEntry);
|
|
211
|
-
// binding 12:
|
|
211
|
+
// binding 12: uniform { intensity: f32 }
|
|
212
212
|
merged.push({
|
|
213
213
|
binding: skylightBindingStart + 5,
|
|
214
214
|
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
215
|
-
sampler: { type: 'filtering' },
|
|
216
|
-
} as BglEntry);
|
|
217
|
-
// binding 13: uniform { intensity: f32 }
|
|
218
|
-
merged.push({
|
|
219
|
-
binding: skylightBindingStart + 6,
|
|
220
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
221
215
|
buffer: { type: 'uniform' },
|
|
222
216
|
} as BglEntry);
|
|
223
217
|
return merged;
|
|
@@ -226,7 +220,7 @@ export function mergeSkylightIntoMaterialBgl(
|
|
|
226
220
|
// ─── Pure merger: BindGroupEntry values (assembly site) ─────────────────────
|
|
227
221
|
|
|
228
222
|
/**
|
|
229
|
-
* Append the
|
|
223
|
+
* Append the 6 Skylight BindGroupEntry resources (binding 7..12) onto the
|
|
230
224
|
* existing PBR material BindGroupEntry values (binding 0..6). The caller
|
|
231
225
|
* passes the result to `device.createBindGroup` as part of the merged
|
|
232
226
|
* material BG.
|
|
@@ -301,10 +295,6 @@ export function assembleMaterialWithSkylightEntries(
|
|
|
301
295
|
},
|
|
302
296
|
{
|
|
303
297
|
binding: iblStart + 5,
|
|
304
|
-
resource: { kind: 'sampler', value: skylight.brdfLutSampler },
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
binding: iblStart + 6,
|
|
308
298
|
resource: { kind: 'buffer', value: { buffer: skylight.intensityBuffer } },
|
|
309
299
|
},
|
|
310
300
|
];
|
|
@@ -413,11 +403,9 @@ export function createSkylightFallback(
|
|
|
413
403
|
device: SkylightDevice,
|
|
414
404
|
queue: SkylightQueue,
|
|
415
405
|
): SkylightFallback {
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
// in assembleMaterialWithSkylightEntries; WebGPU spec permits sampler
|
|
420
|
-
// reuse across BindGroupEntry slots.
|
|
406
|
+
// Diffuse IBL and the BRDF LUT share one binding; prefilter remains a
|
|
407
|
+
// separate binding so per-draw reflection probes can replace it. Both
|
|
408
|
+
// bindings use this linear-clamp sampler for the global environment.
|
|
421
409
|
const samplerResult = device.createSampler({
|
|
422
410
|
label: 'skylight-fallback-sampler',
|
|
423
411
|
magFilter: 'linear',
|
package/src/index.ts
CHANGED
|
@@ -560,6 +560,15 @@ export {
|
|
|
560
560
|
type PointsStyleInput,
|
|
561
561
|
} from './points-lines/admission.js';
|
|
562
562
|
export type { PointsLinesInspection } from './points-lines/inspection.js';
|
|
563
|
+
export {
|
|
564
|
+
type PublishedRenderFrameInput,
|
|
565
|
+
type RenderPublication,
|
|
566
|
+
RenderPublicationError,
|
|
567
|
+
type RenderPublicationIdentity,
|
|
568
|
+
renderPublicationTransfers,
|
|
569
|
+
} from './publication/contract';
|
|
570
|
+
export { createRenderPublisher, type RenderPublicationCandidate } from './publication/publisher';
|
|
571
|
+
export { RenderPublicationTargetOwner, type RenderTargetAuthoring } from './publication/targets';
|
|
563
572
|
/**
|
|
564
573
|
* Public bounded GPU pass facts. Use the single `gpuPassTiming` opt-in,
|
|
565
574
|
* `draw()` receipt, and `observe(receipt, { include: ['timings'] })` route.
|
package/src/instances.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { World } from '@forgeax/engine-ecs';
|
|
2
1
|
import { InstanceTransformsStrideMismatchError } from '@forgeax/engine-ecs/projection';
|
|
3
2
|
import type { RhiErrorCode } from '@forgeax/engine-rhi';
|
|
4
3
|
|
|
@@ -93,7 +92,7 @@ export function instanceUploadRangesForResident(
|
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
interface Projection extends InstanceCollectionSnapshot {
|
|
96
|
-
readonly world:
|
|
95
|
+
readonly world: object;
|
|
97
96
|
readonly entity: number;
|
|
98
97
|
}
|
|
99
98
|
interface Residency {
|
|
@@ -112,11 +111,15 @@ interface Residency {
|
|
|
112
111
|
export class InstanceProjectionStore {
|
|
113
112
|
private nextId = 1;
|
|
114
113
|
private nextGeneration = 1;
|
|
115
|
-
private identities = new WeakMap<
|
|
114
|
+
private identities = new WeakMap<object, Map<number, InstanceCollectionId>>();
|
|
116
115
|
private readonly records = new Map<InstanceCollectionId, Projection>();
|
|
117
116
|
private readonly residency = new Map<InstanceCollectionId, Residency>();
|
|
118
117
|
|
|
119
|
-
project(
|
|
118
|
+
project(
|
|
119
|
+
world: object,
|
|
120
|
+
entity: number,
|
|
121
|
+
transforms: ArrayLike<number>,
|
|
122
|
+
): InstanceCollectionSnapshot {
|
|
120
123
|
const collectionId =
|
|
121
124
|
this.identities.get(world)?.get(entity) ?? (this.nextId++ as InstanceCollectionId);
|
|
122
125
|
const previous = this.records.get(collectionId);
|
|
@@ -147,7 +150,7 @@ export class InstanceProjectionStore {
|
|
|
147
150
|
|
|
148
151
|
/** Called only after the owning RenderScene accepts its prepared changes. */
|
|
149
152
|
accept(
|
|
150
|
-
world:
|
|
153
|
+
world: object,
|
|
151
154
|
entity: number,
|
|
152
155
|
snapshot: {
|
|
153
156
|
readonly collectionId?: InstanceCollectionId;
|
|
@@ -175,7 +178,7 @@ export class InstanceProjectionStore {
|
|
|
175
178
|
});
|
|
176
179
|
}
|
|
177
180
|
|
|
178
|
-
release(world:
|
|
181
|
+
release(world: object, entity: number): void {
|
|
179
182
|
const entities = this.identities.get(world);
|
|
180
183
|
const id = entities?.get(entity);
|
|
181
184
|
if (id === undefined) return;
|
package/src/pbr-pipeline.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// material BGL grows from the derived user region by appending the 7
|
|
7
7
|
// Skylight resources via `mergeSkylightIntoMaterialBgl`. The
|
|
8
8
|
// unlit pipeline keeps its 7-entry material BGL (no Skylight binding
|
|
9
|
-
// 7..
|
|
9
|
+
// 7..12 contamination) so unlit demos don't carry IBL state.
|
|
10
10
|
// - charter P4: same pipeline layout shape drives Skylight present +
|
|
11
11
|
// absent paths -- AI users do not branch on Skylight existence.
|
|
12
12
|
// - feat-20260520-skylight-ibl-cubemap M4 / t59 (round-4): the
|
|
@@ -158,7 +158,10 @@ export function buildPbrMaterialUserRegionEntries(
|
|
|
158
158
|
let entries = derived.bglEntries.map(
|
|
159
159
|
(entry): GPUBindGroupLayoutEntry => ({
|
|
160
160
|
binding: entry.binding,
|
|
161
|
-
visibility:
|
|
161
|
+
visibility:
|
|
162
|
+
entry.texture !== undefined || entry.sampler !== undefined
|
|
163
|
+
? GPU_SHADER_STAGE_VERTEX | GPU_SHADER_STAGE_FRAGMENT
|
|
164
|
+
: entry.visibility,
|
|
162
165
|
...(entry.buffer === undefined ? {} : { buffer: entry.buffer }),
|
|
163
166
|
...(entry.sampler === undefined ? {} : { sampler: entry.sampler }),
|
|
164
167
|
...(entry.texture === undefined ? {} : { texture: entry.texture }),
|
|
@@ -209,8 +212,8 @@ export function buildPbrMaterialUserRegionEntries(
|
|
|
209
212
|
// (group(0) bindings 3..7); this kind is the seam for
|
|
210
213
|
// each per-material shadow override surface a future feat
|
|
211
214
|
// wires onto group(1).
|
|
212
|
-
// - 'ibl' the
|
|
213
|
-
// cube + brdfLut 2d +
|
|
215
|
+
// - 'ibl' the 6 IBL / Skylight entries (irradiance / prefilter
|
|
216
|
+
// cube + brdfLut 2d + 2 samplers + intensity uniform).
|
|
214
217
|
// Used by `buildPbrPipelineLayouts` after the user-region
|
|
215
218
|
// user paramSchema entries are emitted.
|
|
216
219
|
// - 'lightmap' the 4 emissive + occlusion entries (sampler + texture
|
|
@@ -224,7 +227,7 @@ export function buildPbrMaterialUserRegionEntries(
|
|
|
224
227
|
// placeholders because their shaders do not read these slots.
|
|
225
228
|
export type InjectionKind = keyof typeof INJECTION_KIND_LENGTHS;
|
|
226
229
|
|
|
227
|
-
const IBL_INJECTION_LENGTH =
|
|
230
|
+
const IBL_INJECTION_LENGTH = 6;
|
|
228
231
|
const LIGHTMAP_INJECTION_LENGTH = 4;
|
|
229
232
|
const SHADOW_INJECTION_LENGTH = 2;
|
|
230
233
|
const TRANSMISSION_INJECTION_LENGTH = 2;
|
|
@@ -283,16 +286,10 @@ export function appendInjection(
|
|
|
283
286
|
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
284
287
|
texture: { sampleType: 'float', viewDimension: '2d' },
|
|
285
288
|
},
|
|
286
|
-
// binding start+5:
|
|
289
|
+
// binding start+5: uniform { intensity: f32 }
|
|
287
290
|
{
|
|
288
291
|
binding: start + 5,
|
|
289
292
|
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
290
|
-
sampler: { type: 'filtering' },
|
|
291
|
-
},
|
|
292
|
-
// binding start+6: uniform { intensity: f32 }
|
|
293
|
-
{
|
|
294
|
-
binding: start + 6,
|
|
295
|
-
visibility: GPU_SHADER_STAGE_FRAGMENT,
|
|
296
293
|
buffer: { type: 'uniform' },
|
|
297
294
|
},
|
|
298
295
|
];
|
|
@@ -1119,13 +1116,13 @@ export function pbrSkinMeshDynamicOffsets(
|
|
|
1119
1116
|
|
|
1120
1117
|
/**
|
|
1121
1118
|
* Build a stand-alone 7-entry unlit material BGL. Round-4 D-5 keeps unlit
|
|
1122
|
-
* material BG isolated from Skylight binding 7..
|
|
1119
|
+
* material BG isolated from Skylight binding 7..12 -- unlit demos do not
|
|
1123
1120
|
* pay for IBL state. The unlit pipeline still binds material at slot 1,
|
|
1124
1121
|
* just with a 7-entry layout.
|
|
1125
1122
|
*
|
|
1126
1123
|
* Note: at the moment the runtime still routes both unlit + standard
|
|
1127
1124
|
* through a single 14-entry pipeline layout; the unlit material BG
|
|
1128
|
-
* carries fallback identity resources at binding 7..
|
|
1125
|
+
* carries fallback identity resources at binding 7..12. This factory is
|
|
1129
1126
|
* exported as a future-proof seam for the moment when unlit demos own
|
|
1130
1127
|
* their own pipeline layout (t57 (e) test pins the contract today so the
|
|
1131
1128
|
* eventual split has a green target).
|
|
@@ -1241,7 +1238,7 @@ export const SPRITE_PASS_PER_INSTANCE_REGION_VARIANT_SET = '';
|
|
|
1241
1238
|
*
|
|
1242
1239
|
* Three groups by derivation source:
|
|
1243
1240
|
* 1. Shader-derived (paramSchema reflection + injection chain):
|
|
1244
|
-
* - `'pbr-material-merged'` — derived user region + Skylight
|
|
1241
|
+
* - `'pbr-material-merged'` — derived user region + Skylight 6 + transmission 2
|
|
1245
1242
|
* - `'unlit-material'` — 7 entries: base PBR material only (no inject)
|
|
1246
1243
|
* - `'hdrp-7-slot'` — 7 entries (binding 0 + 3..8): HDRP cluster + SSAO group(2) BGL
|
|
1247
1244
|
* 2. Caps-driven literal shapes (no shader):
|
|
@@ -1638,11 +1635,11 @@ export function buildBindGroupLayoutDescriptor(
|
|
|
1638
1635
|
}
|
|
1639
1636
|
case 'pbr-material-merged': {
|
|
1640
1637
|
// Material BGL: per-shader user-region (derive(paramSchema).bglEntries)
|
|
1641
|
-
// + IBL injection (
|
|
1638
|
+
// + IBL injection (6) + transmission injection (2). The user-region
|
|
1642
1639
|
// size is the only variable; injection start = userRegion.length so a
|
|
1643
1640
|
// custom schema shifts IBL/transmission by its sampler/texture pairs.
|
|
1644
|
-
// The built-in standard-PBR
|
|
1645
|
-
//
|
|
1641
|
+
// The built-in standard-PBR user region has 15 entries;
|
|
1642
|
+
// scene rows follow injection, and physical texture pairs follow the effective root schema.
|
|
1646
1643
|
//
|
|
1647
1644
|
// Schema source priority (D-1): explicit materialParamSchema option >
|
|
1648
1645
|
// registry lookup of spec.shader.id > built-in standard-PBR fallback.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { AssetRegistry } from '@forgeax/engine-assets-runtime';
|
|
2
2
|
import { resolveAssetHandle } from '@forgeax/engine-assets-runtime';
|
|
3
|
-
import type { World } from '@forgeax/engine-ecs';
|
|
4
3
|
import type { RenderGraphBuilder, RenderGraphError } from '@forgeax/engine-render-graph';
|
|
5
4
|
import type {
|
|
6
5
|
BindGroup,
|
|
@@ -13,6 +12,7 @@ import type {
|
|
|
13
12
|
} from '@forgeax/engine-rhi';
|
|
14
13
|
import { err, type Handle, ok, type Result, type TextureAsset } from '@forgeax/engine-types';
|
|
15
14
|
import type { GpuResidencyCache, TextureGpuEntry } from '../../device/gpu-residency';
|
|
15
|
+
import type { RenderResourceScope } from '../../publication/resource-scope';
|
|
16
16
|
import type { RenderPipelineFrame, RenderPipelineTarget } from '../../render-pipeline';
|
|
17
17
|
import {
|
|
18
18
|
admitStandardColorLut,
|
|
@@ -41,7 +41,7 @@ export interface StandardLutGraphProjection {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export interface StandardLutGpuPreparationInput {
|
|
44
|
-
readonly world:
|
|
44
|
+
readonly world: RenderResourceScope;
|
|
45
45
|
readonly handle: Handle<'TextureAsset', 'shared'>;
|
|
46
46
|
readonly strength: number;
|
|
47
47
|
readonly assets: AssetRegistry;
|
|
@@ -125,7 +125,7 @@ export function prepareStandardLutGpu(
|
|
|
125
125
|
if (!asset.ok) {
|
|
126
126
|
return failure(
|
|
127
127
|
'standard-lut-residency-failed',
|
|
128
|
-
'the authored TextureAsset handle resolves in the active
|
|
128
|
+
'the authored TextureAsset handle resolves in the active RenderResourceScope',
|
|
129
129
|
'register or rebuild the TextureAsset, then retry the same camera generation',
|
|
130
130
|
causeDetail(asset.error),
|
|
131
131
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { World } from '@forgeax/engine-ecs';
|
|
2
1
|
import { err, ok, type Result, RhiError } from '@forgeax/engine-rhi';
|
|
3
2
|
import { toShared } from '@forgeax/engine-types';
|
|
3
|
+
import type { RenderResourceScope } from '../../publication/resource-scope';
|
|
4
4
|
import type { RenderFrameState } from '../../record/frame-snapshot';
|
|
5
5
|
import type { RenderSystemInternals } from '../../record/render-context';
|
|
6
6
|
import type { CameraSnapshot } from '../../render-contract';
|
|
@@ -17,7 +17,7 @@ export function prepareStandardOutputResources(
|
|
|
17
17
|
state: RenderFrameState,
|
|
18
18
|
internals: RenderSystemInternals,
|
|
19
19
|
camera: CameraSnapshot | undefined,
|
|
20
|
-
world:
|
|
20
|
+
world: RenderResourceScope,
|
|
21
21
|
): Result<void, RhiError> {
|
|
22
22
|
const output = camera?.output;
|
|
23
23
|
if (output?.exposure.kind === 'auto') {
|