02总结--004--OpenGL 渲染流程汇总

[TOC]

TOC

前言

整个渲染流程比较多,这篇文章中只记录流程中的步骤,对于每个步骤中的细节不做过多说明,如果想要了解,可以参考笔者的其他文章。

一、整体流程

1.1 计算机图形渲染简单流程

计算机图形渲染简单流程
  1. Application 应用处理阶段:得到图元
  2. Geometry 几何处理阶段:处理图元,得到新图元
  3. Rasterization 光栅化阶段:图元转换为片元(像素)
  4. Pixel 像素处理阶段:处理片元(像素),得到位图

1.2 计算机层次流程图

image
  1. APP:图层树,UIView/CALayer
  2. Core Graphics:Core Graphics 是一个强大的二维图像绘制引擎,是 iOS 的核心图形库,常用的比如 CGRect 就定义在这个框架下
  3. Core Animation:在 iOS 上,几乎所有的东西都是通过 Core Animation 绘制出来,它的自由度更高,使用范围也更广
  4. Core Image:Core Image 是一个高性能的图像处理分析的框架,它拥有一系列现成的图像滤镜,能对已存在的图像进行高效的处理
  5. Metal:Metal 类似于 OpenGL ES,也是一套第三方标准,具体实现由苹果实现。Core Animation、Core Image、SceneKit、SpriteKit 等等渲染框架都是构建于 Metal 之上的。

二、CPU 阶段:Core Animation

2.1 Core Animation 层级图

Core Animation 层级图
  • Core Animation,它本质上可以理解为一个复合引擎,主要职责包含:渲染、构建和实现动画。

2.2 CALayer 的层级图

CALayer 的层级
  1. backgroundColor
  2. contents
  3. borderWidth / borderColor

【总结】

  1. 设置 layer.cornerRadius 只会设置 backgroundColor 和 border 的圆角,不会设置 contents
  2. 同时设置 layer.masksToBounds 才会设置 contents 的圆角。(对应view中的clipsToBounds属性)

2.3 UIView 和 CALayer 之间的关系

image

【核心关系】

  1. CALayer 是 UIView 的属性之一,负责渲染和动画,提供可视内容的呈现。
  2. UIView 提供了对 CALayer 部分功能的封装,同时也另外负责了交互事件的处理。

【区别】

  • 层级结构:我们对 UIView 的层级结构非常熟悉,由于每个 UIView 都对应 CALayer 负责页面的绘制,所以 CALayer 也具有相应的层级结构。
  • 部分效果的设置:因为 UIView 只对 CALayer 的部分功能进行了封装,而另一部分如圆角、阴影、边框等特效都需要通过调用 layer 属性来设置。
  • 点击事件:CALayer 不负责点击事件,所以不响应点击事件,而 UIView 会响应。
  • 继承关系:CALayer 继承自 NSObject,UIView 由于要负责交互事件,所以继承自 UIResponder。

2.4 Core Animation Pipeline 渲染流水线

image
  1. Handle Events:这个过程中会先处理点击事件,这个过程中有可能会需要改变页面的布局和界面层次。
  2. Commit Transaction:此时 app 会通过 CPU 处理显示内容的前置计算,比如布局计算、图片解码等任务,接下来会进行详细的讲解。之后将计算好的图层进行打包发给 Render Server
  3. Decode:打包好的图层被传输到 Render Server 之后,首先会进行解码。注意完成解码之后需要等待下一个 RunLoop 才会执行下一步 Draw Calls。
  4. Draw Calls:解码完成后,Core Animation 会调用下层渲染框架(比如 OpenGL 或者 Metal)的方法进行绘制,进而调用到 GPU。
  5. Render:这一阶段主要由 GPU 进行渲染。
  6. Display:显示阶段,需要等 render 结束的下一个 RunLoop 触发显示。

2.5 Core Animation 之 Commit Transaction 流程

image
  1. Layout:构建视图
  2. Display:绘制视图
  3. Prepare:Core Animation 额外工作,图片解码和转换
  4. Commit:图层打包发送到 Render Server

三、OpenGL 渲染阶段

3.1 OpenGL 渲染流程:蓝宝书

1. OpenGL 渲染流程图

OpenGL 渲染流程图

