module BABYLON { export interface Engine { /** * Create a new webGL query (you must be sure that queries are supported by checking getCaps() function) * @return the new query */ createQuery(): WebGLQuery; /** * Delete and release a webGL query * @param query defines the query to delete * @return the current engine */ deleteQuery(query: WebGLQuery): Engine; /** * Check if a given query has resolved and got its value * @param query defines the query to check * @returns true if the query got its value */ isQueryResultAvailable(query: WebGLQuery): boolean; /** * Gets the value of a given query * @param query defines the query to check * @returns the value of the query */ getQueryResult(query: WebGLQuery): number; /** * Initiates an occlusion query * @param algorithmType defines the algorithm to use * @param query defines the query to use * @returns the current engine * @see http://doc.babylonjs.com/features/occlusionquery */ beginOcclusionQuery(algorithmType: number, query: WebGLQuery): Engine; /** * Ends an occlusion query * @see http://doc.babylonjs.com/features/occlusionquery * @param algorithmType defines the algorithm to use * @returns the current engine */ endOcclusionQuery(algorithmType: number): Engine; /** * Starts a time query (used to measure time spent by the GPU on a specific frame) * Please note that only one query can be issued at a time * @returns a time token used to track the time span */ startTimeQuery(): Nullable<_TimeToken>; /** * Ends a time query * @param token defines the token used to measure the time span * @returns the time spent (in ns) */ endTimeQuery(token: _TimeToken): int; /** @hidden */ _currentNonTimestampToken: Nullable<_TimeToken>; /** @hidden */ _createTimeQuery(): WebGLQuery; /** @hidden */ _deleteTimeQuery(query: WebGLQuery): void; /** @hidden */ _getGlAlgorithmType(algorithmType: number): number; /** @hidden */ _getTimeQueryResult(query: WebGLQuery): any; /** @hidden */ _getTimeQueryAvailability(query: WebGLQuery): any; } Engine.prototype.createQuery = function(): WebGLQuery { return this._gl.createQuery(); } Engine.prototype.deleteQuery = function(query: WebGLQuery): Engine { this._gl.deleteQuery(query); return this; } Engine.prototype.isQueryResultAvailable = function(query: WebGLQuery): boolean { return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT_AVAILABLE) as boolean; } Engine.prototype.getQueryResult = function(query: WebGLQuery): number { return this._gl.getQueryParameter(query, this._gl.QUERY_RESULT) as number; } Engine.prototype.beginOcclusionQuery = function(algorithmType: number, query: WebGLQuery): Engine { var glAlgorithm = this._getGlAlgorithmType(algorithmType); this._gl.beginQuery(glAlgorithm, query); return this; } Engine.prototype.endOcclusionQuery = function(algorithmType: number): Engine { var glAlgorithm = this._getGlAlgorithmType(algorithmType); this._gl.endQuery(glAlgorithm); return this; } Engine.prototype._createTimeQuery = function(): WebGLQuery { let timerQuery = this.getCaps().timerQuery; if (timerQuery.createQueryEXT) { return timerQuery.createQueryEXT(); } return this.createQuery(); } Engine.prototype._deleteTimeQuery = function(query: WebGLQuery): void { let timerQuery = this.getCaps().timerQuery; if (timerQuery.deleteQueryEXT) { timerQuery.deleteQueryEXT(query); return; } this.deleteQuery(query); } Engine.prototype._getTimeQueryResult = function(query: WebGLQuery): any { let timerQuery = this.getCaps().timerQuery; if (timerQuery.getQueryObjectEXT) { return timerQuery.getQueryObjectEXT(query, timerQuery.QUERY_RESULT_EXT); } return this.getQueryResult(query); } Engine.prototype._getTimeQueryAvailability = function(query: WebGLQuery): any { let timerQuery = this.getCaps().timerQuery; if (timerQuery.getQueryObjectEXT) { return timerQuery.getQueryObjectEXT(query, timerQuery.QUERY_RESULT_AVAILABLE_EXT); } return this.isQueryResultAvailable(query); } Engine.prototype.startTimeQuery = function(): Nullable<_TimeToken> { let caps = this.getCaps(); let timerQuery = caps.timerQuery; if (!timerQuery) { return null; } let token = new _TimeToken(); this._gl.getParameter(timerQuery.GPU_DISJOINT_EXT); if (caps.canUseTimestampForTimerQuery) { token._startTimeQuery = this._createTimeQuery(); timerQuery.queryCounterEXT(token._startTimeQuery, timerQuery.TIMESTAMP_EXT); } else { if (this._currentNonTimestampToken) { return this._currentNonTimestampToken; } 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); } this._currentNonTimestampToken = token; } return token; } Engine.prototype.endTimeQuery = function(token: _TimeToken): int { let caps = this.getCaps(); let timerQuery = caps.timerQuery; if (!timerQuery || !token) { return -1; } if (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 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 result = 0; if (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; } result = this._getTimeQueryResult(token._timeElapsedQuery); this._deleteTimeQuery(token._timeElapsedQuery); token._timeElapsedQuery = null; token._timeElapsedQueryEnded = false; this._currentNonTimestampToken = null; } return result; } return -1; } Engine.prototype._getGlAlgorithmType = function(algorithmType: number): number { return algorithmType === AbstractMesh.OCCLUSION_ALGORITHM_TYPE_CONSERVATIVE ? this._gl.ANY_SAMPLES_PASSED_CONSERVATIVE : this._gl.ANY_SAMPLES_PASSED; } // We also need to update AbstractMesh as there is a portion of the code there AbstractMesh.prototype._checkOcclusionQuery = function() { if (this.occlusionType === AbstractMesh.OCCLUSION_TYPE_NONE) { this._isOccluded = false; return; } var engine = this.getEngine(); if (engine.webGLVersion < 2) { this._isOccluded = false; return; } if (!engine.isQueryResultAvailable) { // Occlusion query where not referenced this._isOccluded = false; return; } if (this.isOcclusionQueryInProgress && this._occlusionQuery) { var isOcclusionQueryAvailable = engine.isQueryResultAvailable(this._occlusionQuery); if (isOcclusionQueryAvailable) { var occlusionQueryResult = engine.getQueryResult(this._occlusionQuery); this._isOcclusionQueryInProgress = false; this._occlusionInternalRetryCounter = 0; this._isOccluded = occlusionQueryResult === 1 ? false : true; } else { this._occlusionInternalRetryCounter++; if (this.occlusionRetryCount !== -1 && this._occlusionInternalRetryCounter > this.occlusionRetryCount) { this._isOcclusionQueryInProgress = false; this._occlusionInternalRetryCounter = 0; // if optimistic set isOccluded to false regardless of the status of isOccluded. (Render in the current render loop) // if strict continue the last state of the object. this._isOccluded = this.occlusionType === AbstractMesh.OCCLUSION_TYPE_OPTIMISTIC ? false : this._isOccluded; } else { return; } } } var scene = this.getScene(); if (scene.getBoundingBoxRenderer) { var occlusionBoundingBoxRenderer = scene.getBoundingBoxRenderer(); if (!this._occlusionQuery) { this._occlusionQuery = engine.createQuery(); } engine.beginOcclusionQuery(this.occlusionQueryAlgorithmType, this._occlusionQuery); occlusionBoundingBoxRenderer.renderOcclusionBoundingBox(this); engine.endOcclusionQuery(this.occlusionQueryAlgorithmType); this._isOcclusionQueryInProgress = true; } } }