【问题标题】:Multiple CALayer masks causing performance issues多个 CALayer 掩码导致性能问题
【发布时间】:2015-12-08 22:33:06
【问题描述】:

我正在尝试使用 6 个CALayer 对象创建一个相当简单的动画,每个对象都被一个路径所掩盖。但是,在尝试为它们设置动画时,我遇到了明显的滞后峰值。 Here is a video of the animation running. 我可以通过将shouldRasterize 设置为YES 来提高性能,但是它会导致文本像素化,正如您从这张图片中看到的那样:

我可以通过将rasterizationScale 设置为屏幕比例来纠正像素化,但这会带回在没有光栅化的情况下出现的延迟峰值!

这是我的代码:

@interface splashLayer : CALayer

@end

@implementation splashLayer {
    UIColor* color;

    CALayer* l1, *l2, *l3, *l4, *l5, *l6;
    CAShapeLayer* m1, *m2, *m3, *m4, *m5, *m6;

    NSUInteger i;
}

-(instancetype) init {
    if (self = [super init]) {

        color = [lzyColors purpleColor];

        i = 0;

        m1 = [CAShapeLayer layer]; m2 = [CAShapeLayer layer]; m3 = [CAShapeLayer layer]; m4 = [CAShapeLayer layer]; m5 = [CAShapeLayer layer]; m6 = [CAShapeLayer layer];

        self.shouldRasterize = YES;
        self.rasterizationScale = screenScale(); // Slows down performance, but stops ugly pixelation.

        CGMutablePathRef p = CGPathCreateMutable();

        CGFloat const meanScreenLength = (screenHeight()+screenWidth())*0.5;

        CGFloat const pythag = lzyMathsPythag(meanScreenLength, meanScreenLength);
        CGFloat const halfPythag = pythag*0.5;
        CGFloat const pythagHalfPythag = lzyMathsPythag(halfPythag, halfPythag);
        CGPoint const center = screenCenter();

        CGPoint p1 = {center.x, center.y-pythagHalfPythag};
        CGPoint p2 = {center.x+pythagHalfPythag, center.y};
        CGPoint p3 = {center.x, center.y+pythagHalfPythag};
        CGPoint p4 = {center.x-pythagHalfPythag, center.y};

        CGPathMoveToPoint(p, nil, p1.x, p1.y);
        lzyCGPathAddLineToPath(p, p2);
        lzyCGPathAddLineToPath(p, p3);
        lzyCGPathAddLineToPath(p, p4);
        CGPathCloseSubpath(p);

        m1.path = p; m2.path = p; m3.path = p; m4.path = p; m5.path = p; m6.path = p;
        CGPathRelease(p);
        m1.position = (CGPoint){-pythag, -pythag}; m2.position = (CGPoint){-pythag, -pythag}; m3.position = (CGPoint){-pythag, -pythag};
        m4.position = (CGPoint){pythag, pythag}; m5.position = (CGPoint){pythag, pythag}; m6.position = (CGPoint){pythag, pythag};

        l1 = [CALayer layer];
        l1.contents = (__bridge id _Nullable)(colorImage([color lightenByValue:0.6], screenSize()).CGImage);
        l1.frame = (CGRect){CGPointZero, screenSize()};
        l1.mask = m1;

        l2 = [CALayer layer];
        l2.contents = (__bridge id _Nullable)(textBG([color lightenByValue:0.3], screenSize()).CGImage);
        l2.frame = (CGRect){CGPointZero, screenSize()};
        l2.mask = m2;

        l3 = [CALayer layer];
      //  l3.rasterizationScale = screenScale(); (Doesn't work)
        l3.contents = (__bridge id _Nullable)(textBG(color, screenSize()).CGImage);
        l3.frame = (CGRect){CGPointZero, screenSize()};
        l3.mask = m3;

        UIColor* color2 = [lzyColors redColor];

        l4 = [CALayer layer];
        l4.contents = (__bridge id _Nullable)(colorImage([color2 lightenByValue:0.6], screenSize()).CGImage);
        l4.frame = (CGRect){CGPointZero, screenSize()};
        l4.mask = m4;

        l5 = [CALayer layer];
        l5.contents = (__bridge id _Nullable)(colorImage([color2 lightenByValue:0.3], screenSize()).CGImage);
        l5.frame = (CGRect){CGPointZero, screenSize()};
        l5.mask = m5;

        l6 = [CALayer layer];
        l6.contents = (__bridge id _Nullable)(colorImage(color2, screenSize()).CGImage);
        l6.frame = (CGRect){CGPointZero, screenSize()};
        l6.mask = m6;

        [self addSublayer:l1]; [self addSublayer:l2]; [self addSublayer:l3]; [self addSublayer:l4]; [self addSublayer:l5]; [self addSublayer:l6];


        CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"position"];
        anim.fromValue = [NSValue valueWithCGPoint:(CGPoint){-pythag, -pythag}];
        anim.toValue = [NSValue valueWithCGPoint:CGPointZero];
        anim.delegate = self;
        anim.beginTime = CACurrentMediaTime()+1;
        anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

        anim.removedOnCompletion = NO;
        anim.fillMode = kCAFillModeForwards;

        anim.duration = 1;
        [m1 addAnimation:anim forKey:@"0"];

        anim.duration = 1.25;
        [m2 addAnimation:anim forKey:@"1"];

        anim.duration = 1.5;
        [m3 addAnimation:anim forKey:@"2"];

        anim.fromValue = [NSValue valueWithCGPoint:(CGPoint){pythag, pythag}];
        anim.beginTime = CACurrentMediaTime()+2.5;

        anim.duration = 1;
        [m4 addAnimation:anim forKey:@"3"];

        anim.duration = 1.25;
        [m5 addAnimation:anim forKey:@"4"];

        anim.duration = 1.5;
        [m6 addAnimation:anim forKey:@"5"];


    }
    return self;
}

