标签:意义 生成 底层原理 iss 界面 c++ date dcl 兄弟节点
在手机客户端尤其是Android应用的开发过程中,我们经常会接触到“硬件加速”这个词。由于操作系统对底层软硬件封装非常完善,上层软件开发者往往对硬件加速的底层原理了解很少,也不清楚了解底层原理的意义,因此常会有一些误解,如硬件加速是不是通过特殊算法实现页面渲染加速,或是通过硬件提高CPU/GPU运算速率实现渲染加速。
本文尝试从底层硬件原理,一直到上层代码实现,对硬件加速技术进行简单介绍,其中上层实现基于Android 6.0。
对于App开发者,简单了解硬件加速原理及上层API实现,开发时就可以充分利用硬件加速提高页面的性能。以Android举例,实现一个圆角矩形按钮通常有两种方案:使用PNG图片;使用代码(XML/Java)实现。简单对比两种方案如下:
CPU(Central Processing Unit,中央处理器)是计算机设备核心器件,用于执行程序代码,软件开发者对此都很熟悉;
GPU(Graphics Processing Unit,图形处理器)主要用于处理图形运算,通常所说“显卡”的核心部件就是GPU。
下面是CPU和GPU的结构对比图。其中:
扩展:很多计算机中的GPU有自己独立的显存;没有独立显存则使用共享内存的形式,从内存中划分一块区域作为显存。显存可以保存GPU指令等信息。
为了方便理解,这里先从底层电路结构的角度举一个例子。如下图为一个加法器,对应实际的数字电路结构。
对于CPU这种串行结构,代码编写很简单,用for循环把所有数字逐个相加即可。串行结构只有一个加法器,需要7次求和运算;每次计算完部分和,还要将其再转移到加法器的输入端,做下一次计算。整个过程至少要消耗十几个机器周期。
而对于并行结构,一种常见的设计是级联加法器,如下图,其中所有的clock连在一起。当需要相加的8个数据在输入端A1~B4准备好后,经过三个时钟周期,求和操作就完成了。如果数据量更大、级联的层级更大,则并行结构的优势更明显。
由于电路的限制,不容易通过提高时钟频率、减小时钟周期的方式提高运算速度。并行结构通过增加电路规模、并行处理,来实现更快的运算。但并行结构不容易实现复杂逻辑,因为同时考虑多个支路的输出结果,并协调同步处理的过程很复杂(有点像多线程编程)。
假设我们有如下图像处理任务,给每个像素值加1。GPU并行计算的方式简单粗暴,在资源允许的情况下,可以为每个像素开一个GPU线程,由其进行加1操作。数学运算量越大,这种并行方式性能优势越明显。
在Android中,大多数应用的界面都是利用常规的View来构建的(除了游戏、视频、图像等应用可能直接使用OpenGL ES)。下面根据Android 6.0原生系统的Java层代码,对View的软件和硬件加速渲染做一些分析和对比。
DisplayList是一个基本绘制元素,包含元素原始属性(位置、尺寸、角度、透明度等),对应Canvas的drawXxx()方法(如下图)。
信息传递流程:Canvas(Java API) —> OpenGL(C/C++ Lib) —> 驱动程序 —> GPU。
在Android 4.1及以上版本,DisplayList支持属性,如果View的一些属性发生变化(比如Scale、Alpha、Translate),只需把属性更新给GPU,不需要生成新的DisplayList。
一个RenderNode包含若干个DisplayList,通常一个RenderNode对应一个View,包含View自身及其子View的所有DisplayList。
下面是安卓View完整的绘制流程图,主要通过阅读源码和调试得出,虚线箭头表示递归调用。下面是绘制的简单流程:
如果支持硬件加速,则生成的是DisplayListCanvas.class的对象;
两者的isHardwareAccelerated()方法返回的值分别为false、true,View根据这个值判断是否使用硬件加速。
下面根据具体的几种场景,具体分析一下硬件加速前后的流程与加速效果。
说明:
场景1中,无论是否加速,遍历View树并都会走Draw路径。硬件加速后Draw路径不做实际绘制工作,只是构建DisplayList,复杂的绘制计算任务被GPU分担,已经有了较大的加速效果。
场景2中,TextView设置前后尺寸位置不变,不会触发重新Layout。
场景3中,软件绘制每一帧都要做大量绘制工作,很容易导致动画卡顿。硬件加速后,动画过程直接走DisplayList路径更新DisplayList的属性,动画流畅度能得到极大提高。
场景4中,两者的性能差距更明显。简单修改透明度,软件绘制仍然要做很多工作;硬件加速后一般直接更新RenderNode的属性,不需要触发invalidate,也不会遍历View树(除了少数View可能要对Alpha做特殊响应并在onSetAlpha()返回true,代码如下)。
public class View {
// ...
public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
ensureTransformationInfo();
if (mTransformationInfo.mAlpha != alpha) {
mTransformationInfo.mAlpha = alpha;
if (onSetAlpha((int) (alpha * 255))) {
// ...
invalidate(true);
} else {
// ...
mRenderNode.setAlpha(getFinalAlpha());
// ...
}
}
}
protected boolean onSetAlpha(int alpha) {
return false;
}
// ...
}
实际阅读源码并实验,得出通常情况下的软件绘制刷新逻辑:
1,默认情况下,View的clipChildren属性为true,即每个View绘制区域不能超出其父View的范围。如果设置一个页面根布局的clipChildren属性为false,则子View可以超出父View的绘制区域。
2,当一个View触发invalidate,且没有播放动画、没有触发layout的情况下:
对于硬件加速我们总结一下:
- CPU更擅长复杂逻辑控制,而GPU得益于大量ALU和并行结构设计,更擅长数学运算。
- 页面由各种基础元素(DisplayList)构成,渲染时需要进行大量浮点运算。
- 硬件加速条件下,CPU用于控制复杂绘制逻辑、构建或更新DisplayList;GPU用于完成图形计算、渲染DisplayList。
- 硬件加速条件下,刷新界面尤其是播放动画时,CPU只重建或更新必要的DisplayList,进一步提高渲染效率。
- 实现同样效果,应尽量使用更简单的DisplayList,从而达到更好的性能(Shape代替Bitmap等)。
标签:意义 生成 底层原理 iss 界面 c++ date dcl 兄弟节点
原文地址:http://blog.csdn.net/xiangzhihong8/article/details/55009344