ImageVerifierCode 换一换
格式:DOCX , 页数:70 ,大小:209.12KB ,
资源ID:9948159      下载积分:12 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/9948159.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Android应用程序UI硬件加速渲染的Display List构建过程分析.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

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

1、Android应用程序UI硬件加速渲染的Display List构建过程分析Android应用程序UI硬件加速渲染的Display List构建过程分析在硬件加速渲染环境中,Android应用程序窗口的UI渲染是分两步进行的。第一步是构建Display List,发生在应用程序进程的Main Thread中;第二步是渲染Display List,发生在应用程序进程的Render Thread中。Display List是以视图为单位进行构建的,因此每一个视图都对应有一个Display List。本文详细分析这些Display List的构建过程。这里说的Display List与Open GL

2、里面的Display List在概念上是类似的,不过是两个不同的实现。Display List的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。这些绘制命令最终会转化为Open GL命令由GPU执行。这意味着我们在调用Canvas API绘制UI时,实际上只是将Canvas API调用及其参数记录在Display List中,然后等到下一个Vsync信号到来时,记录在Display List里面的绘制命令才会转化为Open GL命令由GPU执行。与直接执行绘制命令相比,先将绘制命令记录在Display List中然后再执行有两个好处。第一个好处是在绘制窗口的下一帧时,若某一个视图的UI

3、没有发生变化,那么就不必执行与它相关的Canvas API,即不用执行它的成员函数onDraw,而是直接复用上次构建的Display List即可。第二个好处是在绘制窗口的下一帧时,若某一个视图的UI发生了变化,但是只是一些简单属性发生了变化,例如位置和透明度等简单属性,那么也不必重建它的Display List,而是直接修改上次构建的Display List的相关属性即可,这样也可以省去执行它的成员函数onDraw。 Android应用程序窗口视图是树形结构的,因此它们的Display List是从根视图开始构建的,并且子视图的Display List包含在父视图的Display List中

4、。这意味着根视图的Display List包含了Android应用程序窗口UI所有的绘制命令,因此最后我们只需要对根视图的Display List进行渲染即可得到Android应用程序窗口的UI,如图1所示:Android应用程序窗口的根视图是虚拟的,抽象为一个Root Render Node。此外,一个视图如果设置有Background,那么这个Background也会抽象为一个Background Render Node。Root Render Node、Background Render Node和其它真实的子视图,除了TextureView和软件渲染的子视图之外,都具有Display

5、List,并且是通过一个称为Display List Renderer的对象进行构建的。TextureView不具有Display List,它们是通过一个称为Layer Renderer的对象以Open GL纹理的形式来绘制的,不过这个纹理也不是直接就进行渲染的,而是先记录在父视图的Display List中以后再进行渲染的。同样,软件渲染的子视图也不具有Display List,它们先绘制在一个Bitmap上,然后这个Bitmap再记录在父视图的Display List中以后再进行渲染的。 最后,Root Render Node的Display List被一个称为Open GL Rende

6、rer的对象进行渲染,就得到Android应用程序窗口的UI了。接下来我们就结合源代码来分析Android应用程序窗口视图的Display List的构建过程。 在前面一文提到,Android应用程序窗口UI的绘制过程是从ViewRootImpl类的成员函数performDraw开始的,它的实现如下所示:java view plain copypublic final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks . priv

7、ate void performDraw() . try draw(fullRedrawNeeded); finally . . . 这个函数定义在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。 ViewRootImpl类的成员函数performDraw主要是调用了另外一个成员函数draw执行UI绘制工作,后者的实现如下所示:java view plain copypublic final class ViewRootImpl implements ViewParent, View.AttachInfo.Callback

8、s, HardwareRenderer.HardwareDrawCallbacks . private void draw(boolean fullRedrawNeeded) . final Rect dirty = mDirty; . if (!dirty.isEmpty() | mIsAnimating) . if (mAttachInfo.mHardwareRenderer != null & mAttachInfo.mHardwareRenderer.isEnabled() . mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo,

9、 this); else . if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty) return; . . 这个函数定义在文件frameworks/base/core/java/android/view/ViewRootImpl.java中。 经过一些滚动相关的处理之后,在两种情况下,需要真正地重绘窗口的下一帧。第一种情况是当前需要更新的区域,即ViewRootImpl类的成员变量mDirty描述的脏区域不为空。第二种情况下窗口当前有动画需要执行,即ViewRootImpl类的成员变量

