Synchronous webGL rendering and EXT_disjoint_timer_query_webgl2

87 views Asked by At

I am working on a webgl2 app that renders using the webgl.drawArrays() function. It seems that this is an asynchronous function. I need to be able to get it to render synchronously, or at least be able to detect somehow when it finishes updating its output buffer (on or off screen). I'm trying to use the EXT_disjoint_timer_query_webgl2 extension to do this, as described in...

https://registry.khronos.org/webgl/extensions/EXT_disjoint_timer_query_webgl2/

Here is my code...

// EXT_disjoint_timer_query_webgl2
const ext = gWebGL.getExtension('EXT_disjoint_timer_query_webgl2');
const query = gWebGL.createQuery();
gWebGL.beginQuery(ext.TIME_ELAPSED_EXT, query);

/* Draw the triangle. */
gWebGL.drawArrays(gWebGL.TRIANGLE_STRIP, 0, 4);

// EXT_disjoint_timer_query_webgl2
gWebGL.endQuery(ext.TIME_ELAPSED_EXT);

// EXT_disjoint_timer_query_webgl2
let available = gWebGL.getQueryParameter(query, gWebGL.QUERY_RESULT_AVAILABLE);
let disjoint = gWebGL.getParameter(ext.GPU_DISJOINT_EXT);

while(!available)
    available = gWebGL.getQueryParameter(query, gWebGL.QUERY_RESULT_AVAILABLE);

if (available && !disjoint) {
  // See how much time the rendering of the object took in nanoseconds.
  let timeElapsed = gl.getQueryParameter(query, gWebGL.QUERY_RESULT);
  console.log(timeElapsed);
}

When I run this the 'available' variable is always false. It never becomes true. What am I doing wrong?

If there is another way to get drawArrays() (or similar function) to render synchronously, that would be ok too.

I should note that this is for an event driven scientific application, not a game with a render loop. The requestAnimationFrame() function is not useful to me.

Thanks for any help.

1

There are 1 answers

0
racz16 On

This is an infinite loop, because according to the WebGL specification

A query's result must not be made available until control has returned to the user agent's main loop.

and

A query's result may or may not be made available when control returns to the user agent's event loop. It is not guaranteed that using a single setTimeout callback with a delay of 0, or a single requestAnimationFrame callback, will allow sufficient time for the WebGL implementation to supply the query's results.

So you have to call setTimeout, setInterval, requestAnimationFrame, or something similar to get back the result.

You can try setTimeout, but as the specification says, a simple setTimeout(()=>{...}, 0) might not be enough.

In my experience, in the next frame the result will be available (again, this is not necessarily true), so a single requestAnimationFrame call should be enough (but in the 2nd frame, you don't call requestAnimationFrame again, so there is no render loop).

I'm not sure exactly what is the use-case, however, if you want to wait until the drawing of the image is finished (and not until it is displayed on the screen), you have yet another option. You can render into a texture using a framebuffer, (and if you want, you can still render that texture onto the screen). This is a good tutorial about how to render into a texture. The advantage of this method is that after calling drawArrays, you can use the texture.

A bit about the synchronous/asynchronous situation. First of all, none of the WebGL functions use the JavaScript async and await keywords, so in that sense, they are synchronous. However, under the hood, a lot of function calls are executed asynchronously on the GPU. Usually, the OpenGL (WebGL) driver sends a command to the GPU, and it doesn't wait until the GPU finishes the task. However, the API is designed in a way, that it hides this asynchronous behaviour from you. So if you call a WebGL function, after it returns, you can access the related resources, because the driver makes sure, that the commands already finished the time it lets you access.