Android渲染体系

引子:“Android是多窗口的吗?是多个进程进行渲染的吗?具体是怎么实现的?” 以上问题一位Linux研发咨询的,虽然一直对SurfaceFlinger、三缓冲、多窗口、Z轴这些概念有所了解,但是要系统地汇总介绍出来竟然又有点陌生,而这篇文章初衷就是回答上面三个问题。
关键字:SurfaceFlinger、VSync、Choreographer、Window、Surface、Layer、三缓冲

这篇文章目的仅为了回答上述三个问题,因此内容比较精简,是面向具备一定知识储备的读者,更多详细信息大家可以参考每个模块的参考链接。

1. 多应用(进程)独立绘制,交给SurfaceFlinger合成渲染

image.png
  • NativeWindows-2 面向应用,等同于应用绘制时使用的Surface(就是SurfaceView拿到的Surface一样),是ViewRootImpl通过WMS获取的
  • NativeWindows-1 属于Framebuffer NativeWindowSurfaceFlinger管理,和HWC交互最终交给硬件渲染
  • 当系统中存在多个需要显示UI 的应用时,一方面这种改进设计保证了它们都能获得个“本地窗口”进行绘制,另一方面这些“本地窗口”也都可以被有序地显示到终端屏幕上一一因为SurfaceFlinger 会收集所有程序的显示需求,对它们进行统一的图像混合操作,然后输出到自己的NativeWindow上。(https://blog.csdn.net/Shujie_L/article/details/137829778

2. 从数据流向的维度来看,Apps -> BufferQueue -> SurfaceFlinger

左侧就是各应用的Render线程,是生成图形缓冲区的渲染器,比如主屏幕、状态栏和系统界面等各个应用。SurfaceFlinger 是合成器,而硬件混合渲染器是混合渲染器。
image.png

我们继续展开“生成图形缓冲区的渲染器”和“BufferQueue”看看是什么组件实现的呢?更进一步,BufferQueue的FrontBuffer-OffScreenBuffer和三缓冲机制有什么关联吗?(https://juejin.cn/post/7288963211071979578
双缓冲:Display 独立缓冲区,CPU-GPU共用一个缓冲区
三缓冲:Display、GPU、CPU分别使用一个独立缓冲区

image.png

3. 那Surface是什么呢?

image.png

https://blog.csdn.net/xiaolong662007/article/details/8278903
AI解释Surface 是 Android 图形系统中的一个抽象概念,表示一个可以绘制和显示的图形缓冲区。它提供了一个接口,通过 Canvas 或 OpenGL 等 API 进行图形绘制,并与 SurfaceFlinger 协同工作,将绘制结果显示在屏幕上。
文章1:我们需要一个结构来记录应用程序界面的位置,大小,以及一个buffer 来记录需要显示的内容,所以这就是我们 surface 的概念,surface 实际我们可以把它理解成一个容器,这个容器记录着应用程序界面的控制信息,比如说大小、位置,而它还有buffer 来专门存储需要显示的内容。
文章2:Android的图像系统组件 无论开发者使用什么渲染 API,一切内容都会渲染到 「Surface」 上。Surface 表示缓冲区队列中的生产方,而缓冲区队列通常会被 SurfaceFlinger 消耗。在 Android 平台上创建的每个窗口都由 Surface 提供支持。所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到屏幕

4. BufferQueue是生产消费队列,但是App数据是如何跨进程发给SurfaceFlinger的?

App-WMS-SurfaceFlinger.png
  1. 在 App 进程中创建 PhoneWindow 后会创建 ViewRoot。ViewRoot 的创建会创建一个 Surface,这个 Surface 其实是空的,通过与 WindowManagerService 通信 copyFrom() 一个 NativeSurface。
  2. 在与 SurfaceFlinger 通信时,会创建 SharedClient 一段共享内存,里面存放的是 SharedBufferStack 对应 SurfaceFlinger 中的 SurfaceLayer 每个 Layer 其实是一个 FrameBuffer,每个 FrameBuffer 中有两个 GraphicBuffer 记作 FrontBuffer 和 BackBuffer。
  3. 在 SurfaceFlinger 中 SharedBufferServer 来管理 FrameBuffer。同时在 App 端 copyFrom() 出来 NativeSurface 时会创建一个 SharedBufferClient 与 SharedClient 这块共享内存关联。当客户端 addView() 或者需要更新 View 时,会通过 SharedBufferClient 写入数据到 ShareClient 中,SurfaceFlinger 中的 SharedBufferServer 接收到通知会将 FrameBuffer 中的数据传输到屏幕上。
  4. HWComposer 是基于硬件来产生 VSync 信号的,来通知 SurfaceFlinger 重绘控制显示的帧率。
    https://juejin.cn/post/7292428123849343039

5. SharedClient大数据的共享内存机制

image.png

WMS的作用只是窗口管理,那么图形是怎么绘制的呢?并且这些绘制信息是如何传递给SurfaceFlinger服务的呢?每个View都有自己的onDraw回调,开发者可以在onDraw里绘制自己想要绘制的图像,很明显View的绘制是在APP端,直观上理解,View的绘制也不会交给服务端,不然也太不独立了,可是View绘制的内存是什么时候分配的呢?是谁分配的呢?我们知道每个Activity可以看做是一个图层,其对应一块绘图表面其实就是Surface,Surface绘图表面对应的内存其实是由SurfaceFlinger申请的,并且,内存是APP与SurfaceFlinger间进程共享的。实现机制是基于Linux的共享内存,其实就是MAP+tmpfs文件系统,你可以理解成SF为APP申请一块内存,然后通过binder将这块内存相关的信息传递APP端,APP端往这块内存中绘制内容,绘制完毕,通知SF图层混排,之后,SF再将数据渲染到屏幕。其实这样做也很合理,因为图像内存比较大,普通的binder与socket都无法满足需求,内存共享的示意图如上
https://www.jianshu.com/p/e4b19fc36a0e
https://www.jianshu.com/p/62db83a97a5c

6. 系统如何控制数据传递的时序以及绘制周期呢?Vsync登场

image.png

Vsync 信号可以由硬件产生,也可以用软件模拟,不过现在基本上都是硬件产生,负责产生硬件 Vsync 的是 HWC。HWC 可生成 VSYNC 事件并通过回调将事件发送到 SurfaceFlinger 再通过 DispSync 将 Vsync 生成由 Choreographer 和 SurfaceFlinger 使用的 VSYNC_APP 和 VSYNC_SF 信号。

如下图:
image.png

7. Choreographer在接收到VSYNC_APP时都做了哪些动作?

image.png

工作流程:

  1. Choreographer 初始化
  2. 初始化 FrameHandler ,绑定 Looper
  3. 初始化 FrameDisplayEventReceiver ,与 SurfaceFlinger 建立通信用于接收和请求 Vsync
  4. 初始化 CallBackQueues。SurfaceFlinger 的 appEventThread 唤醒发送 Vsync ,Choreographer 回调 FrameDisplayEventReceiver.onVsync , 进入 Choreographer 的主处理函数 doFrame
  5. Choreographer.doFrame 计算掉帧逻辑
  6. Choreographer.doFrame 处理 Choreographer 的第一个 callback : input
  7. Choreographer.doFrame 处理 Choreographer 的第二个 callback : animation
  8. Choreographer.doFrame 处理 Choreographer 的第三个 callback : insets animation
  9. Choreographer.doFrame 处理 Choreographer 的第四个 callback : traversal,traversal-draw 中 UIThread 与 RenderThread 同步数据
  10. Choreographer.doFrame 处理 Choreographer 的第五个 callback : commit
  11. RenderThread 处理绘制命令,将处理好的绘制命令发给 GPU 处理
  12. 调用 swapBuffer 提交给 SurfaceFlinger 进行合成
    https://juejin.cn/post/7291604571200749609

PS:UIThread和RenderThread的数据通信主要通过RenderNode和DisplayList,相关信息可以参考从源码updateDisplayListIfDirty开始了解

8. 总结

上述的7大主题基本解释了引子中的三个问题。如果尝试用一段话总结的话 :“Android每个应用都有自己独立的渲染区域,每个应用在独立的内存区域绘制,整个内存块是一个BufferQueue队列,按照Z轴排序。BufferQueue队列中的GraphicBuffer存储图形数据,三缓冲机制的具体实现就来源于GraphicBuffer三块内存。应用在vsync_app信号量到达时统一进行绘制工作,而vsync_sf信号量到到达时,SurfaceFlinger服务会从各应用BufferQueue队列中获取BackEnd图形数据,按照Z轴垂直排列,交给HWC处理,最终发给显示终端显示。”
当然除了主要7个步骤,还有各应用的DisplayList机制、Render线程、三缓冲机制、HWC、Skia、OpenGLES等等诸多概念没有涉及,要完整解释Android的渲染体系还有很多知识点需要掌握,最后就用一张体系大图来完成这篇完整吧。

image.png

https://blog.csdn.net/WolfKingzyh/article/details/135625212

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容