Browse Source

several optimisation to speed the dispose of a large number of meshes

Julien Barrois 6 năm trước cách đây
mục cha
commit
d642b7593b

+ 57 - 28
src/Materials/babylon.material.ts

@@ -768,6 +768,12 @@ module BABYLON {
         /** @hidden */
         public _indexInSceneMaterialArray = -1;
 
+        /** @hidden */
+        public disposingLineMeshColorShader: boolean = false;
+
+        /** @hidden */
+        public meshMap: Nullable<{[id: string]: AbstractMesh | undefined}>;
+
         /**
          * Creates a material instance
          * @param name defines the name of the material
@@ -793,6 +799,10 @@ module BABYLON {
             if (!doNotAdd) {
                 this._scene.addMaterial(this);
             }
+
+            if (scene.useMaterialMeshMap) {
+                this.meshMap = {};
+            }
         }
 
         /**
@@ -1059,17 +1069,20 @@ module BABYLON {
          * @returns an array of meshes bound to the material
          */
         public getBindedMeshes(): AbstractMesh[] {
-            var result = new Array<AbstractMesh>();
-
-            for (var index = 0; index < this._scene.meshes.length; index++) {
-                var mesh = this._scene.meshes[index];
-
-                if (mesh.material === this) {
-                    result.push(mesh);
+            if (this.meshMap) {
+                var result = new Array<AbstractMesh>();
+                for (let meshId in this.meshMap) {
+                    const mesh = this.meshMap[meshId];
+                    if (mesh) {
+                        result.push(mesh);
+                    }
                 }
+                return result;
+            }
+            else {
+                const meshes = this._scene.meshes;
+                return meshes.filter((mesh) => mesh.material === this);
             }
-
-            return result;
         }
 
         /**
@@ -1271,25 +1284,23 @@ module BABYLON {
             // Remove from scene
             scene.removeMaterial(this);
 
-            // Remove from meshes
-            for (let index = 0; index < scene.meshes.length; index++) {
-                var mesh = scene.meshes[index];
-
-                if (mesh.material === this) {
-                    mesh.material = null;
-
-                    if ((<Mesh>mesh).geometry) {
-                        var geometry = <Geometry>((<Mesh>mesh).geometry);
-
-                        if (this.storeEffectOnSubMeshes) {
-                            for (var subMesh of mesh.subMeshes) {
-                                geometry._releaseVertexArrayObject(subMesh._materialEffect);
-                                if (forceDisposeEffect && subMesh._materialEffect) {
-                                    scene.getEngine()._releaseEffect(subMesh._materialEffect);
-                                }
-                            }
-                        } else {
-                            geometry._releaseVertexArrayObject(this._effect);
+            if (!this.disposingLineMeshColorShader) {
+                // Remove from meshes
+                if (this.meshMap) {
+                    for (let meshId in this.meshMap) {
+                        const mesh = this.meshMap[meshId];
+                        if (mesh) {
+                            mesh.material = null; // will set the entry in the map to undefined
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
+                        }
+                    }
+                }
+                else {
+                    const meshes = scene.meshes;
+                    for (let mesh of meshes) {
+                        if (mesh.material === this) {
+                            mesh.material = null;
+                            this.releaseVertexArrayObject(mesh, forceDisposeEffect);
                         }
                     }
                 }
@@ -1319,6 +1330,24 @@ module BABYLON {
             }
         }
 
+        /** @hidden */
+        private  releaseVertexArrayObject(mesh: AbstractMesh, forceDisposeEffect?: boolean) {
+            if ((<Mesh>mesh).geometry) {
+                var geometry = <Geometry>((<Mesh>mesh).geometry);
+                const scene = this.getScene();
+                if (this.storeEffectOnSubMeshes) {
+                    for (var subMesh of mesh.subMeshes) {
+                        geometry._releaseVertexArrayObject(subMesh._materialEffect);
+                        if (forceDisposeEffect && subMesh._materialEffect) {
+                            scene.getEngine()._releaseEffect(subMesh._materialEffect);
+                        }
+                    }
+                } else {
+                    geometry._releaseVertexArrayObject(this._effect);
+                }
+            }
+        }
+
         /**
          * Serializes this material
          * @returns the serialized material object

+ 4 - 2
src/Materials/babylon.uniformBuffer.ts

@@ -616,10 +616,12 @@ module BABYLON {
                 return;
             }
 
-            let index = this._engine._uniformBuffers.indexOf(this);
+            const uniformBuffers = this._engine._uniformBuffers;
+            let index = uniformBuffers.indexOf(this);
 
             if (index !== -1) {
-                this._engine._uniformBuffers.splice(index, 1);
+                uniformBuffers[index] = uniformBuffers[uniformBuffers.length - 1];
+                uniformBuffers.pop();
             }
 
             if (!this._buffer) {

+ 13 - 4
src/Mesh/babylon.abstractMesh.ts

@@ -264,8 +264,17 @@ module BABYLON {
                 return;
             }
 
+            // remove from material mesh map id needed
+            if (this._material && this._material.meshMap) {
+                this._material.meshMap[this.uniqueId] = undefined;
+            }
+
             this._material = value;
 
+            if (value && value.meshMap) {
+                value.meshMap[this.uniqueId] = this;
+            }
+
             if (this.onMaterialChangedObservable.hasObservers) {
                 this.onMaterialChangedObservable.notifyObservers(this);
             }
@@ -1196,7 +1205,7 @@ module BABYLON {
         private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh> = null) => {
             //TODO move this to the collision coordinator!
             if (this.getScene().workerCollisions) {
-                newPosition.multiplyInPlace(this._collider._radius);
+                newPosition.multiplyInPlace(this._collider._radius);
             }
 
             newPosition.subtractToRef(this._oldPositionForCollisions, this._diffPositionForCollisions);
@@ -1250,7 +1259,7 @@ module BABYLON {
 
                 // Bounding test
                 if (len > 1 && !subMesh._checkCollision(collider)) {
-                    continue;
+                    continue;
                 }
 
                 this._collideForSubMesh(subMesh, transformMatrix, collider);
@@ -1262,7 +1271,7 @@ module BABYLON {
         public _checkCollision(collider: Collider): AbstractMesh {
             // Bounding box test
             if (!this._boundingInfo || !this._boundingInfo._checkCollision(collider)) {
-                return this;
+                return this;
             }
 
             // Transformation matrix
@@ -1307,7 +1316,7 @@ module BABYLON {
 
                 // Bounding test
                 if (len > 1 && !subMesh.canIntersects(ray)) {
-                    continue;
+                    continue;
                 }
 
                 var currentIntersectInfo = subMesh.intersects(ray, (<Vector3[]>this._positions), (<IndicesArray>this.getIndices()), fastCheck);

+ 4 - 16
src/Mesh/babylon.instancedMesh.ts

@@ -7,13 +7,13 @@ module BABYLON {
         private _sourceMesh: Mesh;
         private _currentLOD: Mesh;
 
-        private _indexInSourceMeshInstanceArray = -1;
+        /** @hidden */
+        public _indexInSourceMeshInstanceArray = -1;
 
         constructor(name: string, source: Mesh) {
             super(name, source.getScene());
 
-            this._indexInSourceMeshInstanceArray = source.instances.length;
-            source.instances.push(this);
+            source.addInstance(this);
 
             this._sourceMesh = source;
 
@@ -320,20 +320,8 @@ module BABYLON {
          * Returns nothing.
          */
         public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
-
             // Remove from mesh
-            const index = this._indexInSourceMeshInstanceArray;
-            if (index != -1) {
-                if (index !== this._sourceMesh.instances.length - 1) {
-                    const last = this._sourceMesh.instances[this._sourceMesh.instances.length - 1];
-                    this._sourceMesh.instances[index] = last;
-                    last._indexInSceneTransformNodesArray = index;
-
-                }
-                this._indexInSourceMeshInstanceArray = -1;
-                this._sourceMesh.instances.pop();
-            }
-
+            this._sourceMesh.removeInstance(this);
             super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
     }

+ 2 - 1
src/Mesh/babylon.linesMesh.ts

@@ -163,8 +163,9 @@ module BABYLON {
          * @param doNotRecurse If children should be disposed
          */
         public dispose(doNotRecurse?: boolean): void {
+            this._colorShader.disposingLineMeshColorShader = true;
             this._colorShader.dispose();
-
+            this._colorShader.disposingLineMeshColorShader = false;
             super.dispose(doNotRecurse);
         }
 

+ 54 - 6
src/Mesh/babylon.mesh.ts

@@ -213,6 +213,8 @@ module BABYLON {
 
         // Will be used to save a source mesh reference, If any
         private _source: Nullable<Mesh> = null;
+        // Will be used to for fast cloned mesh lookup
+        private meshMap: Nullable<{[id: string]: Mesh | undefined}>;
 
         /**
          * Gets the source mesh (the one used to clone this one from)
@@ -266,6 +268,12 @@ module BABYLON {
 
                 // Source mesh
                 this._source = source;
+                if (scene.useClonedMeshhMap) {
+                    if (!source.meshMap) {
+                        source.meshMap = {};
+                    }
+                    source.meshMap[this.uniqueId] = this;
+                }
 
                 // Construction Params
                 // Clone parameters allowing mesh to be updated in case of parametric shapes.
@@ -2014,13 +2022,31 @@ module BABYLON {
             }
 
             // Sources
-            var meshes = this.getScene().meshes;
-            meshes.forEach((abstractMesh: AbstractMesh) => {
-                let mesh = abstractMesh as Mesh;
-                if (mesh._source && mesh._source === this) {
-                    mesh._source = null;
+            if (this._scene.useClonedMeshhMap) {
+                if (this.meshMap) {
+                    for (const uniqueId in this.meshMap) {
+                        const mesh = this.meshMap[uniqueId];
+                        if (mesh) {
+                            mesh._source = null;
+                            this.meshMap[uniqueId] = undefined;
+                        }
+                    }
                 }
-            });
+
+                if (this._source && this._source.meshMap) {
+                    this._source.meshMap[this.uniqueId] = undefined;
+                }
+            }
+            else {
+                var meshes = this.getScene().meshes;
+                for (const abstractMesh of meshes) {
+                    let mesh = abstractMesh as Mesh;
+                    if (mesh._source && mesh._source === this) {
+                        mesh._source = null;
+                    }
+                }
+            }
+
             this._source = null;
 
             // Instances
@@ -3780,5 +3806,27 @@ module BABYLON {
 
             return meshSubclass;
         }
+
+        /** @hidden */
+        public addInstance(instance: InstancedMesh) {
+            instance._indexInSourceMeshInstanceArray = this.instances.length;
+            this.instances.push(instance);
+        }
+
+        /** @hidden */
+        public removeInstance(instance: InstancedMesh) {
+            // Remove from mesh
+            const index = instance._indexInSourceMeshInstanceArray;
+            if (index != -1) {
+                if (index !== this.instances.length - 1) {
+                    const last = this.instances[this.instances.length - 1];
+                    this.instances[index] = last;
+                    last._indexInSourceMeshInstanceArray = index;
+                }
+
+                instance._indexInSourceMeshInstanceArray = -1;
+                this.instances.pop();
+            }
+        }
     }
 }

+ 22 - 1
src/babylon.scene.ts

@@ -70,6 +70,18 @@ module BABYLON {
          * It will improve performance when the number of geometries becomes important.
          */
         useGeometryIdsMap?: boolean;
+
+        /**
+         * Defines that each material of the scene should keep up-to-date a map of referencing meshes for fast diposing
+         * It will improve performance when the number of mesh becomes important, but might consume a bit more memory
+         */
+        useMaterialMeshMap?: boolean;
+
+        /**
+         * Defines that each mesh of the scene should keep up-to-date a map of referencing cloned meshes for fast diposing
+         * It will improve performance when the number of mesh becomes important, but might consume a bit more memory
+         */
+        useClonedMeshhMap?: boolean;
     }
 
     /**
@@ -1009,6 +1021,11 @@ module BABYLON {
          */
         public requireLightSorting = false;
 
+        /** @hidden */
+        public readonly useMaterialMeshMap: boolean;
+        /** @hidden */
+        public readonly useClonedMeshhMap: boolean;
+
         private _pointerOverMesh: Nullable<AbstractMesh>;
 
         private _pickedDownMesh: Nullable<AbstractMesh>;
@@ -1216,6 +1233,9 @@ module BABYLON {
             if (options && options.useGeometryIdsMap === true) {
                 this.geometriesById = {};
             }
+
+            this.useMaterialMeshMap = options && options.useGeometryIdsMap || false;
+            this.useClonedMeshhMap = options && options.useClonedMeshhMap || false;
         }
 
         private _defaultMeshCandidates: ISmartArrayLike<AbstractMesh> = {
@@ -2983,7 +3003,8 @@ module BABYLON {
             var index = this.meshes.indexOf(toRemove);
             if (index !== -1) {
                 // Remove from the scene if mesh found
-                this.meshes.splice(index, 1);
+                this.meshes[index] = this.meshes[this.meshes.length - 1];
+                this.meshes.pop();
             }
 
             this.onMeshRemovedObservable.notifyObservers(toRemove);