iOS 离屏渲染介绍

726 阅读3分钟

原文地址

相关概念

屏幕渲染有以下两种方式:

  • On-Screen Rendering

    前屏幕渲染,意思是 GPU 的渲染操作是在当前用于显示的屏幕缓冲区进行的。缓冲大小受限,一些复杂的操作可能无法完成。

  • Off-Screen Rendering

    离屏渲染,意思是 GPU 的渲染操作是在当前用于显示的屏幕缓冲区之外新开辟的一个缓冲区进行的。

离屏渲染性能问题

大量的离屏渲染会引发性能问题,离屏渲染过程中的两个操作的代价很高:

  • 需要创建额外的缓冲区。

  • 上下文切换。离屏渲染过程中需要先从 On-Screen 切换到 Off-Screen,在渲染完成后,需要将渲染结果从 Off-screen 切换到 On-screen。上下文切换需要付出昂贵的代价。

离屏渲染过程

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

App 通过 CPUGPU 的合作,不停的将内容渲染完成放入 Framebuffer 帧缓冲器中,而屏幕不断的从 Framebuffer 中获取并显示内容。
同正常情况下 GPU 直接将渲染好的内容直接放入 Framebuffer 中不同,离屏渲染需要先创建额外的离屏缓冲区 Offscreen Buffer,将提前渲染好的内容放入其中,等到合适的时机再将 Offscreen Buffer 中的内容进一步合成、渲染,完成后在切换到 Framebuffer 中。

什么情况下会触发离屏渲染

  • 有些特殊效果,无法一次合成,需要额外的 Offscreen Buffer 来保存渲染的中间态,所以不得不使用离屏渲染。

    • cornerRadius + clipsToBounds (圆角 + 裁切)
    • mask(遮罩)
    • shadow(阴影)
    • group opacity(不透明)
    • edge antialiasing (抗锯齿)
    • 渐变
  • 当图层比较复杂时,可以强制离屏渲染缓存那些图层,然后可以用缓存作为合成的结果放到屏幕上,从而提升效率。

    • shouldRasterize(光栅化)

**光栅化:**可以将绘制好的位图缓存下来,从而减少 GPU 压力。

如果合成内容超过100ms没有使用,就会从缓存中清除。
离屏渲染的空间也有限,超过2.5倍屏幕像素大小,也会触发离屏渲染。

通过下面代码开启光栅化:

view.layer.shouldRasterize = YES;
view.layer.rasterizationScale = [UIScreen mainScreen].scale;

另一种特殊的“离屏渲染”

按照之前的说法,如果将不在 GPU 的当前屏幕缓冲区中进行的渲染都称为离屏渲染,那么就还有另一种特殊的“离屏渲染”方式,即 CPU 渲染。

如果我们重写了 -drawRect 方法,并且使用任何 Core GraphicsAPI 进行了绘制操作,就涉及到了 CPU 渲染。

Core Graphics 通常是线程安全的,所以可以进行异步绘制,显示的时候再放回主线程,异步绘制的例子如下:

- (void)drawRect:(CGRect)rect {
     dispatch_async(backgroundQueue, ^{
         CGContextRef ctx = CGBitmapContextCreate(context);
         CGImageRef img = CGBitmapContextCreateImage(ctx);
         CFRelease(ctx);
         dispatch_async(mainQueue, ^{
             layer.contents = img;
         });
     });
  }

如何检测离屏渲染

可以使用 Xcode Debug 下的 Color Hits Green and Misses Red 和 Color Offscreen-Rendered Yellow 工具来检测离屏渲染。

  • Color Hits Green and Misses Red

如果开启光栅化,对应的渲染结果会被缓存,如果图层是绿色,就表示这些缓存会被复用,如果是红色就表示缓存会被重复创建,也就是说该处存在性能问题。

  • Color Offscreen-Rendered Yellow

开启后会把可能产生离屏渲染的图层高亮成黄色,也就是说黄色图层可能存在性能问题。

参考

1、juejin.cn/post/684490…


扫一扫关注公众号,get更多技术好文