Browse Source

New engineInstrumentation

David Catuhe 7 năm trước cách đây
mục cha
commit
1857061fb8

+ 12 - 2
Tools/Gulp/config.json

@@ -72,7 +72,8 @@
             "layer",
             "textureTools",
             "cameraBehaviors",
-            "nullEngine"
+            "nullEngine",
+            "instrumentation"
         ],
         "minimal": [
             "standardMaterial",
@@ -225,7 +226,16 @@
             "dependUpon": [
                 "core"
             ]
-        },        
+        },    
+        "instrumentation": {
+            "files": [
+                "../../src/Engine/babylon.engineInstrumentation.js",
+                "../../src/Engine/babylon.timeToken.js"
+            ],
+            "dependUpon": [
+                "core"
+            ]
+        },      
         "cameraBehaviors": {
             "files": [
                 "../../src/Behaviors/Cameras/babylon.framingBehavior.js",

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3320 - 3223
dist/preview release/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 45 - 45
dist/preview release/babylon.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 370 - 5
dist/preview release/babylon.max.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3320 - 3223
dist/preview release/babylon.module.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 30 - 30
dist/preview release/babylon.worker.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 6766 - 6651
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 28 - 28
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 155 - 5
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -6961,6 +6961,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(PerfCounter.prototype, "count", {
+            get: function () {
+                return this._totalValueCount;
+            },
+            enumerable: true,
+            configurable: true
+        });
         /**
          * Call this method to start monitoring a new frame.
          * This scenario is typically used when you accumulate monitoring time many times for a single frame, you call this method at the start of the frame, then beginMonitoring to start recording and endMonitoring(false) to accumulated the recorded time to the PerfCounter or addCount() to accumulate a monitored count.
@@ -7860,6 +7867,23 @@ var BABYLON;
             // Uniform buffers list
             this.disableUniformBuffers = false;
             this._uniformBuffers = new Array();
+            // Observables
+            /**
+             * Observable raised when the engine begins a new frame
+             */
+            this.onBeginFrameObservable = new BABYLON.Observable();
+            /**
+             * Observable raised when the engine ends the current frame
+             */
+            this.onEndFrameObservable = new BABYLON.Observable();
+            /**
+             * Observable raised when the engine is about to compile a shader
+             */
+            this.onBeforeShaderCompilationObservable = new BABYLON.Observable();
+            /**
+             * Observable raised when the engine has jsut compiled a shader
+             */
+            this.onAfterShaderCompilationObservable = new BABYLON.Observable();
             this._windowIsBackground = false;
             this._webGLVersion = 1.0;
             this._badOS = false;
@@ -8613,6 +8637,13 @@ var BABYLON;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
             this._caps.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
             this._caps.highPrecisionShaderSupported = true;
+            this._caps.timerQuery = this._gl.getExtension("EXT_disjoint_timer_query") || this._gl.getExtension('EXT_disjoint_timer_query_webgl2');
+            if (this._caps.timerQuery) {
+                if (this._webGLVersion === 1) {
+                    this._gl.getQuery = this._caps.timerQuery.getQueryEXT.bind(this._caps.timerQuery);
+                }
+                this._caps.canUseTimestampForTimerQuery = this._gl.getQuery(this._caps.timerQuery.TIMESTAMP_EXT, this._caps.timerQuery.QUERY_COUNTER_BITS_EXT) > 0;
+            }
             // Checks if some of the format renders first to allow the use of webgl inspector.
             this._caps.colorBufferFloat = this._webGLVersion > 1 && this._gl.getExtension('EXT_color_buffer_float');
             this._caps.textureFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_float');
@@ -9026,6 +9057,7 @@ var BABYLON;
             return currentViewport;
         };
         Engine.prototype.beginFrame = function () {
+            this.onBeginFrameObservable.notifyObservers(this);
             this._measureFps();
         };
         Engine.prototype.endFrame = function () {
@@ -9038,6 +9070,7 @@ var BABYLON;
                 // TODO: We should only submit the frame if we read frameData successfully.
                 this._vrDisplay.submitFrame();
             }
+            this.onEndFrameObservable.notifyObservers(this);
         };
         /**
          * resize the view according to the canvas' size.
@@ -9747,10 +9780,13 @@ var BABYLON;
         };
         Engine.prototype.createShaderProgram = function (vertexCode, fragmentCode, defines, context) {
             context = context || this._gl;
+            this.onBeforeShaderCompilationObservable.notifyObservers(this);
             var shaderVersion = (this._webGLVersion > 1) ? "#version 300 es\n" : "";
             var vertexShader = compileShader(context, vertexCode, "vertex", defines, shaderVersion);
             var fragmentShader = compileShader(context, fragmentCode, "fragment", defines, shaderVersion);
-            return this._createShaderProgram(vertexShader, fragmentShader, context);
+            var program = this._createShaderProgram(vertexShader, fragmentShader, context);
+            this.onAfterShaderCompilationObservable.notifyObservers(this);
+            return program;
         };
         Engine.prototype._createShaderProgram = function (vertexShader, fragmentShader, context) {
             var shaderProgram = context.createProgram();
@@ -11894,15 +11930,129 @@ var BABYLON;
         Engine.prototype.getQueryResult = function (query) {
             return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT);
         };
-        Engine.prototype.beginQuery = function (algorithmType, query) {
+        Engine.prototype.beginOcclusionQuery = function (algorithmType, query) {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.beginQuery(glAlgorithm, query);
+            return this;
         };
-        Engine.prototype.endQuery = function (algorithmType) {
+        Engine.prototype.endOcclusionQuery = function (algorithmType) {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.endQuery(glAlgorithm);
             return this;
         };
+        /* Time queries */
+        Engine.prototype._createTimeQuery = function () {
+            var timerQuery = this._caps.timerQuery;
+            if (timerQuery.createQueryEXT) {
+                return timerQuery.createQueryEXT();
+            }
+            return this.createQuery();
+        };
+        Engine.prototype._deleteTimeQuery = function (query) {
+            var timerQuery = this._caps.timerQuery;
+            if (timerQuery.deleteQueryEXT) {
+                timerQuery.deleteQueryEXT(query);
+                return;
+            }
+            this.deleteQuery(query);
+        };
+        Engine.prototype._getTimeQueryResult = function (query) {
+            var timerQuery = this._caps.timerQuery;
+            if (timerQuery.getQueryObjectEXT) {
+                return timerQuery.getQueryObjectEXT(query, timerQuery.QUERY_RESULT_EXT);
+            }
+            return this.getQueryResult(query);
+        };
+        Engine.prototype._getTimeQueryAvailability = function (query) {
+            var timerQuery = this._caps.timerQuery;
+            if (timerQuery.getQueryObjectEXT) {
+                return timerQuery.getQueryObjectEXT(query, timerQuery.QUERY_RESULT_AVAILABLE_EXT);
+            }
+            return this.isQueryResultAvailable(query);
+        };
+        Engine.prototype.startTimeQuery = function () {
+            var timerQuery = this._caps.timerQuery;
+            if (!timerQuery) {
+                return null;
+            }
+            var token = new BABYLON._TimeToken();
+            this._gl.getParameter(timerQuery.GPU_DISJOINT_EXT);
+            if (this._caps.canUseTimestampForTimerQuery) {
+                token._startTimeQuery = this._createTimeQuery();
+                timerQuery.queryCounterEXT(token._startTimeQuery, timerQuery.TIMESTAMP_EXT);
+            }
+            else {
+                token._timeElapsedQuery = this._createTimeQuery();
+                if (timerQuery.beginQueryEXT) {
+                    timerQuery.beginQueryEXT(timerQuery.TIME_ELAPSED_EXT, token._timeElapsedQuery);
+                }
+                else {
+                    this._gl.beginQuery(timerQuery.TIME_ELAPSED_EXT, token._timeElapsedQuery);
+                }
+            }
+            return token;
+        };
+        Engine.prototype.endTimeQuery = function (token) {
+            var timerQuery = this._caps.timerQuery;
+            if (!timerQuery || !token) {
+                return -1;
+            }
+            if (this._caps.canUseTimestampForTimerQuery) {
+                if (!token._startTimeQuery) {
+                    return -1;
+                }
+                if (!token._endTimeQuery) {
+                    token._endTimeQuery = this._createTimeQuery();
+                    timerQuery.queryCounterEXT(token._endTimeQuery, timerQuery.TIMESTAMP_EXT);
+                }
+            }
+            else if (!token._timeElapsedQueryEnded) {
+                if (!token._timeElapsedQuery) {
+                    return -1;
+                }
+                if (timerQuery.endQueryEXT) {
+                    timerQuery.endQueryEXT(timerQuery.TIME_ELAPSED_EXT);
+                }
+                else {
+                    this._gl.endQuery(timerQuery.TIME_ELAPSED_EXT);
+                }
+                token._timeElapsedQueryEnded = true;
+            }
+            var disjoint = this._gl.getParameter(timerQuery.GPU_DISJOINT_EXT);
+            var available = false;
+            if (token._endTimeQuery) {
+                available = this._getTimeQueryAvailability(token._endTimeQuery);
+            }
+            else if (token._timeElapsedQuery) {
+                available = this._getTimeQueryAvailability(token._timeElapsedQuery);
+            }
+            if (available && !disjoint) {
+                var result = 0;
+                if (this._caps.canUseTimestampForTimerQuery) {
+                    if (!token._startTimeQuery || !token._endTimeQuery) {
+                        return -1;
+                    }
+                    var timeStart = this._getTimeQueryResult(token._startTimeQuery);
+                    var timeEnd = this._getTimeQueryResult(token._endTimeQuery);
+                    result = timeEnd - timeStart;
+                    this._deleteTimeQuery(token._startTimeQuery);
+                    this._deleteTimeQuery(token._endTimeQuery);
+                    token._startTimeQuery = null;
+                    token._endTimeQuery = null;
+                }
+                else {
+                    if (!token._timeElapsedQuery) {
+                        return -1;
+                    }
+                    result = this._getTimeQueryResult(token._timeElapsedQuery);
+                    this._deleteTimeQuery(token._timeElapsedQuery);
+                    token._timeElapsedQuery = null;
+                    token._timeElapsedQueryEnded = false;
+                }
+                return result;
+            }
+            return -1;
+        };
         Engine.prototype.getGlAlgorithmType = function (algorithmType) {
             return algorithmType === BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED;
         };
