Android图形系统综述(干货篇)

Android图形系统综述(干货篇)Android 图形系统是 Android 中一个非常重要的子系统,它涉及到许多相当复杂的模块,如 SurfaceFlinger, Choreographer, HardWare Composer 等平时开发中基本上不会直接接触的概念。前后基于 Android 10 版本陆陆续…

概述

Android 图形系统是 Android 中一个非常重要的子系统,它涉及到许多相当复杂的模块,如 SurfaceFlinger, Choreographer, HardWare Composer 等平时开发中基本上不会直接接触的概念。前后基于 Android 10 版本陆陆续续阅读了图形系统各个组成模块的源码,结合网上的一些博客,心中对 Android 的图形系统构成以及各个组件之间的工作流程有了一个整体的理解,这篇文章特意将整个图形系统做一个总结,可能还有所疏漏,后续发现了再接着补全。

这个系列前前后后记录了一些笔记,后续如有遗漏和错误也会接着更新:

  • Android-Window机制源码解读: Android Window 相关的机制,包括 WindowManagerService 的启动,startActivity 过程中 StartingWindow 的流程,以及 startActivity 过程中与 Window 相关的流程,如 DecorView 是何时以及怎么添加的等,最后给出了 WindowManager 添加/移除/更新 View 的源码解析。
  • Android-Window机制原理之Token验证: 基于源码解析关于 Android WMS 在 addWindow 的时候Token验证的逻辑,以及说明为什么不能使用 Application Context 显示 Dialog。
  • Android-View绘制原理: 当接收到 Vsync信号后,App 进程调用 ViewRootImpl.performTraversals 方法来执行 View 的绘制(measure, layout, draw)流程。
  • Android-View原理与实践: View 相关的一些零散知识点,对理解整个图形系统而言不重要。主要介绍了一下 View 的事件分发机制,滑动冲突,View.post 等。
  • Android-SurfaceFlinger启动与工作原理: 介绍 SurfaceFlinger 的启动流程及其如何处理 Vsync 信号来合成 Layer。
  • Android-Choreographer工作原理: App 进程如何通过 Choreographer 来注册和接收 Vsync 信号进而启动 View 的绘制流程。
  • Android-Surface之创建流程及软硬件绘制: 在 View 绘制过程中,View.draw 绘制的数据是如何能够最终被 SurfaceFlinger 合成的,其中包括 Canvas, Bitmap, Surface, Layer, Buffer 以及软硬件绘制的工作流程。
  • Android-Surface之双缓冲及SurfaceView解析: 介绍 View 绘制过程中的双缓冲技术以及 SurfaceView 的基本使用及源码解读。
  • Android图形系统综述: 总结 Android 图形系统的工作流程。

在开始之前先用一张图总结一下这个流程:

Android图形系统工作流程

看了这个图后,接着分步骤看看其中各个组件的具体工作流程。

Android显示基础

参考: “终于懂了” 系列:Android屏幕刷新机制—VSync、Choreographer 全面理解!

相关概念

  • 屏幕刷新频率:一秒内屏幕刷新的次数(一秒内显示了多少帧的图像),单位Hz(赫兹),如常见的60Hz,刷新频率取决于硬件的固定参数(不会变)。
  • 帧率(Frame Rate):单位fps。Android系统为60fps,即每秒钟GPU最多绘制60帧画面,帧率是动态变化的,例如当画面静止时,GPU是没有绘制操作的,屏幕刷新的还是buffer中的数据,即GPU最后操作的帧数据。
  • 逐行扫描:显示器并不是一次性将画面显示到屏幕上,而是从左到右边,从上到下逐行扫描,顺序显示整屏的一个个像素点,不过这一过程快到人眼无法察觉到变化。以 60Hz 刷新率的屏幕为例,这一过程即 1000/60≈16ms。
  • 画面撕裂(tearing):一个屏幕内的数据来自2个不同的帧,画面会出现撕裂感。

