移动设备图形渲染原理

本文最后更新于 2021年5月12日 晚上

大部分手机应用程序都会拥有用户界面(UI), 用户看到的实际是显示在屏幕上的”图片”, 这些”图片”由若干像素组成, 图片在高速变化时, 就形成动画. 当用户触摸某个按钮时, 实际是由设备发送手指在屏幕上触摸的坐标.

显示在屏幕上的”图片”要进行更新, 需要外部事件驱动, 这些事件包括:

  • 用户手势(触摸)
  • 各类传感器(光线/方向/位置等)
  • 网络(network)通信
  • 时间(定时器, 比如动画)

图片被更新后, 显示器如何知道它更新了呢? 显示设备以固定速率(60Hz 或更精确的 59.97Hz)从 GPU 帧缓存中获取, 帧即显示在屏幕上的”图片”描述, 从而完成显示.

图像显示原理

在 iOS 设备上, 使用双缓冲+vSync 的方式进行显示, 在安卓 4.1 之后, 使用三缓冲+vSync 方式.

什么是 vSync ? 双缓冲是如何工作的呢? 我们接着往下看.

vSync 指的是垂直同步. 当显示器准备取下一帧时(不严谨的说法), 它会发送一个 vSync 信号到设备, 这个信号最终会被 CPU 拿到, 从而触发视图布局/渲染流程计算, 计算结果被发送给 GPU, GPU 完成光栅化等操作, 最后将计算好的像素数据送到显示缓存, 从而实现图像显示.

在双缓冲环境下, 这样的同步又是另外一种情况了.

vSync 同步信号工作机制简述

当前在 iOS 或安卓系统下, 均使用 vSync 作为显示同步信号, 对 CPU/GPU/显示器三者的工作进行同步.

就 Flutter 框架而言, 当显示器需要获取下一帧时, 它会发送一个 vSync 信号, 这个信号会被交给引擎, 通过引擎驱动 Framework 层执行相关的布局/渲染/合成工作(CPU 侧), 当这些工作完成后, 将数据打包为”场景”并发送给 GPU 侧(_window.render(scene), 引擎端负责后续处理), 由 GPU 完成这一帧的后续计算, 并将结果发送到帧缓存. 如下引用一张经典的图例:

帧缓存和显示器

显示器从帧缓存取得要显示的”像素数据”. 对拥有两个帧缓存的系统而言, 工作流程如下所示:

可以看到, 前后缓冲区交替变换, 这样显示器从两个帧缓存交替取显示数据, 而 GPU 每次都向下一个需要读取的缓存发送数据(这里的后缓冲区), 这样就可以保证帧数据的连续.

(Flutter 平台 CPU 侧的帧数据处理代码在 RendererBinding 中的 drawFrame 方法中, 即 rendering pipeline)

掉帧的根本原因

通过开启 vSync, 显示设备的取帧速率是固定的, 那为什么会有掉帧的情况呢?

答案就是: 当开启 vSync 垂直同步后, GPU 的帧更新(生成)速率(Frame rate)不足以达到显示设备的取帧速率, 造成若干次取帧时取到的是之前的帧, 从而就造成画面”卡”在那里(帧的连续变化不协调), 从而感受到的就是掉帧.

因此, 要解决掉帧, 就要从每一帧减少 CPU 的处理时间和减少 GPU 的处理时间入手.

Flutter 框架(或者说任何 UI 框架)的基本任务就是合成显示在屏幕上的帧的数据信息并交给 GPU, 同时处理外部事件交互.

并且 Flutter Framework 中的代码几乎全部是由Flutter Engine 帧渲染驱动的, 这个之后会结合代码说明.


移动设备图形渲染原理
https://blog.rayy.top/2021/05/12/2021-05-12-how-mobile-device-render-image-on-the-screen/
作者
貘鸣
发布于
2021年5月12日
许可协议