iOS 原生渲染与 Flutter 有什么区别 (上)

语言: CN / TW / HK

theme: smartblue

「这是我参与2022首次更文挑战的第19天,活动详情查看:2022首次更文挑战」。

前言

今天,我们在这篇文章中来聊聊渲染原理以及iOS原生是如何渲染的。

iOS 原生渲染与 Flutter 有什么区别

渲染原理

首先我们说说渲染原理,对于我们看到的手机界面,都是使用CPU + GPU运算绘制出来的,CPUGPU在设计的的初始时职责就有所不同,CPU拥有强大的运算能力,是整个系统的控制核心,通常会帮助计算显示内容,GPU是专门进行绘图运算工作的处理器,并行计算能力更强,能够通过计算将图形结果显示在屏幕上。我们通常说的渲染也就是内存中的图形数据再经过计算和转换,最终呈现到屏幕上的过程。

在渲染过程中,CPU则处理渲染内容的计算,如视图创建、计算布局、图片解码等,内容计算完成后,再传输给 GPU 进行渲染。

GPU 主要是将 3D 坐标转化成 2D 坐标 继而再转成实际像素,具体实现可以分为顶点着色器(确定形状的点)、形状装配(确定形状的线)、几何着色器(确定三角形个数)、光栅化(确定屏幕像素点)、片段着色器(对像素点着色)、测试与混合(检查深度和透明度进行混合)六个阶段。

原生渲染

原生渲染的层级可以分为以下几个层级: Untitled-2022-02-20-2256.png UIKitCore AnimationOpenGL ESGraphics Hardware

  • UIKit:UI基础库,用来满足我们上层开发;
  • Core Animation:负责图形渲染与动画的基础设施,Core Animation将大部分实际的绘图任务交给了图形硬件来处理。
  • OpenGL ES:OpenGL 的子集,一套专门处理GPU绘制的API。
  • Core Graphics: 基于 Quartz 高级绘图引擎。它提供了具有无与伦比的输出保真度的低级别轻量级 2D 渲染。您可以使用此框架来处理基于路径的绘图,转换,颜色管理,离屏渲染,图案,渐变和阴影,图像数据管理,图像创建和图像遮罩以及 PDF 文档创建,显示和分析。
  • Graphics Hardware: 图形硬件,

原生界面更新渲染的流程,可以分为以下流程。

第一步,更新视图树,同步更新图层树。当在操作 UI 时 如果改变了视图Frame或者更新了 UIView / CALayer的层级时,或者手动调用了 UIView / CALayersetNeedsLayout / setNeedsDisplay方法后,在此过程中 app 可能需要更新 视图树,相应地,图层树 也会被更新;

第二步,CPU 计算要显示的内容,包括视图创建(设置 Layer 的属性)、布局计算、视图绘制(创建 Layer 的 Backing Image)、图像解码转换。当 runloopBeforeWaiting 和 Exit状态时,会通知注册的监听,然后对图层打包,打完包后,将打包数据发送给一个独立负责渲染的进程 Render Server

第三步,数据到达 Render Server 后会被反序列化,得到图层树,按照图层树中图层顺序、RGBA 值、图层 frame 过滤图层中被遮挡的部分,过滤后将图层树转成渲染树,渲染树的信息会转给 OpenGL ES/Metal。前面 CPU 所处理的这些事情统称为 Commit Transaction

第四步Render Server 会调用 GPUGPU 开始进行前面提到的顶点着色器、形状装配、几何着色器、光栅化、片段着色器、测试与混合六个阶段。完成这六个阶段的工作后,再将 CPUGPU 计算后的数据显示在屏幕的每个像素点上。整个渲染过程,如下图所示: Render.png

如上图所示,CPU 处理完渲染内容会输入到 Render Server 中,经图层树和渲染树的转换,通过 OpenGL 接口提供给 GPUGPU 处理完后在屏幕上显示。渲染过程中 Commit Trasaction 的布局计算会重载视图 layoutSubviews 方法,以及执行 addSubview 方法来添加视图。视图绘制会重载视图的 drawRect 方法。这几个方法都是 iOS 开发中常用的。移动视图位置、删除视图、隐藏或显示视图、调用 setNeedsDisplaysetNeedsDisplayInRect 方法,都会触发界面更新,执行渲染流程。

Render Server

Render Server六个阶段如下: 1. 顶点着色器(Vertex Shader)。该阶段的输入是 顶点数据(Vertex Data),比如以数组的形式传递 3 个 3D 坐标用来表示一个三角形。顶点数据是一系列顶点的集合。顶点着色器主要的目的是把 3D 坐标转为另一种 3D 坐标,同时顶点着色器可以对顶点属性进行一些基本处理。

  1. 形状(图元)装配(Shape Assembly)。该阶段将顶点着色器输出的所有顶点作为输入,并将所有的点装配成指定图元的形状。图中则是一个三角形。图元(Primitive) 用于表示如何渲染顶点数据,如:点、线、三角形。

  2. 几何着色器(Geometry Shader)。该阶段把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。例子中,它生成了另一个三角形。

  3. 光栅化(Rasterization)。该阶段会把图元映射为最终屏幕上相应的像素,生成片段。片段(Fragment) 是渲染一个像素所需要的所有数据。

  4. 片段着色器(Fragment Shader)。该阶段首先会对输入的片段进行 裁切(Clipping)。裁切会丢弃超出视图以外的所有像素,用来提升执行效率。

  5. 测试与混合(Tests and Blending)。该阶段会检测片段的对应的深度值(z 坐标),判断这个像素位于其它物体的前面还是后面,决定是否应该丢弃。此外,该阶段还会检查 alpha 值( alpha 值定义了一个物体的透明度),从而对物体进行混合。因此,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同。

Render Server

写在最后

本章着重聊了聊渲染的原理以及iOS原生渲染的过程,那么在下一篇文章中,我们在聊聊目前火热的Flutter它的渲染流程是如何的,以及它跟原生渲染还有一些其他的前端渲染有何区别。敬请期待!