画面撕裂的原因:屏幕刷新频率是固定的,比如每16.6ms从buffer取数据显示完一帧,理想情况下帧率和刷新频率保持一致,即每绘制完成一帧,显示器显示一帧。但是CPU/GPU写数据是不可控的,所以会出现buffer里有些数据根本没显示出来就被重写了,即buffer里的数据可能来自不同帧,当屏幕刷新时,此时它并不知道buffer的状态,因此从buffer抓取的帧并不是完整的一帧画面,即出现画面撕裂。即Display在显示的过程中,buffer内数据被CPU/GPU修改,导致画面撕裂。

双缓存:由于图像绘制和屏幕读取使用的是同个buffer,所以屏幕刷新时可能读取到的是不完整的一帧画面(撕裂/闪烁)。双缓存是让绘制和显示器拥有各自的buffer,GPU始终将完成的一帧图像数据写入到Back Buffer,而显示器使用Front Buffer,当屏幕刷新时,Front Buffer 并不会发生变化,当Back buffer准备就绪后,它们才进行交换。如下图所示:

双缓冲

VSync:如果在Back buffer准备完成一帧数据后就进行两个buffer的交换,那么若此时屏幕还没有完整显示上一帧内容就会出问题。因此只能等到屏幕处理完一帧数据后,才可以执行buffer交换的操作。当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时有一段时间空隙,称为VerticalBlanking Interval(VBI),这个时间点就是我们进行缓冲区交换的最佳时间。VSync(垂直同步)是VerticalSynchronization的简写,它利用VBI时期出现的vertical sync pulse(垂直同步脉冲)来保证双缓冲在最佳时间点才进行交换。另外,交换是指各自的内存地址,可以认为该操作是瞬间完成。V-sync这个概念在早年的PC机领域就已经出现了。

Android 4.1之前:在Android4.1之前,屏幕刷新也遵循上面的双缓存+VSync机制。如下图:

drawing-with-VSync1

从时间顺序来看:

  1. Display显示第0帧数据,此时CPU和GPU渲染第1帧画面,且在Display显示下一帧之前完成;
  2. Display在第0帧显示完成后,即第1个VSync时,缓存进行交换,正常显示第1帧;
  3. 接着第2帧开始处理,可能由于某些原因,直到第2个VSync快来前才开始处理这一帧数据,导致第2个VSync来时,第2帧数据还没有准备就绪,缓存没有交换,显示的还是第1帧。这种情况即丢帧(Jank);
  4. 当第2帧数据准备完成后,它并不会马上被显示,而是要等待下一个VSync进行缓存交换再显示。

所以屏幕会平白多显示了一次第1帧,原因是第2帧的CPU/GPU计算没能在VSync信号到来前完成。

双缓存的交换是在Vsyn到来时进行,交换后屏幕会取Front buffer内的新数据,而实际上此时的Back buffer已经可以供GPU准备下一帧数据了。如果Vsyn到来时 CPU/GPU就开始操作的话,是有完整的16.6ms的,这样会很大程度地减小jank的出现(除非CPU/GPU计算超过了16.6ms)。

Android 4.1之后:实现了Project Butter(黄油工程),drawing with VSync,系统在收到VSync pulse后,将马上开始下一帧的渲染。即一旦收到VSync通知(16ms触发一次),CPU和GPU立刻开始计算然后把数据写入buffer。如下图:

drawing-with-VSync2

CPU/GPU根据Vsync信号处理数据,可以让CPU/GPU有完整的16.6ms时间来处理数据,减少了jank,然而如果CPU/GPU的处理时间超过了16.6ms,则如下图:

drawing-with-VSync3

  1. 在第二个时间段内,因为GPU还在处理B帧,缓存没能交换,导致A帧被重复显示;
  2. 而B帧完成后,又因为缺乏VSync pulse信号,它只能等待下一个signal的来临,这一过程中,有一大段时间是被浪费的;
  3. 当下一个VSync出现时,CPU/GPU马上执行操作(A帧),且缓存交换,相应的显示屏对应的就是B,这时看起来就是正常的。只不过由于执行时间仍然超过16ms,导致下一次应该执行的缓冲区交换又被推迟了。如此循环反复,便出现了越来越多的Jank。

