我在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];
}
...从最近的触摸输入计算车轮的新旋转。这里已经存在一个性能问题,我在一个单独的线程中追求:iOS Core-Animation: Performance issues with CATransaction / Interpolating transform matrices
此例程还会检查滚轮是否已完成另外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];
}
}
现在对于实际的大小调整代码,请在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.];
}
我想知道问题是否与多个transform.scale操作排队的开销有关 - 每个按钮调整大小需要一整秒才能完成,如果我快速旋转滚轮,我可能每秒旋转几转;这意味着每个按钮每秒调整大小24次。
**创建按钮的图层**
我猜的最后一块拼图是看看按钮的内容层。但我试过了
contentsLayer.setRasterize = YES;
应该有效地将其存储为位图。所以设置代码实际上是动态调整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;
}
正如你所看到的,我正在尝试一切可能的方法,我正在尝试两种方法来制作形状,另外我正在尝试切换.shouldRasterize
**妥协UI设计以获得可容忍的帧速率**
编辑:现在我已经尝试禁用动态调整大小行为,直到轮子进入新位置,并设置wheel.setRasterize = YES。所以它有效地旋转了一个预先渲染的UIView(它无疑占据了大部分屏幕)在手指下面(它很高兴地做了~~ 60fps),直到轮子停下来,此时它执行这个滞后的大小调整动画( @&LT; 20FPS)
。虽然这给出了可以容忍的结果,但我不得不以这种方式牺牲我的UI设计。我确信我一定做错了。
编辑:我刚试过手动调整按钮大小的实验;即在每个按钮中放置一个显示链接回调,并动态计算该给定帧的预期大小,使用与我对方向盘相同的CATransaction显式禁用动画,设置新的变换矩阵(从预期大小生成的缩放变换)。添加到此我已设置按钮内容层shouldRasterize = YES。所以它应该简单地将每帧12个位图缩放到一个本身正在旋转的UIView上。令人惊讶的是,这已经慢了,它甚至会让模拟器停下来。它肯定比使用核心动画的动画功能自动慢10倍。
答案 0 :(得分:15)
我没有开发iPad应用程序的经验,但我确实有一些优化视频游戏。所以,我无法给出确切的答案,但我想提供一些优化建议。
不要猜。简介。
您似乎在尝试进行更改而不分析代码。改变一些可疑代码并交叉手指并不真正奏效。您应该通过检查每项任务的long
采用方式以及运行所需often
的方式来描述您的代码。尝试分解您的任务并将分析代码放在衡量time
和frequency
的位置。如果您可以测量每个任务使用memory
的数量,或者有多少其他系统资源,那就更好了。根据证据找出你的瓶颈,而不是你的感觉。
对于您的具体问题,您认为当您的调整大小工作开始时,程序会变得非常慢。但是,您确定吗?它可能是别的东西。在得到实际的分析数据之前,我们不确定。
最小化问题区域并在进行更改前衡量实际效果。
分析后,您有一个候选人可以解决您的瓶颈问题。如果您仍然可以将原因拆分为几个小任务,那么请执行此操作并对其进行分析,直到您无法再将其拆分为止。然后,尝试通过重复运行几千次来测量它们的精确性能。这很重要,因为您需要在进行任何更改之前了解性能(速度和空间),以便将其与未来的更改进行比较。
针对您的具体问题,如果确实需要调整大小,请尝试检查其执行方式。执行一次调整大小调用需要多长时间?您需要多长时间调整一次工作来完成工作?
改进它。怎么样?这取决于。
现在,您遇到了有问题的代码块及其当前性能。你现在必须改进它。怎么样?好吧,这真的取决于问题所在。您可以搜索better algorithms
,如果您可以延迟计算直到确实需要执行,则可以执行lazy fetching
,或者如果您执行相同的操作,则可以通过缓存某些数据来执行over-eager evaluation
太频繁了。你越了解原因,就越容易改进它。
针对您的具体问题,可能只是OS ui功能的限制。通常,调整大小按钮不仅仅是调整按钮本身,还会invalidates
整个区域或其父窗口小部件,以便可以正确呈现每个UI内容。调整大小按钮可能很昂贵,如果是这种情况,您可以通过简单地使用基于图像的方法而不是基于OS ui的系统来解决问题。如果OS API中的图像操作不够,您可以尝试使用OpenGL。但是,我们再次知道,直到我们真正尝试它们并profile
这些替代品。祝好运! :)
答案 1 :(得分:6)
在没有阴影的情况下尝试它,看看是否能提高性能。我想它会大大改善它。然后我会研究使用CALayer的阴影路径来渲染阴影。这将大大提高阴影渲染性能。
来自去年WWDC的Apple核心动画视频有很多关于提高核心动画性能的绝佳信息。
顺便说一下,我现在正在制作更复杂的动画,即使在较旧的iPhone 3G上也能很好地工作。硬件/软件非常强大。
答案 2 :(得分:1)
您应该在OpenGL中重新执行此操作
答案 3 :(得分:1)
你是否在你的图层上使用阴影这是性能问题的原因,那么你有2个选项AFAIK: - 以这种方式设置shadowPath,CA不必每次都计算它 - 删除阴影并使用图像替换
答案 4 :(得分:0)
我知道这个问题很老,但仍然是最新的。 CoreAnimation (CA)只是 OpenGL 的包装 - 或者可能围绕 Metal 。实际上,图层是在矩形上绘制的,并且动画是使用3D变换表示的。由于所有这些都由GPU处理,它应该超快......但它不是。整个CA子系统看起来相当复杂,在AppKit / UIKit和3D世界之间进行翻译比看起来更难(如果你曾试图自己编写这样的包装器,你知道它有多难)。对于程序员来说,CA提供了一个超级简单易用的界面,但这种简单性需要付出代价。到目前为止,我尝试优化非常慢的CA都是徒劳的;你可以加快一点速度,但在某些时候你必须重新考虑你的方法:要么CA足够快就能完成你的工作,要么你需要停止使用CA并使用经典的视图绘图自己实现所有动画(如果是CPU)可以处理这个问题)或者使用3D API自己实现动画(然后GPU会这样做),在这种情况下,您可以决定3D世界如何与应用程序的其他部分进行交互;价格要写的代码要多得多,要使用的API要复杂得多,但结果最终会说明一切。
尽管如此,我还想提供一些关于加速CA的一般提示:
CALayer
,然后将图层放置在您需要的位置。监视何时需要更新它们(例如,如果对象的大小已更改),则重新绘制它们一次并再次缓存结果。 CA本身也会尝试缓存结果,但如果您自己控制缓存,则会获得更好的结果。NSView
不包含其父级所在的区域,请创建自定义子类并覆盖isOpaque
以返回YES
。同样适用于UIView
和图层,父母和兄弟姐妹都不会透过这些图层,但只需将opaque
属性设置为YES
即可。如果这些都没有帮助,那么你正在将CA推向极限,你可能需要用其他东西替换它。