理解Andriod 硬件加速

作者: 林子木

博客地址:http://blog.csdn.net/wolinxuebin 


文章总结:

什么是硬件加速:简单来说是一种采用GPU进行渲染,并且在计算需要重新绘制View上做了相应的优化。

相比原先的绘制模式,硬件加速具有什么优点:
  • 原先绘制模式是在一个view的内容改变的时候,调用invalidate()函数,立即将消息上传将生成一个dirty 区域(Rect 类型),之后将于dirty相交的view都进行重绘(即使内容没发生变化)。
  •  硬件加速模式,是采用dispaly list模式,有内容改变的view ,仍旧调用invalidate()函数,但是不会立马进行重绘,而是将调用了invalidate() 函数的view加入到 list 中,在下一次进行统一的绘制。
优点:
  • 一个优点很明显,就是不会将内容没有改变的view进行重新的绘制。
  • 另一个优点是,在开启了硬件加速,并且view的type设置成了 View.LAYER_TYPE_HARDWAR 模式,那么View的一些属性,如alpha、translateX,Y、rotateX,Y、scaleX,Y以及pivotX,Y将不再调用view的onDraw函数,而是直接在图层上进行操作,会有很高的效率。

以下是GOOGLE官方文档及个人的翻译:

前言:

Beginning in Android 3.0 (API level 11), the Android 2D rendering pipeline supports hardware acceleration, meaning that all drawing operations that are performed on a View 's canvas use the GPU. Because of the increased resources required to enable hardware acceleration, your app will consume more RAM.

硬件加速是在 Anroid3.0(API Level 11)开始引入,主要用于Android2D的渲染。这话有点不是很好理解,简单的说就是View的在canvas上的操作,将由GPU进行执行。为什么要采用GPU呢?这是由于启用硬件加速,将消耗更多的RAM,所以由GPU接管了原先CPU的渲染工作。什么是渲染?简单理解就是将数据转化为图形,呈现在你面前。

Hardware acceleration is enabled by default if your Target API level is >=14, but can also be explicitly enabled. If your application uses only standard views and Drawables, turning it on globally should not cause any adverse drawing effects. However, because hardware acceleration is not supported for all of the 2D drawing operations, turning it on might affect some of your custom views or drawing calls. Problems usually manifest themselves as invisible elements, exceptions, or wrongly rendered pixels. To remedy this, Android gives you the option to enable or disable hardware acceleration at multiple levels. See Controlling Hardware Acceleration.

硬件加速在API大于14的系统版本中默认是开启的,如果不需要,也可以进行关闭。如果你的应用都使用了标准的view和Drawable,关闭硬件加速不会有任何的影响(这里的影响是指显示错误,而不是指性能方面的)。然而当前硬件加速并不支持所有的2D操作,因此可能会影响你的自定义的view或者部分的drawing的操作。会造成元素不可见、异常或者渲染了错误的像素等问题。为了补救该问题,Android在多个层级上给出了开关硬件加速的接口。

If your application performs custom drawing, test your application on actual hardware devices with hardware acceleration turned on to find any problems. The Unsupported drawing operations section describes known issues with hardware acceleration and how to work around them.

Controlling Hardware Acceleration

控制硬件加速


You can control hardware acceleration at the following levels:

控制硬件加速有如下几个级别:

  • Application
  • Activity
  • Window
  • View

Application level

In your Android manifest file, add the following attribute to the <application> tag to enable hardware acceleration for your entire application:

<application android:hardwareAccelerated="true" ...>

Activity level

If your application does not behave properly with hardware acceleration turned on globally, you can control it for individual activities as well. To enable or disable hardware acceleration at the activity level, you can use the android:hardwareAccelerated attribute for the <activity> element. The following example enables hardware acceleration for the entire application but disables it for one activity:

<application android:hardwareAccelerated="true">
    <activity ... />
    <activity android:hardwareAccelerated="false" />
</application>

Window level

If you need even more fine-grained control, you can enable hardware acceleration for a given window with the following code:

getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

Note: You currently cannot disable hardware acceleration at the window level.

注意,当前不能在window 级别禁止硬件加速

View level

You can disable hardware acceleration for an individual view at runtime with the following code:

myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

