Pārlūkot izejas kodu

Tools and rendering improvement with PerfCounter

Tools:
 - PerfCount class added. records min, max, current, average, last second average of a monitored counter

Scene:
 - xxxDuration are now using PerfCounter, one property is added for each to access the underlying PerfCounter
 - getxXXDuration() has still the same behavior as before.
nockawa 9 gadi atpakaļ
vecāks
revīzija
fd8f59539a

+ 2 - 2
src/Rendering/babylon.renderingManager.ts

@@ -40,7 +40,7 @@
                     this._scene._activeParticles += particleSystem.render();
                 }
             }
-            this._scene._particlesDuration += Tools.Now - beforeParticlesDate;
+            this._scene._particlesDuration.updateCounter(Tools.Now - beforeParticlesDate, beforeParticlesDate, false);
         }
 
         private _renderSprites(index: number): void {
@@ -59,7 +59,7 @@
                     spriteManager.render();
                 }
             }
-            this._scene._spritesDuration += Tools.Now - beforeSpritessDate;
+            this._scene._spritesDuration.updateCounter(Tools.Now - beforeSpritessDate, beforeSpritessDate, false);
         }
 
         private _clearDepthBuffer(): void {

+ 113 - 0
src/Tools/babylon.tools.ts

@@ -1020,6 +1020,119 @@
     }
 
     /**
+     * This class is used to track a performance counter which is number based.
+     * The user has access to many properties which give statistics of different nature
+     * The implementer uses the updateCounter() method to update the many statistics.
+     */
+    export class PerfCounter {
+        /**
+         * Returns the smallest value ever
+         */
+        public get min(): number {
+            return this._min;
+        }
+
+        /**
+         * Returns the biggest value ever
+         */
+        public get max(): number {
+            return this._max;
+        }
+
+        /**
+         * Returns the average value since the performance counter is running
+         */
+        public get average(): number {
+            return this._average;
+        }
+
+        /**
+         * Returns the average value of the last second the counter was monitored
+         */
+        public get lastSecAverage(): number {
+            return this._lastSecAverage;
+        }
+
+        /**
+         * Returns the current value
+         */
+        public get current(): number {
+            return this._current;
+        }
+
+        constructor() {
+            this._min                = 0;
+            this._max                = 0;
+            this._average            = 0;
+            this._lastSecAverage     = 0;
+            this._current            = 0;
+            this._totalValueCount    = 0;
+            this._totalAccumulated   = 0;
+            this._lastSecAccumulated = 0;
+            this._lastSecTime        = 0;
+            this._lastSecValueCount  = 0;
+        }
+
+        /**
+         * This method must be called by the implementer of the counter only!
+         * It's used to update the different statistics
+         * @param newValue the new value recorded
+         * @param currentTime the time at which the value was recorded
+         * @param newFrame true by default to monitor a new frame, if false 'newValue' will be added to the current frame
+         */
+        public updateCounter(newValue: number, currentTime: number, newFrame: boolean=true) {
+
+            // First time init?
+            if (this._lastSecTime === 0) {
+                this._min = this._max = this._current = this._average = this._lastSecAverage = this._totalAccumulated = this._lastSecAccumulated = newValue;
+                if (newFrame) {
+                    this._lastSecTime = currentTime;
+                }
+                this._totalValueCount = this._lastSecValueCount = 1;
+                return;
+            }
+
+            // Min/Max update
+            this._min = Math.min(this._min, newValue);
+            this._max = Math.max(this._max, newValue);
+
+            // Update average
+            if (newFrame) {
+                this._totalValueCount++;
+            }
+            this._totalAccumulated += newValue;
+            this._average = this._totalAccumulated / this._totalValueCount;
+
+            // Reset last sec?
+            if (newFrame && ((currentTime - this._lastSecTime) > 1000)) {
+                this._lastSecAverage = this._lastSecAccumulated / this._lastSecValueCount;
+                this._lastSecTime = currentTime;
+                this._lastSecAccumulated = newValue;
+                this._lastSecValueCount = 1;
+            } else {
+                this._lastSecAccumulated += newValue;
+                if (newFrame) {
+                    this._lastSecValueCount++;
+                }
+            }
+
+            // Current update
+            this._current = newValue;
+        }
+
+        private _min: number;
+        private _max: number;
+        private _average: number;
+        private _current: number;
+        private _totalValueCount: number;
+        private _totalAccumulated: number;
+        private _lastSecAverage: number;
+        private _lastSecAccumulated: number;
+        private _lastSecTime: number;
+        private _lastSecValueCount: number;
+    }
+
+    /**
      * Use this className as a decorator on a given class definition to add it a name.
      * You can then use the Tools.getClassName(obj) on an instance to retrieve its class name.
      * This method is the only way to get it done in all cases, even if the .js file declaring the class is minified

+ 39 - 18
src/babylon.scene.ts

@@ -422,12 +422,12 @@
         private _totalVertices = 0;
         public _activeIndices = 0;
         public _activeParticles = 0;
-        private _lastFrameDuration = 0;
-        private _evaluateActiveMeshesDuration = 0;
-        private _renderTargetsDuration = 0;
-        public _particlesDuration = 0;
-        private _renderDuration = 0;
-        public _spritesDuration = 0;
+        private _lastFrameDuration = new PerfCounter();
+        private _evaluateActiveMeshesDuration = new PerfCounter();
+        private _renderTargetsDuration = new PerfCounter();
+        public _particlesDuration = new PerfCounter();
+        private _renderDuration = new PerfCounter();
+        public _spritesDuration = new PerfCounter();
         private _animationRatio = 0;
         private _animationStartDate: number;
         public _cachedMaterial: Material;
@@ -601,10 +601,18 @@
 
         // Stats
         public getLastFrameDuration(): number {
+            return this._lastFrameDuration.current;
+        }
+
+        public get lastFramePerfCounter(): PerfCounter {
             return this._lastFrameDuration;
         }
 
         public getEvaluateActiveMeshesDuration(): number {
+            return this._evaluateActiveMeshesDuration.current;
+        }
+
+        public get evaluateActiveMeshesDurationPerfCounter(): PerfCounter {
             return this._evaluateActiveMeshesDuration;
         }
 
@@ -613,18 +621,30 @@
         }
 
         public getRenderTargetsDuration(): number {
-            return this._renderTargetsDuration;
+            return this._renderTargetsDuration.current;
         }
 
         public getRenderDuration(): number {
+            return this._renderDuration.current;
+        }
+
+        public get renderDurationPerfCounter(): PerfCounter {
             return this._renderDuration;
         }
 
         public getParticlesDuration(): number {
+            return this._particlesDuration.current;
+        }
+
+        public get particlesDurationPerfCounter(): PerfCounter {
             return this._particlesDuration;
         }
 
         public getSpritesDuration(): number {
+            return this._spritesDuration.current;
+        }
+
+        public get spriteDuractionPerfCounter(): PerfCounter {
             return this._spritesDuration;
         }
 
@@ -1863,7 +1883,7 @@
                 }
                 Tools.EndPerformanceCounter("Particles", this.particleSystems.length > 0);
             }
-            this._particlesDuration += Tools.Now - beforeParticlesDate;
+            this._particlesDuration.updateCounter(Tools.Now - beforeParticlesDate, null, false);
         }
 
         private _activeMesh(mesh: AbstractMesh): void {
@@ -1914,6 +1934,7 @@
 
         private _renderForCamera(camera: Camera): void {
             var engine = this._engine;
+            var startTime = Tools.Now;
 
             this.activeCamera = camera;
 
@@ -1936,7 +1957,7 @@
             var beforeEvaluateActiveMeshesDate = Tools.Now;
             Tools.StartPerformanceCounter("Active meshes evaluation");
             this._evaluateActiveMeshes();
-            this._evaluateActiveMeshesDuration += Tools.Now - beforeEvaluateActiveMeshesDate;
+            this._evaluateActiveMeshesDuration.updateCounter(Tools.Now - beforeEvaluateActiveMeshesDate, startTime, false);
             Tools.EndPerformanceCounter("Active meshes evaluation");
 
             // Software skinning
@@ -1965,7 +1986,7 @@
                 this._renderId++;
                 engine.restoreDefaultFramebuffer(); // Restore back buffer
             }
-            this._renderTargetsDuration += Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.updateCounter(Tools.Now - beforeRenderTargetDate, startTime, false);
 
             // Prepare Frame
             this.postProcessManager._prepareFrame();
@@ -2023,7 +2044,7 @@
                 engine.setDepthBuffer(true);
             }
 
-            this._renderDuration += Tools.Now - beforeRenderDate;
+            this._renderDuration.updateCounter(Tools.Now - beforeRenderDate, startTime, false);
 
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
@@ -2098,12 +2119,12 @@
 
         public render(): void {
             var startDate = Tools.Now;
-            this._particlesDuration = 0;
-            this._spritesDuration = 0;
+            this._particlesDuration.updateCounter(0, startDate);
+            this._spritesDuration.updateCounter(0, startDate);
             this._activeParticles = 0;
-            this._renderDuration = 0;
-            this._renderTargetsDuration = 0;
-            this._evaluateActiveMeshesDuration = 0;
+            this._renderDuration.updateCounter(0, startDate);
+            this._renderTargetsDuration.updateCounter(0, startDate);
+            this._evaluateActiveMeshesDuration.updateCounter(0, startDate);
             this._totalVertices = 0;
             this._activeIndices = 0;
             this._activeBones = 0;
@@ -2171,7 +2192,7 @@
             if (this.customRenderTargets.length > 0) { // Restore back buffer
                 engine.restoreDefaultFramebuffer();
             }
-            this._renderTargetsDuration += Tools.Now - beforeRenderTargetDate;
+            this._renderTargetsDuration.updateCounter(Tools.Now - beforeRenderTargetDate, startDate);
             this.activeCamera = currentActiveCamera;
 
             // Procedural textures
@@ -2256,7 +2277,7 @@
             }
 
             Tools.EndPerformanceCounter("Scene rendering");
-            this._lastFrameDuration = Tools.Now - startDate;
+            this._lastFrameDuration.updateCounter(Tools.Now - startDate, startDate);
         }
 
         private _updateAudioParameters() {