Android_硬件加速简述

        Android代码在 manifest 中 activity 属性上可以使用  hardwareAccelerated = “true” 来开启硬件加速,我们刚开始,从字面上看出硬件加速,好像利用硬件解码,能使图形图像输出更加快速省电,但是究竟为什么,却不求审结。

        本文,力求使用最简单的语言,最少的文字和代码量,让你,知道为什么,和相关的类方法。在知道硬件加速之前,起码,你需要知道 framework 中 view 树的几个相关的根类, window(PhoneWindow为实现类),Activity:setContentView,DecorView,ViewRoot,主入口 ActivityThread 中 handleResumeActivity 中几个关键点,WindowManagerGlobal(与WIndowManagerImpl),Choreographer负责帧率刷新控制。关键的是,ViewRoot(实现类 ViewRootImpl)。

        本文的诞生,离不开社区技术人员的添火加薪,融合很很多博客写画的流程图,和标注的代码段。

        ViewRoot 是 framework 硬件native 层连接 framework View层关键的中间者,它驱动来 performDraw,performEvent,把事件分发到 view 树上,和绘图责任链,画出整个屏幕的视图。

ViewRoot中一个中枢方法是 performTraverslas() 接近800行代码,

//ViewRootImpl


    private void performDraw() {
        ``````
        try {
            draw(fullRedrawNeeded);
        }
    }

    private void draw(boolean fullRedrawNeeded) {
        Surface surface = mSurface;
        ``````
        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
            //支持及开启了硬件加速
            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
                //ThreadedRenderer对象
                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);//硬件加速绘制
            } else {
                //软件绘制
                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
            }
        }
    }

    // These can be accessed by any thread, must be protected with a lock.
    // Surface can never be reassigned or cleared (use Surface.clear()).
    final Surface mSurface = new Surface();//每个ViewRootImpl对应一个Surface
/**
     * @return true if drawing was successful, false if an error occurred
     */
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        final Canvas canvas;

        canvas = mSurface.lockCanvas(dirty);//获取Skia Canvas

        try {

            mView.draw(canvas);//图形绘制
        } finally {
                surface.unlockCanvasAndPost(canvas);//将绘制的结果进行提交
        }
        return true;
    }

        硬件加速构建阶段,会递归遍历所有视图,View视图抽象成 RenderNode节点,绘制抽象成DrawOp(DisplayListOp),View 背景也会被看做RenderNode上关联到宿主RenderNode上。全局构成 DisplayList(图形缓冲区(绘制命令缓冲区)),

然后引出硬件加速最关键的类,ThreadedRender。RenderProxy对象-->这个对象就是用来跟渲染线程进行通信的句柄,RenderThread是一个单例线程,每个进程最多只有一个硬件渲染线程。

//ThreadedRenderer

    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
        attachInfo.mIgnoreDirtyState = true;

        final Choreographer choreographer = attachInfo.mViewRootImpl.mChoreographer;
        choreographer.mFrameInfo.markDrawStart();
        //<!--关键点1:构建View的DrawOp树-->
        updateRootDisplayList(view, callbacks);//构建View的DrawOp树
        ``````
        //<!--关键点2:通知RenderThread线程绘制-->
        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
        ... ...
    }

    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
        //来构建参数view(DecorView)视图的Display List.<!--更新-->
        updateViewTreeDisplayList(view);

        //mRootNodeNeedsUpdate true表示要更新视图
        //mRootNode.isValid()  表示已经构建了Display List
        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
            //<!--获取DisplayListCanvas-->
            DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
            try {
                //<!--利用canvas缓存Op-->
                final int saveCount = canvas.save();
                canvas.translate(mInsetLeft, mInsetTop);
                callbacks.onHardwarePreDraw(canvas);

                //ReorderBarrie表示会按照Z轴坐标值重新排列子View的渲染顺序
                canvas.insertReorderBarrier();
                //构建并缓存所有的DrawOp
                canvas.drawRenderNode(view.updateDisplayListIfDirty());
                canvas.insertInorderBarrier();

                canvas.restoreToCount(saveCount);
            } finally {
                //将所有的DrawOp填充到根RootNode中,作为新的Display List
                //<!--将所有Op填充到RootRenderNode-->
                mRootNode.end(canvas);
            }
        }
    }

!只有支持并开启硬件加速的View才会关联有RenderNode,同时GPU不是支持所有的2D UI 绘制命令具体可以查看android developer文档,所以GPU不支持的绘制命令只能通过软件方式来绘制渲染。

DrawOp树构建完毕后,UI线程利用RenderProxy向RenderThread线程发送一个DrawFrameTask任务请求,RenderThread被唤醒,开始渲染,大致流程如下:

  • 首先进行DrawOp的合并
  • 接着绘制特殊的Layer
  • 最后绘制其余所有的DrawOpList
  • 调用swapBuffers将前面已经绘制好的图形缓冲区提交给Surface Flinger合成和显示。