@@ -14939,9 +15089,9 @@ var BABYLON;
             if (!this._occlusionQuery) {
                 this._occlusionQuery = engine.createQuery();
             }
-            engine.beginQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
+            engine.beginOcclusionQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
-            engine.endQuery(this.occlusionQueryAlgorithmType);
+            engine.endOcclusionQuery(this.occlusionQueryAlgorithmType);
             this._isOcclusionQueryInProgress = true;
         };
         // Statics

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 6766 - 6651
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.js


+ 6 - 0
dist/preview release/inspector/babylon.inspector.js

@@ -3862,6 +3862,12 @@ var INSPECTOR;
                     elem: elemValue,
                     updateFct: function () { return (_this._engine.getCaps().vertexArrayObject ? "Yes" : "No"); }
                 });
+                elemLabel = _this._createStatLabel("Timer query", _this._panel);
+                elemValue = INSPECTOR.Helpers.CreateDiv('stat-value', _this._panel);
+                _this._updatableProperties.push({
+                    elem: elemValue,
+                    updateFct: function () { return (_this._engine.getCaps().timerQuery ? "Yes" : "No"); }
+                });
             }
             title = INSPECTOR.Helpers.CreateDiv('stat-title2', _this._panel);
             title.textContent = "Caps.";

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 2
dist/preview release/inspector/babylon.inspector.min.js


