iOS/Core-Animation:性能调优

P i*_*P i 16 optimization performance core-animation ios

我在iPad上运行了我的应用程序.但它的表现非常糟糕 - 我的速度低于15fps.谁能帮我优化?

它基本上是一个包含12个按钮(来自UIControl)的轮子(源自UIView). 控制的形象

当用户旋转它时,按钮会动态扩展和收缩(例如,12点钟位置的按钮应始终是最大的)

所以我的车轮包含:

- (void) displayLinkIsCallingBack: (CADisplayLink *) dispLink 
{
    :
    // using CATransaction like this goes from 14fps to 19fps
    [CATransaction begin];
    [CATransaction setDisableActions: YES];

    // NEG, as coord system is flipped/fucked
    self.transform = CGAffineTransformMakeRotation(-thetaWheel);

    [CATransaction commit];

    if (BLA)
        [self rotateNotch: direction];  
}
Run Code Online (Sandbox Code Playgroud)

...根据最近的触摸输入计算车轮的新旋转.这里已经存在一个性能问题,我在一个单独的线程上追求:iOS Core-Animation:CATransaction/Interpolating转换矩阵的性能问题

此例程还检查车轮是否已完成另外1/12旋转,如果是,则指示所有12个按钮调整大小:

// Wheel.m
- (void) rotateNotch: (int) direction
{
    for (int i=0; i < [self buttonCount] ; i++)
    {
            CustomButton * b = (CustomButton *) [self.buttons objectAtIndex: i];

    // Note that b.btnSize is a dynamic property which will calculate
    // the desired button size based on the button index and the wheels rotation.
            [b resize: b.btnSize];
    }
}
Run Code Online (Sandbox Code Playgroud)

现在对于实际的大小调整代码,在button.m中:

// Button.m

- (void) scaleFrom: (float) s_old
                to: (float) s_new
              time: (float) t
{
    CABasicAnimation * scaleAnimation = [CABasicAnimation animationWithKeyPath: @"transform.scale"];

    [scaleAnimation setDuration:  t ];
    [scaleAnimation setFromValue:  (id) [NSNumber numberWithDouble: s_old] ];
    [scaleAnimation setToValue:  (id) [NSNumber numberWithDouble: s_new] ];
    [scaleAnimation setTimingFunction:  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut] ];
    [scaleAnimation setFillMode: kCAFillModeForwards];
    scaleAnimation.removedOnCompletion = NO;

    [self.contentsLayer addAnimation: scaleAnimation 
                              forKey: @"transform.scale"];

    if (self.displayShadow && self.shadowLayer)
        [self.shadowLayer addAnimation: scaleAnimation 
                                forKey: @"transform.scale"];    

    size = s_new;
}

// - - - 

- (void) resize: (float) newSize
{
    [self scaleFrom: size
                 to: newSize
               time: 1.];
}
Run Code Online (Sandbox Code Playgroud)

我想知道问题是否与多个transform.scale操作排队的开销有关 - 每个按钮调整大小需要一整秒才能完成,如果我快速旋转滚轮,我可能每秒旋转几转; 这意味着每个按钮每秒调整大小24次.

**创建按钮的图层**

我猜想最后一块拼图是看看按钮的内容层.但我试过了

contentsLayer.setRasterize = YES;
Run Code Online (Sandbox Code Playgroud)

应该有效地将其存储为位图.所以设置代码实际上是动态调整12位图的大小.

我无法相信这会使设备超出其极限.然而,核心动画乐器却告诉我; 当我旋转滚轮时(通过将我的手指拖动到圆圈中),它报告〜15fps.

这并不好:我最终需要在每个按钮内放置一个文本层,这将进一步拖累性能(...除非我使用上面的.setRasterize设置,在这种情况下它应该是相同的).

一定有什么我做错了!但什么?

编辑:这是负责生成按钮内容层的代码(即带阴影的形状):

