经常绘制CGPath时的性能

我正在开发一个iOS应用程序,可将数据可视化为线图。 该图形在全屏自定义UIView绘制为CGPath ,最多包含320个数据点。 数据经常更新,图表需要相应地重新绘制 – 刷新率为10 /秒会很好。

到目前为止这么容易。 但是,我的方法似乎耗费了大量的CPU时间。 每秒10次刷新320个段的图形会导致iPhone 4S上的进程占用45%的CPU负载。

也许我低估了引擎盖下的图形工作,但对我而言,CPU负载对于该任务似乎很重要。

下面是我的drawRect()函数,每次准备好一组新数据时都会调用它。 N保持点的数量,点是带有绘制坐标的CGPoint*向量。

 - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); // set attributes CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor); CGContextSetLineWidth(context, 1.f); // create path CGMutablePathRef path = CGPathCreateMutable(); CGPathAddLines(path, NULL, points, N+1); // stroke path CGContextAddPath(context, path); CGContextStrokePath(context); // clean up CGPathRelease(path); } 

我尝试将路径渲染到离线CGContext,然后再按照此处的建议将其添加到当前层,但没有任何正面结果。 我也摆弄了直接向CALayer绘图的方法,但这也没有任何区别。

有关如何提高此任务性能的任何建议吗? 或者渲染对于我意识到的CPU来说更加有用吗? OpenGL会有任何意义/差异吗?

谢谢/安迪

更新:我也尝试使用UIBezierPath而不是CGPath 。 这篇文章给出了一个很好的解释,为什么这没有帮助。 调整CGContextSetMiterLimit等。 也没有带来很大的解脱。

更新#2:我最终切换到OpenGL。 这是一个陡峭而令人沮丧的学习曲线,但性能提升令人难以置信。 但是,CoreGraphics的抗锯齿算法比OpenGL中的4x多重采样可以实现更好的工作。

这篇文章给出了一个很好的解释,为什么这没有帮助。

它还解释了为什么你的drawRect:方法很慢。

您每次绘制时都在创建一个CGPath对象。 你不需要这样做; 每次修改点集时,只需要创建一个新的CGPath对象。 将CGPath的创建移动到仅在点集更改时调用的新方法,并在对该方法的调用之间保留CGPath对象。 有drawRect:只需检索它。

你已经发现渲染是你正在做的最昂贵的事情,这很好:你不能让渲染速度更快,是吗? 实际上, drawRect:理想情况下应该只做渲染,所以你的目标应该是尽可能地将渲染所花费的时间推到100% – 这意味着尽可能地将所有其他内容移出绘图代码。

根据您创建路径的方式,绘制300个单独路径可能比一个路径300点快。 这样做的原因是绘图算法通常会寻找重叠线以及如何使交叉点看起来“完美” – 当你可能只希望线条不透明地相互重叠时。 许多重叠和交叉算法的复杂度为N ** 2左右,因此绘制速度与一条路径中的点数的平方成比例。

这取决于您使用的确切选项(其中一些是默认选项)。 你需要尝试一下。

您是否尝试过使用UIBezierPath? UIBezierPath在引擎盖下使用了CGPath,但是看看性能是否因某些微妙原因而有所不同是很有趣的。 来自Apple的文档 :

要在iOS中创建路径,建议您使用UIBezierPath而不是CGPath函数,除非您需要一些仅Core Graphics提供的function,例如向路径添加省略号。 有关在UIKit中创建和渲染路径的更多信息,请参阅“使用Bezier路径绘制形状”。

我还会尝试在CGContext上设置不同的属性,特别是使用CGContextSetLineJoin()设置不同的行连接样式,看看是否有任何区别。

您是否使用仪器中的Time Profiler仪器分析了代码? 这可能是找到性能瓶颈实际发生位置的最佳方法,即使瓶颈位于系统框架内部。

我不是这方面的专家,但我首先要怀疑的是,它可能需要时间来更新“点”而不是自我渲染。 在这种情况下,您可以简单地停止更新点并重复渲染相同的路径,并查看它是否需要几乎相同的CPU时间。 如果没有,您可以提高注重更新算法的性能。

如果它确实是渲染的问题,我认为OpenGL肯定会提高性能,因为它理论上会同时渲染所有320行。

我在我的项目中使用Metal完成了这个。 使用Metal显然有额外的复杂性,因此根据您的性能要求,它可能适合也可能不适合。

使用Metal,所有工作都在GPU中完成,因此CPU使用率接近于零。 在大多数iOS设备上,您可以平滑地渲染数百或数千条曲线,速度为60 FPS。

这是我写的一些示例代码,它可能为某人提供了一个很好的起点: https : //github.com/eldade/ios_metal_bezier_renderer