WebGL
Khronos
 

WebGL EXT_disjoint_timer_query_webgl2 Extension Specification

Name

EXT_disjoint_timer_query_webgl2

Contact

WebGL working group (public_webgl 'at' khronos.org)

Contributors

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

Version

Last modified date: June 01, 2023
Revision: 4

Number

WebGL extension #33

Dependencies

Written against the WebGL API 2.0 specification.

Overview

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:

IDL

[Exposed=(Window,Worker), LegacyNoInterfaceObject]
interface EXT_disjoint_timer_query_webgl2 {
  const GLenum QUERY_COUNTER_BITS_EXT      = 0x8864;
  const GLenum TIME_ELAPSED_EXT            = 0x88BF;
  const GLenum TIMESTAMP_EXT               = 0x8E28;
  const GLenum GPU_DISJOINT_EXT            = 0x8FBB;

  undefined queryCounterEXT(WebGLQuery query, GLenum target);
};
  

New Functions

undefined queryCounterEXT(WebGLQuery query, GLenum target)

If query was generated by a different WebGL2RenderingContext than this one, generates an INVALID_OPERATION error.

target accepts TIMESTAMP_EXT.

New Tokens

beginQuery(GLenum target, WebGLQuery? query)
target accepts TIME_ELAPSED_EXT.
undefined endQuery(GLenum target)
target accepts TIME_ELAPSED_EXT.
any getQuery(GLenum target, GLenum pname)
target and pname accept the following combinations of parameters. The return type of this method now depends on the parameter queried.
targetpnamereturned type
TIME_ELAPSED_EXTCURRENT_QUERYWebGLQuery?
TIMESTAMP_EXTCURRENT_QUERYnull
TIME_ELAPSED_EXTQUERY_COUNTER_BITS_EXTGLint
TIMESTAMP_EXTQUERY_COUNTER_BITS_EXTGLint
any getParameter(GLenum pname)
pname accepts TIMESTAMP_EXT or GPU_DISJOINT_EXT.
The return type depends on the parameter queried:
pnamereturned type
TIMESTAMP_EXTGLuint64
GPU_DISJOINT_EXTboolean

Sample Code

        // Example (1) -- uses beginQuery/endQuery.
        let ext = gl.getExtension('EXT_disjoint_timer_query_webgl2');
        let query = gl.createQuery();
        gl.beginQuery(ext.TIME_ELAPSED_EXT, query);

        // Draw object
        gl.drawElements(...);

        gl.endQuery(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 = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
          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 = gl.getQueryParameter(query, gl.QUERY_RESULT);

            // 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.
            gl.deleteQuery(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_webgl2');
        let startQuery = gl.createQuery();
        let endQuery = gl.createQuery();
        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 = gl.getQueryParameter(endQuery, gl.QUERY_RESULT_AVAILABLE);
          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 = gl.getQueryParameter(startQuery, gl.QUERY_RESULT);
            let timeEnd = gl.getQueryParameter(endQuery, gl.QUERY_RESULT);

            // 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.
            gl.deleteQuery(startQuery);
            gl.deleteQuery(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_webgl2');
        let timeElapsedQuery;
        let startQuery;
        let endQuery;

        let useTimestamps = false;

        if (gl.getQuery(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 = gl.createQuery();
          endQuery = gl.createQuery();
          ext.queryCounterEXT(startQuery, ext.TIMESTAMP_EXT);
        } else {
          timeElapsedQuery = gl.createQuery();
          gl.beginQuery(ext.TIME_ELAPSED_EXT, timeElapsedQuery);
        }

        // Draw object
        gl.drawElements(...);

        if (useTimestamps) {
          ext.queryCounterEXT(endQuery, ext.TIMESTAMP_EXT);
        } else {
          gl.endQuery(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 = gl.getQueryParameter(endQuery, gl.QUERY_RESULT_AVAILABLE);
            } else {
              available = gl.getQueryParameter(timeElapsedQuery, gl.QUERY_RESULT_AVAILABLE);
            }

            if (available) {
              let timeElapsed;
              if (useTimestamps) {
                // See how much time the rendering of the object took in nanoseconds.
                let timeStart = gl.getQueryParameter(startQuery, gl.QUERY_RESULT);
                let timeEnd = gl.getQueryParameter(endQuery, gl.QUERY_RESULT);
                timeElapsed = timeEnd - timeStart;
              } else {
                timeElapsed = gl.getQueryParameter(query, gl.QUERY_RESULT);
              }

              // 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) {
              gl.deleteQuery(startQuery);
              gl.deleteQuery(endQuery);
              // Don't re-enter the polling loop above.
              startQuery = null;
              endQuery = null;
            } else {
              gl.deleteQuery(timeElapsedQuery);
              // Don't re-enter the polling loop above.
              timeElapsedQuery = null;
            }
          }
        }
    

Revision History

Revision 1, 2016/09/30

Revision 2, 2016/10/11

Revision 3, 2021/04/01

Revision 4, 2023/06/01