zoukankan      html  css  js  c++  java
  • UI绘制原理及卡顿 掉帧原因

    CPU与GPU是通过总线连接起来,通过CPU输出一个位图,经由总线传输在合适的时机上传到GPU,GPU获取到位图后,会进行图层渲染、纹理合成,将结果放入帧缓存区中,由视频控制器根据VSyn信号,在指定时间之前提取帧缓存区中的屏幕显示内容,最终显示到手机屏幕上。

    显示原理

    CPU与GPU分别做了什么?

    创建UIView后,显示部分由CALayer负责,CALayer有一个contents属性,就是我们最终要绘制到屏幕上的位图,如果我们创建的是UILabel,那么contents上最终要绘制就是“Hello”的位图,系统在合适的时候回调给我们一个drawRect的方法,然后我们可以在此基础上绘制一些我们想自定义绘制的内容,绘制好的这个位图,会经过Core Animation提交给GPU部分的OpenGL渲染管线,进行最终的位图的渲染以及文理的合成,最终显示到屏幕上。

    CPU与GPU分工

    CPU要做的工作

    1. Layout
      • UI布局
      • 文本计算
    2. Display
      • 绘制(drawRect)
    3. Prepare
      • 图片解码
    4. Commit
      • 提交位图

    GPU 渲染管线

    • 顶点着色
    • 图元装配
    • 光栅化
    • 片段着色
    • 片段处理

    提交到帧缓冲区中

    UI卡顿、掉帧的原因

    一般来说,页面滑动的流畅性是60FPS,代表着每秒钟有六十帧画面更新,换种说法就是每一帧画面的生成时间在1/60秒即16.7毫秒以内就不会卡顿。在16.7毫秒内需要CPU和GPU共同来生成一帧的数据。如果时间超过了16.7ms就会产生卡顿。

    掉帧原因

    在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

    从上面的图中可以看到,CPU 和 GPU 不论哪个阻碍了显示流程,都会造成掉帧现象。所以开发时,也需要分别对 CPU 和 GPU 压力进行评估和优化。

    滑动优化方案

    CPU

    • 对象的创建、调整、销毁
    • 预排版(布局计算、文本计算)
    • 预渲染(文本等异步绘制,图片编解码等)

    GPU

    • 文理渲染(避免离屏渲染)
    • 视图混合(减少视图层级)

    UIView的绘制原理

    当调用UIViewsetNeedsDisplay方法时,会调用CALayer的同名方法setNeedsDisplay,这时并没有立即发生绘制,而只是相当于在当前layer打上了脏标记, 会在Runloop即将结束时才会调用[CALayer display],而这个方法的内部会判断是否实现了displayLayer这个方法,如果没有实现,那么走系统调用,如果实现了,就为我们异步绘制提供了入口。

    绘制流程

    系统绘制实现

    系统绘制流程

    异步绘制实现

    通过实现layer的代理方法displayLayer

    • 代理负责生成对应的bitmap
    • 设置该bitmap作为layer.contents属性的值

    异步绘制流程

    离屏渲染

    On-Screen Rendering 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行渲染操作

    Off-Screen Rendering 意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作

    当设置某些UI视图的图层属性,如果标记为未合成之前不能用于显示,那么就会触发离屏渲染

    触发离屏渲染的条件

    • 圆角(和maskToBounds一起使用时)
    • 图层蒙版
    • 阴影
    • 光栅化

    为何要避免离屏渲染?

    触发离屏渲染,增加GPU工作量,而GPU的工作的增加可能导致CPU+GPU产生一帧画面的时间超过16.7ms,就会产生卡顿掉帧,所以要避免离屏渲染。

    • 创建新的渲染缓存区
    • 上下文切换
  • 相关阅读:
    Prometheus监控MySQL服务(六)
    Prometheus监控Docker服务(五)
    Grafana与Prometheus集成(四)
    Prometheus监控Linux服务器(三)
    Prometheus配置文件(二)
    elasticdump数据迁移方法
    Prometheus介绍及二进制部署(一)
    PHP二维数组的引用赋值容易犯的错误
    Maven仓库国内镜像站
    加密文件之Java改进版
  • 原文地址:https://www.cnblogs.com/allencelee/p/12621484.html
Copyright © 2011-2022 走看看