PDA

View Full Version : wglMakeCurrent - clarification wanted



zeckensack
05-07-2004, 02:40 AM
MSDN says (http://msdn.microsoft.com/library/en-us/opengl/ntopnglr_2u0k.asp) :
"A rendering context can be current to only one thread at a time. You cannot make a rendering context current to multiple threads."

This isn't precise enough for my tastes. What happens if I try to make current a rendering context that is still current to another thread?
Will the call fail and the "new" thread won't have a GL context?
Or will the call succeed, ie the "new" thread will "steal" the rendering context from the "old" thread?

Is there some sort of specification for this sort of collision, or a consensus between driver developers about what should be going on?

I can of course just check it out, but I don't have hardware from all vendors and neither do I have all Windows versions. Even if my testing shows good results, it wouldn't be anything I could really rely on. So, clarification wanted :)

I know I shouldn't be doing this in the first place, but if you're writing libraries, there's often a need to cover up for flawed client applications.

A workaround has been designed but not implemented yet. It's a sort of FIFO buffer that serializes and transfers data and commands from an arbitrary number of client threads into the context of a single worker thread, which will be the only one that touches OpenGL. This will probably come at a performance penalty and is something I'd rather not waste my time on, if there's a reliable way to avoid it.

evanGLizr
05-07-2004, 02:06 PM
Originally posted by zeckensack:
MSDN says (http://msdn.microsoft.com/library/en-us/opengl/ntopnglr_2u0k.asp) :
"A rendering context can be current to only one thread at a time. You cannot make a rendering context current to multiple threads."

This isn't precise enough for my tastes. What happens if I try to make current a rendering context that is still current to another thread?
Will the call fail and the "new" thread won't have a GL context?
Or will the call succeed, ie the "new" thread will "steal" the rendering context from the "old" thread?
When you do a wglMakeCurrent, the ICD actually receives two calls: DrvReleaseContext on the previous current context in this thread, followed of DrvSetContext on the new current context.

Now, for your case, I would say you've just been lucky because the graphics cards you've tested don't really do multithreading rendering. You cannot just happily access variables from other thread. An interesting test to do is what happens when you do

T = 0
Thread 1:
wglMakeCurrent(glrc1);
glCommands();

T = 1
Thread 2:
wglMakeCurrent(glrc1);
glCommands();

T = 2
Thread 1:
glCommands // Without doing a wglMakeCurrent first.

My guess is that the glCommands will still go to glrc1, even if it's current in Thread 2.

If you want to make a context current in a thread while it's current on another thread, you have to first detach the context from the thread it's current on (this will flush the rendering commands) and then you will be able to attach it to the other thread.

One of the reasons why it has to be this way is because the pointer to the current context is part of the Thread Local Storage (TLS) information. There's no easy & safe way to access one thread's TLS from another thread (what if a third thread is also trying to steal the context from the first and second?). Besides, allowing that behaviour would force the driver to guard all OpenGL calls with synchronization primitives (and you don't want to be doing that on each glVertex).

You'd better make a given context current in the same thread for the whole life of the context.

zeckensack
05-07-2004, 10:46 PM
evanGLizr,
thanks for answering. That's what I feared.

I cannot detach contexts first because this is just a library. I have no control over when thread switches occur, I can only detect that it has happened. The client application is the process that creates all threads in the process, and I've seen it happen that my entry points were called from different threads. This doesn't work well with OpenGL for obvious reasons.

Imagine this library as being incepted as a software renderer. It isn't designed to be thread safe, or rather it doesn't care about threads at all, because all state and resources are in regular system memory and thus are visible to all threads in the process.

Synchronization and serialization of commands isn't an issue. I can handle that. It's really only about getting "my" OpenGL context current to whatever thread currently requires it.

If context "stealing" were possible, I'd use a per-thread "time of MakeCurrent" timestamp in TLS and a global "most recent MakeCurrent" timestamp. Before issuing GL commands, I'd check whether the current thread was the last one that grabbed the GL context by comparing the TLS timestamp to the global timestamp. This would avoid redundant calls to MakeCurrent.

If another thread loses access to the GL context in the process, I wouldn't mind. I would just get it back the next time I need it.