@forgeax/engine-render 0.1.32 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +379 -33
- package/dist/assembly/analytic-fog-registration.d.ts +3 -0
- package/dist/assembly/analytic-fog-registration.d.ts.map +1 -0
- package/dist/assembly/fxaa-registration.d.ts +4 -0
- package/dist/assembly/fxaa-registration.d.ts.map +1 -0
- package/dist/assembly/lod-observation.d.ts +4 -0
- package/dist/assembly/lod-observation.d.ts.map +1 -0
- package/dist/assembly/motion-blur-registration.d.ts +3 -0
- package/dist/assembly/motion-blur-registration.d.ts.map +1 -0
- package/dist/assembly/renderer-facade.d.ts.map +1 -1
- package/dist/assembly/temporal-post-process-prewarm.d.ts +13 -0
- package/dist/assembly/temporal-post-process-prewarm.d.ts.map +1 -0
- package/dist/assembly/webgpu-ready-contract.d.ts +4 -3
- package/dist/assembly/webgpu-ready-contract.d.ts.map +1 -1
- package/dist/assembly/webgpu-ready.d.ts +1 -1
- package/dist/assembly/webgpu-ready.d.ts.map +1 -1
- package/dist/assembly/webgpu-renderer.d.ts.map +1 -1
- package/dist/bloom-admission.d.ts +13 -0
- package/dist/bloom-admission.d.ts.map +1 -0
- package/dist/{chunk-DRR7D3KX.mjs → chunk-3RTCJTH4.mjs} +12 -9
- package/dist/chunk-3RTCJTH4.mjs.map +1 -0
- package/dist/{chunk-S665NJNF.mjs → chunk-LEAXOMYJ.mjs} +3 -3
- package/dist/{chunk-S665NJNF.mjs.map → chunk-LEAXOMYJ.mjs.map} +1 -1
- package/dist/{chunk-6TFBXLRJ.mjs → chunk-NB2S7ZRX.mjs} +155 -11
- package/dist/chunk-NB2S7ZRX.mjs.map +1 -0
- package/dist/{chunk-LULCA3OF.mjs → chunk-NNQL7CB3.mjs} +4 -4
- package/dist/{chunk-LULCA3OF.mjs.map → chunk-NNQL7CB3.mjs.map} +1 -1
- package/dist/{chunk-4NNZQCL3.mjs → chunk-OO3DRYK7.mjs} +66 -200
- package/dist/chunk-OO3DRYK7.mjs.map +1 -0
- package/dist/{chunk-GWCEMDVR.mjs → chunk-PLAXOOX5.mjs} +232 -7
- package/dist/chunk-PLAXOOX5.mjs.map +1 -0
- package/dist/{chunk-JRIPCMYC.mjs → chunk-VOZWDM3L.mjs} +283 -193
- package/dist/chunk-VOZWDM3L.mjs.map +1 -0
- package/dist/{chunk-JCIRYQ4V.mjs → chunk-ZDQWGQW7.mjs} +10 -4
- package/dist/chunk-ZDQWGQW7.mjs.map +1 -0
- package/dist/components/atmosphere.d.ts +2 -0
- package/dist/components/atmosphere.d.ts.map +1 -1
- package/dist/components/camera.d.ts +20 -4
- package/dist/components/camera.d.ts.map +1 -1
- package/dist/components/mesh-filter.d.ts +6 -5
- package/dist/components/mesh-filter.d.ts.map +1 -1
- package/dist/components/motion-blur.d.ts +1 -0
- package/dist/components/motion-blur.d.ts.map +1 -1
- package/dist/construct-renderer.mjs +2865 -526
- package/dist/construct-renderer.mjs.map +1 -1
- package/dist/device/gpu-residency.d.ts +6 -0
- package/dist/device/gpu-residency.d.ts.map +1 -1
- package/dist/environment/analytic-fog.d.ts +6 -0
- package/dist/environment/analytic-fog.d.ts.map +1 -0
- package/dist/errors/render.d.ts +6 -2
- package/dist/errors/render.d.ts.map +1 -1
- package/dist/extract/camera.d.ts.map +1 -1
- package/dist/extract/contracts.d.ts +7 -0
- package/dist/extract/contracts.d.ts.map +1 -1
- package/dist/extract/environment.d.ts +2 -0
- package/dist/extract/environment.d.ts.map +1 -1
- package/dist/features/error-reporting.d.ts +13 -0
- package/dist/features/error-reporting.d.ts.map +1 -0
- package/dist/features/host.d.ts +5 -0
- package/dist/features/host.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-feature.d.ts +20 -2
- package/dist/features/motion-blur/motion-blur-feature.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-params.d.ts +21 -0
- package/dist/features/motion-blur/motion-blur-params.d.ts.map +1 -1
- package/dist/features/motion-blur/motion-blur-runtime.d.ts +54 -0
- package/dist/features/motion-blur/motion-blur-runtime.d.ts.map +1 -0
- package/dist/features/plan.d.ts +7 -4
- package/dist/features/plan.d.ts.map +1 -1
- package/dist/features/prepared-gpu-work.d.ts +9 -5
- package/dist/features/prepared-gpu-work.d.ts.map +1 -1
- package/dist/features/render-graph-compute.d.ts.map +1 -1
- package/dist/features/render-graph-contribution.d.ts.map +1 -1
- package/dist/gpu-driven/material-artifacts.d.ts.map +1 -1
- package/dist/gpu-driven/prepared-draw.d.ts +7 -0
- package/dist/gpu-driven/prepared-draw.d.ts.map +1 -1
- package/dist/gpu-driven/production-raster.d.ts +3 -1
- package/dist/gpu-driven/production-raster.d.ts.map +1 -1
- package/dist/gpu-driven/view-gpu.d.ts +9 -0
- package/dist/gpu-driven/view-gpu.d.ts.map +1 -1
- package/dist/gpu-scene.d.ts +6 -1
- package/dist/gpu-scene.d.ts.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +5 -5
- package/dist/inspection-types.d.ts +43 -2
- package/dist/inspection-types.d.ts.map +1 -1
- package/dist/instances.d.ts +8 -1
- package/dist/instances.d.ts.map +1 -1
- package/dist/internal.mjs +6 -6
- package/dist/pipeline/standard-post.d.ts +28 -19
- package/dist/pipeline/standard-post.d.ts.map +1 -1
- package/dist/pipeline-spec.d.ts +2 -2
- package/dist/record/frame-snapshot.d.ts +3 -0
- package/dist/record/frame-snapshot.d.ts.map +1 -1
- package/dist/record/frame-targets.d.ts.map +1 -1
- package/dist/record/frame.d.ts +5 -2
- package/dist/record/frame.d.ts.map +1 -1
- package/dist/record/main-pass-geometry.d.ts.map +1 -1
- package/dist/record/mesh-ssbo.d.ts +6 -5
- package/dist/record/mesh-ssbo.d.ts.map +1 -1
- package/dist/record/render-context.d.ts +12 -28
- package/dist/record/render-context.d.ts.map +1 -1
- package/dist/record/shadow-pass.d.ts.map +1 -1
- package/dist/record/skybox-post-pass.d.ts +5 -3
- package/dist/record/skybox-post-pass.d.ts.map +1 -1
- package/dist/record/typed-frame-graph.d.ts +1 -1
- package/dist/record/typed-frame-graph.d.ts.map +1 -1
- package/dist/recovery/render-system-candidate.d.ts.map +1 -1
- package/dist/render-contract.d.ts +24 -4
- package/dist/render-contract.d.ts.map +1 -1
- package/dist/render-pipeline.d.ts +3 -1
- package/dist/render-pipeline.d.ts.map +1 -1
- package/dist/render-system-extract-tail.d.ts.map +1 -1
- package/dist/render-system-extract.d.ts +8 -1
- package/dist/render-system-extract.d.ts.map +1 -1
- package/dist/render-system.d.ts +7 -1
- package/dist/render-system.d.ts.map +1 -1
- package/dist/scene/render-scene.d.ts +2 -0
- package/dist/scene/render-scene.d.ts.map +1 -1
- package/dist/temporal/frame-coordinator.d.ts.map +1 -1
- package/dist/temporal/frame.d.ts +7 -0
- package/dist/temporal/frame.d.ts.map +1 -1
- package/dist/temporal/index.mjs +5 -5
- package/dist/temporal/view.d.ts +6 -1
- package/dist/temporal/view.d.ts.map +1 -1
- package/dist/typed-render-graph-primitives.d.ts +10 -7
- package/dist/typed-render-graph-primitives.d.ts.map +1 -1
- package/dist/volume/component.d.ts +0 -1
- package/dist/volume/component.d.ts.map +1 -1
- package/dist/volume/passes.d.ts.map +1 -1
- package/dist/volume/temporal.d.ts +3 -1
- package/dist/volume/temporal.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/__tests__/analytic-fog-graph.unit.test.ts +199 -0
- package/src/__tests__/analytic-fog-optics.unit.test.ts +38 -0
- package/src/__tests__/bloom-exact-zero.unit.test.ts +112 -18
- package/src/__tests__/bloom-lifecycle.integration.test.ts +17 -4
- package/src/__tests__/dynamic-resolution-plugin.integration.test.ts +20 -0
- package/src/__tests__/environment-fog-lanes.integration.test.ts +14 -0
- package/src/__tests__/environment-selection.unit.test.ts +12 -0
- package/src/__tests__/factory-contract.integration.test.ts +331 -68
- package/src/__tests__/frame-camera-selection.unit.test.ts +39 -1
- package/src/__tests__/frame-plan-contract.unit.test.ts +2 -0
- package/src/__tests__/gpu-driven-indirect-raster-evidence.ts +2 -1
- package/src/__tests__/gpu-driven-shadow-views.dawn.test.ts +1 -1
- package/src/__tests__/gpu-driven-shadow-views.integration.test.ts +1 -1
- package/src/__tests__/gpu-driven-view-gpu-evidence.ts +11 -0
- package/src/__tests__/gpu-resource-store-stride.unit.test.ts +12 -0
- package/src/__tests__/gpu-scene.unit.test.ts +91 -0
- package/src/__tests__/instances-store.unit.test.ts +75 -0
- package/src/__tests__/mesh-submission-lifetime.unit.test.ts +31 -0
- package/src/__tests__/motion-blur-compute.dawn.test.ts +583 -0
- package/src/__tests__/prepared-gpu-driven-pbr.integration.test.ts +21 -0
- package/src/__tests__/prepared-graphics-lifecycle.unit.test.ts +86 -0
- package/src/__tests__/public-surface.test-d.ts +8 -0
- package/src/__tests__/render-error-code-owner.test-d.ts +2 -0
- package/src/__tests__/render-feature-error-reporting.unit.test.ts +24 -0
- package/src/__tests__/render-feature-stage-fault-recovery.unit.test.ts +23 -0
- package/src/__tests__/render-scene-temporal-retry.unit.test.ts +31 -0
- package/src/__tests__/renderer-frame-transaction.integration.test.ts +7 -1
- package/src/__tests__/shader-manifest-fixture.ts +4 -4
- package/src/__tests__/skinned-shadow-caster.test.ts +2 -0
- package/src/__tests__/standard-lighting-write.unit.test.ts +2 -1
- package/src/__tests__/standard-pipeline.dawn.test.ts +1 -1
- package/src/__tests__/standard-pipeline.integration.test.ts +18 -8
- package/src/__tests__/temporal-reset-matrix.unit.test.ts +10 -0
- package/src/__tests__/temporal-view.unit.test.ts +2 -1
- package/src/__tests__/typed-pipeline-topology.unit.test.ts +20 -4
- package/src/__tests__/volume-optics-oracle.unit.test.ts +3 -1
- package/src/__tests__/volumetric-fog-budget-recovery.integration.test.ts +177 -13
- package/src/__tests__/volumetric-fog-stage-readback.dawn.test.ts +447 -61
- package/src/__tests__/volumetric-fog-temporal.unit.test.ts +27 -0
- package/src/__tests__/volumetric-fog-topology.unit.test.ts +19 -3
- package/src/assembly/analytic-fog-registration.ts +14 -0
- package/src/assembly/fxaa-registration.ts +17 -0
- package/src/assembly/lod-observation.ts +12 -0
- package/src/assembly/motion-blur-registration.ts +17 -0
- package/src/assembly/renderer-facade.ts +2 -0
- package/src/assembly/temporal-post-process-prewarm.ts +40 -0
- package/src/assembly/webgpu-ready-contract.ts +8 -3
- package/src/assembly/webgpu-ready.ts +181 -245
- package/src/assembly/webgpu-renderer.ts +16 -31
- package/src/bloom-admission.ts +21 -0
- package/src/components/__tests__/motion-blur.unit.test.ts +1 -0
- package/src/components/atmosphere.ts +4 -0
- package/src/components/camera.ts +66 -10
- package/src/components/mesh-filter.ts +11 -9
- package/src/components/motion-blur.ts +1 -0
- package/src/device/gpu-residency.ts +56 -12
- package/src/environment/analytic-fog.ts +44 -0
- package/src/environment/background.ts +2 -2
- package/src/errors/render.ts +14 -1
- package/src/extract/camera.ts +20 -7
- package/src/extract/contracts.ts +7 -0
- package/src/extract/environment.ts +4 -0
- package/src/features/error-reporting.ts +78 -0
- package/src/features/host.ts +25 -12
- package/src/features/motion-blur/__tests__/motion-blur-feature.unit.test.ts +111 -1
- package/src/features/motion-blur/__tests__/motion-blur-params.unit.test.ts +58 -3
- package/src/features/motion-blur/motion-blur-feature.ts +1519 -15
- package/src/features/motion-blur/motion-blur-params.ts +70 -2
- package/src/features/motion-blur/motion-blur-runtime.ts +176 -0
- package/src/features/plan.ts +25 -8
- package/src/features/prepared-gpu-work.ts +86 -35
- package/src/features/render-graph-compute.ts +13 -5
- package/src/features/render-graph-contribution.ts +2 -0
- package/src/gpu-driven/material-artifacts.ts +2 -7
- package/src/gpu-driven/prepared-draw.ts +15 -10
- package/src/gpu-driven/production-raster.ts +83 -7
- package/src/gpu-driven/view-gpu.ts +17 -0
- package/src/gpu-scene.ts +188 -21
- package/src/index.ts +32 -0
- package/src/inspection-types.ts +60 -2
- package/src/instances.ts +80 -1
- package/src/pipeline/standard-post.ts +307 -78
- package/src/pipeline-spec.ts +5 -5
- package/src/points-lines/__tests__/view-ubo.unit.test.ts +2 -1
- package/src/record/__tests__/temporal-payload.unit.test.ts +2 -1
- package/src/record/frame-snapshot.ts +5 -1
- package/src/record/frame-targets.ts +10 -6
- package/src/record/frame.ts +163 -22
- package/src/record/main-pass-geometry.ts +30 -1
- package/src/record/main-pass-sprite-draws.ts +6 -1
- package/src/record/mesh-ssbo.ts +43 -6
- package/src/record/render-context.ts +12 -28
- package/src/record/shadow-pass.ts +6 -1
- package/src/record/skybox-post-pass.ts +122 -196
- package/src/record/typed-frame-graph.ts +153 -9
- package/src/recovery/render-system-candidate.ts +22 -10
- package/src/render-contract.ts +23 -4
- package/src/render-pipeline.ts +6 -1
- package/src/render-system-extract-tail.ts +16 -0
- package/src/render-system-extract.ts +45 -13
- package/src/render-system.ts +255 -120
- package/src/scene/render-scene.ts +85 -4
- package/src/temporal/__tests__/temporal-frame-transaction.unit.test.ts +36 -0
- package/src/temporal/frame-coordinator.ts +8 -1
- package/src/temporal/frame.ts +20 -0
- package/src/temporal/view.ts +13 -2
- package/src/typed-render-graph-primitives.ts +121 -48
- package/src/volume/component.ts +0 -1
- package/src/volume/passes.ts +8 -4
- package/src/volume/resources.ts +1 -1
- package/src/volume/temporal.ts +11 -0
- package/dist/chunk-4NNZQCL3.mjs.map +0 -1
- package/dist/chunk-6TFBXLRJ.mjs.map +0 -1
- package/dist/chunk-DRR7D3KX.mjs.map +0 -1
- package/dist/chunk-GWCEMDVR.mjs.map +0 -1
- package/dist/chunk-JCIRYQ4V.mjs.map +0 -1
- package/dist/chunk-JRIPCMYC.mjs.map +0 -1
|
@@ -2,6 +2,7 @@ import type { MaterialShaderArtifact } from '@forgeax/engine-shader';
|
|
|
2
2
|
import { gpuDrivenMaterialArtifactKey } from '../extract/gpu-driven';
|
|
3
3
|
import type { RenderSystemInternals } from '../record/render-context';
|
|
4
4
|
import type { DispatchEntry, RenderableSnapshot } from '../render-system-extract';
|
|
5
|
+
import { hasRequiredVertexInputs } from './prepared-draw';
|
|
5
6
|
|
|
6
7
|
type ArtifactRow = { readonly source: RenderableSnapshot; readonly renderableIndex: number };
|
|
7
8
|
|
|
@@ -136,13 +137,7 @@ export function collectGpuDrivenMaterialArtifacts({
|
|
|
136
137
|
const shadowVertexInputsMatch =
|
|
137
138
|
pass !== 'shadow' ||
|
|
138
139
|
(receipt !== undefined &&
|
|
139
|
-
receipt.vertexInputs
|
|
140
|
-
receipt.vertexInputs.every(
|
|
141
|
-
(input, index) =>
|
|
142
|
-
input.semantic === prepared.vertexInputs[index]?.semantic &&
|
|
143
|
-
input.location === prepared.vertexInputs[index]?.location &&
|
|
144
|
-
input.format === prepared.vertexInputs[index]?.format,
|
|
145
|
-
));
|
|
140
|
+
hasRequiredVertexInputs(receipt.vertexInputs, prepared.vertexInputs));
|
|
146
141
|
if (
|
|
147
142
|
selectedIdentityArtifact === undefined ||
|
|
148
143
|
receipt === undefined ||
|
|
@@ -75,18 +75,23 @@ function failure(
|
|
|
75
75
|
return { ok: false, error: new GpuDrivenPreparationError(code, detail) };
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
/**
|
|
79
|
+
* A material receipt declares the inputs its shader consumes. Geometry may
|
|
80
|
+
* carry additional authored attributes (for example an unused TEXCOORD_1),
|
|
81
|
+
* so admission checks required locations and formats without treating those
|
|
82
|
+
* producer-owned extras as an ABI mismatch.
|
|
83
|
+
*/
|
|
84
|
+
export function hasRequiredVertexInputs(
|
|
79
85
|
expected: readonly MaterialShaderVertexInput[],
|
|
80
86
|
actual: readonly MaterialShaderVertexInput[],
|
|
81
87
|
): boolean {
|
|
82
|
-
return (
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
)
|
|
88
|
+
return expected.every((input) =>
|
|
89
|
+
actual.some(
|
|
90
|
+
(candidate) =>
|
|
91
|
+
candidate.semantic === input.semantic &&
|
|
92
|
+
candidate.location === input.location &&
|
|
93
|
+
candidate.format === input.format,
|
|
94
|
+
),
|
|
90
95
|
);
|
|
91
96
|
}
|
|
92
97
|
|
|
@@ -167,7 +172,7 @@ export function prepareGpuDrivenDraw(
|
|
|
167
172
|
actual: `${input.geometry.vertexInputs.filter((vertex) => vertex.semantic.startsWith('uv')).length} geometry UV sets`,
|
|
168
173
|
});
|
|
169
174
|
}
|
|
170
|
-
if (!
|
|
175
|
+
if (!hasRequiredVertexInputs(receipt.vertexInputs, input.geometry.vertexInputs)) {
|
|
171
176
|
return failure('vertex-input-mismatch', {
|
|
172
177
|
reason: 'vertex-semantic-mismatch',
|
|
173
178
|
owner: 'geometry',
|
|
@@ -84,7 +84,12 @@ import {
|
|
|
84
84
|
type ShadowViewUpdateInput,
|
|
85
85
|
shadowViewIdentityKey,
|
|
86
86
|
} from './shadow-views';
|
|
87
|
-
import {
|
|
87
|
+
import {
|
|
88
|
+
type GpuDrivenLodSelectionInspection,
|
|
89
|
+
type GpuDrivenLodSubmitIdentity,
|
|
90
|
+
GpuDrivenView,
|
|
91
|
+
selectGpuLodLane,
|
|
92
|
+
} from './view-gpu';
|
|
88
93
|
|
|
89
94
|
export interface StandardPbrSceneCandidate {
|
|
90
95
|
readonly primitiveIndex: number;
|
|
@@ -2246,6 +2251,8 @@ export class GpuDrivenProduction {
|
|
|
2246
2251
|
readonly activeEntityRevision?: number;
|
|
2247
2252
|
/** Total extracted candidates, including facet-suppressed rows. */
|
|
2248
2253
|
readonly telemetryCandidateCount?: number;
|
|
2254
|
+
/** Public receipt identity for the GPU telemetry copy encoded this frame. */
|
|
2255
|
+
readonly telemetrySubmit?: GpuDrivenLodSubmitIdentity;
|
|
2249
2256
|
readonly occlusion?: OcclusionFrameProjection;
|
|
2250
2257
|
/** Per-frame deltas from the existing RenderScene/GPU Scene/palette owners. */
|
|
2251
2258
|
readonly structureMetrics?: GpuDrivenStructureMetrics;
|
|
@@ -2560,6 +2567,7 @@ export class GpuDrivenProduction {
|
|
|
2560
2567
|
}
|
|
2561
2568
|
const updated = this.view.update(filtered.plan, scene.scene, activeFrustum(input.camera));
|
|
2562
2569
|
if (!updated.ok) return updated;
|
|
2570
|
+
this.view.setTelemetrySubmit(input.telemetrySubmit);
|
|
2563
2571
|
this.telemetryPrepared = true;
|
|
2564
2572
|
const view = this.view;
|
|
2565
2573
|
const visibleBuffer = view.visibleBuffer;
|
|
@@ -3638,11 +3646,49 @@ export class GpuDrivenProduction {
|
|
|
3638
3646
|
this.laneReason = 'overflow';
|
|
3639
3647
|
}
|
|
3640
3648
|
}
|
|
3641
|
-
if (selection === undefined
|
|
3649
|
+
if (selection === undefined) return selection;
|
|
3642
3650
|
if (filtered === undefined) return selection;
|
|
3643
|
-
|
|
3644
|
-
|
|
3651
|
+
type LodBatchAttribution = {
|
|
3652
|
+
readonly batch: GpuDrivenBatch;
|
|
3653
|
+
readonly worldKey: number;
|
|
3654
|
+
readonly primitiveSlot: number;
|
|
3655
|
+
readonly slotGeneration: number;
|
|
3656
|
+
};
|
|
3657
|
+
const preparedByBatchId = new Map<number, LodBatchAttribution>(
|
|
3658
|
+
filtered.batches.map((prepared) => [
|
|
3659
|
+
prepared.batch.batchId,
|
|
3660
|
+
{
|
|
3661
|
+
batch: prepared.batch,
|
|
3662
|
+
worldKey: prepared.worldKey,
|
|
3663
|
+
primitiveSlot: prepared.primitiveSlot,
|
|
3664
|
+
slotGeneration: prepared.slotGeneration,
|
|
3665
|
+
},
|
|
3666
|
+
]),
|
|
3645
3667
|
);
|
|
3668
|
+
// Selector-only batches remain in filtered.plan so their GPU counters
|
|
3669
|
+
// can describe candidates suppressed from the raster admission lane.
|
|
3670
|
+
// They have no PreparedBatch entry because no draw is encoded, but they
|
|
3671
|
+
// still need the same World join for receipt attribution. Recover that
|
|
3672
|
+
// metadata from the stable primitive slot instead of dropping the whole
|
|
3673
|
+
// readback when the first such batch is encountered.
|
|
3674
|
+
const filteredSlots = this.filteredCache?.slots;
|
|
3675
|
+
const filteredWorldKeys = this.filteredCache?.worldKeys;
|
|
3676
|
+
if (filteredSlots !== undefined) {
|
|
3677
|
+
const slotByPrimitive = new Map(filteredSlots.map((slot) => [slot.slot, slot] as const));
|
|
3678
|
+
for (const batch of filtered.plan.batches) {
|
|
3679
|
+
if (preparedByBatchId.has(batch.batchId)) continue;
|
|
3680
|
+
const candidate = batch.candidates[0];
|
|
3681
|
+
const slot =
|
|
3682
|
+
candidate === undefined ? undefined : slotByPrimitive.get(candidate.primitiveIndex);
|
|
3683
|
+
if (slot === undefined) continue;
|
|
3684
|
+
preparedByBatchId.set(batch.batchId, {
|
|
3685
|
+
batch,
|
|
3686
|
+
worldKey: filteredWorldKeys?.[slot.snapshot.worldId] ?? slot.snapshot.worldId,
|
|
3687
|
+
primitiveSlot: slot.slot,
|
|
3688
|
+
slotGeneration: slot.generation,
|
|
3689
|
+
});
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3646
3692
|
const worldSelections = new Map<
|
|
3647
3693
|
number,
|
|
3648
3694
|
{
|
|
@@ -3655,9 +3701,33 @@ export class GpuDrivenProduction {
|
|
|
3655
3701
|
readonly lodHistogram: Map<number, number>;
|
|
3656
3702
|
}
|
|
3657
3703
|
>();
|
|
3704
|
+
const lodHistogram = new Map<number, number>();
|
|
3705
|
+
// The selector visits the full captured plan, including candidates that
|
|
3706
|
+
// the CPU visibility projection suppressed. Use that same plan for the
|
|
3707
|
+
// LOD denominator so World attribution cannot fail merely because the
|
|
3708
|
+
// two lanes have different frustum scopes.
|
|
3709
|
+
let lodCandidateCount = 0;
|
|
3710
|
+
let lodVisible = 0;
|
|
3658
3711
|
for (const batchSelection of selection.batches) {
|
|
3659
3712
|
const prepared = preparedByBatchId.get(batchSelection.batchId);
|
|
3660
|
-
if (prepared === undefined) return
|
|
3713
|
+
if (prepared === undefined) return undefined;
|
|
3714
|
+
// The GPU selector also visits ordinary GPU-driven meshes so one
|
|
3715
|
+
// compact compute path can populate their indirect args. Those rows
|
|
3716
|
+
// are level-0 by construction, but they are not LOD candidates and
|
|
3717
|
+
// must not enter receipt-bound LOD attribution. Batch topology keeps
|
|
3718
|
+
// LOD coverage/ranges in the grouping key, so a batch is expected to
|
|
3719
|
+
// be homogeneous; fail closed if a producer violates that invariant
|
|
3720
|
+
// instead of guessing a split for aggregate counters.
|
|
3721
|
+
const lodCandidateRows = prepared.batch.candidates.filter(
|
|
3722
|
+
(candidate) => (candidate.lodCoverages?.length ?? 0) > 1,
|
|
3723
|
+
);
|
|
3724
|
+
if (lodCandidateRows.length === 0) continue;
|
|
3725
|
+
if (lodCandidateRows.length !== batchSelection.candidateCount) return undefined;
|
|
3726
|
+
lodCandidateCount += batchSelection.candidateCount;
|
|
3727
|
+
lodVisible += batchSelection.visible;
|
|
3728
|
+
for (const row of batchSelection.lodHistogram) {
|
|
3729
|
+
lodHistogram.set(row.level, (lodHistogram.get(row.level) ?? 0) + row.count);
|
|
3730
|
+
}
|
|
3661
3731
|
let aggregate = worldSelections.get(prepared.worldKey);
|
|
3662
3732
|
if (aggregate === undefined) {
|
|
3663
3733
|
aggregate = {
|
|
@@ -3683,8 +3753,14 @@ export class GpuDrivenProduction {
|
|
|
3683
3753
|
}
|
|
3684
3754
|
return {
|
|
3685
3755
|
...selection,
|
|
3686
|
-
candidateCount:
|
|
3687
|
-
|
|
3756
|
+
candidateCount: lodCandidateCount,
|
|
3757
|
+
visible: lodVisible,
|
|
3758
|
+
occluded: Math.max(0, lodCandidateCount - lodVisible),
|
|
3759
|
+
lodHistogram: Object.freeze(
|
|
3760
|
+
[...lodHistogram.entries()]
|
|
3761
|
+
.sort(([left], [right]) => left - right)
|
|
3762
|
+
.map(([level, count]) => Object.freeze({ level, count })),
|
|
3763
|
+
),
|
|
3688
3764
|
worldSelections: Object.freeze(
|
|
3689
3765
|
[...worldSelections.values()].map((aggregate) =>
|
|
3690
3766
|
Object.freeze({
|
|
@@ -302,8 +302,14 @@ type LodTelemetryCopy = {
|
|
|
302
302
|
readonly buffers: ViewBuffers;
|
|
303
303
|
readonly plan: SubmissionPlan;
|
|
304
304
|
readonly resourceGeneration: number;
|
|
305
|
+
readonly submit?: GpuDrivenLodSubmitIdentity;
|
|
305
306
|
};
|
|
306
307
|
|
|
308
|
+
export interface GpuDrivenLodSubmitIdentity {
|
|
309
|
+
readonly frameId: number;
|
|
310
|
+
readonly deviceGeneration: number;
|
|
311
|
+
}
|
|
312
|
+
|
|
307
313
|
export interface GpuDrivenViewGraphResources {
|
|
308
314
|
readonly primitive: GraphBuffer;
|
|
309
315
|
readonly instance: GraphBuffer;
|
|
@@ -369,6 +375,8 @@ export interface GpuDrivenLodSelectionInspection {
|
|
|
369
375
|
}[];
|
|
370
376
|
readonly geometryWork: number;
|
|
371
377
|
readonly rootGeometryWork: number;
|
|
378
|
+
/** Public receipt identity captured when the telemetry copy was encoded. */
|
|
379
|
+
readonly submit?: GpuDrivenLodSubmitIdentity;
|
|
372
380
|
}
|
|
373
381
|
|
|
374
382
|
function nextCapacity(required: number): number {
|
|
@@ -431,6 +439,8 @@ export class GpuDrivenView {
|
|
|
431
439
|
private resourceGeneration = 0;
|
|
432
440
|
/** The last readback copy that was actually encoded and is not consumed. */
|
|
433
441
|
private telemetryPending: LodTelemetryCopy | undefined;
|
|
442
|
+
/** Receipt identity for the copy pass encoded by the current frame. */
|
|
443
|
+
private telemetrySubmit: GpuDrivenLodSubmitIdentity | undefined;
|
|
434
444
|
/** Coalesce observers while the readback buffer is map-pending. */
|
|
435
445
|
private lodSelectionReadback:
|
|
436
446
|
| {
|
|
@@ -449,6 +459,10 @@ export class GpuDrivenView {
|
|
|
449
459
|
private readonly labelPrefix: string,
|
|
450
460
|
) {}
|
|
451
461
|
|
|
462
|
+
setTelemetrySubmit(identity: GpuDrivenLodSubmitIdentity | undefined): void {
|
|
463
|
+
this.telemetrySubmit = identity;
|
|
464
|
+
}
|
|
465
|
+
|
|
452
466
|
static create(input: {
|
|
453
467
|
readonly device: RhiDevice;
|
|
454
468
|
readonly shaderModuleFactory: PipelineBuilderShaderModuleFactory;
|
|
@@ -848,6 +862,7 @@ export class GpuDrivenView {
|
|
|
848
862
|
),
|
|
849
863
|
geometryWork,
|
|
850
864
|
rootGeometryWork,
|
|
865
|
+
...(copy.submit === undefined ? {} : { submit: copy.submit }),
|
|
851
866
|
});
|
|
852
867
|
this.telemetryPending = undefined;
|
|
853
868
|
return this.lodSelection;
|
|
@@ -1048,6 +1063,7 @@ export class GpuDrivenView {
|
|
|
1048
1063
|
buffers,
|
|
1049
1064
|
plan: submittedPlan,
|
|
1050
1065
|
resourceGeneration: graphResourceGeneration,
|
|
1066
|
+
...(this.telemetrySubmit === undefined ? {} : { submit: this.telemetrySubmit }),
|
|
1051
1067
|
};
|
|
1052
1068
|
}
|
|
1053
1069
|
},
|
|
@@ -1080,6 +1096,7 @@ export class GpuDrivenView {
|
|
|
1080
1096
|
this.sceneMaterial = undefined;
|
|
1081
1097
|
this.sceneCapacity = 0;
|
|
1082
1098
|
this.telemetryPending = undefined;
|
|
1099
|
+
this.telemetrySubmit = undefined;
|
|
1083
1100
|
this.lodSelectionReadback = undefined;
|
|
1084
1101
|
this.lodSelection = undefined;
|
|
1085
1102
|
this.destroyAfterSubmittedWork(buffers);
|
package/src/gpu-scene.ts
CHANGED
|
@@ -17,6 +17,9 @@ import type { RenderSceneApplyResult, RenderSceneSlot } from './scene/render-sce
|
|
|
17
17
|
|
|
18
18
|
export type { GpuSceneInspection } from './inspection-types';
|
|
19
19
|
|
|
20
|
+
type GpuSceneTemporalSnapshot = NonNullable<RenderSceneSlot['snapshot']['temporal']>;
|
|
21
|
+
type GpuSceneTemporalResolver = (slot: RenderSceneSlot) => GpuSceneTemporalSnapshot | undefined;
|
|
22
|
+
|
|
20
23
|
const PRIMITIVE = GPU_SCENE_LAYOUTS.primitive;
|
|
21
24
|
const INSTANCE = GPU_SCENE_LAYOUTS.instance;
|
|
22
25
|
const TRANSFORM = GPU_SCENE_LAYOUTS.transform;
|
|
@@ -219,6 +222,56 @@ function coalesceSlots(
|
|
|
219
222
|
return ranges;
|
|
220
223
|
}
|
|
221
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Build a lookup from the current instance identity to its submitted ordinal.
|
|
227
|
+
* The GPU table is physically compacted by ordinal, so this small CPU lookup
|
|
228
|
+
* is the seam that keeps a stable logical instance from inheriting another
|
|
229
|
+
* row's previous transform after reorder. A missing or duplicate generation
|
|
230
|
+
* deliberately returns no proof and makes the caller seed the current pose.
|
|
231
|
+
*/
|
|
232
|
+
function previousInstanceOrdinals(
|
|
233
|
+
current: NonNullable<RenderSceneSlot['snapshot']['instances']> | undefined,
|
|
234
|
+
temporal: GpuSceneTemporalSnapshot | undefined,
|
|
235
|
+
): Map<number, number> | undefined {
|
|
236
|
+
const previous = temporal?.previousInstances;
|
|
237
|
+
const currentGenerations = current?.generations;
|
|
238
|
+
const previousGenerations = previous?.generations;
|
|
239
|
+
if (
|
|
240
|
+
temporal?.motionValid !== true ||
|
|
241
|
+
current === undefined ||
|
|
242
|
+
previous === undefined ||
|
|
243
|
+
currentGenerations === undefined ||
|
|
244
|
+
previousGenerations === undefined ||
|
|
245
|
+
currentGenerations.length !== current.instanceCount ||
|
|
246
|
+
previousGenerations.length !== previous.instanceCount ||
|
|
247
|
+
current.transforms.length < current.instanceCount * 16 ||
|
|
248
|
+
previous.transforms.length < previous.instanceCount * 16
|
|
249
|
+
) {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
const currentSet = new Set<number>();
|
|
253
|
+
for (const generation of currentGenerations) {
|
|
254
|
+
if (generation === 0 || currentSet.has(generation)) return undefined;
|
|
255
|
+
currentSet.add(generation);
|
|
256
|
+
}
|
|
257
|
+
const ordinals = new Map<number, number>();
|
|
258
|
+
for (let ordinal = 0; ordinal < previousGenerations.length; ordinal += 1) {
|
|
259
|
+
const generation = previousGenerations[ordinal] ?? 0;
|
|
260
|
+
if (generation === 0 || ordinals.has(generation)) return undefined;
|
|
261
|
+
ordinals.set(generation, ordinal);
|
|
262
|
+
}
|
|
263
|
+
return ordinals;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function instanceIdentityChanged(
|
|
267
|
+
resident: Uint32Array | undefined,
|
|
268
|
+
current: Uint32Array | undefined,
|
|
269
|
+
ordinal: number,
|
|
270
|
+
): boolean {
|
|
271
|
+
if (resident === undefined || current === undefined) return resident !== current;
|
|
272
|
+
return resident[ordinal] !== current[ordinal];
|
|
273
|
+
}
|
|
274
|
+
|
|
222
275
|
/** Persistent GPU tables derived only from renderer projection slots. */
|
|
223
276
|
export class GpuScene {
|
|
224
277
|
private disposed = false;
|
|
@@ -238,6 +291,8 @@ export class GpuScene {
|
|
|
238
291
|
private sceneContentRevision = 0;
|
|
239
292
|
private identityUploaded = false;
|
|
240
293
|
private readonly allocations = new Map<number, PrimitiveAllocation>();
|
|
294
|
+
/** Current stable instance identities retained beside compact GPU rows. */
|
|
295
|
+
private readonly instanceGenerations = new Map<number, Uint32Array>();
|
|
241
296
|
private readonly instances = new StableRangeAllocator();
|
|
242
297
|
private readonly transforms = new StableRangeAllocator();
|
|
243
298
|
private readonly draws = new StableRangeAllocator();
|
|
@@ -322,7 +377,10 @@ export class GpuScene {
|
|
|
322
377
|
return allocation.materialStart + materialSlot;
|
|
323
378
|
}
|
|
324
379
|
|
|
325
|
-
sync(
|
|
380
|
+
sync(
|
|
381
|
+
delta: RenderSceneApplyResult,
|
|
382
|
+
temporalResolver?: GpuSceneTemporalResolver,
|
|
383
|
+
): Result<GpuSceneSyncResult, RhiError> {
|
|
326
384
|
for (const transformIndex of this.pendingTemporalUploads) {
|
|
327
385
|
this.writesByTable.transform.push(transformIndex);
|
|
328
386
|
}
|
|
@@ -393,11 +451,11 @@ export class GpuScene {
|
|
|
393
451
|
for (const record of publications) {
|
|
394
452
|
const reset = newSlots.has(record.slot) || allocationResets.has(record.slot);
|
|
395
453
|
if (reset || contentUpdatedSlotIds.has(record.slot)) {
|
|
396
|
-
this.writeSlot(record, reset, writesByTable);
|
|
454
|
+
this.writeSlot(record, reset, writesByTable, temporalResolver?.(record));
|
|
397
455
|
continue;
|
|
398
456
|
}
|
|
399
457
|
if (instanceUpdatedSlotIds.has(record.slot))
|
|
400
|
-
this.writeInstanceTransforms(record, writesByTable);
|
|
458
|
+
this.writeInstanceTransforms(record, writesByTable, temporalResolver?.(record));
|
|
401
459
|
this.writeRootTransform(record, false, writesByTable);
|
|
402
460
|
}
|
|
403
461
|
const uploaded = this.uploadRows(writesByTable);
|
|
@@ -459,6 +517,7 @@ export class GpuScene {
|
|
|
459
517
|
|
|
460
518
|
rebuild(slots: readonly RenderSceneSlot[]): Result<GpuSceneSyncResult, RhiError> {
|
|
461
519
|
this.allocations.clear();
|
|
520
|
+
this.instanceGenerations.clear();
|
|
462
521
|
this.instances.reset();
|
|
463
522
|
this.transforms.reset(1);
|
|
464
523
|
this.draws.reset();
|
|
@@ -551,6 +610,7 @@ export class GpuScene {
|
|
|
551
610
|
record: RenderSceneSlot,
|
|
552
611
|
resetPrevious: boolean,
|
|
553
612
|
writes: Record<GpuSceneTableName, number[]>,
|
|
613
|
+
temporal?: GpuSceneTemporalSnapshot,
|
|
554
614
|
): void {
|
|
555
615
|
const allocation = this.allocations.get(record.slot);
|
|
556
616
|
if (allocation === undefined) throw new RangeError('GPU Scene allocation unavailable');
|
|
@@ -610,6 +670,11 @@ export class GpuScene {
|
|
|
610
670
|
|
|
611
671
|
const instance = new DataView(this.tableBytes.instance);
|
|
612
672
|
const transform = this.transformView;
|
|
673
|
+
const instances = record.snapshot.instances;
|
|
674
|
+
const previousInstances = temporal?.previousInstances;
|
|
675
|
+
const previousOrdinals = previousInstanceOrdinals(instances, temporal);
|
|
676
|
+
const currentGenerations = instances?.generations;
|
|
677
|
+
const residentGenerations = this.instanceGenerations.get(record.slot);
|
|
613
678
|
for (let ordinal = 0; ordinal < allocation.instanceCount; ordinal += 1) {
|
|
614
679
|
const instanceIndex = allocation.instanceStart + ordinal;
|
|
615
680
|
const instanceOffset = instanceIndex * INSTANCE.stride;
|
|
@@ -636,7 +701,7 @@ export class GpuScene {
|
|
|
636
701
|
writeChangedU32(instance, instanceOffset + offset(INSTANCE, 'flags'), PRIMITIVE_ACTIVE) ||
|
|
637
702
|
metadataChanged;
|
|
638
703
|
if (allocation.instanceTransformCount > 0) {
|
|
639
|
-
const transforms =
|
|
704
|
+
const transforms = instances?.transforms;
|
|
640
705
|
const hasLocal = transforms !== undefined && transforms.length >= (ordinal + 1) * 16;
|
|
641
706
|
const local = hasLocal ? transforms : IDENTITY_MATRIX;
|
|
642
707
|
const sourceOffset = hasLocal ? ordinal * 16 : 0;
|
|
@@ -647,19 +712,66 @@ export class GpuScene {
|
|
|
647
712
|
local,
|
|
648
713
|
sourceOffset,
|
|
649
714
|
);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
);
|
|
657
|
-
|
|
658
|
-
|
|
715
|
+
const identityChanged = instanceIdentityChanged(
|
|
716
|
+
residentGenerations,
|
|
717
|
+
currentGenerations,
|
|
718
|
+
ordinal,
|
|
719
|
+
);
|
|
720
|
+
if (resetPrevious) {
|
|
721
|
+
const previousOrdinal = previousOrdinals?.get(currentGenerations?.[ordinal] ?? 0);
|
|
722
|
+
const previousSource =
|
|
723
|
+
previousOrdinal === undefined || previousInstances === undefined
|
|
724
|
+
? undefined
|
|
725
|
+
: previousInstances.transforms;
|
|
726
|
+
if (previousSource === undefined || previousOrdinal === undefined) {
|
|
727
|
+
writeChangedMat4(
|
|
728
|
+
transform,
|
|
729
|
+
localOffset + offset(TRANSFORM, 'previousWorld'),
|
|
730
|
+
local,
|
|
731
|
+
sourceOffset,
|
|
732
|
+
);
|
|
733
|
+
} else {
|
|
734
|
+
writeChangedMat4(
|
|
735
|
+
transform,
|
|
736
|
+
localOffset + offset(TRANSFORM, 'previousWorld'),
|
|
737
|
+
previousSource,
|
|
738
|
+
previousOrdinal * 16,
|
|
739
|
+
);
|
|
740
|
+
this.pendingTemporalTransforms.add(transformIndex);
|
|
741
|
+
}
|
|
742
|
+
} else if (changed || identityChanged) {
|
|
743
|
+
const previousOrdinal = previousOrdinals?.get(currentGenerations?.[ordinal] ?? 0);
|
|
744
|
+
const previousSource =
|
|
745
|
+
previousOrdinal === undefined || previousInstances === undefined
|
|
746
|
+
? undefined
|
|
747
|
+
: previousInstances.transforms;
|
|
748
|
+
if (previousSource === undefined || previousOrdinal === undefined) {
|
|
749
|
+
// A changed row without an identity proof must not borrow the
|
|
750
|
+
// compacted row's old velocity. Seed only this row and keep other
|
|
751
|
+
// stable instances eligible for motion.
|
|
752
|
+
writeChangedMat4(
|
|
753
|
+
transform,
|
|
754
|
+
localOffset + offset(TRANSFORM, 'previousWorld'),
|
|
755
|
+
local,
|
|
756
|
+
sourceOffset,
|
|
757
|
+
);
|
|
758
|
+
} else {
|
|
759
|
+
writeChangedMat4(
|
|
760
|
+
transform,
|
|
761
|
+
localOffset + offset(TRANSFORM, 'previousWorld'),
|
|
762
|
+
previousSource,
|
|
763
|
+
previousOrdinal * 16,
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
this.pendingTemporalTransforms.add(transformIndex);
|
|
767
|
+
}
|
|
768
|
+
if (changed || identityChanged || resetPrevious) writes.transform.push(transformIndex);
|
|
659
769
|
}
|
|
660
770
|
if (metadataChanged || resetPrevious) writes.instance.push(instanceIndex);
|
|
661
771
|
}
|
|
662
772
|
|
|
773
|
+
this.rememberInstanceGenerations(record.slot, currentGenerations, allocation.instanceCount);
|
|
774
|
+
|
|
663
775
|
this.writeRootTransform(record, resetPrevious, writes);
|
|
664
776
|
writes.primitive.push(record.slot);
|
|
665
777
|
|
|
@@ -740,6 +852,7 @@ export class GpuScene {
|
|
|
740
852
|
private writeInstanceTransforms(
|
|
741
853
|
record: RenderSceneSlot,
|
|
742
854
|
writes: Record<GpuSceneTableName, number[]>,
|
|
855
|
+
temporal?: GpuSceneTemporalSnapshot,
|
|
743
856
|
): void {
|
|
744
857
|
const allocation = this.allocations.get(record.slot);
|
|
745
858
|
const instances = record.snapshot.instances;
|
|
@@ -749,21 +862,74 @@ export class GpuScene {
|
|
|
749
862
|
if (allocation.instanceTransformCount !== instances.instanceCount) {
|
|
750
863
|
throw new RangeError('GPU Scene instance update changed instance count');
|
|
751
864
|
}
|
|
865
|
+
const previousOrdinals = previousInstanceOrdinals(instances, temporal);
|
|
866
|
+
const previousInstances = temporal?.previousInstances;
|
|
867
|
+
const currentGenerations = instances.generations;
|
|
868
|
+
const residentGenerations = this.instanceGenerations.get(record.slot);
|
|
752
869
|
for (let ordinal = 0; ordinal < allocation.instanceTransformCount; ordinal += 1) {
|
|
753
870
|
const transformIndex = allocation.instanceTransformStart + ordinal;
|
|
754
871
|
const transformOffset = transformIndex * TRANSFORM.stride;
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
872
|
+
const changed = writeChangedMat4(
|
|
873
|
+
this.transformView,
|
|
874
|
+
transformOffset + offset(TRANSFORM, 'currentWorld'),
|
|
875
|
+
instances.transforms,
|
|
876
|
+
ordinal * 16,
|
|
877
|
+
);
|
|
878
|
+
const identityChanged = instanceIdentityChanged(
|
|
879
|
+
residentGenerations,
|
|
880
|
+
currentGenerations,
|
|
881
|
+
ordinal,
|
|
882
|
+
);
|
|
883
|
+
if (changed || identityChanged) {
|
|
884
|
+
const previousOrdinal = previousOrdinals?.get(currentGenerations?.[ordinal] ?? 0);
|
|
885
|
+
const previousSource =
|
|
886
|
+
previousOrdinal === undefined || previousInstances === undefined
|
|
887
|
+
? undefined
|
|
888
|
+
: previousInstances.transforms;
|
|
889
|
+
if (previousSource === undefined || previousOrdinal === undefined) {
|
|
890
|
+
writeChangedMat4(
|
|
891
|
+
this.transformView,
|
|
892
|
+
transformOffset + offset(TRANSFORM, 'previousWorld'),
|
|
893
|
+
instances.transforms,
|
|
894
|
+
ordinal * 16,
|
|
895
|
+
);
|
|
896
|
+
} else {
|
|
897
|
+
writeChangedMat4(
|
|
898
|
+
this.transformView,
|
|
899
|
+
transformOffset + offset(TRANSFORM, 'previousWorld'),
|
|
900
|
+
previousSource,
|
|
901
|
+
previousOrdinal * 16,
|
|
902
|
+
);
|
|
903
|
+
}
|
|
763
904
|
this.pendingTemporalTransforms.add(transformIndex);
|
|
764
905
|
writes.transform.push(transformIndex);
|
|
765
906
|
}
|
|
766
907
|
}
|
|
908
|
+
this.rememberInstanceGenerations(
|
|
909
|
+
record.slot,
|
|
910
|
+
currentGenerations,
|
|
911
|
+
allocation.instanceTransformCount,
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
private rememberInstanceGenerations(
|
|
916
|
+
slot: number,
|
|
917
|
+
generations: Uint32Array | undefined,
|
|
918
|
+
instanceCount: number,
|
|
919
|
+
): void {
|
|
920
|
+
if (generations === undefined || generations.length !== instanceCount) {
|
|
921
|
+
this.instanceGenerations.delete(slot);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const resident = this.instanceGenerations.get(slot);
|
|
925
|
+
if (
|
|
926
|
+
resident !== undefined &&
|
|
927
|
+
resident.length === generations.length &&
|
|
928
|
+
resident.every((generation, ordinal) => generation === generations[ordinal])
|
|
929
|
+
) {
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
this.instanceGenerations.set(slot, generations.slice());
|
|
767
933
|
}
|
|
768
934
|
|
|
769
935
|
private writeRootTransform(
|
|
@@ -795,6 +961,7 @@ export class GpuScene {
|
|
|
795
961
|
}
|
|
796
962
|
const allocation = this.allocations.get(slot);
|
|
797
963
|
if (allocation === undefined) return;
|
|
964
|
+
this.instanceGenerations.delete(slot);
|
|
798
965
|
this.clearAllocation(allocation, writes);
|
|
799
966
|
this.allocations.delete(slot);
|
|
800
967
|
}
|
package/src/index.ts
CHANGED
|
@@ -37,6 +37,14 @@ export {
|
|
|
37
37
|
ANTIALIAS_TAA,
|
|
38
38
|
BLOOM_DISABLED,
|
|
39
39
|
BLOOM_ENABLED,
|
|
40
|
+
CAMERA_BLOOM_INTENSITY_MAX,
|
|
41
|
+
CAMERA_BLOOM_INTENSITY_MIN,
|
|
42
|
+
CAMERA_BLOOM_SCATTER_MAX,
|
|
43
|
+
CAMERA_BLOOM_SCATTER_MIN,
|
|
44
|
+
CAMERA_BLOOM_SOFT_KNEE_MAX,
|
|
45
|
+
CAMERA_BLOOM_SOFT_KNEE_MIN,
|
|
46
|
+
CAMERA_BLOOM_THRESHOLD_MAX,
|
|
47
|
+
CAMERA_BLOOM_THRESHOLD_MIN,
|
|
40
48
|
CAMERA_EXPOSURE_MODE_AUTO,
|
|
41
49
|
CAMERA_EXPOSURE_MODE_MANUAL,
|
|
42
50
|
CAMERA_PROJECTION_ORTHOGRAPHIC,
|
|
@@ -59,6 +67,7 @@ export {
|
|
|
59
67
|
TONEMAP_NONE,
|
|
60
68
|
TONEMAP_REINHARD,
|
|
61
69
|
TONEMAP_REINHARD_EXTENDED,
|
|
70
|
+
validateCameraBloom,
|
|
62
71
|
validateCameraColorGrading,
|
|
63
72
|
validateCameraExposure,
|
|
64
73
|
} from './components/camera.js';
|
|
@@ -155,6 +164,10 @@ export {
|
|
|
155
164
|
createDynamicGeometryLifecycle,
|
|
156
165
|
DynamicGeometryError,
|
|
157
166
|
} from './dynamic-geometry.js';
|
|
167
|
+
// The environment selector owns this structured recovery error. Keep the
|
|
168
|
+
// class on the public environment path so consumers can branch by `code` and
|
|
169
|
+
// inspect its typed `detail` without importing an owner-private module.
|
|
170
|
+
export { SunCardinalityError } from './environment/frame.js';
|
|
158
171
|
export type {
|
|
159
172
|
EnvironmentInspection,
|
|
160
173
|
EnvironmentInspectionFailure,
|
|
@@ -225,6 +238,22 @@ export {
|
|
|
225
238
|
validateDepthOfFieldFrameParams,
|
|
226
239
|
validateDepthOfFieldParams,
|
|
227
240
|
} from './features/depth-of-field/depth-of-field-params.js';
|
|
241
|
+
export type {
|
|
242
|
+
MotionBlurInvalidParamsDetail,
|
|
243
|
+
MotionBlurParams,
|
|
244
|
+
MotionBlurSampleTier,
|
|
245
|
+
} from './features/motion-blur/motion-blur-params.js';
|
|
246
|
+
export {
|
|
247
|
+
DEFAULT_MOTION_BLUR_PARAMS,
|
|
248
|
+
effectiveMotionBlurSampleCount,
|
|
249
|
+
isMotionBlurIntervalValid,
|
|
250
|
+
MotionBlurValidationError,
|
|
251
|
+
motionBlurExposureScale,
|
|
252
|
+
motionBlurSampleDelta,
|
|
253
|
+
motionBlurTemporalDemand,
|
|
254
|
+
resolveMotionBlurParams,
|
|
255
|
+
validateMotionBlurParams,
|
|
256
|
+
} from './features/motion-blur/motion-blur-params.js';
|
|
228
257
|
export type {
|
|
229
258
|
RenderFeatureLogicalTarget,
|
|
230
259
|
RenderFeatureMaterialShaderBindingContract,
|
|
@@ -273,7 +302,10 @@ export type {
|
|
|
273
302
|
LightInspection,
|
|
274
303
|
LodOcclusionInspection,
|
|
275
304
|
MaterialTextureSourceInspection,
|
|
305
|
+
MotionBlurExecutionPass,
|
|
306
|
+
MotionBlurExecutionReceipt,
|
|
276
307
|
MotionBlurInspection,
|
|
308
|
+
MotionBlurInspectionLane,
|
|
277
309
|
MotionBlurInspectionStatus,
|
|
278
310
|
ReflectionFallbackFailureStage,
|
|
279
311
|
ReflectionFallbackInspection,
|