7

ユーザーの入力に応じてリアルタイムでベジエ曲線を描画する必要がある iOS アプリに取り組んでいます。最初は、すばらしいベクター描画 API を備えた CoreGraphics を使用することにしました。しかし、私はすぐに、Retina iPad のたった 1 つのカーブでフレームレートが大幅に低下し始めるところまで、パフォーマンスがひどく、耐え難いほど遅いことを発見しました。(確かに、これは非効率的なコードを使った簡単なテストでした。たとえば、曲線はフレームごとに再描画されていました。しかし、今日のコンピューターは 1/60 秒ごとに単純な曲線の描画を処理するのに十分な速さですよね?!)

この実験の後、私は OpenGL とMonkVGライブラリに切り替えましたが、これ以上ないほど満足しています。フレームレートを落とすことなく、何百もの曲線を同時にレンダリングできるようになりました。忠実度への影響は最小限に抑えられます (私の使用例の場合)。

  1. CoreGraphics を何らかの形で誤用した可能性はありますか (OpenGL ソリューションよりも数桁遅くなるまで)、またはパフォーマンスは本当にひどいのでしょうか? 私の推測では、CG パフォーマンスに関する StackOverflow/フォーラムの質問と回答の数に基づいて、問題は CoreGraphics にあるということです。(何人かの人々が、CG は実行ループに入ることを意図しておらず、まれなレンダリングにのみ使用する必要があると述べているのを見てきました。) 技術的に言えば、なぜこれが当てはまるのでしょうか?
  2. CoreGraphics が本当に遅いのなら、一体どうして Safari はこんなにスムーズに動作するのでしょうか? 私は、Safari はハードウェア アクセラレーションを備えていないという印象を受けましたが、フレームをドロップすることなく、数百 (数千ではないにしても) のベクトル文字を同時に表示する必要があります。
  3. より一般的に言えば、ベクトルを頻繁に使用するアプリケーション (ブラウザー、Illustrator など) は、ハードウェア アクセラレーションなしでどのように高速を維持できるのでしょうか? (私が理解しているように、多くのブラウザーとグラフィックス スイートにはハードウェア アクセラレーション オプションが付属していますが、多くの場合、既定ではオンになっていません。)

アップデート:

パフォーマンスをより正確に測定するための簡単なテスト アプリを作成しました。以下は、私のカスタム CALayer サブクラスのコードです。

NUM_PATHS を 5 に設定し、NUM_POINTS を 15 (パスごとに 5 つの曲線セグメント) に設定すると、コードは iPad 3 の非網膜モードで 20 fps、網膜モードで 6 fps で実行されます。プロファイラーは CGContextDrawPath を CPU 時間の 96% としてリストします. はい — もちろん、再描画四角形を制限することで最適化できますが、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

4 に答える 4

4

Core Graphics の描画を OpenGL と比較するべきではありません。まったく異なる目的でまったく異なる機能を比較しているのです。

画質の点では、Core Graphics と Quartz は、少ない労力で OpenGL よりもはるかに優れています。Core Graphics フレームワークは、最適な外観、自然なアンチエイリアス処理された線と曲線、および Apple UI に関連する洗練のために設計されています。ただし、この画質には代償が伴います。レンダリング速度です。

一方、OpenGL は速度を優先して設計されています。高性能で高速な描画は、OpenGL に勝るものはありません。しかし、この速度には代償が伴います。OpenGL で滑らかで洗練されたグラフィックを実現するのははるかに困難です。OpenGL のアンチエイリアシングのように「単純」なことを行うには、さまざまな戦略があり、Quartz/Core Graphics でより簡単に処理できます。

于 2013-04-12T12:43:18.410 に答える
3

最初に、UIBezierPath が Core Graphics パスよりも速いのはなぜですか?を参照してください。パスを最適に構成していることを確認してください。デフォルトでCGContextは、多くの「pretty」オプションがパスに追加され、多くのオーバーヘッドが追加される可能性があります。これらをオフにすると、速度が大幅に向上する可能性があります。

Core Graphics のベジエ曲線で見つけた次の問題は、1 つの曲線に多くのコンポーネントがある場合です (約 3000 ~ 5000 要素を超えたときに問題が発生しました)。に驚くほど多くの時間を費やしましたCGPathAdd...。パス内の要素の数を減らすことは、大きな勝利になる可能性があります。昨年の Core Graphics チームとの話し合いによると、これは Core Graphics のバグであり、修正された可能性があります。再検査はしていません。


編集: 次の変更を加えることで、iPad 3 の Retina で 18 ~ 20 FPS が表示されます。

CGContextStrokePath()ループの外側に移動します。すべてのパスをストロークする必要はありません。最後に一度ストロークする必要があります。これにより、私のテストでは ~8FPS から ~12FPS になります。

アンチエイリアスをオフにします (OpenGL テストではおそらくデフォルトでオフになっています):

CGContextSetShouldAntialias(ctx, false);

これで 18 ~ 20 FPS (Retina) になり、非 Retina では最大で約 40 FPS になります。

OpenGL で何が表示されているかわかりません。Core Graphics は物事を美しくするように設計されていることを忘れないでください。OpenGL は物事を高速化するように設計されています。Core Graphics は OpenGL に依存しています。そのため、適切に作成された OpenGL コードはより高速であると常に期待しています。

于 2013-03-12T20:10:28.170 に答える
1

あなたの速度低下は、次のコード行によるものです。

[self setNeedsDisplay];

これを次のように変更する必要があります。

[self setNeedsDisplayInRect:changedRect];

フレームごとに変更された四角形を計算するのはあなた次第ですが、これを適切に行うと、他の変更を行わなくても、1 桁以上のパフォーマンスの向上が見られる可能性があります。

于 2013-11-09T23:10:08.177 に答える