天天看点

Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)Android显示之图层合成

1.图层合成指综合各个窗口的绘制内容,送往lcd显示的过程。从原理上可分为在线合成与离线合成两种方式。

2.在android的surfaceflinger代码流程中,图层合成方式分3d合成(opengl)和硬件合成两大类。

3.图形系统采用垂直同步vsync机制,由lcd上报vsync,触发图层合成。

以android原生版本的launcher为例,这个场景下有四个图层,状态栏、导航栏由systemui绘制,壁纸由壁纸服务提供,图标由launcher应用绘制,图层合成就是把这四个图层按既定的显示区域,展现到显示屏上。

Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)Android显示之图层合成

这幅图描述的是带虚拟导航栏的情况,导航栏在最下,状态栏(显示电池容量、时间的那个)在最上,中间大块的区域由墙纸和图标层混合而得,其中墙纸只取一部分。

Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)Android显示之图层合成

这幅是小米平板的一个截屏,由于没有虚拟导航栏,它只有三个图层。图标层中空白部分的alpha值为0,其余部分为255,这样在与墙纸混合时,便可形成遮档效果。

三个图层是怎么看出来的呢,

输 adb shell dumpsys surfaceflinger :

Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)Android显示之图层合成

截取出这段信息,这段信息是surfaceflinger告知硬件合成器如何进行合成的。最后一个framebuffertarget是目标层,不算进去,参与合成的图层是三个,分别是

com.android.systemui.imagewallpaper,

com.miui.home/com.miui.home.launcher.launcher,

statusbar

至于其他各个参数是什么意思到后面再讲。

Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)Android显示之图层合成

先将所有图层画到一个最终层(framebuffer)上,再将framebuffer送到lcd显示。由于合成framebuffer与送lcd显示一般是异步的(线下生成framebuffer,需要时线上的lcd去取),因此叫离线合成。

不使用framebuffer,在lcd需要显示某一行的像素时,用显示控制器将所有图层与该行相关的数据取出,合成一行像素送过去。只有一个图层时,又叫overlay技术。

由于省去合成framebuffer时读图层,写framebuffer的步骤,大幅降低了内存传输量,减少了功耗,但这个需要硬件支持。

大部分情况下,在线合成比起离线合成有很明显的优势,大幅降低了内存带宽的消耗。不过对于多屏显示,静态场景(仅限lcd不带缓存的情况),离线合成会有优势,做下简单的计算不难推得。

surfaceflinger是android里面用于提供图层合成的服务,负责给应用层提供窗口,并按指定位置合成所有图层到屏幕。

surfaceflinger的代码位于

frameworks/native/services/surfaceflinger

目录下。

见 frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp

典型的 binder 服务端写法

surfaceflinger采用commander设计模式,surfaceflinger主线程接受消息,形成消息队列,逐个处理消息队列中的信息。

因此直接从onmessagereceived看起

handlemessagerefresh处理合成任务:

往下根据设备情况,会走3d合成或硬件合成。

所谓3d合成,其实是使用opengl标准,用gpu把图层画到统一的framebuffer上,然后送显。毫无疑问这是离线合成的一种。既然是按opengl标准的,我们来带着如下问题阅读:

1、opengl渲染的framebuffer是如何送到lcd的?

2、为了使用opengl绘制图像,必须把该图像内容作为纹理上传到gpu内存,然后使用opengl绑定纹理渲染。那么,每个图层是怎么样变成纹理的?glteximage2d传输上去?

3、每个图层的绘制区域是如何计算的,图层间存在重叠区域时,如何混合颜色?

4、使用opengl es的哪套标准(1.1 /2.0 /3.0)?

egl标准下,opengl环境创建的一般流程如下图所示:

Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)Android显示之图层合成

这部分工作在surfaceflinger::init函数完成,也即服务初起之时:

注意到,创建的窗口是framebuffersurface,在3d渲染完成后,会由eglswapbuffers触发queuebuffer,进而触发framebuffersurface中的onframeavailable方法:

把graphicbuffer上传为纹理,再渲染是非常cost的,因此android用的方式是共享:

Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)Android显示之图层合成

1、应用层绘制命令完成,queuebuffer回去。

2、通过binder机制,触发layer的onframeavailable回调,给surfaceflinger消息处理线程发一个layer更新的消息。

3、在收到layer更新的消息后,surfaceflinger更新所有的dirty layer,把graphicbuffer映射为opengl的texture

Android图形显示系统——下层显示4:图层合成上(合成原理与3D合成)Android显示之图层合成

中间代码流程很复杂,我们只看下实际创建的一段:

android在此新增一个renderengine类,用来屏蔽opengl es1.0、1.1和2.0的用法差异。基本用法和opengl是一样的,没有简化太多。

在创建renderengine的时候,会根据gpu所支持的opengl es 版本号,优先选择 2.0,没有2.0可用时使用1.1/1.0。

在 docomposition->dodisplaycomposition->docomposesurfaces中,会让每个layer画到每个显示屏上:

layer::draw->layer::ondraw->layer::drawwithopengl:

opengl的顶点坐标(位置坐标与纹理坐标)都在 mmesh 之中,其计算方式可以仔细看看,较为繁琐,这里不讲。

硬件合成将在下篇讲述

继续阅读