An app may draw the same pixel more than once within a single frame, an event called overdraw. Overdraw is usually unnecessary, and best eliminated. It manifests itself as a performance problem by wasting GPU time to render pixels that don't contribute to what the user sees on the screen.
This document explains overdraw: what it is, how to diagnose it, and actions you can take to eliminate or mitigate it.
Overdraw refers to the system's drawing a pixel on the screen multiple times in a single frame of rendering. For example, if we have a bunch of stacked UI cards, each card hides a portion of the one below it.
However, the system still needs to draw even the hidden portions of the cards in the stack. This is because stacked cards are rendered according to the painter's algorithm: that is, in back-to-front order. This sequence of rendering allows the system to apply proper alpha blending to translucent objects such as shadows.
Note: Overdraw is no longer as significant a problem as it was when discussed in Google I/O performance sessions, and performance pattern videos. This is because low-end devices have continued to grow in GPU performance, while their displays have plateaued at relatively low resolutions. Unless optimizing for a known low-performance GPU device, it's recommended to focus on optimizing UI thread work instead to ensure smooth app performance. In addition to this, OS optimizations avoid overdraw within your app in many cases (for example, a Fragment background overdrawing the window background).
Find overdraw problems
The platform offers the following tools to help you determine if overdraw is affecting your app's performance.
Debug GPU overdraw tool
The Debug GPU Overdraw tool uses color-coding to show the number of times your app draws each pixel on the screen. The higher this count, the more likely it is that overdraw affects your app's performance.
For more information, see how to visualize GPU overdraw.
Profile GPU rendering tool
The Profile GPU Rendering tool displays, as a scrolling histogram, the time each stage of the rendering pipeline takes to display a single frame. The Process part of each bar, indicated in orange, shows when the system is swapping buffers; this metric provides important clues about overdraw.
On less performant GPUs, available fill-rate (the speed at which the GPU can fill the frame buffer) can be quite low. As the number of pixels required to draw a frame increases, the GPU may take longer to process new commands, and ask the rest of the system to wait until it can catch up. The Process bar shows that this spike happens as the GPU gets overwhelmed trying to draw pixels as fast as possible. Issues other than raw numbers of pixels may also cause this metric to spike. For example, if the Debug GPU Overdraw tool shows heavy overdraw and Process spikes, there's likely an issue with overdraw.
For more information, see how to profile GPU rendering speed.
Note: The Profile GPU Rendering tool does not work with apps that use the NDK. This is because the system pushes framework messages to the background whenever OpenGL takes a full-screen context. In such cases, you may find a profiling tool provided by the GPU manufacturer helpful.
There are several strategies you can pursue to reduce or eliminate overdraw:
- Removing unneeded backgrounds in layouts.
- Flattening the view hierarchy.
- Reducing transparency.
This section provides information about each of these approaches.
Remove unneeded backgrounds in layouts
By default, a layout does not have a background, which means it does not render anything directly by itself. When layouts do have backgrounds, however, they may contribute to overdraw.
Removing unnecessary backgrounds is a quick way of improving rendering performance. An unnecessary background may never be visible because it's completely covered by everything else the app is drawing on top of that view. For example, the system may entirely cover up a parent's background when it draws child views on top of it.
To find out why you're overdrawing, walk through the hierarchy in the Layout Inspector tool. As you do so, look out for any backgrounds you can eliminate because they are not visible to the user. Cases where many containers share a common background color offer another opportunity to eliminate unneeded backgrounds: You can set the window background to the main background color of your app, and leave all of the containers above it with no background values defined.
Flatten view hierarchy
Modern layouts make it easy to stack and layer views to produce beautiful design. However, doing so can degrade performance by resulting in overdraw, especially in scenarios where each stacked view object is opaque, requiring the drawing of both seen and unseen pixels to the screen.
If you encounter this sort of issue, you may be able to improve performance by optimizing your view hierarchy to reduce the number of overlapping UI objects. For more information about how to accomplish this, see Optimize view hierarchies.
Rendering of transparent pixels on screen, known as alpha rendering, is a key
contributor to overdraw. Unlike standard overdraw,
in which the system completely hides existing drawn pixels by drawing
opaque pixels on top of them, transparent
objects require existing pixels to be drawn first, so that the right blending
equation can occur. Visual effects like transparent animations, fade-outs, and
drop shadows all involve some sort of transparency, and can therefore contribute
significantly to overdraw. You can improve overdraw in these situations by
reducing the number of transparent objects you render. For example, you can get
gray text by drawing black text in a
TextView with a
translucent alpha value set on it. But you can get the same effect with far
better performance by simply drawing the text in gray.
To learn more about performance costs that transparency imposes throughout the entire drawing pipeline, watch the video Hidden Costs of Transparency.