- (CALayer *) makeContentsLayer
{
    CAShapeLayer * shapeOutline = [CAShapeLayer layer];
    shapeOutline.path = self.pOutline;

    CALayer * contents = [CALayer layer];

    // get the smallest rectangle centred on (0,0) that completely contains the button
    CGRect R = CGRectIntegral(CGPathGetPathBoundingBox(self.pOutline)); 
    float xMax = MAX(abs(R.origin.x), abs(R.origin.x+R.size.width));
    float yMax = MAX(abs(R.origin.y), abs(R.origin.y+R.size.height));
    CGRect S = CGRectMake(-xMax, -yMax, 2*xMax, 2*yMax);
    contents.bounds = S; 

    contents.shouldRasterize = YES; // try NO also

    switch (technique)
    {
        case kMethodMask:
            // clip contents layer by outline (outline centered on (0, 0))
            contents.backgroundColor = self.clr; 
            contents.mask = shapeOutline;
            break;

        case kMethodComposite:
            shapeOutline.fillColor = self.clr;
            [contents addSublayer: shapeOutline];
            self.shapeLayer = shapeOutline;
            break;

        default:
            break;
    }

    if (NO)
        [self markPosition: CGPointZero
                   onLayer: contents ];

    //[self refreshTextLayer];

    //[contents addSublayer: self.shapeLayerForText];

    return contents;
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我正在尝试一切可能的方法,我正在尝试两种方法来制作形状,另外我正在切换.shouldRasterize

**妥协UI设计以获得可容忍的帧速率**

编辑:现在我已经尝试禁用动态调整大小行为,直到车轮进入新位置,并设置wheel.setRasterize = YES.所以它有效地旋转了一个预先渲染的UIView(它无疑占据了大部分屏幕)在手指下面(它很高兴地做了~~ 60fps),直到轮子停下来,此时它执行这个滞后的大小调整动画( @ <20FPS).

虽然这给出了一个可以容忍的结果,但我不得不以这种方式牺牲我的UI设计.我确信我一定做错了.

编辑:我刚试过手动调整按钮大小的实验; 即在每个按钮中放置一个显示链接回调,并动态计算该给定帧的预期大小,使用与我对方向盘相同的CATransaction显式禁用动画,设置新的变换矩阵(从预期大小生成的缩放变换).添加到此我已设置按钮内容层shouldRasterize = YES.所以它应该简单地将每帧12个位图缩放到一个本身正在旋转的UIView上.令人惊讶的是,这已经慢了,它甚至会让模拟器停下来.它肯定比使用核心动画的动画功能自动慢10倍.

you*_*ung 15

我没有开发iPad应用程序的经验,但我确实有一些优化视频游戏.所以,我无法给出确切的答案,但我想提供一些优化提示.

不要猜.简介它.

您似乎在尝试进行更改而不分析代码.改变一些可疑代码并交叉手指并不真正奏效.您应该通过检查long每个任务的often运行方式以及运行方式来分析代码.尝试分解您的任务并将分析代码放到测量timefrequency.如果您可以测量memory每项任务的使用量或者有多少其他系统资源,那就更好了.根据证据找出你的瓶颈,而不是你的感觉.

对于您的具体问题,您认为当您的调整大小工作开始时,程序会变得非常慢.但是,您确定吗?它可能是别的东西.在得到实际的分析数据之前,我们不确定.

在进行更改之前,尽量减少有问题的区域并测量实际性能.

分析后,您有一个候选人来解决您的瓶颈问题.如果您仍然可以将原因拆分为几个小任务,那么请执行此操作并对其进行分析,直到您无法再将其拆分为止.然后,尝试通过重复运行几千次来测量它们的精确性能.这很重要,因为您需要在进行任何更改之前了解性能(速度和空间),以便将其与未来的更改进行比较.

对于您的具体问题,如果调整大小确实是问题,请尝试检查它的执行方式.执行一次调整大小调用需要多长时间?您需要多长时间调整一次工作来完成工作?

改进它.怎么样?这取决于.

现在,您遇到了有问题的代码块及其当前性能.你现在必须改进它.怎么样?好吧,这真的取决于问题所在.你可以搜索better algorithms,你可以做,lazy fetching如果你可以延迟计算,直到你真的需要执行,或者你可以over-eager evaluation通过缓存一些数据,如果你太频繁地做同样的操作.你越了解原因,就越容易改进它.

对于您的具体问题,它可能只是OS ui功能的限制.通常,调整大小按钮不仅仅是调整按钮本身,还invalidates包括整个区域或其父窗口小部件,以便可以正确呈现每个UI内容.调整大小按钮可能很昂贵,如果是这种情况,您可以通过简单地使用基于图像的方法而不是基于OS ui的系统来解决问题.如果OS API中的图像操作不够,您可以尝试使用OpenGL.但是,我们再次知道,直到我们真正尝试它们和profile这些替代方案.祝好运!:)


Jay*_*Jay 6

在没有阴影的情况下尝试它,看看是否能提高性能.我想它会大大改善它.然后我会研究使用CALayer的阴影路径来渲染阴影.这将大大提高阴影渲染性能.

来自去年WWDC的Apple核心动画视频提供了许多关于提高核心动画性能的绝佳信息.

顺便说一句,我现在正在制作一些比现在更复杂的动画,即使在较旧的iPhone 3G上也能很好地工作.硬件/软件非常强大.


小智 1

你可能应该在 OpenGL 中重新执行此操作

  • 另外,这确实不是一个有用的答案。显然这可以在 GL 中重做。但重写是一项繁重的工作。在真正深入挖掘并理解问题所在之前重写是非常愚蠢的。或许半小时内就能修好……? (9认同)