在每个 App
中如果想要实现界面的精美,多数情况下我们需要对相关控件进行分装,采用CocoaPods
进行插件化管理。
但是我们在程序里的空间是怎么显示在屏幕上的,下面将初略的讲述屏幕上显示控件的过程。
下面给出整理的图形绘制思维导图:
使用 Xmind
来展示博客讲述 App
控件在屏幕上显示的思路和相关知识点的整理思路,具体思路看下图:
屏幕显示图像的原理
记得第一台电视机是最老式的黑白电视机,上面具备天线、需要手动转动旋转调台那种,目前这种电视📺很少出现额。
上面那种老式的电视机画面的显示就是采用 CRT
显示,显示的原理如下图:
【图片来至于网络】
其原理:CRT
按照上面图片展示的那样,从上到下进行逐行扫描,当扫描完成就会显示一整张图像,随后电子枪回到原来的位置从头开始进行图像素材的扫描。具体的过程当显示器扫完一行换到新的一行,显示器就会发出一个水平信号:HSync,当显示器扫完整张的图片后电子进行复位,开始下一次的重新扫描,显示器也会发出一个新的垂直信号:VSync
我们
App
在屏幕上的刷新的频率就是VSync
信号产生的频率,所以我们在解决界面卡顿的过程就要根据VSync
的基础上进行相关的完善和优化。目前我们一般是采取双缓冲的机制,
GPU
会预先缓冲好一帧存放在缓冲中,让视频控制器读取。当下一阵渲染好,就直接把视频控制器的指针指向第二个缓冲器 : 这样做的好处就是可以大大提高缓冲的效率问题。
👉🏿
采用双缓存可能会引入一个问题就是屏幕内容显示一半, GPU
将新的内容提交到缓存区对其进行交换,视频控制器把新的下半段的显示在屏幕上
可能形成画面撕裂。
解决方式:
GPU
引入垂直同步(V-Sync)
当开启垂直同步后,GPU
会等待VSync
信号发出,才会对缓存的内容进行更新,这样就可以是画面更加流畅,减少延迟iOS 设备采用双缓存 + 垂直同步; Android采用的三缓存 + 垂直同步
在一些游戏中我们经常会见到一些场景如下:
【图片来至于网络】
CPU 和 GPU 的工作原理
CPU 工作原理
CPU
(中央处理器,Central Processing Unit
)主要是解释计算机中的指令,处理计算机软件中的数据。CPU
内部存储器和输入/输出设备是计算机的三大核心组件。
CPU
目前很大一部分是采用 冯·诺依曼结构,在工作的过程之中其过程一般可以分为四个阶段:提取、解码、执行和调回
- 提取:
- 解码:
- 执行:
- 调回:
【图片来至于网络】
GPU 工作原理
GPU
(图形处理器,Graphics Processing Unit
)主要是进行绘图运算的微处理器,对图像进行处理和相关显示绘制工作
后面随着 OpenGL APL
和 DirectX
功能的出现,图形处理器可以增加可编程着色的能力,目前我们处理在屏幕上显示的任何像素,也可以在纹理在屏幕上显示前和每一个几何顶点在投射到屏幕前都可以进行相关的处理工作
iOS 绘制像素在屏幕上
App绘制像素过程分析
下面绘制像素在屏幕上的原理图:
【图片来至于网络】
iOS
界面上的内容大多是通过 Core Animation
来完成绘制
在 Mac OS
大多是绕过 Core Animation
使用 Core Graphics
来进行绘制
手游行业中直接使用 OpenGL/OpenGL ES
来进行相关材质的内容绘制
Core Animation
大多使用 Core Graphics
来进行内容的渲染
GPU Driver
是直接和 GPU
进行相关交互的代码块,可以对对 GPU
直接操作
GPU
是专门为图形高并发计算量身定制的处理单元,优点:短时间可以实现图像浮点运算,耗电少,高效
App 绘制像素硬件分析
在图片显示的过程中,我们需要相关的 GPU
对图片生成 frame
的纹理,
我们在生成纹理的过程中会使用 VRAM
的空间,所以在生成过程是有限制的
把相关的数据从 CPU
上传输到 GPU
上 ——> 既是把内容从 RAM
上传到 VRAM
上, 如果上传的纹理太大就会对性能有所影响。
在文本显示的过程中,我们使用 GPU
进行文本的绘制比较复杂,这会促使 Core Text
和 Core Graphics
来合成生成一个文本,然后上传到 GPU
。
但我们在界面滑动过程中, CPU
只需要把相关的文本坐标发送到 GPU
就行, GPU
会根据相关的信息进行绘制。
APP绘制过程方式和细节讲解
合成:
在我们看到的所有的视图都是基于不同图片进行相关的合成而来
这里我么准守一切的图像皆纹理的概念,当把第二个纹理遮在第一个纹理上时, GPU
就会把第二个纹理合成到第一个纹理中
- 合成方法很多种:
- 如果两个纹理对其时:采用公式 ——>
R
(最终显示的色彩的值) =S
(上层纹理的相关的参数) +D
(下层纹理相关参数) * (1 -Sa
(上层纹理的透明度))
透明 VS 不透明:
当纹理完全不透明时目标像素就是纹理的值, GPU
就只会从目标像素直接取出纹理显示 不会做相关的操作
在加载一张没有 alpha
的图片进行显示,会被默认为 opaque
的属性是 YES
; 加载一张图片 alpha
= 100%, Core Animation
会认为是不为 100% 对其进行解析。
像素对其 VS 不重合在一起:
当两个 layer
完全重合在一起,这时就需要我们对其进行相关的计算layer
上所有的像素和屏幕上的像素完全对其,这就是 layer
像素对其;
- 像素对不齐主要原因:
- 缩放,在图像进行缩方时,
layer
和屏幕的像素很难对其 - 纹理的起点在在一个像素的边界
- 在像素不对其的情况下可能会要经过过多的运算, 在
Core Animation
和 模拟器中color misaligned images
的选项可以进行相关的调节
- 缩放,在图像进行缩方时,
离屏渲染:
离屏渲染会 合并/渲染 图层树一部分到一个新的缓存区,然后该缓冲区被渲染到屏幕上
- 离屏渲染也可能产生副作用。如果你正在直接或者间接的将mask应用到一个图层上,
Core Animation
为了应用这个mask
,会强制进行屏幕外渲染。这会对GPU
产生重负。通常情况下mask
只能被直接渲染到帧的缓冲区中(在屏幕内)Instrument
的Core Animation
工具有一个叫做Color Offscreen-Rendered Yellow
的选项,它会将已经被渲染到屏幕外缓冲区的区域标注为黄色(这个选项在模拟器中也可以用)。同时记得检查Color Hits Green and Misses Red
选项。绿色代表无论何时一个屏幕外缓冲区被复用,而红色代表当缓冲区被重新创建
rasterized layer
的缓冲区大小是有所限制的,大概是屏幕大小的两倍空间 屏幕外的缓冲区
CALayer:
GPU 测试的步骤:
要告诉你,如果是 GPU
限制了你的性能,你可以使用 OpenGL ES Driver instrument
。点击上面那个小的 i 按钮,配置一下,同时注意勾选 Device Utilization%
。现在,当你运行你的 app
时,你可以看到你 GPU
的负荷。如果这个值靠近 100%,那么你就需要把你工作的重心放在GPU
方面了。
Core Graphics & Quartz 2D:
Core Graphics
是功能强大的绘图框架
GPU加速下的图像处理:好的图像处理的开源第三方框架。
GPUImage:
ShaderToy:
Shaderific:
着色器的调试 :是测算帧渲染的时间,不是 1s 渲染多少帧
FPS是帧时间的倒数,帧渲染的时间:是帧开始处理到完全结束并且渲染到屏幕或者一张图片所花费的时间。
👉🏿 在移动端的 GPU
中有很多是 把指令集中起来,最后统一做一次渲染 ——> 在测试时需要测试整个过程,而不是一个中间过程
想要保持高的帧速 就要保证着色器比较低的周期数 (如果是 60帧每秒, 那么在完成一个周期就需要仅仅 16.67 毫秒)
降低着色器渲染周期方法:
消除条件逻辑 :条件逻辑有时是必须的,但有时可以做优化。着色器中使用
step()
函数变通避免逻辑的运用。减少依赖纹理的读取 :在片段的着色器取样时,如果纹理坐标不是以 varying 的形式传入,而是在片段着色器中进行计算,就会发生纹理依赖。(👉🏿想从附近的像素取样,而不是计算和片段着色器中相邻像素的偏差,最好在顶点着色器中进行计算,然后把结果以
varying
的方式传入片段着色器👈)让计算更加简单 : 在计算过程中如果使用简单的计算可以得到一个足够近似的值,就需要避免昂贵的计算方式(例如:
sin()
,cos()
,tan()
)如果有可能的话,把相关计算使用到顶点着色器。😁 😀😁😀😁 😀 顶点着色器是每个顶点运行一次,片段着色器是每一个像素运行一次
参考资料:
中央处理器
圖形處理器
不再安全的 OSSpinLock
GPU 加速下的图像处理
GPU 加速下的图像视觉
objccn.io-issue-3-1