Hardware acceleration is a term that many people are interested in whenever it is mentioned. The word gives most people two ideas: fast and unstable. To many people, hardware acceleration seems like a high-end technology to be seen from a distance, not to be played with: yes, I hear it’s awesome, but I’m afraid to mess with it because I’m afraid I can’t handle it.

Today I tried to take hardware acceleration off the table (I didn’t) and talk about how it works and how it works:

  1. The nature and principle of hardware acceleration;
  2. Hardware acceleration in Android applications;
  3. Limitations of hardware acceleration in Android.

This is the last article in the HenCoder Advanced Android Development Custom View section: Hardware Acceleration.

If you haven’t heard of HenCoder, you can also check this out: HenCoder: An advanced manual for Advanced Android engineers

concept

Before we begin, it is important to note that this is the last installment of the drawing section. This installment is just a supplement for completeness, as the previous installments have covered some technical aspects of hardware acceleration, and some readers have raised some questions because they are not familiar with hardware acceleration. So this episode is not too difficult just in terms of difficulty, and you can find most of this episode on these two pages:

  1. Hardware Acceleration | Android Developers
  2. Google I/O 2011: Accelerated Android Rendering

Let’s get down to business.

Hardware acceleration refers to handing over some of the computing work to specialized hardware rather than handing it over to the CPU as usual. This not only takes the pressure off the CPU, but also speeds up the computation process by having “people” handling it. This is called “hardware acceleration”.

For Android, hardware acceleration has its own meaning: In Android, hardware acceleration specifically refers to handing over the computation work drawn in the View to the GPU. To be more specific, the “computation of drawing” refers to turning canvas.drawxxx () in the drawing method into actual pixels.

The principle of

When hardware acceleration is turned off, Canvas drawing works by writing the content to be drawn into a Bitmap, and the pixel content of that Bitmap is used to render directly to the screen later in the rendering process. The main computational work of this drawing method is to convert the drawing operation into pixels (for example, a sentence canvas.drawcircle () to obtain the pixel information of a specific circle), which is computed by the CPU. Something like this:

However, when hardware acceleration is enabled, the working mode of Canvas changes: it just saves the operation of converting the drawing content to GPU, and then hands it to GPU, and finally the GPU completes the actual display work. It goes something like this:

As shown in the figure, when hardware acceleration is turned on, all the CPU does is convert the drawing work into GPU operation, which is relatively small.

Why is it “accelerated”?

As you can see from the above figure, when hardware acceleration is enabled, the computing work of drawing is transferred from the CPU to the GPU. But how does this “speed up” and make the drawing faster?

Hardware acceleration makes drawing faster for three main reasons:

  1. Originally by the CPU itself to do things, apportioned to the GPU part, naturally can improve efficiency;
  2. Compared with CPU, the design of GPU itself has advantages for the calculation of many common types of content (such as simple circles and simple squares).
  3. Due to the different drawing process, hardware acceleration can optimize the drawing process when the interface content is redrawn, avoiding some repetitive operations and greatly improving the drawing efficiency.

The first two points can be summarized as follows: with GPU, drawing is fast. The reason for this is pretty straightforward.

On the third point, let me outline how it works:

As mentioned earlier, when hardware acceleration is turned off, the drawn content is converted by the CPU into actual pixels and rendered directly to the screen. Specifically, this “actual pixel” is carried by a Bitmap. If hardware acceleration is not enabled when a View in the interface calls the invalidate() method due to changes in its content, then in order to calculate the Bitmap pixels correctly, The parent View, the parent View’s parent View, all the way up to the top View, and any sibling View that intersects it, need to be redrawn with a call to invalidate(). The change of a View makes half of the interface or even the whole interface be redrawn, which is very heavy workload.

When hardware acceleration is enabled, as mentioned above, the drawn content will be converted into the OPERATION of GPU and saved (in the form of display list, and the corresponding class is also called DisplayList), and then transferred to GPU. Since all the drawn contents are independent of each other because they are not the final pixels, when the interface content changes, just call the invalidate() method of the changed View to update its corresponding GPU operation. As for its parent View and brother View, Just keep it the same. So that’s a very small amount of work.

Because of the above reasons, hardware acceleration not only improves the drawing efficiency due to the introduction of GPU, but also greatly improves the refresh efficiency when the interface content changes due to the change of the drawing mechanism.

So to summarize the above three items, hardware accelerates faster for two reasons:

  1. With GPU, drawing is faster;
  2. The change of drawing mechanism greatly improves the refresh efficiency when the interface content changes.

limit

If this were the case, and hardware acceleration had only benefits and no drawbacks, then we wouldn’t have to care about hardware acceleration, and this article wouldn’t be here: Why care so much about the principles of hardware acceleration?

However, the fact is that hardware acceleration is not only beneficial, but also has its limitations: due to the limitations of GPU drawing mode, some methods of Canvas will fail or fail to work properly in hardware acceleration open mode. For example, clipPath() only works on systems with API 18 and above when hardware acceleration is turned on. The relationship between specific API restrictions and API versions is shown below:

