二、屏幕卡顿 及 iOS中的渲染流程解析

简介: 屏幕卡顿是指图形图像的在显示时出现了撕裂(即图片错位显示)、掉帧(重复显示同一帧数据)等问题,导致用户能直观的从屏幕上看到的一种异常现象

屏幕卡顿


屏幕卡顿是指图形图像的在显示时出现了撕裂(即图片错位显示)、掉帧(重复显示同一帧数据)等问题,导致用户能直观的从屏幕上看到的一种异常现象

为什么会出现这种情况呢?下面就来详细解说下屏幕卡顿


【高频面试题】屏幕卡顿的原因


主要有以下三种原因


  • CPU和GPU在渲染的流水线中耗时过长,导致从缓存区获取位图显示时,下一帧的数据还没有准备好,获取的仍是上一帧的数据,产生掉帧现象,掉帧就会导致屏幕卡顿
  • 苹果官方针对屏幕撕裂问题,目前一直使用的方案是垂直同步+双缓存区,可以从根本上防止和解决屏幕撕裂,但是同时也导致了新的问题掉帧。虽然我们采用了双缓存区,但是我们并不能解决CPU和GPU处理图形图像的速度问题,导致屏幕在接收到垂直信号时,数据尚未准备好,缓存区仍是上一帧的数据,因此导致掉帧
  • 在垂直同步+双缓存区的方案上,再次进行优化,将双缓存区,改为三缓存区,这样其实也并不能从根本上解决掉帧的问题,只是比双缓存区掉帧的概率小了很多,仍有掉帧的可能性,对于用户而言,可能是无感知的。


接下来,详细解析下屏幕撕裂及掉帧问题


屏幕撕裂


如图所示,屏幕撕裂就类似于这样的情形

微信图片_20220512215232.png

在讲屏幕撕裂之前,首先说说屏幕是如何成像的,主要的流程是什么


屏幕成像过程


请看下面这张图,详细说明了屏幕成像的一个流程

微信图片_20220512215551.png

  • 将需要显示的图像,经由GPU渲染
  • 将渲染后的结果,存储到帧缓存区,帧缓存区中存储的格式是位图
  • 由视屏控制器从帧缓存区中读取位图,交由显示器,从左上角逐行扫描进行显示


屏幕撕裂的原因



  • 在屏幕显示图形图像的过程中,是不断从帧缓存区获取一帧一帧数据进行显示的,
  • 然后在渲染的过程中,帧缓存区中仍是旧的数据,屏幕拿到旧的数据去进行显示,
  • 在旧的数据没有读取完时 ,新的一帧数据处理好了,放入了缓存区,这时就会导致屏幕另一部分的显示是获取的新数据,从而导致屏幕上呈现图片不匹配,人物、景象等错位显示的情况。
    图示如下:

微信图片_20220512215803.png


苹果官方的解决方案



苹果官方针对屏幕撕裂现象,目前一直采用的是 垂直同步+双缓存,该方案是强制要求同步,且是以掉帧为代价的。

以下是垂直同步+双缓存的一个图解过程,如有描述错误的地方,欢迎留言指出

微信图片_20220512220201.png


  • 垂直同步:是指给帧缓冲加锁,当电子光束扫描的过程中,只有扫描完成了才会读取下一帧的数据,而不是只读取一部分
  • 双缓冲区:采用两个帧缓冲区用途GPU处理结果的存储,当屏幕显示其中一个缓存区内容时,另一个缓冲区继续等待下一个缓冲结果,两个缓冲区依次进行交替


掉帧


采用苹果的双缓冲区方案后,又会出现新的问题,掉帧。

什么是掉帧?简单来说就是 屏幕重复显示同一帧数据的情况就是掉帧

如图所示:当前屏幕显示的是A,在收到垂直信号后,CPU和GPU处理的B还没有准备好,此时,屏幕显示的仍然是A

微信图片_20220512220355.png

  • 针对掉帧情况,我们可以在苹果方案的基础上进行优化,即采用三缓存区,意味着,在屏幕显示时,后面还准备了3个数据用于显示。


