Подтвердить что ты не робот

Проблемы с производительностью при использовании многих масок CALayer

Я пытаюсь использовать CAShapeLayer для маскирования CALayer в моем приложении iOS, поскольку для маскирования изображения вместо маскировки изображения требуется меньшее количество CPU time в bitmap context;

Когда у меня есть несколько десятков или более изображений, расположенных друг над другом, CAShapeLayer в масках UIImageView медленно перемещается на мое прикосновение.

Вот пример кода:

UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"SomeImage.jpg" ofType:nil]];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25));

for (int i = 0; i < 200; i++) {

    SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:image];
    imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), image.size.width * .25, image.size.height * .25);

    CAShapeLayer *shape = [CAShapeLayer layer];
    shape.path = path;
    imageView.layer.mask = shape;

    [self.view addSubview:imageView];
    [imageView release];

}
CGPathRelease(path);

С приведенным выше кодом imageView очень отсталый. Однако он мгновенно реагирует, если я маскирую его вручную в bitmap context:

UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"3.0-Pad-Classic0.jpg" ofType:nil]];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25));


for (int i = 0; i < 200; i++) {

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(image.size.width * .25, image.size.height * .25), NO, [[UIScreen mainScreen]scale]);
    CGContextRef ctx = UIGraphicsGetCurrentContext();

    CGContextAddPath(ctx, path);
    CGContextClip(ctx);

    [image drawInRect:CGRectMake(-(image.size.width * .25), -(image.size.height * .25), image.size.width, image.size.height)];
    UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:finalImage];
    imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), finalImage.size.width, finalImage.size.height);

    [self.view addSubview:imageView];
    [imageView release];

}
CGPathRelease(path);

Кстати, вот код SLTUIImageView, это просто простой подкласс UIImageView, который реагирует на касания (для любого, кто задавался вопросом):

-(id)initWithImage:(UIImage *)image{

    self = [super initWithImage:image];
    if (self) {
        self.userInteractionEnabled = YES;
    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [self.superview bringSubviewToFront:self];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{

    UITouch *touch = [touches anyObject];
    self.center = [touch locationInView:self.superview];
}

Можно ли как-то оптимизировать, как CAShapeLayer маскирует UIImageView, чтобы улучшить производительность? Я попытался выяснить, где бутылочная шее использует Time Profiler в Instruments, но я не могу точно сказать, что именно вызывает его.

Я попытался установить shouldRasterize на YES как на layer, так и на layer.mask, но ни один из них не имеет никакого эффекта. Я не уверен, что делать.

Edit:

Я провел больше тестирования и обнаружил, что если я использую только обычный CALayer для маскировки другого CALayer (layer.mask = someOtherLayer), у меня такие же проблемы с производительностью. Похоже, что проблема не специфична для CAShapeLayer -rather, она специфична для свойства mask CALayer.

Изменить 2:

Поэтому, узнав больше об использовании Core Animation tool в Instruments, я узнал, что представление визуализируется за кадром при каждом его перемещении. Установка shouldRaster на YES при появлении касания и выключении при окончании нажатия делает вид зеленым (сохраняя кэш) в Instruments, но производительность все еще ужасна. Я считаю, что это связано с тем, что, хотя представление кэшируется, если оно не непрозрачно, оно должно быть повторно отображено с каждым фреймом.

Одна вещь подчеркнуть заключается в том, что если в масках есть только несколько видов (скажем, даже около десяти), производительность довольно хорошая. Однако, когда вы увеличиваете это до 100 or more, производительность отстает. Я предполагаю, что это происходит потому, что, когда кто-то перемещается над другими, все они должны быть повторно отображены.

Мое заключение таково: У меня есть один из двух вариантов.

Во-первых, должно быть какое-то время, чтобы надолго замаскировать представление (визуализировать его один раз и называть его хорошим). Я знаю, что это можно сделать с помощью графического или растрового контекстного маршрута, как показано в моем примере кода, но когда слой маскирует его представление, это происходит мгновенно. Когда я делаю это в растровом контексте, как показано, он довольно медленный (так как в нем почти невозможно даже сравнить, насколько он медленнее).

Во-вторых, должен быть некоторый способ faster сделать это через трассировку контекста битмапа. Если есть эксперт в маскировке изображений или представлений, их помощь будет очень оценена.

4b9b3361

Ответ 1

Вы прошли довольно далеко, и я верю, что это почти решение. Я бы сделал просто расширение того, что вы уже пробовали. Поскольку вы говорите, что многие из этих слоев "заканчиваются" в конечных положениях, которые остаются постоянными относительно других слоев и маски. Поэтому просто визуализируйте все эти "готовые" слои в один растровый контекст. Таким образом, каждый раз, когда вы выписываете слой в этот единственный контекст, у вас будет один меньше слоев, чтобы беспокоиться об этом, замедляя процесс анимации/рендеринга.

Ответ 2

Кварц (drawRect:) медленнее CoreAnimation по многим причинам: CALayer vs CGContext drawRect vs CALayer, Но нужно использовать его правильно.

В документации вы можете увидеть некоторые советы. УлучшениеCoreAnimationPerformance

Если вам нужна высокая производительность, возможно, вы можете попробовать использовать AsyncDisplayKit. Эта структура позволяет создавать плавные и гибкие приложения.