glad
glad copied to clipboard
Multicontext documentation ambiguity and possible bug?
I'm kind of new to this stuff so some things might sound off. As far as I understand it the multicontext version of glad isn't needed if you're using multiple OpenGL contexts. It's only needed if you are using multiple graphics drivers (i.e. using 2 graphics cards from different vendors) or 2 different OpenGL versions since you'll have to manage multiple dynamic-link libraries in that case. This is the case because GladGLContext
's only contain version/extension metadata and function pointers which I'm sure is obvious to the author.
Even if this is true though I am still confused since I would expect glad_gl_context
to be thread local. Otherwise there will be race conditions when rendering in multiple threads unless you manually synchronize your draw calls with your calls to gladSetGLContext
If we could resolve my confusion I'd be happy to amend the documentation to make it more clear.
On Windows function pointers are indeed context specific, on X they are not. So you do indeed need one instance of the struct per context, but you also need to make sure the correct context is active when you're rendering/using the struct (glfwMakeContextCurrent()
). But even if you're on X, I wouldn't rely on this behaviour and just treat it like a loaded function pointer is context specific, which I mean depending on your context it very well may be (since you can request a different profile and some functions may not be available on this context).
See also: Load OpenGL Functions
Multi-Threading in OpenGL is another rabbit hole, I attempted it once and never tried again. It's not only OpenGL that needs to be properly synchronized and taken care off, also your windowing system. Iirc GLFW requires you to run glfwPollEvents
on the 'main' thread, which then in turn also runs all callbacks in the main thread.
Maybe you can 'bind' a context to a specific thread and they work in parallel, but personally I can't help you here other than saying "It sounds like it could work".
Back to the original question, you can use the non mx version of glad with multiple contexts, as mentioned on a platform that does not return context specific pointers or if you call gladLoad*
every time you swap contexts (which is gonna be slow if you do that every frame). This is where gladSetGLContext
comes in handy, you would call it together with glfwMakeContextCurrent
and keep using the global function aliases, now maybe it would make sense if this was a thread local, but personally I would just use the explicit version and pass the GL struct around, maybe with your own typedef
.
Also a small correction, you don't have multiple GL dynamic libraries for different versions.
Now why is gladSetGLContext
not thread local, honestly I haven't thought about it so far, it's nothing I would use. Right now I don't see an immediate disadvantage, it would only be a problem if you're using multiple threads and if you do that, you'd probably benefit more from it than that it would be a problem.
Maybe thread locals aren't available on all platforms? But does OpenGL even exist on these platforms? __thread
and __declspec(thread)
cover enough probably.
So apparently it's not that easy to get a cross platform (cross compiler) thread local, this is the best I could find:
#ifndef thread_local
# if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
# define thread_local _Thread_local
# elif defined _WIN32 && ( \
defined _MSC_VER || \
defined __ICL || \
defined __DMC__ || \
defined __BORLANDC__ )
# define thread_local __declspec(thread)
/* note that ICC (linux) and Clang are covered by __GNUC__ */
# elif defined __GNUC__ || \
defined __SUNPRO_C || \
defined __xlC__
# define thread_local __thread
# else
# error "Cannot define thread_local"
# endif
#endif
From: https://stackoverflow.com/a/18298965
Currently debating if this is something glad should do. Input welcome.
I also hit this problem. I think it is not a good idea to change glad to use thread-local by default, just provide a macro that we can use to inject thread_local
(C++11), or anything else, in all global definitions.
Why not just place the --mx
loader's static state inside a struct that the user can allocate in their own preferred manner? Then we can either put it inside thread-local static storage, or just dump it into our already-existing heap-alloc'd context structs / dispatch tables.
Actually, looking into it further, I don't understand why the --mx
loader uses static state at all. As far as I can tell, it appears to immediately close the dlopen
'd handle right after it's done loading anyway - although it does so in an unnecessarily race-y way.
Wouldn't it make a billion times more sense to just delete the static variable altogether and keep the void *handle
on the function stack entirely, where it already is, anyway?
MX_GLOBAL is gone for now. MX stores the handle on the struct, custom loaders have the ability to attach their own state through the *userptr
.