OpenGL and multithreading

From OpenGL.org
Jump to: navigation, search

Can I create an OpenGL context for a control in a different thread than the one in which the was created on?

Yes. According to the Microsoft documents (MSDN), there is no restriction. You can probably do the same on *nix systems as well.

Can I create and dispose of resources in a different thread than the one the OpenGL context is in?

If you make the context current (wglMakeCurrent for Windows). However, it does not buy you much, if anything at all, so it's not worth the trouble.

It is recommended that you call wglMakeCurrent(NULL, NULL) if GL context is current on another thread, then call wglMakeCurrent(dc, glrc) in the other thread.

Example :

  Thread_1:
  wglMakeCurrent(NULL, NULL);
  WaitForThread2(); OrDoSomeCPUJob();
  wglMakeCurrent(dc, glrc);
  Thread_2:
  wglMakeCurrent(dc, glrc);
  DoSome_GL_Work();
  wglMakeCurrent(NULL, NULL);
  TerminateThread2_And_GiveControlToThread1();

Another solution is to have multiple GL rendering contexts.

You can create both contexts from your main thread where you already created your window. You can then setup resource sharing with wglShareLists(glrc1, glrc2). As we have explained in the Windows specific section

http://www.opengl.org/wiki/Platform_specifics:_Windows

wglShareLists causes a few things to be shared such as textures, display lists, VBO/IBO, shaders. You should call wglShareLists as soon as possible, meaning before you create any resources. You can then make GL rendering context 1 current in thread1 and make GL rendering context 2 current in thread2 at the same time. If you upload a texture from thread2, then it will be available in thread1.

Example :

  Thread_1:
  glrc1=wglCreateContext(dc);
  glrc2=wglCreateContext(dc);
  BOOL error=wglShareLists(glrc1, glrc2);
  if(error == FALSE)
  {
     DWORD errorCode=GetLastError();
     LPVOID lpMsgBuf;
     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf, 0, NULL);
     MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
     LocalFree(lpMsgBuf);
     //Destroy the GL context and just use 1 GL context
     wglDeleteContext(glrc2);
  }
  wglMakeCurrent(dc, glrc1);
  RunMyGame();
  Thread_2:
  wglMakeCurrent(dc, glrc2);
  LoadTexturesOnTheFly();

Obviously, the above code is Windows specific but we thought it would be good to see an example of creating context, resource sharing, using GetLastError() and using FormatMessage() and MessageBox() and LocalFree().

The above method is usually done to load textures on the fly as a user walks through some gigantic scene in a game. If you were to use a single GL context, each texture creation (and mipmap creation as well) would cause things to halt for a split second. It would be noticeable to the user.

It is an old technique. GL 2.1 brought PBO. PBO stands for Pixel Buffer Object. It allows you to upload a texture to GL in a asynchronous fashion which means that calls to glTexImage2D are instantaneous.

The driver just keeps a copy in RAM and uploads to VRAM at the best possible moment such that no lag will be noticeable. Typically, the texture would be ready for use by the next frame. If you do use the texture immediately after creation, the driver is forced to upload it immediately and you'll notice a lag.

PBOs can be used for other cases as well. Examples are in the extension definition file at http://www.opengl.org/registry/specs/ARB/pixel_buffer_object.txt

and the Wiki page about PBO http://www.opengl.org/wiki/Pixel_Buffer_Object

Can I make GL calls?

Yes, see above to see how to call wglMakeCurrent. Again, it doesn't improve performance. GL function calls go to the driver and the driver collects the function calls into command lists, which finally get sent to the GPU. Usually there is just 1 GPU so there is no performance increase.