wrosecrans
wrosecrans

Reputation: 1085

Sharing between QOpenGLContext and QGLWidget

The short form of the question: How can I draw in my QGLWidget using my FBO as a texture, without just getting a blank white image?

And, some background and details... I am using Qt 5.1 for an app that does some image processing on the GPU. have a “compositor” class which uses a QOffscreenSurface, with a QOpenGLContext, and a QOpenGLFramebufferObject. It renders some things to the FBO. If the app is running in a render only mode, the result will get written to a file. Run interactively, it gets shown on my subclass of QGLWidget the “viewer.”

If I make a QImage from the FBO and draw that to the viewer, it works. However, this requires round-tripping from GPU-> QImage-> Back to the GPU as a texture. In theory, I should be able to use the FBO as a texture directly, which is what I want.

I am trying to share between my QOpenGLContext and the QGLWidget’s QGLContext like so:

viewer = new tl::ui::glViewer(this); compositor = new tl::playback::glCompositor(1280, 720, this); viewer->context()->contextHandle()->setShareContext(compositor->context);

Is it possible to share between the two types of contexts? Is this the way to do it? Do I need to do something else to draw in the viewer using the FBO in the compositor? I’m just getting solid white when I draw the FBO directly instead of the QImage, so I’m clearly doing something wrong.


So I have figured out my problem. I was misinterpreting the documentation for setShareContext() which notes that it "Won't take effect until create() is called" which I mistakenly thought meant you had to share the context after it was created. Instead, sharing has to be established right before:

viewer = new tl::ui::glViewer(this);
compositor = new tl::playback::glCompositor(512, 512, viewer->context()->contextHandle(), this);

and the new constructor for my glCompositor:

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(srcCtx);
context->setFormat(format);
context->create();
context->makeCurrent(offscreenSurface);

QOpenGLFramebufferObjectFormat f;
f.setSamples(0);
f.setInternalTextureFormat(GL_RGBA32F);
frameBuffer = new QOpenGLFramebufferObject(w, h, f);

Will create a new FBO in a new context which is sharing with the viewer's context. When it comes time to draw, I just bind glBindTexture(GL_TEXTURE_2D, frameBuffer->texture());

I am marking the answer I got correct, even though it didn't directly solve my problem. It was very informative.

Upvotes: 3

Views: 3365

Answers (1)

Andon M. Coleman
Andon M. Coleman

Reputation: 43369

I believe your problem has to do with the fact that FBOs themselves cannot be shared across contexts. What you can, however, do is share the attached data stores (renderbuffers, textures, etc.).

It really boils down to the fact that FBOs are little more than a pretty front-end to manage collections of read/draw buffers, the FBOs themselves are not actually a sharable resource. In fact, despite the name, they are not even Buffer Objects as far as the rest of the API is concerned; have you ever wondered why you do not use glBufferData (...), etc. on them? :)

Critically Important Point:

FrameBuffer Objects are not Buffer Objects; they contain no data stores; they only manage state for attachment points and provide an interface for validation and binding semantics.

This is why they cannot be shared, the same way that Vertex Array Objects cannot be shared, but their constituent Vertex Buffer Objects can.

The takeaway message here is:

  • If it does not store data, it is generally not a sharable resource.

The solution you will have to pursue will involve using the renderbuffers and textures that are attached to your FBO. Provided you do not do anything crazy like try and render in both contexts at once, sharing these resources should not present too much trouble. It could get really ugly if you started trying to read from the renderbuffer or texture while the other context is drawing into it, so do not do this :P


Due to the following language in the OpenGL specification, you will probably have to detach the texture before using it in your other context:

OpenGL 3.2 (Core Profile) - 4.4.3 Feedback Loops Between Textures and the Framebuffer - pp. 230:

A feedback loop may exist when a texture object is used as both the source and destination of a GL operation. When a feedback loop exists, undefined behavior results. This section describes rendering feedback loops (see section 3.8.9) and texture copying feedback loops (see section 3.8.2) in more detail.

To put this bluntly, your white textures could be the result of feedback loops (OpenGL did not give this situation a name in versions of the spec. prior to 3.1, so proper discussion of "feedback loops" will be found in 3.1+ literature only). Because this invokes undefined behavior it will behave differently between vendors. Detaching will eliminate the source of undefined behavior and you should be good to go.

Upvotes: 2

Related Questions