In my previous post on WebKit, I explained how we can debug the WebProcess using GDB. In this post, I am going to talk about another useful debugging tool that can be used to debug graphics: APItrace.
APITrace is a debugging tool for OpenGL and Direct3D programs. To learn about its features you can read the documentation online . In this post, I am going to describe how it could be used to examine the OpenGL state at a given time in order to detect the cause of a WebGL bug in an example test I’ve written to reproduce it.
I am going to use the command line to generate a
.trace file with debugging information and a UI frontend called
qapitrace to examine the contents of this file. The frontent can be installed on Debian by running
apt-get install apitrace-gui.
I’ve noticed that many WebGL tests that use VAOs generate an INVALID OPERATION ERROR when glDrawArrays is called and the default ( null) VAO is bound in WebGL. And so, I’ve written a minimal WebGL test that simply binds the default VAO and draws a quad calling glDrawArrays. Something like that:
gl.vertexAttrib4f(colorLocation, 0.5, 0.5, 0.5, 1);
verifyDraw("with the default VAO", 1, true);
In the snippet above, function verifyDraw calls the function clearAndDrawUnitQuad that calls gl.drawArrays inside.
The test result is something like that:
According to the OpenGL spec, a GL_INVALID_OPERATION error is generated by glDrawArrays in the following two cases:
- GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to an enabled array and the buffer object’s data store is currently mapped.
- GL_INVALID_OPERATION is generated if a geometry shader is active and mode is incompatible with the input primitive type of the geometry shader in the currently installed program object.
which means that we need to figure out if a geometry shader is active at the time that glDrawArrays is called or if a buffer object is bound and its data store is currently mapped.
Using APITrace to examine the problem
APITrace can help us detect the call that generates the error, and examine the whole OpenGL state at the time that this call took place.
Let’s see how:
As we’ve seen in my previous post on debugging WebKit with GDB, the WebGL implementation is part of the WebProcess. So, we’d rather attach APITrace to the WebProcess instead of MiniBrowser.
First of all we need to detect the running WebProcess:
We start the MiniBrowser from our build directory:
and find the WebProcess using ps :
ps aux | grep -i webproc
eleni 4570 0.2 0.2 86247496 209280 pts/0 SLl+ 13:33 0:09 /home/eleni/igalia/install/libexec/webkit2gtk-4.0/WebKitWebProcess 8 20
ps returned the following process:
Knowing the process, we can use APITrace as follows:
apitrace trace /home/eleni/igalia/install/libexec/webkit2gtk-4.0/WebKitWebProcess
But we need to be inside the directory we started the MiniBrowser and to have set the same environment variables we’d set to run WebKit!
For me that directory is my WebKit checkout directory (
/home/eleni/igalia/code/WebKit) and I run
MiniBrowser from there like that:
./build/bin/MiniBrowser bar.html (where
bar.html is the WebGL test).
The command above creates a file
WebKitWebProcess.trace in the current directory. This file can be opened and examined using
opens a window where we can examine the OpenGL calls of each frame:
We can open a frame and see a list of draw calls. We want one of the many glDrawArrays but which one? Checking View->Show Errors Dock will make visible a dock with all errors.
Clicking the line:
glGetError(glDrawArrays) = GL_INVALID_OPERATION will select the buggy
glDrawArrays , and will let us examine it:
Now double clicking the selected glDrawArrays will show a new dock where we can examine the state. There, we can check if a geometry shader was active or a buffer attached to the VAO was mapped at the time that glDrawArrays was called.
Let’s first check the shaders:
In the drop-down list we can see that at the time of the call the active shaders were: a vertex shader, a fragment shader, and two Nvidia specific shaders. There was no geometry shader.
Let’s check the buffers:
No buffers were found.
So, this is weird. None of the potential causes of
GL_INVALID_OPERATION error is satisfied here but we are still getting the error!
Looking back at the error dock we can re-examine the messages for more information:
The message seems to be array not active! What does this mean?
Well, one thing we can suspect by checking the version (
GL_VERSION) in the parameters of the current state is that we run in a versioned OpenGL 3.x (probably for compatibility with GLESv2/3)! In OpenGL 3.x rendering without VAOs is not supported! We can’t simply bind a 0 (null) VAO in the implementation, we need to construct and store a default VAO which will be bound when no other VAOs are bound! This is what is done in our current WebGL implementation but is it done correctly? We need to make sure that we always bind the default VAO when no other VAO is active instead of calling internally something like
glBindVertexArray(0) for example!
So, thanks to APITrace, we now have an idea on what to look at next. Further debugging and error fixing is beyond the scope of this post, and I don’t know if my suspicion is correct yet.
But APITrace gave me a first insight into what might be wrong in WebGL, and some ideas to continue. I believe it’s a very handy tool for debugging graphics in big projects like WebKit.
See you next time!