由于只有两个buffer,Back buffer正在被GPU用来处理B帧的数据,Front buffer的内容用于Display的显示,两个buffer都被占用,因此CPU无法准备下一帧的数据。

三缓存(Tripple Buffer):在双缓冲机制基础上增加了一个Graphic Buffer缓冲区,CPU、GPU和显示设备都能使用各自的buffer工作,互不影响,优点是能最大限度利用空闲时间,缺点是会多占用一个Graphic Buffer大小的内存。如下图:

三缓存

第一个Jank不可避免,但是在第二个16ms时间段,CPU/GPU使用第三个Buffer完成C帧的计算,虽然还是会多显示一次A帧,但后续显示就比较顺畅了,有效避免Jank的进一步加剧。

三缓冲有效利用了等待Vsync的时间,减少了Jank,但是带来了延迟。所以,Buffer正常情况下还是两个,当出现Jank后三个足以。

小结

  • 当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时会出现的vertical sync pulse(垂直同步脉冲)来保证双缓冲在最佳时间点才进行交换。且Android 4.1后CPU/GPU的绘制是在VSYNC到来时开始。
  • 双缓存是Back buffer, Front buffer,用于解决画面撕裂;三缓存增加一个Back buffer,用于减少Jank。
  • 丢帧(掉帧)是说这一帧延迟显示了,因为缓存交换的时机只能等下一个VSync。
  • 只有当 App 注册监听下一个 Vsync 信号后才能接收到 Vsync 到来的回调。如果界面一直保持不变,那么 App 不会去接收每隔 16.6ms 一次的 Vsync 事件,但底层依旧会以这个频率来切换每一帧的画面(也是通过监听 Vsync 信号实现)。即当界面不变时屏幕也会固定每 16.6ms 刷新,但 CPU/GPU 不走绘制流程。
  • 当 View 请求刷新时,这个任务并不会马上开始,而是需要等到下一个 Vsync 信号到来时才开始;measure/layout/draw 流程运行完后,界面也不会立刻刷新,而会等到下一个 VSync 信号到来时才进行缓存交换和显示。

Android图形组件

参考: Android-Graphics

开发者可通过三种方式将图像绘制到屏幕上:Canvas, OpenGL ESVulkan。无论使用什么渲染API,一切内容都会渲染到Surface,Surface 表示 BufferQueue 中的生产方,而 BufferQueue 通常被 SurfaceFlinger 消费。在 Android 平台上创建的每个 Window 都由 Surface 提供支持,所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到显示部分。

下图显示了关键组件如何协同工作:

ape_fwk_graphics

相关组件如下:

  • Image Stream Producers: 图像流生产方可以是生成图形缓冲区以供消费的任何内容,例如 OpenGL ES、Canvas 2D 和 mediaserver 视频解码器。
  • Image Stream Consumers: 图像流最常见的消费者是 SurfaceFlinger,该系统服务会消费当前可见的 Surface,并使用 WindowManager 中提供的信息将它们合成交到 Display。SurfaceFlinger 使用 OpenGL 和 HardWare Composer 来合成 Surface。其他 OpenGL ES 应用也可以消费图像流,例如相机应用会消费相机预览图像流;非 GL 应用也可以是使用方,例如 ImageReader 类。
  • Hardware Composer: 这是显示子系统的硬件抽象层,SurfaceFlinger 可以将某些合成工作委托给 Hardware Composer,以分担 OpenGL 和 GPU 上的工作量。SurfaceFlinger 在收集可见层的所有缓冲区之后会询问 Hardware Composer 应如何进行合成。
  • Gralloc: 使用图形内存分配器 (Gralloc) 来分配图像生产方请求的内存。

BufferQueue

参考: Android-BufferQueue

Android 图形数据流管道如下图:

