简体   繁体   English

非常缓慢的软件向量,尤其是 CoreGraphics 与 OpenGL

[英]Painfully slow software vectors, particularly CoreGraphics vs. OpenGL

I'm working on an iOS app that requires drawing Bézier curves in real time in response to the user's input.我正在开发一个需要实时绘制贝塞尔曲线以响应用户输入的 iOS 应用程序。 At first, I decided to try using CoreGraphics, which has a fantastic vector drawing API.起初,我决定尝试使用 CoreGraphics,它有一个很棒的矢量绘图 API。 However, I quickly discovered that performance was painfully, excruciatingly slow, to the point where the framerate started dropping severely with just ONE curve on my retina iPad.然而,我很快发现性能令人痛苦,极其缓慢,以至于我的视网膜 iPad 上只有一条曲线,帧率开始严重下降。 (Admittedly, this was a quick test with inefficient code. For example, the curve was getting redrawn every frame. But surely today's computers are fast enough to handle drawing a simple curve every 1/60th of a second, right?!) (诚​​然,这是一个使用低效代码的快速测试。例如,曲线每帧都被重新绘制。但今天的计算机肯定足够快,可以每 1/60 秒绘制一条简单的曲线,对吧?!)

After this experiment, I switched to OpenGL and the MonkVG library, and I couldn't be happier.经过这个实验,我切换到了 OpenGL 和MonkVG库,我高兴得不能再高兴了。 I can now render HUNDREDS of curves simultaneously without any framerate drop, with only a minimal impact on fidelity (for my use case).我现在可以同时渲染数百条曲线而不会降低帧率,对保真度的影响很小(对于我的用例)。

  1. Is it possible that I misused CoreGraphics somehow (to the point where it was several orders of magnitude slower than the OpenGL solution), or is performance really that terrible?是否有可能我以某种方式滥用了 CoreGraphics(以至于它比 OpenGL 解决方案慢了几个数量级),或者性能真的那么糟糕吗? My hunch is that the problem lies with CoreGraphics, based on the number of StackOverflow/forum questions and answers regarding CG performance.我的直觉是,问题出在 CoreGraphics 上,基于 StackOverflow/论坛上关于 CG 性能的问题和答案的数量。 (I've seen several people state that CG isn't meant to go in a run loop, and that it should only be used for infrequent rendering.) Why is this the case, technically speaking? (我见过好几个人说 CG 不打算进入运行循环,它应该只用于不频繁的渲染。)从技术上讲,为什么会这样?
  2. If CoreGraphics really is that slow, how on earth does Safari work so smoothly?如果 CoreGraphics 真的那么慢,那么 Safari 到底是怎么运行得这么顺利的? I was under the impression that Safari isn't hardware-accelerated, and yet it has to display hundreds (if not thousands) of vector characters simultaneously without dropping any frames.我的印象是 Safari 不是硬件加速的,但它必须同时显示数百(如果不是数千)矢量字符而不会丢失任何帧。
  3. More generally, how do applications with heavy vector use (browsers, Illustrator, etc.) stay so fast without hardware acceleration?更一般地说,在没有硬件加速的情况下,使用大量矢量的应用程序(浏览器、Illustrator 等)如何保持如此快的速度? (As I understand it, many browsers and graphics suites now come with a hardware acceleration option, but it's often not turned on by default.) (据我所知,许多浏览器和图形套件现在都带有硬件加速选项,但默认情况下通常不会打开。)

UPDATE:更新:

I have written a quick test app to more accurately measure performance.我编写了一个快速测试应用程序来更准确地衡量性能。 Below is the code for my custom CALayer subclass.下面是我的自定义 CALayer 子类的代码。

With NUM_PATHS set to 5 and NUM_POINTS set to 15 (5 curve segments per path), the code runs at 20fps in non-retina mode and 6fps in retina mode on my iPad 3. The profiler lists CGContextDrawPath as having 96% of the CPU time. NUM_PATHS 设置为 5,NUM_POINTS 设置为 15(每条路径 5 条曲线段),代码在 iPad 3 上的非视网膜模式下以 20fps 运行,在视网膜模式下以 6fps 运行。分析器将 CGContextDrawPath 列为具有 96% 的 CPU 时间. Yes — obviously, I can optimize by limiting my redraw rect, but what if I really, truly needed full-screen vector animation at 60fps?是的——显然,我可以通过限制重绘矩形来优化,但如果我真的、真的需要 60fps 的全屏矢量动画呢?

OpenGL eats this test for breakfast. OpenGL 将这个测试作为早餐。 How is it possible for vector drawing to be so incredibly slow?矢量绘图怎么可能这么慢?

#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

You really should not compare Core Graphics drawing with OpenGL, you are comparing completely different features for very different purposes.您真的不应该将 Core Graphics 绘图与 OpenGL 进行比较,您正在比较完全不同的功能以用于非常不同的目的。

In terms of image quality, Core Graphics and Quartz are going to be far superior than OpenGL with less effort.在图像质量方面,Core Graphics 和 Quartz 将远远优于 OpenGL。 The Core Graphics framework is designed for optimal appearance , naturally antialiased lines and curves and a polish associated with Apple UIs. Core Graphics 框架旨在实现最佳外观、自然抗锯齿的线条和曲线以及与 Apple UI 相关的润色。 But this image quality comes at a price: rendering speed.但是这种图像质量是有代价的:渲染速度。

OpenGL on the other hand is designed with speed as a priority.另一方面,OpenGL 设计时优先考虑速度。 High performance, fast drawing is hard to beat with OpenGL. OpenGL 难以超越高性能、快速绘图。 But this speed comes at a cost: It is much harder to get smooth and polished graphics with OpenGL.但是这种速度是有代价的:使用 OpenGL 获得平滑和抛光的图形要困难得多。 There are many different strategies to do something as "simple" as antialiasing in OpenGL, something which is more easily handled by Quartz/Core Graphics.有许多不同的策略可以做一些像 OpenGL 中的抗锯齿一样“简单”的事情,而 Quartz/Core Graphics 更容易处理这些事情。

First, see Why is UIBezierPath faster than Core Graphics path?首先,请参阅为什么 UIBezierPath 比 Core Graphics 路径更快? and make sure you're configuring your path optimally.并确保您以最佳方式配置您的路径。 By default, CGContext adds a lot of "pretty" options to paths that can add a lot of overhead.默认情况下, CGContext为路径添加了很多“漂亮”的选项,这会增加很多开销。 If you turn these off, you will likely find dramatic speed improvements.如果您关闭这些功能,您可能会发现速度显着提高。

The next problem I've found with Core Graphics Bézier curves is when you have many components in a single curve (I was seeing problems when I went over about 3000-5000 elements).我发现 Core Graphics Bézier 曲线的下一个问题是当你在一条曲线中有许多组件时(当我浏览大约 3000-5000 个元素时,我看到了问题)。 I found very surprising amounts of time spent in CGPathAdd... .我发现在CGPathAdd...花费的时间非常惊人。 Reducing the number of elements in your path can be a major win.减少路径中的元素数量可能是一个重大胜利。 From my talks with the Core Graphics team last year, this may have been a bug in Core Graphics and may have been fixed.从我去年与 Core Graphics 团队的谈话来看,这可能是 Core Graphics 中的一个错误并且可能已经修复。 I haven't re-tested.我没有重新测试。


EDIT: I'm seeing 18-20FPS in Retina on an iPad 3 by making the following changes:编辑:通过进行以下更改,我在 iPad 3 上的 Retina 中看到 18-20FPS:

Move the CGContextStrokePath() outside the loop.CGContextStrokePath()循环外。 You shouldn't stroke every path.你不应该抚摸每条路径。 You should stroke once at the end.你应该在最后抚摸一次。 This takes my test from ~8FPS to ~12FPS.这使我的测试从 ~8FPS 到 ~12FPS。

Turn off anti-aliasing (which is probably turned off by default in your OpenGL tests):关闭抗锯齿(可能在 OpenGL 测试中默认关闭):

CGContextSetShouldAntialias(ctx, false);

That gets me to 18-20FPS (Retina) and up to around 40FPS non-Retina.这让我达到 18-20FPS(Retina)和高达 40FPS 的非 Retina。

I don't know what you're seeing in OpenGL.我不知道你在 OpenGL 中看到了什么。 Remember that Core Graphics is designed to make things beautiful;请记住,Core Graphics 旨在使事物变得美丽; OpenGL is designed to make things fast. OpenGL 旨在让事情变得更快。 Core Graphics relies on OpenGL; Core Graphics 依赖于 OpenGL; so I would always expect well-written OpenGL code to be faster.所以我总是希望编写好的 OpenGL 代码更快。

Disclaimer: I'm the author of MonkVG.免责声明:我是 MonkVG 的作者。

The biggest reason that MonkVG is so much faster then CoreGraphics is actually not so much that it is implemented with OpenGL ES as a render backing but because it "cheats" by tessellating the contours into polygons before any rendering is done. MonkVG 比 CoreGraphics 快得多的最大原因实际上并不是它是用 OpenGL ES 作为渲染支持实现的,而是因为它通过任何渲染完成之前将轮廓细分为多边形来“欺骗”。 The contour tessellation is actually painfully slow, and if you were to dynamically generate contours you would see a big slowdown.轮廓细分实际上非常缓慢,如果您要动态生成轮廓,您会看到明显的减速。 The great benefit of an OpenGL backing (verse CoreGraphics using direct bitmap rendering) is that any transform such a translation, rotation or scaling does not force a complete re-tessellation of the contours -- it's essentially for "free". OpenGL 支持(与使用直接位图渲染的 CoreGraphics 相比)的巨大好处是,任何变换,如平移、旋转或缩放,都不会强制对轮廓进行完全重新细分——它本质上是“免费”的。

Your slowdown is because of this line of code:你的速度变慢是因为这行代码:

[self setNeedsDisplay];

You need to change this to:您需要将其更改为:

[self setNeedsDisplayInRect:changedRect];

It's up to you to calculate what rectangle has changed every frame, but if you do this properly, you will likely see over an order of magnitude performance improvement with no other changes.由您来计算每一帧改变了什么矩形,但如果您正确地执行此操作,您可能会看到一个数量级的性能改进而没有其他变化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM