@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
package/src/inspection-types.ts
CHANGED
|
@@ -92,6 +92,10 @@ export type BloomGraphStatus = 'empty' | 'valid' | 'invalid';
|
|
|
92
92
|
export interface BloomInspection {
|
|
93
93
|
readonly graphStatus: BloomGraphStatus;
|
|
94
94
|
readonly enabled: boolean;
|
|
95
|
+
readonly levelCount: number;
|
|
96
|
+
readonly levelDimensions: readonly { readonly width: number; readonly height: number }[];
|
|
97
|
+
readonly downsamplePassCount: number;
|
|
98
|
+
readonly upsamplePassCount: number;
|
|
95
99
|
readonly targetCount: number;
|
|
96
100
|
readonly targetBytes: number;
|
|
97
101
|
readonly resourceCount: number;
|
|
@@ -99,10 +103,30 @@ export interface BloomInspection {
|
|
|
99
103
|
readonly encodeCount: number;
|
|
100
104
|
readonly bindGroupCount: number;
|
|
101
105
|
readonly uploadCount: number;
|
|
106
|
+
/** Descriptor-derived bytes for the active Bloom target child in the compiled graph. */
|
|
102
107
|
readonly residentChildBytes: number;
|
|
103
108
|
readonly generation: number;
|
|
104
109
|
readonly state: 'off' | 'active' | 'retiring';
|
|
105
110
|
}
|
|
111
|
+
|
|
112
|
+
export const emptyBloomInspection = (): BloomInspection => ({
|
|
113
|
+
graphStatus: 'empty',
|
|
114
|
+
enabled: false,
|
|
115
|
+
levelCount: 0,
|
|
116
|
+
levelDimensions: [],
|
|
117
|
+
downsamplePassCount: 0,
|
|
118
|
+
upsamplePassCount: 0,
|
|
119
|
+
targetCount: 0,
|
|
120
|
+
targetBytes: 0,
|
|
121
|
+
resourceCount: 0,
|
|
122
|
+
passCount: 0,
|
|
123
|
+
encodeCount: 0,
|
|
124
|
+
bindGroupCount: 0,
|
|
125
|
+
uploadCount: 0,
|
|
126
|
+
residentChildBytes: 0,
|
|
127
|
+
generation: 0,
|
|
128
|
+
state: 'off',
|
|
129
|
+
});
|
|
106
130
|
export type { TransmissionInspection } from './transmission/inspection';
|
|
107
131
|
|
|
108
132
|
export interface BatchTopologyInspection {
|
|
@@ -555,7 +579,29 @@ export interface TemporalTargetInspection {
|
|
|
555
579
|
}
|
|
556
580
|
|
|
557
581
|
/** Detached, bounded Motion Blur facts; no graph, device, target, or history handle. */
|
|
558
|
-
export type MotionBlurInspectionStatus = 'off' | 'active' | 'reset' | 'invalid';
|
|
582
|
+
export type MotionBlurInspectionStatus = 'off' | 'active' | 'reset' | 'limited' | 'invalid';
|
|
583
|
+
export type MotionBlurInspectionLane = 'compute' | 'raster-limited';
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Submission receipt for the admitted compute lane. This is derived from the
|
|
587
|
+
* compiled graph and the resolved feature dispatches after a successful
|
|
588
|
+
* queue submit; capability intent alone never produces this receipt.
|
|
589
|
+
*/
|
|
590
|
+
export interface MotionBlurExecutionPass {
|
|
591
|
+
readonly name: 'motion-blur-tile-summary' | 'motion-blur';
|
|
592
|
+
readonly kind: 'compute';
|
|
593
|
+
readonly entryPoint: 'tile_summary' | 'reconstruct';
|
|
594
|
+
readonly workgroups: readonly [number, number, number];
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export interface MotionBlurExecutionReceipt {
|
|
598
|
+
readonly frameId: number;
|
|
599
|
+
readonly deviceGeneration: number;
|
|
600
|
+
readonly graphGeneration: number;
|
|
601
|
+
readonly program: 'motion-blur-compute';
|
|
602
|
+
readonly outputFormat: 'rgba16float';
|
|
603
|
+
readonly passes: readonly [MotionBlurExecutionPass, MotionBlurExecutionPass];
|
|
604
|
+
}
|
|
559
605
|
|
|
560
606
|
/** Bounded renderer-owned Depth of Field facts; no graph or GPU handles. */
|
|
561
607
|
export type DepthOfFieldInspectionStatus = 'off' | 'active' | 'reset' | 'invalid' | 'unsupported';
|
|
@@ -597,13 +643,25 @@ export interface DepthOfFieldInspection {
|
|
|
597
643
|
export interface MotionBlurInspection {
|
|
598
644
|
readonly enabled: boolean;
|
|
599
645
|
readonly status: MotionBlurInspectionStatus;
|
|
646
|
+
/** Capability-selected implementation lane; raster is explicitly limited. */
|
|
647
|
+
readonly lane: MotionBlurInspectionLane;
|
|
600
648
|
readonly shutterAngle: number;
|
|
601
649
|
readonly maxRadiusPixels: number;
|
|
602
650
|
readonly sampleCount: number;
|
|
651
|
+
/** Target presentation rate used by the accepted exposure calculation. */
|
|
652
|
+
readonly targetFps: number;
|
|
653
|
+
/** Bounded tier selected from the authored sample budget. */
|
|
654
|
+
readonly effectiveSampleCount: 0 | 4 | 8 | 16;
|
|
603
655
|
readonly temporalDemand: 'none' | 'scene-data-temporal-v1';
|
|
604
656
|
readonly passName: 'motion-blur';
|
|
605
657
|
readonly historyWrites: 0;
|
|
606
|
-
|
|
658
|
+
/** Zero when demand is disabled; compute uses one summary plus one fused pass. */
|
|
659
|
+
readonly passCount: 0 | 1 | 2;
|
|
660
|
+
readonly tapBudget: 0 | 4 | 8 | 16;
|
|
661
|
+
/** Present only when the current accepted frame executed both compute passes. */
|
|
662
|
+
readonly execution?: MotionBlurExecutionReceipt;
|
|
663
|
+
readonly resetReason?: import('./temporal/view').TemporalResetReason;
|
|
664
|
+
readonly lastFailure?: 'invalid-params' | 'scene-data-unavailable' | 'submit-failure';
|
|
607
665
|
}
|
|
608
666
|
|
|
609
667
|
export type RenderSceneResyncReason =
|
package/src/instances.ts
CHANGED
|
@@ -41,6 +41,8 @@ export function instanceUploadRangesForResident(
|
|
|
41
41
|
|
|
42
42
|
export interface InstanceCollectionSnapshot extends InstanceCollectionInfo {
|
|
43
43
|
readonly transforms: Float32Array;
|
|
44
|
+
/** Unique identity generations that travel with an instance through reorder. */
|
|
45
|
+
readonly generations: Uint32Array;
|
|
44
46
|
readonly dirtyRanges: readonly InstanceDirtyRange[];
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -171,6 +173,7 @@ export type InstanceCollectionResult<T> = Result<T, InstanceCollectionError>;
|
|
|
171
173
|
interface CollectionRecord {
|
|
172
174
|
readonly id: InstanceCollectionId;
|
|
173
175
|
transforms: Float32Array;
|
|
176
|
+
generations: Uint32Array;
|
|
174
177
|
count: number;
|
|
175
178
|
capacity: number;
|
|
176
179
|
revision: number;
|
|
@@ -307,6 +310,7 @@ function missing(
|
|
|
307
310
|
*/
|
|
308
311
|
export class InstanceCollectionStore {
|
|
309
312
|
private nextId = 1;
|
|
313
|
+
private nextInstanceGeneration = 1;
|
|
310
314
|
private epochValue = 0;
|
|
311
315
|
private disposed = false;
|
|
312
316
|
private readonly records = new Map<InstanceCollectionId, CollectionRecord>();
|
|
@@ -330,9 +334,11 @@ export class InstanceCollectionStore {
|
|
|
330
334
|
if (invalidPayload !== undefined) return err(invalidPayload);
|
|
331
335
|
const id = this.nextId++ as InstanceCollectionId;
|
|
332
336
|
const count = input.transforms.length / MATRIX_STRIDE;
|
|
337
|
+
const generations = this.allocateGenerations(count);
|
|
333
338
|
const record: CollectionRecord = {
|
|
334
339
|
id,
|
|
335
340
|
transforms: new Float32Array(input.transforms),
|
|
341
|
+
generations,
|
|
336
342
|
count,
|
|
337
343
|
capacity: nextCapacity(Math.max(INITIAL_CAPACITY, count)),
|
|
338
344
|
revision: 1,
|
|
@@ -348,13 +354,26 @@ export class InstanceCollectionStore {
|
|
|
348
354
|
replace(
|
|
349
355
|
id: InstanceCollectionId,
|
|
350
356
|
transforms: Float32Array,
|
|
357
|
+
/** Prior identity generations in the replacement order; omitted rows are new. */
|
|
358
|
+
generations?: Uint32Array,
|
|
351
359
|
): InstanceCollectionResult<InstanceCollectionInfo> {
|
|
352
360
|
const record = this.live('replace', id);
|
|
353
361
|
if (!record.ok) return record;
|
|
354
362
|
const invalidPayload = validateTransforms('replace', transforms, id);
|
|
355
363
|
if (invalidPayload !== undefined) return err(invalidPayload);
|
|
356
364
|
const count = transforms.length / MATRIX_STRIDE;
|
|
365
|
+
const generationResult =
|
|
366
|
+
generations === undefined
|
|
367
|
+
? ok(this.allocateGenerations(count))
|
|
368
|
+
: this.validateReplacementGenerations(generations, count, id);
|
|
369
|
+
if (!generationResult.ok) return generationResult;
|
|
370
|
+
// Replacement is a topology boundary by default. A producer that keeps
|
|
371
|
+
// logical instance identity through compaction/reorder may pass the prior
|
|
372
|
+
// generation tokens in their new order; the record stage then maps the
|
|
373
|
+
// real previous transform by token instead of by draw ordinal.
|
|
374
|
+
const replacementGenerations = generationResult.value;
|
|
357
375
|
record.value.transforms = new Float32Array(transforms);
|
|
376
|
+
record.value.generations = replacementGenerations;
|
|
358
377
|
record.value.count = count;
|
|
359
378
|
record.value.capacity = Math.max(record.value.capacity, nextCapacity(Math.max(1, count)));
|
|
360
379
|
record.value.revision += 1;
|
|
@@ -402,6 +421,11 @@ export class InstanceCollectionStore {
|
|
|
402
421
|
record.value.dirtyRanges = [];
|
|
403
422
|
}
|
|
404
423
|
record.value.transforms.set(input.transforms, input.start * MATRIX_STRIDE);
|
|
424
|
+
// An update changes a pose while retaining the same ordinal identity.
|
|
425
|
+
// Keep its generation stable so the record stage can pair this current
|
|
426
|
+
// transform with the last submitted transform and expose real velocity.
|
|
427
|
+
// `replace` is the explicit topology boundary that advances generations
|
|
428
|
+
// for compaction, reorder, retirement, and newly occupied ordinals.
|
|
405
429
|
record.value.revision += 1;
|
|
406
430
|
appendDirtyRange(record.value, input.start, input.start + span);
|
|
407
431
|
this.epochValue += 1;
|
|
@@ -502,7 +526,12 @@ export class InstanceCollectionStore {
|
|
|
502
526
|
if (!record.ok) return record;
|
|
503
527
|
const value = record.value;
|
|
504
528
|
value.lastSnapshotRevision = value.revision;
|
|
505
|
-
const result =
|
|
529
|
+
const result = {
|
|
530
|
+
...this.info(value),
|
|
531
|
+
transforms: new Float32Array(value.transforms),
|
|
532
|
+
generations: new Uint32Array(value.generations),
|
|
533
|
+
dirtyRanges: value.dirtyRanges.map((range) => ({ ...range })),
|
|
534
|
+
};
|
|
506
535
|
// Keep the current ranges stable for repeated reads by entities sharing
|
|
507
536
|
// this collection. The next authoring mutation starts a fresh interval
|
|
508
537
|
// set once this revision has been observed.
|
|
@@ -579,6 +608,7 @@ export class InstanceCollectionStore {
|
|
|
579
608
|
return {
|
|
580
609
|
...this.info(record),
|
|
581
610
|
transforms: new Float32Array(record.transforms),
|
|
611
|
+
generations: new Uint32Array(record.generations),
|
|
582
612
|
dirtyRanges: record.dirtyRanges.map((range) => ({ ...range })),
|
|
583
613
|
};
|
|
584
614
|
}
|
|
@@ -592,6 +622,55 @@ export class InstanceCollectionStore {
|
|
|
592
622
|
});
|
|
593
623
|
}
|
|
594
624
|
|
|
625
|
+
private allocateGenerations(count: number): Uint32Array {
|
|
626
|
+
const generations = new Uint32Array(count);
|
|
627
|
+
for (let index = 0; index < count; index += 1) {
|
|
628
|
+
generations[index] = this.nextInstanceGeneration;
|
|
629
|
+
this.nextInstanceGeneration = (this.nextInstanceGeneration + 1) >>> 0 || 1;
|
|
630
|
+
}
|
|
631
|
+
return generations;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
private validateReplacementGenerations(
|
|
635
|
+
generations: Uint32Array,
|
|
636
|
+
count: number,
|
|
637
|
+
collectionId: InstanceCollectionId,
|
|
638
|
+
): InstanceCollectionResult<Uint32Array> {
|
|
639
|
+
if (!(generations instanceof Uint32Array) || generations.length !== count) {
|
|
640
|
+
return err(
|
|
641
|
+
invalid(
|
|
642
|
+
'replace',
|
|
643
|
+
`generations is a Uint32Array with ${count} entries`,
|
|
644
|
+
generations instanceof Uint32Array ? generations.length : typeof generations,
|
|
645
|
+
collectionId,
|
|
646
|
+
{ requestedBytes: generations?.byteLength ?? 0, cause: 'invalid-generation-shape' },
|
|
647
|
+
),
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
const seen = new Set<number>();
|
|
651
|
+
for (const generation of generations) {
|
|
652
|
+
if (generation === 0 || seen.has(generation)) {
|
|
653
|
+
return err(
|
|
654
|
+
invalid(
|
|
655
|
+
'replace',
|
|
656
|
+
'generations contain unique non-zero identity tokens',
|
|
657
|
+
generation,
|
|
658
|
+
collectionId,
|
|
659
|
+
{
|
|
660
|
+
requestedBytes: generations.byteLength,
|
|
661
|
+
cause: 'invalid-generation-identity',
|
|
662
|
+
},
|
|
663
|
+
),
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
seen.add(generation);
|
|
667
|
+
if (generation >= this.nextInstanceGeneration) {
|
|
668
|
+
this.nextInstanceGeneration = (generation + 1) >>> 0 || 1;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return ok(new Uint32Array(generations));
|
|
672
|
+
}
|
|
673
|
+
|
|
595
674
|
private projectInspection(record: CollectionRecord): InstanceCollectionInspection {
|
|
596
675
|
const residency = this.residency.get(record.id);
|
|
597
676
|
return Object.freeze({
|