OpenGL

003---离屛渲染

2020-07-16  本文已影响0人  清风烈酒2157

Offscreen Rendering 离屏渲染

离屏渲染具体过程

通常的渲染流程是这样的:

image.png

App 通过 CPUGPU 的合作,不停地将内容渲染完成放入 Framebuffer 帧缓冲器中,而显示屏幕不断地从 Framebuffer 中获取内容,显示实时的内容

而离屏渲染的流程是这样的:

image.png

与普通情况下 GPU 直接将渲染好的内容放入 Framebuffer 中不同,需要先额外创建离屏渲染缓冲区 Offscreen Buffer,将提前渲染好的内容放入其中,等到合适的时机再将 Offscreen Buffer 中的内容进一步叠加、渲染,完成后将结果切换到 Framebuffer 中。

从上面的流程来看,离屏渲染时由于 App 需要提前对部分内容进行额外的渲染并保存到 Offscreen Buffer,以及需要在必要时刻对 Offscreen BufferFramebuffer 进行内容切换,所以会需要更长的处理时间(实际上这两步关于 buffer 的切换代价都非常大)。

并且Offscreen Buffer 本身就需要额外的空间,大量的离屏渲染可能早能内存的过大压力。与此同时,Offscreen Buffer的总大小也有限,不能超过屏幕总像素的 2.5倍。

可见离屏渲染的开销非常大,一旦需要离屏渲染的内容过多,很容易造成掉帧的问题。所以大部分情况下,我们都应该尽量避免离屏渲染。

为什么使用离屏渲染

那么为什么要使用离屏渲染呢?主要是因为下面这两种原因:

image.png

如图所示,由于最终的内容是由两层渲染结果叠加,所以必须要利用额外的内存空间对中间的渲染结果进行保存,因此系统会默认触发离屏渲染。

image.png image.png

整个模糊过程分为多步:Pass 1 先渲染需要模糊的内容本身,Pass 2 对内容进行缩放,Pass 3 4 分别对上一步内容进行横纵方向的模糊操作,最后一步用模糊后的结果叠加合成,最终实现完整的模糊特效。

而第二种情况,为了复用提高效率而使用离屏渲染一般是主动的行为,是通过 CALayershouldRasterize 光栅化操作实现的。

shouldRasterize 光栅化

When the value of this property is YES, the layer is rendered as a bitmap in its local coordinate space and then composited to the destination with any other content.

不过使用光栅化的时候需要注意以下几点:

圆角的离屏渲染

通常来讲,设置了 layer 的圆角效果之后,会自动触发离屏渲染。但是究竟什么情况下设置圆角才会触发离屏渲染呢?

image.png

如上图所示,layer 由三层组成,我们设置圆角通常会首先像下面这行代码一样进行设置:

view.layer.cornerRadius = 2

根据 cornerRadius - Apple 的描述,上述代码只会默认设置 backgroundColorborder 的圆角,而不会设置 content 的圆角,除非同时设置了 layer.masksToBoundstrue(对应 UIViewclipsToBounds 属性):

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 true causes the content to be clipped to the rounded corners.

如果只是设置了 cornerRadius 而没有设置 masksToBounds,由于不需要叠加裁剪,此时是并不会触发离屏渲染的。而当设置了裁剪属性的时候,由于 masksToBounds 会对 layer 以及所有 subLayercontent 都进行裁剪,所以不得不触发离屏渲染。

view.layer.masksToBounds = true // 触发离屏渲染的原因

所以,Texture 也提出在没有必要使用圆角裁剪的时候,尽量不去触发离屏渲染而影响效率:

image.png

离屏渲染的具体逻辑

刚才说了圆角加上masksToBounds 的时候,因为 masksToBounds 会对 layer 上的所有内容进行裁剪,从而诱发了离屏渲染,那么这个过程具体是怎么回事呢,下面我们来仔细讲一下。

image.png image.png

而当我们设置了 cornerRadius 以及masksToBounds 进行圆角 + 裁剪时,如前文所述,masksToBounds 裁剪属性会应用到所有的 sublayer 上。这也就意味着所有的 sublayer 必须要重新被应用一次圆角+裁剪,这也就意味着所有的 sublayer 在第一次被绘制完之后,并不能立刻被丢弃,而必须要被保存在 Offscreen buffer 中等待下一轮圆角+裁剪,这也就诱发了离屏渲染,具体过程如下:

image.png

实际上不只是圆角+裁剪,如果设置了透明度+组透明layer.allowsGroupOpacity+layer.opacity),阴影属性(shadowOffset 等)都会产生类似的效果,因为组透明度、阴影都是和裁剪类似的,会作用与 layer 以及其所有 sublayer 上,这就导致必然会引起离屏渲染

避免圆角离屏渲染

除了尽量减少圆角裁剪的使用,还有什么别的办法可以避免圆角+裁剪引起的离屏渲染吗

由于刚才我们提到,圆角引起离屏渲染的本质是裁剪的叠加,导致 masksToBoundslayer 以及所有 sublayer 进行二次处理。那么我们只要避免使用 masksToBounds 进行二次处理,而是对所有的 sublayer 进行预处理,就可以只进行“画家算法”,用一次叠加就完成绘制。

那么可行的实现方法大概有下面几种:

触发离屏渲染原因的总结

不过,需要注意的是,重写 drawRect:方法并不会触发离屏渲染。前文中我们提到过,重写 drawRect: 会将 GPU 中的渲染操作转移到 CPU 中完成,并且需要额外开辟内存空间。但根据苹果工程师的说法,这和标准意义上的离屏渲染并不一样,在 Instrument 中开启 Color offscreen rendered yellow 调试时也会发现这并不会被判断为离屏渲染。

UIButton设置圆角离屛渲染问题

btn1.layer.cornerRadius = 50;
    btn1.backgroundColor = [UIColor blueColor];
    btn1.clipsToBounds = YES;
image.png

给Button设置圆角,如果Button的imageView设置圆角会发生离屛渲染.

btn1.imageView.layer.cornerRadius = 50;
image.png

如果只给Button的imageView设置圆角不会触发离屛渲染.

上一篇 下一篇

猜你喜欢

热点阅读