+ 127 - 30
src/Engine/babylon.engine.ts

@@ -597,6 +597,28 @@
             return this.webGLVersion > 1 && !this.disableUniformBuffers;
         }
 
+        // Observables
+
+        /**
+         * Observable raised when the engine begins a new frame
+         */
+        public onBeginFrameObservable = new Observable<Engine>();
+
+        /**
+         * Observable raised when the engine ends the current frame
+         */
+        public onEndFrameObservable = new Observable<Engine>();      
+
+        /**
+         * Observable raised when the engine is about to compile a shader
+         */
+        public onBeforeShaderCompilationObservable = new Observable<Engine>();    
+
+        /**
+         * Observable raised when the engine has jsut compiled a shader
+         */
+        public onAfterShaderCompilationObservable = new Observable<Engine>();    
+
         // Private Members
         private _gl: WebGLRenderingContext;
         private _renderingCanvas: Nullable<HTMLCanvasElement>;
@@ -1112,7 +1134,7 @@
                 if (this._webGLVersion === 1) {
                     this._gl.getQuery = (<any>this._caps.timerQuery).getQueryEXT.bind(this._caps.timerQuery);
                 }
-                this._caps.canUseTimestampForTimerQuery = this._gl.getQuery(this._caps.timerQuery.TIMESTAMP_EXT, this._caps.timerQuery.QUERY_COUNTER_BITS_EXT) > 0
+                this._caps.canUseTimestampForTimerQuery = this._gl.getQuery(this._caps.timerQuery.TIMESTAMP_EXT, this._caps.timerQuery.QUERY_COUNTER_BITS_EXT) > 0;
             }
 
             // Checks if some of the format renders first to allow the use of webgl inspector.
