How to render to an FBO on a shared context?
Asked Answered
C

2

5

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:

Contingence answered 28/5, 2013 at 0:41 Comment(0)
C
3

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
Contingence answered 28/5, 2013 at 15:48 Comment(1)
Hi @smokris, if you get a chance, would you mind posting the working code? I am trying to work these 2 into the above project & get it working but not having much success. Thanks!Zo
S
9

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.

Subdue answered 28/5, 2013 at 0:47 Comment(4)
I'm not trying to share the FBO; I'm just trying to share the textures. I'm creating the FBO on the secondary context — in the second code block, where I create the FBO, the first line is CGLSetCurrentContext(childContext);.Contingence
@smokris: Oops, I missed that :PSubdue
Oh, interesting idea. I tried adding glBindTexture(GL_TEXTURE_2D, 0); just after each glDisableVertexAttribArray() call, but that doesn't seem to make any difference. For now, I'm just switching between the two contexts on a single thread, so I'm not doing any locking, but I'll keep that in mind for when I move the secondary context to another thread (after I get it working...).Contingence
Yes, FBO cannot be shared between context!Zagazig
C
3

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
Contingence answered 28/5, 2013 at 15:48 Comment(1)
Hi @smokris, if you get a chance, would you mind posting the working code? I am trying to work these 2 into the above project & get it working but not having much success. Thanks!Zo

© 2022 - 2024 — McMap. All rights reserved.