10、mIsAnimating的值等于true。 在上述两种情况下,如果ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer的值不为null,并且调用它指向的一个HardwareRenderer对象的成员函数isEnabled的返回值为true,那么就调用这个HardwareRenderer对象的另外一个成员函数draw执行渲染工作。从前面一文可以知道,当使用硬件加速渲染时,ViewRootImpl类的成员变量mAttachInfo指向的一个AttachInfo对象的成员变量mHardwareRenderer的值不为

11、null,并且它指向的是一个ThreadedRenderer对象。如果该ThreadedRenderer对象也设置了支持硬件加速渲染,那么调用它的成员函数isEnabled的返回值就为true。这意味着当使用硬件加速渲染时,ViewRootImpl类的成员函数draw调用的是ThreadedRenderer类的成员函数draw。另一方面,当使用软件渲染时,ViewRootImpl类的成员函数draw调用的是另外一个成员函数drawSoftware。 软件渲染的执行过程可以参考前面一文。这里我们只关注硬件渲染的执行过程,因此接下来我们继续分析ThreadedRenderer类的成员函数draw的

12、实现,如下所示:java view plain copypublic class ThreadedRenderer extends HardwareRenderer . Override void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) . updateRootDisplayList(view, callbacks); . if (attachInfo.mPendingAnimatingRenderNodes != null) final int count = attachInfo.mPe

13、ndingAnimatingRenderNodes.size(); for (int i = 0; i count; i+) registerAnimatingRenderNode( attachInfo.mPendingAnimatingRenderNodes.get(i); attachInfo.mPendingAnimatingRenderNodes.clear(); / We dont need this anymore as subsequent calls to / ViewRootImpl#attachRenderNodeAnimator will go directly to

14、us. attachInfo.mPendingAnimatingRenderNodes = null; int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, recordDuration, view.getResources().getDisplayMetrics().density); if (syncResult & SYNC_INVALIDATE_REQUIRED) != 0) attachInfo.mViewRootImpl.invalidate(); . 这个函数定义在文件frameworks/base/co

15、re/Java/android/view/ThreadedRenderer.java中。 ThreadedRenderer类的成员函数draw主要是完成以下四件事情: 1. 调用成员函数updateRootDisplayList构建参数view描述的视图的Display List,该视图即为图1所示的Decor View。 2. 调用成员函数registerAnimatingRenderNode将保存在参数attachInfo指向的一个AttachInfo对象的成员变量mPendingAnimatingRenderNodes描述的一个列表中的Render Node注册到Native层中去。这些

16、Render Node描述的是当前窗口设置的动画。 3. 调用成员函数nSyncAndDrawFrame通知Render Thread绘制下一帧。 4. 如果成员函数nSyncAndDrawFrame的返回值syncResult的SYNC_INVALIDATE_REQUIRED位不等于0,就表明Render Thread可能需要与Main Thread进行信息同步,这时候就时候向Main Thread发送一个INVALIDATE消息,以便Main Thread可以进行信息同步。这种情况一般发生在当前绘制的一帧包含有同步动画时。例如,同步动画显示到一半,需要中止,这个中止的操作就是由Main T

17、hread发出的,然后由Render Thread检测到这个中止操作。 这里我们只关注第一件事情,其余三件事情在接下来的两篇文章中再详细分析。 ThreadedRenderer类的成员函数updateRootDisplayList的实现如下所示:java view plain copypublic class ThreadedRenderer extends HardwareRenderer . private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) . updateViewTreeDispl

