Skip to content

Commit 437ebea

Browse files
committed
fix: free more memory and prevent infinite loop when doing so
1 parent 189bc8d commit 437ebea

9 files changed

Lines changed: 178 additions & 92 deletions

example/main.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ unloadBtn.addEventListener('click', () => {
2323

2424
viewer.unload();
2525
loaded = false;
26+
pointCloud = undefined;
2627
});
2728

2829
const loadBtn = document.createElement('button');
@@ -55,8 +56,24 @@ loadBtn.addEventListener('click', () => {
5556
.catch(err => console.error(err));
5657
});
5758

59+
const slider = document.createElement('input');
60+
slider.type = 'range';
61+
slider.min = String(10_000);
62+
slider.max = String(500_000);
63+
slider.className = 'budget-slider';
64+
65+
slider.addEventListener('change', () => {
66+
if (!pointCloud) {
67+
return;
68+
}
69+
70+
pointCloud.potree.pointBudget = parseInt(slider.value, 10);
71+
console.log(pointCloud.potree.pointBudget);
72+
});
73+
5874
const btnContainer = document.createElement('div');
5975
btnContainer.className = 'btn-container';
6076
document.body.appendChild(btnContainer);
6177
btnContainer.appendChild(unloadBtn);
6278
btnContainer.appendChild(loadBtn);
79+
btnContainer.appendChild(slider);

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,18 @@
3232
"devDependencies": {
3333
"@babel/core": "^7.5.5",
3434
"@babel/preset-env": "^7.5.5",
35-
"@types/node": "^12.7.0",
35+
"@types/node": "^12.7.2",
3636
"babel-core": "^6.26.3",
3737
"babel-loader": "^8.0.6",
3838
"babel-preset-env": "^1.7.0",
39-
"circular-dependency-plugin": "^5.0.2",
39+
"circular-dependency-plugin": "^5.2.0",
4040
"css-loader": "^3.2.0",
4141
"html-loader": "^0.5.5",
4242
"html-webpack-plugin": "^3.2.0",
4343
"install": "^0.13.0",
4444
"prettier": "1.18.2",
4545
"raw-loader": "^3.1.0",
46-
"rimraf": "^2.6.3",
46+
"rimraf": "^3.0.0",
4747
"size-plugin": "^1.2.0",
4848
"standard-version": "7.0.0",
4949
"style-loader": "^1.0.0",
@@ -52,10 +52,10 @@
5252
"ts-loader": "^6.0.4",
5353
"tslint": "^5.18.0",
5454
"typescript": "^3.5.3",
55-
"webpack": "^4.39.1",
55+
"webpack": "^4.39.2",
5656
"webpack-bundle-analyzer": "^3.4.1",
5757
"webpack-cli": "^3.3.6",
58-
"webpack-dev-server": "^3.7.2",
58+
"webpack-dev-server": "^3.8.0",
5959
"worker-loader": "^2.0.0"
6060
},
6161
"keywords": [

src/materials/point-cloud-material.ts

Lines changed: 75 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -131,26 +131,28 @@ export class PointCloudMaterial extends RawShaderMaterial {
131131
fog = false;
132132
numClipBoxes: number = 0;
133133
clipBoxes: IClipBox[] = [];
134-
readonly visibleNodesTexture: Texture;
134+
visibleNodesTexture: Texture | undefined;
135135

136136
private _gradient = SPECTRAL;
137-
private gradientTexture = generateGradientTexture(this._gradient);
137+
private gradientTexture: Texture | undefined = generateGradientTexture(this._gradient);
138138

139139
private _classification: IClassification = DEFAULT_CLASSIFICATION;
140-
private classificationTexture: Texture = generateClassificationTexture(this._classification);
140+
private classificationTexture: Texture | undefined = generateClassificationTexture(
141+
this._classification,
142+
);
141143

142144
uniforms: IPointCloudMaterialUniforms & Record<string, IUniform<any>> = {
143145
bbSize: makeUniform('fv', [0, 0, 0] as [number, number, number]),
144146
blendDepthSupplement: makeUniform('f', 0.0),
145147
blendHardness: makeUniform('f', 2.0),
146-
classificationLUT: makeUniform('t', this.classificationTexture),
148+
classificationLUT: makeUniform('t', this.classificationTexture || new Texture()),
147149
clipBoxCount: makeUniform('f', 0),
148150
clipBoxes: makeUniform('Matrix4fv', [] as any),
149151
depthMap: makeUniform('t', null),
150152
diffuse: makeUniform('fv', [1, 1, 1] as [number, number, number]),
151153
far: makeUniform('f', 1.0),
152154
fov: makeUniform('f', 1.0),
153-
gradient: makeUniform('t', this.gradientTexture),
155+
gradient: makeUniform('t', this.gradientTexture || new Texture()),
154156
heightMax: makeUniform('f', 1.0),
155157
heightMin: makeUniform('f', 0.0),
156158
intensityBrightness: makeUniform('f', 0),
@@ -175,7 +177,7 @@ export class PointCloudMaterial extends RawShaderMaterial {
175177
toModel: makeUniform('Matrix4f', []),
176178
transition: makeUniform('f', 0.5),
177179
uColor: makeUniform('c', new Color(0xffffff)),
178-
visibleNodes: makeUniform('t', this.visibleNodesTexture),
180+
visibleNodes: makeUniform('t', this.visibleNodesTexture || new Texture()),
179181
vnStart: makeUniform('f', 0.0),
180182
wClassification: makeUniform('f', 0),
181183
wElevation: makeUniform('f', 0),
@@ -187,48 +189,48 @@ export class PointCloudMaterial extends RawShaderMaterial {
187189
filterByNormalThreshold: makeUniform('f', 0),
188190
};
189191

190-
@uniform('bbSize') bbSize!: [number, number, number]; // prettier-ignore
191-
@uniform('depthMap') depthMap!: Texture | null; // prettier-ignore
192-
@uniform('far') far!: number; // prettier-ignore
193-
@uniform('fov') fov!: number; // prettier-ignore
194-
@uniform('heightMax') heightMax!: number; // prettier-ignore
195-
@uniform('heightMin') heightMin!: number; // prettier-ignore
196-
@uniform('intensityBrightness') intensityBrightness!: number; // prettier-ignore
197-
@uniform('intensityContrast') intensityContrast!: number; // prettier-ignore
198-
@uniform('intensityGamma') intensityGamma!: number; // prettier-ignore
199-
@uniform('intensityRange') intensityRange!: [number, number]; // prettier-ignore
200-
@uniform('maxSize') maxSize!: number; // prettier-ignore
201-
@uniform('minSize') minSize!: number; // prettier-ignore
202-
@uniform('near') near!: number; // prettier-ignore
203-
@uniform('opacity', true) opacity!: number; // prettier-ignore
204-
@uniform('rgbBrightness', true) rgbBrightness!: number; // prettier-ignore
205-
@uniform('rgbContrast', true) rgbContrast!: number; // prettier-ignore
206-
@uniform('rgbGamma', true) rgbGamma!: number; // prettier-ignore
207-
@uniform('screenHeight') screenHeight!: number; // prettier-ignore
208-
@uniform('screenWidth') screenWidth!: number; // prettier-ignore
209-
@uniform('size') size!: number; // prettier-ignore
210-
@uniform('spacing') spacing!: number; // prettier-ignore
211-
@uniform('transition') transition!: number; // prettier-ignore
212-
@uniform('uColor') color!: Color; // prettier-ignore
213-
@uniform('wClassification') weightClassification!: number; // prettier-ignore
214-
@uniform('wElevation') weightElevation!: number; // prettier-ignore
215-
@uniform('wIntensity') weightIntensity!: number; // prettier-ignore
216-
@uniform('wReturnNumber') weightReturnNumber!: number; // prettier-ignore
217-
@uniform('wRGB') weightRGB!: number; // prettier-ignore
218-
@uniform('wSourceID') weightSourceID!: number; // prettier-ignore
219-
@uniform('opacityAttenuation') opacityAttenuation!: number; // prettier-ignore
220-
@uniform('filterByNormalThreshold') filterByNormalThreshold!: number; // prettier-ignore
221-
222-
@requiresShaderUpdate() useClipBox: boolean = false; // prettier-ignore
223-
@requiresShaderUpdate() weighted: boolean = false; // prettier-ignore
224-
@requiresShaderUpdate() pointColorType: PointColorType = PointColorType.RGB; // prettier-ignore
225-
@requiresShaderUpdate() pointSizeType: PointSizeType = PointSizeType.ADAPTIVE; // prettier-ignore
226-
@requiresShaderUpdate() clipMode: ClipMode = ClipMode.DISABLED; // prettier-ignore
227-
@requiresShaderUpdate() useEDL: boolean = false; // prettier-ignore
228-
@requiresShaderUpdate() shape: PointShape = PointShape.SQUARE; // prettier-ignore
229-
@requiresShaderUpdate() treeType: TreeType = TreeType.OCTREE; // prettier-ignore
230-
@requiresShaderUpdate() pointOpacityType: PointOpacityType = PointOpacityType.FIXED; // prettier-ignore
231-
@requiresShaderUpdate() useFilterByNormal: boolean = false; // prettier-ignore
192+
@uniform('bbSize') bbSize!: [number, number, number];
193+
@uniform('depthMap') depthMap!: Texture | undefined;
194+
@uniform('far') far!: number;
195+
@uniform('fov') fov!: number;
196+
@uniform('heightMax') heightMax!: number;
197+
@uniform('heightMin') heightMin!: number;
198+
@uniform('intensityBrightness') intensityBrightness!: number;
199+
@uniform('intensityContrast') intensityContrast!: number;
200+
@uniform('intensityGamma') intensityGamma!: number;
201+
@uniform('intensityRange') intensityRange!: [number, number];
202+
@uniform('maxSize') maxSize!: number;
203+
@uniform('minSize') minSize!: number;
204+
@uniform('near') near!: number;
205+
@uniform('opacity', true) opacity!: number;
206+
@uniform('rgbBrightness', true) rgbBrightness!: number;
207+
@uniform('rgbContrast', true) rgbContrast!: number;
208+
@uniform('rgbGamma', true) rgbGamma!: number;
209+
@uniform('screenHeight') screenHeight!: number;
210+
@uniform('screenWidth') screenWidth!: number;
211+
@uniform('size') size!: number;
212+
@uniform('spacing') spacing!: number;
213+
@uniform('transition') transition!: number;
214+
@uniform('uColor') color!: Color;
215+
@uniform('wClassification') weightClassification!: number;
216+
@uniform('wElevation') weightElevation!: number;
217+
@uniform('wIntensity') weightIntensity!: number;
218+
@uniform('wReturnNumber') weightReturnNumber!: number;
219+
@uniform('wRGB') weightRGB!: number;
220+
@uniform('wSourceID') weightSourceID!: number;
221+
@uniform('opacityAttenuation') opacityAttenuation!: number;
222+
@uniform('filterByNormalThreshold') filterByNormalThreshold!: number;
223+
224+
@requiresShaderUpdate() useClipBox: boolean = false;
225+
@requiresShaderUpdate() weighted: boolean = false;
226+
@requiresShaderUpdate() pointColorType: PointColorType = PointColorType.RGB;
227+
@requiresShaderUpdate() pointSizeType: PointSizeType = PointSizeType.ADAPTIVE;
228+
@requiresShaderUpdate() clipMode: ClipMode = ClipMode.DISABLED;
229+
@requiresShaderUpdate() useEDL: boolean = false;
230+
@requiresShaderUpdate() shape: PointShape = PointShape.SQUARE;
231+
@requiresShaderUpdate() treeType: TreeType = TreeType.OCTREE;
232+
@requiresShaderUpdate() pointOpacityType: PointOpacityType = PointOpacityType.FIXED;
233+
@requiresShaderUpdate() useFilterByNormal: boolean = false;
232234

233235
attributes = {
234236
position: { type: 'fv', value: [] },
@@ -266,7 +268,31 @@ export class PointCloudMaterial extends RawShaderMaterial {
266268
this.updateShaderSource();
267269
}
268270

269-
updateShaderSource() {
271+
dispose(): void {
272+
super.dispose();
273+
274+
if (this.gradientTexture) {
275+
this.gradientTexture.dispose();
276+
this.gradientTexture = undefined;
277+
}
278+
279+
if (this.visibleNodesTexture) {
280+
this.visibleNodesTexture.dispose();
281+
this.visibleNodesTexture = undefined;
282+
}
283+
284+
if (this.classificationTexture) {
285+
this.classificationTexture.dispose();
286+
this.classificationTexture = undefined;
287+
}
288+
289+
if (this.depthMap) {
290+
this.depthMap.dispose();
291+
this.depthMap = undefined;
292+
}
293+
}
294+
295+
updateShaderSource(): void {
270296
this.vertexShader = this.applyDefines(require('./shaders/pointcloud.vert').default);
271297
this.fragmentShader = this.applyDefines(require('./shaders/pointcloud.frag').default);
272298

src/point-cloud-octree-geometry-node.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class PointCloudOctreeGeometryNode extends EventDispatcher implements IPo
4040
boundingSphere: Sphere;
4141
mean: Vector3 = new Vector3();
4242
numPoints: number = 0;
43-
geometry: BufferGeometry = new BufferGeometry();
43+
geometry: BufferGeometry | undefined;
4444
loaded: boolean = false;
4545
loading: boolean = false;
4646
failed: boolean = false;
@@ -69,7 +69,7 @@ export class PointCloudOctreeGeometryNode extends EventDispatcher implements IPo
6969
}
7070

7171
this.geometry.dispose();
72-
this.geometry = new BufferGeometry();
72+
this.geometry = undefined;
7373
this.loaded = false;
7474

7575
this.oneTimeDisposeHandlers.forEach(handler => handler());

src/point-cloud-octree-node.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Box3, EventDispatcher, Object3D, Points, Sphere } from 'three';
1+
import { Box3, BufferGeometry, EventDispatcher, Object3D, Points, Sphere } from 'three';
22
import { PointCloudOctreeGeometryNode } from './point-cloud-octree-geometry-node';
33
import { IPointCloudTreeNode } from './types';
44

@@ -24,6 +24,26 @@ export class PointCloudOctreeNode extends EventDispatcher implements IPointCloud
2424
this.geometryNode.dispose();
2525
}
2626

27+
disposeSceneNode(): void {
28+
const node = this.sceneNode;
29+
30+
if (node.geometry instanceof BufferGeometry) {
31+
const attributes = node.geometry.attributes;
32+
33+
// tslint:disable-next-line:forin
34+
for (const key in attributes) {
35+
if (key === 'position') {
36+
delete attributes[key].array;
37+
}
38+
39+
delete attributes[key];
40+
}
41+
42+
node.geometry.dispose();
43+
node.geometry = undefined as any;
44+
}
45+
}
46+
2747
traverse(cb: (node: IPointCloudTreeNode) => void, includeSelf?: boolean): void {
2848
this.geometryNode.traverse(cb, includeSelf);
2949
}

src/point-cloud-octree.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export class PointCloudOctree extends PointCloudTree {
123123
this.root.dispose();
124124
}
125125

126+
this.pcoGeometry.root.traverse(n => this.potree.lru.remove(n));
126127
this.pcoGeometry.dispose();
127128
this.material.dispose();
128129

@@ -136,7 +137,7 @@ export class PointCloudOctree extends PointCloudTree {
136137
this.pickState = undefined;
137138
}
138139

139-
this.disposed = false;
140+
this.disposed = true;
140141
}
141142

142143
get pointSizeType(): PointSizeType {
@@ -151,25 +152,26 @@ export class PointCloudOctree extends PointCloudTree {
151152
geometryNode: PointCloudOctreeGeometryNode,
152153
parent?: PointCloudOctreeNode | null,
153154
): PointCloudOctreeNode {
154-
const sceneNode = new Points(geometryNode.geometry, this.material);
155-
const node = new PointCloudOctreeNode(geometryNode, sceneNode);
156-
sceneNode.name = geometryNode.name;
157-
sceneNode.position.copy(geometryNode.boundingBox.min);
158-
sceneNode.frustumCulled = false;
159-
sceneNode.onBeforeRender = this.makeOnBeforeRender(node);
155+
const points = new Points(geometryNode.geometry, this.material);
156+
const node = new PointCloudOctreeNode(geometryNode, points);
157+
points.name = geometryNode.name;
158+
points.position.copy(geometryNode.boundingBox.min);
159+
points.frustumCulled = false;
160+
points.onBeforeRender = this.makeOnBeforeRender(node);
160161

161162
if (parent) {
162-
parent.sceneNode.add(sceneNode);
163+
parent.sceneNode.add(points);
163164
parent.children[geometryNode.index] = node;
164165

165166
geometryNode.oneTimeDisposeHandlers.push(() => {
167+
node.disposeSceneNode();
166168
parent.sceneNode.remove(node.sceneNode);
167169
// Replace the tree node (rendered and in the GPU) with the geometry node.
168170
parent.children[geometryNode.index] = geometryNode;
169171
});
170172
} else {
171173
this.root = node;
172-
this.add(sceneNode);
174+
this.add(points);
173175
}
174176

175177
return node;
@@ -297,8 +299,10 @@ export class PointCloudOctree extends PointCloudTree {
297299
}
298300

299301
const texture = material.visibleNodesTexture;
300-
texture.image.data.set(data);
301-
texture.needsUpdate = true;
302+
if (texture) {
303+
texture.image.data.set(data);
304+
texture.needsUpdate = true;
305+
}
302306
}
303307

304308
private helperSphere = new Sphere();

src/utils/lru.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,14 @@ export class LRU {
135135
while (this.numPoints > this.pointBudget * 2) {
136136
const node = this.getLRUItem();
137137
if (node) {
138-
this.disposeDescendants(node);
138+
this.disposeSubtree(node);
139139
}
140140
}
141141
}
142142

143-
disposeDescendants(node: Node): void {
143+
disposeSubtree(node: Node): void {
144144
// Collect all the nodes which are to be disposed and removed.
145-
const nodesToDispose: Node[] = [];
145+
const nodesToDispose: Node[] = [node];
146146
node.traverse(n => {
147147
if (n.loaded) {
148148
nodesToDispose.push(n);

webpack.config.example.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module.exports = {
1111
filename: 'example.bundle.js',
1212
path: buildPath,
1313
},
14-
devtool: 'cheap-eval-source-map',
14+
devtool: 'source-map',
1515
devServer: {
1616
contentBase: buildPath,
1717
compress: true,

0 commit comments

Comments
 (0)