Hi.
I was unable to write anything here for some time.
BUT, finally I was able to find a way to make multithread image loading using OpenGL with Windows extensions for multiple context creation.
It was quite a struggle to find anything useful in the web, only some minor things at few forums and mailing groups. But using that with some thinking and reading between lines I finally found the solution I’d like to share, as it can save (in my opinion) much time of searching and experimenting for others.
So what do you need if you’d like to create multithread texture loading in OpenGL ?
It’s tricky. First of all you have to know, that you can’t do it straight forward, as you need OpenGL context for each thread you’d like to use OpenGL procedures.
To create new rendering context (as it’s called), you need device context first. You can get it from your HWND.
For me it was not straight forward, as main window is created using SDL. But fortunately it’s quite easy to get it from there too.
#include <wingdi.h>// for wgl* stuff
#include<SDL_syswm.h>//for SDL stuff
//...
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
if(SDL_GetWMInfo(&wmInfo) < 0) //returns (-1) on error
{
std::cout << "error in SDL_GetWMInfo");
}
HWND hWnd = wmInfo.window;
So, now we have HWND. To get device context, you only need to call
HDC hDC = GetDC(hWnd);
.
OK, that was easy.
Now we need more OpenGL contexts.
To create those, we need some Windows extension for OpenGL.
To be honest, to create contexts we need only one – wglCreateContext
.
This procedure takes device context as parameter and returns new rendering context (or NULL).
No matter what people says in forums – you CAN have more than one rendering context for only ONE device context. More than that – in my opinion it’s very natural. And it works just fine. Let’s create two of those contexts.
HGLRC rendContext1 = wglCreateContext(hDC);
HGLRC rendContext2 = wglCreateContext(hDC);
Cool. We have two OpenGL rendering contexts. What we would like to do now is:
use one context for images loading, textures creation etc. and let second context in main thread do rendering stuff etc. This way if you have multiple images to load, you can simply load them in background thread and in rendering thread start showing loaded one. The good thing is – you can load textures in background and it won’t freeze your interface, as it’s done in other thread.
But it’s not as easy as it looks like, because you have to fix sharing of textures between contexts. Normally, if you create a texture at one context, it’s not available in second one and vice versa.
Here is another procedure from Windows extension pool:
wglShareLists(rendContext2, rendContext1);
I didn’t try different order of arguments, probably it’s not meaningless (but MSDN is useless in most important things and sadly wrong in too many places :( ). The safest solution is to do this BEFORE you load anything in any context. In documentation you can find, that you can’t have nothing loaded in second of passed contexts (in our case it would be rendContext1
). To be sure, call this before you start messing with OpenGL.
Now, you have to set default context for each of your threads, so it can work properly with OpenGL calls. You don’t need to call it every time, only once in a thread.
renderingThread()
{
//...
wglMakeCurrent(hDC, rendContext1);
//...
}
loadingThread()
{
//...
wglMakeCurrent(hDC, rendContext2);
//...
}
If you have this written in above manner, you can simply ‘fork’ loading thread from main thread using e.g. boost::thread class. Again, according to some OpenGL forums, it’s important to create ‘loading thread’ from rendering thread, but I didn’t check that either. It’s important, that wglShareLists will work only in the space of one process, not between processes, so only way for parallel execution is using threads.
Calling wglMakeCurrent only makes one of rendering contexts default to the thread in which is called, so it doesn’t matter if the call is done exactly in the same time in both threads. Nevertheless, it’s important to have different contexts for each thread.
After making above steps, you should be able call whole loading stuff in loadingThread thread (as e.g. loading data from disk [ilLoadImage
], then creating texture name using glGenTextures()
, set parameters glTexParameteri()
and finally load data to OpenGL using gluBuild2DMipmaps
or glTexImage2D
), and in renderingThread thread you will have access to textures loaded this way.
If you’d like to also load data in renderingThread (e.g. for blocking image loading), you have to remember that some image loading libraries (like DevIL, indirectly mentioned earlier) doesn’t have support for multithreading, so you need mutex to provide atomic calls to it’s methods.
I didn’t try to render from both contexts, but according to some resources found in the web, it’s possible but pointless, as in that case OpenGL would have to switch between contexts (calling wglShareLists shares bunch of stuff, but not the rendering context itself), which is very memory intensive and creates unnecessary overhead.
The most important thing is waiting for as at the end of the road ;-)
Don’t forget to release created contexts. In each of threads you should call default rendering context to none using wglMakeCurrent(NULL, NULL)
.
Deinitialization step in main thread should look similar to code below
wglMakeCurrent(NULL, NULL);
wglDeleteContext(rendContext2);
wglDeleteContext(rendContext1);
Don’t forget to call this NULLed-wglMakeCurrent in loadingThread on exit from it.
Hopefully I covered most of important things in multithread and multicontext image/texture handling using OpenGL and WGL.
Please, do not hesitate to ask a question regarding those problems.
If you find a bug or something, that looks weird to you, let me know ASAP – maybe I’m missing something (or you are ;-) )
Till next time…
January 17th, 2009 at 09:59
Thank you so much for this! I’ve been trying for hours to move the ogl context to another thread so that my rendering code would not block the input loop. This worked like a charm, and I’m looking forward to threading out my texture loading!
January 17th, 2009 at 14:06
ah, cool that it was useful for someone ;-)
btw in meantime I figured, that you don’t need to do:
HGLRC rendContext1 = wglCreateContext(hDC);
you can simply do
HGLRC rendContext1 = wglGetCurrentContext();
and it should work:)
March 16th, 2009 at 18:37
I have found this only work if you do it in the absolute beginning, absolutely nothing done in the context. It’s not the safest way, it’s the only way ;)
March 16th, 2009 at 19:00
hmm maybe you’re right :-)
October 5th, 2009 at 04:53
[…] I chose the ‘difficult way’. After doing some reading up on multi-threading assets on the iPhone I figured it wouldn’t be too difficult to get running and I could implement it […]
November 24th, 2009 at 09:17
Very useful thank you but im wondering why the cpu-usage goes up when the loader thread doesnt do anything.
http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=267603#Post267603
November 24th, 2009 at 10:06
ColacX, its hard to say anything useful unless we can see your code. But it seems really weird – maybe your rendering thread is going mad when no loading is taking CPU time ?
November 24th, 2009 at 10:14
uhm the code is the link i posted.
the rendering thread is supposed to block for verticalsync as i understood it.
November 24th, 2009 at 10:29
ah, sorry – I couldn’t access that link before my last post, that’s why ;-)
I’m not sure though, why you get so strange behaviour.
November 24th, 2009 at 15:49
Do you get the same result on your program?
November 30th, 2009 at 21:00
i got my answer its in the link
glFinish(); ftw
August 27th, 2010 at 16:45
Thaks For Share
July 26th, 2012 at 21:54
you made a big error
sure, you have two opengl threads, and they both make opengl things
but that’s the only thing you’ve done, all opengl calls are chainly executed, that’s mean, while you call opengl from thread2, calls from thread1 are waiting, i just tested it. if you want to verify it, just load a 20480*20480 texture in thread2. your thread1 renderer will sleep like a shit.
if you want to use this method for remove hhd load times from render thread, just do that and share results by ICM, no need to make 2 opengl threads.
damn, don’t waste your time on this page
October 17th, 2012 at 20:10
Did you test what part was actually blocking the call? I suppose, that moving the data to gfx memory was blocking the calls (glTexImage2D), not reading the images from HDD, unpacking JPG/PNG etc.
I didn’t test that code on this kind of huge images (for me max was around 2048×1024).
OpenGL by design is NOT multithread, so if you’d have a little bit knowledge about OpenGL, you’d know that gl calls are ALWAYS called in chain.
Thing is I wanted to rule out the HDD read time, not the time between memory and GFX graphics, since with OpenGL you won’t optimize that – that’s GL thing, not problem with this solution.
There is no other way to avoid waiting for HDD, that sharing two contexts, and that’s how you do it.
Please do your big textures in one thread and check out the result, or even better – try to load 20 images 2048×1024 and check out the difference. I was actually using this solution for multiple images around mentioned size and the difference was significant. And I was able to show them one by one, not in big shot. You won’t do that without second context.
October 17th, 2012 at 18:25
[…] https://veelck.wordpress.com/2008/11/28/multithread-texture-loading-in-opengl/ […]
May 5th, 2013 at 21:26
Thank you. Thank you. Thank you. Thank you. Oh yeah, thanks!
June 27th, 2013 at 14:21
Very nice post. I just stumbled upon your weblog and wished to say that I’ve really loved surfing around your weblog posts. In any case I will be subscribing for your rss feed and I’m hoping you write again very soon!
June 27th, 2013 at 15:07
I would love to, but due to limited time currently I didn’t have time to prepare more useful tips.
Thanks for comment :)
January 23rd, 2017 at 05:23
[…] to render to multiple monitors A FAQ on parallel OpenGL Programming OpenGL and Multithreading A tutorial on using a second OpenGL context for texture streaming. The story of multi-monitor rendering (Mainly about DirectX but there’s some interesting stuff on […]