Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why OpenGL ES functions cannot be called from another thread

I am experimenting with writing a small game engine for Android using OpenGL ES. I have created a Game Thread which updates the game objects and the GLThread with draws the scene. I had read that you need to load the textures in onSurfaceCreated method of GLSurfaceView. I am following that but for some debugging purpose I was trying to load textures from my Game Thread instead of the GLThread. I got no errors and the texture were not showing up on the screen. I spent my entire day trying to figure out the problem and finally I read the below here

"Just be sure to only use OpenGL in the main thread." Very important. You cannot call in your Game Engine (which may be in another thread) a texture-loading function which is not synchronized with the gl-thread. Set there a flag to signal your gl-thread to load a new texture (for example, you can place a function in OnDrawFrame(GL gl) which checks if there must be a new texture loaded.

I will modify my code so that the textures will be loaded from the GL Thread. I just could not understand why is it so? Why do the OpenGL functions do not work from another thread?

I know how to create threads but I do not know what synchronization means. The above extract mentions, "You cannot call in your Game Engine (which may be in another thread) a texture-loading function which is not synchronized with the gl-thread." So I guess my Game Thread might not be synchronized with the GL Thread. Is it possible to create another thread which is synchronized to the GL Thread so that GL functions can be called from it? What should I learn in threading to understand these concepts?

like image 850
Cracker Avatar asked Oct 24 '11 17:10

Cracker


1 Answers

quixoto's comment is closest, I think. The traditional reason that OpenGL contexts are thread specific on pretty much every platform is that OpenGL is heavily dependent on states and has no semantics for making a series of changes atomic. So, for example, the draw operation on one thread might be:

glVertexPointer(... supply vertex positions ...)
glTexCoordPointer(... provide texture positions ...)
/* and supply a few other pointers, maybe bind a texture */

glDrawArrays(... draw some geometry ...)

So the final call provides predictable results only in the context of the preceding calls. If you allow for that bit of code being paused after, say, glVertexPointer, another thread hopping in and doing much the same sequence to draw its geometry and then this code proceeding, it'll draw with quite the wrong geometry, possibly even causing out-of-bounds memory accesses if some of the replaced arrays are smaller than the originals.

Android supplies EGL, which supports the common concept of an OpenGL share group (although implicitly; you supply an existing context that you want a new context to be in a common group with via the third argument to eglCreateContext). If two contexts are in a share group then each of them has an independent state and is safe to call from only one thread but named objects such as textures or vertex buffer objects are available to each of them. So using share groups you can perform OpenGL actions on multiple threads simultaneously so as to be able to combine the results on a single thread.

Tying a context to a single thread therefore isn't so much of an issue. Planning for the issue in OpenGL itself would also be a non-starter because part of the reason that OpenGL contexts are created, managed and disposed of in an OS-specific fashion is that some OSs need to handle this stuff in radically different ways to others or are able to offer better solutions by exposing their own unique solutions.

like image 75
Tommy Avatar answered Oct 26 '22 15:10

Tommy