在 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) 与多重采样组合失败,而不是在模拟器上的主要内容,如果未能解决你的问题,请参考以下文章