上一篇讲了Activity的绘制流程(四)Surface
(//www.greatytc.com/p/5315fea34445),本篇主讲(五)RenderThread。
(一)窗口的添加
(二)Choreographer
(三)VSync
(四)Surface
(五)RenderThread
(六)StartingWIndow
(七)窗口切换
从Android4.0开始,系统默认开启硬件加速渲染。Android中的硬件渲染是指Android应用程序通过GPU将图形渲染到GraphicsBuffer的过程。当然除了硬件渲染,还有软件渲染,即使用CPU完成渲染。GPU相对于CPU的渲染优势在于,GPU擅长数学运算,CPU擅长逻辑运算,所以GPU更擅长于图形计算。
初学硬件渲染的时候,经常会看见GPU、OpenGL和OpenGL ES,在这里稍微解释一下三者的区别。GPU是一个硬件,用户空间不可直接使用。OpenGL是一个跨编程语言、跨平台的图形程序接口,GPU厂商按照OpenGL规范实现的驱动来操作GPU。所以Android中的硬件加速渲染,具体是指Android应用程序调用OpenGL接口使用GPU将图形渲染到GraphicsBuffer的过程。而OpenGL ES是OpenGL三维图形API的子集,是OpenGL的嵌入式设备版本。
在上一篇文章提到了BufferQueue、应用端、SurfaceFlinger构成了一个生产者-消费者模型。生产者(应用端)从BufferQueue中取得GraphicsBuffer,将UI绘制到GraphicsBuffer,然后将GraphicsBuffer放回BufferQueue。消费者(SurfaceFlinger)从BufferQueue中取得GraphicsBuffer,进行合成及显示,然后将GraphicsBuffer放回BufferQueue。生产者的工作其实主要是由RenderThread负责的。
一、RenderThread的创建
从代码中可以看到,Android4.0开始会默认添加flag FLAG_HARDWARE_ACCELERATED,系统开启硬件加速渲染。在代码里还有另外两个flag PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED和PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED,用于开启一个假的硬件加速和强制开启硬件加速。
PackageParser.java
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError) {
...
owner.baseHardwareAccelerated = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
if (owner.baseHardwareAccelerated) {
ai.flags |= ApplicationInfo.FLAG_HARDWARE_ACCELERATED;
}
...
}
WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
if (context != null && (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
...
}
ViewRootImpl.java
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
...
final boolean hardwareAccelerated =
(attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
if (hardwareAccelerated) {
final boolean fakeHwAccelerated = (attrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) != 0;
final boolean forceHwAccelerated = (attrs.privateFlags &
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) != 0;
if (fakeHwAccelerated) {
...
} else if (!ThreadedRenderer.sRendererDisabled
|| (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) {
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
attrs.getTitle().toString());
}
...
}
...
}
开启硬件加速后,会启一个名为RenderThread的线程,RenderThread线程会创建一个EglManager对象,EglManager用于调用OpenGL ES的API函数,如eglCreateWindowSurface创建一个EglSurface对象,不同厂商的实现不一样。
二、EglSurface
Android使用OpenGL ES(GLES)的API渲染图形。为了创建GLES上下文并为GLES渲染提供窗口系统,Android使用EGL库。在使用 GLES 进行绘制之前,需要创建 GL上下文。在 EGL中,这意味着要创建一个 EGLContext和一个 EGLSurface。 EGLSurface可以是由EGL分配的离屏缓冲区,也可以是由操作系统分配的窗口。调用 eglCreateWindowSurface() 函数可创建EGL窗口Surface,并将其连接到窗口对象的 BufferQueue 的生产方接口。eglCreateWindowSurface() 将“窗口对象”作为参数,在 Android 上,该对象是 Surface。
三、构建DisplayList
其实这部分的工作是在主线程完成,而不是RenderThread线程,在此进行说明是为了更好的梳理RenderThread线程的工作。
Activity的绘制流程(二)Choreographer中讲到了应用接收到Vsync后,向主线程发送一个异步消息,在主线程中执行Choreographer的doFrame函数,一一处理CallbackQueue数组中保存的操作,其中CALLBACK_TRAVERSAL会向WMS请求重新布局该窗口,同时还会对窗口进行测量、布局、绘制。其中绘制就是递归DecorView为根的View树,构建或更新DisplayList。DisplayList主要包含两个重要的对象,一个是deque队列mChildNodes,View对应一个RenderNode,RenderNode被封装在一个RenderNodeDrawable对象中,并加入到该队列,该队列用于后续同步时取得保存的子RenderNode。另一个是DisplayListData对象mDisplayList,该对象中保存了各个Op,一个Op代表一个绘制操作。
四、同步DisplayList
当主线构建或更新DisplayList完毕后,会进入睡眠等待RenderThread线程同步DisplayList,当同步完成后再唤醒主线程。
在同步时,会调度到DisplayList中每个RenderNode上,将RenderNode的mStagingProperties和mStagingDisplayList保存到mProperties和mDisplayList上。mStagingProperties在三、构建DisplayList中调用RenderNode的setStagingDisplayList函数设置。
而mStagingDisplayList的设置这里以设置透明度举例,调用RenderNode的mutateStagingProperties函数拿到的便是mStagingProperties,将透明度设置到mStagingProperties中。
同步还会判断若UI线程注册动画到Render线程,则由Render线程进行动画的计算和绘制。同步时发现View被设置为LAYER_TYPE_HARDWARE,则会将View转变成一个Layer进行绘制,在GPU上有一个帧缓冲区,不会每一帧都去销毁和重建离屏渲染buffer。
五、绘制
RenderThread线程同步了DisplayList后,RenderThread线程会从Surface对应的图形缓冲区队列BufferQueue中申请一块图形缓冲区GraphicBuffer进行绘制,将DisplayList中的各个绘制操作填充进去,最后将该GraphicBuffer放回队列中。