Does WGL support more than one window/context per thread?

263 views
Skip to first unread message

Mick Pearson

unread,
Jun 24, 2021, 1:37:38 AM6/24/21
to angleproject
I'm trying to understand why the OPENGL backend can't seem to work more than one window/context at a time. In DisplayWGL.cpp DisplayWGL::makeCurrent is where eglMakeCurrent is supposed to change the context by proxy.

However, every time I pass a display/surface/context triplet through this, that are distinct, when it gets to its guard it always prevents calling wglMakeCurrent:

    if (newDC != currentContext.dc || newContext != currentContext.glrc)
    {
        ASSERT(newDC != 0);

        if (!mFunctionsWGL->makeCurrent(newDC, newContext))
        {
            // TODO(geofflang): What error type here?
            return egl::EglContextLost() << "Failed to make the WGL context current.";
        }
        currentContext.dc   = newDC;
        currentContext.glrc = newContext;
    }

The net effect of this is the first created context is always the current context even after eglMakeCurrent appears to succeed. Calls to glViewport (for example) that work with the same app under D3D11 and non-ANGLE context change the viewport of the first made/bound context's instead of the context that last called eglMakeCurrent.

In the same function at the top it has:

CurrentNativeContext &currentContext = mCurrentNativeContexts[std::this_thread::get_id()];

For me this vector has 1 element, suggesting 1 thread. The currentContext.dc and currentContext.glrc members are always different when DisplayWGL::makeCurrent is entered. It looks like it's already matching the values it's compared to in the guard. That suggests that it's already done the change. But if it did then the real wglMakeCurrent context shouldn't be changing the viewport for the first made context in fact.

There's no other calls to wglMakeCurrent in my app I'm seeing this with. I.e. ANGLE is fully in control of OpenGL/WGL. At least I'm pretty sure (I make mistakes sometimes.)

What's glaring is if I look for mFunctionsWGL->makeCurrent in that file, I see this all over the place in the file. This is actually calling wglMakeCurrent. It doesn't add to me why ANGLE would changing the current context autonomously all over the place. It seems like there should only one path to it that's delegated to the user via eglMakeCurrent, but if not it seems like any one of these could be a source of problems. But I never actually see it called via eglMakeCurrent, so it seems like the simplest explanation is somehow this guard always fails for multiple windows/contexts and so the context never changes.

