面试题 02.08 环路检测
给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点
为了让 UI 显得更好看 应用中有非常多的地方会使用的圆角的图片 但是一般的系统 layer 圆角设置方式通常会导致严重的离屏渲染问题, 尤其是在列表中 本文主要是对seedante的解决方案总结
来自 Demystify and eliminate hitches in the render phase
谈离屏渲染之前, 首先要明白什么是屏内渲染.
App
-> GPU(frame buffer
) -> 屏幕
Framebuffer 是 RAM 的一部分,包含一份能驱动视图显示的位图. frame buffer 由 GPU
掌管.
obj 中国有一篇文章专门提到了离屏渲染的问题,文章中提到 直接将图层合成到当前显示屏幕的帧缓冲区中,比先在屏幕外面创建新的缓冲区,然后渲染到纹理中,最后将结果渲染到当前显示屏幕的帧缓冲区中,性能要好的多
性能损耗的点:
其他
iOS核心动画高级技巧中有表示: 当图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制时,屏幕外渲染就被唤起了。屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示之前在一个屏幕外上下文中被渲染(不论CPU还是GPU).
上面的未预合成 ?
猜测有 圆角, 阴影, group opacity , mask, blurEffect等效果 都无法直接生成.
最简单的一个解释阴影 (摘自 关于iOS离屏渲染的深入研究)
shadow,其原因在于,虽然layer本身是一块矩形区域,但是阴影默认是作用在其中”非透明区域“的,而且需要显示在所有layer内容的下方,因此根据画家算法必须被渲染在先。但矛盾在于此时阴影的本体(layer和其子layer)都还没有被组合到一起,怎么可能在第一步就画出只有完成最后一步之后才能知道的形状呢?这样一来又只能另外申请一块内存,把本体内容都先画好,再根据渲染结果的形状,添加阴影到frame buffer,最后把内容画上去(这只是我的猜测,实际情况可能更复杂)。不过如果我们能够预先告诉CoreAnimation(通过shadowPath属性)阴影的几何形状,那么阴影当然可以先被独立渲染出来,不需要依赖layer本体,也就不再需要离屏渲染了。
关于圆角的画画比喻,
按照图层树 从父 layer 便利 子layer 的方式处理, 一层一层的绘制到显卡 缓冲区上, 绘制后 原来那层的 layer数据 比如 圆角范围, alpha 都不能访问了, 下一层 layer也就不知道 我自己的内容有没有超出 父视图的圆角区域 ; 所以如果父视图有 圆角, 会开辟缓冲区, 不关心父视图的圆角, 先将视图最终效果画完 再设置圆角.
就是画到缓冲图的 父视图仅仅是 最终的像素数据, 子视图再绘制的时候, 能看到的只是缓冲区最终的 像素而已
GPU版本的离屏渲染 ->
更改 mask,shadow,Group opacity,edge antialiasing
CPU 版本的离屏渲染 ->
使用 Core Graphic 里面的绘制 API 也会触发离屏渲染 ,比如 drawRect:
系统圆角的离屏渲染
view.layer.cornerRadius = aRadius;
view.layer.maskToBounds = true
这里需要注意的是, 仅仅只是设置 layer.cornerRadius
不会有离屏渲染问题, 但是要是和maskToBounds
一起配合layer.cornerRadius
使用就会有问题.
如图
优化是使用CoreGraphic
圆角绘制与缓存(memory,disk cache) 配合使用
比如下面:
视情况选择以上其中一种!!!
Setting the radius to a value greater than 0.0 causes the layer to begin drawing rounded corners on its background. By default,the corner radius does not apply to the image in the layer's contents property ;it applies only to the background color and border of the layer . However, setting the masksToBounds property to YES causes the content to be clipped to the rounded corners.
只对前景框和背景色起作用 , 很重要
如果仅仅只是让 Layer的背景色 设置圆角, 不处理 content 的内容是不会有离屏渲染的
UTextField 自带圆角效果
UILabel UITextView 首先保证contents 呈现透明的背景色 ,只需要设置 layer 的 backgroundColor ,再加上 cornerRadius 就 OK
阴影设置 shadowPath ,默认为 nil
我个人觉得@已惊雷的说法有部分道理,但不是主要的原因,在WWDC2014上面的Session https://developer.apple.com/videos/play/wwdc2014/419/ 讲过做mask操作确实会比普通的rendering多一些步骤,所以会增加Draw Call的数量。更重要的是Andy Matuschak(UIKit早期成员)在一篇回复中说的:It’s expensive for the GPU to switch contexts from on-screen to off-screen drawing (it must flush its pipelines and barrier), so for simple drawing operations, the setup cost may be greater than the total cost of doing the drawing in CPU via e.g. CoreGraphics would have been. 最耗费性能的是flush its pipelines and barrier,就如在OpenGL中实际调用Draw Call只是将命令放入Command Buffer队列,真正执行是当你调用glFlush一样。所以我觉得离屏渲染导致卡顿的主要原因是有更多的glFlush操作 摘自 OpenGL ES中的offscreen rendering与iOS中的offscreening rendering