码迷,mamicode.com
首页 > 移动开发 > 详细

Android HWUI硬件加速模块浅析

时间:2015-08-03 14:41:25      阅读:612      评论:0      收藏:0      [点我收藏+]

标签:android   硬件加速   移动开发   

关键词:RenderNode,ThreadedRenderer,DisplayList,UvMapper,FontRenderer


什么是硬件加速(What)

传统软件的UI绘制是依靠CPU来完成的,硬件加速就是将绘制任务交由GPU来执行。GPU相比CPU更加适合完成光栅化、动画变换等耗时任务,在移动设备上比起使用CPU来完成这些任务,GPU会更加省电些,带来的用户体验也会更佳。


为什么要硬件加速(Why)

Android的硬件加速的底层实现是基于OpenGL ES接口向GPU提交指令来完成绘制的。相对于CPU实现的软绘制,硬件加速的帧率会高于CPU,效能更高。屏幕分辨率越大(尤其对于高清电视而言),硬件加速的优势更加明显。

下图是Android 5.0的HWUI绘制执行流程:

技术分享

源码位于目录android/platform/framework/base/libs/hwui

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的LayerType设置为HARDWARE时,View就会绑定到一个OpenGL ES的FrameBufferObject上去,如果要对这个View进行Animation(Alpha,Rotation,etc.),会将这个FrameBufferObject渲染至Texture(2D),然后再进行动画的渲染,这样有一个坏处,消耗的内存增加了。


硬件加速的实现(How)


独立的渲染线程

技术分享

在Android 5上,ThreadedRenderer的出现减轻了主线程的负担,可以更快的响应用户的操作。


Deferred Display List

技术分享

DisplayList记录了绘制操作和状态,主线程将它们提交至OpenGL渲染器,由渲染器执行最终的DrawCall。

Android 4.4之后,HWUI模块引入了Deferred Display List,它在Display List的基础上做了一些优化,比如剔除过绘制的区域、对DrawCall进行分批或合并,在一定程度上提升了绘制的性能。


创建纹理集

技术分享

AssetAtlasService对资源的加载在做了专门的管理,它为了减少图片资源上传至GPU的操作,对对各资源进行合并,打包成单张纹理进行上传,通过UvMapper重新映射纹理坐标,使UI依然能够正确绘制。

如果设备支持OpenGL ES 3.0,Android会使用PixelBufferObject绑定实现纹理的异步上传,这样做的好处在于通过映射方式(glMapBuffer)更加高效的上传纹理数据至GPU。下面这段代码来自于桌面端的实现,移动端的实现差异不大。

// 绑定纹理单元和PBO
glBindTexture(GL_TEXTURE_2D, textureId);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[index]);
// 将PBO的像素复制到纹理中去
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, 0);
//准备上传下一份纹理
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pboIds[nextIndex]);
//这里如果直接调用glMapBuffer会引起OpenGL状态机的一个同步检查(开销较大),但是使用BufferData的话就不会,他可以直接分配完数据返回
glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
//将GPU数据映射至内存
GLubyte* ptr = (GLubyte*)glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB);
if(ptr) {
    updatePixels(ptr, DATA_SIZE); //直接更新纹理数据
    glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); // 释放映射的缓冲区
}
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);

ATLAS的使用减少了GPU显存的消耗,以及纹理单元(Texture Unit)绑定调用(Resource Binding)的开销。


合并DrawCall

技术分享

Android硬件加速的过程中并没有启用深度测试(DepthTest),所有的UI绘制都是按照绘制的次序展示在屏幕上。由于OpenGL ES的驱动实现的复杂性,每个GL的绑定调用以及GL状态的切换都是昂贵的,所以才引入了Deferred Display List对DrawCall合并以及分批,相关类,如上图。


字体绘制

技术分享

文字的渲染也是一个令人头疼的问题,APP的文字渲染不像游戏中那样的简单,通常游戏发布的时候可以预先将固定的文字转换成单张的纹理,渲染文字时直接映射纹理坐标即可。Android底层对文字字形纹理数据进行了Cache。Android使用了Skia和第三方开源字体库(FreeType)对字体进行栅格化。

技术分享


硬件加速的改进

iOS的UI绘制使用了Multiple GL Context,在iOS 8之后又引入了Metal Framework,原生支持多线程UI渲染,Android由于GL驱动以及GPU厂商实现的差异无法很好地榨干GPU的机能,但是在下一代的图形API(Vulkan,驱动变薄、支持多线程)普及之后,仍然有较大的优化空间,UI流畅性可以进一步提升。


参考

  1. Efficient text rendering with OpenGL ES
  2. Rendering Text in Metal with Signed-Distance Fields
  3. 老罗的Android之旅
  4. How about some Android graphics true facts?
  5. Introduction to the Android Graphics Pipeline

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android HWUI硬件加速模块浅析

标签:android   硬件加速   移动开发   

原文地址:http://blog.csdn.net/tomicyo/article/details/47254413

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!