Skip to content

Commit 52afe52

Browse files
committed
Fix memory leak in ReadbackSplatSorter
1 parent e4d7397 commit 52afe52

File tree

7 files changed

+52
-23
lines changed

7 files changed

+52
-23
lines changed

examples/hello-world/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
</script>
2424
<script type="module">
2525
import * as THREE from "three";
26-
import { SplatLoader, Splat } from "@sparkjsdev/spark";
26+
import { SplatLoader } from "@sparkjsdev/spark";
2727
import { getAssetFileURL } from "/examples/js/get-asset-url.js";
2828

2929
const scene = new THREE.Scene();

examples/interactivity/index.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ <h2>Food scans by <a href="https://x.com/tipatat" target="_blank">Tipatat</a></h
175175
if (food) food.opacity = 1 - easeInOutSine(fadeOutTime / TRANSITION_LENGTH);
176176
} else {
177177
// Fade out finished
178-
if (food) food.dispose();
179178
fadeOutTime = null;
180179
// Fade in next food
181180
fadeInTime = 0;

src/BatchedSplat.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ class BatchedSplatData implements SplatData {
199199
getInstanceIndexFor(splatIndex: number): number {
200200
let instanceIndex = 0;
201201
let instanceEnd = this.sources[instanceIndex].numSplats;
202-
while (splatIndex > instanceEnd) {
202+
while (splatIndex >= instanceEnd) {
203203
instanceIndex++;
204204
instanceEnd += this.sources[instanceIndex].numSplats;
205205
}

src/Splat.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,9 @@ export class Splat extends THREE.Mesh<SplatGeometry, THREE.ShaderMaterial> {
562562
}
563563

564564
dispose() {
565-
// TODO
565+
this.geometry.dispose();
566+
this.material.dispose();
567+
this.splatData.dispose();
566568
}
567569

568570
set needsUpdate(value: boolean) {

src/SplatSorter.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ export class ReadbackSplatSorter implements SplatSorter {
7070

7171
private capacity = 0;
7272
private target?: THREE.WebGLArrayRenderTarget;
73-
private readbackBufferPool: ReadbackBufferPool;
73+
private readonly readbackBufferPool: ReadbackBufferPool;
74+
75+
private readonly material: THREE.RawShaderMaterial;
7476

7577
constructor(options: ReadbackSorterOptions = {}) {
7678
this.sortRadial = options.sortRadial ?? false;
@@ -79,6 +81,7 @@ export class ReadbackSplatSorter implements SplatSorter {
7981
this.sort32 = options.sort32 ?? false;
8082

8183
this.readbackBufferPool = new ReadbackBufferPool();
84+
this.material = ReadbackSplatSorter.createMaterial();
8285
}
8386

8487
async sort(
@@ -92,16 +95,6 @@ export class ReadbackSplatSorter implements SplatSorter {
9295
const maxSplats = splatData.maxSplats;
9396
const numSplats = splatData.numSplats;
9497

95-
const material = this.createMaterial(splatData);
96-
material.uniforms.sortRadial.value = this.sortRadial;
97-
material.uniforms.sortDepthBias.value = this.depthBias;
98-
material.uniforms.sort360.value = this.sort360;
99-
material.uniforms.splatModelViewMatrix.value.multiplyMatrices(
100-
camera.matrixWorldInverse,
101-
splat.matrixWorld,
102-
);
103-
material.defines.SORT32 = this.sort32;
104-
10598
// Render
10699
const count = this.sort32 ? numSplats : numSplats / 2;
107100
const readbackBuffer = await this.ensureCapacity(count);
@@ -111,6 +104,17 @@ export class ReadbackSplatSorter implements SplatSorter {
111104

112105
const renderState = this.saveRenderState(renderer);
113106

107+
const material = this.material;
108+
splatData.setupMaterial(material);
109+
material.uniforms.sortRadial.value = this.sortRadial;
110+
material.uniforms.sortDepthBias.value = this.depthBias;
111+
material.uniforms.sort360.value = this.sort360;
112+
material.uniforms.splatModelViewMatrix.value.multiplyMatrices(
113+
camera.matrixWorldInverse,
114+
splat.matrixWorld,
115+
);
116+
material.defines.SORT32 = this.sort32;
117+
114118
this.render(renderer, count, material);
115119
const promise = this.read(renderer, count, readback);
116120

@@ -293,7 +297,7 @@ export class ReadbackSplatSorter implements SplatSorter {
293297
return Promise.all(promises).then(() => readback);
294298
}
295299

296-
private createMaterial(splatData: SplatData) {
300+
private static createMaterial() {
297301
const shaders = getShaders();
298302
const material = new THREE.RawShaderMaterial({
299303
name: "SplatDistanceShader",
@@ -313,8 +317,6 @@ export class ReadbackSplatSorter implements SplatSorter {
313317
fragmentShader: shaders.splatDistanceFragment,
314318
});
315319

316-
splatData.setupMaterial(material);
317-
318320
return material;
319321
}
320322

src/encoding/ExtendedSplats.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,23 @@ export class ExtendedSplats implements IterableSplatData {
209209
}
210210

211211
dispose(): void {
212-
this.splatTexture1?.dispose();
213-
this.splatTexture2?.dispose();
214-
this.shTexture?.dispose();
212+
if (this.splatTexture1) {
213+
this.splatTexture1.dispose();
214+
this.splatTexture1.source.data = null;
215+
}
216+
if (this.splatTexture2) {
217+
this.splatTexture2.dispose();
218+
this.splatTexture2.source.data = null;
219+
}
220+
if (this.shTexture) {
221+
this.shTexture.dispose();
222+
this.shTexture.source.data = null;
223+
}
224+
this.packedArray1 = EMPTY_UINT32_ARRAY;
225+
this.packedArray2 = EMPTY_UINT32_ARRAY;
226+
this.packedShArray = null;
227+
this.numSplats = -1;
228+
this.maxSplats = 0;
215229
}
216230

217231
static encodingName = "extended";
@@ -447,3 +461,4 @@ export type EncodedExtendedSplats = {
447461
};
448462

449463
const tempQuaternion = new THREE.Quaternion();
464+
const EMPTY_UINT32_ARRAY = new Uint32Array(0);

src/encoding/PackedSplats.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,18 @@ export class PackedSplats implements IterableSplatData {
280280
}
281281

282282
dispose() {
283-
this.texture?.dispose();
284-
this.shTexture?.dispose();
283+
if (this.texture) {
284+
this.texture.dispose();
285+
this.texture.source.data = null;
286+
}
287+
if (this.shTexture) {
288+
this.shTexture.dispose();
289+
this.shTexture.source.data = null;
290+
}
291+
this.packedArray = EMPTY_UINT32_ARRAY;
292+
this.shArray = null;
293+
this.numSplats = -1;
294+
this.maxSplats = 0;
285295
}
286296

287297
static encodingName = "packed";
@@ -536,3 +546,4 @@ export type EncodedPackedSplats = {
536546
};
537547

538548
const tempQuaternion = new THREE.Quaternion();
549+
const EMPTY_UINT32_ARRAY = new Uint32Array(0);

0 commit comments

Comments
 (0)