Explicit context sharing with QOpenGLWidget
-
So I have a QOpenGLContext with a QOffscreenSurface that I use with QOpenGLFramebuffers to make some stuff. Eventually, I may want to create a QOpenGLWidget to display this stuff. QGLWidget could take a QGLContext for explicit sharing. QopenGLWidget doesn't seem to have anything similar. When I try to use my QOpenGLFramebuffer as a texture to draw in my QOpenGLWidget, I get a blank white draw, as if there was no texture, which has me suspicious that my FBO isn't sharing correctly with the QOpenGLWidget.
This happens at application startup:offscreenSurface = new QOffscreenSurface(); QSurfaceFormat format; format.setMajorVersion(4); format.setMinorVersion(0); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setSamples(0); format.setOption(QSurfaceFormat::DebugContext); QSurfaceFormat::setDefaultFormat(format); offscreenSurface->setFormat(format); offscreenSurface->create(); context = new QOpenGLContext(); context->setFormat(format); context->create(); context->makeCurrent(offscreenSurface);
Then I eventually make an FBO to prepare my image in which does this:
offscreenSurface = new QOffscreenSurface(); QSurfaceFormat format; format.setMajorVersion(4); format.setMinorVersion(0); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setSamples(0); offscreenSurface->setFormat(format); offscreenSurface->create(); context = new QOpenGLContext(); context->setShareContext(contextThatWasSetUpInApplicationStartup); context->setFormat(format); context->create(); context->makeCurrent(offscreenSurface); QOpenGLFramebufferObjectFormat f; f.setSamples(0); f.setInternalTextureFormat(GL_RGBA32F); frameBuffer = new QOpenGLFramebufferObject(width, height, f); frameBuffer->bind();
And then later on I make a QOpenGLWidget, and try to draw using that framebuffer's texture() with soem simple immediate mode. But the result is blank, as if there was no texture. If I try to use shaders that were generated in app startup using my global context, I get a lot of things like """QOpenGLDebugMessage("APISource", 1281, "GL_INVALID_VALUE error generated. Program handle does not refer to an object generated by OpenGL.", "HighSeverity", "ErrorType")""" from my debug logger. So, it definitely seems that sharing isn't happening. How can I do it explicitly? Anybody have any ideas?
-
Your first context is a debug context, while the other is not. I can't find an official doc that would prohibit this but it might be the issue. Since debug context might be implemented in an entirely separate driver path, eg. in software, it's unlikely IMO that it could be shared with non-debug one.
Usually you'll want shared contexts to be as similar (identical ideally) as possible to avoid any drivers weirdness.
If that doesn't help you might try the other way to share contexts instead of calling setShareContext() set the Qt::AA_ShareOpenGLContexts attribute on the application. Of course this would make all contexts in your app shared.
-
Thanks for taking a look at this. I have been using
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); QApplication a(argc, argv);
in main and it doesn't seem to fix this. All contexts shared would be fine for what I am doing. That's why I was trying to figure out how to manually set context sharing to force it. The docs don't seem to make it 100% clear that the auto sharing will apply to things like offscreen surfaces. It mentions only "Enables resource sharing between the OpenGL contexts used by classes like QOpenGLWidget and QQuickWidget. " Which may or may not mean any OpenGL contexts, but is frustratingly vague. I also just tested your suggestion that debug contexts might have been my problem and did a build where I purged everything that set a debug flag, so the contexts should all be set up the same now. But I didn't see any change in behavior.
It's incredibly frustrating being so close to having some very cool stuff working. I have my pixels on the GPU and I just want to draw them to the screen. It sounds like that should be the easy part. :/
-
I might be shooting in the dark here, but:
if I try to use shaders that were generated in app startup using my global context, I get a lot of things like
I'm not clear on that part. Are you trying to use shaders in the second context that were created in the first one before the second one was created? Resources created before the shared context was created are not guaranteed to be shared (IIRC) and using their handles can cause errors. Actually now that I think about it creating an FBO in the second context also creates a texture, which might interfere.
It works pretty much like this:
C1 = create context C2 = create shared context T1 = create texture in context C1 (gets handle ID 1) T2 = create texture in context C2 (gets handle ID 2)
And that's ok, but if you create your shared context much later then you can end up with this:
C1 = create context T1 = create texture in context C1 (gets handle ID 1) C2 = create shared context T2 = create texture in context C2 (can also get handle ID 1 !!! )
Same goes for shaders and other shareable resources. FBOs are not shared but Qt wrapper for FBO also creates a texture.
The general rule of thumb with shared contexts is that you should create them at the same time to prevent this resource handle collision.If that is a miss too then another thing to check is if your framebuffer is valid (
isValid()
). You set a floating point texture format. Is it supported on your graphics card? -
Thanks again for the suggestions. I am still hacking away at this trying to figure out what I am doing wrong, but I am still bashing my head against a wall. I fixed the shaders stuff. I now have a singleton global Application instance which will regenerate the shaders for a specific context when that is created. Which is a bit redundant, but I can live with it.
So, my plan was very much to go C1, T1, C2, draw T1 in C2. Good to know that's probably going to be a bad plan. So, what would be a good approach to having a bunch of processing happen, and then a use may open a new window to look at the results? The context for a window that the user may or may not ever open naturally can't be forced to exist at application start. Even if I created a context pool at application start, there's no way to force a QOpenGLWidget to be bound to an existing context.
Floating point seems okay. I can certainly draw into the FP FBO, and run toImage on it witha correct result. I tried dropping that part out so the FBO is using a default (8 bit) format and didn't see a change in behavior.
I have also tried regenerating the FBO on every frame, which would recreate the FBO's texture after the QOpenGLWidget has been created, but I still get a blank draw.
I know that the QOpenGLWidget has glEnable(GL_TEXTURE_2D) and everything else in needs because I also tried ("viewport" is the QOpenGLWidget subclass, and "Timeline" has a QOpenGLFrameufferObject and basically wraps its ->toImage()
auto img = Timeline->getQImageAtTime(time); viewport->makeCurrent(); QOpenGLTexture *texture = new QOpenGLTexture(img.mirrored()); viewport->setTextureID(texid);
and that works fine, so the viewport is mostly doing the right thing, whereas this (which wraps fbo->texture(). I have also tried fbo->takeTexture(), and I have tried both making sure the FBO does and does not do a ->release() in getGLTextureAtTime)
auto texid = Timeline->getGlTextureAtTime(time); viewport->setTextureID(texid);
is still blank, even after the stuff I mentioned like dropping the FBO down to 8 bits. So even with the FBO being regenerated every frame after the viewport has been created I am still not sure that the sharing is happening correctly.
-
I finally noticed a way to check that contexts are actually sharing is a method of QOpenGLContext:
context()->areSharing(context(), ac->getGLContext())
I ran that from from QOpenGLWidget subclass to check if it is sharing with the global share context created at application startup, and they are not sharing. So that global app flag doesn't seem to work with offscreen contexts. So, that's useless. And I still haven't found a way to explicitly tell a QOpenGLWidget to share with an existing context. I'm honestly kind of pissed off that QGL* has theoretically been deprecated since 5.0, and now with Qt 5.4, we still don't have a replication of the functionality in QOpenGL*.
-
@wrosecrans I know this thread is quite old, but in case someone is having trouble doing the same thing.
I was doing something similar and was doing a lot of research, what finally works for me is to have a global QOpenGlContext pointer and in QOpenGLWidget::initializeGL()
global_glContext = context();
then in QOffScreenSurface::initializeGL():
context->setShaereContext(global_glContext);
context()->create();and this works for me so far.
-
@Shunxu said in Explicit context sharing with QOpenGLWidget:
global_glContext = context();
Hey
I'm having the same issue now.
So where do you call the global_glContext = context(); ?
I'm trying to find a way to use offscreen surface to do some calcs and then send that data down to qopenglwidget to display.