@@ -1577,9 +1599,10 @@
             this._gl.viewport(x, y, width, height);
 
             return currentViewport;
-        }
-
+        }       
+                
         public beginFrame(): void {
+            this.onBeginFrameObservable.notifyObservers(this);
             this._measureFps();
         }
 
@@ -1594,6 +1617,8 @@
                 // TODO: We should only submit the frame if we read frameData successfully.
                 this._vrDisplay.submitFrame();
             }
+
+            this.onEndFrameObservable.notifyObservers(this);
         }
 
         /**
@@ -2444,11 +2469,17 @@
         public createShaderProgram(vertexCode: string, fragmentCode: string, defines: Nullable<string>, context?: WebGLRenderingContext): WebGLProgram {
             context = context || this._gl;
 
+            this.onBeforeShaderCompilationObservable.notifyObservers(this);
+
             var shaderVersion = (this._webGLVersion > 1) ? "#version 300 es\n" : "";
             var vertexShader = compileShader(context, vertexCode, "vertex", defines, shaderVersion);
             var fragmentShader = compileShader(context, fragmentCode, "fragment", defines, shaderVersion);
 
-            return this._createShaderProgram(vertexShader, fragmentShader, context);
+            let program = this._createShaderProgram(vertexShader, fragmentShader, context);
+
+            this.onAfterShaderCompilationObservable.notifyObservers(this);
+
+            return program;
         }
 
         private _createShaderProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader, context: WebGLRenderingContext): WebGLProgram {
@@ -5054,23 +5085,21 @@
             return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT) as number;
         }
 
-        public beginQuery(algorithmType: number, query: WebGLQuery): Engine {
+        public beginOcclusionQuery(algorithmType: number, query: WebGLQuery): Engine {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.beginQuery(glAlgorithm, query);
 
             return this;
         }
 
-        public endQuery(algorithmType: number): Engine {
+        public endOcclusionQuery(algorithmType: number): Engine {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.endQuery(glAlgorithm);
 
             return this;
         }
 
-        private _startTimeQuery: Nullable<WebGLQuery>;
-        private _endTimeQuery: Nullable<WebGLQuery>;
-        private _timeElapsedQuery: Nullable<WebGLQuery>;
+        /* Time queries */
 
         private _createTimeQuery(): WebGLQuery {
             let timerQuery = <EXT_disjoint_timer_query>this._caps.timerQuery;
@@ -5082,49 +5111,117 @@
             return this.createQuery();
         }
 
