EXT_disjoint_timer_query
WebGL working group (public_webgl 'at' khronos.org)
Contributors to ARB_occlusion_query
Contributors to EXT_timer_query
Contributors to ARB_timer_query
Ben Vanik, Google Inc.
Daniel Koch, TransGaming Inc.
Florian Boesch (pyalot 'at' gmail.com)
Members of the WebGL working group
Last modified date: February 14, 2023
Revision: 10
WebGL extension #26
Written against the WebGL API 1.0 specification.
This extension exposes the EXT_disjoint_timer_query functionality to WebGL.
The following WebGL-specific behavioral changes apply:
Consult the above extension for documentation, issues and new functions and enumerants.
When this extension is enabled:
typedef unsigned long long GLuint64EXT; [Exposed=(Window,Worker), LegacyNoInterfaceObject] interface WebGLTimerQueryEXT : WebGLObject { }; [Exposed=(Window,Worker), LegacyNoInterfaceObject] interface EXT_disjoint_timer_query { const GLenum QUERY_COUNTER_BITS_EXT = 0x8864; const GLenum CURRENT_QUERY_EXT = 0x8865; const GLenum QUERY_RESULT_EXT = 0x8866; const GLenum QUERY_RESULT_AVAILABLE_EXT = 0x8867; const GLenum TIME_ELAPSED_EXT = 0x88BF; const GLenum TIMESTAMP_EXT = 0x8E28; const GLenum GPU_DISJOINT_EXT = 0x8FBB; WebGLTimerQueryEXT? createQueryEXT(); undefined deleteQueryEXT(WebGLTimerQueryEXT? query); [WebGLHandlesContextLoss] boolean isQueryEXT(WebGLTimerQueryEXT? query); undefined beginQueryEXT(GLenum target, WebGLTimerQueryEXT query); undefined endQueryEXT(GLenum target); undefined queryCounterEXT(WebGLTimerQueryEXT query, GLenum target); any getQueryEXT(GLenum target, GLenum pname); any getQueryObjectEXT(WebGLTimerQueryEXT query, GLenum pname); };
deleteQueryEXT
implicitly calls endQueryEXT
if the query
is active.
In GLES, DeleteQueries
does not end queries, even if they are active.
However, all WebGL implementations implemented this implicit EndQuery
due to conformance tests.
WebGLTimerQueryEXT
is valid and false otherwise. Returns false if
the query's invalidated
flag is set.
target
accepts TIME_ELAPSED_EXT
.
target
accepts TIME_ELAPSED_EXT
.
target
accepts TIMESTAMP_EXT
.
target
and pname
accept the following combinations of
parameters. The return type of this method depends on the parameter queried.
target | pname | returned type |
---|---|---|
TIME_ELAPSED_EXT | CURRENT_QUERY_EXT | WebGLTimerQueryEXT? |
TIMESTAMP_EXT | CURRENT_QUERY_EXT | null |
TIME_ELAPSED_EXT | QUERY_COUNTER_BITS_EXT | GLint |
TIMESTAMP_EXT | QUERY_COUNTER_BITS_EXT | GLint |
pname
accepts QUERY_RESULT_EXT
or QUERY_RESULT_AVAILABLE_EXT
.
pname | returned type |
---|---|
QUERY_RESULT_EXT | GLuint64EXT |
QUERY_RESULT_AVAILABLE_EXT | boolean |
QUERY_RESULT_AVAILABLE_EXT
parameter in a loop, without
returning control to the user agent, must always return the same value. requestAnimationFrame
callback, will allow sufficient time for
the WebGL implementation to supply the query's results.
pname
accepts TIMESTAMP_EXT
or GPU_DISJOINT_EXT
.
pname | returned type |
---|---|
TIMESTAMP_EXT | GLuint64EXT |
GPU_DISJOINT_EXT | boolean |
// Example (1) -- uses beginQueryEXT/endQueryEXT. let ext = gl.getExtension('EXT_disjoint_timer_query'); let query = ext.createQueryEXT(); ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); // Draw object gl.drawElements(...); ext.endQueryEXT(ext.TIME_ELAPSED_EXT); // ...at some point in the future, after returning control to the browser and being called again: // (Note that this code might be called multiple times) if (query) { let available = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); let disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT); if (available && !disjoint) { // See how much time the rendering of the object took in nanoseconds. let timeElapsed = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT); // Do something useful with the time. Note that care should be // taken to use all significant bits of the result, not just the // least significant 32 bits. adjustObjectLODBasedOnDrawTime(timeElapsed); } if (available || disjoint) { // Clean up the query object. ext.deleteQueryEXT(query); // Don't re-enter this polling loop. query = null; } } //---------------------------------------------------------------------- // Example (2) -- same as the example above, but uses queryCounterEXT instead. let ext = gl.getExtension('EXT_disjoint_timer_query'); let startQuery = ext.createQueryEXT(); let endQuery = ext.createQueryEXT(); ext.queryCounterEXT(startQuery, ext.TIMESTAMP_EXT); // Draw object gl.drawElements(...); ext.queryCounterEXT(endQuery, ext.TIMESTAMP_EXT); // ...at some point in the future, after returning control to the browser and being called again: // (Note that this code might be called multiple times) if (startQuery) { let available = ext.getQueryObjectEXT(endQuery, ext.QUERY_RESULT_AVAILABLE_EXT); let disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT); if (available && !disjoint) { // See how much time the rendering of the object took in nanoseconds. let timeStart = ext.getQueryObjectEXT(startQuery, ext.QUERY_RESULT_EXT); let timeEnd = ext.getQueryObjectEXT(endQuery, ext.QUERY_RESULT_EXT); // Do something useful with the time. Note that care should be // taken to use all significant bits of the result, not just the // least significant 32 bits. adjustObjectLODBasedOnDrawTime(timeEnd - timeStart); } if (available || disjoint) { // Clean up the query objects. ext.deleteQueryEXT(startQuery); ext.deleteQueryEXT(endQuery); // Don't re-enter this polling loop. startQuery = null; endQuery = null; } } //---------------------------------------------------------------------- // Example (3) -- check the number of timestamp bits to determine how to best // measure elapsed time. let ext = gl.getExtension('EXT_disjoint_timer_query'); let timeElapsedQuery; let startQuery; let endQuery; let useTimestamps = false; if (ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) > 0) { useTimestamps = true; } // Clear the disjoint state before starting to work with queries to increase // the chances that the results will be valid. gl.getParameter(ext.GPU_DISJOINT_EXT); if (useTimestamps) { startQuery = ext.createQueryEXT(); endQuery = ext.createQueryEXT(); ext.queryCounterEXT(startQuery, ext.TIMESTAMP_EXT); } else { timeElapsedQuery = ext.createQueryEXT(); ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, timeElapsedQuery); } // Draw object gl.drawElements(...); if (useTimestamps) { ext.queryCounterEXT(endQuery, ext.TIMESTAMP_EXT); } else { ext.endQueryEXT(ext.TIME_ELAPSED_EXT); } // ...at some point in the future, after returning control to the browser and being called again: // (Note that this code might be called multiple times) if (startQuery || endQuery || timeElapsedQuery) { let disjoint = gl.getParameter(ext.GPU_DISJOINT_EXT); let available; if (disjoint) { // Have to redo all of the measurements. } else { if (useTimestamps) { available = ext.getQueryObjectEXT(endQuery, ext.QUERY_RESULT_AVAILABLE_EXT); } else { available = ext.getQueryObjectEXT(timeElapsedQuery, ext.QUERY_RESULT_AVAILABLE_EXT); } if (available) { let timeElapsed; if (useTimestamps) { // See how much time the rendering of the object took in nanoseconds. let timeStart = ext.getQueryObjectEXT(startQuery, ext.QUERY_RESULT_EXT); let timeEnd = ext.getQueryObjectEXT(endQuery, ext.QUERY_RESULT_EXT); timeElapsed = timeEnd - timeStart; } else { timeElapsed = ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT); } // Do something useful with the time. Note that care should be // taken to use all significant bits of the result, not just the // least significant 32 bits. adjustObjectLODBasedOnDrawTime(timeElapsed); } } if (available || disjoint) { // Clean up the query objects. if (useTimestamps) { ext.deleteQueryEXT(startQuery); ext.deleteQueryEXT(endQuery); // Don't re-enter the polling loop above. startQuery = null; endQuery = null; } else { ext.deleteQueryEXT(timeElapsedQuery); timeElapsedQuery = null; } } }
Can getQueryObjectEXT be exposed in its current form according to ECMAScript semantics? ECMAScript's de-facto concurrency model is "shared nothing" communicating event loops. Is it acceptable for sequential calls to getQueryObjectEXT to return different answers? Note that Date.now() advances during script execution, so this may be fine; but if concerns are raised, then the API may need to be redesigned to use callbacks.
Revision 1, 2013/04/02
Revision 2, 2013/04/03
Revision 3, 2014/02/12
Revision 4, 2014/07/15
Revision 5, 2015/10/05
Revision 6, 2015/10/19
Revision 7, 2016/06/08
Revision 8, 2016/09/30
Revision 9, 2021/04/01
Revision 10, 2023/02/14