文章

硬件加速

硬件加速

硬件加速

什么是硬件加速?

所谓硬件加速,指的是把某些计算工作交给专门的硬件 GPU 来做,而不是和普通的计算工作一样交给 CPU 来处理。这样不仅减轻了 CPU 的压力,而且由于有了专门硬件 GPU 的处理,这份计算工作的速度也被加快了。

在 Android 里,硬件加速专指把 View 中绘制的计算工作交给 GPU 来处理。进一步地明确一下,这个绘制的计算工作指的就是把绘制方法中的那些 Canvas.drawXXX() 变成实际的像素。

Android 3.0,API 11 开始引入;Android4.0,API 14 默认开启

Android 开发人员可以通过使用 OpenGL ES 来实现硬件加速渲染图形。

如果一个设备支持 GPU 硬件加速渲染,那么当 Android 应用程序调用 Open GL 接口来绘制 UI 时,Android 应用程序的 UI 就是通过硬件加速技术进行渲染的。

View 是否开启硬件加速绘制的区别

硬件加速,实际上应该叫 GPU 加速,软硬件加速的区别主要是图形的绘制究竟是 GPU 来处理还是 CPU,如果是 GPU,就认为是硬件加速绘制,反之,则是软件绘制

硬件加速

当硬件加速关闭的时候, Canvas 绘制的工作方式是:把要绘制的内容写进一个 Bitmap,然后在之后的渲染过程中,这个 Bitmap 的像素内容被直接用于渲染到屏幕。这种绘制方式的主要计算工作在于把绘制操作转换为像素的过程(例如由一句 Canvas.drawCircle() 来获得一个具体的圆的像素信息),这个过程的计算是由 CPU 来完成的。

  1. 记录绘制的操作(RenderList)
  2. 使用 GPU 加速
  3. 部分绘制操作不支持

软件加速

开启硬件加速后,Canvas 的工作方式改变了:它把绘制的内容转为 GPU 的操作保存下来,然后交给 GPU 来完成显示工作。

  1. 直接进行绘制
  2. 使用 CPU

硬件加速基础

Android 硬件加速范围

Application > Activity > Window > View

Application 级别

1
<application android:hardwareAccelerated="true" />

其影响范围是整个 APP,也就是说该 App 下的所有 Activity、Window、View 的硬件加速支持会受到该配置的影响。(当然,硬件加速默认是开启的)

Activity 级别

不想全局都启用硬件加速,那么个别 Activity 可以不指定硬件加速,在 Activity 级别启动或者不启用硬件加速,你可以标签指定 android:hardwareAccelerated 属性

1
<activity android:hardwareAccelerated="false" />

Window 级别

1
window.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)

目前还不允许在 Window 级别关闭硬件加速的,这个方法基本不用

View 级别

  1. 开启硬件加速
1
setLayerType(View.LAYER_TYPE_HARDWARE, mPaint)
  1. 关闭硬件加速
1
setLayerType(View.LAYER_TYPE_SOFTWARE, mPaint)

或者在 xml 中的控件属性中,使用 android:layerType=”software” 来关闭硬件加速:比如

1
android:layerType="software"

目前还不允许运行时启用硬件加速,View layers 还是一些函数是不支持硬件加速的。

判断一个 View 是否开启硬件加速
  1. View.isHardwareAcclerated() 返回 true 如果 View 是依附到启动了硬件加速的 window;只会受到 Application、Activity 的影响,如果 Activity 本身不支持硬件加速,那么返回 false,反之返回 true
  2. Canvas.isHardwareAccelerated 返回 true 如果 canvas 是硬件加速的;会受到 Application、Activity 的影响,如果设置了 setLayerType,那么会被 setLayerType 直接影响;假如没有设置 setLayerType,如果 Activity 支持硬件加速,那么返回 true,反之返回 false

强烈建议使用 Canvas.isHardwareAcclerated 代替 View.isHardwareAcclerated,因为 View 的这个方法真的非常不靠谱,即使这个 View 是依附在一个硬件加速的 Window 上,但是仍然可以使用一个不启用硬件加速的 Canvas 进行绘制,比如当我们把 View 绘制到 bitmap 上的时候。而且很多时候,我们如果对 window 不熟悉的话,view 有没有跟 Window 绑定在一起都不知道。

