可可西

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显示流程(上)

自上而下解读Android显示流程(中上)

自上而下解读Android显示流程(下)—Display Processor的设计

Android GPU硬件加速渲染流程(上)

Android GPU硬件加速渲染流程(下)

 

posted on 2023-12-30 18:25  可可西  阅读(686)  评论(0编辑  收藏  举报

导航