@ifc-lite/renderer 6.0.1 → 6.3.0
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 +21 -0
- package/dist/ao-pass.d.ts +0 -16
- package/dist/ao-pass.d.ts.map +1 -1
- package/dist/ao-pass.js +2 -0
- package/dist/ao-pass.js.map +1 -1
- package/dist/camera-animation.d.ts +1 -1
- package/dist/camera-animation.d.ts.map +1 -1
- package/dist/camera-animation.js +11 -11
- package/dist/camera-animation.js.map +1 -1
- package/dist/camera-inertia.d.ts +27 -0
- package/dist/camera-inertia.d.ts.map +1 -0
- package/dist/camera-inertia.js +33 -0
- package/dist/camera-inertia.js.map +1 -0
- package/dist/camera.d.ts.map +1 -1
- package/dist/camera.js +3 -0
- package/dist/camera.js.map +1 -1
- package/dist/deviation/deviation-computer.d.ts +5 -1
- package/dist/deviation/deviation-computer.d.ts.map +1 -1
- package/dist/deviation/deviation-computer.js +13 -6
- package/dist/deviation/deviation-computer.js.map +1 -1
- package/dist/deviation/deviation-readback.d.ts +16 -3
- package/dist/deviation/deviation-readback.d.ts.map +1 -1
- package/dist/deviation/deviation-readback.js +46 -36
- package/dist/deviation/deviation-readback.js.map +1 -1
- package/dist/deviation/deviation-statistics-kernel.d.ts +111 -0
- package/dist/deviation/deviation-statistics-kernel.d.ts.map +1 -0
- package/dist/deviation/deviation-statistics-kernel.js +303 -0
- package/dist/deviation/deviation-statistics-kernel.js.map +1 -0
- package/dist/deviation/deviation-statistics.d.ts +100 -0
- package/dist/deviation/deviation-statistics.d.ts.map +1 -0
- package/dist/deviation/deviation-statistics.js +100 -0
- package/dist/deviation/deviation-statistics.js.map +1 -0
- package/dist/device-recovery.d.ts.map +1 -1
- package/dist/device-recovery.js +5 -0
- package/dist/device-recovery.js.map +1 -1
- package/dist/device.d.ts +0 -3
- package/dist/device.d.ts.map +1 -1
- package/dist/device.js +5 -1
- package/dist/device.js.map +1 -1
- package/dist/edge-pass.d.ts +0 -12
- package/dist/edge-pass.d.ts.map +1 -1
- package/dist/edge-pass.js +3 -0
- package/dist/edge-pass.js.map +1 -1
- package/dist/edl-pass.d.ts +0 -21
- package/dist/edl-pass.d.ts.map +1 -1
- package/dist/edl-pass.js +23 -0
- package/dist/edl-pass.js.map +1 -1
- package/dist/frame-timing-gpu.d.ts +11 -15
- package/dist/frame-timing-gpu.d.ts.map +1 -1
- package/dist/frame-timing-gpu.js +57 -26
- package/dist/frame-timing-gpu.js.map +1 -1
- package/dist/gpu-counters.d.ts +30 -0
- package/dist/gpu-counters.d.ts.map +1 -0
- package/dist/gpu-counters.js +84 -0
- package/dist/gpu-counters.js.map +1 -0
- package/dist/gpu-static-upload.d.ts.map +1 -1
- package/dist/gpu-static-upload.js +2 -0
- package/dist/gpu-static-upload.js.map +1 -1
- package/dist/index.d.ts +33 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +70 -59
- package/dist/index.js.map +1 -1
- package/dist/picker-depth-sample.d.ts +9 -0
- package/dist/picker-depth-sample.d.ts.map +1 -0
- package/dist/picker-depth-sample.js +63 -0
- package/dist/picker-depth-sample.js.map +1 -0
- package/dist/picker-readbacks.d.ts +8 -1
- package/dist/picker-readbacks.d.ts.map +1 -1
- package/dist/picker-readbacks.js +18 -1
- package/dist/picker-readbacks.js.map +1 -1
- package/dist/picker.d.ts +2 -0
- package/dist/picker.d.ts.map +1 -1
- package/dist/picker.js +70 -94
- package/dist/picker.js.map +1 -1
- package/dist/picking-manager.d.ts +5 -2
- package/dist/picking-manager.d.ts.map +1 -1
- package/dist/picking-manager.js +25 -8
- package/dist/picking-manager.js.map +1 -1
- package/dist/pipeline.d.ts +8 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +22 -0
- package/dist/pipeline.js.map +1 -1
- package/dist/pointcloud/point-cloud-keyed-chunks.d.ts +23 -0
- package/dist/pointcloud/point-cloud-keyed-chunks.d.ts.map +1 -0
- package/dist/pointcloud/point-cloud-keyed-chunks.js +44 -0
- package/dist/pointcloud/point-cloud-keyed-chunks.js.map +1 -0
- package/dist/pointcloud/point-cloud-node.d.ts +5 -1
- package/dist/pointcloud/point-cloud-node.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-node.js +8 -4
- package/dist/pointcloud/point-cloud-node.js.map +1 -1
- package/dist/pointcloud/point-cloud-placement.d.ts +6 -0
- package/dist/pointcloud/point-cloud-placement.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-placement.js +8 -0
- package/dist/pointcloud/point-cloud-placement.js.map +1 -1
- package/dist/pointcloud/point-cloud-ray-transform.d.ts +17 -8
- package/dist/pointcloud/point-cloud-ray-transform.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-ray-transform.js +17 -13
- package/dist/pointcloud/point-cloud-ray-transform.js.map +1 -1
- package/dist/pointcloud/point-cloud-renderer.d.ts +10 -8
- package/dist/pointcloud/point-cloud-renderer.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-renderer.js +20 -31
- package/dist/pointcloud/point-cloud-renderer.js.map +1 -1
- package/dist/pointcloud/point-cloud-stream-lifecycle.d.ts +8 -1
- package/dist/pointcloud/point-cloud-stream-lifecycle.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-stream-lifecycle.js +17 -2
- package/dist/pointcloud/point-cloud-stream-lifecycle.js.map +1 -1
- package/dist/pointcloud/point-cloud-uniforms.d.ts +11 -0
- package/dist/pointcloud/point-cloud-uniforms.d.ts.map +1 -1
- package/dist/pointcloud/point-cloud-uniforms.js +8 -0
- package/dist/pointcloud/point-cloud-uniforms.js.map +1 -1
- package/dist/post-pass-chain.d.ts +2 -0
- package/dist/post-pass-chain.d.ts.map +1 -1
- package/dist/post-pass-chain.js +1 -1
- package/dist/post-pass-chain.js.map +1 -1
- package/dist/raycast-point-cloud-query.d.ts.map +1 -1
- package/dist/raycast-point-cloud-query.js +11 -1
- package/dist/raycast-point-cloud-query.js.map +1 -1
- package/dist/renderer-frame-timing.d.ts +21 -0
- package/dist/renderer-frame-timing.d.ts.map +1 -0
- package/dist/renderer-frame-timing.js +104 -0
- package/dist/renderer-frame-timing.js.map +1 -0
- package/dist/renderer-viewport.d.ts +13 -7
- package/dist/renderer-viewport.d.ts.map +1 -1
- package/dist/renderer-viewport.js +15 -7
- package/dist/renderer-viewport.js.map +1 -1
- package/dist/scene-batch-rebuild.d.ts +7 -0
- package/dist/scene-batch-rebuild.d.ts.map +1 -1
- package/dist/scene-batch-rebuild.js +12 -0
- package/dist/scene-batch-rebuild.js.map +1 -1
- package/dist/scene-contents.d.ts +3 -2
- package/dist/scene-contents.d.ts.map +1 -1
- package/dist/scene-flush.d.ts +43 -0
- package/dist/scene-flush.d.ts.map +1 -0
- package/dist/scene-flush.js +103 -0
- package/dist/scene-flush.js.map +1 -0
- package/dist/scene-geometry.d.ts.map +1 -1
- package/dist/scene-geometry.js +2 -0
- package/dist/scene-geometry.js.map +1 -1
- package/dist/scene.d.ts +10 -27
- package/dist/scene.d.ts.map +1 -1
- package/dist/scene.js +38 -107
- package/dist/scene.js.map +1 -1
- package/dist/selection-mask-pass.d.ts +2 -27
- package/dist/selection-mask-pass.d.ts.map +1 -1
- package/dist/selection-mask-pass.js +44 -39
- package/dist/selection-mask-pass.js.map +1 -1
- package/dist/selection-mask-pipelines.d.ts +3 -3
- package/dist/selection-mask-pipelines.d.ts.map +1 -1
- package/dist/selection-mask-pipelines.js +11 -8
- package/dist/selection-mask-pipelines.js.map +1 -1
- package/dist/shaders/depth-nudge.wgsl.d.ts +67 -0
- package/dist/shaders/depth-nudge.wgsl.d.ts.map +1 -0
- package/dist/shaders/depth-nudge.wgsl.js +130 -0
- package/dist/shaders/depth-nudge.wgsl.js.map +1 -0
- package/dist/shaders/ghost.wgsl.d.ts +2 -0
- package/dist/shaders/ghost.wgsl.d.ts.map +1 -0
- package/dist/shaders/ghost.wgsl.js +46 -0
- package/dist/shaders/ghost.wgsl.js.map +1 -0
- package/dist/shaders/main.wgsl.d.ts +1 -1
- package/dist/shaders/main.wgsl.d.ts.map +1 -1
- package/dist/shaders/main.wgsl.js +6 -4
- package/dist/shaders/main.wgsl.js.map +1 -1
- package/dist/shaders/section-2d-overlay.wgsl.d.ts +1 -16
- package/dist/shaders/section-2d-overlay.wgsl.d.ts.map +1 -1
- package/dist/shaders/section-2d-overlay.wgsl.js +6 -6
- package/dist/shaders/section-2d-overlay.wgsl.js.map +1 -1
- package/dist/shaders/selection-mask.wgsl.d.ts +2 -10
- package/dist/shaders/selection-mask.wgsl.d.ts.map +1 -1
- package/dist/shaders/selection-mask.wgsl.js +9 -42
- package/dist/shaders/selection-mask.wgsl.js.map +1 -1
- package/dist/shaders/symbolic-overlay.wgsl.d.ts +1 -1
- package/dist/shaders/symbolic-overlay.wgsl.d.ts.map +1 -1
- package/dist/shaders/symbolic-overlay.wgsl.js +5 -4
- package/dist/shaders/symbolic-overlay.wgsl.js.map +1 -1
- package/dist/shadow-pass.d.ts +0 -19
- package/dist/shadow-pass.d.ts.map +1 -1
- package/dist/shadow-pass.js +19 -0
- package/dist/shadow-pass.js.map +1 -1
- package/dist/test/frame-timing-device.d.ts +17 -0
- package/dist/test/frame-timing-device.d.ts.map +1 -0
- package/dist/test/frame-timing-device.js +70 -0
- package/dist/test/frame-timing-device.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/xray-alpha.d.ts +8 -0
- package/dist/xray-alpha.d.ts.map +1 -1
- package/dist/xray-alpha.js +23 -0
- package/dist/xray-alpha.js.map +1 -1
- package/package.json +4 -3
package/dist/scene.js
CHANGED
|
@@ -9,6 +9,7 @@ import { createInstancedRteDeltaStream, invalidateInstancedRteDeltas } from './i
|
|
|
9
9
|
import { materializeInstances } from './scene-instance-materialization.js';
|
|
10
10
|
import { InstanceSuppression } from './scene-instance-suppression.js';
|
|
11
11
|
import { createSceneBatch } from './scene-batch-upload.js';
|
|
12
|
+
import { flushQueuedMeshes } from './scene-flush.js';
|
|
12
13
|
import { createStaticGpuBuffer } from './gpu-static-upload.js';
|
|
13
14
|
import { createSceneAppearancePreview, rebindSceneAppearanceAccess } from './scene-appearance-preview.js';
|
|
14
15
|
import { AppearanceBuckets } from './scene-appearance-buckets.js';
|
|
@@ -36,62 +37,15 @@ import { translateSceneModel, rotateSceneModelInstances, releaseInstanceVertices
|
|
|
36
37
|
import { ModelTranslations } from './model-translation.js';
|
|
37
38
|
import { unionInstancedWorldAabb as unionInstanceBounds } from './scene-instance-bounds.js';
|
|
38
39
|
import { DerivedMeshProvenance } from './scene-derived-mesh-provenance.js';
|
|
39
|
-
import { rebuildSceneBatches } from './scene-batch-rebuild.js';
|
|
40
|
+
import { forgetEmptyModelFrames, rebuildSceneBatches } from './scene-batch-rebuild.js';
|
|
40
41
|
import { regroupStreamedBuckets } from './scene-finalize-regroup.js';
|
|
42
|
+
import { perfCount, perfCounters, perfTally } from '@ifc-lite/load-trace';
|
|
41
43
|
import { dropAllPartialCaches as dropAllPartialCachesIn, dropPartialCacheForBatch as dropPartialCacheForBatchIn, retireUnusedAlphaSlots as retireUnusedAlphaSlotsIn, } from './partial-batch-cache.js';
|
|
42
44
|
import { prepareInstancedRender, foldOccurrenceWorldBox, INSTANCE_STRIDE_BYTES, INSTANCE_COLOR_OFFSET, INSTANCE_FLAGS_OFFSET, INSTANCE_FLAG_SELECTED, INSTANCE_FLAG_HIDDEN, INSTANCE_FINISH_FLAGS_MASK, } from './instanced-render.js';
|
|
43
45
|
import { translateInstanceRecord } from './scene-instance-translation.js';
|
|
44
46
|
import { discardSceneGpuResourcesForRecovery, prepareSceneDeviceRecovery, repartitionHydratedRecoveryBucket, restoreSceneGpuResourcesAfterRecovery } from './scene-device-recovery.js';
|
|
45
47
|
const EMPTY_INSTANCED_TEMPLATES = [];
|
|
46
|
-
|
|
47
|
-
* Pure helper: compute the exclusive end index of the next flushPending()
|
|
48
|
-
* append chunk, starting at `readIndex` and bounded by BOTH mesh count
|
|
49
|
-
* (`hardEnd`, computed by the caller) and index volume (`maxIndicesPerAppend`).
|
|
50
|
-
* Always takes at least one mesh past `readIndex` -- a single oversize mesh is
|
|
51
|
-
* split upstream by splitMeshForStreaming, so the volume cap never blocks the
|
|
52
|
-
* first mesh of a chunk.
|
|
53
|
-
*
|
|
54
|
-
* Non-finite-safe by construction: every non-finite `next` (a malformed mesh
|
|
55
|
-
* reporting NaN, +Infinity, or -Infinity for `indices.length`) closes the
|
|
56
|
-
* chunk explicitly instead of being folded into the running `chunkIndices`
|
|
57
|
-
* total. Only NaN would have made a naive cap check `chunkIndices + next >
|
|
58
|
-
* maxIndicesPerAppend` silently `false` forever (`NaN > cap` is always
|
|
59
|
-
* `false`); +Infinity actually made that same check fire immediately
|
|
60
|
-
* (`chunkIndices + Infinity > cap` is `true`), closing the chunk after a
|
|
61
|
-
* single oversize mesh instead of growing it unbounded. The current
|
|
62
|
-
* `!(chunkIndices + next <= maxIndicesPerAppend)` form below rejects both
|
|
63
|
-
* NaN and +Infinity explicitly rather than relying on that asymmetry.
|
|
64
|
-
* -Infinity needed a separate, explicit check: `-Infinity <= cap` is always
|
|
65
|
-
* `true`, so `!(... <= cap)` lets it straight through, and folding it into
|
|
66
|
-
* `chunkIndices` would poison the running total to -Infinity permanently,
|
|
67
|
-
* keeping the cap vacuous for every mesh after it, not just the malformed
|
|
68
|
-
* one.
|
|
69
|
-
*/
|
|
70
|
-
export function computeFlushChunkEnd(getIndicesLength, readIndex, hardEnd, maxIndicesPerAppend) {
|
|
71
|
-
let chunkEnd = readIndex;
|
|
72
|
-
let chunkIndices = 0;
|
|
73
|
-
while (chunkEnd < hardEnd) {
|
|
74
|
-
const next = getIndicesLength(chunkEnd);
|
|
75
|
-
if (!Number.isFinite(next)) {
|
|
76
|
-
// A malformed mesh reporting a non-finite indices.length (NaN, +/-Infinity)
|
|
77
|
-
// must close the chunk here rather than being folded into chunkIndices:
|
|
78
|
-
// `chunkIndices += -Infinity` would poison the running total to -Infinity
|
|
79
|
-
// permanently, making `!(chunkIndices + next <= maxIndicesPerAppend)`
|
|
80
|
-
// false forever and letting the volume cap never fire again for the
|
|
81
|
-
// rest of this chunk. Always take at least the first mesh past
|
|
82
|
-
// readIndex (same progress guarantee as the NaN case below).
|
|
83
|
-
if (chunkEnd === readIndex)
|
|
84
|
-
chunkEnd++;
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
if (chunkEnd > readIndex && !(chunkIndices + next <= maxIndicesPerAppend)) {
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
chunkIndices += next;
|
|
91
|
-
chunkEnd++;
|
|
92
|
-
}
|
|
93
|
-
return chunkEnd;
|
|
94
|
-
}
|
|
48
|
+
export { computeFlushChunkEnd } from './scene-flush.js';
|
|
95
49
|
export class Scene {
|
|
96
50
|
meshes = [];
|
|
97
51
|
batchedMeshes = []; // flat render array (rebuilt from buckets)
|
|
@@ -1199,6 +1153,7 @@ export class Scene {
|
|
|
1199
1153
|
}, device, pipeline);
|
|
1200
1154
|
this.batchedMeshes = [...this.buckets.values()].flatMap(bucket => bucket.batchedMesh ? [bucket.batchedMesh] : []);
|
|
1201
1155
|
this.pendingBatchKeys.clear();
|
|
1156
|
+
forgetEmptyModelFrames(this.buckets, this.sharedFrameOrigins);
|
|
1202
1157
|
}
|
|
1203
1158
|
/**
|
|
1204
1159
|
* Check if there are pending batch rebuilds
|
|
@@ -1782,64 +1737,20 @@ export class Scene {
|
|
|
1782
1737
|
* @param budgetMs Time slice per call. The 12 ms default keeps the main
|
|
1783
1738
|
* thread returning to the worker-message pump while geometry streams; a
|
|
1784
1739
|
* host with nothing else to serve may drain faster (#6436).
|
|
1740
|
+
* @param trace The streaming load's trace (#6979): a call that uploaded
|
|
1741
|
+
* anything records one `scene.flushPending` span.
|
|
1742
|
+
* @param deterministicUploads Benchmark-only per-mesh fragments with count
|
|
1743
|
+
* and index-volume bounds. These totals describe a different timing workload.
|
|
1785
1744
|
* @returns true if any meshes were processed (caller should render)
|
|
1786
1745
|
*/
|
|
1787
|
-
flushPending(device, pipeline, budgetMs = 12) {
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
// Drain the queue in chunks bounded by BOTH mesh count AND triangle volume,
|
|
1791
|
-
// yielding the frame back at the TOP of the loop. The mesh-count-only chunker
|
|
1792
|
-
// could merge a 512-mesh chunk of high-poly meshes (e.g. 899 Velux roof
|
|
1793
|
-
// windows at 7624 tris each → ~3.9M tris) in ONE indivisible mergeGeometry +
|
|
1794
|
-
// GPU buffer upload copy — hundreds of ms that parked the main thread
|
|
1795
|
-
// past the 16s stream watchdog. Capping each appendToBatches by index volume
|
|
1796
|
-
// keeps every synchronous slice ≈ one bounded fragment merge (~12-15ms).
|
|
1797
|
-
const MAX_MESHES_PER_FLUSH = 4096;
|
|
1798
|
-
const MESHES_PER_APPEND = 512;
|
|
1799
|
-
const MAX_INDICES_PER_APPEND = Scene.STREAMING_FRAGMENT_MAX_INDICES;
|
|
1800
|
-
const start = performance.now();
|
|
1801
|
-
let processed = 0;
|
|
1802
|
-
while (this.meshQueueReadIndex < this.meshQueue.length && processed < MAX_MESHES_PER_FLUSH) {
|
|
1803
|
-
// Yield once the budget is spent (after at least one append) so the main
|
|
1804
|
-
// thread returns to the worker-message pump and the watchdog never trips.
|
|
1805
|
-
if (processed > 0 && performance.now() - start >= budgetMs) {
|
|
1806
|
-
break;
|
|
1807
|
-
}
|
|
1808
|
-
const hardEnd = Math.min(this.meshQueue.length, this.meshQueueReadIndex + MESHES_PER_APPEND, this.meshQueueReadIndex + (MAX_MESHES_PER_FLUSH - processed));
|
|
1809
|
-
const chunkEnd = computeFlushChunkEnd((i) => this.meshQueue[i].indices.length, this.meshQueueReadIndex, hardEnd, MAX_INDICES_PER_APPEND);
|
|
1810
|
-
// Defensive, not reachable today: chunkEnd is provably > meshQueueReadIndex
|
|
1811
|
-
// here because hardEnd is provably > meshQueueReadIndex whenever this outer
|
|
1812
|
-
// loop iterates, via three invariants that hold simultaneously above:
|
|
1813
|
-
// (1) this.meshQueue.length > this.meshQueueReadIndex -- the outer while
|
|
1814
|
-
// condition that got us into this iteration;
|
|
1815
|
-
// (2) this.meshQueueReadIndex + MESHES_PER_APPEND, and MESHES_PER_APPEND
|
|
1816
|
-
// (512) is a positive constant;
|
|
1817
|
-
// (3) this.meshQueueReadIndex + (MAX_MESHES_PER_FLUSH - processed), and
|
|
1818
|
-
// processed < MAX_MESHES_PER_FLUSH -- the other half of the outer
|
|
1819
|
-
// while condition -- so that term is >= readIndex + 1 too.
|
|
1820
|
-
// hardEnd is the min of all three, so hardEnd >= readIndex + 1, and
|
|
1821
|
-
// computeFlushChunkEnd always advances by at least one past readIndex.
|
|
1822
|
-
// If a future change breaks any one of those three invariants, hardEnd
|
|
1823
|
-
// could collapse to readIndex and the loop would spin the main thread at
|
|
1824
|
-
// 100% CPU doing zero allocation -- the exact signature that made #2379
|
|
1825
|
-
// expensive to diagnose. This break turns that failure mode into "flush
|
|
1826
|
-
// stops early" instead.
|
|
1827
|
-
if (chunkEnd === this.meshQueueReadIndex)
|
|
1828
|
-
break;
|
|
1829
|
-
const chunk = this.meshQueue.slice(this.meshQueueReadIndex, chunkEnd);
|
|
1830
|
-
this.meshQueueReadIndex = chunkEnd;
|
|
1746
|
+
flushPending(device, pipeline, budgetMs = 12, trace, deterministicUploads = false) {
|
|
1747
|
+
const result = flushQueuedMeshes(this.meshQueue, this.meshQueueReadIndex, (chunk, nextReadIndex) => {
|
|
1748
|
+
this.meshQueueReadIndex = nextReadIndex;
|
|
1831
1749
|
this.appendToBatches(chunk, device, pipeline, true);
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
this.meshQueueReadIndex = 0;
|
|
1837
|
-
}
|
|
1838
|
-
else if (this.meshQueueReadIndex >= 8192 && this.meshQueueReadIndex * 2 >= this.meshQueue.length) {
|
|
1839
|
-
this.meshQueue = this.meshQueue.slice(this.meshQueueReadIndex);
|
|
1840
|
-
this.meshQueueReadIndex = 0;
|
|
1841
|
-
}
|
|
1842
|
-
return processed > 0;
|
|
1750
|
+
}, { budgetMs, trace, deterministicUploads, maxIndicesPerAppend: Scene.STREAMING_FRAGMENT_MAX_INDICES });
|
|
1751
|
+
this.meshQueue = result.queue;
|
|
1752
|
+
this.meshQueueReadIndex = result.readIndex;
|
|
1753
|
+
return result.processed > 0;
|
|
1843
1754
|
}
|
|
1844
1755
|
/**
|
|
1845
1756
|
* Create lightweight fragment batches from a single streaming batch.
|
|
@@ -1925,6 +1836,7 @@ export class Scene {
|
|
|
1925
1836
|
// Save references to old fragments/batches — keep them rendering
|
|
1926
1837
|
// until the new proper batches are fully built (no visual gap).
|
|
1927
1838
|
const oldFragments = this.streamingFragments;
|
|
1839
|
+
perfTally('render.finalize', oldFragments.length, 'fragments'); // #6957 fragment rebuilds
|
|
1928
1840
|
const oldBatches = this.batchedMeshes;
|
|
1929
1841
|
const fragmentSet = new Set(oldFragments);
|
|
1930
1842
|
const oldBatchSet = new Set(oldBatches);
|
|
@@ -1962,6 +1874,9 @@ export class Scene {
|
|
|
1962
1874
|
// already retired by rebuildPendingBatches.
|
|
1963
1875
|
this.retireFinalizedBatches(oldFragments);
|
|
1964
1876
|
this.retireFinalizedBatches(regroup?.retired ?? []);
|
|
1877
|
+
// #6957: same batch tally as the time-sliced path (only reached when the rebuild succeeded).
|
|
1878
|
+
if (perfCounters.enabled)
|
|
1879
|
+
perfCount('render.finalize.batches', this.batchedMeshes.filter((b) => !oldBatchSet.has(b)).length);
|
|
1965
1880
|
}
|
|
1966
1881
|
/**
|
|
1967
1882
|
* Time-sliced version of finalizeStreaming.
|
|
@@ -1972,15 +1887,21 @@ export class Scene {
|
|
|
1972
1887
|
* @param device GPU device
|
|
1973
1888
|
* @param pipeline Render pipeline
|
|
1974
1889
|
* @param budgetMs Max milliseconds per chunk (default 8 — half a 60fps frame)
|
|
1890
|
+
* @param trace The load's trace (#6979): `scene.finalize` from this call
|
|
1891
|
+
* until the promise settles, with `scene.finalize.regroup` under it
|
|
1975
1892
|
* @returns Promise that resolves when all batches are rebuilt
|
|
1976
1893
|
*/
|
|
1977
|
-
finalizeStreamingAsync(device, pipeline, budgetMs = 8) {
|
|
1894
|
+
finalizeStreamingAsync(device, pipeline, budgetMs = 8, trace) {
|
|
1895
|
+
const span = trace?.enabled ? trace.begin('scene.finalize', { fragments: this.streamingFragments.length, ephemeral: this.ephemeralStreamingMode }) : -1;
|
|
1978
1896
|
if (this.ephemeralStreamingMode) {
|
|
1979
1897
|
this.finishEphemeralStreaming();
|
|
1898
|
+
trace?.end(span);
|
|
1980
1899
|
return Promise.resolve();
|
|
1981
1900
|
}
|
|
1982
|
-
if (this.streamingFragments.length === 0)
|
|
1901
|
+
if (this.streamingFragments.length === 0) {
|
|
1902
|
+
trace?.end(span);
|
|
1983
1903
|
return Promise.resolve();
|
|
1904
|
+
}
|
|
1984
1905
|
// Mark the rebuild as in-flight: the preamble empties streamingFragments
|
|
1985
1906
|
// synchronously, so settle-sensitive consumers need this flag until the
|
|
1986
1907
|
// time-sliced rebuild swaps the new batch array in.
|
|
@@ -1989,6 +1910,7 @@ export class Scene {
|
|
|
1989
1910
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
1990
1911
|
const scene = this;
|
|
1991
1912
|
const oldFragments = this.streamingFragments;
|
|
1913
|
+
perfTally('render.finalize', oldFragments.length, 'fragments'); // #6957 fragment rebuilds
|
|
1992
1914
|
const oldBatches = this.batchedMeshes;
|
|
1993
1915
|
const fragmentSet = new Set(oldFragments);
|
|
1994
1916
|
const oldBatchSet = new Set(oldBatches);
|
|
@@ -2010,6 +1932,7 @@ export class Scene {
|
|
|
2010
1932
|
let regroup = null;
|
|
2011
1933
|
let pendingKeys = [];
|
|
2012
1934
|
let keyIdx = 0;
|
|
1935
|
+
let chunks = 0;
|
|
2013
1936
|
function rollback() {
|
|
2014
1937
|
for (const { bucket, previous, previousFrameOrigin, batch } of createdOwned) {
|
|
2015
1938
|
// Repair the owner BEFORE the free, so no bucket is ever observable
|
|
@@ -2031,6 +1954,7 @@ export class Scene {
|
|
|
2031
1954
|
function processChunk() {
|
|
2032
1955
|
try {
|
|
2033
1956
|
const chunkStart = performance.now();
|
|
1957
|
+
chunks++;
|
|
2034
1958
|
while (keyIdx < pendingKeys.length) {
|
|
2035
1959
|
const key = pendingKeys[keyIdx++];
|
|
2036
1960
|
const bucket = scene.buckets.get(key);
|
|
@@ -2070,23 +1994,30 @@ export class Scene {
|
|
|
2070
1994
|
...scene.streamingFragments,
|
|
2071
1995
|
];
|
|
2072
1996
|
scene.retireFinalizedBatches(retired);
|
|
1997
|
+
perfCount('render.finalize.batches', createdOwned.length);
|
|
2073
1998
|
scene.finalizeInProgress = false;
|
|
1999
|
+
trace?.end(span, { batches: createdOwned.length, chunks });
|
|
2074
2000
|
resolve();
|
|
2075
2001
|
}
|
|
2076
2002
|
catch (err) {
|
|
2077
2003
|
rollback();
|
|
2004
|
+
trace?.end(span, { error: true });
|
|
2078
2005
|
reject(err);
|
|
2079
2006
|
}
|
|
2080
2007
|
}
|
|
2081
2008
|
try {
|
|
2082
2009
|
// --- Synchronous preamble: re-group the streamed meshes only ---
|
|
2083
2010
|
scene.streamingFragments = [];
|
|
2011
|
+
const regroupStart = performance.now();
|
|
2084
2012
|
regroup = scene.regroupStreamed();
|
|
2013
|
+
if (trace?.enabled)
|
|
2014
|
+
trace.record('scene.finalize.regroup', regroupStart, performance.now(), undefined, span);
|
|
2085
2015
|
pendingKeys = Array.from(scene.pendingBatchKeys);
|
|
2086
2016
|
scene.pendingBatchKeys.clear();
|
|
2087
2017
|
}
|
|
2088
2018
|
catch (err) {
|
|
2089
2019
|
rollback();
|
|
2020
|
+
trace?.end(span, { error: true });
|
|
2090
2021
|
reject(err);
|
|
2091
2022
|
return;
|
|
2092
2023
|
}
|