What's worse is after eglDestroyContext maybe it can't switch to a new context. I filed a bug earlier today (https://bugs.chromium.org/p/angleproject/issues/detail?id=6092) that gets trapped in ClearError because there's no context when it calls glGetError in a loop. At the time I reported the bug I thought this was because I had some wglMakeContext(0,0) calls in places that I felt (probably rightly) were interfering with ANGLE. (The bug is clear cut regardless of what triggers it) however in this case I've removed those to make sure there's no interference, and still encounter this bug after eglDestroyContext, suggesting this is another way to enter the same ClearError endless loop bug.

I think my build is kosher. I don't know what could account for this other than everyone else is putting their contexts/windows on separate threads. Anyway I hope this post is productive. Sorry if not. I worry I'm being overbearing today. Here (https://groups.google.com/g/angleproject/c/qnQo1kK1vKQ) is another post from earlier, I'm really trying to get one backend that can work for my short term needs. D3D11 doesn't seem to handle resizing windows well, whereas OPENGL does, but doesn't seem to handle more than one window at a time. At least not in my set up.

Mick Pearson

unread,
Jun 24, 2021, 1:47:26 AM6/24/21
to angleproject
P.S. (Sorry double-posting) I realized technically it does switch the context the first time, but not subsequent times after that. I wasn't thinking about what order the two windows in my test demos are created. It's the last one that seems to take control and after which not let any of the previous windows take back the context ownership. I.e. if I set a breakpoint inside the DisplayWGL::makeCurrent guard block it's not entered even though (GLUT like) interactions are taking place in the windows/contexts. Also in a 3 window scenario it's the third window that ends up being the only functioning context/window. The others try to switch the context but end up using the same one, which causes everything to appear to go haywire.

Mick Pearson

unread,
Jun 24, 2021, 6:44:24 AM6/24/21
to angleproject
Here's the problem (I think) and a fix that seems questionable to me... what I realized is DisplayWGL is may be the "display" in EGL terms and that eglGetDisplay (awful name, always confuse me) is returning a different display (both the word "get" and "display" seem extremely inappropriate here) for each window. That is if you go by examples of using GetDC() to get a window's "display" (what else is there that's a DC after all?) and so, WGL on the OPENGL backend is making multiple "displays" on a single thread, even though wglMakeCurrent is a per thread context, as far as I know. And anyway, it's not calling wglMakeCurrent because the guards only know about their display's contexts, and that display only has one context, because every context is an island to itself in this implementation. So as a result the display can only make its context current once before its guards kick in (is this making sense?) and then prevent it from ever making itself current again.

IOW I think probably DisplayWGL should know about every other DisplayWGL and which one of themselves is current on a given thread. It's really unclear what the heck a "display" is even supposed to be. It sounds like it should be a display adapter. Whatever it is it doesn't seem right to work this way... there is no "make current" for displays. And D3D11 seems to work in spite of this. (And I don't know what to pass to eglGetPlatformDisplayEXT other than 0 for EGLNativeDisplayType. I think (HWND)0 is the desktop. I'm not sure what (HDC)0 is. I think EGL is one context per thread? Isn't it? I'm pretty sure WGL is.)

Geoff Lang

unread,
Jun 28, 2021, 3:40:45 PM6/28/21
to ho...@swordofmoonlight.net, angleproject
Ok, lots going on here but I'll try to make a quick explanation:

ANGLE's GL backend virtualizes contexts using one native context. It assumes that it is the sole user of egl/wgl/glx and that it's state tracking of current context is not changed behind it's back, it would be very expensive to do those checks every gl/egl call.  Good find with the multiple displays, eglGetDisplay returns a display based on a key generated from all the parameters and our GL backends do not play nicely when there is more than one simultaneous instance. An error would be good for this case.

When using ANGLE, you'll have best results if you use 1 EGLDisplay and 1 thread. You can use multiple contexts and surfaces just fine.

--
You received this message because you are subscribed to the Google Groups "angleproject" group.
To unsubscribe from this group and stop receiving emails from it, send an email to angleproject...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/angleproject/6d10eb89-1bd8-4c3e-baee-0fd6aa155767n%40googlegroups.com.

Mick Pearson

unread,
Jun 29, 2021, 1:36:32 AM6/29/21
to angleproject
For the record it occurred to me EGL's ideas of "Displays" seems akin to GLX/X displays. Does Google Chrome use "1 thread"? I wonder myself if having ANGLE do a TLS (thread local storage) handshake on every ES call is a serious penalty compared to if it only used regular storage. Of course it's not as good as having a stack pointer like the ContextANGLE extensions. You make me wonder if TLS has a built in optimization that makes it fast as long as the object is only initialized for one thread (it would ease my concern to know this) (I've done some cursory searches on the topic) have you guys profiled it with TLS disabled on gl::GetValidGlobalContext and/or with single thread as opposed to work on multiple threads? (BTW: On another topic I just replied to you recommended "GL renderer strings" and now I wonder if those part of this "gl::" namespace that I think maybe isn't limited to the OPENGL backend.)


>" It assumes that it is the sole user of egl/wgl/glx and that it's state tracking of current context is not changed behind it's back, it would be very expensive to do those checks every gl/egl call."

It seems this is not a problem for a separate context as long as the MakeCurrent status is synchronized (as long as ANGLE doesn't autonomously takeover the context on the main thread... i.e. not via an ES procedure... and if it does it restores the context.)


>" Good find with the multiple displays, eglGetDisplay returns a display based on a key generated from all the parameters and our GL backends do not play nicely when there is more than one simultaneous instance. An error would be good for this case. " 

That's not the reaction I would like to hear. It seems like an error would just be confusing. It seems like the OPENGL backend is structured wrong, since contexts belong to threads and not displays. D3D11 seems to work correctly. OPENGL should just solve this problem. The problem is OPENGL implements eglMakeCurrent like it's making the context current for the display handle passed to eglMakeCurrent... and if there's only one handle on that display, it assumes it is current, whereas really the semantics of eglMakeCurrent is to make the display/context pair current for the thread. (Not make the context current for that display. There is no way to nominate a current display for the GLES calls to be relative to. They're reltive to contexts.)

Mick Pearson

unread,
Jun 30, 2021, 1:52:16 AM6/30/21
to angleproject
For the record (I'll try to submit a bug report maybe) another gotcha related to this that may not apply to all backends is eglMakeCurrent(0,0,0,0) is an error. This seems counterintuitive. I had to resort to eglGetCurrentDisplay(eglGetCurrentDisplay(),0,0,0) to get OPENGL to sync with non-ANGLE OpenGL. It feels like a kludge other than I'm already forced to use eglGetDisplay(0) (or eglGetPlatformDisplayEXT) just to work with OPENGL on Windows. eglGetCurrentDisplay(eglGetDisplay(0),0,0,0) might also work more sensibly in hindsight.

But simply put eglMakeCurrent(0,0,0,0) seems to make sense to me unless I'm missing something that's not obvious. I don't see why that should generate an error. Querying the current display or default display seems like a source of errors compared to unbinding OpenGL completely.
Reply all
Reply to author
Forward
0 new messages