OpenGL02 - 离屏渲染

639 阅读3分钟

一. 离屏渲染介绍

正常图片的渲染流程基本是:CPU计算完图片布局等数据,发送给GPU,GPU进行渲染,渲染结果发送到帧缓冲区,显示器从帧缓冲区读取内容,进行显示。简单的示例如下图:

离屏渲染的流程相对于正常的渲染流程会需要一些额外的操作,所以当前的帧缓冲区不够了,需要申请一个新的渲染缓冲区,来对渲染中间状态的保存,下文会对这个进行详细讲解。离屏缓冲区大小有限制,最大为屏幕大小的2.5倍。

在开发过程中,我们可以通过模拟器,选择Color off-screen Rendered来标记离屏渲染区域。

二. 经典场景解析

离屏渲染常见场景如下:

  • 圆角,对layer进行裁剪 (layer.masksToBounds / view.clipsToBounds)
  • 使用了mask的layer(layer.mask)
  • 毛玻璃效果
  • 视图子view之间设置了不同的透明度
  • 增加投影
  • 开启光栅化 (layer.shouldRasterize)
  • layer添加文字

本文接下来会选择其中几种情况进行详细说明。

1. 圆角处理

UIImageView为例,我们想要圆角,一般实现如下:

   UIImageView *imageView = [[UIImageView alloc]init];
   imageView.layer.cornerRadius = 50;
   imageView.layer.masksToBounds = YES;

不设置masksToBounds, 不会发生离屏渲染,但是设置了,是不是就会一定产生离屏渲染?其实不一定。当我们仅显示图片,不设置背景颜色或者border的宽度的时候,并不会触发离屏渲染。

当我们设置了border的宽度为1的时候,就触发了离屏渲染:

当我们设置backgroudColor的时候,也会触发离屏渲染:

这个是为什么勒?其实UIImageView的图层其实可以分为三部分:background colorcontentsborder。如下图所示:

设置cornerRadius的时候,根据苹果文档的描述:

它仅作用于background colorborder,只有设置了masksToBounds(或者view的clipsToBounds)才会对content也进行圆角处理。

设置了masksToBounds为YES后,当仅显示图片,没有设置background color以及border。那么圆角的操作,只需要对contents进行处理,一次性就处理完了。不需要额外的缓冲区存储中间的内容,所以不会产生离屏渲染。 当设置了背景色或者border后,那么就需要对两个图层:content以及background Color(border)进行圆角操作,我们就需要额外的一个缓冲区临时存储每个图层的圆角操作结果,然后等都处理完了,再合并在一起,显示到屏幕上。这就发生了离屏渲染。

2. mask操作

当理解了设置圆角产生离屏渲染的原理后,理解mask,就会很简单了。

上图中1和2的结果都需要临时保存在离屏缓冲区中,然后合并生成最后的结果图。

3. shouldRasterize

开发过程中,layer.shouldRasterize为YES,会把图层渲染为一个屏幕之外的为徒,然后将这个位图缓存起来,便于复用。设置了shouldRasterize,需要注意也需要设置rasterizationScale。使用shouldRasterize的几个建议:

  • 如果layer不能复用,则没必要打开光栅化
  • 如果layer不是静态的,需要频繁被修改,例如处于动画中,则也不需要打开光栅化
  • 离屏渲染缓存内容有时间限制,如果缓存内容100ms内没有被使用的话,就会被丢弃,那么就无法进行复用了
  • 缓存的内容,如果超过屏幕大小的2.5倍,则光栅化也会失效

三. 圆角替代方案

如何不触发离屏渲染,实现圆角,我们可以使用贝塞尔曲线重新,对图片进行重新绘制,关键代码如下:

    UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
    [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:5] addClip];
    [image drawInRect:rect];
    roundImage = UIGraphicsGetImageFromCurrentImageContext(); // 圆角图片
    UIGraphicsEndImageContext();

参考链接

swift.diagon.me/shouldRaste…