Android显示流程
Android的显示过程具体包含这三部分:内容端(绘制)、SurfaceFlinger/Hwcomposer(合成)、 DisplayProcessor及显示接口如LCD(显示)
UE游戏的绘制过程:
绘制
Application/Activity/View
Application包括4大组件:Activity、Service、Broadcast、ContentProvider
Activity是带有生命周期的逻辑单位,由ActivityManager管理,当前App打开其他App的Activity,会将该Activity插入到自己的Activity栈中
负责显示的是Window(根View 如:DecorView)和View,Activity与window的关系是1:n
View具体包括Button、InputText、可装载子View的View(如FrameLayout、LinearLayout)、ViewStub(action_mode_bar_stub、状态栏)、SurfaceView(用于游戏Render)
具体关系详见下图:
注:Status Bar(状态栏)和Navigation Bar(导航栏)是由系统进程来渲染
UE游戏的View结构:
DecorView、TextView的类继承关系
DecorView是可以嵌套其他View控件的(如下图DecorView中嵌套了TextView)
Window
主要分为两种:
① PhoneWindow(应用窗口)
PhoneWindow是应用相关的窗口类,从Window上派生,针对手机屏幕做了一些优化工作,里面核心的是DecorView mDecorView这个变量
mDecorView是一个顶层的View,窗口的添加就是通过WindowManager.addView()把该mDecorView添加到WindowManager中。
注:悬浮窗口虽然与Activity相关联,但并不是PhoneWindow,直接调用通过WindowManager.addView()添加。
一个Activity有一个主窗口(PhoneWindow),弹出的对话框是一个PhoneWindow,Menu菜单也是一个PhoneWindow。在同一个Activity中,主窗口、对话框、Menu窗口之间通过该Activity关联起来。
② 公共界面Window
如最近运行对话框、关机对话框、状态栏下拉栏、锁屏界面等。这些Window都是系统级别的,不属于任何应用,和Activity没有任何关系。
这些Window没有任何窗口类来封装,也是直接调用WindowManager.addView()来把一个view添加到WindowManager中。
Window -> DecorView-> ViewRootImpl -> Surface(WindowState) -> Layer 是一一对应的。
SurfaceView-> ViewRootImpl -> Surface(WindowState) -> Layer是一一对应的。
一般的Activity包括的多个View会组成View hierachy的树形结构,只有最顶层的DecorView,也就是根结点视图,才是对WMS可见的,即有对应的Window和WindowState。
为了改进性能,引入SurfaceView,并让其对WMS可见,原因是让MainThread不要有太重的绘制任务,将绘制任务移到另外一个线程。可以看下其draw函数只是将自己清空,相当于挖个洞,具体内容由绘制线程来做。
Surface
一个应用程序窗口分别位于应用程序进程和WMS服务中的两个Surface对象有什么区别呢?
虽然它们都是用来操作位于SurfaceFlinger服务中的同一个Layer对象的,但是它们的操作方式却不一样。
具体来说,就是位于应用程序进程这一侧的Surface对象(包裹在ViewRootImpl中)负责绘制应用程序窗口的UI,即往应用程序窗口的图形缓冲区填充UI数据。
而位于WMS服务这一侧的Surface对象(包裹在WindowState中)负责设置应用程序窗口的属性,例如位置、大小等属性。
这两种不同的操作方式分别是通过C++层的Surface对象和SurfaceControl对象来完成的。
因此,位于应用程序进程和WMS服务中的两个Surface对象的用法是有区别的。
之所以会有这样的区别,是因为绘制应用程序窗口是独立的,由应用程序进程来完即可,而设置应用程序窗口的属性却需要全局考虑,即需要由WMS服务来统筹安排。
软件窗口绘制
在Android应用程序进程这一侧,每一个窗口都关联有一个Surface。每当窗口需要绘制时,就会调用其关联的Surface的成员函数lock获得一个Canvas,其本质上是向SurfaceFlinger服务分配一个Graphic Buffer。
绘制完成之后,Android应用程序进程再调用前面获得的Canvas的成员函数unlockAndPost请求显示在屏幕中,其本质上是向SurfaceFlinger服务提交一个Graphic Buffer 注:SurfaceFlinger服务在后会对Graphic Buffer的内容进行合成
不过这些绘制任务都是在MainThread中做的。
硬件窗口绘制
在Android系统中,ANativeWindow和Surface可以是认为等价的,只不过是ANativeWindow常用于Native层中,而Surface常用于Java层中。
OpenGL获得了一个ANativeWindow,并且进行了硬件加速渲染环境初始化工作之后,Android应用程序就可以调用OpenGL提供的API进行绘制了,绘制出来内容就保存在前面获得的Graphic Buffer中。
此时可以用一个独立线程来跑这些绘制任务,以此来解放MainThread。注:一个OpenGL渲染上下文只能与一个线程关联。在一个OpenGL渲染上下文创建的OpenGL对象一般来说只能在关联的OpenGL线程中操作,否则容易发生多线程并发访问冲突问题。
开始绘制时,会清理当前FrameBuffer,此时SurfaceFlinger服务会分配一个Graphic Buffer(dequeueBuffer,出队一个Buffer),传递给OpenGL进行绘制。
注:堆栈来自于Android Studio Profiler的CPU Profiler下的Sample C/C++ Functions数据的Flame Chart(火焰图)
绘制完毕,再调用eglSwapBuffer上屏,其本质上也是向SurfaceFlinger服务提交一个Graphic Buffer(queueBuffer,等候入队一个Buffer) 注:SurfaceFlinger服务在后续会对Graphic Buffer的内容进行合成
注:堆栈来自于Android Studio Profiler的CPU Profiler下的Sample C/C++ Functions数据的Flame Chart(火焰图)
SurfaceView上可以查看当前缓冲区的累积的个数:开始执行dequeueBuffer就会加1(+1),SurfaceFlinger开始执行handleMessageInvalidate就会减1(-1)
合成
执行doComposition函数,进行合成操作
另外一台手机:
SurfaceFlinger在收集可见层的所有缓冲区之后,便会询问 Hardware Composer,然后对它们进行合成。
显示
DPU(Display Processor Unit)的功能便是将合成的FrameBuffer输出到显示设备上。
参考
自上而下解读Android显示流程(下)—Display Processor的设计