Android应用程序UI硬件加速渲染的Display List构建过程分析.docx

上传人:b****8 文档编号:9948159 上传时间:2023-02-07 格式:DOCX 页数:70 大小:209.12KB
下载 相关 举报
Android应用程序UI硬件加速渲染的Display List构建过程分析.docx_第1页
第1页 / 共70页
Android应用程序UI硬件加速渲染的Display List构建过程分析.docx_第2页
第2页 / 共70页
Android应用程序UI硬件加速渲染的Display List构建过程分析.docx_第3页
第3页 / 共70页
Android应用程序UI硬件加速渲染的Display List构建过程分析.docx_第4页
第4页 / 共70页
Android应用程序UI硬件加速渲染的Display List构建过程分析.docx_第5页
第5页 / 共70页
点击查看更多>>
下载资源
资源描述

Android应用程序UI硬件加速渲染的Display List构建过程分析.docx

《Android应用程序UI硬件加速渲染的Display List构建过程分析.docx》由会员分享,可在线阅读,更多相关《Android应用程序UI硬件加速渲染的Display List构建过程分析.docx(70页珍藏版)》请在冰豆网上搜索。

Android应用程序UI硬件加速渲染的Display List构建过程分析.docx

Android应用程序UI硬件加速渲染的DisplayList构建过程分析

Android应用程序UI硬件加速渲染的DisplayList构建过程分析

在硬件加速渲染环境中,Android应用程序窗口的UI渲染是分两步进行的。

第一步是构建DisplayList,发生在应用程序进程的MainThread中;第二步是渲染DisplayList,发生在应用程序进程的RenderThread中。

DisplayList是以视图为单位进行构建的,因此每一个视图都对应有一个DisplayList。

本文详细分析这些DisplayList的构建过程。

这里说的DisplayList与OpenGL里面的DisplayList在概念上是类似的,不过是两个不同的实现。

DisplayList的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。

这些绘制命令最终会转化为OpenGL命令由GPU执行。

这意味着我们在调用CanvasAPI绘制UI时,实际上只是将CanvasAPI调用及其参数记录在DisplayList中,然后等到下一个Vsync信号到来时,记录在DisplayList里面的绘制命令才会转化为OpenGL命令由GPU执行。

与直接执行绘制命令相比,先将绘制命令记录在DisplayList中然后再执行有两个好处。

第一个好处是在绘制窗口的下一帧时,若某一个视图的UI没有发生变化,那么就不必执行与它相关的CanvasAPI,即不用执行它的成员函数onDraw,而是直接复用上次构建的DisplayList即可。

第二个好处是在绘制窗口的下一帧时,若某一个视图的UI发生了变化,但是只是一些简单属性发生了变化,例如位置和透明度等简单属性,那么也不必重建它的DisplayList,而是直接修改上次构建的DisplayList的相关属性即可,这样也可以省去执行它的成员函数onDraw。

Android应用程序窗口视图是树形结构的,因此它们的DisplayList是从根视图开始构建的,并且子视图的DisplayList包含在父视图的DisplayList中。

这意味着根视图的DisplayList包含了Android应用程序窗口UI所有的绘制命令,因此最后我们只需要对根视图的DisplayList进行渲染即可得到Android应用程序窗口的UI,如图1所示:

Android应用程序窗口的根视图是虚拟的,抽象为一个RootRenderNode。

此外,一个视图如果设置有Background,那么这个Background也会抽象为一个BackgroundRenderNode。

RootRenderNode、BackgroundRenderNode和其它真实的子视图,除了TextureView和软件渲染的子视图之外,都具有DisplayList,并且是通过一个称为DisplayListRenderer的对象进行构建的。

TextureView不具有DisplayList,它们是通过一个称为LayerRenderer的对象以OpenGL纹理的形式来绘制的,不过这个纹理也不是直接就进行渲染的,而是先记录在父视图的DisplayList中以后再进行渲染的。

