Sto lavorando a un'app per iOS che richiede il disegno di curve di Bézier in tempo reale in risposta all'input dell'utente. All'inizio, ho deciso di provare a utilizzare CoreGraphics, che ha una fantastica API di disegno vettoriale. Tuttavia, ho scoperto rapidamente che la performance era dolorosamente, atrocemente lenta, al punto in cui il framerate ha iniziato a cadere gravemente con una sola curva sul mio iPad retina. (Certo, si trattava di un test rapido con codice inefficiente. Ad esempio, la curva veniva ridisegnata su ogni frame, ma sicuramente i computer di oggi sono abbastanza veloci da gestire una semplice curva ogni 1/60 di secondo, giusto ?!)Vettori software dolorosamente lenti, in particolare CoreGraphics vs. OpenGL
Dopo questo esperimento, sono passato a OpenGL e alla libreria MonkVG e non potrei essere più felice. Ora posso rendere HUNDREDS delle curve simultaneamente senza alcuna caduta di framerate, con un impatto minimo sulla fedeltà (per il mio caso d'uso).
- E 'possibile che io abusato CoreGraphics in qualche modo (al punto in cui è stato diversi ordini di grandezza più lento rispetto alla soluzione OpenGL), o è la prestazione davvero così terribile? La mia impressione è che il problema si trova con CoreGraphics, basato sul numero di StackOverflow/forum domande e risposte riguardanti le prestazioni CG. (Ho visto diverse persone affermare che CG non è pensato per andare in un ciclo di esecuzione, e che dovrebbe essere usato solo per il rendering infrequente.) Perché è questo il caso, tecnicamente parlando?
- Se CoreGraphics è davvero così lento, come mai Safari funziona così bene? Avevo l'impressione che Safari non fosse accelerato dall'hardware, eppure deve mostrare centinaia (se non migliaia) di caratteri vettoriali contemporaneamente senza perdere alcun frame.
- Più in generale, in che modo le applicazioni con un vettore pesante (browser, Illustrator, ecc.) Rimangono così veloci senza l'accelerazione hardware? (A quanto mi risulta, molti browser e le suite grafica ora con un'opzione di accelerazione hardware, ma non è spesso attivati per impostazione predefinita.)
UPDATE:
Ho scritto un test rapido app per misurare più accuratamente le prestazioni. Di seguito è riportato il codice per la sottoclasse CALayer personalizzata.
Con NUM_PATHS impostato su 5 e NUM_POINTS impostato su 15 (5 segmenti di curva per percorso), il codice viene eseguito a 20fps in modalità non-retina e 6fps in modalità retina sul mio iPad 3. Il profiler elenca CGContextDrawPath ad avere il 96% del tempo della CPU. Sì, ovviamente, posso ottimizzare limitando il mio redirect rect, ma se avessi davvero davvero bisogno di animazione vettoriale a schermo intero a 60fps?
OpenGL mangia questo test per la colazione. Com'è possibile che il disegno vettoriale sia così incredibilmente lento?
#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
È difficile dire se si è utilizzato in modo improprio la grafica principale senza visualizzare il codice o almeno una descrizione più dettagliata. Hai creato un nuovo CGPathRef su ciascun frame (esplicitamente o implicitamente) o ne hai creato uno in anticipo e riutilizzalo? Scommetto che avrebbe un impatto sulle prestazioni. – benzado
Forse ho creato un nuovo CGPathRef per ogni fotogramma, ma dovrò ricontrollare. (Ma anche se lo facessi, non posso immaginare un miglioramento delle prestazioni di diversi ordini di grandezza, sai?) So che ho provato a limitare i miei ridisegni solo a ogni segmento appena aggiunto della spline, ma anche quello non mi è stato di grande aiuto. – Archagon
Ho creato un'app che ha ridisegnato un percorso complesso diverse volte per ogni frame utilizzando Core Graphics. Le prestazioni erano buone, persino migliori del previsto. Il percorso consisteva di circa 100 elementi, larghezza della linea fino a 100 px, con molti tappi rotondi tra le parti non connesse. Sono rimasto impressionato dalle prestazioni quando disegno a schermo intero su un iPad 2 e 3 (con risoluzione retina). –