硬件加速绘制到共享内存中,才会被SurfaceFlinger合成,
        硬件加速渲染和软件渲染一样,在开始渲染之前,都是要先向 SurfaceFlinger 服务Dequeue一个Graphic Buffer。不过对硬件加速渲染来说,这个Graphic Buffer会被封装成一个ANativeWindow,并且传递给Open GL进行硬件加速渲染环境初始化。在Android系统中,ANativeWindow和Surface可以是认为等价的,只不过是ANativeWindow常用于Native层中,而Surface常用于Java层中。另外,我们还可以将ANativeWindow和Surface看作是像Skia和Open GL这样图形渲染库与操作系统底层的图形系统建立连接的一个桥梁。

private void performTraversals() {
        ...
        if (mAttachInfo.mHardwareRenderer != null) {
            try {
提前分配,而不是像软件绘制,由surface的lockCanvas引起。
                hwInitialized = mAttachInfo.mHardwareRenderer.initialize(mSurface);
                if (hwInitialized && (host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
                    mSurface.allocateBuffers();
                }
            } catch (OutOfResourcesException e) {
                handleOutOfResourcesException(e);
                return;
            }
        }
      ....
      
/**
 * Allocate buffers ahead of time to avoid allocation delays during rendering
 * @hide
 */
public void allocateBuffers() {
    synchronized (mLock) {
        checkNotReleasedLocked();
        nativeAllocateBuffers(mNativeObject);
    }
}


//Native层surface对应ANativeWindow,通过这个方法RenderProxy将ANativeWindow绑定到RenderThread
static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
        jlong proxyPtr, jobject jsurface) {
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
    sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface);
    return proxy->initialize(window);
}
//绘图上下文绑定绘图内存。
//所有操作对应到eglApi抽象井口中去
bool RenderProxy::initialize(const sp<ANativeWindow>& window) {
    SETUP_TASK(initialize);
    args->context = mContext;
    args->window = window.get();
    return (bool) postAndWait(task);
}

        Main Thread主要是负责调用View的成员函数onDraw来构造它们的Display List,然后在下一个Vsync信号到来时,再通过一个Render Proxy对象向Render Thread发出一个drawFrame命令。Render Thread内部有一个Task Queue。


基础普及:
        要知道一个手机有 CPU,和 GPU 最基本的两个主要处理器,其中处理器作用决定了性能差异,CPU主要处理逻辑计算,GPU主要负责算术浮点运算,而图形绘制主要以算术的浮点计算为主,所以硬件解码处理,已经跳出了软件程序层面的空间时间,利用更专业的硬件,同样的耗电量可以换取更高的帧率,刷新率等指标。
        Android手机使用Skia引擎绘制2D图形,使用 openGL接口绘制 2D/3D 图形,

1. Android 3.0,也就是Honeycomb版本,开始引用OpenGLRenderer图形渲染库,支持Android应用程
序UI可选地使用硬件加速渲染。

2. Android 4.0,也就是Ice Cream Sandwich版本,要求设备默认支持Android应用程序UI硬件加速渲
染,并且增加一个TextureView控件,该控件直接支持以Open GL纹理的形式来绘制UI。

3. Android 4.1、4.2和4.3,也就是Jelly Bean版本,加入了Project Butter(黄油计划)的特性,
包括:A. 通过Vsync信号来同步UI绘制和动画,使得它们可以获得一个达到60fps的固定的帧率;B. 三
缓冲支持,改善GPU和CPU之间绘制节奏不一致的问题;C. 将用户输入,例如touch event,同步到下一个
Vsync信号到来时再处理;D. 预测用户的touch行为,以获得更好的交互响应;E. 每次用户touch屏幕时
,进行CPU Input Boost,以便减少处理延时。

4. Android 4.4,也就是KitKat版本,一方面通过优化内存使用,另一方面是可选地支持使用ART运行时
替换Dalvik虚拟机,来提高应用程序的运行效率,使得其UI更流畅。

5. Android 5.0,也就是Lollipop版本,ART运行时引进了Compacting GC,进一步优化了Android应用
程序的内存使用,并且ART运行时正式替换了Dalvik虚拟机,同时,Android应用程序增加了一个Render 
Thread,专门负责UI渲染和动画显示。

总结

        软件绘制同硬件加速的区别主要是在绘制上,内存分配、图层合成等整体流程是一样的,只不过硬件加速相比软件绘制算法更加合理,同时采用单独的渲染线程,减轻了主线程的负担。

  • CPU更擅长复杂逻辑控制,而GPU得益于大量ALU和并行结构设计,更擅长数学运算。
  • 页面由各种基础元素(DisplayList)构成,渲染时需要进行大量浮点运算。
  • 硬件加速条件下,CPU用于控制复杂绘制逻辑、构建或更新DisplayList;GPU用于完成图形计算、渲染DisplayList。
  • 硬件加速条件下,刷新界面尤其是播放动画时,CPU只重建或更新必要的DisplayList,进一步提高渲染效率。
  • 实现同样效果,应尽量使用更简单的DisplayList,从而达到更好的性能(Shape代替Bitmap等)。

发布了155 篇原创文章 · 获赞 125 · 访问量 34万+

猜你喜欢

转载自blog.csdn.net/u011216417/article/details/84556140