Ich arbeite an einer iOS-App, die es erfordert, Bézier-Kurven in Echtzeit als Antwort auf die Eingabe des Benutzers zu zeichnen. Zuerst habe ich mich entschieden, CoreGraphics zu verwenden, das eine fantastische Vektorgrafik-API hat. Ich stellte jedoch schnell fest, dass die Leistung schmerzhaft und quälend langsam war, bis zu dem Punkt, an dem die Framerate mit nur einer Kurve auf meinem Retina-iPad stark abfiel. (Zugegebenermaßen war dies ein schneller Test mit ineffizientem Code. Zum Beispiel wurde die Kurve in jedem Frame neu gezeichnet. Aber die heutigen Computer sind sicher schnell genug, um alle 1/60 Sekunden eine einfache Kurve zu zeichnen, oder ?!)Schmerzhaft langsame Softwarevektoren, insbesondere CoreGraphics vs. OpenGL
Nach diesem Experiment wechselte ich zu OpenGL und der MonkVG Bibliothek, und ich könnte nicht glücklicher sein. Ich kann jetzt HUNDERTE von Kurven gleichzeitig ohne jede Framerate-Drop, mit nur einer minimalen Auswirkung auf die Treue (für meinen Anwendungsfall).
- Ist es möglich, dass ich CoreGraphics irgendwie missbraucht habe (bis zu dem Punkt, wo es mehrere Größenordnungen langsamer war als die OpenGL-Lösung), oder ist die Leistung wirklich so schrecklich? Meine Vermutung ist, dass das Problem bei CoreGraphics liegt, basierend auf der Anzahl der StackOverflow/Forum-Fragen und Antworten bezüglich der CG-Leistung. (Ich habe mehrere Leute feststellen lassen, dass CG nicht dazu gedacht ist, in eine Run-Schleife zu gehen, und dass es nur für seltenes Rendering verwendet werden sollte.) Warum ist das technisch gesehen der Fall?
- Wenn CoreGraphics wirklich so langsam ist, wie in aller Welt funktioniert Safari so reibungslos? Ich hatte den Eindruck, dass Safari nicht hardwarebeschleunigt ist und dennoch Hunderte von Vektorzeichen gleichzeitig anzeigen muss, ohne dass Frames verloren gehen.
- Allgemeiner, wie bleiben Anwendungen mit starker Vektornutzung (Browser, Illustrator usw.) ohne Hardwarebeschleunigung so schnell? (Wie ich es verstehe, jetzt viele Browser und Grafiken Suiten sind mit einer Hardware-Beschleunigung Option, aber es ist oft nicht standardmäßig aktiviert.)
UPDATE:
ich geschrieben habe einen schnellen Test App, um die Leistung genauer zu messen. Unten ist der Code für meine benutzerdefinierte CALayer-Unterklasse.
Mit NUM_PATHS auf 5 und NUM_POINTS auf 15 (5 Kurvensegmente pro Pfad) eingestellt, läuft der Code mit 20fps im Nicht-Retina-Modus und 6fps im Retina-Modus auf meinem iPad 3. Der Profiler listet CGContextDrawPath mit 96% auf der CPU-Zeit. Ja - natürlich kann ich optimieren, indem ich mein Redraw-Rect beschränkt, aber was wäre, wenn ich wirklich, Full-Screen-Vektoranimation bei 60 fps brauche?
OpenGL isst diesen Test zum Frühstück. Wie ist es möglich, dass Vektorzeichnen so unglaublich langsam ist?
#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
Es ist schwer zu sagen, ob Sie Core Graphics missbraucht haben, ohne Ihren Code oder zumindest eine detailliertere Beschreibung zu sehen. Haben Sie für jeden Frame (explizit oder implizit) einen neuen CGPathRef erstellt oder haben Sie einen im Voraus erstellt und erneut verwendet? Ich wette, das hätte Auswirkungen auf die Performance. – benzado
Ich habe vielleicht jeden Frame ein neues CGPathRef erstellt, aber ich muss es nochmal überprüfen. (Aber selbst wenn ich es täte, kann ich mir nicht vorstellen, dass sich die Leistung um mehrere Größenordnungen verbessert, weißt du?) Ich weiß, dass ich versucht habe, meine Neuzeichnungen auf jedes neu hinzugefügte Segment des Splines zu beschränken, aber selbst das half nicht sehr. – Archagon
Ich habe eine App erstellt, die einen komplexen Pfad mehrere Male in jedem Frame mithilfe von Core Graphics neu erstellt. Die Performance war gut, sogar besser als erwartet. Der Pfad bestand aus ungefähr 100 Elementen, Linienbreite bis zu 100 px, mit vielen runden Kappen zwischen nicht verbundenen Teilen. Ich war beeindruckt von der Leistung beim Vollbild auf einem iPad 2 und 3 (mit Retina-Auflösung). –