iphone - iOS上的核心图形性能

标签 iphone ios performance core-graphics quartz-2d

概括

我正在为iOS开发一款非常简单的2D塔防游戏。

到目前为止,我一直在专门使用Core Graphics来处理渲染。该应用程序中根本没有图像文件(尚未)。在进行相对简单的绘制时,我一直遇到一些重大的性能问题,并且我一直在寻找有关如何解决此问题的想法,而不是转向OpenGL。

游戏设定

在较高的层次上,我有一个Board类,它是UIView的子类,用来表示游戏板。游戏中的所有其他对象(塔,小兵,武器,爆炸等)也是UIView的子类,并且在创建时作为 subview 添加到Board中。

我将游戏状态与对象内的 View 属性完全分开,并且每个对象的状态在主游戏循环中更新(由NSTimer以60-240 Hz的频率触发,具体取决于游戏速度设置)。该游戏完全可玩,无需绘制,更新或设置 View 动画。

我使用CADisplayLink计时器以 native 刷新率(60 Hz)处理 View 更新,该计时器在需要根据游戏状态变化更新其 View 属性的棋盘对象上调用setNeedsDisplay。板上的所有对象都覆盖drawRect:,以在其框架中绘制一些非常简单的2D形状。因此,例如,对某件武器进行动画处理时,它将根据该武器的新状态重新绘制自身。

性能问题

在iPhone 5上进行测试,板上总共有大约2打游戏对象,帧速率明显低于60 FPS(目标帧速率),通常降至10-20 FPS范围内。随着屏幕上的更多 Action ,它从这里下坡。在iPhone 4上,情况甚至更糟。

通过使用Instruments,我已经确定只有5%的CPU时间用于实际更新游戏状态-绝大部分时间都用于渲染。具体来说,CGContextDrawPath函数(据我所知是矢量路径的栅格化是在哪里完成的)占用了大量CPU时间。有关更多详细信息,请参见底部的Instruments屏幕截图。

从对StackOverflow和其他站点的一些研究来看,似乎Core Graphics不能完全满足我的需求。显然,抚摸矢量路径非常昂贵(尤其是在绘制不透明且alpha值<1.0的东西时)。我几乎可以确定OpenGL可以解决我的问题,但是它的水平很低,我对使用它并不感到非常兴奋-看来我在这里所做的事情似乎没有必要。

问题

我是否应该进行一些优化以尝试使Core Graphics达到60 FPS的平滑度?

一些想法...

有人建议我考虑将所有对象绘制到单个CALayer上,而不是将每个对象放在自己的CALayer上,但是我不认为这会根据Instruments的显示而有所帮助。

就我个人而言,我有一个理论,即使用CGAffineTransforms进行动画处理(即在drawRect:中绘制对象的形状一次,然后进行变换以在后续帧中移动/旋转/调整其图层的大小)将解决我的问题,因为这些基于直接在OpenGL上。但是我认为,做到这一点不会比完全使用OpenGL更容易。

样例代码

为了让您感觉到我正在做的绘图水平,以下是我的一个武器对象(从塔发射的“光束”)的drawRect:实现的示例。

注意:该光束可以“重新定向”,并横穿整个电路板,因此,为简单起见,其框架与电路板的尺寸相同。但是,板上的大多数其他对象的框架都设置为可能的最小外接矩形。

- (void)drawRect:(CGRect)rect
{
    CGContextRef c = UIGraphicsGetCurrentContext();

    // Draw beam
    CGContextSetStrokeColorWithColor(c, [UIColor greenColor].CGColor);
    CGContextSetLineWidth(c, self.width);
    CGContextMoveToPoint(c, self.origin.x, self.origin.y);
    CGPoint vector = [TDBoard vectorFromPoint:self.origin toPoint:self.destination];
    double magnitude = sqrt(pow(self.board.frame.size.width, 2) + pow(self.board.frame.size.height, 2));
    CGContextAddLineToPoint(c, self.origin.x+magnitude*vector.x, self.origin.y+magnitude*vector.y);
    CGContextStrokePath(c);

}

仪器运行

让游戏运行一会儿后,再来看一下Instruments:

TDGreenBeam类具有上面“示例代码”部分中所示的drawRect:实现。

Full Size Screenshot

最佳答案

核心图形工作由CPU执行。然后将结果推送到GPU。当您调用setNeedsDisplay时,表明需要重新进行绘图工作。

假设您的许多对象保持一致的形状并且只是移动或旋转,则只需在父 View 上调用setNeedsLayout,然后将该 View 的layoutSubviews中的最新对象位置推送到center属性即可。仅调整位置就不会导致需要重画任何东西。合成器将只要求GPU在不同的位置重现它已经具有的图形。

对于游戏而言,更通用的解决方案可能是忽略centerboundsframe,而不是进行初始设置。只需将您想要的仿射变换推送到transform,可能是使用these helpers的某种组合创建的。这样一来,您就可以在无需CPU干预的情况下灵活地重新定位,旋转和缩放对象,而这一切都将由GPU来完成。

如果您想要更多的控制权,则每个 View 都有一个带有自己的CALayeraffineTransform,但它们也有一个与子图层的转换结合在一起的sublayerTransform。因此,如果您对3d如此感兴趣,那么最简单的方法是将合适的透视图矩阵作为sublayerTransform加载到超层上,然后将合适的3d变换推送到子层或 subview 。

这种方法有一个明显的缺点,即如果您绘制一次然后按比例放大,您将能够看到像素。您可以预先调整图层的contentsScale尝试对此进行改善,但是否则,您只会看到允许GPU进行合成的自然结果。如果要在线性过滤和最接近过滤之间切换,则图层上有一个magnificationFilter属性。线性是默认设置。

关于iphone - iOS上的核心图形性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13277031/

相关文章:

iphone - 从应用程序复制图像并将其粘贴到 iPhone 中的 native SMS 应用程序中

iphone - Table View的带有图片的标题在iPhone 5中不起作用

ios - 类型 'Reactive<UITextField>' 的值没有成员 'text'

Android SDK - 获取联系人非常慢

iphone - 如何使用 Core Data (iPhone) 存储 CLLocation?

ios - 如何在 xcode 7.1 中减小 ios APP 的大小

ios - 将数据从 iOS 应用程序安全地发送到服务器上的 php 脚本

ios - 使用 drawInRect 对齐文本 :withAttributes:

performance - 初始化或预分配的最佳实践 - MATLAB

python - Python 中无开销的有意义的 IO 错误消息