欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

Android HWUI硬件加速模块浅析

来源: 开发者 投稿于  被查看 1292 次 评论:260

Android HWUI硬件加速模块浅析


 


什么是硬件加速(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<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> 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进行分批或合并,在一定程度上提升了绘制的性能。

创建纹理集

这里写图片描述seo8L3N0cm9uZz6jrMq5VUnSwMi7xNy5u9X9yLe75tbGoaM8L3A+DQo8L2Jsb2NrcXVvdGU+DQo8cD7I57n7yeixuNans9ZPcGVuR0wgRVMgMy4wo6xBbmRyb2lku+HKudPDPHN0cm9uZz5QaXhlbEJ1ZmZlck9iamVjdLDztqg8L3N0cm9uZz7Ktc/WzsbA7bXE0uyyvcnPtKujrNXi0fnX9rXEusO0ptTa09rNqLn907PJ5Le9yr2jqGdsTWFwQnVmZmVyo6m4/LzTuN/Qp7XEyc+0q87GwO3K/b7d1sFHUFWho8/Cw+bV4rbOtPrC68C019TT2tfAw+a2y7XEyrXP1qOs0sa2r7bLtcTKtc/Wsu7S7LK7tPOhozwvcD4NCjxwcmUgY2xhc3M9"brush:java;"> // 绑定纹理单元和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流畅性可以进一步提升。

 

用户评论