Browse Source

New engineInstrumentation

David Catuhe 7 years ago
parent
commit
1857061fb8

+ 12 - 2
Tools/Gulp/config.json

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

File diff suppressed because it is too large
+ 3320 - 3223
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 45 - 45
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 370 - 5
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 3320 - 3223
dist/preview release/babylon.module.d.ts


File diff suppressed because it is too large
+ 30 - 30
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 6766 - 6651
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


File diff suppressed because it is too large
+ 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,
             enumerable: true,
             configurable: 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.
          * 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.
          * 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
             // Uniform buffers list
             this.disableUniformBuffers = false;
             this.disableUniformBuffers = false;
             this._uniformBuffers = new Array();
             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._windowIsBackground = false;
             this._webGLVersion = 1.0;
             this._webGLVersion = 1.0;
             this._badOS = false;
             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.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.fragmentDepthSupported = this._webGLVersion > 1 || this._gl.getExtension('EXT_frag_depth') !== null;
             this._caps.highPrecisionShaderSupported = true;
             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.
             // 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.colorBufferFloat = this._webGLVersion > 1 && this._gl.getExtension('EXT_color_buffer_float');
             this._caps.textureFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_float');
             this._caps.textureFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_float');
@@ -9026,6 +9057,7 @@ var BABYLON;
             return currentViewport;
             return currentViewport;
         };
         };
         Engine.prototype.beginFrame = function () {
         Engine.prototype.beginFrame = function () {
+            this.onBeginFrameObservable.notifyObservers(this);
             this._measureFps();
             this._measureFps();
         };
         };
         Engine.prototype.endFrame = function () {
         Engine.prototype.endFrame = function () {
@@ -9038,6 +9070,7 @@ var BABYLON;
                 // TODO: We should only submit the frame if we read frameData successfully.
                 // TODO: We should only submit the frame if we read frameData successfully.
                 this._vrDisplay.submitFrame();
                 this._vrDisplay.submitFrame();
             }
             }