graphics-pipeline

左侧的对象是生成图形缓冲区的渲染器,如主屏幕、状态栏和系统界面。SurfaceFlinger 是合成器,而 Hardware Composer 是制作器。BufferQueue 是 Android 图形系统很重要的组成部分,它负责数据的传递:

bufferqueue

图中的 producer 和 consumer 运行在不同的进程里,BufferQueue 是将缓冲区池与队列相结合的数据结构,它使用 Binder IPC 在进程之间传递缓冲区。几个重要函数如下:

  • producers 通过 BufferQueue 请求一块空闲的缓存区(GraphicBuffer): IGraphicBufferProducer.dequeueBuffer 方法
  • 往缓存区(GraphicBuffer)填充了数据(绘制等)后, producers 将缓存区(GraphicBuffer)入队列到 BufferQueue 中: IGraphicBufferProducer.queueBuffer 方法
  • consumer 从 BufferQueue 中出队列一块缓存区(GraphicBuffer): IGraphicBufferConsumer.acquireBuffer 方法
  • consumer 消费完毕后(典型的是 SurfaceFlinger 合成数据)将缓存区(GraphicBuffer)返回给队列: IGraphicBufferConsumer.releaseBuffer 方法

其中 IGraphicBufferProducer 是 BufferQueue 的生产者接口,实现类是 BufferQueueProducer 生产者类;IGraphicBufferConsumer 是 BufferQueue 的消费者接口,实现类是 BufferQueueConsumer 消费者类。

查看 BufferQueue 相关的源码,可以在 frameworks/native/libs/gui/include/gui/BufferSlot.h 注释中看到 GraphicBuffer 有几种状态:

  • FREE: 该 Buffer 没有被 producer/consumer 所使用,其所有权属于 BufferQueue
  • DEQUEUED: 该 Buffer 被 producer 获取了,其所有权属于 producer
  • QUEUED: 该 Buffer 被 producer 填充了数据且入队列到 BufferQueue 了,其所有权属于 BufferQueue
  • ACQUIRED: 该 Buffer 被 consumer 获取了,该Buffer的所有权属于 consumer
  • SHARED: 该 Buffer 处于 shared buffer 模式

之所以需要这些状态,应该是为了维护一个 buffer pool 的结构,而不是每次使用 Buffer 时便创建一段共享内存,使用完毕便释放之,效率较低。

GraphicBuffer 状态的变化过程: FREE -> dequeueBuffer() -> DEQUEUED -> queueBuffer() -> QUEUED -> acquireBuffer() -> ACQUIRED -> releaseBuffer() -> FREE.

至于 BufferQueue 相关的源码,有兴趣可以深入查阅。

