在drawRect方法中绘制圆角比使用CALayer的setCornerRadius方法更快吗?

9

在2011年的WWDC视频会议121中,为了提高UI性能,演讲者选择使用UIBezierPath在drawRect:中绘制圆角,而不是直接在图层上设置圆角半径。

为什么使用UIBezierPath进行绘制一定更快?因为drawRect:发生在软件中,可能也很慢。


我自己也想知道这个。 - khanh.tran.vinh
在我看来,绘图是在硬件中进行的,drawRect会建立显示列表,然后将其传递给硬件进行渲染。 - AlexWien
1个回答

23

简短回答:在没有性能问题之前,最好使用CALayer的cornerRadius。

长话短说:

我们首先需要区分“绘画”和“合成”。

iOS上的绘画是将纹理用像素填充的简单行为(一个受CPU限制的任务)。合成是将所有这些纹理压平成一个单独的帧以打印到屏幕上的操作(一个受GPU限制的任务)。一般来说,在滚动或动画时,你主要会消耗GPU资源,这是好事,因为像将所有像素向下移动一个像素之类的操作对于GPU而言轻而易举。

-drawRect: 是纯粹的绘图,使用CPU填充纹理。CALayer的cornerRadius是在合成阶段完成的,会给GPU带来压力。

使用-drawRect: 有很高的初始成本(它可能比一个帧还要长),并且内存使用量相当大,但之后滚动非常流畅(现在它只是像任何其他纹理一样的纹理)。使用CALayer的cornerRadius创建一堆具有圆角的视图非常快,但一旦你拥有超过十几个视图,你就可以忘记滚动速度了(因为GPU不仅需要执行正常的滚动任务,还需要不断地添加圆角到你的视图中)。

但是不要轻信我的话,看一些数字数据。我改编了Florian Kugler的基准测试,并在运行iOS 6.1.3的iPhone 4S上进行了测试。我测量了在1/60秒内最多可以创建多少个视图,然后测量了在帧率低于60fps之前可以动画显示多少个视图。换句话说:初始成本与帧速成本的对比。

                                        | -drawRect:     | CALayer’s cornerRadus
16.6ms内渲染的最大视图数             |     5 views    |     110 views
以60fps动画显示的最大视图数            |    ~400 views   |     12 views

(请注意,当500个-drawRect:视图使用太多内存时,应用程序会被终止)

归根结底,在我的项目中,我倾向于尽可能多地使用CALayer的cornerRadius。 我很少需要具有圆角的几个视图,并且-drawRect:具有太大的初始性能损失。 而为了使角落变圆而子类化一个视图只是一种非常讨厌的方法。

无论您选择哪种方法,请确保测量和关注您的应用程序的流畅性和响应能力,并据此做出相应的反应。


你能否评论其他类似的东西,比如阴影、遮罩、文本大小和渲染等? 这些操作是在 CPU 或 GPU 上完成的,或者在什么情况下应考虑使用 drawRect: vs CALayer/UIKit 属性? - Evil Nodoer
前面链接的帖子(http://floriankugler.com/blog/2013/5/24/layer-trees-vs-flat-drawing-graphics-performance-across-ios-device-generations)主要是关于测量drawRect和标准图层之间的性能。除此之外,我没有特别的反馈,只是关于阴影的问题。有很多处理阴影的方法(手动绘制、CALayer阴影、CALayer阴影+光栅化、CALayer阴影+阴影路径),它们的性能不同: http://www.omnigroup.com/blog/entry/ipad_drop_shadow_performance_test - shusta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接