ユーザーの入力に応じてリアルタイムでベジエ曲線を描画する必要がある iOS アプリに取り組んでいます。最初は、すばらしいベクター描画 API を備えた CoreGraphics を使用することにしました。しかし、私はすぐに、Retina iPad のたった 1 つのカーブでフレームレートが大幅に低下し始めるところまで、パフォーマンスがひどく、耐え難いほど遅いことを発見しました。(確かに、これは非効率的なコードを使った簡単なテストでした。たとえば、曲線はフレームごとに再描画されていました。しかし、今日のコンピューターは 1/60 秒ごとに単純な曲線の描画を処理するのに十分な速さですよね?!)
この実験の後、私は OpenGL とMonkVGライブラリに切り替えましたが、これ以上ないほど満足しています。フレームレートを落とすことなく、何百もの曲線を同時にレンダリングできるようになりました。忠実度への影響は最小限に抑えられます (私の使用例の場合)。
- CoreGraphics を何らかの形で誤用した可能性はありますか (OpenGL ソリューションよりも数桁遅くなるまで)、またはパフォーマンスは本当にひどいのでしょうか? 私の推測では、CG パフォーマンスに関する StackOverflow/フォーラムの質問と回答の数に基づいて、問題は CoreGraphics にあるということです。(何人かの人々が、CG は実行ループに入ることを意図しておらず、まれなレンダリングにのみ使用する必要があると述べているのを見てきました。) 技術的に言えば、なぜこれが当てはまるのでしょうか?
- CoreGraphics が本当に遅いのなら、一体どうして Safari はこんなにスムーズに動作するのでしょうか? 私は、Safari はハードウェア アクセラレーションを備えていないという印象を受けましたが、フレームをドロップすることなく、数百 (数千ではないにしても) のベクトル文字を同時に表示する必要があります。
- より一般的に言えば、ベクトルを頻繁に使用するアプリケーション (ブラウザー、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