在 iOS 设备上将多个渲染目标 (MRT) 与多重采样组合失败,而不是在模拟器上

Posted

技术标签:

【中文标题】在 iOS 设备上将多个渲染目标 (MRT) 与多重采样组合失败,而不是在模拟器上【英文标题】:Combining Multiple Render Targets (MRT) with Multisampling fails on iOS devices, not on the simulator 【发布时间】:2016-08-18 11:48:48 【问题描述】:

我正在尝试在 ios (>= 8.0) 上编写一个使用多个渲染目标 (MRT) 的 OpenGLES-3.0 Swift 应用程序。为了获得适当的抗锯齿效果,我启用了多重采样。

详细来说,我的渲染架构是这样的:

显示帧缓冲区附加了一个渲染缓冲区:

显示渲染缓冲区:由 iOS 通过 EAGLContext.renderbufferStorage() 控制,附加为 GL_COLOR_ATTACHMENT0

示例帧缓冲区附加了两个渲染缓冲区:

颜色渲染缓冲区 I:GL_RGBA8,多重采样,附加为 GL_COLOR_ATTACHMENT0 颜色渲染缓冲区 II:GL_RGBA8,多重采样,附加为 GL_COLOR_ATTACHMENT1

每当我的图层更改其边界时,我都会调整所有渲染缓冲区的大小,就像 Apple 在 GLPaint 示例中所做的那样。

我为您创建了一个最小的示例。渲染本身如下所示:

//Set the GL context, bind the sample framebuffer and specify the viewport:
EAGLContext.setCurrentContext(context)
glBindFramebuffer(GLenum(GL_FRAMEBUFFER), self.sampleFramebuffer)
glViewport(0, 0, self.layerWidth, self.layerHeight)

//Clear both render targets:
glClearBufferfv(GLenum(GL_COLOR), 0, self.colorRenderbufferIClearColor)
glClearBufferfv(GLenum(GL_COLOR), 1, self.colorRenderbufferIIClearColor)

//Specify the vertex attribute (only position, 6 floats for a triangle):
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(2 * sizeof(GLfloat)), nil)

//Use the shader program and render a single triangle:
glUseProgram(self.program)
glDrawArrays(GLenum(GL_TRIANGLES), 0, 3)

//Prepare both framebuffers as source and destination to do multisampling:
glBindFramebuffer(GLenum(GL_READ_FRAMEBUFFER), self.sampleFramebuffer)
glBindFramebuffer(GLenum(GL_DRAW_FRAMEBUFFER), self.displayFramebuffer)

//Specify from which of the attachments we do the multisampling.
//This is GL_COLOR_ATTACHMENT0 or GL_COLOR_ATTACHMENT1.
glReadBuffer(self.blitAttachment)

//Transfer data between framebuffers and do multisampling:
glBlitFramebuffer(0, 0, self.layerWidth, self.layerHeight, 0, 0, self.layerWidth, self.layerHeight, GLbitfield(GL_COLOR_BUFFER_BIT), GLenum(GL_LINEAR))

//Invalidate the sample framebuffer for this pass:
glInvalidateFramebuffer(GLenum(GL_READ_FRAMEBUFFER), 2, [GLenum(GL_COLOR_ATTACHMENT0), GLenum(GL_COLOR_ATTACHMENT1)])

//Bind the display renderbuffer and present it:
glBindRenderbuffer(GLenum(GL_RENDERBUFFER), self.displayRenderbuffer)
self.eaglContext.presentRenderbuffer(Int(GL_RENDERBUFFER))

现在问题来了:我的示例项目将红色背景上的蓝色三角形绘制到第一个渲染目标(颜色渲染缓冲区 I)中,并将绿色背景上的紫色三角形绘制到第二个渲染目标(颜色渲染缓冲区 II)中。通过在代码中设置 blitAttachment,您可以选择将两个附件中的哪一个解析到显示帧缓冲区中。

在 iOS 模拟器上一切正常(所有设备,所有 iOS 版本)。 目前我只能使用 iPad Air(型号 A1475,iOS 9.3.4)。但是在设备上,存在问题: 如果我禁用多重采样(glRenderbufferStorageMultisample() 中的级别 = 0),一切正常。 如果启用多重采样(级别 = 4),我只能从 GL_COLOR_ATTACHMENT0(即颜色渲染缓冲区 I)进行 blit。 从 GL_COLOR_ATTACHMENT1 进行 Blitting 会产生相同的结果(红色上的蓝色三角形),但应该会导致另一个结果(绿色上的紫色三角形)。

您可以使用我附加的sample code (DropBox) 重现该问题。 所以有两个问题:

    有人可以确认这在模拟器上有效,但在真实设备上无效? 有人知道我的代码中的错误吗?或者这是一个已知的错误?

提前致谢!

【问题讨论】:

【参考方案1】:

此 API 中似乎有一些奇怪的行为。您链接的代码确实可以在模拟器上运行,但模拟器与实际设备有很大不同,因此我建议您不要将其用作参考。

所以似乎发生的事情是渲染缓冲区被丢弃得太快了。为什么以及如何发生这种情况我不知道。您对缓冲区进行 blit,然后使它们无效,因此只需删除缓冲区无效即可解决问题。但不建议删除缓冲区失效,因此请确保所有任务都已由 GPU 执行,然后再使它们失效。这意味着只需调用flush

在致电glInvalidateFramebuffer(GLenum(GL_READ_FRAMEBUFFER), 2, [GLenum(GL_COLOR_ATTACHMENT0), GLenum(GL_COLOR_ATTACHMENT1)]) 之前,只需致电glFlush()

//Resolve from source to destination while applying multisampling:
glBlitFramebuffer(0, 0, self.layerWidth, self.layerHeight, 0, 0, self.layerWidth, self.layerHeight, GLbitfield(GL_COLOR_BUFFER_BIT), GLenum(GL_LINEAR))
OpenGLESView.checkError(dbgDomain, andDbgText: "Failed to blit between framebuffers")

glFlush()

//Invalidate the whole sample framebuffer:
glInvalidateFramebuffer(GLenum(GL_READ_FRAMEBUFFER), 2, [GLenum(GL_COLOR_ATTACHMENT0), GLenum(GL_COLOR_ATTACHMENT1)])
OpenGLESView.checkError(dbgDomain, andDbgText: "Failed to invalidate sample framebuffer")

【讨论】:

确实是非常奇怪的行为。但是那个冲洗似乎已经解决了这个问题。非常感谢,这拯救了我的一天:-)

以上是关于在 iOS 设备上将多个渲染目标 (MRT) 与多重采样组合失败,而不是在模拟器上的主要内容,如果未能解决你的问题,请参考以下文章

将两个图像附加到 fbo 以进行 mrt 渲染

OpenGL中的多个渲染目标与Cg

双源混合与多渲染目标链接错误

OpenGL FBO 与 MRT 写入后台缓冲区

如何在macOS上将磁盘分为多个(非Apple)磁盘分区

UnityShader:MRT多重渲染