From 4d26619ea34d046c2892655624a94c93830146ae Mon Sep 17 00:00:00 2001 From: Noeri Huisman <8823461+mrxz@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:13:47 +0200 Subject: [PATCH] Fix reference counting for SplatAccumulator during updates --- src/PackedSplats.ts | 2 +- src/SparkRenderer.ts | 3 ++- src/SparkViewpoint.ts | 29 +++++++++++++++++++++-------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/PackedSplats.ts b/src/PackedSplats.ts index f9ba443..327d318 100644 --- a/src/PackedSplats.ts +++ b/src/PackedSplats.ts @@ -464,7 +464,7 @@ export class PackedSplats { let maxSplats = 0; const mapping = splatCounts.map((numSplats) => { 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; maxSplats += rounded; return { base, count: numSplats }; diff --git a/src/SparkRenderer.ts b/src/SparkRenderer.ts index 4e090d0..3b6368b 100644 --- a/src/SparkRenderer.ts +++ b/src/SparkRenderer.ts @@ -314,6 +314,7 @@ export class SparkRenderer extends THREE.Mesh { this.splatEncoding = options.splatEncoding ?? { ...DEFAULT_SPLAT_ENCODING }; this.active = new SplatAccumulator(); + this.active.refCount = 1; this.accumulatorCount = 1; this.freeAccumulators = []; // 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) const originChanged = !withinCoorientDist({ matrix1: originToWorld, - matrix2: this.active.toWorld, + matrix2: accumulator.toWorld, maxDistance: 0.00001, minCoorient: 0.99999, }); diff --git a/src/SparkViewpoint.ts b/src/SparkViewpoint.ts index 4a5e51e..b3fcd5b 100644 --- a/src/SparkViewpoint.ts +++ b/src/SparkViewpoint.ts @@ -479,9 +479,15 @@ export class SparkViewpoint { // Splat mapping has not changed, so reuse the existing sorted // geometry to show updates faster. We will still fire off // a re-sort if necessary. First release old accumulator. + accumulator.refCount += 1; this.spark.releaseAccumulator(this.display.accumulator); this.display.accumulator = accumulator; + this.display.viewToWorld.copy(this.viewToWorld); displayed = true; + + if (this.spark.viewpoint === this) { + this.spark.prepareViewpoint(this); + } } } @@ -509,15 +515,12 @@ export class SparkViewpoint { } if (accumulator) { - // Hold a reference to the accumulator so it isn't released + // Hold a reference to the accumulator for sorting accumulator.refCount += 1; } - if ( - accumulator && - this.pending?.accumulator && - this.pending.accumulator !== this.display?.accumulator - ) { + if (this.pending?.accumulator) { + // Release the reference of the pending accumulator this.spark.releaseAccumulator(this.pending.accumulator); } this.pending = { accumulator, viewToWorld: this.viewToWorld, displayed }; @@ -533,9 +536,10 @@ export class SparkViewpoint { } const { viewToWorld, displayed } = this.pending; - let accumulator = this.pending.accumulator ?? this.display?.accumulator; + let accumulator = this.pending.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; } this.pending = null; @@ -546,6 +550,10 @@ export class SparkViewpoint { this.sorting = { viewToWorld }; await this.sortUpdate({ accumulator, viewToWorld, displayed }); this.sorting = null; + + // Release the reference to the accumulator + this.spark.releaseAccumulator(accumulator); + // Continue in loop with any queued sort } } @@ -668,6 +676,8 @@ export class SparkViewpoint { displayed?: boolean; }) { if (!this.display) { + // Hold a reference to the accumulator while part of display + accumulator.refCount += 1; this.display = { accumulator, viewToWorld, @@ -675,6 +685,9 @@ export class SparkViewpoint { }; } else { 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.display.accumulator = accumulator; }