Fix reference counting for SplatAccumulator during updates (#200)

This commit is contained in:
Noeri Huisman
2025-10-24 18:27:40 +02:00
committed by GitHub
parent 5d5493e8a4
commit 8f90fe0ca6
3 changed files with 24 additions and 10 deletions
+1 -1
View File
@@ -464,7 +464,7 @@ export class PackedSplats {
let maxSplats = 0; let maxSplats = 0;
const mapping = splatCounts.map((numSplats) => { const mapping = splatCounts.map((numSplats) => {
const base = maxSplats; const base = maxSplats;
// Generation happens in horizonal row chunks, so round up to full width // Generation happens in horizontal row chunks, so round up to full width
const rounded = Math.ceil(numSplats / SPLAT_TEX_WIDTH) * SPLAT_TEX_WIDTH; const rounded = Math.ceil(numSplats / SPLAT_TEX_WIDTH) * SPLAT_TEX_WIDTH;
maxSplats += rounded; maxSplats += rounded;
return { base, count: numSplats }; return { base, count: numSplats };
+2 -1
View File
@@ -314,6 +314,7 @@ export class SparkRenderer extends THREE.Mesh {
this.splatEncoding = options.splatEncoding ?? { ...DEFAULT_SPLAT_ENCODING }; this.splatEncoding = options.splatEncoding ?? { ...DEFAULT_SPLAT_ENCODING };
this.active = new SplatAccumulator(); this.active = new SplatAccumulator();
this.active.refCount = 1;
this.accumulatorCount = 1; this.accumulatorCount = 1;
this.freeAccumulators = []; this.freeAccumulators = [];
// Start with the minimum of 2 total accumulators // Start with the minimum of 2 total accumulators
@@ -765,7 +766,7 @@ export class SparkRenderer extends THREE.Mesh {
// minimum co-orientation (dot product of quaternions) // minimum co-orientation (dot product of quaternions)
const originChanged = !withinCoorientDist({ const originChanged = !withinCoorientDist({
matrix1: originToWorld, matrix1: originToWorld,
matrix2: this.active.toWorld, matrix2: accumulator.toWorld,
maxDistance: 0.00001, maxDistance: 0.00001,
minCoorient: 0.99999, minCoorient: 0.99999,
}); });
+21 -8
View File
@@ -479,9 +479,15 @@ export class SparkViewpoint {
// Splat mapping has not changed, so reuse the existing sorted // Splat mapping has not changed, so reuse the existing sorted
// geometry to show updates faster. We will still fire off // geometry to show updates faster. We will still fire off
// a re-sort if necessary. First release old accumulator. // a re-sort if necessary. First release old accumulator.
accumulator.refCount += 1;
this.spark.releaseAccumulator(this.display.accumulator); this.spark.releaseAccumulator(this.display.accumulator);
this.display.accumulator = accumulator; this.display.accumulator = accumulator;
this.display.viewToWorld.copy(this.viewToWorld);
displayed = true; displayed = true;
if (this.spark.viewpoint === this) {
this.spark.prepareViewpoint(this);
}
} }
} }
@@ -509,15 +515,12 @@ export class SparkViewpoint {
} }
if (accumulator) { if (accumulator) {
// Hold a reference to the accumulator so it isn't released // Hold a reference to the accumulator for sorting
accumulator.refCount += 1; accumulator.refCount += 1;
} }
if ( if (this.pending?.accumulator) {
accumulator && // Release the reference of the pending accumulator
this.pending?.accumulator &&
this.pending.accumulator !== this.display?.accumulator
) {
this.spark.releaseAccumulator(this.pending.accumulator); this.spark.releaseAccumulator(this.pending.accumulator);
} }
this.pending = { accumulator, viewToWorld: this.viewToWorld, displayed }; this.pending = { accumulator, viewToWorld: this.viewToWorld, displayed };
@@ -533,9 +536,10 @@ export class SparkViewpoint {
} }
const { viewToWorld, displayed } = this.pending; const { viewToWorld, displayed } = this.pending;
let accumulator = this.pending.accumulator ?? this.display?.accumulator; let accumulator = this.pending.accumulator;
if (!accumulator) { if (!accumulator) {
accumulator = this.spark.active; // Hold a reference to the accumulator while sorting
accumulator = this.display?.accumulator ?? this.spark.active;
accumulator.refCount += 1; accumulator.refCount += 1;
} }
this.pending = null; this.pending = null;
@@ -546,6 +550,10 @@ export class SparkViewpoint {
this.sorting = { viewToWorld }; this.sorting = { viewToWorld };
await this.sortUpdate({ accumulator, viewToWorld, displayed }); await this.sortUpdate({ accumulator, viewToWorld, displayed });
this.sorting = null; this.sorting = null;
// Release the reference to the accumulator
this.spark.releaseAccumulator(accumulator);
// Continue in loop with any queued sort // Continue in loop with any queued sort
} }
} }
@@ -668,6 +676,8 @@ export class SparkViewpoint {
displayed?: boolean; displayed?: boolean;
}) { }) {
if (!this.display) { if (!this.display) {
// Hold a reference to the accumulator while part of display
accumulator.refCount += 1;
this.display = { this.display = {
accumulator, accumulator,
viewToWorld, viewToWorld,
@@ -675,6 +685,9 @@ export class SparkViewpoint {
}; };
} else { } else {
if (!displayed && accumulator !== this.display.accumulator) { if (!displayed && accumulator !== this.display.accumulator) {
// Hold a reference to the new accumulator being displayed
accumulator.refCount += 1;
// Release the reference to the previously displayed accumulator
this.spark.releaseAccumulator(this.display.accumulator); this.spark.releaseAccumulator(this.display.accumulator);
this.display.accumulator = accumulator; this.display.accumulator = accumulator;
} }