Skip to content

Commit

Permalink
fix: free more memory and prevent infinite loop when doing so
Browse files Browse the repository at this point in the history
  • Loading branch information
hccampos committed Aug 15, 2019
1 parent 189bc8d commit 437ebea
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 92 deletions.
17 changes: 17 additions & 0 deletions example/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ unloadBtn.addEventListener('click', () => {

viewer.unload();
loaded = false;
pointCloud = undefined;
});

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

const slider = document.createElement('input');
slider.type = 'range';
slider.min = String(10_000);
slider.max = String(500_000);
slider.className = 'budget-slider';

slider.addEventListener('change', () => {
if (!pointCloud) {
return;
}

pointCloud.potree.pointBudget = parseInt(slider.value, 10);
console.log(pointCloud.potree.pointBudget);
});

const btnContainer = document.createElement('div');
btnContainer.className = 'btn-container';
document.body.appendChild(btnContainer);
btnContainer.appendChild(unloadBtn);
btnContainer.appendChild(loadBtn);
btnContainer.appendChild(slider);
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,18 @@
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@types/node": "^12.7.0",
"@types/node": "^12.7.2",
"babel-core": "^6.26.3",
"babel-loader": "^8.0.6",
"babel-preset-env": "^1.7.0",
"circular-dependency-plugin": "^5.0.2",
"circular-dependency-plugin": "^5.2.0",
"css-loader": "^3.2.0",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"install": "^0.13.0",
"prettier": "1.18.2",
"raw-loader": "^3.1.0",
"rimraf": "^2.6.3",
"rimraf": "^3.0.0",
"size-plugin": "^1.2.0",
"standard-version": "7.0.0",
"style-loader": "^1.0.0",
Expand All @@ -52,10 +52,10 @@
"ts-loader": "^6.0.4",
"tslint": "^5.18.0",
"typescript": "^3.5.3",
"webpack": "^4.39.1",
"webpack": "^4.39.2",
"webpack-bundle-analyzer": "^3.4.1",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.7.2",
"webpack-dev-server": "^3.8.0",
"worker-loader": "^2.0.0"
},
"keywords": [
Expand Down
124 changes: 75 additions & 49 deletions src/materials/point-cloud-material.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,26 +131,28 @@ export class PointCloudMaterial extends RawShaderMaterial {
fog = false;
numClipBoxes: number = 0;
clipBoxes: IClipBox[] = [];
readonly visibleNodesTexture: Texture;
visibleNodesTexture: Texture | undefined;

private _gradient = SPECTRAL;
private gradientTexture = generateGradientTexture(this._gradient);
private gradientTexture: Texture | undefined = generateGradientTexture(this._gradient);

private _classification: IClassification = DEFAULT_CLASSIFICATION;
private classificationTexture: Texture = generateClassificationTexture(this._classification);
private classificationTexture: Texture | undefined = generateClassificationTexture(
this._classification,
);

uniforms: IPointCloudMaterialUniforms & Record<string, IUniform<any>> = {
bbSize: makeUniform('fv', [0, 0, 0] as [number, number, number]),
blendDepthSupplement: makeUniform('f', 0.0),
blendHardness: makeUniform('f', 2.0),
classificationLUT: makeUniform('t', this.classificationTexture),
classificationLUT: makeUniform('t', this.classificationTexture || new Texture()),
clipBoxCount: makeUniform('f', 0),
clipBoxes: makeUniform('Matrix4fv', [] as any),
depthMap: makeUniform('t', null),
diffuse: makeUniform('fv', [1, 1, 1] as [number, number, number]),
far: makeUniform('f', 1.0),
fov: makeUniform('f', 1.0),
gradient: makeUniform('t', this.gradientTexture),
gradient: makeUniform('t', this.gradientTexture || new Texture()),
heightMax: makeUniform('f', 1.0),
heightMin: makeUniform('f', 0.0),
intensityBrightness: makeUniform('f', 0),
Expand All @@ -175,7 +177,7 @@ export class PointCloudMaterial extends RawShaderMaterial {
toModel: makeUniform('Matrix4f', []),
transition: makeUniform('f', 0.5),
uColor: makeUniform('c', new Color(0xffffff)),
visibleNodes: makeUniform('t', this.visibleNodesTexture),
visibleNodes: makeUniform('t', this.visibleNodesTexture || new Texture()),
vnStart: makeUniform('f', 0.0),
wClassification: makeUniform('f', 0),
wElevation: makeUniform('f', 0),
Expand All @@ -187,48 +189,48 @@ export class PointCloudMaterial extends RawShaderMaterial {
filterByNormalThreshold: makeUniform('f', 0),
};

@uniform('bbSize') bbSize!: [number, number, number]; // prettier-ignore
@uniform('depthMap') depthMap!: Texture | null; // prettier-ignore
@uniform('far') far!: number; // prettier-ignore
@uniform('fov') fov!: number; // prettier-ignore
@uniform('heightMax') heightMax!: number; // prettier-ignore
@uniform('heightMin') heightMin!: number; // prettier-ignore
@uniform('intensityBrightness') intensityBrightness!: number; // prettier-ignore
@uniform('intensityContrast') intensityContrast!: number; // prettier-ignore
@uniform('intensityGamma') intensityGamma!: number; // prettier-ignore
@uniform('intensityRange') intensityRange!: [number, number]; // prettier-ignore
@uniform('maxSize') maxSize!: number; // prettier-ignore
@uniform('minSize') minSize!: number; // prettier-ignore
@uniform('near') near!: number; // prettier-ignore
@uniform('opacity', true) opacity!: number; // prettier-ignore
@uniform('rgbBrightness', true) rgbBrightness!: number; // prettier-ignore
@uniform('rgbContrast', true) rgbContrast!: number; // prettier-ignore
@uniform('rgbGamma', true) rgbGamma!: number; // prettier-ignore
@uniform('screenHeight') screenHeight!: number; // prettier-ignore
@uniform('screenWidth') screenWidth!: number; // prettier-ignore
@uniform('size') size!: number; // prettier-ignore
@uniform('spacing') spacing!: number; // prettier-ignore
@uniform('transition') transition!: number; // prettier-ignore
@uniform('uColor') color!: Color; // prettier-ignore
@uniform('wClassification') weightClassification!: number; // prettier-ignore
@uniform('wElevation') weightElevation!: number; // prettier-ignore
@uniform('wIntensity') weightIntensity!: number; // prettier-ignore
@uniform('wReturnNumber') weightReturnNumber!: number; // prettier-ignore
@uniform('wRGB') weightRGB!: number; // prettier-ignore
@uniform('wSourceID') weightSourceID!: number; // prettier-ignore
@uniform('opacityAttenuation') opacityAttenuation!: number; // prettier-ignore
@uniform('filterByNormalThreshold') filterByNormalThreshold!: number; // prettier-ignore

@requiresShaderUpdate() useClipBox: boolean = false; // prettier-ignore
@requiresShaderUpdate() weighted: boolean = false; // prettier-ignore
@requiresShaderUpdate() pointColorType: PointColorType = PointColorType.RGB; // prettier-ignore
@requiresShaderUpdate() pointSizeType: PointSizeType = PointSizeType.ADAPTIVE; // prettier-ignore
@requiresShaderUpdate() clipMode: ClipMode = ClipMode.DISABLED; // prettier-ignore
@requiresShaderUpdate() useEDL: boolean = false; // prettier-ignore
@requiresShaderUpdate() shape: PointShape = PointShape.SQUARE; // prettier-ignore
@requiresShaderUpdate() treeType: TreeType = TreeType.OCTREE; // prettier-ignore
@requiresShaderUpdate() pointOpacityType: PointOpacityType = PointOpacityType.FIXED; // prettier-ignore
@requiresShaderUpdate() useFilterByNormal: boolean = false; // prettier-ignore
@uniform('bbSize') bbSize!: [number, number, number];
@uniform('depthMap') depthMap!: Texture | undefined;
@uniform('far') far!: number;
@uniform('fov') fov!: number;
@uniform('heightMax') heightMax!: number;
@uniform('heightMin') heightMin!: number;
@uniform('intensityBrightness') intensityBrightness!: number;
@uniform('intensityContrast') intensityContrast!: number;
@uniform('intensityGamma') intensityGamma!: number;
@uniform('intensityRange') intensityRange!: [number, number];
@uniform('maxSize') maxSize!: number;
@uniform('minSize') minSize!: number;
@uniform('near') near!: number;
@uniform('opacity', true) opacity!: number;
@uniform('rgbBrightness', true) rgbBrightness!: number;
@uniform('rgbContrast', true) rgbContrast!: number;
@uniform('rgbGamma', true) rgbGamma!: number;
@uniform('screenHeight') screenHeight!: number;
@uniform('screenWidth') screenWidth!: number;
@uniform('size') size!: number;
@uniform('spacing') spacing!: number;
@uniform('transition') transition!: number;
@uniform('uColor') color!: Color;
@uniform('wClassification') weightClassification!: number;
@uniform('wElevation') weightElevation!: number;
@uniform('wIntensity') weightIntensity!: number;
@uniform('wReturnNumber') weightReturnNumber!: number;
@uniform('wRGB') weightRGB!: number;
@uniform('wSourceID') weightSourceID!: number;
@uniform('opacityAttenuation') opacityAttenuation!: number;
@uniform('filterByNormalThreshold') filterByNormalThreshold!: number;

@requiresShaderUpdate() useClipBox: boolean = false;
@requiresShaderUpdate() weighted: boolean = false;
@requiresShaderUpdate() pointColorType: PointColorType = PointColorType.RGB;
@requiresShaderUpdate() pointSizeType: PointSizeType = PointSizeType.ADAPTIVE;
@requiresShaderUpdate() clipMode: ClipMode = ClipMode.DISABLED;
@requiresShaderUpdate() useEDL: boolean = false;
@requiresShaderUpdate() shape: PointShape = PointShape.SQUARE;
@requiresShaderUpdate() treeType: TreeType = TreeType.OCTREE;
@requiresShaderUpdate() pointOpacityType: PointOpacityType = PointOpacityType.FIXED;
@requiresShaderUpdate() useFilterByNormal: boolean = false;

attributes = {
position: { type: 'fv', value: [] },
Expand Down Expand Up @@ -266,7 +268,31 @@ export class PointCloudMaterial extends RawShaderMaterial {
this.updateShaderSource();
}

updateShaderSource() {
dispose(): void {
super.dispose();

if (this.gradientTexture) {
this.gradientTexture.dispose();
this.gradientTexture = undefined;
}

if (this.visibleNodesTexture) {
this.visibleNodesTexture.dispose();
this.visibleNodesTexture = undefined;
}

if (this.classificationTexture) {
this.classificationTexture.dispose();
this.classificationTexture = undefined;
}

if (this.depthMap) {
this.depthMap.dispose();
this.depthMap = undefined;
}
}

updateShaderSource(): void {
this.vertexShader = this.applyDefines(require('./shaders/pointcloud.vert').default);
this.fragmentShader = this.applyDefines(require('./shaders/pointcloud.frag').default);

Expand Down
4 changes: 2 additions & 2 deletions src/point-cloud-octree-geometry-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class PointCloudOctreeGeometryNode extends EventDispatcher implements IPo
boundingSphere: Sphere;
mean: Vector3 = new Vector3();
numPoints: number = 0;
geometry: BufferGeometry = new BufferGeometry();
geometry: BufferGeometry | undefined;
loaded: boolean = false;
loading: boolean = false;
failed: boolean = false;
Expand Down Expand Up @@ -69,7 +69,7 @@ export class PointCloudOctreeGeometryNode extends EventDispatcher implements IPo
}

this.geometry.dispose();
this.geometry = new BufferGeometry();
this.geometry = undefined;
this.loaded = false;

this.oneTimeDisposeHandlers.forEach(handler => handler());
Expand Down
22 changes: 21 additions & 1 deletion src/point-cloud-octree-node.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box3, EventDispatcher, Object3D, Points, Sphere } from 'three';
import { Box3, BufferGeometry, EventDispatcher, Object3D, Points, Sphere } from 'three';
import { PointCloudOctreeGeometryNode } from './point-cloud-octree-geometry-node';
import { IPointCloudTreeNode } from './types';

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

disposeSceneNode(): void {
const node = this.sceneNode;

if (node.geometry instanceof BufferGeometry) {
const attributes = node.geometry.attributes;

// tslint:disable-next-line:forin
for (const key in attributes) {
if (key === 'position') {
delete attributes[key].array;
}

delete attributes[key];
}

node.geometry.dispose();
node.geometry = undefined as any;
}
}

traverse(cb: (node: IPointCloudTreeNode) => void, includeSelf?: boolean): void {
this.geometryNode.traverse(cb, includeSelf);
}
Expand Down
26 changes: 15 additions & 11 deletions src/point-cloud-octree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class PointCloudOctree extends PointCloudTree {
this.root.dispose();
}

this.pcoGeometry.root.traverse(n => this.potree.lru.remove(n));
this.pcoGeometry.dispose();
this.material.dispose();

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

this.disposed = false;
this.disposed = true;
}

get pointSizeType(): PointSizeType {
Expand All @@ -151,25 +152,26 @@ export class PointCloudOctree extends PointCloudTree {
geometryNode: PointCloudOctreeGeometryNode,
parent?: PointCloudOctreeNode | null,
): PointCloudOctreeNode {
const sceneNode = new Points(geometryNode.geometry, this.material);
const node = new PointCloudOctreeNode(geometryNode, sceneNode);
sceneNode.name = geometryNode.name;
sceneNode.position.copy(geometryNode.boundingBox.min);
sceneNode.frustumCulled = false;
sceneNode.onBeforeRender = this.makeOnBeforeRender(node);
const points = new Points(geometryNode.geometry, this.material);
const node = new PointCloudOctreeNode(geometryNode, points);
points.name = geometryNode.name;
points.position.copy(geometryNode.boundingBox.min);
points.frustumCulled = false;
points.onBeforeRender = this.makeOnBeforeRender(node);

if (parent) {
parent.sceneNode.add(sceneNode);
parent.sceneNode.add(points);
parent.children[geometryNode.index] = node;

geometryNode.oneTimeDisposeHandlers.push(() => {
node.disposeSceneNode();
parent.sceneNode.remove(node.sceneNode);
// Replace the tree node (rendered and in the GPU) with the geometry node.
parent.children[geometryNode.index] = geometryNode;
});
} else {
this.root = node;
this.add(sceneNode);
this.add(points);
}

return node;
Expand Down Expand Up @@ -297,8 +299,10 @@ export class PointCloudOctree extends PointCloudTree {
}

const texture = material.visibleNodesTexture;
texture.image.data.set(data);
texture.needsUpdate = true;
if (texture) {
texture.image.data.set(data);
texture.needsUpdate = true;
}
}

private helperSphere = new Sphere();
Expand Down
6 changes: 3 additions & 3 deletions src/utils/lru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,14 @@ export class LRU {
while (this.numPoints > this.pointBudget * 2) {
const node = this.getLRUItem();
if (node) {
this.disposeDescendants(node);
this.disposeSubtree(node);
}
}
}

disposeDescendants(node: Node): void {
disposeSubtree(node: Node): void {
// Collect all the nodes which are to be disposed and removed.
const nodesToDispose: Node[] = [];
const nodesToDispose: Node[] = [node];
node.traverse(n => {
if (n.loaded) {
nodesToDispose.push(n);
Expand Down
2 changes: 1 addition & 1 deletion webpack.config.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = {
filename: 'example.bundle.js',
path: buildPath,
},
devtool: 'cheap-eval-source-map',
devtool: 'source-map',
devServer: {
contentBase: buildPath,
compress: true,
Expand Down
Loading

0 comments on commit 437ebea

Please sign in to comment.