我使用OpenGL ES在iPhone 4S上为每个帧绘制2560个非常纤细的多边形。问题是我的帧速率大约是30,这对我来说不够顺畅。我认为它应该比那更快。
是吗?
请帮我找出可以改进的地方。
UPDATE :我在主线程上进行渲染。是否有关于执行渲染操作的线程的建议?
有点背景: 我正在尝试在iPhone视图坐标中进行大小为320x200的平滑滚动(目标为60 FPS)波形,因此在视网膜显示屏上为640x400像素。 我的测试设备是iPhone 4S。使用iOS 6和6.1,我可以通过普通的UIKit绘图操作轻松实现这一点。但是,由于我将设备更新到iOS 7,速度要慢得多,所以我决定使用OpenGL ES,因为我多次阅读它可以更快地进行2D绘图。
我实现了使用OpenGL ES 2.0绘制波形,但现在它在设备上的速度比UIKit快一点。和UIKit一样,速度很大程度上取决于绘制的像素数量,这让我想知道发生了什么。
波形由条形/矩形组成,每个条形宽度恰好为1个像素。我为每个像素列绘制两个条形,每个条形由两个多边形组成,这意味着我为每个框架绘制1280个条形或2560个多边形。多边形非常纤细。它们中的每一个最多为1个像素宽。我认为使用OpenGL ES以60FPS绘制应该没问题。
我画了一个这样的栏:
- (void) glFillRect: (Float32)x0 : (Float32)y0 : (Float32)x1 : (Float32)y1 {
glEnableVertexAttribArray(GLKVertexAttribPosition);
GLfloat vertices[8];
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, 0, vertices);
GLfloat* vp = vertices;
*vp++ = x0; *vp++ = y0;
*vp++ = x1; *vp++ = y0;
*vp++ = x0; *vp++ = y1;
*vp++ = x1; *vp++ = y1;
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(GLKVertexAttribPosition);
}
调用上述方法的代码如下。 _maxDrawing
和_ avgDrawing
是我的效果,在应用启动时就像这样组成:
_maxDrawing = [[GLKBaseEffect alloc] init];
_maxDrawing.useConstantColor = GL_TRUE;
_maxDrawing.constantColor = GLKVector4Make(0.075f, 0.1f, 0.25f, 1.0f);
我稍后调整投影矩阵,以便我的OpenGL ES绘图坐标与我视图的视图坐标对齐,afaik是2D绘图的标准方法。
[_maxDrawing prepareToDraw];
x_Cu = [self transformViewXToWaveformX:rect.origin.x];
for (Float32 x_Vu = rect.origin.x; x_Vu < viewEndX_Vu; x_Vu += onePixelInViewUnits) {
x_Cu += onePixelInContentUnits;
if (x_Cu < 0 || x_Cu >= waveformEndX_Cu) {
continue;
}
SInt64 frameIdx = (SInt64) x_Cu;
CBWaveformElement element;
element = [self.dataSource getElementContainingFrame:frameIdx];
prevMax = curMax;
curMax = futureMax;
futureMax = element.max;
smoothMax = prevMax * 0.25 + curMax * 0.5 + futureMax * 0.25;
if (smoothMax < curMax)
smoothMax = curMax;
Float32 barHeightHalf = smoothMax * heightScaleHalf;
Float32 barY0 = viewHeightHalf - barHeightHalf;
Float32 barY1 = viewHeightHalf + barHeightHalf;
[self glFillRect: x_Vu : barY0 : x_Vu + onePixelInViewUnits : barY1];
}
[_avgDrawing prepareToDraw];
x_Cu = [self transformViewXToWaveformX:rect.origin.x];
for (Float32 x_Vu = rect.origin.x; x_Vu < viewEndX_Vu; x_Vu += onePixelInViewUnits) {
x_Cu += onePixelInContentUnits;
if (x_Cu < 0 || x_Cu >= waveformEndX_Cu) {
continue;
}
SInt64 frameIdx = (SInt64) x_Cu;
CBWaveformElement element;
element = [self.dataSource getElementContainingFrame:frameIdx];
Float32 barHeightHalf = element.avg * heightScaleHalf;
Float32 barY0 = viewHeightHalf - barHeightHalf;
Float32 barY1 = viewHeightHalf + barHeightHalf;
[self glFillRect: x_Vu : barY0 : x_Vu + onePixelInViewUnits : barY1];
}
当我取出所有OpenGL调用时,一帧的执行持续时间约为1ms,这意味着它理论上可以达到1000 FPS。所有其他时间(约33毫秒)花在绘图上。
答案 0 :(得分:2)
根据丹尼尔的要求,我发布这个作为解决问题的答案。
在上面的代码中,您似乎每个方框都使用glDrawArrays()
来电。这会导致大量的开销带来大量的开销。
更有效的方法是使用包含场景的所有顶点的VBO(可能是动态更新的),或至少包含更大的一组框,并绘制所有这些顶点单个电话。
正如rickster所指出的,iOS 7为实例化添加了一些不错的支持,这也可能是一种帮助。
关于是否在后台线程上渲染,根据我的经验,在后台线程上渲染我的OpenGL ES场景时,我通常会看到显着的性能提升(10-40%,特别是在多核设备上)。使用串行GCD队列,以安全的方式执行此操作也非常容易。