var GL_QUERY_RESULT_EXT = 0x8866 var GL_QUERY_RESULT_AVAILABLE_EXT = 0x8867 var GL_TIME_ELAPSED_EXT = 0x88BF module.exports = function (gl, extensions) { if (!extensions.ext_disjoint_timer_query) { return null } // QUERY POOL BEGIN var queryPool = [] function allocQuery () { return queryPool.pop() || extensions.ext_disjoint_timer_query.createQueryEXT() } function freeQuery (query) { queryPool.push(query) } // QUERY POOL END var pendingQueries = [] function beginQuery (stats) { var query = allocQuery() extensions.ext_disjoint_timer_query.beginQueryEXT(GL_TIME_ELAPSED_EXT, query) pendingQueries.push(query) pushScopeStats(pendingQueries.length - 1, pendingQueries.length, stats) } function endQuery () { extensions.ext_disjoint_timer_query.endQueryEXT(GL_TIME_ELAPSED_EXT) } // // Pending stats pool. // function PendingStats () { this.startQueryIndex = -1 this.endQueryIndex = -1 this.sum = 0 this.stats = null } var pendingStatsPool = [] function allocPendingStats () { return pendingStatsPool.pop() || new PendingStats() } function freePendingStats (pendingStats) { pendingStatsPool.push(pendingStats) } // Pending stats pool end var pendingStats = [] function pushScopeStats (start, end, stats) { var ps = allocPendingStats() ps.startQueryIndex = start ps.endQueryIndex = end ps.sum = 0 ps.stats = stats pendingStats.push(ps) } // we should call this at the beginning of the frame, // in order to update gpuTime var timeSum = [] var queryPtr = [] function update () { var ptr, i var n = pendingQueries.length if (n === 0) { return } // Reserve space queryPtr.length = Math.max(queryPtr.length, n + 1) timeSum.length = Math.max(timeSum.length, n + 1) timeSum[0] = 0 queryPtr[0] = 0 // Update all pending timer queries var queryTime = 0 ptr = 0 for (i = 0; i < pendingQueries.length; ++i) { var query = pendingQueries[i] if (extensions.ext_disjoint_timer_query.getQueryObjectEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT)) { queryTime += extensions.ext_disjoint_timer_query.getQueryObjectEXT(query, GL_QUERY_RESULT_EXT) freeQuery(query) } else { pendingQueries[ptr++] = query } timeSum[i + 1] = queryTime queryPtr[i + 1] = ptr } pendingQueries.length = ptr // Update all pending stat queries ptr = 0 for (i = 0; i < pendingStats.length; ++i) { var stats = pendingStats[i] var start = stats.startQueryIndex var end = stats.endQueryIndex stats.sum += timeSum[end] - timeSum[start] var startPtr = queryPtr[start] var endPtr = queryPtr[end] if (endPtr === startPtr) { stats.stats.gpuTime += stats.sum / 1e6 freePendingStats(stats) } else { stats.startQueryIndex = startPtr stats.endQueryIndex = endPtr pendingStats[ptr++] = stats } } pendingStats.length = ptr } return { beginQuery: beginQuery, endQuery: endQuery, pushScopeStats: pushScopeStats, update: update, getNumPendingQueries: function () { return pendingQueries.length }, clear: function () { queryPool.push.apply(queryPool, pendingQueries) for (var i = 0; i < queryPool.length; i++) { extensions.ext_disjoint_timer_query.deleteQueryEXT(queryPool[i]) } pendingQueries.length = 0 queryPool.length = 0 }, restore: function () { pendingQueries.length = 0 queryPool.length = 0 } } }