相关概念

  • View: 视图,绘制到屏幕上的内容,如 TextView, ImageView 等。
  • Window: View 的载体,对 Window 进行添加和删除需要通过 WindowManager 来操作。Window 并不是真实存在的,View 才是 Android 中的视图呈现形式,View 不能单独存在,它必须依附在 Window 这个抽象的概念上面。
  • WindowManager: 管理系统中的 Window, 实际功能通过 Binder IPC 借由 WindowManagerService 实现。
  • Canvas: 提供一些对 Surface 绘图的 API 用来进行实际的绘图操作。如果是软件绘制,其 drawXXX 方法会将内容绘制到 Bitmap 上;如果是硬件绘制,其 drawXXX 方法会抽象成 DrawOp 操作,然后添加到 DisplayList 中被 GPU 渲染。
  • Surface: 一个 Window 对应一个 Surface(当存在 SurfaceView 则例外,Java Surface 实例存在于 ViewRootImpl 中,对应 native 层的 Surface 对象)。Surface 内部持有一个 BufferQueueProducer 指针(在 Layer 中创建)可以生产图像缓存区用来绘图,与 App 和 SurfaceFlinger 形成一个生产者消费者模型。
  • Layer: App 请求创建 Surface 时 SurfaceFlinger 会创建 Layer 对象,它是 SurfaceFlinger 合成的基本操作单元,因此一个 Surface 对应一个 Layer。它创建有一个 BufferQueueProducer 生产者和 BufferQueueConsumer 消费者,这两个对象与像素数据的存储与转移相关。用户最终看到的屏幕内容是许多 Layer 按照 z-order 混合的结果。
  • SurfaceView: 一种较之 TextView, Button 等更为特殊的 View, 它不与其宿主的 Window 共享一个 Surface, 而是有自己的独立 Surface。并且它可以在一个独立的线程中绘制 UI。因此 SurfaceView 一般用来实现比较复杂的图像或动画/视频的显示。
  • Choreographer: 编舞者,用来控制当收到 VSync 信号后才开始绘制任务,保证绘制拥有完整的16.6ms。通常应用层不会直接使用Choreographer,而是使用更高级的API,如View.invalidate()等,可以通过Choreographer来监控应用的帧率。
  • SurfaceFlinger: 管理消费当前可见的 Surface,所有被渲染的可见 Surface 都被 SurfaceFlinger,通过 WindowManager 提供的信息合成(使用 OpenGL 和 HardWare Composer,合成的数据源是上面提及的 BufferQueue 中的 GraphicBuffer)提交到屏幕的后缓冲区,等待屏幕的下一个Vsync信号到来,再显示到屏幕上。SufaceFlinger 通过屏幕后缓冲区与屏幕建立联系,同时通过 Surface 与上层建立联系,起到了一个承上启下的作用。
  • HWComposer: HardWare Composer, 定义一套 HAL 层接口,芯片厂商根据硬件特点来实现这套接口。其主要工作是将 SurfaceFlinger 计算后的 Layer 显示参数合成到显示器 Buffer 上。当然 SurfaceFlinger 并非是 HWC 的唯一输入源,例如摄像头的预览输入 Buffer 可以由硬件设备直接写入,然后作为 HWC 的输入之一与 SurfaceFlinger 的输出做最后的合成。
  • OpenGL: 一个 2D/3D 的图形库,需要底层硬件(GPU)和驱动来支持。移动端通常使用其子集 OpenGl ES(OpenGl for Embedded System)。
  • Display: 显示设备的抽象,传统的 Display 设备是手机屏,此外 Android 也支持其他的外部输入设备如 HDMI, Wifi Display 等,将 SurfaceFlinger, OpenGL, HWComposer 合成后的数据输出到 Display 设备的缓存区用来显示。

SurfaceFinger工作流程

详细文章见 Android-SurfaceFlinger启动与工作原理

surfaceflinger 是在 Android 系统启动时解析 init.rc 文件启动的守护进程,在 SurfaceFlinger 的启动流程中:

  1. 首先会创建 SurfaceFlinger 对象,在构造器中创建了 DispSync 同步模型对象;
  2. 然后执行初始化 SurfaceFlinger 的逻辑:
    • 注册监听,接收 HWC 的相关事件。
    • 启动 APP 和 SF 的 EventThread 线程,用来管理基于 DispSync 创建的两个 DispSyncSource 延时源对象,分别是用于绘制(app–mEventThreadSource)和合成(SurfaceFlinger–mSfEventThreadSource)。启动了 EventThread 线程后,会一直阻塞在 waitForEventLocked 方法中(期间会根据需要设置监听器),直到接收到 Vsync 信号至少有一个连接正在等待 Vsync 信号才会继续执行线程逻辑,即通知监听者;
    • 通过 MessageQueue.setEventThread 方法创建了一个连接,并通过 Looper.addFd 方法监听 BitTube 数据。
    • 创建 HWComposer 对象(通过 HAL 层的 HWComposer 硬件模块 或 软件模拟产生 Vsync 信号),现在的 Android 系统基本上都可以看成是通过硬件 HWComposer 产生 Vsync 信号,而不使用软件模拟,所以下面解析都只谈及硬件 HWComposer 的 Vsync 信号
    • 初始化非虚拟的显示屏;
    • 启动开机动画服务;
  3. 最后执行 SurfaceFlinger.run 逻辑,该方法会在 SurfaceFlinger 主线程通过死循环执行 MessageQueue.waitMessage 方法等待消息的到来,其内部调用了 Looper.pollOnce 方法,该方法会从 Looper.addFd 方法监听的 BitTube 中读取数据,当有数据到来时执行对应的回调方法。