2. 单个流程解释

  • ①【必选】:准备阶段提供的数据,例如:位置、颜色、纹理等
  • ②【必选】:顶点着色器,执行大量的计算来计算顶点在屏幕上的位置,这里会用到变换矩阵的概念。也可能只作为传递着色器,仅仅是将数据复制并传递到下一个着色阶段;
  • ③【可选】:细分控制着色器,使用面片(patch)来描述一个物体的形状,增加几何图元的数量,使外观更加平顺;
  • ④【可选】:细分计算着色器,很明显,一个控制,控制那些几何图元需要做哪些设置,一个计算,针对这些要求做计算。例如:修改几何图元类型或者放弃所有的凸缘;
  • ⑤【可选】:几何着色,创建新的图元;
  • ⑥【必选】:也称为图元装配,将顶点与相关的几何图元之间组织起来,为后面的裁剪和光栅化做准备;
  • ⑦【必选】:调整图元,保证相关的像素不会在视口外绘制,这个过程是由OpenGL自动完成的;
  • ⑧【必选】:光栅化的作用就是产生片元(像素),光栅化意味着一个片元的生命伊始,可以将片元理解为“候选的像素”、“像素占位”;
  • ⑨【必选】:片元着色阶段,通过编程控制屏幕上显示颜色的阶段。通过纹理映射的方式对顶点处理阶段所计算的颜色进行补充。
    顶点着色:决定一个图元应该位于屏幕什么位置;
    片元着色:使用这些信息来决定片元的颜色应该是什么;
  • ⑩【必选】:逐片元的操作:独立片元处理阶段,这里会使用深度测试(z缓存)、模板测试来决定一个片元是否可见;
    如果一个片元通过了所激活的测试,既可以被绘制到帧缓存里面;
    如果一个片元开启了融混模式,则该片元的颜色会与该像素的颜色叠加,形成一个新的颜色值存入帧缓存中。

3. 流程总结

  1. 四个阶段

红宝书上给出的流程是三排,也就是将顶点数据准备阶段和顶点着色器阶段放在一起了,这里为了方便记忆,我将每一个职责单独分开,成了四个阶段。

  • 第一阶段:顶点数据准备阶段,对应流程①;
  • 第二阶段:产生图元阶段,对应流程②、③、④、⑤,这四个流程;
  • 第三阶段:处理图元,产生片元阶段,对应流程⑥、⑦、⑧,这三个流程;
  • 第四阶段:处理片元,渲染上屏阶段,对应流程⑨、⑩,这两个阶段;
  1. 可编程阶段
  • 第一阶段:提供顶点数据肯定是需要用户自己来完成的。
  • 第二阶段:在 OpenGL 部分,我们只能对顶点着色器进行修改,而细分着色器、几何着色器,都没有提供相关设置的接口,所以虽然他们是可编程的,但是在 OpenGL 部分,我们也无法处理他们。
  • 第四阶段:对片元的处理,颜色着色、混合等,是否满足各种测试,是否需要丢弃这些都是在第四阶段完成的。
  1. OpenGL 自动处理阶段
  • 第三阶段:在第二阶段我们提供了图元数据之后,顶点数据、顶点之间的连接方式、位置、纹理数据等,这些都会交给 OpenGL,由 OpenGL 来处理图元,并在光栅化阶段生成片元。

3.1 OpenGL 渲染架构:蓝宝书【补】

OpenGL 渲染架构
OpenGL 渲染架构名词解释

3.2 OpenGL 渲染原理:Render Server

Render Server
  1. GPU 收到 Command Buffer,包含图元 primitives 信息
  2. Tiler 开始工作:先通过顶点着色器 Vertex Shader 对顶点进行处理,更新图元信息
  3. 平铺过程:平铺生成 tile bucket 的几何图形,这一步会将图元信息转化为像素,之后将结果写入 Parameter Buffer 中
  4. Tiler 更新完所有的图元信息,或者 Parameter Buffer 已满,则会开始下一步
  5. Renderer 工作:将像素信息进行处理得到 bitmap,之后存入 Render Buffer
  6. Render Buffer 中存储有渲染好的 bitmap,供之后的 Display 操作使用

3.3 OpenGL 渲染原理:画家算法

画家算法
  • 由远及近绘制
  • 图层树中,最上层的离屏幕最近,最下层的离屏幕最远
  • 图层重叠时无法使用画家算法绘制

3.4 OpenGL 渲染过程:正常和离屏的简易流程

简易渲染流程
  • 正常流程:将内容渲染完成之后,不停地放入 Framebuffer 中,然后显示屏幕不断地从 Framebuffer 中读取内容,显示实时内容;
  • 离屏渲染:创建 Offscreenbuffer >> 将提前渲染好的内容放入其中 >> 等到合适的时机在将 OffScreenbuffer 中的内容进一步叠加、渲染 >> 最后将结果放入 Framebuffer 中;
  • 显示屏幕最后获取数据的来源都是 帧缓存Framebuffer;
  • Offscreenbuffer 只是一个临时存储渲染数据的地方

