5

我有一个应用程序需要执行以下操作:

  1. 将纹理从磁盘加载到 GL 纹理中
  2. 在其上运行图像过滤器(通过 FBO 将其渲染到另一个纹理上)
  3. 在屏幕上显示生成的纹理

我有那么多工作。

接下来,我希望能够将第 2 步移至单独的共享 GL 上下文。

在初始化时,我创建了一个共享上下文:

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

...然后使其成为最新并在其上设置帧缓冲区...

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);

然后,当需要渲染每一帧时,我childContext将其设为当前并渲染:

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);

...然后我制作rootContext当前并将 FBO 的纹理渲染到屏幕:

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);

如果我注释掉CGLSetCurrentContext(childContext);电话,这将非常有效。

但是,如果我切换到共享上下文以渲染到 FBO,我会在屏幕上看到出现故障的垃圾,好像什么都没有渲染到childTexture

使用共享上下文时在屏幕上呈现的故障垃圾

...这真的很酷,但我会在这里寻求一种更现实主义的美学。

关于在使用共享上下文时如何使其工作的任何想法?


我已经试过了

  1. 创建共享上下文时检查CGLErrors — 没有错误;它已成功创建。
  2. 检查glGetError()——没有错误。
  3. 检查glCheckFramebufferStatus()- 它是GL_FRAMEBUFFER_COMPLETE.
  4. 按照@datenwolf 的建议,glBindTexture(GL_TEXTURE_2D, 0);在每次通话后进行通话。glDisableVertexAttribArray()不用找了。

完整的源代码

我创建了 2 个简单的测试应用程序,它们都表现出相同的问题:

4

2 回答 2

9

帧缓冲区对象 (FBO) 不能在上下文之间共享。只有携带数据的对象(纹理、缓冲区对象等)是共享的。抽象对象,即仅绑定其他对象并管理状态的对象(帧缓冲区对象、顶点数组对象)不是。

因此绑定您在其他上下文中创建的 FBO 不起作用。但是您可以在辅助上下文中创建一个 FBO,并将主要上下文中的纹理对象绑定为颜色附件就好了。

更新

如果 FBO 目标纹理在主要上下文中绑定为纹理源,则可能会出现另一个问题,同时它也应该用作渲染目标。实际上,对于 OpenGL 规范对这种情况的说法,我并不确定^1(我可能不得不深入研究规范,大概是我们的一半左右),但我猜驱动程序可能会认为这是一种情况,即FBO 不能以纹理为目标。所以我建议你使用互斥锁或信号量来同步主线程中纹理的绑定/解除绑定与辅助线程中FBO的绑定。


1编辑OpenGL规范说,可以在光栅化操作中获取片段的纹理层不能同时用作FBO渲染目标;但是他们自己的 OpenGL 规范的措辞并没有明确考虑共享上下文。但我很确定,受共享上下文的约束并有可能随时获取资源,满足了这个约束。在我自己的项目中,如果我在这种情况下实现这样的东西,我会自动实现双缓冲或三缓冲方案,并(n+1)%M在从 buffer 获取数据时渲染到缓冲区n%M

于 2013-05-28T00:47:38.540 回答
3

我得到了它,对上面的示例代码进行了 2 处更改:

  • glViewport()在创建 childContext 后调用一次
  • glFlushRenderAPPLE();每帧渲染到 FBO 后调用
于 2013-05-28T15:48:53.973 回答