当硬件或软件模拟发出 Vsync 信号时:

  1. 回调 SF 相关方法,SF 调用 DispSync 同步模型的方法处理 Vsync 信号(统计和计算模型的偏移和周期),并根据返回值判断是否使能/关闭 HWC Vsync 信号的发出。
  2. DispSync 根据计算的偏移和周期计算下次 Vsync 信号发生时间,并通知监听者 Vsync 信号到达的事件,传递给 DispSyncSource 延时源,延时源通过 EventThread 来管理 Vsync 信号的收发。
  3. EventThread 调用连接 Connection 对象向 BitTube 发送数据,触发 addFd 函数中设置的回调方法,回调方法进而调用 SF.onMessageReceived 函数,然后进行图像的合成等工作。

另一方面,Choreographer 会通过上面创建的 APP 延时源 mEventThreadSource 对象及其对应的 EventThread 线程来监听同步模拟发出的 Vsync 信号,然后进行绘制(measure/layout/draw)操作。具体逻辑见 Android-Choreographer工作原理

将 SurfaceFlinger 的工作流程总结如下图:

SurfaceFlinger工作流程

Choreographer工作流程

详细文章见 Android-Choreographer工作原理

  • Choreographer: 使 CPU/GPU 的绘制是在 VSYNC 到来时开始。Choreographer 初始化时会创建一个表示对 Vsync 信号感兴趣的连接,当有绘制请求时通过 postCallback 方法请求下一次 Vsync 信号,当信号到来后才开始执行绘制任务。
  • 只有当 App 注册监听下一个 Vsync 信号后才能接收到 Vsync 到来的回调。如果界面一直保持不变,那么 App 不会去接收每隔 16.6ms 一次的 Vsync 事件,但底层依旧会以这个频率来切换每一帧的画面(也是通过监听 Vsync 信号实现)。即当界面不变时屏幕也会固定每 16.6ms 刷新,但 CPU/GPU 不走绘制流程。
  • 当 View 请求刷新时,这个任务并不会马上开始,而是需要等到下一个 Vsync 信号到来时才开始;measure/layout/draw 流程运行完后,界面也不会立刻刷新,而会等到下一个 VSync 信号到来时才进行缓存交换和显示。
  • 造成丢帧主要有两个原因:一是遍历绘制 View 树以及计算屏幕数据超过了16.6ms;二是主线程一直在处理其他耗时消息,导致绘制任务迟迟不能开始(同步屏障不能完全解决这个问题)。
  • 可通过Choreographer.getInstance().postFrameCallback()来监听帧率情况。

阅读这篇文章建议先阅读 Android-SurfaceFlinger启动与工作原理 这篇文章,然后结合 Choreographer 的工作流程,可以对 Vsync 信号是怎么协调 App 端的绘制任务以及 SurfaceFlinger 的合成任务有一个比较清晰的认识。

用一张图总结一下 Choreographer 的工作流程:

Choreographer工作流程

View绘制流程

详细文章见 Android-Window机制源码解读Android-View绘制原理

在 Choreographer 接收到 Vsync 信号后便开始 View 的绘制过程,即老生常谈的 measure, layout, draw 三大步骤。

Surface工作流程及软硬件绘制

详细文章见 Android-Surface之创建流程及软硬件绘制Android-Surface之双缓冲及SurfaceView解析