+            this.onEndFrameObservable.notifyObservers(this);
         };
         };
         /**
         /**
          * resize the view according to the canvas' size.
          * resize the view according to the canvas' size.
@@ -9747,10 +9780,13 @@ var BABYLON;
         };
         };
         Engine.prototype.createShaderProgram = function (vertexCode, fragmentCode, defines, context) {
         Engine.prototype.createShaderProgram = function (vertexCode, fragmentCode, defines, context) {
             context = context || this._gl;
             context = context || this._gl;
+            this.onBeforeShaderCompilationObservable.notifyObservers(this);
             var shaderVersion = (this._webGLVersion > 1) ? "#version 300 es\n" : "";
             var shaderVersion = (this._webGLVersion > 1) ? "#version 300 es\n" : "";
             var vertexShader = compileShader(context, vertexCode, "vertex", defines, shaderVersion);
             var vertexShader = compileShader(context, vertexCode, "vertex", defines, shaderVersion);
             var fragmentShader = compileShader(context, fragmentCode, "fragment", 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) {
         Engine.prototype._createShaderProgram = function (vertexShader, fragmentShader, context) {
             var shaderProgram = context.createProgram();
             var shaderProgram = context.createProgram();
@@ -11894,15 +11930,129 @@ var BABYLON;
         Engine.prototype.getQueryResult = function (query) {
         Engine.prototype.getQueryResult = function (query) {
             return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT);
             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);
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.beginQuery(glAlgorithm, query);
             this._gl.beginQuery(glAlgorithm, query);
+            return this;
         };
         };
-        Engine.prototype.endQuery = function (algorithmType) {
+        Engine.prototype.endOcclusionQuery = function (algorithmType) {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.endQuery(glAlgorithm);
             this._gl.endQuery(glAlgorithm);
             return this;
             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) {
         Engine.prototype.getGlAlgorithmType = function (algorithmType) {
             return algorithmType === BABYLON.AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED;
             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) {
             if (!this._occlusionQuery) {
                 this._occlusionQuery = engine.createQuery();
                 this._occlusionQuery = engine.createQuery();
             }
             }
-            engine.beginQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
+            engine.beginOcclusionQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
-            engine.endQuery(this.occlusionQueryAlgorithmType);
+            engine.endOcclusionQuery(this.occlusionQueryAlgorithmType);
             this._isOcclusionQueryInProgress = true;
             this._isOcclusionQueryInProgress = true;
         };
         };
         // Statics
         // Statics

File diff suppressed because it is too large
+ 6766 - 6651
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


File diff suppressed because it is too large
+ 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,
                     elem: elemValue,
                     updateFct: function () { return (_this._engine.getCaps().vertexArrayObject ? "Yes" : "No"); }
                     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 = INSPECTOR.Helpers.CreateDiv('stat-title2', _this._panel);
             title.textContent = "Caps.";
             title.textContent = "Caps.";

File diff suppressed because it is too large
+ 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;
             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 Members
         private _gl: WebGLRenderingContext;
         private _gl: WebGLRenderingContext;
         private _renderingCanvas: Nullable<HTMLCanvasElement>;
         private _renderingCanvas: Nullable<HTMLCanvasElement>;
@@ -1112,7 +1134,7 @@
                 if (this._webGLVersion === 1) {
                 if (this._webGLVersion === 1) {
                     this._gl.getQuery = (<any>this._caps.timerQuery).getQueryEXT.bind(this._caps.timerQuery);
                     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.
             // 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);
             this._gl.viewport(x, y, width, height);
 
 
             return currentViewport;
             return currentViewport;
-        }
-
+        }       
+                
         public beginFrame(): void {
         public beginFrame(): void {
+            this.onBeginFrameObservable.notifyObservers(this);
             this._measureFps();
             this._measureFps();
         }
         }
 
 
@@ -1594,6 +1617,8 @@
                 // TODO: We should only submit the frame if we read frameData successfully.
                 // TODO: We should only submit the frame if we read frameData successfully.
                 this._vrDisplay.submitFrame();
                 this._vrDisplay.submitFrame();
             }
             }
+
+            this.onEndFrameObservable.notifyObservers(this);
         }
         }
 
 
         /**
         /**
@@ -2444,11 +2469,17 @@
         public createShaderProgram(vertexCode: string, fragmentCode: string, defines: Nullable<string>, context?: WebGLRenderingContext): WebGLProgram {
         public createShaderProgram(vertexCode: string, fragmentCode: string, defines: Nullable<string>, context?: WebGLRenderingContext): WebGLProgram {
             context = context || this._gl;
             context = context || this._gl;
 
 
+            this.onBeforeShaderCompilationObservable.notifyObservers(this);
+
             var shaderVersion = (this._webGLVersion > 1) ? "#version 300 es\n" : "";
             var shaderVersion = (this._webGLVersion > 1) ? "#version 300 es\n" : "";
             var vertexShader = compileShader(context, vertexCode, "vertex", defines, shaderVersion);
             var vertexShader = compileShader(context, vertexCode, "vertex", defines, shaderVersion);
             var fragmentShader = compileShader(context, fragmentCode, "fragment", 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 {
         private _createShaderProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader, context: WebGLRenderingContext): WebGLProgram {
@@ -5054,23 +5085,21 @@
             return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT) as number;
             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);
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.beginQuery(glAlgorithm, query);
             this._gl.beginQuery(glAlgorithm, query);
 
 
             return this;
             return this;
         }
         }
 
 
-        public endQuery(algorithmType: number): Engine {
+        public endOcclusionQuery(algorithmType: number): Engine {
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             var glAlgorithm = this.getGlAlgorithmType(algorithmType);
             this._gl.endQuery(glAlgorithm);
             this._gl.endQuery(glAlgorithm);
 
 
             return this;
             return this;
         }
         }
 
 
-        private _startTimeQuery: Nullable<WebGLQuery>;
-        private _endTimeQuery: Nullable<WebGLQuery>;
-        private _timeElapsedQuery: Nullable<WebGLQuery>;
+        /* Time queries */
 
 
         private _createTimeQuery(): WebGLQuery {
         private _createTimeQuery(): WebGLQuery {
             let timerQuery = <EXT_disjoint_timer_query>this._caps.timerQuery;
             let timerQuery = <EXT_disjoint_timer_query>this._caps.timerQuery;
@@ -5082,49 +5111,117 @@
             return this.createQuery();
             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;
             let timerQuery = this._caps.timerQuery;
             if (!timerQuery) {
             if (!timerQuery) {
-                return this;
+                return null;
             }
             }
 
 
+            let token = new _TimeToken();
+            this._gl.getParameter(timerQuery.GPU_DISJOINT_EXT);
             if (this._caps.canUseTimestampForTimerQuery) {
             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 {
             } else {
-                this._timeElapsedQuery = this._createTimeQuery();
+                token._timeElapsedQuery = this._createTimeQuery();
                 if (timerQuery.beginQueryEXT) {
                 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;
             let timerQuery = this._caps.timerQuery;
-            if (!timerQuery || !this._startTimeQuery) {
+            if (!timerQuery || !token) {
                 return -1;
                 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 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) {
             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;
             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();
                 this._occlusionQuery = engine.createQuery();
             }
             }
 
 
-            engine.beginQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
+            engine.beginOcclusionQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
             occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this);
-            engine.endQuery(this.occlusionQueryAlgorithmType);
+            engine.endOcclusionQuery(this.occlusionQueryAlgorithmType);
             this._isOcclusionQueryInProgress = true;
             this._isOcclusionQueryInProgress = true;
         }
         }
 
 

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

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

+ 6 - 1
src/babylon.mixins.ts

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