硬件加速动画执行过程分析

本文深入探讨了Android 5.0中引入的Render Thread如何提升动画流畅性,包括在动画开始前为目标View设置LAYER_TYPE_HARDWARE以利用GPU渲染和Main Thread不参与动画显示时将动画注册到Render Thread。文章通过代码分析了动画显示的两种优化,涉及Main Thread与Render Thread的交互、Layer的创建及动画计算与显示的职责划分,揭示了Android系统中实现流畅动画的关键技术细节。
摘要由CSDN通过智能技术生成

通常我们说一个系统不如另一个系统流畅,说的就是前者动画显示不如后者流畅,因此动画显示流畅程度是衡量一个系统流畅性的关键指标。为什么这样说呢?这是因为流畅的动画显示需要60fps的UI刷新速度,然而这却不是一个容易达到的速度。Android 5.0通过引入Render Thread尽最大努力提升动画显示流畅性。

在前面我们提到了Render Thread对动画显示的两个优化。第一个优化是在动画显示期间,临时将动画的目标View的Layer Type设置为LAYER_TYPE_HARDWARE,这样就可以使得目标View以Open GL里面的Frame Buffer Object(FBO)进行渲染。这种优化的效果就如Render Thread直接以Open GL里面的Texture来渲染TextureView一样。第二个优化是在Main Thread不需要参与动画的显示过程时,动画就会被注册到Render Thread中,这样动画的计算和显示过程就完全由Render Thread来负责。这种优化带来的好处就是在动画显示期间,Main Thread可以去处理其它的用户输入,而且动画的显示也会更加流畅。

上面描述的两种动画优化涉及到的Main Thread和Render Thread的交互过程如图所示:

在这里插入图片描述
接下来,我们就通过代码分析上述的两种动画显示优化过程。

我们通过调用View类的成员函数animate可以获得一个ViewPropertyAnimator对象,如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,  
        AccessibilityEventSource {
     
    ......  
  
    public ViewPropertyAnimator animate() {
     
        if (mAnimator == null) {
     
            mAnimator = new ViewPropertyAnimator(this);  
        }  
        return mAnimator;  
    }  
  
    ......  
} 

有了这个ViewPropertyAnimator对象之后,就可以调用它的成员函数withLayer将它关联的View的Layer Type设置为LAYER_TYPE_HARDWARE,如下所示:

public class ViewPropertyAnimator {
     
    ......  
  
    public ViewPropertyAnimator withLayer() {
     
         mPendingSetupAction= new Runnable() {
     
            @Override  
            public void run() {
     
                mView.setLayerType(View.LAYER_TYPE_HARDWARE, null);  
                if (mView.isAttachedToWindow()) {
     
                    mView.buildLayer();  
                }  
            }  
        };  
        final int currentLayerType = mView.getLayerType();  
        mPendingCleanupAction = new Runnable() {
     
            @Override  
            public void run() {
     
                mView.setLayerType(currentLayerType, null);  
            }  
        };  
        ......  
        return this;  
    }  
  
    ......  
}  

ViewPropertyAnimator类的成员函数withLayer创建了两个Runnable,分别保存在成员变量mPendingSetupAction和mPendingCleanupAction中。其中,成员变量mPendingSetupAction指向的Runnable在动画开始显示之前执行,而成员变量mPendingCleanupAction指向的Runnable在动画结束显示之后执行。由此我们就可以看到:

  1. 在动画开始显示之前,目标View的Layer Type会被设置为LAYER_TYPE_HARDWARE,并且它的成员函数buildLayer会被调用来创建一个Layer。
  2. 在动画结束显示之后,目标View的Layer Type会被恢复为它之前的Layer Type。注意,这里调用目标View的成员函数getLayerType获得的是它的Layer Type未被设置为LAYER_TYPE_HARDWARE的Layer Type。

接下来我们就继续分析View类的成员函数buildLayer的实现,以便可以了解为一个View设置一个Layer的过程。

View类的成员函数buildLayer的实现如下所示:

public class View implements Drawable.Callback, KeyEvent.Callback,  
        AccessibilityEventSource {
     
    ......  
  
    public void buildLayer() {
     
        ......  
  
        final AttachInfo attachInfo = mAttachInfo;  
        ......  
  
        switch (mLayerType) {
     
            case LAYER_TYPE_HARDWARE:  
                updateDisplayListIfDirty();  
                if (attachInfo.mHardwareRenderer != null && mRenderNode.isValid()) {
     
                    attachInfo.mHardwareRenderer.buildLayer(mRenderNode);  
                }  
                break;  
            case LAYER_TYPE_SOFTWARE:  
                buildDrawingCache(true);  
                break;  
        }  
    }  
  
    ......  
}  

前面已经将当前正在处理的View的Layer Type设置为LAYER_TYPE_HARDWARE,因此View类的成员函数buildLayer首先是调用我们在前面Android应用程序UI硬件加速渲染的Display List构建过程分析一文中分析过的View类的另外一个成员函数updateDisplayListIfDirty更新它的Display List。更新完毕之后,再调用保存在成员变量mAttachInfo描述的一个AttachInfo对象的成员变量mHardwareRenderer指向的一个ThreadedRenderer对象的成员函数buildLayer为当前正在处理的View创建一个Layer。

