博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android_硬件加速简述
阅读量:4292 次
发布时间:2019-05-27

本文共 7362 字,大约阅读时间需要 24 分钟。

        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();        //
updateRootDisplayList(view, callbacks);//构建View的DrawOp树 `````` //
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 canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight); try { //
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 //
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绑定到RenderThreadstatic jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,        jlong proxyPtr, jobject jsurface) {    RenderProxy* proxy = reinterpret_cast
(proxyPtr); sp
window = android_view_Surface_getNativeWindow(env, jsurface); return proxy->initialize(window);}//绘图上下文绑定绘图内存。//所有操作对应到eglApi抽象井口中去bool RenderProxy::initialize(const sp
& 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等)。

转载地址:http://ejegi.baihongyu.com/

你可能感兴趣的文章
腾讯面试:一条SQL语句执行得很慢的原因有哪些?
查看>>
架构:负载均衡
查看>>
阿里面试中遇到的一些架构问题
查看>>
java并发之DelayQueue实际运用示例
查看>>
SpringBoot启动原理及相关流程
查看>>
百度社招面试题——如何用Redis实现分布式锁
查看>>
redis 的并发竞争问题是什么?了解 redis 事务的 CAS 方案吗?
查看>>
关于分布式限流,这几点你必须掌握!
查看>>
搞懂这个,什么分布式锁的面试题都能应对
查看>>
MyBatis自动映射级别和缓存
查看>>
一不小心就让Java开发踩坑的fail-fast是个什么鬼?
查看>>
秒杀系统流量削峰这事儿应该怎么做?
查看>>
Mysql面试经典20个问题,你知道吗?
查看>>
算法和数据结构最全最易懂总结
查看>>
面试官问到分布式技术,一脸懵逼怎么办?
查看>>
Java架构-亿级网站大数据量下的高并发同步讲解
查看>>
Java 四种线程池的用法分析
查看>>
公司架构师常常提起的DNS负载均衡是个什么鬼?
查看>>
【Zookeeper】Zookeeper初级面试七小问。
查看>>
不使用synchronized和lock,如何实现一个线程安全的单例
查看>>