3.5 OpenGL 渲染过程:Layer的绘制【正常和离屏】

正常流程
  1. 绘制 sublayer1,绘制完了丢弃
  2. 绘制 sublayer2,绘制完了丢弃
  3. 绘制 sublayer3,绘制完了丢弃
离屏流程
  1. 绘制 sublayer1,存入离屏缓存区
  2. 绘制 sublayer2,存入离屏缓存区
  3. 绘制 sublayer3,存入离屏缓存区
  4. 从离屏缓存区取出 sublayer1
  5. 从离屏缓存区取出 sublayer2
  6. 从离屏缓存区取出 sublayer3
  7. 混合 -> 呈现

3.6 OpenGL 渲染案例:Mask渲染流程【离屏】

Mask渲染流程
  1. 渲染mask,存入离屏缓存区;
  2. 渲染layer,存入离屏缓存区;
  3. 读取离屏缓存区的数据,然后进行混合操作,将结果存入帧缓存区;
  4. 等待下一次 runloop到来,显示到屏幕上;

3.7 OpenGL 渲染案例:UIBlurEffectView渲染流程【离屏】

UIBlurEffectView渲染流程
  1. Content:渲染内容
  2. capture content:捕获内容
  3. Horizontal Blur:水平模糊
  4. Vertical Blur:垂直模糊
  5. Compositing Pass:合并过程
  6. 合并完成之后将结果存入帧缓存区,等待下一次 runloop到来,显示到屏幕上

四、显示阶段:帧缓存区到显示、帧缓存区的切换

4.1 位图显示到屏幕流程

位图显示到屏幕流程
  1. 视频控制器(Video Controller)会读取帧缓冲器中的信息
  2. 经过数模转换传递给显示器(Monitor)
光束扫描
  1. 电子束会从屏幕的左上角开始逐行扫描,屏幕上的每个点的图像信息都从帧缓冲器中读取的位图

4.2 撕裂流程(需要画图)

撕裂流程
  1. 新的位图还没有渲染好;
  2. 电子束从头开始新的一帧的扫描;
  3. 电子束扫描到一半的时候,新的位图渲染好了;
  4. 电子束扫描另一半内容,这一半内容显示的是新的位图,所以造成撕裂现象

4.3 垂直同步 Vsync + 双缓冲机制 Double Buffering

垂直同步信号(vertical synchronisation,Vsync)相当于给帧缓存区加锁:当电子束完成一帧的扫描,将要从头开始扫描时,就会发出一个垂直同步信号,只有当视频控制器接收到 Vsync 之后,才会将帧缓存区中的位图更新为下一帧。

双缓冲机制
  1. 视频控制器读取 primary surface 缓存区里的内容进行显示;
  2. 渲染服务(render server)将内容渲染存入 back Buffer 后台缓存区里;
  3. 等下一次呈现指令到来时,交换缓存区
    1. 视频控制器读取 back Buffer 的内容
    2. 渲染服务将内容存入 primary surface

4.4 掉帧流程

掉帧流程

【说明】

  • CPU 表示 CPU 处理需要的时长
  • GPU 表示 GPU 处理需要的时长
  • 两个 Vsync 之间的长度表示两帧之间的间隔时长
  • 流程中不管是处理B时GPU耗时更多,还是处理A时CPU耗时更多,在 CPU+GPU 的耗时时长都是大于了两次同步信号之间的间隔时长,所以都会造成掉帧

【过程】

  1. 第一帧显示 A
  2. 第二帧显示 A(第一个 Vsync 到来,但是 B 还没有渲染完成,因为同步问题,所以这一次不会更新帧缓存区里的位图,也即当前帧缓存区里面的位图还是 A);
  3. 第三帧显示 B(CPU+GPU过程 A 没有处理完成)
  4. 第四帧显示 A(A已经处理完成了)

4.5 三缓冲 Triple Buffering

三缓冲
  • 双缓存区过程中,CPU和GPU 在处理 B 到 A 的过程中,各自有一定的闲置状态
  • 三缓存区,就是充分利用 CPU和GPU 的性能,降低掉帧的概率

参考:iOS Rendering 渲染全解析(长文干货)

你可能感兴趣的:(02总结--004--OpenGL 渲染流程汇总)