View Layer 离屏缓存

LAYER_TYPE_NONE

view 只会普通地进行渲染,并且不会使用离屏缓存回退,这是默认的行为。

LAYER_TYPE_HARDWARE

如果应用启动了硬件加速,那么这个 view 就会使用硬件里面的 texture 渲染,如果应用不能够硬件加速,那么它的效果就跟 LAYER_TYPE_SOFTWARE 一样。

LAYER_TYPE_SOFTWARE

这个 view 将会使用软件来渲染到一个 bitmap 里。

手动关闭硬件加速:

1
view.setLayerType(LAYER_TYPE_SOFTWARE, null);

事实上,这个方法的本来作用并不是用来开关硬件加速的,只是当它的参数为 LAYER_TYPE_SOFTWARE 的时候,可以「顺便」把硬件加速关掉而已;并且除了这个方法之外,Android 并没有提供专门的 View 级别的硬件加速开关,所以它就「顺便」成了一个开关硬件加速的方法。

硬件加速原理

在硬件加速关闭的时候,Canvas 绘制的工作方式是:把要绘制的内容写进一个 Bitmap,然后在之后的渲染过程中,这个 Bitmap 的像素内容被直接用于渲染到屏幕。这种绘制方式的主要计算工作在于把绘制操作转换为像素的过程(例如由一句 Canvas.drawCircle() 来获得一个具体的圆的像素信息),这个过程的计算是由 CPU 来完成的。大致就像这样:

而在硬件加速开启时,Canvas 的工作方式改变了:它只是把绘制的内容转换为 GPU 的操作保存了下来,然后就把它交给 GPU,最终由 GPU 来完成实际的显示工作。大致是这样:

硬件加速能够让绘制变快,主要有三个原因

  1. 本来由 CPU 自己来做的事,分摊给了 GPU 一部分,自然可以提高效率;
  2. 相对于 CPU 来说,GPU 自身的设计本来就对于很多常见类型内容的计算(例如简单的圆形、简单的方形)具有优势;
  3. 由于绘制流程的不同,硬件加速在界面内容发生重绘的时候绘制流程可以得到优化,避免了一些重复操作,从而大幅提升绘制效率。

在硬件加速关闭时,绘制内容会被 CPU 转换成实际的像素,然后直接渲染到屏幕。具体来说,这个「实际的像素」,它是由 Bitmap 来承载的。在界面中的某个 View 由于内容发生改变而调用 invalidate() 方法时,如果没有开启硬件加速,那么为了正确计算 Bitmap 的像素,这个 View 的父 View、父 View 的父 View 乃至一直向上直到最顶级 View,以及所有和它相交的兄弟 View,都需要被调用 invalidate() 来重绘。一个 View 的改变使得大半个界面甚至整个界面都重绘一遍,这个工作量是非常大的。

而在硬件加速开启时,前面说过,绘制的内容会被转换成 GPU 的操作保存下来(承载的形式称为 display list,对应的类也叫做 DisplayList),再转交给 GPU。由于所有的绘制内容都没有变成最终的像素,所以它们之间是相互独立的,那么在界面内容发生改变的时候,只要把发生了改变的 View 调用 invalidate() 方法以更新它所对应的 GPU 操作就好,至于它的父 View 和兄弟 View,只需要保持原样。那么这个工作量就很小了。

硬件加速限制

硬件加速不只是好处,也有它的限制:受到 GPU 绘制方式的限制,Canvas 的有些方法在硬件加速开启式会失效或无法正常工作。比如,在硬件加速开启时, clipPath() 在 API 18 及以上的系统中才有效。具体的 API 限制和 API 版本的关系如下图:

image.png

所以,如果你的自定义控件中有自定义绘制的内容,最好参照一下这份表格,确保你的绘制操作可以正确地在所有用户的手机里能够正常显示,而不是只在你的运行了最新版本 Android 系统的 Nexus 或 Pixel 里测试一遍没问题就发布了。

不过有一点可以放心的是,所有的原生自带控件,都没有用到 API 版本不兼容的绘制操作,可以放心使用。所以你只要检查你写的自定义绘制就好。

硬件加速和离屏缓存 (LayerType) 区别

https://zhuanlan.zhihu.com/p/75458539

Ref

本文由作者按照 CC BY 4.0 进行授权