从这里还可以看到,如果当前正在处理的View的Layer Type被设置为LAYER_TYPE_SOFTWARE,即该View是以软件方式进行渲染的,那么就会调用另外一个成员函数buildDrawingCache将View上次绘制得到的UI缓存在一个Bitmap中,以便下次可以快速地绘制View动画的下一帧。View类的成员函数buildDrawingCache的实现,同样可以参考前面Android应用程序UI硬件加速渲染的Display List构建过程分析一文。

接下来我们主要关注为一个View创建一个Layer的过程,即ThreadedRenderer对象的成员函数buildLayer的实现,如下所示:

public class ThreadedRenderer extends HardwareRenderer {
     
    ......  
  
    @Override  
    void buildLayer(RenderNode node) {
     
        nBuildLayer(mNativeProxy, node.getNativeDisplayList());  
    }  
  
    ......  
}  

ThreadedRenderer对象的成员函数buildLayer调用另外一个成员函数nBuildLayer为参数node描述的一个Render Node关联的View创建一个Layer。

ThreadedRenderer对象的成员函数nBuildLayer是一个JNI函数,由Native层的函数android_view_ThreadedRenderer_buildLayer实现,如下所示:

static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz,  
        jlong proxyPtr, jlong nodePtr) {
     
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);  
    RenderNode* node = reinterpret_cast<RenderNode*>(nodePtr);  
    proxy->buildLayer(node);  
}  

参数proxyPtr描述的是一个RenderProxy对象,这里调用它的成员函数buildLayer为参数nodePtr描述的一个Render Node创建一个Layer。

RenderProxy类的成员函数buildLayer的实现如下所示:

CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) {
     
    args->context->buildLayer(args->node);  
    return NULL;  
}  
  
void RenderProxy::buildLayer(RenderNode* node) {
     
    SETUP_TASK(buildLayer);  
    args->context = mContext;  
    args->node = node;  
    postAndWait(task);  
}  

RenderProxy类的成员函数buildLayer首先是通过宏SETUP_TASK创建一个Task,接下来再调用另外一个成员函数postAndWait将该Task添加到Render Thread的Task Queue去等待执行。最后这个Task由宏CREATE_BRIDGE2声明的函数buildLayer来执行,主要就是调用参数context描述的一个CanvasContext对象的成员函数buildLayer为参数node描述的一个Render Node创建一个Layer。

CanvasContext类的成员函数buildLayer的实现如下所示:

void CanvasContext::buildLayer(RenderNode* node) {
     
    ......  
  
    TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());  
    ......  
    node->prepareTree(info);  
    ......  
  
    mCanvas->flushLayerUpdates();  
  
    ......  
} 

这里就可以看到CanvasContext类的成员函数buildLayer调用了我们在前面Android应用程序UI硬件加速渲染的Display List渲染过程分析一文的关键函数——RenderNode类的成员函数prepareTree,它用来将参数node描述的Render Node的Display List从Main Thread同步到Render Thread中,并且为该Render Node创建了一个Layer,但是这个Layer处理待更新状态。

CanvasContext类的成员函数buildLayer接下来继续调用成员变量mCanvas指向的一个OpenGLRenderer对象的成员函数flushLayerUpdates更新刚才创建的Layer,它的实现如下所示:

void OpenGLRenderer::flushLayerUpdates() {
     
    ......  
    updateLayers();  
    flushLayers();  
    ......  
}  

OpenGLRenderer类的成员函数flushLayerUpdates主要是执行了以下两个操作:

  1. 调用成员函数updateLayers重排和合并所有设置了Layer的Render Node的Display List的绘制命令,这些Layer包括了我们在前面一步创建的Layer。
  2. 调用成员函数flushLayers执行所有设置了Layer的经过了重排和合并的Render Node的Display List的绘制命令,使得每一个这样的Render Node的UI都分别渲染在一个FBO上。

这样,我们就临时地为一个即将要显示动画的View创建了一个Layer,这个Layer将即将要显示的动画的View的UI渲染在一个FBO,这样以后就可以基于这个FBO来更高效率地显示动画了。

后面的动画显示过程实质上就不断地渲染应用程序窗口的UI,这个过程可以参考前面Android应用程序UI硬件加速渲染的Display List渲染过程分析一文。不过,再结合前面Android应用程序UI硬件加速渲染的Display List构建过程分析一文,我们可以知道,在这个过程中,并不是应用程序窗口视图中的每一个View的Display List都是需要重建的,而且对于要显示动画的View,我们也只是需要将动画参数应用在前面为它创建的FBO之上即可。当然,为了得到应用程序窗口的UI,在渲染的过程中,需要重新执行一遍应用程序窗口视图中的每一个View的Display List的绘制命令。我们可以将这个过程看作是应用程序窗口的各个View的UI合成过程。相对于应用程序窗口的每一个View的Display List构建,以及对它里面的绘制命令进行重排和合并的过程来说,上述合成过程的成本是低很多的。

以上分析的就是View动画显示过程中的第一种优化,即在View的动画开始显示之前,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值