在 View 的 draw 过程中会接触到 Surface 的逻辑,通过它可以向 SurfaceFlinger 申请一块缓存区用来绘制。绘制任务可以分为软件绘制与硬件绘制两种。

  • Java 层的 Surface 对象中 mNativeObject 属性指向 native 层中创建的 Surface 对象。
  • Surface 对应 SurfaceFlinger 中的 Layer 对象,它持有 Layer 中的 BufferQueueProducer 指针(生产者),通过这个生产者对象可以在绘制时向 BufferQueue 申请一块空闲的图形缓存区 GraphicBuffer,在 Surface 上绘制的内容会存入该缓存区内。
  • SurfaceFlinger 通过 BufferQueueConsumer 消费者从 BufferQueue 中取出 GraphicBuffer 中的数据进行合成渲染并送到显示器显示。

软件绘制

软件绘制可能会绘制到不需要重绘的视图,且其绘制过程在主线程进行的,可能会造成卡顿等情况。它把要绘制的内容写进一个 Bitmap 位图,其实就是填充到了 Surface 申请的图形缓存区里。

软件绘制可分为三个步骤:

  1. Surface.lockCanvas — dequeueBuffer 从 BufferQueue 中出队列一块缓存区。
  2. View.draw — 绘制内容。
  3. Surface.unlockCanvasAndPost — queueBuffer 将填充了数据的缓存区存入 BufferQueue 队列中,然后通知给 SurfaceFlinger 进行合成(请求 Vsync 信号)。

硬件绘制

硬件绘制会将绘制函数作为绘制指令(DrawOp)记录在一个列表(DisplayList)中,然后交给单独的 Render 线程使用 GPU 进行硬件加速渲染。它只需要针对需要更新的 View 对象的脏区进行记录或更新,无需更新的 View 对象则能重用先前 DisplayList 中记录的指令。

硬件绘制可分为两个阶段:

  1. 构建阶段:将 View 的绘制操作(drawLine…)抽象成 DrawOp 操作并存入 DisplayList 中。
  2. 绘制阶段:首先分配缓存区(同软件绘制),然后将 Surface 绑定到 Render 线程,最后通过 GPU 渲染 DrawOp 数据。

硬件加速的内存申请跟软件绘制一样都是借助 Layer 中的 BufferQueueProducer 生产者从 BufferQueue 中出队列一块空闲缓存区 GraphicBuffer 用来渲染数据的,之后也都会通知 SurfaceFlinger 进行合成。不一样的地方在于硬件加速相比软件绘制而言算法可能更加合理,同时采用了一个单独的 Render 线程,减轻了主线程的负担。

双缓冲

一般来说将双缓冲用到的两块缓冲区称为 — 前缓冲区(front buffer) 和 后缓冲区(back buffer)。显示器显示的数据来源于 front buffer 前缓存区,而每一帧的数据都绘制到 back buffer 后缓存区,在 Vsync 信号到来后会交互缓存区的数据(指针指向),这时 front buffer 和 back buffer 的称呼及功能倒转。

在 View 的绘制过程中 Surface 使用了双缓冲技术。

SurfaceView

SurfaceView 就是一块拥有自己独立 Surface 的特殊 View 视图,由于这个特性,它一般用来实现比较复杂的图像或动画/视频的显示。

用一张图总结一下 Android 软硬件绘制的流程:

Android-软硬件绘制流程

图形系统工作流程总结

到这里总算将 Android 图形系统的工作流程串联起来了,在阅读了源码之后感觉对这个流程有了更清晰的理解,而不是跟着网上的结论人云亦云,感觉每个人说的都有道理,但是总有些地方觉得又有冲突,自己阅读源码跟看别人阅读源码之后给出的结论,感觉确实不一样,Read The Fucking Source Code 是一种很好的解惑手段,接下来剩下的就是这些流程中的某些细节了,比如说 requestLayout 和 invalidate 方法的区别之类的,这些相关的问题以后有时间也会慢慢记录一下。文中内容如有错误欢迎指出,共同进步!觉得不错的留个再走哈~

用一张图总结一下图形系统工作的整体流程:

Android图形系统工作流程

今天的文章Android图形系统综述(干货篇)分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/23212.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注