ソースを参照

Merge pull request #36 from NASA-AMMOS/disposal-performance

Disposal performance
Garrett Johnson 5 年 前
コミット
95015bd5b9
2 ファイル変更37 行追加23 行削除
  1. 25 17
      src/three/TilesRenderer.js
  2. 12 6
      src/utilities/LRUCache.js

+ 25 - 17
src/three/TilesRenderer.js

@@ -25,6 +25,7 @@ const vecZ = new Vector3();
 
 const X_AXIS = new Vector3( 1, 0, 0 );
 const Y_AXIS = new Vector3( 0, 1, 0 );
+const useImageBitmap = typeof createImageBitmap !== 'undefined';
 
 function emptyRaycast() {}
 
@@ -314,7 +315,6 @@ export class TilesRenderer extends TilesRendererBase {
 		const loadIndex = tile._loadIndex;
 		const manager = new LoadingManager();
 
-		const useImageBitmap = typeof createImageBitmap !== 'undefined';
 		if ( useImageBitmap ) {
 
 			// TODO: We should verify that `flipY` is false on the resulting texture after load because it can't be modified after
@@ -381,6 +381,7 @@ export class TilesRenderer extends TilesRendererBase {
 
 			const materials = [];
 			const geometry = [];
+			const textures = [];
 			scene.traverse( c => {
 
 				if ( c.geometry ) {
@@ -391,14 +392,27 @@ export class TilesRenderer extends TilesRendererBase {
 
 				if ( c.material ) {
 
+					const material = c.material;
 					materials.push( c.material );
 
+					for ( const key in material ) {
+
+						const value = material[ key ];
+						if ( value && value.isTexture ) {
+
+							textures.push( value );
+
+						}
+
+					}
+
 				}
 
 			} );
 
 			cached.materials = materials;
 			cached.geometry = geometry;
+			cached.textures = textures;
 
 		} );
 
@@ -410,17 +424,9 @@ export class TilesRenderer extends TilesRendererBase {
 		const cached = tile.cached;
 		if ( cached.scene ) {
 
-			// TODO: This should never get called if the scene hasn't been removed from the scene yet, right?
-			// TODO: this can possibly be slow because so many discard can happen at once -- maybe iteratively do it? Or lower the eviction ratio / put a cap
-			// on the amount disposed per frame.
-			const scene = cached.scene;
 			const materials = cached.materials;
 			const geometry = cached.geometry;
-			if ( scene.parent ) {
-
-				scene.parent.remove( scene );
-
-			}
+			const textures = cached.textures;
 
 			for ( let i = 0, l = geometry.length; i < l; i ++ ) {
 
@@ -430,23 +436,25 @@ export class TilesRenderer extends TilesRendererBase {
 
 			for ( let i = 0, l = materials.length; i < l; i ++ ) {
 
-				const material = materials[ i ];
-				for ( const key in material ) {
+				materials[ i ].dispose();
 
-					const value = material[ key ];
-					if ( value && value.isTexture ) {
+			}
 
-						value.dispose();
+			for ( let i = 0, l = textures.length; i < l; i ++ ) {
 
-					}
+				const texture = textures[ i ];
+				texture.dispose();
+				if ( useImageBitmap && 'close' in texture.image ) {
+
+					texture.image.close();
 
 				}
-				material.dispose();
 
 			}
 
 			cached.scene = null;
 			cached.materials = null;
+			cached.textures = null;
 			cached.geometry = null;
 
 		}

+ 12 - 6
src/utilities/LRUCache.js

@@ -1,6 +1,13 @@
 // TODO: can we remove the use of `indexOf` here because it's potentially slow? Possibly use time and sort as needed?
 // Keep a used list that we can sort as needed when it's dirty, a map of item to last used time, and a binary search
 // of the array to find an item that needs to be removed
+
+// Fires at the end of the frame and before the next one
+function enqueueMicrotask( callback ) {
+
+	Promise.resolve().then( callback );
+
+}
 class LRUCache {
 
 	constructor() {
@@ -8,7 +15,7 @@ class LRUCache {
 		// options
 		this.maxSize = 800;
 		this.minSize = 600;
-		this.unloadPercent = 0.2;
+		this.unloadPercent = 0.05;
 
 		this.usedSet = new Set();
 		this.itemSet = new Set();
@@ -109,14 +116,14 @@ class LRUCache {
 		const usedSet = this.usedSet;
 		const callbacks = this.callbacks;
 		const unused = itemList.length - usedSet.size;
+		const excess = itemList.length - targetSize;
 
-		if ( itemList.length > targetSize && unused > 0 ) {
+		if ( excess > 0 && unused > 0 ) {
 
 			// TODO: sort by priority
 
-			let nodesToUnload = Math.max( itemList.length - targetSize, targetSize ) * unloadPercent;
+			let nodesToUnload = Math.min( targetSize * unloadPercent, unused );
 			nodesToUnload = Math.ceil( nodesToUnload );
-			nodesToUnload = Math.min( unused, nodesToUnload );
 
 			const removedItems = itemList.splice( 0, nodesToUnload );
 			for ( let i = 0, l = removedItems.length; i < l; i ++ ) {
@@ -128,7 +135,6 @@ class LRUCache {
 
 			}
 
-
 		}
 
 	}
@@ -138,7 +144,7 @@ class LRUCache {
 		if ( ! this.scheduled ) {
 
 			this.scheduled = true;
-			Promise.resolve().then( () => {
+			enqueueMicrotask( () => {
 
 				this.scheduled = false;
 				this.unloadUnusedContent( prioritySortCb );