Note: You currently cannot enable hardware acceleration at the view level. View layers have other functions besides disabling hardware acceleration. See View layers for more information about their uses.

注意,当前不能在view级别开启硬件加速

Determining if a View is Hardware Accelerated

以下的一些操作可以查看一个view是否开启了硬件加速


It is sometimes useful for an application to know whether it is currently hardware accelerated, especially for things such as custom views. This is particularly useful if your application does a lot of custom drawing and not all operations are properly supported by the new rendering pipeline.

There are two different ways to check whether the application is hardware accelerated:

If you must do this check in your drawing code, use Canvas.isHardwareAccelerated() instead of View.isHardwareAccelerated() when possible. When a view is attached to a hardware accelerated window, it can still be drawn using a non-hardware accelerated Canvas. This happens, for instance, when drawing a view into a bitmap for caching purposes.

Android Drawing Models

Android的绘制模型


When hardware acceleration is enabled, the Android framework utilizes a new drawing model that utilizes display lists to render your application to the screen. To fully understand display lists and how they might affect your application, it is useful to understand how Android draws views without hardware acceleration as well. The following sections describe the software-based and hardware-accelerated drawing models.

如果开启了硬件加速,Android将采用一种新的绘制模型,该模型采用display lists的方式来渲染你的应用。为了更好的理解的工作原理,我们首先来理解Android最原始的绘制模型。接下来的章节,我们将分别介绍基于软件和开启硬件加速的绘制模型。

Software-based drawing model

基于软件的绘制模型

In the software drawing model, views are drawn with the following two steps:

在软件绘制模型中,一个view将进行下面两个步骤:

  1. Invalidate the hierarchy
  2. Draw the hierarchy

Whenever an application needs to update a part of its UI, it invokes invalidate() (or one of its variants) on any view that has changed content. The invalidation messages are propagated all the way up the view hierarchy to compute the regions of the screen that need to be redrawn (the dirty region). The Android system then draws any view in the hierarchy that intersects with the dirty region. Unfortunately, there are two drawbacks to this drawing model:

当我们的应用需要更新部分UI时,有内容变动的view将会执行invalidate()函数。这时候将会将这个message不断的向上传递,最后由rootView计算出一个 dirty 区域。之后根据这个dirty 区域,找出与之相交的所有的view,并执行重绘操作。这将带来以下几个缺点:

  • First, this model requires execution of a lot of code on every draw pass. For example, if your application calls invalidate() on a button and that button sits on top of another view, the Android system redraws the view even though it hasn't changed.
        首先,将会执行很多不必要的代码。比如一个button位于另一个view之上,一旦这个button内容发生了改变,调用了invalidate()这个函数,那么即使位于之下的那个view没有发生变化,进了重绘操作。
  • The second issue is that the drawing model can hide bugs in your application. Since the Android system redraws views when they intersect the dirty region, a view whose content you changed might be redrawn even though invalidate() was not called on it. When this happens, you are relying on another view being invalidated to obtain the proper behavior. This behavior can change every time you modify your application. Because of this, you should always call invalidate() on your custom views whenever you modify data or state that affects the view’s drawing code.
     其次,这里有一个隐藏的问题。当系统对之前和ditry区域相交的view进行重绘处理的时候,一个有内容变化的view即使没有调用invalidate()函数,也将被重绘。那么这将造成一个不同步的问题,比如View1的内容改变了并且被重绘了,而view2内容改变了,但是没有被重绘。

Note: Android views automatically call invalidate() when their properties change, such as the background color or the text in a TextView.

Hardware accelerated drawing model

硬件加速绘制模型


The Android system still uses invalidate() and draw() to request screen updates and to render views, but handles the actual drawing differently. Instead of executing the drawing commands immediately, the Android system records them inside display lists, which contain the output of the view hierarchy’s drawing code. Another optimization is that the Android system only needs to record and update display lists for views marked dirty by an invalidate() call. Views that have not been invalidated can be redrawn simply by re-issuing the previously recorded display list. The new drawing model contains three stages:

在硬件加速绘制模型中,Android系统仍旧使用invalidate() 和 draw()这两个函数来请求屏幕更新及渲染视图的操作,当时与基于软件的绘制模型相比,有很大的却别。与之前在调用函数就里面进行绘制不同,Android系统将把请求的view记录到一个display list中。另一个优化是,系统对invalidate()函数的处理,仅仅只用更新display list就可以。最终只要重绘list中的view,而不用全部重绘。具体的重绘将会包含下面三个阶段:

  1. Invalidate the hierarchy
  2. Record and update display lists
  3. Draw the display lists