18、ayList(view); if (mRootNodeNeedsUpdate | !mRootNode.isValid() HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); try final int saveCount = canvas.save(); . canvas.insertReorderBarrier(); canvas.drawRenderNode(view.getDisplayList(); canvas.insertInorderBarrier(); . canvas.restore

19、ToCount(saveCount); mRootNodeNeedsUpdate = false; finally mRootNode.end(canvas); . . 这个函数定义在文件frameworks/base/core/java/android/view/ThreadedRenderer.java中。 ThreadedRenderer类的成员函数updateRootDisplayList通过调用另一个成员函数updateViewTreeDisplayList来构建参数view描述的视图的Display List,即图1中的Decor View的Display List。构建好的这个D

20、isplay List可以通过调用参数view描述的视图的成员函数getDisplayList获得的一个Render Node来描述。 ThreadedRenderer类的成员变量mRootNodeNeedsUpdate是一个布尔变量,当它的值等于true的时候,就表示要更新另外一个成员变量mRootNode描述的一个Render Node的Display List。另外,如果ThreadedRenderer类的成员变量mRootNode描述的Render Node还未构建过Display List,那么这时候调用它的成员函数isValid的返回值为true,这种情况也表示要更新它的Displ

21、ay List。 从前面一文可以知道,ThreadedRenderer类的成员变量mRootNode描述的Render Node即为即为当前窗口的Root Node,更新它的Display List实际上就是要将参数view描述的视图的Display List记录到它里面去,具体方法如下所示: 1. 调用ThreadedRenderer类的成员变量mRootNode描述的Render Node的成员函数start获得一个Hardware Canvas。 2. 调用上面获得的Hardware Canvas的成员函数drawRenderNode将参数view描述的视图的Display List绘制

22、在它里面。在绘制参数view描述的视图的Display List的前后,会调用Hardware Canvas的成员函数insertReorderBarrier和insertInorderBarrier分别设置一个Reorder Barrier和一个Inorder Barrier。后面我们在分析Display List绘制在Hardware Canvas的过程时就会看到,插入这些Barrier是为了将一个View的所有的Draw Op及其子View对应的Draw Op记录在一个Chunk中。其中,Reorder Barrier表明在真正渲染这些Chunck记录的Draw Op时,需要考虑按照Z轴

23、坐标值重新排列子View的渲染顺序。 3. 调用ThreadedRenderer类的成员变量mRootNode描述的Render Node的成员函数end取出上述已经绘制好的Hardware Canvas的数据,并且作为上述Render Node的新的Display List。 接下来,我们首先分析ThreadedRenderer类的成员变量mRootNode描述的Render Node的Display List的更新过程,即RootNode类的成员函数start、HardwareCanvas类的成员函数drawRenderNode和RootNode类的成员函数end的实现,然后再回过头来分析

24、参数view描述的视图的Display List的构建过程,即ThreadedRenderer类的成员函数updateViewTreeDisplayList的实现。 RootNode类的成员函数start的实现如下所示:java view plain copypublic class RenderNode . public HardwareCanvas start(int width, int height) HardwareCanvas canvas = GLES20RecordingCanvas.obtain(this); canvas.setViewport(width, height)

25、; . return canvas; . 这个函数定义在文件frameworks/base/core/java/android/view/RenderNode.java中。 RootNode类的成员函数start的核心是调用GLES20RecordingCanvas类的静态成员函数obtain一个类型为GLES20RecordingCanvas的Hardware Canvas,然后在设置了该Hardware Canvas的View Port之后,返回给调用者。 GLES20RecordingCanvas类的静态成员函数obtain的实现如下所示:java view plain copy jav

26、a view plain copyclass GLES20RecordingCanvas extends GLES20Canvas . private static final int POOL_LIMIT = 25; private static final SynchronizedPool sPool = new SynchronizedPool(POOL_LIMIT); RenderNode mNode; private GLES20RecordingCanvas() super(); static GLES20RecordingCanvas obtain(NonNull RenderN

27、ode node) . GLES20RecordingCanvas canvas = sPool.acquire(); if (canvas = null) canvas = new GLES20RecordingCanvas(); canvas.mNode = node; return canvas; . 这个函数定义在文件frameworks/base/core/java/android/view/GLES20RecordingCanvas.java中。 GLES20RecordingCanvas类的静态成员函数obtain首先是从一个GLES20RecordingCanvas对象池中请求

28、一个GLES20RecordingCanvas对象。如果获取失败,再直接创建一个GLES20RecordingCanvas对象。在将获取到的GLES20RecordingCanvas对象返回给调用者之前,还会将参数node描述的Render Node保存在其成员变量mNode中。 接下来我们继续关注GLES20RecordingCanvas对象的创建过程,即GLES20RecordingCanvas类的构造函数的实现。GLES20RecordingCanvas类的构造函数只是简单调用了父类GLES20Canvas的构造函数,它的实现如下所示:java view plain copy 在CODE

29、上查看代码片派生到我的代码片class GLES20Canvas extends HardwareCanvas . protected long mRenderer; . protected GLES20Canvas() . mRenderer = nCreateDisplayListRenderer(); . . 这个函数定义在文件frameworks/base/core/java/android/view/GLES20Canvas.java中。 GLES20Canvas类的构造函数最主要做的事情就是调用另外一个成员函数nCreateDisplayListRenderer在Native层创建

30、了一个Display List Renderer,并且将它的地址保存在成员变量mRenderer中。 GLES20Canvas类的成员函数nCreateDisplayListRenderer是一个JNI函数,由Native层的函数android_view_GLES20Canvas_createDisplayListRenderer实现,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片static jlong android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env, jobject clazz) return reinterpret_cast(new DisplayListR

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

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