Welcome back to GTKA, everyone's favorite investigative series where we learn all about the newest version of Android (with a heavy emphasis on "all"). The previous two episodes, if you didn't catch them, are here and here.
Today we'll be doing something a little different and looking at something near and dear to everyone's hearts: performance. Jelly Bean is crazy fast. Slap it on a Galaxy Nexus and it'll feel like a brand new phone. Scrolling is faster and smoother, and the touch response is hyper-sensitive. In addition to all the smoothness work, there are new transitions all over the place.
I'm sure you've all seen a sweeping overview of this, but those are boring. What exactly is new, and how did they do it? That's what we're here to figure out. So, grab your popcorn, kids; it's time to learn all about Project Butter.
How They Did It
So how do you make an 8 month old Galaxy Nexus run like a Galaxy S III? Lots of hard work. Hard work that was thoughtfully detailed to us at Google I/O by two of my favorite I/O presenters, Chet Haase and Romain Guy. An hour long PowerPoint presentation geared towards developers is not the most accessible thing in the world, so I'm going steal some slides and try to cover the more interesting parts, and (hopefully) condense it down to a few minutes. Doesn't that sound like fun?
VSync Turns Frame Drawing Into A Well-Oiled Machine
PC Gamers are probably familiar with the term "VSync." It's that graphics option checkbox that will stop your screen from tearing in a video game.
To understand what exactly VSync is, we're going to need a quick display primer: Video (ie a phone display doing stuff) is made of individual pictures called "frames." Smooth animation is usually 60 frames per second. Frames are made of pixels, and, when the display draws a frame, the pixels are filled in row by row. Got it? Good.
The display (LCD, AMOLED, whatever) gets each frame from the graphics chip, and starts drawing it line by line. Ideally, you want the display to get a new frame from the graphics chip after it is finished drawing the previous frame. Tearing occurs when the graphics chip loads a new frame in the middle of the LCD draw, so you get half of one frame and half of another.
VSync, well, synchronizes things. It tells the GPU to wait for the screen to finish its line by line drawing before loading the next frame.
Android has always used VSync to stop screen tearing, but Jelly Bean takes things a step further. The VSync pulse is now used to start all the processing for the next frame.
Most Android displays run at or around 60 frames per second (or 60 Hz, in display jargon). In order to have a smooth animation, you have to actually be able to process 60 frames per second - that means you've got 16ms to process each frame. If you take longer than 16ms, the animation will stutter, and that buttery smooth feeling we're aiming for melts away.
16 milliseconds isn't a lot of time, so you're going to want to make the most of it. In Ice Cream Sandwich, processing for the next frame would just kind-of lazily start whenever the system got around to it. In Jelly Bean, the whole process of making the next frame starts as soon as the last frame is finished, at the beginning of the VSync pulse. In other words, they're using as much of the 16ms as they can. Here's an example:
This is what life is like without running everything off of VSync. In these slides, the numbers are frames. First the frame is processed by the CPU and GPU, and, when it's finished, it's handed to the display at the beginning of the next VSync.
So in this picture, Frame 0 is displayed, and, during the 16ms of display time, the CPU and GPU prepare the next frame. The calculations get done in time, and at the next VSync pulse, they hand over the next frame, "Frame 1." Great.
We're now showing frame 1, and it's time to start working on frame 2. Something slows the system down, though, and processing for the next frame doesn't start until we're well into our 16ms time limit. The system now only has about 4ms to figure out frame 2, so the processing for it doesn't get finished in time. With no frame 2, the display is forced to show frame 1 again, for another 16ms. Team Android has dubbed this "Jank," and it basically means any animations happening during this time won't be fluid; the user will see stuttering.
Here's the new way of doing things in Jelly Bean. All the processing for the next frame starts at the VSync pulse, so you're now making the most of your 16ms of render time.
Frame processing has moved from "Yeah whenever we get around to it," to a rigidly scheduled, highly organized affair. Sort of like the difference between a car mechanic and a NASCAR pit crew. In this example, all the processing happens within the 16ms time limit, all the frames get delivered on time, and you get a smooth, buttery experience.
Triple Buffering Stops Jank From Snowballing
VSync isn't the only thing that helps out animation smoothness, Android is also able to recover from a slowdown more smoothly.
So what the heck is a "buffer"? Put simply, a buffer is the container the frame is built and stored in. We've been referring to frames by number, but really, those frames sit in one of the two buffers. Android is double buffered, a pretty typical setup, meaning it can display 1 frame while working on another. In this picture the buffers are labeled "A" and "B." While displaying buffer A, the system starts building a new frame in buffer B. When that's ready, they swap buffers. B gets displayed, and A gets cleared and a new frame is worked on.
The problem with double buffer arises when you take longer than 16ms to draw your frame. Here the processing on buffer B runs over, which means a stutter (Jank!) happens. Stuttering is bad and all, but the real problem here is all the white space in the CPU and GPU graphs, that's wasted time. In the first frame, buffer B runs over, so that buffer is in use until it can be displayed, buffer A is also in use, because it is being displayed for 2 frames in a row, and remember, you can only switch frames (and hence, buffers) at the VSync pulse. The CPU and GPU are out of usable buffers, so they just sit there. One slowdown puts the system behind, which causes more slowdowns. This was how Ice Cream Sandwich worked.
In Jelly Bean, we've got a solution for 2 full buffers, a third one! Same situation as before, buffer B takes too long, and A is in use displaying the current frame. This time though, instead of wasting processing time during the repeated buffer, the systems creates a C buffer, and gets to work on the next frame. Triple buffering stops the stutter fest, and after the initial skip, the user sees a smooth animation. It's all about presenting a stiff upper lip to the user even though things aren't going so smoothly under the hood.
So why don't they just do triple buffering all the time? Well, as you can see from the diagram, triple buffering introduces a bit of input lag into the process. Look, for instance, at the distance between the rendering of buffer C (the blue/green part), and the displaying of it. So, when things are misbehaving, you get a choice of 2 evils: input lag (your touches taking longer to have an effect) or choppy animation.
To address this, Jelly Bean doesn't do triple buffering all the time. It normally runs a double buffer, but that third one is around whenever you need it. This way you get less input lag, and when things go wrong, you've got a third buffer at the ready to help you recover.
The result of all this hard work is buttery smooth animation. The team was so impressed with their new animation aptitude that they spruced up many of the transition animations, which I will now present to you in excruciating detail. Check out ICS vs Jelly Bean, in super slow-mo.
Icon Launch Transitions - Left: ICS's no-frills app launch transition. Right: JB zooms in from the location of the icon
There isn't much to say about these videos, but I'll try my best. Ice Cream Sandwich (left) has an absolutely boring icon launch transition. It's just a simple stretch and fade to the center of the screen. In Jelly Bean (right), applications zoom out from the icon position. This manages to be both neater looking and more communicative: "You pressed this icon, and this is opening from it." I like it.
Uninstalling - Left: ICS just uses the standard screen transition. Right: Jelly Bean's fun "toilet flush" animation
Uninstall gets a cool transition too. It's a slide/fade/shrink animation to the bottom of the screen. It sort of reminds me of a toilet flush. The previous screen also gracefully slides into view when the app is gone.
Launching from within an app - Left: ICS does a simple fade. Right: JB slides things all over the place.
This transition is pretty much the uninstall animation in reverse, and it is used all over the place.
I think it's triggered whenever an app opens another app. So here, you see the play store launching an app, and you'll see it when something like Google Reader launches a web browser, it's all over the place.
"One thing I'd like to clarify -- the "uninstall" and "launch" animations are actually the same thing. This is the animation set for a "task switch". That is, moving in the UI from one task to another. A task is the thing you see in recent tasks, so this effectively tells you that you are moving between tasks that would be seen in the recent tasks UI."
So there's even more communication through animation. Thanks Dianne! <3
Recent Apps - Left: ICS oddly fades to a blank desktop. Right: JB's awesome transition
Recent apps probably has the coolest animation out of the bunch. The app grows out of the thumbnail, which is, again, neat looking and communicative - the perfect use of an animation system like this.
And finally, the new Jelly Bean camera animation. Snapping a picture sends your still flying off to the right, and a swipe will bring it back. I didn't bother showing you the ICS version because, well, nothing happens.
That's about it for Project Butter. You really have to experience it to get the full effect. Everything is smooth and fast. It really is impressive how much more power they've managed to squeeze out of my old Nexus.