痛苦地减慢软件向量,特别是CoreGraphics与OpenGL

时间:2013-03-12 19:25:46

标签: ios opengl-es core-graphics vector-graphics openvg

我正在开发一款iOS应用程序,需要实时绘制Bézier曲线以响应用户的输入。起初,我决定尝试使用CoreGraphics,它具有出色的矢量绘图API。然而,我很快就发现性能很痛苦,极其缓慢,在我的视网膜iPad上只有一条曲线,帧速率开始严重下降。 (不可否认,这是一个代码效率低下的快速测试。例如,曲线每帧都重新绘制。但是今天的计算机确实足够快,每隔1/60秒处理一条简单的曲线,对吧?!)

在这个实验之后,我切换到了OpenGL和MonkVG库,我感到非常高兴。我现在可以在没有任何帧率下降的情况下同时渲染HUNDREDS曲线,对保真度的影响很小(对于我的用例)。

  1. 我是否有可能以某种方式误用CoreGraphics(至少比OpenGL解决方案慢几个数量级),或者性能真的那么可怕?我的预感是问题在于CoreGraphics,基于StackOverflow /论坛问题的数量以及有关CG性能的答案。 (我已经看到有几个人声称CG并不意味着进入一个运行循环,并且它只应该用于不频繁的渲染。)从技术上讲,为什么会这样呢?
  2. 如果CoreGraphics真的那么慢,Safari究竟如何顺利地工作?我的印象是Safari不是硬件加速的,但它必须同时显示数百个(如果不是数千个)矢量字符而不丢弃任何帧。
  3. 更一般地说,如果没有硬件加速,使用重载体的应用程序(浏览器,Illustrator等)如何保持如此快速? (据我所知,许多浏览器和图形套件现在都带有硬件加速选项,但默认情况下它通常不会打开。)

  4. 更新:

    我编写了一个快速测试应用程序,以更准确地衡量性能。下面是我的自定义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
    

4 个答案:

答案 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中的一个错误,可能已经修复。我没有重新测试过。


编辑:通过进行以下更改,我在iPad 3上看到了Retina的18-20FPS:

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];

由你来计算每一帧的矩形变化,但如果你这样做,你可能会看到一个数量级的性能提升而没有其他变化。