-        public startTimeQuery(): Engine {
+        private _deleteTimeQuery(query: WebGLQuery): void {
+            let timerQuery = <EXT_disjoint_timer_query>this._caps.timerQuery;
+
+            if (timerQuery.deleteQueryEXT) {
+                timerQuery.deleteQueryEXT(query);
+                return;
+            }
+
+            this.deleteQuery(query);
+        }
+
+        private _getTimeQueryResult(query: WebGLQuery): any {
+            let timerQuery = <EXT_disjoint_timer_query>this._caps.timerQuery;
+
+            if (timerQuery.getQueryObjectEXT) {
+                return timerQuery.getQueryObjectEXT(query, timerQuery.QUERY_RESULT_EXT);
+            }
+            return this.getQueryResult(query);
+        }
+
+        private _getTimeQueryAvailability(query: WebGLQuery): any {
+            let timerQuery = <EXT_disjoint_timer_query>this._caps.timerQuery;
+
+            if (timerQuery.getQueryObjectEXT) {
+                return timerQuery.getQueryObjectEXT(query, timerQuery.QUERY_RESULT_AVAILABLE_EXT);
+            }
+            return this.isQueryResultAvailable(query);
+        }
+
+        public startTimeQuery(): Nullable<_TimeToken> {
             let timerQuery = this._caps.timerQuery;
             if (!timerQuery) {
-                return this;
+                return null;
             }
 
+            let token = new _TimeToken();
+            this._gl.getParameter(timerQuery.GPU_DISJOINT_EXT);
             if (this._caps.canUseTimestampForTimerQuery) {
-                if (!this._startTimeQuery) {
-                    this._startTimeQuery = this._createTimeQuery();
+                token._startTimeQuery = this._createTimeQuery();
 
-                    timerQuery.queryCounterEXT(this._startTimeQuery, timerQuery.TIMESTAMP_EXT);
-                }
+                timerQuery.queryCounterEXT(token._startTimeQuery, timerQuery.TIMESTAMP_EXT);
             } else {
-                this._timeElapsedQuery = this._createTimeQuery();
+                token._timeElapsedQuery = this._createTimeQuery();
                 if (timerQuery.beginQueryEXT) {
-                    
+                    timerQuery.beginQueryEXT(timerQuery.TIME_ELAPSED_EXT, token._timeElapsedQuery);
+                } else {
+                    this._gl.beginQuery(timerQuery.TIME_ELAPSED_EXT, token._timeElapsedQuery);
                 }
             }
-            return this;
+            return token;
         }
 
-        public endTimeQuery(): int {
+        public endTimeQuery(token: _TimeToken): int {
             let timerQuery = this._caps.timerQuery;
-            if (!timerQuery || !this._startTimeQuery) {
+            if (!timerQuery || !token) {
                 return -1;
             }
 
-            if (!this._endTimeQuery) {
-                this._endTimeQuery = this._createTimeQuery();
-                timerQuery.queryCounterEXT(this._endTimeQuery, timerQuery.TIMESTAMP_EXT);
+            if (this._caps.canUseTimestampForTimerQuery) {
+                if (!token._startTimeQuery) {
+                    return -1;
+                }
+                if (!token._endTimeQuery) {
+                    token._endTimeQuery = this._createTimeQuery();
+                    timerQuery.queryCounterEXT(token._endTimeQuery, timerQuery.TIMESTAMP_EXT);
+                }
+            } else if (!token._timeElapsedQueryEnded) {
+                if (!token._timeElapsedQuery) {
+                    return -1;
+                }
+                if (timerQuery.endQueryEXT) {
+                    timerQuery.endQueryEXT(timerQuery.TIME_ELAPSED_EXT);
+                } else {
+                    this._gl.endQuery(timerQuery.TIME_ELAPSED_EXT);
+                }
+                token._timeElapsedQueryEnded = true;
             }
 
-            let available = this._gl.getQueryParameter(this._endTimeQuery, this._gl.QUERY_RESULT_AVAILABLE);
             let disjoint = this._gl.getParameter(timerQuery.GPU_DISJOINT_EXT);
+            let available: boolean = false;
+            if (token._endTimeQuery) {
+                available = this._getTimeQueryAvailability(token._endTimeQuery);
+            } else if (token._timeElapsedQuery) {
+                available = this._getTimeQueryAvailability(token._timeElapsedQuery);
+            }
 
             if (available && !disjoint) {
-                let timeStart = this._gl.getQueryParameter(this._startTimeQuery, this._gl.QUERY_RESULT);
-                let timeEnd = this._gl.getQueryParameter(this._endTimeQuery, this._gl.QUERY_RESULT);
+                let result = 0;
+                if (this._caps.canUseTimestampForTimerQuery) {
+                    if (!token._startTimeQuery || !token._endTimeQuery) {
+                        return -1;
+                    }
+                    let timeStart = this._getTimeQueryResult(token._startTimeQuery);
+                    let timeEnd = this._getTimeQueryResult(token._endTimeQuery);
+
+                    result = timeEnd - timeStart;
+                    this._deleteTimeQuery(token._startTimeQuery);
+                    this._deleteTimeQuery(token._endTimeQuery);
+                    token._startTimeQuery = null;
+                    token._endTimeQuery = null;                    
+                } else {
+                    if (!token._timeElapsedQuery) {
+                        return -1;
+                    }
 
-                this.deleteQuery(this._startTimeQuery);
-                this.deleteQuery(this._endTimeQuery);
-                this._startTimeQuery = null;
-                return timeEnd - timeStart;
+                    result = this._getTimeQueryResult(token._timeElapsedQuery);
+                    this._deleteTimeQuery(token._timeElapsedQuery);
+                    token._timeElapsedQuery = null;
+                    token._timeElapsedQueryEnded = false;
+                }
+                return result;
             }
 
             return -1;

+ 178 - 0
src/Engine/babylon.engineInstrumentation.ts

@@ -0,0 +1,178 @@
+module BABYLON {
+    /**
+     * This class can be used to get instrumentation data from a Babylon engine
+     */
+    export class EngineInstrumentation implements IDisposable {
+        private _captureGPUFrameTime = false;
+        private _gpuFrameTimeToken: Nullable<_TimeToken>;
+        private _gpuFrameTime = new PerfCounter();
+
+        private _captureShaderCompilationTime = false;
+        private _shaderCompilationTime = new PerfCounter();        
+
+        // Observers
+        private _onBeginFrameObserver: Nullable<Observer<Engine>> = null;
+        private _onEndFrameObserver: Nullable<Observer<Engine>> = null;
+        private _onBeforeShaderCompilationObserver: Nullable<Observer<Engine>> = null;
+        private _onAfterShaderCompilationObserver: Nullable<Observer<Engine>> = null;
+
+        // Properties
+        /**
+         * Get the perf counter used for GPU frame time
+         */
+        public get gpuFrameTimeCounter(): PerfCounter {
+            return this._gpuFrameTime;
+        }
+
+        /**
+         * Get the current GPU frame time (in nanoseconds)
+         */
+        public get currentGPUFrameTime(): number {
+            return this._gpuFrameTime.current;
+        }
+
+        /**
+         * Get the average GPU frame time (in nanoseconds)
+         */        
+        public get averageGPUFrameTime(): number {
+            return this._gpuFrameTime.average;
+        }
+
+        /**
+         * Gets the current GPU frame time capture status
+         */
+        public get captureGPUFrameTime(): boolean {
+            return this._captureGPUFrameTime;
+        }
+
+        /**
+         * Enable or disable the GPU frame time capture
+         */        
+        public set captureGPUFrameTime(value: boolean) {
+            if (value === this._captureGPUFrameTime) {
+                return;
+            }
+
+            if (value) {
+                this._onBeginFrameObserver = this.engine.onBeginFrameObservable.add(()=>{
+                    if (!this._gpuFrameTimeToken) {
+                        this._gpuFrameTimeToken = this.engine.startTimeQuery();
+                    }
+                });
+
+                this._onEndFrameObserver = this.engine.onEndFrameObservable.add(()=>{
+                    if (!this._gpuFrameTimeToken) {
+                        return;
+                    }
+                    let time = this.engine.endTimeQuery(this._gpuFrameTimeToken);
+
+                    if (time > -1) {
+                        this._gpuFrameTimeToken = null;
+                        this._gpuFrameTime.fetchNewFrame();
+                        this._gpuFrameTime.addCount(time, true);
+                    }
+                });
+            } else {
+                this.engine.onBeginFrameObservable.remove(this._onBeginFrameObserver);
+                this._onBeginFrameObserver = null;
+                this.engine.onEndFrameObservable.remove(this._onEndFrameObserver);
+                this._onEndFrameObserver = null;
+            }
+        }
+
+        /**
+         * Get the perf counter used for shader compilation time
+         */
+        public get shaderCompilationTimeCounter(): PerfCounter {
+            return this._shaderCompilationTime;
+        }
+
+        /**
+         * Get the current shader compilation time (in milliseconds)
+         */
+        public get currentShaderCompilationTime(): number {
+            return this._shaderCompilationTime.current;
+        }
+
+        /**
+         * Get the average shader compilation time (in milliseconds)
+         */        
+        public get averageShaderCompilationTime(): number {
+            return this._shaderCompilationTime.average;
+        }
+
+        /**
+         * Get the total shader compilation time (in milliseconds)
+         */        
+        public get totalShaderCompilationTime(): number {
+            return this._shaderCompilationTime.total;
+        }               
+
+        /**
+         * Get the number of compiled shaders
+         */        
+        public get compiledShadersCount(): number {
+            return this._shaderCompilationTime.count;
+        }  
+
+        /**
+         * Gets the perf counter associated with shader compilation
+         */
+        public get captureShaderCompilationTime(): boolean {
+            return this._captureShaderCompilationTime;
+        }
+
+        /**
+         * Enable or disable the shader compilation time capture
+         */        
+        public set captureShaderCompilationTime(value: boolean) {
+            if (value === this._captureShaderCompilationTime) {
+                return;
+            }
+
+            if (value) {
+                this._onBeforeShaderCompilationObserver = this.engine.onBeforeShaderCompilationObservable.add(()=>{
+                    this._shaderCompilationTime.fetchNewFrame();
+                    this._shaderCompilationTime.beginMonitoring();                    
+                });
+
+                this._onAfterShaderCompilationObserver = this.engine.onAfterShaderCompilationObservable.add(()=>{
+                    this._shaderCompilationTime.endMonitoring();       
+                });
+            } else {
+                this.engine.onBeforeShaderCompilationObservable.remove(this._onBeforeShaderCompilationObserver);
+                this._onBeforeShaderCompilationObserver = null;
+                this.engine.onAfterShaderCompilationObservable.remove(this._onAfterShaderCompilationObserver);
+                this._onAfterShaderCompilationObserver = null;
+            }
+        }
+
+        public constructor(public engine: Engine) {
+
+        }
+
+        public dispose() {
+            if (this._onBeginFrameObserver) {
+                this.engine.onBeginFrameObservable.remove(this._onBeginFrameObserver);
+                this._onBeginFrameObserver = null;
+            }
+
+            if (this._onEndFrameObserver) {
+                this.engine.onEndFrameObservable.remove(this._onEndFrameObserver);
+                this._onEndFrameObserver = null;
+            }
+
+            if (this._onBeforeShaderCompilationObserver) {
+                this.engine.onBeforeShaderCompilationObservable.remove(this._onBeforeShaderCompilationObserver);
+                this._onBeforeShaderCompilationObserver = null;
+            }
+
+            if (this._onAfterShaderCompilationObserver) {
+                this.engine.onAfterShaderCompilationObservable.remove(this._onAfterShaderCompilationObserver);
+                this._onAfterShaderCompilationObserver = null;     
+            }       
+
+            (<any>this.engine) = null;
+        }
+    }
+}

+ 8 - 0
src/Engine/babylon.timeToken.ts

@@ -0,0 +1,8 @@
+module BABYLON {
+    export class _TimeToken {
+        public _startTimeQuery: Nullable<WebGLQuery>;
+        public _endTimeQuery: Nullable<WebGLQuery>;
+        public _timeElapsedQuery: Nullable<WebGLQuery>;
+        public _timeElapsedQueryEnded = false;
+    }
+}

+ 2 - 2
src/Mesh/babylon.abstractMesh.ts

@@ -2461,9 +2461,9 @@
                 this._occlusionQuery = engine.createQuery();
             }
 
-            engine.beginQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
+            engine.beginOcclusionQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
-            engine.endQuery(this.occlusionQueryAlgorithmType);
+            engine.endOcclusionQuery(this.occlusionQueryAlgorithmType);
             this._isOcclusionQueryInProgress = true;
         }
 

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

@@ -1353,6 +1353,10 @@
             return this._totalAccumulated;
         }
 
+        public get count(): number {
+            return this._totalValueCount;
+        }
+
         constructor() {
             this._startMonitoringTime = 0;
             this._min = 0;

+ 6 - 1
src/babylon.mixins.ts

@@ -44,7 +44,7 @@ interface WebGLRenderingContext {
     getUniformBlockIndex(program: WebGLProgram, uniformBlockName: string): number;
     uniformBlockBinding(program: WebGLProgram, uniformBlockIndex: number, uniformBlockBinding: number): void;
 
-    // Occlusion Query
+    // Queries
     createQuery(): WebGLQuery;
     deleteQuery(query: WebGLQuery): void;
     beginQuery(target: number, query: WebGLQuery): void;
@@ -610,7 +610,12 @@ interface EXT_disjoint_timer_query {
     TIME_ELAPSED_EXT: number;
     TIMESTAMP_EXT: number;
     GPU_DISJOINT_EXT: number;
+    QUERY_RESULT_EXT: number;
+    QUERY_RESULT_AVAILABLE_EXT: number;
     queryCounterEXT(query: WebGLQuery, target: number): void;
     createQueryEXT(): WebGLQuery;
     beginQueryEXT(target: number, query: WebGLQuery): void;
+    endQueryEXT(target: number): void;
+    getQueryObjectEXT(query: WebGLQuery, target: number): any;
+    deleteQueryEXT(query: WebGLQuery): void;
 }