Explain OpenGL context's relationship with QGLWidget
Its a point of confusion for me how OpenGL context works when using One QGLWidget and even more if I had multiple QGLWidgets.
If I attempt to do any OpenGL calls prior to my QGLWidget being initialized with initializeGL(), the application will assert. But once I've initializeGL() I can start doing things like compiling shaders, setting up VBO's, VAO's etc.
So I assume, once the initializeGL() has been called, any openGL calls are in context of the QGLWidget. I understand OpenGL is a state machine. So any openGL calls go towards the context last set.
So how do I work with multiple OpenGLWidget windows? I don't understand how all the shaders, VBO's, etc relate if I had two or more OpenGLWidgets. Can someone give me some insight how this works?
chrisaverage last edited by
OpenGL calls are run in something called a context. Only one context can be "current" at a time and every OpenGL call is made in that current context.
Now think of a QGLWidget constructor like this:
//create a rendering surface (a window so to speak)
//create an OpenGL context attached to that window
//make that context current
//make that context "not current"
Similarly before Qt calls methods like paintGL or resizeGL it makes this internally held context current and "uncurrents" it afterwards. This is of course made so that you don't have to do it yourself, but if you want to (eg. when painting outside of paintGL) you can force that associated context to become current with QGLWidget::makeCurrent() and QGLWidget::doneCurrent().
Because of this if you keep all your OpenGL calls inside the QGLWidget called methods (initializeGL, paintGL, resizeGL) you don't have to worry about which window it will relate to. It will always be called in the context associated with that window. If you want to do stuff outside of these methods then you need to manage these context switches yourself using the methods I mentioned.
So to work with multiple QGLWidgets/contexts outside of their predefined functions all you need to do is make the context of the right widget current and release it after you're done.
So suppose I have a QGLWidget A and a QGLWidget B--Like two viewports on a single Window, it seems necessary (though I suspect incorrect) that I would have to recompile shaders two times. Once for A and once for B. How can I share the context so I only need to create one set openGL assets?
Rereading your response made me wonder one thing. Are you saying if I have a window with multiple QGLWidgets, that they all share the same underlying openGL context? Any OGL functions executed in initializeGL, paintGL and resizeGL in any of the OGLWidgets all feed into the same OGL context?
chrisaverage last edited by
Sorry if i confused you. Unfortunately terminology in this area is shady at best so it's easy to get lost. I meant a window in an OS sense - a button is a window, a lineedit is a window etc. I'm not talking about a frame (which is what is called a window from a user perspective).
Each QGLWidget gets a separate surface (or window). For the purpose of understanding this you can sorta think that a window is a QWidget (although it's not entirely accurate).
This surface (or widget) can be placed on another surface and so on (a widget with a parent or layout etc.)
You used a word 'viewport' there, but in a wrong way. A viewport in OpenGL terminology is the part of the active framebuffer that is marked for clipping operations. Two different QGLWidgets in the same frame (or a 'window' if you want to shuffle the terms) are not viewports. They can have viewports inside them, but that's not the topic here.
Ok, lets skip some stuff to get to the point. If you have QGLWidgets A and B, each of them gets a separate underlying OS provided surface (That is again not entirely true because Qt does some pretty awesome stuff with it but it's a close enough model to think of). It doesn't matter if these surfaces share a parent or not. They are separate.
An OpenGL context is attached to that surface, which means you have as many contexts as you have QGLWidgets, no matter if they are placed in the same parent widget window or float separately. Each of these get its own initializeGL, paintGL and resizeGL and each of these widgets calls makeCurrent() before they call these methods to set their own context current.
I hope that clears it. Now to the sharing. You can't have disjoint surfaces having the same context. It's just not how it works so you can't have two QGLWidgets having the same context.
However, there is an OpenGL mechanism that lets you share (some) resources between two different contexts. It's called context sharing, which is again very unfortunate name, because it's not the context that is shared but the resources that the two contexts manage.
To create such sharing contexts you can pass a pointer of one QGLWidget to another in its "constructor":http://qt-project.org/doc/qt-5/qglwidget.html#QGLWidget-2
There are some limitations to it, like both have to have the same format and only some types of resources can be shared(eg. textures), but that's another question and you can look it up for yourself.
Very helpful. Thanks for your help!