With this model, you cannot rely on a view intersecting the dirty region to have its draw() method executed. To ensure that the Android system records a view’s display list, you must call invalidate(). Forgetting to do so causes a view to look the same even after it has been changed.

在这个模型中,将不会重绘那些没有改变的view,仅仅重绘被标记在display list中的view。

Using display lists also benefits animation performance because setting specific properties, such as alpha or rotation, does not require invalidating the targeted view (it is done automatically). This optimization also applies to views with display lists (any view when your application is hardware accelerated.) For example, assume there is a LinearLayout that contains a ListView above a Button. The display list for the LinearLayout looks like this:

采用display list 对动画的流畅性具有很大的帮助,比如动画改变alpha、旋转等,不需要将View进行invalidate标记,因为这些属性不需要对view进行处理,仅仅只要对图层做下相应的处理就可以。举一个例子,在LinearLayout中有一个lisiView,在lisiView之上又放了一个button,那么展现LinearLayout将如下几步:

  • DrawDisplayList(ListView)
  • DrawDisplayList(Button)

Assume now that you want to change the ListView's opacity. After invoking setAlpha(0.5f) on the ListView, the display list now contains this:

假设此时对listview进行了更新alpha的处理,那将如下操作:

  • SaveLayerAlpha(0.5)
  • DrawDisplayList(ListView)
  • Restore
  • DrawDisplayList(Button)

The complex drawing code of ListView was not executed. Instead, the system only updated the display list of the much simpler LinearLayout. In an application without hardware acceleration enabled, the drawing code of both the list and its parent are executed again.

由于ListView内容没有改变,所以不会重绘,仅仅是将图层的alpha进行改变就可以。如果不采用硬件加速,那么不仅listview要进行重绘,Button及LinearLayout都要进行重绘操作。

Unsupported Drawing Operations

下面是一些为未支持硬件加速的操作


When hardware accelerated, the 2D rendering pipeline supports the most commonly used Canvas drawing operations as well as many less-used operations. All of the drawing operations that are used to render applications that ship with Android, default widgets and layouts, and common advanced visual effects such as reflections and tiled textures are supported.

当前硬件加速支持大部分的Canvas的操作。此外还支持widgets和 layouts的一些常见的视觉效果,如反射和铺砖文理。

The following table describes the support level of various operations across API levels:

下面的表格将描述各种操作的支持的API级别

   
  First supported API level
Canvas
drawBitmapMesh() (colors array) 18
drawPicture() 23
drawPosText() 16
drawTextOnPath() 16
drawVertices()
setDrawFilter() 16
clipPath() 18
clipRegion() 18
clipRect(Region.Op.XOR) 18
clipRect(Region.Op.Difference) 18
clipRect(Region.Op.ReverseDifference) 18
clipRect() with rotation/perspective 18
Paint
setAntiAlias() (for text) 18
setAntiAlias() (for lines) 16
setFilterBitmap() 17
setLinearText()
setMaskFilter()
setPathEffect() (for lines)
setRasterizer()
setShadowLayer() (other than text)
setStrokeCap() (for lines) 18
setStrokeCap() (for points) 19
setSubpixelText()
Xfermode
PorterDuff.Mode.DARKEN (framebuffer)
PorterDuff.Mode.LIGHTEN (framebuffer)
PorterDuff.Mode.OVERLAY (framebuffer)
Shader
ComposeShader inside ComposeShader
Same type shaders inside ComposeShader
Local matrix on ComposeShader 18

Canvas Scaling

画布的缩放操作


The hardware accelerated 2D rendering pipeline was built first to support unscaled drawing, with some drawing operations degrading quality significantly at higher scale values. These operations are implemented as textures drawn at scale 1.0, transformed by the GPU. In API level <17, using these operations will result in scaling artifacts increasing with scale.

硬件加速最开始并没有支持缩放绘制,一些操作在大的放大情况下,显得很低的显示质量。这些操作都是基于不缩放的情况下进行使用的。如果在API < 17的情况下使用这些操作将会带来不利的影响。

