Unsolved binding same QOpenGLFramebufferObject in second QGLWidget fails
-
I'm trying to bind a QOpenGLFramebufferObject in two QGLWidgets (not necessarily at the same time). I have some code that generates the FBO during the first render, so they should both be using the same object. I'd like to be able to draw something into the FBO in one widget and render from it in another widget.
QOpenGLFramebufferObject* sharedPaintFbo = 0; QOpenGLFramebufferObject* GLView::paintFbo() { if (!sharedPaintFbo) { QOpenGLFramebufferObjectFormat format; format.setInternalTextureFormat(GL_RED); sharedPaintFbo = new QOpenGLFramebufferObject(PAINT_FBO_WIDTH, PAINT_FBO_WIDTH, format); } return sharedPaintFbo; }
The problem is the bind() call in the second GL widget always fails. Doing some debugging it looks like sometimes the context of the shared FBO is different. When I create the two QGLWidgets the second uses the first one as the "sharedWidget" to share the same context. Can someone help me understand why the context is different on the FBO when I use it in the other QGLWidget?
-
I would recommend using QOpenGLWidget instead of QGLWidget. It is even marked int the Qt Docs of QGLWidget :(This class is obsolete. It is provided to keep old source code working. We strongly advise against using it in new code.)
have a look at this blog post for more details.
more specifically the "Context Sharing" part will be of interest. -
Thanks for the tip. I started this project five years ago and must have missed this when it was coming out. I swapped out the QGLWidget references with QOpenGLWidget and removed the shared reference in the widget constructors.
From this:
GLView::GLView(QWidget *parent) : QGLWidget(parent, getSharedWidget())
to this:
GLView::GLView(QWidget *parent) : QOpenGLWidget(parent)
and everything seems to be working as it previously did, which seems like a good first step, but I'm still seeing the same behavior where the QOpenGLFramebufferObject created by the first one has a different context when I query it between widgets. Looking at the context sharing doc in QOpenGLWidget, they should all have the same context because they're in the same window. I've even set
Qt::AA_ShareOpenGLContexts
explicitly before starting the app and still don't see the same context.void GLView::drawPaintStrokes() { paintFbo()->bind(); // NOT BINDING!!! std::cout << "view: " << this << std::endl; std::cout << paintFbo()->isBound() << std::endl; // returning false for every widget but first std::cout << "context: " << this->context() << std::endl; // this is different between widgets std::cout << "fbo: " << paintFbo() << std::endl; // confirmed they're sharing same FBO instance
To be clear I'm calling the above paintFBO() function in paintGL (after the widget is initiailized), and then looking at the context() object that is returned by the same FBO and it's a different context. Is there a problem with how or when I'm creating the QOpenGLFramebufferObject?
-
Sigh, yeah, it's all way harder than it should be.
Here's some stuff I have at the start of one of my apps. You mention that you have context sharing set, but it's possible that setting an explicit default surface format will help ensure that everything is consistent:
QSurfaceFormat format; format.setMajorVersion(3); format.setMinorVersion(2); format.setProfile(QSurfaceFormat::CoreProfile); format.setOption(QSurfaceFormat::DebugContext); format.setSamples(0); QSurfaceFormat::setDefaultFormat(format); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
Note, I am explicitly requesting a debug context by default, so I can use the QOpenGLDebugLogger to get some hints about some of the ten thousand maddening reasons you can get a black screen. See if it pops out anything helpful telling you what is catching fire. Are you releasing the FBO in the one where it works? You need to explicitly keep track of the full lifecycle of the FBO, and that snippet you posted has the bind but not the release. If you don't release it properly, binding it again can make it crap itself and fall in a hole.
Are you doing anything where the FBO could get bound simultaneously as a texture to draw with, and a target to render to? Both are called "binding" but it will fall over and catch fire (and sometimes make kind of cool fractally pictures) and die if you have it as both the source and target simultaneously.
When exactly does paintFBO get created? What context is active when it gets created? If it gets constructed before the QOpenGLWidgets are created (or maybe before they are shown?) then it won't work right. The FBO needs to be created when the right context is bound, and the QOpenGLFrameBufferObject wrapper class can't really keep track of the context where the FBO belongs because it is implicit, so creating it at the wrong time will make it fail before it can fall over and catch fire.
Are you doing anything with threads? Because the implicit current context is thread local, you can bind an FBO in a valid context, use a std::future or something to do asynchronous work, and the context will no longer be valid in the thread where the work is actually done, so it will fall over, kick your dog, die, and some other seemingly random things will have previously caught fire.
I know that's not very specific, but there are a comic number of ways for it to be slightly wrong, so it's hard to be more specific than giving a handful of vague topic areas to look into.
-
The paintFBO gets created lazily in paintGL call to make sure GL context is valid. I'm releasing the FBO but removed the release from the snippet. I'm not doing anything specifically with threads. Just normal widgets. I've trimmed my code down to just drawPaintStrokes() and I'm getting the following error:
QOpenGLDebugMessage("APISource", 1282, "GL_INVALID_OPERATION error generated. Object is owned by another context and may not be bound here.", "HighSeverity", "ErrorType")
Searching around the web, I've seen people claim this is an nvidia driver issue specific to not cleaning up FBOs. Right now all the code is doing is binding the FBO in widget 1, releasing the FBO in widget 1, then trying to do the same in widget 2.
Here's a link where someone seems to be running into the same issue:
https://devtalk.nvidia.com/default/topic/1043241/linux/framebuffer-doesnt-get-unbound-glitches-only-on-nvidia/post/5292053/#5292053