同样,软件渲染的子视图也不具有DisplayList,它们先绘制在一个Bitmap上,然后这个Bitmap再记录在父视图的DisplayList中以后再进行渲染的。

最后,RootRenderNode的DisplayList被一个称为OpenGLRenderer的对象进行渲染,就得到Android应用程序窗口的UI了。

接下来我们就结合源代码来分析Android应用程序窗口视图的DisplayList的构建过程。

在前面一文提到,Android应用程序窗口UI的绘制过程是从ViewRootImpl类的成员函数performDraw开始的,它的实现如下所示:

[java]viewplaincopy

publicfinalclassViewRootImplimplementsViewParent,

View.AttachInfo.Callbacks,HardwareRenderer.HardwareDrawCallbacks{

......

privatevoidperformDraw(){

......

try{

draw(fullRedrawNeeded);

}finally{

......

}

......

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。

ViewRootImpl类的成员函数performDraw主要是调用了另外一个成员函数draw执行UI绘制工作,后者的实现如下所示:

[java]viewplaincopy

publicfinalclassViewRootImplimplementsViewParent,

View.AttachInfo.Callbacks,HardwareRenderer.HardwareDrawCallbacks{

......

privatevoiddraw(booleanfullRedrawNeeded){

......

finalRectdirty=mDirty;

......

if(!

dirty.isEmpty()||mIsAnimating){

......

if(mAttachInfo.mHardwareRenderer!

=null&&mAttachInfo.mHardwareRenderer.isEnabled()){

......

mAttachInfo.mHardwareRenderer.draw(mView,mAttachInfo,this);

}else{

......

if(!

drawSoftware(surface,mAttachInfo,xOffset,yOffset,scalingRequired,dirty)){

return;

}

}

}

......

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。

经过一些滚动相关的处理之后,在两种情况下,需要真正地重绘窗口的下一帧。

第一种情况是当前需要更新的区域,即ViewRootImpl类的成员变量mDirty描述的脏区域不为空。

第二种情况下窗口当前有动画需要执行,即ViewRootImpl类的成员变量mIsAnimating的值等于true。

在上述两种情况下,如果ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer的值不为null,并且调用它指向的一个HardwareRenderer对象的成员函数isEnabled的返回值为true,那么就调用这个HardwareRenderer对象的另外一个成员函数draw执行渲染工作。

从前面一文可以知道,当使用硬件加速渲染时,ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer的值不为null,并且它指向的是一个ThreadedRenderer对象。

如果该ThreadedRenderer对象也设置了支持硬件加速渲染,那么调用它的成员函数isEnabled的返回值就为true。

这意味着当使用硬件加速渲染时,ViewRootImpl类的成员函数draw调用的是ThreadedRenderer类的成员函数draw。

另一方面,当使用软件渲染时,ViewRootImpl类的成员函数draw调用的是另外一个成员函数drawSoftware。

软件渲染的执行过程可以参考前面一文。

这里我们只关注硬件渲染的执行过程,因此接下来我们继续分析ThreadedRenderer类的成员函数draw的实现,如下所示:

[java]viewplaincopy

publicclassThreadedRendererextendsHardwareRenderer{

......

@Override

voiddraw(Viewview,AttachInfoattachInfo,HardwareDrawCallbackscallbacks){

......

updateRootDisplayList(view,callbacks);

......

if(attachInfo.mPendingAnimatingRenderNodes!

=null){

finalintcount=attachInfo.mPendingAnimatingRenderNodes.size();

for(inti=0;i

registerAnimatingRenderNode(

attachInfo.mPendingAnimatingRenderNodes.get(i));

}

attachInfo.mPendingAnimatingRenderNodes.clear();

//Wedon'tneedthisanymoreassubsequentcallsto

//ViewRootImpl#attachRenderNodeAnimatorwillgodirectlytous.

attachInfo.mPendingAnimatingRenderNodes=null;

}

intsyncResult=nSyncAndDrawFrame(mNativeProxy,frameTimeNanos,

recordDuration,view.getResources().getDisplayMetrics().density);

if((syncResult&SYNC_INVALIDATE_REQUIRED)!

=0){

attachInfo.mViewRootImpl.invalidate();

}

}

......

}

这个函数定义在文件frameworks/base/core/Java/android/view/ThreadedRenderer.java中。

ThreadedRenderer类的成员函数draw主要是完成以下四件事情:

1.调用成员函数updateRootDisplayList构建参数view描述的视图的DisplayList,该视图即为图1所示的DecorView。

2.调用成员函数registerAnimatingRenderNode将保存在参数attachInfo指向的一个AttachInfo对象的成员变量mPendingAnimatingRenderNodes描述的一个列表中的RenderNode注册到Native层中去。

这些RenderNode描述的是当前窗口设置的动画。

3.调用成员函数nSyncAndDrawFrame通知RenderThread绘制下一帧。

4.如果成员函数nSyncAndDrawFrame的返回值syncResult的SYNC_INVALIDATE_REQUIRED位不等于0,就表明RenderThread可能需要与MainThread进行信息同步,这时候就时候向MainThread发送一个INVALIDATE消息,以便MainThread可以进行信息同步。

这种情况一般发生在当前绘制的一帧包含有同步动画时。

例如,同步动画显示到一半,需要中止,这个中止的操作就是由MainThread发出的,然后由RenderThread检测到这个中止操作。

这里我们只关注第一件事情,其余三件事情在接下来的两篇文章中再详细分析。

ThreadedRenderer类的成员函数updateRootDisplayList的实现如下所示:

[java]viewplaincopy

publicclassThreadedRendererextendsHardwareRenderer{

......

privatevoidupdateRootDisplayList(Viewview,HardwareDrawCallbackscallbacks){

......

updateViewTreeDisplayList(view);

if(mRootNodeNeedsUpdate||!

mRootNode.isValid()){

HardwareCanvascanvas=mRootNode.start(mSurfaceWidth,mSurfaceHeight);

try{

finalintsaveCount=canvas.save();

.......

canvas.insertReorderBarrier();

canvas.drawRenderNode(view.getDisplayList());

canvas.insertInorderBarrier();

......

canvas.restoreToCount(saveCount);

mRootNodeNeedsUpdate=false;

}finally{

mRootNode.end(canvas);

}

}

......

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。

ThreadedRenderer类的成员函数updateRootDisplayList通过调用另一个成员函数updateViewTreeDisplayList来构建参数view描述的视图的DisplayList,即图1中的DecorView的DisplayList。

构建好的这个DisplayList可以通过调用参数view描述的视图的成员函数getDisplayList获得的一个RenderNode来描述。

ThreadedRenderer类的成员变量mRootNodeNeedsUpdate是一个布尔变量,当它的值等于true的时候,就表示要更新另外一个成员变量mRootNode描述的一个RenderNode的DisplayList。

另外,如果ThreadedRenderer类的成员变量mRootNode描述的RenderNode还未构建过DisplayList,那么这时候调用它的成员函数isValid的返回值为true,这种情况也表示要更新它的DisplayList。

从前面一文可以知道,ThreadedRenderer类的成员变量mRootNode描述的RenderNode即为即为当前窗口的RootNode,更新它的DisplayList实际上就是要将参数view描述的视图的DisplayList记录到它里面去,具体方法如下所示:

1.调用ThreadedRenderer类的成员变量mRootNode描述的RenderNode的成员函数start获得一个HardwareCanvas。

2.调用上面获得的HardwareCanvas的成员函数drawRenderNode将参数view描述的视图的DisplayList绘制在它里面。

在绘制参数view描述的视图的DisplayList的前后,会调用HardwareCanvas的成员函数insertReorderBarrier和insertInorderBarrier分别设置一个ReorderBarrier和一个InorderBarrier。

后面我们在分析DisplayList绘制在HardwareCanvas的过程时就会看到,插入这些Barrier是为了将一个View的所有的DrawOp及其子View对应的DrawOp记录在一个Chunk中。

其中,ReorderBarrier表明在真正渲染这些Chunck记录的DrawOp时,需要考虑按照Z轴坐标值重新排列子View的渲染顺序。

3.调用ThreadedRenderer类的成员变量mRootNode描述的RenderNode的成员函数end取出上述已经绘制好的HardwareCanvas的数据,并且作为上述RenderNode的新的DisplayList。

接下来,我们首先分析ThreadedRenderer类的成员变量mRootNode描述的RenderNode的DisplayList的更新过程,即RootNode类的成员函数start、HardwareCanvas类的成员函数drawRenderNode和RootNode类的成员函数end的实现,然后再回过头来分析参数view描述的视图的DisplayList的构建过程,即ThreadedRenderer类的成员函数updateViewTreeDisplayList的实现。

RootNode类的成员函数start的实现如下所示:

[java]viewplaincopy

publicclassRenderNode{

......

publicHardwareCanvasstart(intwidth,intheight){

HardwareCanvascanvas=GLES20RecordingCanvas.obtain(this);

canvas.setViewport(width,height);

......

returncanvas;

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/RenderNode.java中。

RootNode类的成员函数start的核心是调用GLES20RecordingCanvas类的静态成员函数obtain一个类型为GLES20RecordingCanvas的HardwareCanvas,然后在设置了该HardwareCanvas的ViewPort之后,返回给调用者。

GLES20RecordingCanvas类的静态成员函数obtain的实现如下所示:

[java]viewplaincopy

[java]viewplaincopy

classGLES20RecordingCanvasextendsGLES20Canvas{

......

privatestaticfinalintPOOL_LIMIT=25;

privatestaticfinalSynchronizedPoolsPool=

newSynchronizedPool(POOL_LIMIT);

RenderNodemNode;

privateGLES20RecordingCanvas(){

super();

}

staticGLES20RecordingCanvasobtain(@NonNullRenderNodenode){

......

GLES20RecordingCanvascanvas=sPool.acquire();

if(canvas==null){

canvas=newGLES20RecordingCanvas();

}

canvas.mNode=node;

returncanvas;

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/GLES20RecordingCanvas.java中。

GLES20RecordingCanvas类的静态成员函数obtain首先是从一个GLES20RecordingCanvas对象池中请求一个GLES20RecordingCanvas对象。

如果获取失败,再直接创建一个GLES20RecordingCanvas对象。

在将获取到的GLES20RecordingCanvas对象返回给调用者之前,还会将参数node描述的RenderNode保存在其成员变量mNode中。

接下来我们继续关注GLES20RecordingCanvas对象的创建过程,即GLES20RecordingCanvas类的构造函数的实现。

GLES20RecordingCanvas类的构造函数只是简单调用了父类GLES20Canvas的构造函数,它的实现如下所示:

[java]viewplaincopy在CODE上查看代码片派生到我的代码片

classGLES20CanvasextendsHardwareCanvas{

......

protectedlongmRenderer;

......

protectedGLES20Canvas(){

......

mRenderer=nCreateDisplayListRenderer();

......

}

......

}

这个函数定义在文件frameworks/base/core/java/android/view/GLES20Canvas.java中。

GLES20Canvas类的构造函数最主要做的事情就是调用另外一个成员函数nCreateDisplayListRenderer在Native层创建了一个DisplayListRenderer,并且将它的地址保存在成员变量mRenderer中。

GLES20Canvas类的成员函数nCreateDisplayListRenderer是一个JNI函数,由Native层的函数android_view_GLES20Canvas_createDisplayListRenderer实现,如下所示:

[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片

staticjlongandroid_view_GLES20Canvas_createDisplayListRenderer(JNIEnv*env,jobjectclazz){

returnreinterpret_cast(newDisplayListR

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1