The following table shows when implementation was changed to correctly handle large scales:
   
Drawing operation to be scaled First supported API level
drawText() 18
drawPosText()
drawTextOnPath()
Simple Shapes* 17
Complex Shapes*
drawPath()
Shadow layer

Note: 'Simple' shapes are drawRect(), drawCircle(), drawOval(), drawRoundRect(), and drawArc() (with useCenter=false) commands issued with a Paint that doesn't have a PathEffect, and doesn't contain non-default joins (via setStrokeJoin() / setStrokeMiter()). Other instances of those draw commands fall under 'Complex,' in the above chart.

注意:’Simple’ 形状是指drawRect(), drawCircle(), drawOval(), drawRoundRect(), and drawArc() ( useCenter这参数设为false时),这些事不存在路径影响的函数。其他的都是‘Complex’现在还暂时不支持。

If your application is affected by any of these missing features or limitations, you can turn off hardware acceleration for just the affected portion of your application by calling setLayerType(View.LAYER_TYPE_SOFTWARE, null). This way, you can still take advantage of hardware acceleration everywhere else. See Controlling Hardware Acceleration for more information on how to enable and disable hardware acceleration at different levels in your application.

如果你的应用存在这种问题,只要采用setLayerType(View.LAYER_TYPE_SOFTWARE, null)进行关闭,在其他地方依旧可以使用硬件加速带来的好处。

View Layers

View 图层


In all versions of Android, views have had the ability to render into off-screen buffers, either by using a view's drawing cache, or by using Canvas.saveLayer(). Off-screen buffers, or layers, have several uses. You can use them to get better performance when animating complex views or to apply composition effects. For instance, you can implement fade effects using Canvas.saveLayer() to temporarily render a view into a layer and then composite it back on screen with an opacity factor.

在所有的版本的Android中,所有的View都具备采用drawing cache或采用Canvas.saveLayer()来实现离屏缓存(简单的理解,在不同的图层绘制,不会影响其他图层)。

Beginning in Android 3.0 (API level 11), you have more control on how and when to use layers with the View.setLayerType() method. This API takes two parameters: the type of layer you want to use and an optional Paint object that describes how the layer should be composited. You can use the Paint parameter to apply color filters, special blending modes, or opacity to a layer. A view can use one of three layer types:

在Android 3.0开始,就可以使用View.setLayerType()函数来实现离屏缓存功能。这个函数具有两个参数:第一个是图层的type,另一个是paint对象。你可以同时patin对象实现颜色滤镜、特殊的混合模式以及图层的透明度操作等功能。一个View可以采用如下3个类型:

  • LAYER_TYPE_NONE: The view is rendered normally and is not backed by an off-screen buffer. This is the default behavior.
      这个是默认操作,采用普通个渲染模式,没有采用离屏缓冲。
  • LAYER_TYPE_HARDWARE: The view is rendered in hardware into a hardware texture if the application is hardware accelerated. If the application is not hardware accelerated, this layer type behaves the same as LAYER_TYPE_SOFTWARE.
      如果设置了这个模式,在应用没有开启硬件加速的时候,会默认采用LAYER_TYPE_SOFTWARE,否则采用LAYER_TYPE_HARDWARE模式。
     这个模式将view渲染成一个bitmap。

The type of layer you use depends on your goal:

  • Performance: Use a hardware layer type to render a view into a hardware texture. Once a view is rendered into a layer, its drawing code does not have to be executed until the view calls invalidate(). Some animations, such as alpha animations, can then be applied directly onto the layer, which is very efficient for the GPU to do.
      性能方面:如果采用硬件层,那么将把view采用硬件文理渲染。一旦视图加入该层,只有当view调用了invalidate()函数之后,才会调用绘制相关的代码。一些动画,比如透明度动画,就可以直接在图层上进行变换,这些操作对GPU来说,是很高效的。
  • Visual effects: Use a hardware or software layer type and a Paint to apply special visual treatments to a view. For instance, you can draw a view in black and white using a ColorMatrixColorFilter.
     视觉影响: 可以使用硬件或软件层以及配合Paint的功能,实现黑白画面等。
  • Compatibility: Use a software layer type to force a view to be rendered in software. If a view that is hardware accelerated (for instance, if your whole application is hardware acclerated), is having rendering problems, this is an easy way to work around limitations of the hardware rendering pipeline.
      兼容性:采用软件图层将强制将view采用软件渲染模式。如果一个view采用了硬件加速,但是却遇到了渲染方面的问题,将自动转化为软件图层的模式。     

