Skip to content

Commit

Permalink
fix(view): improve resource disposal by removing textures and allow t…
Browse files Browse the repository at this point in the history
…o remove cache
  • Loading branch information
jailln committed Jan 24, 2023
1 parent 47db4c7 commit 2497d00
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 58 deletions.
30 changes: 19 additions & 11 deletions src/Core/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { getMaxColorSamplerUnitsCount } from 'Renderer/LayeredMaterial';
import Scheduler from 'Core/Scheduler/Scheduler';
import Picking from 'Core/Picking';
import LabelLayer from 'Layer/LabelLayer';
import ObjectRemovalHelper from 'Process/ObjectRemovalHelper';

export const VIEW_EVENTS = {
/**
Expand Down Expand Up @@ -271,35 +272,41 @@ class View extends THREE.EventDispatcher {
* - remove all layers
* - remove all frame requester
* - remove all events
* @param {boolean} [clearCache=false] Whether to clear all the caches or not (layers cache, style cache, tilesCache)
*/
dispose() {
dispose(clearCache = false) {
const id = viewers.indexOf(this);
if (id == -1) {
console.warn('View already disposed');
return;
}
// controls dispose
if (this.controls && this.controls.dispose) {
this.controls.dispose();
if (this.controls) {
if (typeof this.controls.dispose === 'function') {
this.controls.dispose();
}
delete this.controls;
}
// remove alls frameRequester
this.removeAllFrameRequesters();
// remove alls events
this.removeAllEvents();
// remove alls layers
// remove all layers
const layers = this.getLayers(l => !l.isTiledGeometryLayer && !l.isAtmosphere);
for (const layer of layers) {
this.removeLayer(layer.id);
this.removeLayer(layer.id, clearCache);
}
const atmospheres = this.getLayers(l => l.isAtmosphere);
for (const atmosphere of atmospheres) {
this.removeLayer(atmosphere.id);
this.removeLayer(atmosphere.id, clearCache);
}
const tileLayers = this.getLayers(l => l.isTiledGeometryLayer);
for (const tileLayer of tileLayers) {
this.removeLayer(tileLayer.id);
this.removeLayer(tileLayer.id, clearCache);
}
viewers.splice(id, 1);
// Remove remaining objects in the scene (e.g. helpers, debug, etc.)
this.scene.traverse(ObjectRemovalHelper.cleanup);
}

/**
Expand Down Expand Up @@ -378,16 +385,17 @@ class View extends THREE.EventDispatcher {
* Removes a specific imagery layer from the current layer list. This removes layers inserted with attach().
* @example
* view.removeLayer('layerId');
* @param {string} layerId The identifier
* @return {boolean}
* @param {string} layerId The identifier
* @param {boolean} [clearCache=false] Whether to clear all the layer cache or not
* @return {boolean}
*/
removeLayer(layerId) {
removeLayer(layerId, clearCache) {
const layer = this.getLayerById(layerId);
if (layer) {
const parentLayer = layer.parent;

// Remove and dispose all nodes
layer.delete();
layer.delete(clearCache);

// Detach layer if it's attached
if (parentLayer && !parentLayer.detach(layer)) {
Expand Down
45 changes: 10 additions & 35 deletions src/Layer/GeometryLayer.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,7 @@
import Layer from 'Layer/Layer';
import Picking from 'Core/Picking';
import { CACHE_POLICIES } from 'Core/Scheduler/Cache';

function disposeMesh(obj) {
if (obj.dispose) {
obj.dispose();
} else {
if (obj.geometry) {
obj.geometry.dispose();
}
if (obj.material) {
if (Array.isArray(obj.material)) {
for (const material of obj.material) {
material.dispose();
}
} else {
obj.material.dispose();
}
}
}
}

function traverse(obj, callback) {
for (const child of obj.children) {
traverse(child, callback);
}
callback(obj);
}
import ObjectRemovalHelper from 'Process/ObjectRemovalHelper';

/**
* Fires when the opacity of the layer has changed.
Expand Down Expand Up @@ -174,23 +149,23 @@ class GeometryLayer extends Layer {
}

/**
* All layer's meshs are removed from scene and disposed from video device.
* All layer's 3D objects are removed from the scene and disposed from the video device.
* @param {boolean} [clearCache=false] Whether to clear the layer cache or not
*/
delete() {
delete(clearCache) {
if (clearCache) {
this.cache.clear();
}

// if Layer is attached
if (this.parent) {
traverse(this.parent.object3d, (obj) => {
if (obj.layer && obj.layer.id == this.id) {
obj.parent.remove(obj);
disposeMesh(obj);
}
});
ObjectRemovalHelper.removeChildrenAndCleanupRecursively(this, this.parent.object3d);
}

if (this.object3d.parent) {
this.object3d.parent.remove(this.object3d);
}
this.object3d.traverse(disposeMesh);
ObjectRemovalHelper.removeChildrenAndCleanupRecursively(this, this.object3d);
}

/**
Expand Down
9 changes: 8 additions & 1 deletion src/Layer/LabelLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,14 @@ class LabelLayer extends Layer {
}
}

delete() {
/**
* All layer's objects and domElements are removed.
* @param {boolean} [clearCache=false] Whether to clear the layer cache or not
*/
delete(clearCache) {
if (clearCache) {
this.cache.clear();
}
this.domElement.parentElement.removeChild(this.domElement);

this.parent.level0Nodes.forEach(obj => this.removeLabelsFromNodeRecursive(obj));
Expand Down
3 changes: 2 additions & 1 deletion src/Layer/Layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,10 @@ class Layer extends THREE.EventDispatcher {

/**
* Remove and dispose all objects from layer.
* @param {boolean} [clearCache=false] Whether to clear the layer cache or not
*/
// eslint-disable-next-line
delete() {
delete(clearCache) {
console.warn('Function delete doesn\'t exist for this layer');
}
}
Expand Down
19 changes: 15 additions & 4 deletions src/Layer/OrientedImageLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,14 @@ class OrientedImageLayer extends GeometryLayer {
}
super(id, new THREE.Group(), config);

this.background = config.background || createBackground(config.backgroundDistance);
this.isOrientedImageLayer = true;

this.background = config.background || createBackground(config.backgroundDistance);

if (this.background) {
// Add layer id to easily identify the objects later on (e.g. to delete the geometries when deleting the layer)
this.background.layer = this.background.layer ?? {};
this.background.layer.id = this.background.layer.id ?? id;
this.object3d.add(this.background);
}

Expand Down Expand Up @@ -205,9 +209,16 @@ class OrientedImageLayer extends GeometryLayer {
* Delete background, but doesn't delete OrientedImageLayer.material. For the moment, this material visibility is set to false.
* You need to replace OrientedImageLayer.material applied on each object, if you want to continue displaying them.
* This issue (see #1018 {@link https://github.com/iTowns/itowns/issues/1018}) will be fixed when OrientedImageLayer will be a ColorLayer.
*/
delete() {
super.delete();
* @param {boolean} [clearCache=false] Whether to clear the layer cache or not
*/
delete(clearCache) {
if (this.background) {
// only delete geometries if it has some
super.delete();
}
if (clearCache) {
this.cache.clear();
}
this.material.visible = false;
console.warn('You need to replace OrientedImageLayer.material applied on each object. This issue will be fixed when OrientedImageLayer will be a ColorLayer. the material visibility is set to false. To follow issue see https://github.com/iTowns/itowns/issues/1018');
}
Expand Down
6 changes: 5 additions & 1 deletion src/Layer/RasterLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ class RasterLayer extends Layer {

/**
* All layer's textures are removed from scene and disposed from video device.
* @param {boolean} [clearCache=false] Whether to clear the layer cache or not
*/
delete() {
delete(clearCache) {
if (clearCache) {
this.cache.clear();
}
for (const root of this.parent.level0Nodes) {
root.traverse(removeLayeredMaterialNodeLayer(this.id));
}
Expand Down
24 changes: 19 additions & 5 deletions src/Process/ObjectRemovalHelper.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
function disposeSingleMaterialAndTextures(material) {
material.dispose();
// dispose textures
for (const key of Object.keys(material)) {
const val = material[key];
if (val && val.isTexture) {
val.dispose();
}
}
}

export default {
/**
* Cleanup obj to release three.js allocated resources
Expand All @@ -6,7 +17,8 @@ export default {
cleanup(obj) {
obj.layer = null;

if (typeof obj.dispose === 'function') {
// THREE.Scene dispose function displays a deprecation message
if (!obj.isScene && typeof obj.dispose === 'function') {
obj.dispose();
} else {
if (obj.geometry) {
Expand All @@ -20,10 +32,10 @@ export default {
if (obj.material) {
if (Array.isArray(obj.material)) {
for (const material of obj.material) {
material.dispose();
disposeSingleMaterialAndTextures(material);
}
} else {
obj.material.dispose();
disposeSingleMaterialAndTextures(obj.material);
}
}
obj.dispatchEvent({ type: 'dispose' });
Expand All @@ -44,7 +56,7 @@ export default {
},

/**
* Remove obj's children belonging to a layer and cleanup objexts.
* Remove an obj and all its children belonging to a layer and only cleanup the obj (and not its children).
* obj will be disposed but its children **won't**!
* @param {Layer} layer The layer that objects must belong to. Other object are ignored
* @param {Object3D} obj The Object3D we want to clean
Expand All @@ -61,13 +73,15 @@ export default {
},

/**
* Recursively remove obj's children belonging to a layer.
* Recursively remove an obj and all its children belonging to a layer.
* All removed obj will have their geometry/material disposed.
* @param {Layer} layer The layer that objects must belong to. Other object are ignored
* @param {Object3D} obj The Object3D we want to clean
* @return {Array} an array of removed Object3D from obj (not including the recursive removals)
*/
removeChildrenAndCleanupRecursively(layer, obj) {
// Objects are filtered by id because the obj hierarchy may also contain labels that have been added as childs
// of the objects which have their own removal logic
let toRemove = obj.children.filter(c => (c.layer && c.layer.id) === layer.id);
if (obj.link) {
toRemove = toRemove.concat(obj.link);
Expand Down

0 comments on commit 2497d00

Please sign in to comment.