【问题标题】:Performance Issues When Using Many CALayer Masks使用多个 CALayer 掩码时的性能问题
【发布时间】:2013-06-07 21:48:35
【问题描述】:

我正在尝试使用CAShapeLayer 在我的iOS 应用程序中屏蔽CALayer,因为它需要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,但我无法确切说明是什么原因造成的。

我尝试在layerlayer.mask 上将shouldRasterize 设置为YES,但似乎都没有任何效果。我不知道该怎么办。

编辑:

我做了更多的测试,发现如果我只使用一个普通的CALayer 来掩盖另一个CALayer (layer.mask = someOtherLayer) 我会遇到同样的性能问题。似乎问题并不特定于CAShapeLayer,而是特定于CALayermask 属性。

编辑 2:

所以在了解了更多关于在Instruments 中使用Core Animation tool 的信息后,我了解到视图在每次移动时都会呈现在屏幕外。在触摸开始时将shouldRaster 设置为YES 并在触摸结束时将其关闭会使instruments 中的视图保持绿色(从而保持缓存),但性能仍然很糟糕。我相信这是因为即使视图正在被缓存,如果它不是不透明的,那么它仍然必须在每一帧中重新渲染。

需要强调的一点是,如果只有几个视图被屏蔽(甚至大约十个),则性能非常好。但是,当您将其增加到 100 or more 时,性能会滞后。我想这是因为当一个人越过其他人时,他们都必须重新渲染。

我的结论是,我有两种选择之一。

首先,必须有办法永久屏蔽视图(渲染一次并称其为良好)。我知道这可以通过我在示例代码中展示的图形或位图上下文路由来完成,但是当一个图层掩盖它的视图时,它会立即发生。当我在如图所示的位图上下文中执行此操作时,它非常慢(因为它几乎无法比较它有多慢)。

其次,必须有一些faster 的方式通过位图上下文路由来实现。如果有屏蔽图像或视图方面的专家,我们将非常感谢他们的帮助。

【问题讨论】:

  • 您最好的选择确实是使用此处提到的代码将您在 for 循环中添加的 200 张图像压缩为 1 张大图像:stackoverflow.com/questions/4334233/… 您也可以尝试使用仪器,并使用时间profiler 来检查你的代码的瓶颈在哪里。
  • 您是否尝试过在应用程序中使用它们之前将图像提前渲染到最终需要的状态,以便应用程序不需要执行所有这些图形处理,特别是因为您有这么多要对其执行此处理的图像,并且它们正在四处移动,因此它必须不断地重新渲染所有图像。我不确定这对您来说是否可行,并且您是否尝试过。对于没有图片的图像,也很难掌握您要做什么,在这种情况下,如果您附上屏幕截图会有所帮助,这很重要。
  • 这是由于您已经指出的屏幕外渲染。我认为没有办法优化该层的性能,但正如您已经看到的那样,您可以优化该过程。更进一步的可能是子类化 UIView 覆盖 drawRect 来实现你所需要的。只有当您的视图被标记为脏时,才会调用 drawRect。不要继承 UIImageView,因为 drawRect 永远不会被调用。

标签: ios objective-c performance calayer masking


【解决方案1】:

您已经取得了很大进展,我相信几乎可以找到解决方案。我要做的只是您已经尝试过的扩展。由于您说其中许多图层“结束”在相对于其他图层和蒙版保持不变的最终位置。所以只需将所有这些“完成”图层渲染到单个位图上下文。那样的话,每次你写出一个层到那个单一的上下文时,你就会少一个层来担心会减慢动画/渲染过程的速度。

【讨论】:

    【解决方案2】:

    Quartz (drawRect:) 比 CoreAnimation 慢的原因有很多:CALayer vs CGContextdrawRect vs CALayer。但有必要正确使用它。

    在文档中您可以看到一些建议。 ImprovingCoreAnimationPerformance

    如果您想要高性能,也许您可​​以尝试使用AsyncDisplayKit。该框架允许创建流畅且响应迅速的应用程序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-24
      • 1970-01-01
      • 2022-11-10
      • 2015-01-08
      • 1970-01-01
      • 1970-01-01
      • 2017-10-01
      相关资源
      最近更新 更多