Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render to an FBO on a shared context?

I have an application where I need to do the following:

  1. load a texture from disk into a GL texture
  2. run an image filter on it (render it onto another texture via an FBO)
  3. display the resulting texture on screen

I've got that much working.

Next, I'd like to be able to move step #2 to a separate shared GL context.

On initialization, I create a shared context:

rootContext = CGLGetCurrentContext();
CGLPixelFormatObj pf = CGLGetPixelFormat(rootContext);
CGLCreateContext(pf, rootContext, &childContext);

...then make it current and set up the framebuffer on it...

CGLSetCurrentContext(childContext);
glGenTextures(1, &childTexture);
glBindTexture(GL_TEXTURE_2D, childTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glGenFramebuffers(1, &childFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, childFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, childTexture, 0);

Then, when it's time to render each frame, I make childContext current and render to it:

CGLSetCurrentContext(childContext);
glBindFramebuffer(GL_FRAMEBUFFER, childFramebuffer);
glUseProgram(childProgram);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, inputTexture);
glUniform1i(childTextureUniform, 0);

glBindBuffer(GL_ARRAY_BUFFER, childQuadPositionBuffer);
glVertexAttribPointer(childPositionAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*2, (void*)0);
glEnableVertexAttribArray(childPositionAttribute);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, childQuadElementBuffer);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void*)0);
glDisableVertexAttribArray(childPositionAttribute);

glUseProgram(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

...then I make rootContext current and render the FBO's texture to screen:

CGLSetCurrentContext(rootContext);
glUseProgram(rootProgram);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, childTexture);  // This texture was created and populated on childContext.
glUniform1i(rootTextureUniform, 0);

glBindBuffer(GL_ARRAY_BUFFER, rootQuadPositionBuffer);
glVertexAttribPointer(rootPositionAttribute, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*2, (void*)0);
glEnableVertexAttribArray(rootPositionAttribute);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rootQuadElementBuffer);
glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void*)0);
glDisableVertexAttribArray(rootPositionAttribute);

glUseProgram(0);

This works perfectly if I comment out the CGLSetCurrentContext(childContext); calls.

But if I switch to the shared context to render to the FBO, I see glitchy garbage rendered onscreen, as though nothing ever gets rendered onto childTexture:

glitchy garbage rendered onscreen when using shared contexts

...which is really cool, but I'm going for a more of a realist aesthetic here.

Any ideas about how I can get this working when using shared contexts?


I've already tried

  1. Checking for CGLErrors when creating the shared context — there are no errors; it's created successfully.
  2. Checking glGetError() — there are no errors.
  3. Checking glCheckFramebufferStatus() — it's GL_FRAMEBUFFER_COMPLETE.
  4. Calling glBindTexture(GL_TEXTURE_2D, 0); after each glDisableVertexAttribArray() call, as suggested by @datenwolf. No change.

Complete source code

I created 2 simple test apps which both exhibit the same problem:

  • http://softpixel.com/~smokris/FBOTest-Cocoa.zip — using Cocoa to set up a GL window
  • http://softpixel.com/~smokris/FBOTest-Qt.zip — using Qt to set up a GL window
like image 379
smokris Avatar asked May 28 '13 00:05

smokris


2 Answers

Framebuffer Objects (FBOs) can not be shared between contexts. Only data carrying objects (textures, buffer objects, etc) are shared. Abstract objects, i.e. objects that just tie up other objects and manage state (framebuffer objects, vertex array objects) are not.

So binding the FBO you created in the other context doesn't work. BUT you can create a FBO in your secondary context and bind the texture object from the primary context as color attachment just fine.

Update

Another problem may arise if the FBO target texture is bound in the primary contexts as texturing source, while it should also be used as rendering destination. I'm actually not firm on what the OpenGL specification says about this situation^1 (I'd have to dig into the spec for probably half an our or so), but I guess the driver might decide that this is a situation where the texture can not be targeted by the FBO. So I suggest you use a mutex or semaphore to synchronize the binding/unbinding of the texture in the primary thread with the binding of the FBO in the secondary thread.


1: EDIT the OpenGL spec says that texture layers that can source fragments in rasterization operations can not be used as FBO render targets at the same time; but the wording of the OpenGL specifications themseld does not explicitly consider shared contexts. But I'm pretty sure, that being bound by a shared context with the possibility of being sourced at any time, satisfies this constraint. In my own projects, were I in such a situation to implement something like this, I automatically implemented double or triple buffering schemes with rendering to buffer (n+1)%M while sourcing data from buffer n%M.

like image 99
datenwolf Avatar answered Oct 31 '22 08:10

datenwolf


I got it working, with 2 changes to the sample code above:

  • call glViewport() on childContext once after creating it
  • call glFlushRenderAPPLE(); after rendering to the FBO each frame
like image 3
smokris Avatar answered Oct 31 '22 08:10

smokris