iOS中的渲染


在iOS中渲染的整体流程如下所示

微信图片_20220512220727.png

  • App通过调用CoreGraphics、CoreAnimation、CoreImage等框架的接口触发图形渲染操作
  • CoreGraphics、CoreAnimation、CoreImage等框架将渲染交由OpenGL ES,由OpenGL ES来驱动GPU做渲染,最后显示到屏幕上
  • 由于OpenGL ES 是跨平台的,所以在他的实现中,是不能有任何窗口相关的代码,而是让各自的平台为OpenGL ES提供载体。在ios中,如果需要使用OpenGL ES,就是通过CoreAnimation提供窗口,让App可以去调用。


iOS中渲染框架总结



主要由以下六种框架,表格中已经说明了,就不再详细解释了

微信图片_20220512220910.png

View 与 CALayer 的关系



首先分别简单说下UIView和CALayer各自的作用


UIView


  • UIView属于UIKIt
  • 负责绘制图形和动画操作
  • 用于界面布局和子视图的管理
  • 处理用户的点击事件


CALayer


  • CALayer属于CoreAnimation
  • 只负责显示,且显示的是位图
  • CALayer既用于UIKit,也用于APPKit,
    ==> UIKit是iOS平台的渲染框架,APPKit是Mac OSX系统下的渲染框架,
    ==> 由于iOS和Mac两个系统的界面布局并不是一致的,iOS是基于多点触控的交互方式,而Mac OSX是基于鼠标键盘的交互方式,且分别在对应的框架中做了布局的操作,所以并不需要layer载体去布局,且不用迎合任何布局方式。


【面试题】UIView和CALayer的关系


  • UIView基于UIKit框架,可以处理用户触摸事件,并管理子视图
  • CALayer基于CoreAnimation,而CoreAnimation是基于QuartzCode的。所以CALayer只负责显示,不能处理用户的触摸事件
  • 从父类来说,CALayer继承的是NSObject,而UIView是直接继承自UIResponder的,所以UIVIew相比CALayer而言,只是多了事件处理功能,
  • 从底层来说,UIView属于UIKit的组件,而UIKit的组件到最后都会被分解成layer,存储到图层树中
  • 在应用层面来说,需要与用户交互时,使用UIView,不需要交互时,使用两者都可以


UIView和CALayer的渲染


下图可以说明view 和 layer之间是如何渲染的

微信图片_20220512221206.png


  • 界面触发的方式有两种
    ==> 通过loadView中子View的drawRect方法触发:会回调CoreAnimation中监听Runloop的BeforeWaiting的RunloopObserver,通过RunloopObserver来进一步调用CoreAnimation内部的CA::Transaction::commit(),进而一步步走到drawRect方法
    ==> 用户点击事件触发:唤醒Runloop,由source1处理(__IOHIDEventSystemClientQueueCallback),并且在下一个runloop里由source0转发给UIApplication(_UIApplicationHandleEventQueue),从而能通过source0里的事件队列来调用CoreAnimation内部的CA::Transaction::commit();方法,进而一步一步的调用drawRect
    最终都会走到CoreAnimation中的CA::Transaction::commit()方法,从而来触发UIView和CALayer的渲染
  • 这时,已经到了CoreAnimation的内部,即调用CA::Transaction::commit();来创建CATrasaction,然后进一步调用 CALayer drawInContext:()
  • 回调CALayer的Delegate(UIView),问UIView没有需要画的内容,即回调到drawRect:方法
  • 在drawRect:方法里可以通过CoreGraphics函数或UIKit中对CoreGraphics封装的方法进行画图操作
  • 将绘制好的位图交由CALayer,由OpenGL ES 传送到GPU的帧缓冲区
  • 等屏幕接收到垂直信号后,就读取帧缓冲区的数据,显示到屏幕上

CoreAnimation


在苹果官方的描述中,Render、Compose,and animate visual elements,CoreAnimationg中的动画只是一部分,它其实是一个复合引擎,主要的职责包括 渲染、构建和动画实现。