@end

我知道代码很粗糙,但我只是为了调试而对其进行了简化。

colorImage()textBG() 函数只是做一些核心图形渲染来为 6 层生成 6 个图像。这不应该是问题的根源,因为绘图很简单,并且动画在开始前会延迟一秒钟。

我尝试仅将rasterizationScale 设置为显示文本的图层的屏幕比例,但这不起作用。

我还尝试通过移除第三层下方的两层来提高性能,一旦它完成动画,但它并没有显着提高性能。

-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    if (flag) {

        if (i == 2) {

            [l1 removeFromSuperlayer];
            l1 = nil;
            m1 = nil;
            [l2 removeFromSuperlayer];
            l2 = nil;
            m2 = nil;
            l3.mask = nil;
            m3 = nil;
        }

        i++;
    }
}

对如何提高性能有什么建议吗?

【问题讨论】:

  • 我在您的帖子中看到的唯一内容是静止图像。您可以发布您创建的视频的链接吗?
  • 如果您愿意接受预渲染(根据 Duncan 的回答中的 cmets),为什么不在图形应用程序中创建静态图像并从资产目录中加载它们呢?您的背景颜色可能仍然是随机的。这种技术没有那么灵活,但它的性能可能会更好。
  • 嗯,在 1x、2x、3x、iPad、2x iPad 上会有 15 种随机颜色……所以如果动画在 40fps 时长 4 秒……那就需要 12,000 张图像预渲染...我只是不认为这是一个实用的解决方案。
  • 此外,我总是更喜欢用 CG 或 CA 实际完成的稍微不那么流畅的动画,而不是导入资产目录,这样我可以更容易地证明我的应用程序,并且能够调整/随时更改动画。
  • 哦,在动画的后半部分,我想使用应用背景颜色......所以它甚至不是我可以在不同应用中重复使用的解决方案......所以是的,绝对不是一个选项.

标签: ios objective-c performance core-animation calayer


【解决方案1】:

我建议将图层和蒙版合成为静态图像并为其设置动画。这应该很快。

【讨论】:

  • 我最初确实尝试过,但是由于它没有利用 GPU,因此预渲染流畅动画所需的所有图像需要很长时间,占用大量内存并且播放并不总是流畅。
  • 您需要以屏幕分辨率预渲染图像。您应该能够执行一次且仅一次,然后将图像保存到磁盘,然后只需加载它们即可。屏幕大小的图像应该可以快速生成动画。
  • 我以屏幕分辨率进行预渲染,但我仍然发现动画并不总是流畅,并且仍然需要相当长的时间来渲染。我还想为动画设置一个随机的背景颜色(大约 15 种颜色);因此,为什么我放弃了预渲染并前往 CA。我会回去再进行一次预渲染,然后告诉你进展如何。
  • 好吧,我试图在预渲染方面取得一些进展,但我仍然遇到了与我开始时遇到的相同的问题......你可以在这里看到我的第二个问题:@ 987654321@
【解决方案2】:

好吧,在尝试了无数方法之后,尝试实现了这个动画;我得出的结论是,我可能最好直接跳到 openGL。这将使我能够为这个问题创建一个更通用的解决方案,使用更复杂的动画效果会更好。

【讨论】:

    猜你喜欢
    • 2013-06-07
    • 1970-01-01
    • 2010-10-24
    • 1970-01-01
    • 1970-01-01
    • 2019-05-04
    • 2012-04-17
    • 2011-06-24
    • 2018-06-08
    相关资源
    最近更新 更多