在 OpenGL ES iOS 中绘制多个纹理时帧率下降
Posted
技术标签:
【中文标题】在 OpenGL ES iOS 中绘制多个纹理时帧率下降【英文标题】:Framerate dropping when drawing multiple textures in OpenGL ES iOS 【发布时间】:2015-04-13 17:38:03 【问题描述】:基本上我正在做的是制作一个简单的手指绘图应用程序。我有一个单独的类,它接受输入的接触点,并完成所有有趣的工作,将这些接触点变成贝塞尔曲线,计算顶点等等。这一切都很好。
我正在使用的唯一有趣的限制是我需要笔画相互融合,而不是与它们自身融合。想象一下,有一条涂鸦线与自身相交并具有 50% 的不透明度。在线条与自身相交的地方,不应该有可见的混合(它应该看起来都是相同的颜色)。但是,这条线应该与它下面的其余图形混合。
为了实现这一点,我使用了两种纹理。背面纹理和划痕纹理。当线条正在积极更新时(在笔划过程中),我禁用混合,在划痕纹理上绘制顶点,然后启用混合,并将背面纹理和划痕纹理绘制到我的帧缓冲区中。描边完成后,我将划痕纹理绘制到背面纹理中,我们准备开始下一个描边。
这一切在较新的设备上运行得非常顺利,但在较旧的设备上,帧速率会受到严重影响。从一些测试来看,最大的性能影响似乎是将纹理绘制到帧缓冲区,因为它们是相对较大的纹理(由于 iPhone 的视网膜分辨率)。 有人对解决此问题的一些策略有任何提示吗?我很乐意提供更多细节或代码,但我不知道从哪里开始。
我正在使用 OpenGL ES 2.0,目标是 ios 7.0,但在 iPhone 4S 上进行测试
以下是我用来绘制帧缓冲区的代码:
- (void)drawRect:(CGRect)rect
[self drawRect:rect
ofTexture:_backTex
withOpacity:1.0];
if (_activeSpriteStroke)
[self drawStroke:_activeSpriteStroke
intoFrameBuffer:0];
那些依赖于以下几种方法:
- (void)drawRect:(CGRect)rect
ofTexture:(GLuint)tex
withOpacity:(CGFloat)opacity
_texShader.color = GLKVector4Make(1.0, 1.0, 1.0, opacity);
[_texShader prepareToDraw];
glBindTexture(GL_TEXTURE_2D, tex);
glBindVertexArrayOES(_texVertexVAO);
glBindBuffer(GL_ARRAY_BUFFER, _texVertexVBO);
[self bufferTexCoordsForRect:rect];
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArrayOES(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, tex);
- (void)drawStroke:(AHSpriteStroke *)stroke
intoFrameBuffer:(GLuint)frameBuffer
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
[self renderStroke:stroke
ontoTexture:_scratchTex
inFrameBuffer:_scratchFrameBuffer];
if (frameBuffer == 0)
[self bindDrawable];
else
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
[self setScissorRect:_activeSpriteStroke.boundingRect];
glEnable(GL_SCISSOR_TEST);
[self drawRect:self.bounds
ofTexture:_scratchTex
withOpacity:stroke.lineOpacity];
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
- (void)renderStroke:(AHSpriteStroke *)stroke
ontoTexture:(GLuint)tex
inFrameBuffer:(GLuint)framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, _msFrameBuffer);
glBindTexture(GL_TEXTURE_2D, tex);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
[stroke render];
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, framebuffer);
glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, _msFrameBuffer);
glResolveMultisampleFramebufferAPPLE();
const GLenum discards[] = GL_COLOR_ATTACHMENT0 ;
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, discards);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
还有几个帮助方法只是为了完整性,以便您可以遵循它:
- (void)bufferTexCoordsForRect:(CGRect)rect
AHTextureMap textureMaps[4] =
[self textureMapForPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect))
inRect:self.bounds],
[self textureMapForPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect))
inRect:self.bounds]
;
glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(AHTextureMap), textureMaps, GL_DYNAMIC_DRAW);
- (AHTextureMap)textureMapForPoint:(CGPoint)point
inRect:(CGRect)outerRect
CGPoint pt = CGPointApplyAffineTransform(point, CGAffineTransformMakeScale(self.contentScaleFactor, self.contentScaleFactor));
return (AHTextureMap) pt.x, pt.y , point.x / outerRect.size.width, 1.0 - (point.y / outerRect.size.height) ;
【问题讨论】:
> “并将背面纹理和划痕纹理绘制到我的帧缓冲区中”......你是如何完成这部分的? 我在上面添加了一些代码,为我如何绘制这些帧缓冲区提供更多背景信息。随意看看,让我知道你的想法或者如果你有任何问题!任何帮助将不胜感激! 【参考方案1】:据我了解,您在单独的绘图调用中绘制每个四边形。 如果您的笔画包含许多四边形(从贝塞尔曲线采样),您的代码将在每帧进行多次绘制调用。
在旧 iOS 设备上的 OpenGL ES 2 中有许多绘图调用可能会在 CPU 上产生瓶颈。 原因是 OpenGL ES 2 中的绘图调用可能会在驱动程序中产生大量开销。 驱动程序尝试将您进行的绘图调用组织成 GPU 可以消化的东西,并使用 CPU 进行这种组织。
如果您打算绘制许多四边形来模拟笔触,您应该更新一个顶点缓冲区以包含许多四边形,然后使用一次绘制调用来绘制它,而不是对每个四边形进行一次绘制调用。
您可以使用 Time Profiler 仪器验证您的瓶颈是否在 CPU 中。 然后,您可以检查 CPU 是否将大部分时间花在 OpenGL 绘制调用方法上,或者更确切地说是花在您自己的函数上。
如果 CPU 将大部分时间花在 OpenGL 绘制调用方法上,很可能是因为您每帧进行了太多绘制调用。
【讨论】:
感谢您的深思熟虑的回复,但实际情况并非如此。上面的代码都没有显示任何实际的绘图代码,它接受输入的触摸点并将其转换为贝塞尔曲线,然后是贝塞尔曲线的顶点。我已经使用计时器工具和 OpenGL 工具对所有这些代码进行了相当广泛的测试,这似乎不是瓶颈。瓶颈肯定在纹理渲染的某个地方。 “纹理渲染”?你的意思是渲染目标?如果您的渲染低于目标 FPS,xcode OpenGL 帧分析器通常会告诉您什么是瓶颈。尝试在滑动手指时抓取 OpenGL 快照,看看是否低于渲染目标? 您知道您可以通过点击 FPS 仪表来查看您使用了多少 CPU/GPU(甚至您使用了多少 tiler)? 再次感谢!是的,我确实知道如何使用 CPU/GPU 仪表进行实时调试,而且我经常使用它。你能告诉我更多关于 Xcode 帧分析器告诉我瓶颈在哪里吗?我从来没有见过,只有单帧的快照。至于纹理渲染部分,我的意思是:一般过程是 1. 将笔划绘制到纹理(通过绑定纹理支持的 FBO) 2. 启用混合 3. 将纹理绘制到主 FBO(通过绑定到该 FBO)瓶颈似乎是第 3 步。这有意义吗?我在这方面相对较新,所以如果我的词汇有误,请纠正! 是的,旧设备不喜欢使用渲染目标(渲染到的纹理)作为另一个着色器的资源。这就是我认为你正在做的事情。一种解决方法可能是双缓冲。因此,步骤 3 从前一帧中获取步骤 2 的缓冲区。我从未真正见证过这种方式的性能提升,但它可能会奏效。当您选择 FPS 栏时,您的分析器会告诉您各种警告和错误。阅读它所说的一切。以上是关于在 OpenGL ES iOS 中绘制多个纹理时帧率下降的主要内容,如果未能解决你的问题,请参考以下文章
OpenGL ES 2.0 Framebuffer 渲染到纹理 iOS:没有显示