ios中CoreAnimation如图所示


微信图片_20220512221447.png微信图片_20220512221447.png

微信图片_20220512221447.png


  • ios中基于CoreAnimation构建的框架有两个:UIKit和APPKit
  • CoreAnimation 又是基于Metal 、CoreGraphics封装的


苹果为什么要基于UIView和CALayer提供两个平行的层级关系(UIKit 和APPKit)?


  • 职责分离,可以避免大量重复代码
  • 两个系统交互规则不一致,虽然功能上类似,但实现上有显著区别

CoreAnimation中的渲染流水线


CoreAnimation中渲染的流程如图所示

微信图片_20220512221748.png

主要分为两部分:

  • CoreAnimation部分
  • GPU部分


CoreAnimation部分


  • App处理UIView、UIButton等载体的事件,然后通过CPU完成对显示内容的计算,并将计算后的图层进行打包,在下一次runloop时,发送到渲染服务器
  • Render Server中主要对收到的准备显示的内容进行解码,然后执行OpenGL等相关程序,并调用GPU进行渲染
    ==> Render Server 操作分析

微信图片_20220512221944.png

GPU部分


  • GPU中通过顶点着色器、片元着色器完成对显示内容的渲染,将结果存入帧缓存区
  • GPU通过帧缓存区、视频控制器等相关部件,将其显示到屏幕上
相关文章
|
1月前
|
算法 数据处理 开发者
FFmpeg库的使用与深度解析:解码音频流流程
FFmpeg库的使用与深度解析:解码音频流流程
36 0
|
1月前
|
API 数据安全/隐私保护 iOS开发
利用uni-app 开发的iOS app 发布到App Store全流程
利用uni-app 开发的iOS app 发布到App Store全流程
88 3
|
1月前
|
消息中间件 Unix Linux
Linux进程间通信(IPC)介绍:详细解析IPC的执行流程、状态和通信机制
Linux进程间通信(IPC)介绍:详细解析IPC的执行流程、状态和通信机制
55 1
|
1月前
|
数据采集 数据可视化 大数据
Python在数据科学中的实际应用:从数据清洗到可视化的全流程解析
Python在数据科学中的实际应用:从数据清洗到可视化的全流程解析
38 1
|
23天前
|
C++
C++ While 和 For 循环:流程控制全解析
本文介绍了C++中的`switch`语句和循环结构。`switch`语句根据表达式的值执行匹配的代码块,可以使用`break`终止执行并跳出`switch`。`default`关键字用于处理没有匹配`case`的情况。接着,文章讲述了三种类型的循环:`while`循环在条件满足时执行代码,`do/while`至少执行一次代码再检查条件,`for`循环适用于已知循环次数的情况。`for`循环包含初始化、条件和递增三个部分。此外,还提到了嵌套循环和C++11引入的`foreach`循环,用于遍历数组元素。最后,鼓励读者关注微信公众号`Let us Coding`获取更多内容。
21 0
|
1月前
|
存储 编解码 算法
【解码与渲染 异常情况】深入解析视频中绿色竖线现象(二)
【解码与渲染 异常情况】深入解析视频中绿色竖线现象
38 1
|
1月前
|
数据安全/隐私保护 iOS开发 开发者
uniapp IOS从打包到上架流程(详细简单) 原创
uniapp IOS从打包到上架流程(详细简单) 原创
17 1
|
1月前
|
安全 数据安全/隐私保护 虚拟化
iOS应用加固方案解析:ipa加固安全技术全面评测
iOS应用加固方案解析:ipa加固安全技术全面评测
37 3
|
1月前
|
开发者 iOS开发
iOS App上架新规解析:如何进行App备案
iOS App上架新规解析:如何进行App备案
142 0
|
1月前
|
机器学习/深度学习 测试技术 API
iOS系统下轻松构建自动化数据收集流程
iOS系统下轻松构建自动化数据收集流程
26 0

推荐镜像

更多