So, if you have custom draws in your custom controls, it’s a good idea to look at this chart and make sure your draw works correctly on all users’ phones, rather than just testing it once on your Nexus or Pixel running the latest version of Android. Be careful of being sacrificed to heaven.

However, there is a point can rest assured that all native controls, are not using API version incompatible drawing operations, can rest assured use. So all you have to do is check your custom drawing.

View Layer

As I mentioned several times in previous installments, if your drawing operation does not support hardware acceleration, you need to manually turn hardware acceleration off to draw the interface, using this line of code:

view.setLayerType(LAYER_TYPE_SOFTWARE, null);Copy the code

A lot of people have a question: what is layer Type? If this method is a switch on hardware acceleration, why does it take three arguments instead of LAYER_TYPE_SOFTWARE to turn hardware acceleration off and LAYER_TYPE_HARDWARE to turn hardware acceleration on, Is there a LAYER_TYPE_NONE outside of SOFTWARE and HARDWARE? Is it possible to draw with neither software nor hardware?

In fact, the purpose of this method is not to turn hardware acceleration on or off, but to “incidentally” turn hardware acceleration off when LAYER_TYPE_SOFTWARE is used. Besides this method, Android does not provide a dedicated View-level hardware acceleration switch, so it is incidentally a method of turning hardware acceleration on and off.

The setLayerType() method does exactly what it says: sets the View Layer type. The View Layer, also known as the off-screen Buffer, is used to enable a separate place to draw the View, rather than using a software-drawn Bitmap or a hardware-accelerated GPU. Depending on whether hardware acceleration is enabled, this “place” can be a separate Bitmap or an OpenGL texture. What is used to draw the View is not the key, the key is that when the View Layer is set, its drawing will be cached, and the cache is the final drawing result, instead of just saving the OPERATION of GPU and handing it to GPU for calculation like hardware acceleration. With this further caching, View redrawing is even more efficient: as long as the drawing remains the same, neither the CPU nor the GPU will have to recalculate, but only use the previously cached drawing results.

In fact, the off-screen Buffer would be more appropriately called the off-screen Cache. The reason is explained in the previous paragraph, because it is actually a cache, not a buffer. (This is my personal opinion only)

Based on this principle, turning on the Hardware Layer when animating the properties of movement and rotation (without calling invalidate()) will greatly improve the efficiency of the animation, because the View itself does not change during the animation, only its position or Angle is changed. This change can be done by the GPU through simple calculation without redrawing the entire View. Therefore, turning on the Hardware Layer in the process of this animation can make the animation that has become smooth by relying on Hardware acceleration become smoother. The implementation would look something like this:

view.setLayerType(LAYER_TYPE_HARDWARE, null);
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY".180);

animator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        view.setLayerType(LAYER_TYPE_NONE, null); }}); animator.start();Copy the code

Or even simpler if using ViewPropertyAnimator:

view.animate()
        .rotationY(90)
        .withLayer(); // withLayer() automates the complexity of the above codeCopy the code

Note, however, that this only works if you’re animating properties like translationX translationY rotation alpha that don’t require invalidate() calls, because this method itself takes advantage of the fact that when the interface doesn’t occur, Time savings from not updating the cache. So in a nutshell —

This approach does not work for animations drawn based on custom properties. Remember those words.

In addition to turning off hardware acceleration and auxiliary property animation, Layer can also be used to add some rendering effects to the View, such as setting a ColorMatrixColorFilter to make the View black and white:

ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);

Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));

view.setLayerType(LAYER_TYPE_HARDWARE, paint);Copy the code

In addition, after setting the View Layer, the View needs to be drawn twice (once to the Layer and once from the Layer to the display) during its initial drawing and redrawing after invalidate(), so its efficiency of each drawing is actually reduced. Be careful with the View Layer and use it only when you need it.

conclusion

That’s all for this episode. As I said at the beginning, this episode is just a complete supplement, not much important or difficult, and I didn’t prepare a video or too many screenshots or GIFs to illustrate it. To summarize the conventions:

Hardware acceleration refers to the use of a GPU instead of a CPU to do the computation of the drawing. It improves the rendering speed from two aspects of work allocation and rendering mechanism optimization.

Hardware acceleration can be turned off using setLayerType(), but this method is actually used to set the View Layer:

  1. Parameters forLAYER_TYPE_SOFTWARE, use software to draw the View Layer, draw to a Bitmap, and incidentally turn off hardware acceleration;
  2. Parameters forLAYER_TYPE_HARDWAREUse the GPU to draw the View Layer to an OpenGL texture (if hardware acceleration is turned off, then the behavior andVIEW_TYPE_SOFTWAREConsistent);
  3. Parameters forLAYER_TYPE_NONE, close the View Layer.

The View Layer can speed up the refresh efficiency without invalidate(), but it cannot speed up the refresh that requires invalidate().

The actual time to draw the View Layer is higher than it would be without the View Layer, so use it with caution.