Project Butter was an initiative to make the OS faster and more responsive in Android 4.1. This presentation explains some of the tools and techniques used by the Android team to achieve its goals.
refresh at 60 Hz, allowing apps to render at 60fps VSync (vertical sync) is a way to synchronize the rendering sub-system with the refresh of the hardware display.
and 60 Hz. If your app does not hit 60 fps you might just be maxing out the device’s refresh rate. adb shell dumpsys SurfaceFlinger will tell you the exact refresh rate of your display.
when swapping buffers does not happen at the vsync. When this occurs, you will see part of the screen showing the previous buffer and part of the screen showing the new buffer. This effect can be seen in many games on consoles for instance. Prior to Android 4.1 Jelly Bean, animations and drawing requests were however not vsync’d.
happens when swapping buffers does not happen at the vsync. When this occurs, you will see part of the screen showing the previous buffer and part of the screen showing the new buffer. This effect can be seen in many games on consoles for instance. Prior to Android 4.1 Jelly Bean, animations and drawing requests were however not vsync’d.
Properties Without DisplayList Properties Comparison of drawing the transition from launcher to the list of all apps with and without display list properties. You can see that display list properties cost almost nothing
gfxinfo dumpsys gfxinfo is a useful tool to analyze hardware accelerated apps shows memory usage, number of views and, with the right setting, frame profiling data
of values: Draw = update display lists Process = execute display lists Execute = swap buffers Copy the array and paste it in a spreadsheet to visualize the data. You’ll see the last 128 frames at most. Every time you capture a series of value, the buffer is cleared.
Update display lists Process display lists Swap buffers Scrolling the main screen in Settings on a Nexus 7 Everything is below 16ms. You can see processing the display lists is usually what takes time (turning Canvas commands into OpenGL primitives)
new, system-wide, instrumentation tool Useful to see how your app interacts with the rest of the world It can be used for optimization work. For instance if performance looks bad from gfxinfo or gfxinfo looks good but perf is bad
settings, open the result and explain what we see: performTraversals/draw/etc. Open trace-slow.html and show what a bad app looks like and what to do with it. Go in traceview to see what the app does
you can attach to bug reports, email to co-workers, etc. Open the help menu to see all the keys you can use. Most useful: WASD (just like in FPS games on PC) to move around and zoom in/out
... | com...SlowListActivity OVERLAY | ... | StatusBar OVERLAY | ... | NavigationBar Here’s a sample output. You’ll find this table at the end of the dump. You want as many windows as possible to go into overlays. Here we have 3 windows on screen and everything is composited with overlays, it’s great
... | com...SlowListActivity OVERLAY | ... | StatusBar OVERLAY | ... | NavigationBar ✔ ✔ ✔ Here’s a sample output. You’ll find this table at the end of the dump. You want as many windows as possible to go into overlays. Here we have 3 windows on screen and everything is composited with overlays, it’s great
... | com...SlowListActivity FB | ... | PopupWindow:424d4cc8 FB | ... | StatusBar FB | ... | NavigationBar In this case our application creates an extra window, the PopupWindow The windows now cannot all go in overlays and 3 of them must be flattened using GPU composition There is a cost: GPU has more work to do, burns fillrate/bandwidth, etc. You want to avoid this.
... | com...SlowListActivity FB | ... | PopupWindow:424d4cc8 FB | ... | StatusBar FB | ... | NavigationBar ✔ ✘ ✘ ✘ In this case our application creates an extra window, the PopupWindow The windows now cannot all go in overlays and 3 of them must be flattened using GPU composition There is a cost: GPU has more work to do, burns fillrate/bandwidth, etc. You want to avoid this.
opacity of the window may impact whether it goes in an overlay or not. Always use dumpsys while the app is drawing, or shortly after it’s done. Android has an optimization to move everything to FB (GPU) after a short period of time (saves battery when the app doesn’t update)
the official documentation in the SDK It’s a tracing profiler that helps you see what’s going on in your app and how much time it takes It’s one of the most important tools at your disposal, and you can invoke it from ADT or DDMS
optimizing your UI. Use it stand-alone or as part of ADT. It shows you the tree of views straight from your app. You can inspect numerous properties, capture partial screenshots, force layouts/repaints, export PSD files and much more. Custom properties are supported with an annotation.
the box on consumer devices. You must use an emulator, a userdebug or eng build, or use ViewServer.java available on github. Apache 2.0, easy to use and secure. We’re thinking of ways to integrate this directly into ADT.
and profile GL apps. Can be useful for hardware accelerated apps to see exactly what’s going on behind the scenes. Shows you how many commands your app generates, how much overdraw they cause, etc. You can also see the time each command takes to execute. Note that the commands are grouped by view, making it easy to read.
easiest way to track down and remove unnecessary allocations from your app. Each allocation has a type, size and stack trace. Anecdote: as I was preparing this talk, I decided to take a screenshot of Allocation Tracker. This lead me to discover numerous unnecessary allocations caused by the GL rendering pipeline and that affected most apps.
don’t use it. Use DDMS’ allocation tracker to ensure you allocate only what you need in performance sensitive code paths (onDraw, onMeasure, onLayout, touch events, Adapter.getView, etc.)
don’t use it. Use DDMS’ allocation tracker to ensure you allocate only what you need in performance sensitive code paths (onDraw, onMeasure, onLayout, touch events, Adapter.getView, etc.)
4.1 Jelly Bean. It lets you schedule callbacks on vsync. This is the API used internally by the framework to schedule animations, redraws, etc. This API is great if your app does its own animations; games for instance.
method when implementing animations in your view. This is how the UI toolkit animates scrolls and flings in ListView, ScrollView, etc. Can be invoked from any thread.
render(frameTime); } }; Choreographer c = Choreographer.getInstance(); c.postFrameCallback(callback); If you write a game, you might not have a View, or you’re not using the UI thread, so use Choreographer direction. Simply create a FrameCallback and pass it to Choreographer. Note that there is one Choreographer instance per Looper thread. You can post a callback from any thread, but they will run on the Choreographer’s looper. The frametime in nanoseconds is very useful to synchronize animations on a single time-base. It’s the time at which the vsync event occurred.
A layer is a GPU snapshot of your view, and it’s extremely efficient to render. For more information, please refer to our Google I/O 2011 talk called “Android Accelerated Rendering”, available on YouTube.
to ensure the system only draws what needs to be drawn. Usually you don’t have to do anything but if you have custom drawing code or custom views you should make sure you don’t draw more than what you really need. Here are a couple of things you can do to help.
the entire view. In many situations a partial invalidate is enough and will help performance. Simply specify the region of the view that needs to be redrawn. A great example of this is EditText and its blinking cursor.
need to redraw the entire view. In many situations a partial invalidate is enough and will help performance. Simply specify the region of the view that needs to be redrawn. A great example of this is EditText and its blinking cursor.
on a View will cause that entire View (and any view it intersects) to redraw. In this example, a complex view causes all of its children to redraw. Each display list must be executed.
invalidate() on a View will cause that entire View (and any view it intersects) to redraw. In this example, a complex view causes all of its children to redraw. Each display list must be executed.
display lists Calling invalidate() on a View will cause that entire View (and any view it intersects) to redraw. In this example, a complex view causes all of its children to redraw. Each display list must be executed.
DisplayList DisplayList DisplayList DisplayList DisplayList DisplayList invalidate() Draw display lists Calling invalidate() on a View will cause that entire View (and any view it intersects) to redraw. In this example, a complex view causes all of its children to redraw. Each display list must be executed.
t, r, b) the rendering pipeline can cull (i.e. ignore) entire parts of the render tree. This leads to significant performance improvements when used properly.
r, b) With invalidate(l, t, r, b) the rendering pipeline can cull (i.e. ignore) entire parts of the render tree. This leads to significant performance improvements when used properly.
r, b) Draw display lists With invalidate(l, t, r, b) the rendering pipeline can cull (i.e. ignore) entire parts of the render tree. This leads to significant performance improvements when used properly.
DisplayList DisplayList invalidate(l, t, r, b) Draw display lists With invalidate(l, t, r, b) the rendering pipeline can cull (i.e. ignore) entire parts of the render tree. This leads to significant performance improvements when used properly.
system what parts of the screen to redraw. It is equally important to never draw pieces of the UI that will never be visible to the user. In this section we’ll focus on a real-world example, using the Google application, new in Android 4.1 Jelly Bean. When the app shows you suggestion cards, they can appear stacked. If you use standard views, overdraw will occur.
here is how they will be drawn. First the bottom view, then the top one. There is quite a lot of overdraw happening here, highlighted in red. What can we do to get rid of that overdraw?
here is how they will be drawn. First the bottom view, then the top one. There is quite a lot of overdraw happening here, highlighted in red. What can we do to get rid of that overdraw?
here is how they will be drawn. First the bottom view, then the top one. There is quite a lot of overdraw happening here, highlighted in red. What can we do to get rid of that overdraw?
here is how they will be drawn. First the bottom view, then the top one. There is quite a lot of overdraw happening here, highlighted in red. What can we do to get rid of that overdraw?
c.clipRect(headerLeft, headerTop, headerRight, headerBottom); } drawHeader(c); drawContent(c); c.restore(); } The trick is to set the clip rect to include only the “header” of the stacked card. This way the content won’t be drawn at all.
{ if (!c.quickReject(item.l, item.t, item.r, item.b, Canvas.EdgeType.BW)) { item.draw(c); } } } If quickReject() returns true, then the specified rectangle is outside of the current clip region. Which means whatever you draw within the bounds of the specified rectangle will not show up on screen. In this case you can simply avoid calling your drawing code. This is what the UI toolkit does automatically with views.
Android, managed by the window manager and SurfaceFlinger. Any window can request the dim layer to draw attention to the foreground. It helps the user by giving context (you are still performing the same task) but removes distractions.
... | com...MyActivity FB | ... | DimAnimator FB | ... | StatusBar FB | ... | NavigationBar Using a dim layer will unfortunately introduce a new surface that SurfaceFlinger must composite. This is usually enough to make apps fall out of the optimized all-overlays case. In addition, the dim layer is *always* composited by the GPU. Even if there are enough overlays available to do the composition, the dim layer will force a GPU composition.
... | com...MyActivity FB | ... | DimAnimator FB | ... | StatusBar FB | ... | NavigationBar ✔ ✘ ✘ ✘ Using a dim layer will unfortunately introduce a new surface that SurfaceFlinger must composite. This is usually enough to make apps fall out of the optimized all-overlays case. In addition, the dim layer is *always* composited by the GPU. Even if there are enough overlays available to do the composition, the dim layer will force a GPU composition.
background drawable instead. This is what Android 4.1 Jelly Bean does in the quick contact window to ensure the opening animation is smooth, with a consistent frame-rate.
resources, use a background drawable instead. This is what Android 4.1 Jelly Bean does in the quick contact window to ensure the opening animation is smooth, with a consistent frame-rate.
you avoid using the dim layer, you can avoid spending GPU resources compositing the special dim layer. This will help improve the time spent drawing display lists. The fewer layers that need to be composited, the better.