I have an application where I need to do the following:
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
:
...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?
CGLError
s when creating the shared context — there are no errors; it's created successfully.glGetError()
— there are no errors.glCheckFramebufferStatus()
— it's GL_FRAMEBUFFER_COMPLETE
.glBindTexture(GL_TEXTURE_2D, 0);
after each glDisableVertexAttribArray()
call, as suggested by @datenwolf. No change.I created 2 simple test apps which both exhibit the same problem:
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.
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
.
I got it working, with 2 changes to the sample code above:
glViewport()
on childContext once after creating itglFlushRenderAPPLE();
after rendering to the FBO each frameIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With