关键词:RenderNode,ThreadedRenderer,DisplayList,UvMapper,FontRenderer
原文:https://github.com/TsinStudio/AndroidDev
传统软件的UI绘制是依靠CPU来完成的,硬件加速就是将绘制任务交由GPU来执行。GPU相比CPU更加适合完成光栅化、动画变换等耗时任务,在移动设备上比起使用CPU来完成这些任务,GPU会更加省电些,带来的用户体验也会更佳。
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),然后再进行动画的渲染,这样有一个坏处,消耗的内存增加了。
在Android 5上,ThreadedRenderer的出现减轻了主线程的负担,可以更快的响应用户的操作。
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)的开销。
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流畅性可以进一步提升。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/tomicyo/article/details/47132453