我正在开发一款iOS应用程序,需要实时绘制Bézier曲线以响应用户的输入。起初,我决定尝试使用CoreGraphics,它具有出色的矢量绘图API。然而,我很快就发现性能很痛苦,极其缓慢,在我的视网膜iPad上只有一条曲线,帧速率开始严重下降。 (不可否认,这是一个代码效率低下的快速测试。例如,曲线每帧都重新绘制。但是今天的计算机确实足够快,每隔1/60秒处理一条简单的曲线,对吧?!)
在这个实验之后,我切换到了OpenGL和MonkVG库,我感到非常高兴。我现在可以在没有任何帧率下降的情况下同时渲染HUNDREDS曲线,对保真度的影响很小(对于我的用例)。
更新:
我编写了一个快速测试应用程序,以更准确地衡量性能。下面是我的自定义CALayer子类的代码。
将NUM_PATHS设置为5并将NUM_POINTS设置为15(每个路径5个曲线段),代码在非视网膜模式下以20fps运行,在iPad 3上以视网膜模式运行6fps。探查器将CGContextDrawPath列为96% CPU时间。是的 - 显然,我可以通过限制我的重绘矩形进行优化,但如果我真的需要60fps的全屏矢量动画呢?
OpenGL在早餐时吃这个测试。矢量绘图怎么可能这么慢?
#import "CGTLayer.h"
@implementation CGTLayer
- (id) init
{
self = [super init];
if (self)
{
self.backgroundColor = [[UIColor grayColor] CGColor];
displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(updatePoints:)] retain];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
initialized = false;
previousTime = 0;
frameTimer = 0;
}
return self;
}
- (void) updatePoints:(CADisplayLink*)displayLink
{
for (int i = 0; i < NUM_PATHS; i++)
{
for (int j = 0; j < NUM_POINTS; j++)
{
points[i][j] = CGPointMake(arc4random()%768, arc4random()%1024);
}
}
for (int i = 0; i < NUM_PATHS; i++)
{
if (initialized)
{
CGPathRelease(paths[i]);
}
paths[i] = CGPathCreateMutable();
CGPathMoveToPoint(paths[i], &CGAffineTransformIdentity, points[i][0].x, points[i][0].y);
for (int j = 0; j < NUM_POINTS; j += 3)
{
CGPathAddCurveToPoint(paths[i], &CGAffineTransformIdentity, points[i][j].x, points[i][j].y, points[i][j+1].x, points[i][j+1].y, points[i][j+2].x, points[i][j+2].y);
}
}
[self setNeedsDisplay];
initialized = YES;
double time = CACurrentMediaTime();
if (frameTimer % 30 == 0)
{
NSLog(@"FPS: %f\n", 1.0f/(time-previousTime));
}
previousTime = time;
frameTimer += 1;
}
- (void)drawInContext:(CGContextRef)ctx
{
// self.contentsScale = [[UIScreen mainScreen] scale];
if (initialized)
{
CGContextSetLineWidth(ctx, 10);
for (int i = 0; i < NUM_PATHS; i++)
{
UIColor* randomColor = [UIColor colorWithRed:(arc4random()%RAND_MAX/((float)RAND_MAX)) green:(arc4random()%RAND_MAX/((float)RAND_MAX)) blue:(arc4random()%RAND_MAX/((float)RAND_MAX)) alpha:1];
CGContextSetStrokeColorWithColor(ctx, randomColor.CGColor);
CGContextAddPath(ctx, paths[i]);
CGContextStrokePath(ctx);
}
}
}
@end
答案 0 :(得分:4)
你真的不应该将Core Craphics绘图与OpenGL进行比较,而是为了不同的目的比较完全不同的特征。
在图像质量方面,Core Graphics和Quartz将比OpenGL更加优越,省力。 Core Graphics框架旨在实现最佳外观,自然抗锯齿线条和曲线,以及与Apple UI相关的润色。但这种图像质量是有代价的:渲染速度。
另一方面,OpenGL的设计以速度为优先考虑。 OpenGL难以击败高性能,快速绘图。但是这种速度是有代价的:使用OpenGL获得平滑和抛光的图形要困难得多。有许多不同的策略可以像OpenGL中的“抗锯齿”那样“简单”,这是Quartz / Core Graphics更容易处理的。答案 1 :(得分:3)
首先,请参阅Why is UIBezierPath faster than Core Graphics path?并确保您以最佳方式配置路径。默认情况下,CGContext
会为可能增加大量开销的路径添加许多“漂亮”选项。如果你关掉它们,你可能会发现速度的显着改善。
我在CoreGraphicsBézier曲线中发现的下一个问题是当你在一条曲线中有很多组件时(当我查看大约3000-5000个元素时,我看到了问题)。我在CGPathAdd...
找到了非常惊人的时间。减少路径中元素的数量可能是一个重大胜利。从我去年与Core Graphics团队的谈话中,这可能是Core Graphics中的一个错误,可能已经修复。我没有重新测试过。
将CGContextStrokePath()
移到循环外部。你不应该每走一条路。你应该在最后一次击球。这需要我的测试从~8FPS到~12FPS。
关闭消除锯齿功能(在OpenGL测试中默认情况下可能会关闭):
CGContextSetShouldAntialias(ctx, false);
这让我达到18-20FPS(Retina)和高达40FPS非Retina。
我不知道你在OpenGL中看到了什么。请记住,Core Graphics旨在让事物变得美丽; OpenGL旨在使事情变得更快。 Core Graphics依赖于OpenGL;所以我总是期望编写良好的OpenGL代码更快。
答案 2 :(得分:3)
免责声明:我是MonkVG的作者。
MonkVG比CoreGraphics快得多的最大原因实际上并不是因为它是用OpenGL ES作为渲染支持实现的,而是因为它&#34;欺骗&#34;通过在任何渲染完成之前将轮廓细分为多边形。轮廓曲面细分实际上非常缓慢,如果您要动态生成轮廓,您会看到一个大幅减速。 OpenGL支持(使用直接位图渲染的CoreGraphics)的巨大好处是任何转换,如平移,旋转或缩放都不会强制轮廓的完全重新细分 - 它主要用于&#34;免费&#34 ;.
答案 3 :(得分:1)
你的减速是因为这行代码:
[self setNeedsDisplay];
您需要将其更改为:
[self setNeedsDisplayInRect:changedRect];
由你来计算每一帧的矩形变化,但如果你这样做,你可能会看到一个数量级的性能提升而没有其他变化。