View layers and animations

视图图层和动画


Hardware layers can deliver faster and smoother animations when your application is hardware accelerated. Running an animation at 60 frames per second is not always possible when animating complex views that issue a lot of drawing operations. This can be alleviated by using hardware layers to render the view to a hardware texture. The hardware texture can then be used to animate the view, eliminating the need for the view to constantly redraw itself when it is being animated. The view is not redrawn unless you change the view's properties, which calls invalidate(), or if you call invalidate() manually. If you are running an animation in your application and do not obtain the smooth results you want, consider enabling hardware layers on your animated views.

When a view is backed by a hardware layer, some of its properties are handled by the way the layer is composited on screen. Setting these properties will be efficient because they do not require the view to be invalidated and redrawn. The following list of properties affect the way the layer is composited. Calling the setter for any of these properties results in optimal invalidation and no redrawing of the targeted view:

在开启了硬件加速的情况下,以下几个属性的改变,将不会引起view的重绘。

  • alpha: Changes the layer's opacity
  • x, y, translationX, translationY: Changes the layer's position
  • scaleX, scaleY: Changes the layer's size
  • rotation, rotationX, rotationY: Changes the layer's orientation in 3D space
  • pivotX, pivotY: Changes the layer's transformations origin

These properties are the names used when animating a view with an ObjectAnimator. If you want to access these properties, call the appropriate setter or getter. For instance, to modify the alpha property, call setAlpha(). The following code snippet shows the most efficient way to rotate a viewiew in 3D around the Y-axis:

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator.ofFloat(view, "rotationY", 180).start();

Because hardware layers consume video memory, it is highly recommended that you enable them only for the duration of the animation and then disable them after the animation is done. You can accomplish this using animation listeners:

View.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180);
animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(View.LAYER_TYPE_NONE, null);
    }
});
animator.start();

For more information on property animation, see Property Animation.

Tips and Tricks

建议等


Switching to hardware accelerated 2D graphics can instantly increase performance, but you should still design your application to use the GPU effectively by following these recommendations:

以下是关于一些提高应用性能的操作:

Reduce the number of views in your application
The more views the system has to draw, the slower it will be. This applies to the software rendering pipeline as well. Reducing views is one of the easiest ways to optimize your UI.
减少View的个数,是最简单的优化你的UI的方法。
Avoid overdraw
Do not draw too many layers on top of each other. Remove any views that are completely obscured by other opaque views on top of it. If you need to draw several layers blended on top of each other, consider merging them into a single layer. A good rule of thumb with current hardware is to not draw more than 2.5 times the number of pixels on screen per frame (transparent pixels in a bitmap count!).
避免过度重绘。
Don't create render objects in draw methods
A common mistake is to create a new Paint or a new Path every time a rendering method is invoked. This forces the garbage collector to run more often and also bypasses caches and optimizations in the hardware pipeline.
不要在onDraw函数中进行new Paint 与 new Path等操作,这样会产生大量的垃圾。
Don't modify shapes too often
Complex shapes, paths, and circles for instance, are rendered using texture masks. Every time you create or modify a path, the hardware pipeline creates a new mask, which can be expensive.
复杂的形状、路径、圆等,是采用纹理面具渲染。每次修改路径都会引起硬件加速重新创建新的面具(mask),很消耗资源。
Don't modify bitmaps too often
Every time you change the content of a bitmap, it is uploaded again as a GPU texture the next time you draw it.
避免多次修改bitmap,因为每次进行修改,都会让CPU重新进行纹理处理。
Use alpha with care
When you make a view translucent using setAlpha(), AlphaAnimation, or ObjectAnimator, it is rendered in an off-screen buffer which doubles the required fill-rate. When applying alpha on very large views, consider setting the view's layer type to LAYER_TYPE_HARDWARE.
小心使用alpha,因为这在离屏缓冲中使用双倍的填充速率。当如果要修改一个大的view的alpha时,可以设置LAYER_TYPE_HARDWARE来提升效率。


©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页