Tip:
Highlight text to annotate it
X
>>Chet Haase: So just a reminder this talk is being live streamed so if you would like
to catch Glenn and Rafe's excellent performance you can go to that one and then watch ours
at the same time. Last year we gave a talk where we introduced
Android Jelly Bean 4.1, and 4.2 came out soon after that. And today we would like to announce
-- >>Romain Guy: Graphics and performance.
[ Laughter ] >>Romain Guy: The joke would have worked a
lot better, but for some reason for the live stream we have to show the title slide first
so we have to go back. Hi, I'm Romain Guy and this is Chet Haase
and we both work on the Android framework team and we both work on graphics, performance
and animations. So this talk is divided into three, sections.
First we'll talk about architecture and -- I'll get back to this.
Then we'll talk about tools and finally we'll give you some tips and tricks on how to improve
performance on your applications. The architecture section is interesting because
for the first time I think ever we're going to talk about stuff that doesn't exist yet.
We're going to talk about stuff that's coming. So --
>>Chet Haase: It's a really good way for us not to be here next year to give any talk
to you on behalf of Google, but we're going to try it anyway.
>>Romain Guy: That doesn't mean it's going to come out this year or next year. We can't
tell you when, we can't tell you how and we can't tell you what version number, what name.
It's just coming sometime in the future. >>Chet Haase: Maybe.
>>Romain Guy: Hopefully. >>Chet Haase: So first of all, one of the
architectural changes that we have implemented in this fictional future release is reordering
and merging. If you recall last year if you happened to
be in a session called for better or worse, we gave a really confusing diagram of what's
actually going on in the rendering engine, and it bottoms out in something we call display
list which is basically our intermediate mode of the commands that we're going to send down
to the GPU. Then we compose OpenGL commands out of that and send them on down and they
get rendered, hardware, acceleration. Life is good.
What we have done recently is implemented some changes so that we can actually re-order
and merge commands to make it more optimal the way that we actually talk to the GPU.
We have in previous talks and in articles recommended a tip where you should batch your
commands. So if you're doing a bunch of -- like I'm going to draw a bitmap, I'm going to draw
some text, I'm going to draw a bitmap, draw some text, it's better for us and for the
GPU if you draw all the bitmaps at once and all the text at once.
We've tried to remove that from you and just do it more automatically. In fact, it turns
out that we can actually do even better than that.
>>Romain Guy: For once we're making your life easier instead of harder.
>>Chet Haase: I don't know how that happened. >>Romain Guy: It is not even an API. It's
all automatic. >>Chet Haase: So let's pretend that this is
a sample UI, a very boring sample UI, but an sample UI nonetheless. We have a couple
of chat boxes, each one with some text. We have a couple of buttons. Let's pretend the
order that we see it on the screen is the order in which they're added to the containers,
so they're basically going to draw it in this order.
So we're going to -- there's the order. We're going to draw the check boxes, so we draw
a bitmap with the checked check box, and then we're going to draw --
>>Romain Guy: [ Inaudible ] >>Chet Haase: That animation was so awesome
we're going to do it again. [ Laughter ]
>>Chet Haase: Draw a bitmap, we're going to draw some text, we're going to draw a bitmap,
draw some text, we're going to draw a nine patch and draw some text. Eight commands,
not a lot to worry about, but let's see what happens when we can actually re-order these
things. So we can switch things around so that before
we send it down to the GPU in the display list itself we switch the order of commands
so you draw a bitmap twice, draw two nine patches and then draw the text that are next
to and on top of those objects. That tends to be a lot faster for the GPU to process
because it doesn't have to change state. It's not constantly changing the state of the shaders
that it's using, the state that we draw bitmaps with versus the state of text. None of those
things have to change and the GPU is really happy when you don't change states.
So this is a good step to begin with and this is where you could get to if you manually
batched your stuff according to when you made your drawing step. We can do that for you
now in this fictional upcoming release. But on top of that we could do something that
we call merging, which is we can actually take all of those commands that we have now
reordered and say, do you know what? What if we just did one command that drew two bitmaps
in a row and did one command that drew to nine patches in a row, and then draw all of
the text all at the same time? Even though they're actually on different
views on the screen, that's irrelevant to the GPU. All it's doing is drawing pixels
on the screen. If we can do that we've reduced essentially eight commands to the GPU, which
were originally changing state and interweaving it the entire time into three commands, which
is much better for the GPU and much better for performance in general.
>>Romain Guy: So we have a demo. We'll use Google+ as an example and I'm going to switch
to one of our debugging tools. I'm going to show you what happens when -- without the
reordering and the merging. So I'll launch that tool to open the debugging and I'll capture
a frame and we'll see exactly what the app draws. Are getting everything? Yes.
So we have to look at in details, but as you can see this is the G+ UI so you have all
your cards in your stream of posts, and you can see that we draw the cards one by one
because that's how they're defined. We'll draw the white backgrounds first and then
Chet's face and then we'll go to the text and then we go to the next card and we do
that over and over again. So in total we have 88 drawing commands that have been executed
on this frame. And as you can see there's a lot of -- Chet was talking about state changes
and you can see it happening here. Here we draw a bunch of text, then we switch to bitmaps,
then we draw text again, then bitmaps again and text and bitmap and so on and so forth.
So now if we re-enable the new optimizations -- I have to kill the app first.
Of course, it's reloaded the post so now they are different.
>>Chet Haase: Tension is building. What's it going to look like?
>>Romain Guy: What makes me happy is we have everyone -- people at the conference this
week, so if it insists on not working we can go yell at them.
>>Chet Haase: Is there a dev doctor in the house?
>>Romain Guy: So this is what we do everyday and everyday it's the same. We have to try
again and again and again until it works. [ Laughter ]
>>Romain Guy: So you know whenever you complain about the dev tools just know that we're in
the same boat. [ Laughter ]
>>Romain Guy: All right. So we went from 88 drawing commands to 39. So it's not exactly
the same configuration, but you can see what's going on here. Things are drawn out of order.
You can see the text appear in pretty much one block. We're going to go back to that.
Let's go here at the beginning. So we can see that we're drawing the white
backgrounds of the cards together and then we go back to fill the inside of the cards.
And then when we draw the text there's this one spot where we draw a huge batch of text,
right here. And you can see the geometry here, you can
see that we draw pretty much the entire text on screen in one draw call instead of many.
So this is what it does for you. You don't have to worry about it. Don't change your
app. It's going to be faster and better automatically. And in the future version of Android that
may or may not exist, and that you may or may not get on your device, we are also going
to introduce a little bit of multi-threading to the rendering pipeline. So we're still
going to do the rendering on the main thread, so it's still very important that you do not
block the main thread to get smooth animations; however, some of the operations that the render
performs on your behalf will now happen on multiple cores, if you have multiple cores.
>>Chet Haase: Which is more and more common. The way that manufacturers are scaling performance
in general tends to be going wide, so you've got as opposed to faster and faster CPUs,
which was the model for year, now we're getting multiple cores, two cores, four cores. If
you've got these multiple cores down there can you take advantage of parallel processing
instead of just continuing to do everything as fast as you can on one.
>>Romain Guy: So here we're executing the drawing command. If you're using drop shadows
in your application, if you have drop shadows on text, that's what the launcher does a lot
on Android, we'll use renderscript and we're going to spin up on the four core device.
We're going to use four threads to generate the drop shadows, so we'll use all the cores
at our disposal. So if you draw a little circles or arbitrary geometric shapes with curves,
we're also going to use multiple cores to draw this in the background as open textures
so then we can draw them on the UI thread. So once again, you don't have anything to
do, this all under the hood, but just be aware what's going on.
We also have support for non-rectangular clipping. It wasn't supported in the (Indiscernible)
pipeline, so non-rectangular clipping can be used to draw very useful things such as
text in the shape of a heart. If you ever do that in your app, like I don't want to
know what your app does, but you can also be using -- we'll talk more about -- we will
talk more about that later. It can also be used to clip when you're doing
rotations. So let's say you have a ListView or a ScrollView and you have a 3D rotation
so you want to flip it over. If you're not using a layer and you run that animation on
existing versions of Android, like 4.1 or 4.2, you will see that the clipping breaks
because the clip rect of the list as you rotate it in 3D doesn't -- turns into something that's
not a rectangle any more. And we didn't know how to deal with that before, but now that's
fixed or it will be fixed. And if you're curious on how to do a non-rectangular
clipping and want to clip your text with a heart, this is how you do it. You get a canvas,
you can call clip path. There are other methods that you can use. You can do those fancy shapes.
And then you just draw your content and it will do the right thing for you.
>>Chet Haase: Now a discussion about some of the tools that we use. Some of these exist
now, but we'll be talking about some slight modifications and improvements going forward
in possible and fictional releases. >>Romain Guy: So the first one was introduced
in 4.2, I think. It's a debug tool that's on the device so you go to settings, developer
options, there's a check box called show dp overdraw. I wrote about it online. I wrote
an article about that. How many of you know about this option or have used this option?
Why are we here? [ Laughter ]
>>Romain Guy: You're making my job useless. So show dp overdraw will colorize the screen,
it will use several different colors to show you how many times you draw each individual
pixel on the screen. So here you have a picture of the Google Play Store with the option turned
on, and you can see there's this bluish tint in the background. That doesn't come from
the app. That's the tool adding this blue color on the background. You can see there's
a lot of red in the middle, the app called 1-800-flowers. You can see there's a lot of
red in the middle because we have a lot of pixels here. So the color code is pretty simple.
Blue means you've drawn a 1X overdraw. So it means that you've drawn that pixel once
and then you've drawn on top of it. So there's a 1X overdraw.
Green means you have a 2X overview, so you've drawn the pixel three times. The slight difference
between the two reds. It doesn't really matter because if it's red you're doing something
wrong. Red is 3X or 4X or more. >>Chet Haase: And if it's 5X we just reboot
the machine. >>Romain Guy: I wish I could do that.
>>Chet Haase: And there is a 0X. If you actually have no redraw, then you will just see the
natural color that they're painting on the screen.
>>Romain Guy: We'll see the video of the overdrew and how it works and the kind of techniques
that you can use to fix overdraw in a few minutes.
>>Chet Haase: So this is a tool that we introduced in 4.1.
>>Romain Guy: Last year. >>Chet Haase: To track your frame rate. It
seemed to be a little bit sort of inconsistent on how you would get the frame rate and how
to understand how fast you were running and more importantly how consistently fast you
were running, what your effective frame rate was in your application.
So we instrumented the system to give you this information, and if you did an ADB shell
command dump GFX info, then we would spit out the information for the last 120 frames
and then you could take that data and you could paste it into a spreadsheet and then
you could create a chart, and apparently that was too tedious for people. So we have made
that a little bit easier. Actually, if you go back to the previous slide,
this gives you the information about the time that we're spending actually creating the
drawing commands that are going to be issued and then issuing the drawing commands and
then waiting for OpenGL to come back because it processed the drawing commands and swapped
the buffers. This gives you an indication of those relative times as well as how many
times that's happening per second or what your frame rate is.
So we have the same exact tool available now, but instead of making you go through the incredibly
tedious and overbearing process of creating a spreadsheet to see a graph, we actually
just create the graph for you on the screen. So you can pull up developer settings, you
can select an option in the dialogue box to see how you want to display the information,
whether you want to use the tedious method or some onscreen graphical method. Instead,
you can see the results in real-time, such as is demonstrated here. We're running a calendar
application, and we can see as the calendar application, I think you were probably scrolling
back and forth in the calendar application. And you can see in real time us update an
overlay on the device so you can see what the rendering performance and frame rate is
like. So you will actually see this for all active
activities on the screen, which is why there's actually three separate charts that you will
see. You will see one for calendar, you will also see one for the navigation bar and the
notification bar at the top. >>Romain Guy: It works in every application.
And you can see it working here -- right here on the device in real time. There's a green
line that wasn't shown in the graph that we used last year as a demonstration. So the
green line is the 16 milliseconds threshold. So if all your vertical bars are under that
line, then you will run at 60 FPS. And if you cross that line then there's going to
be jank. So it's more complicated than that, but basically
if you can stay below the line you're part -- you're in good shape in your application.
>>Chet Haase: The general tip is smaller is better. Try to stay away from the green line
on the underside of it. And what you will see -- and also be aware
of situations where you're tripping very occasionally over that green line because then basically
you drop down to half the frame rate immediately and the net effect on the screen is that the
user is going to see a stuttery animation where it was smooth in 60 FPS and then you
skipped a frame in the middle. It's a very discontinuous and disturbing artifact.
[ Applause ] >>Romain Guy: I have to say, most of the time
when you guys send us emails or ask questions, you're pretty mad at us. It's pretty cool
when you are happy. I like that. Keep doing it.
>>Chet Haase: Maybe it was an angry clap. >>Romain Guy: Maybe it was an angry clap.
Yeah, I'm worried now. Next we want to talk about systrace. Systrace
is this awesome tool, and you can thank Jamie right here for the tool. It's a great performing
tool we introduced last year. It was a little difficult to use, just like
the performance graph we showed you. It was a little bit involved to set it up. And in
this future version of Android -- sorry, future update of Android, not necessarily version
number, it's easier to use systrace. So now the way you invoke systrace is still
from the command line, but you had to turn on several tags. Like you had to tell us I
want to profile graphics, I want to profile views, I want to profile the window manager,
and then you had to restart the runtime on the device, effectively rebooting the device,
and then you could run your command. Now the way you do it you just invoke the
script, you specify the tags that you want to trace. So here we are tracing gfx that's
all, that's all the graphics information; view, that's all the UI toolkit information;
things like inflation, layout, measure. Freq is the frequency of the different CPUs, and
sched is the scheduler, so that way you can see who scheduled on what the CPU and when.
And I am going to show you and example of that.
We also added in 4.2 the ability to trace open OpenGL commands. So if you use OpenGL
in your application and or if you write a normal application and you are curious about
how many GL commands we execute on your behalf, you can go turn that on in the developer options,
and we're going to trace every individual OpenGL command and you are going to see them
in systrace. Actually, let's go with the slides for a while.
We are also introducing a new API. So there's this new class called Android.os.trace. That's
the API that we use internally in the framework to do all of this instrumentation, but we're
opening it up a little bit. There's this new API called begin section
and end section. And here, this is an adapter. It's an adapter I created in a little test
application, and at the beginning I call begin section. I specify string. So that's just
the name of the section you're going to see the in profiler. So that name can be anything
you want. At the end you close the section by saying end section, and in the middle we
can have another section called bind. So you can nest the sections.
The advantage of using this over TraceView is that the overhead is almost nonexistent.
So it's not going to modify the behavior of your application when it's running, and the
timing that it gives you are very, very, very precise.
And if you do this -- so if you use this new API, you have to use this other flag when
you invoke systrace. You have to use dash A and you have to enter the name, the package
name of your application. >>Chet Haase: So to be clear, we're not recommending
don't use TraceView. TraceView is awesome. It is the tool to get all the information
on all the method calls that happened. However, if it's showing you timing about method call
overhead at the Java layer, be suspicious because there's a lot of overhead associated
with the tool and with the instrumentation in particular. So if you want to eliminate
that as a factor and you know the code path that you want to instrument very carefully,
this is a really good way to get that information. >>Romain Guy: And very often the way we will
use systrace, at least at our level, we will use systrace to get an idea of where the application
is slow, and then we have pinpointed that part of the application then we will use systrace
to see inside that block what is taking time. This is much more efficient usually than going
to TraceView directly. So here I have two more examples. Here is
-- Okay. The first one, so this is what the systrace file looks like. It is pretty scary
but it is actually pretty cool. At the top here you can see three CPUs, and if I zoom
in all the way -- >>Chet Haase: I like the text heart better.
It was more romantic. >>Romain Guy: So if I zoom in all the way,
it may be hard to see but you can see the name of the process that was scheduled on
the CPU, on CPU zero at that time. So if you're doing a lot of multi-threading in your application,
it's a great way to see what exactly is going on. So maybe all your threads are running
one after the other because you have a synchronization issue. You can see that in systrace.
You can see the frequency of each CPU. So here the CPU zero and CPU one were running
at full frequency, and you can see that sometimes they go back down a little bit. So I took
this when I was scrolling -- I don't remember. It was Gmail. It was Gmail.
And here at the bottom I have the name of the application cell, so this is Gmail and
we can see what Gmail was doing. So here we can see one frame of the Gmail application,
and we can see that Gmail took 28 milliseconds to render one frame, so that's too slow, but
only less than one millisecond was spent on the Dalvik side executing Java code.
So most of the time is spent executing the OpenGL commands, so that means that the app
is not doing much at the Java level, but it gave us so many drawing commands that we cannot
draw at 60 fps. And I keep zooming here, you can see all the
individual OpenGL commands that we've executed for you.
So if you work on games, that's probably a lot more useful than if you work on apps.
But this is what the systrace looks like. And we should -- there's a lot to do -- to
say about systrace. We would need a couple hours, I think, to teach you exactly how it
works. But go give it a try. It's really cool. Now, if you use that new API I just mentioned,
the begin and end section, this is what it's going to look like. So here you can see the
two tags I created. I know they are hard to read, but you see the strings create list
item and bind image. I'm trying to zoom. So again, you can see exactly how much time you
are spending executing the section. So here, I'm spending 31 milliseconds in GetView, and
out of 31 milliseconds, almost the entire time was spent binding an image so I was probably
loading the image from the GetView method. >>Chet Haase: Tips and tricks.
>>Romain Guy: Overdraw then? >>Chet Haase: Yeah.
>>Romain Guy: So we'll switch to code. >>Chet Haase: Is this the one you did your
analysis -- >>Romain Guy: No. It's a different one.
>>Chet Haase: Okay. >>Romain Guy: So I have a very simple application
that I'm going to install on this phone over there, and I am using the new Android Studio,
so hopefully it works. Yeah, it worked. So this app is just a demo app; right? So
it's a list of images, and what I do is I fake loading the images from the network,
so when I scroll really quickly, you can see a placeholder, and after a random amount of
time, the image appears. So the scrolling is pretty good but this is a Nexus 4 so performance
is usually awesome on the device. Now, if we go into settings and we turn on
the overdraw debug -- where is it? Here we go. So now everything becomes green and blue
and it's hard to read but you can see my application is mostly red. So the background of the app
is blue that means there is something behind the background of the app so we should get
rid of that. The images are deep red so there is clearly something else also behind them,
and my nine patches are green so there's, again, something behind those things.
So now we're going to go to the code and see what we can do to fix this application.
So if we look at the layout of the activity, or look at the XML -- how do I zoom? Here
we go. Is the text big enough for everybody? Okay.
You can see here I have a simple ListView. That's all there is in my application. I have
a ListView and I gave it a background color because I wanted that custom color. But by
default, on Android when you create and application you have a background already. It's the window
background. It comes with the theme. So right now I have two backgrounds. I have the one
that comes from the theme and the one in my list and we're going to draw both, and that's
where most of the overdraw comes from. So instead of setting that background here
on the list, what I could do is remove it from the list, then go to my style that was
created for me when I created the application, and you can specify a window background. And
here I'm just going to specify the color I was using in the ListView. So now if I run
the demo again. So you can see it's not deep red anymore. We have this light red on the
images, and the background of the app, it's real hard to read on the screen perhaps but
it's white. It's not blue anymore. And the cards themselves, the nine patches
of the cards are not green anymore. It's blue. So we lost one layer of overdraw.
But we can be even more (indiscernible) than that. So we have all these placeholder images
that we show before we load the real image. And what's going on is once we have done binding,
loading an image from the network and binding it on the image view, we're not removing the
placeholder. So the way to do this is -- I lost my placeholder. I don't remember how
to do it in that demo but you could just remove -- make sure that if you have placeholders
and you put an image on top of it, make sure to remove the placeholder.
>>Chet Haase: We're going to leave that as an exercise to the reader.
>>Romain Guy: It's an exercise to the reader. [ Laughter ]
>>> (Speaking off microphone.) >>Romain Guy: Yes. Don't get ahead.
Also, they can't hear you. And the last trick you can use and a lot of
applications can benefit from that, so this is the nine patch I'm using for the cards.
So this is this little -- you know, there was a little drop shadow and there was this
little white area under the image. So this is a perfectly regular nine patch. There's
nothing weird about it but as you can see, the entire inside of the nine patch -- and
let me show you the inside. Show content. So the blue area here is where my images will
go. So that area is a single opaque color. It's
white. But because we're going to hide it with an
image, there's no reason for that to be drawn. So instead what you can use is you can modify
your nine patch and it's real hard to read on screen.
Here you can see under my mask cursor, there's a single transparent pixel. So just cut a
hole in the nine patch and this is the area that we stretch. So here again the blue area
on the right, you can see it's transparent. So we won't be drawing anything behind the
images anymore. And the rendering pipeline is smart enough to recognize there is a hole
in the nine patch the it will remove that part of the geometry and get rid of the overdraw.
>>Chet Haase: Trilinear filtering is something we --
>>Romain Guy: 4.2. >>Chet Haase: Right. 4.2. So this API actually
exists in a release that's out there right now.
It's also referred to as MIP mapping, sort of classic, traditional graphics technique
of getting better image filtering when your down scaling sort of extremely. If you are
going down to more than half the size, it doesn't really matter that much. It's not
going to change that, but the effect is if you know that bilinear filtering is basically
sampling the pixels on either side and top and bottom of the pixel you're going to fill
in and then blending that result, trilinear filtering is doing the same thing on the two
images that bracket the image size that you really need, and then filtering that result
as well and you get a much better scaling result.
For instance, I think we zoom in on these images. You can't really tell the difference
there. >>Romain Guy: Go back. So in this demo, the
two images were really large. They were bigger than the screen and the same image was reduced
twice on the left and on the right to less than half its size. On the left side it was
using the normal filtering and on the right side it's using trilinear filtering.
>>Chet Haase: So if we zoom up so you can see the effect a little more clearly, you
can see the one on the right is much smoother. We're not running into the same artifacts,
the jaggy artifacts that we have on the left because we're using more information about
all of the MIP levels. So we can see the -- was there the representation
to MIP map? Did I go past that one? Yeah; right. So (indiscernible) this, this is what
MIP mapping or trilinear filtering actually produces. Instead of just the one image that
we're going to filter from, we actually produced powers of two sizes below that. And then we
have all of those choices to then filter from when we do the MIP mapping.
And there it is again because it's so awesome. And here is the code. If you want to do it
in code at runtime, set has MIP map to true, or you can do it in your XML drawable resource
instead with the MIP map attribute. >>Romain Guy: We're going to talk about canvas
layers. So this is the (indiscernible) API on the canvas class.
So we have two types of layers. And if you don't write custom code, they don't really
matter to you directly but we see why they do matter even when you write an application
using only views. So the first type of layer is what we call
clip layers. So when you call the save layer method on the canvas, you can pass a flag
at the end the last parameter, and here we pass the value clip to layer save flag. Then
we draw stuff. So what this method does is very simple. So here we have our canvas. We
invoke save layer. We're going to create an upscreen bitmap, so we're basically creating
some -- you can see that as a second canvas. And then when we draw the contents, or here
we're drawing an image, that image will be constrained to that new canvas. So it's not
going to bleed out anywhere else, but we've created a second canvas. We have the second
image in memory. This is very useful for a number of effects,
especially opacity and we're going to talk about that.
If you have save layer and you pass zero as the flag at the end, you're creating an unclipped
layer. And unclipped layers are extremely expensive because when you invoke them, the
save layer method, we still have to create that extra buffer in memory. But every time
you draw something, what you're drawing is going to be drawn on every layer that the
parameter you're drawing with intersects. So if you create end layers and the parameters
you're drawing, your bitmap, intersects all of them, we're going to execute the command
end times. So that can be extremely expensive. So this is what we do in software. In hardware
we do it a little differently but it's also extremely expensive, and I think we give details
in one of our talks. I think maybe at Devoxx at the end of 2012, we explained exactly what
happens in hardware. But the point is if you're using save layer, be very careful because
either in software or in hardware the results can be pretty bad for your performance.
And if you remember, on Android we used to have those fading edges in lists and all scroll
views all over the place, and that's how the filling edges are implemented. They use the
save layer commands. So the top and bottom edges are one save layer each, and they were
really expensive to run and also they didn't fit the new Ice Cream Sandwich themes so we
got rid of them. >>Chet Haase: Let's talk about alpha. So alpha
is a cool property on view. You can make views translucent with alpha. It's very handy for
fading views in and out and it's really, really easy with alpha to give yourself performance
problems that you really don't need to be suffering from.
So the general tip is use it, but be aware of what you're doing and maybe there's a better
way to actually get the effect that you're looking for.
So there's lots of different ways to tie yourself in knots with alpha. You can set the alpha
on a view, in a couple of different ways. You can create an animation, object animator
or view property animator that evaluate alpha over time or you can use one of the old style
alpha animations. These all end up doing the same thing under
the hood, which is essentially creating an alpha layer, just like Romain already talked
about. So we're going to create these intermediate buffers, we're going to draw stuff there,
we're going to copy it back and composite it. A fair amount of work to go through, which
is fine if that's what you need. But in a lot of situations, that's actually not what
you need. You just didn't know there were alternatives, so maybe it would be good to
find out what the alternatives are. It's also good to understand why you actually
care about this. You might think, well, I've got this view and it's drawing several things.
Why don't you just draw each one of these things with alpha directly into the screen
as opposed to creating this separate buffer over here, drawing everything and then compositing
it back. So there's a handy animation here to see what kind of effect you're going to
get if you're just going to draw things with alpha directly to the screen, if we wanted
to draw all of these images and then show a translucent view of the images, if we drew
the images separately with alpha, this is what you get, which in graphics is called
a mess. And what you actually wanted was something
more like this; right? We just wanted to fade everything when it was drawn together, not
fade the individual items so that they all mushed together into that mess.
So what are some of the alternatives? So here's one. This is a simple one. We ran into this
one in notification panel, which won't be named.
>>Romain Guy: It was Dr. Dan's fault. >>Chet Haase: It was Dr. Dan's fault, actually.
So the engineer who was playing with this was trying to achieve a particular effect,
a specific color. It was basically a 70% translucent white value; right? Gave this nice gray, which
is wonderful, except what ended up happening was alpha was set on the text view, and the
text view occupied most of the notification panel item, and there were several items on
the screen and you got a significant performance problem because of that. We were basically
drawing most of the screen into these separate off-screen buffers and compositing back all
so that we could have gray text on the screen. The correct solution for that particular egregious
performance problem was to use gray text. [ Laughter ]
>>Chet Haase: A slightly more direct or alternative way is if you really want alpha to achieve
the exact color you're going for, if you're animating the translucency of that text, you
can set alpha directly on the text color itself and we're going to draw that much more optimally
than using the separate buffer approach. Image view, similarly, you can set the image
alpha. This was new API in ICS. >>Romain Guy: Jelly Bean.
>>Chet Haase: Jelly Bean? >>Romain Guy: Yes.
>>Chet Haase: Okay. 4.1. You can set the image alpha and then when we draw the image of that
image view, we will use that alpha. Again, we're drawing directly into the destination
buffer as opposed to creating this set-aside buffer that we then composite from. Much faster
if that's the effect that you're looking for. If you have a custom view and you know that
you're going to draw all these different operations and you're happy to draw them translucently,
then you can actually change the alpha on the paint and draw directly into the destination
instead of setting the alpha directly on the view itself.
Again, you know what you're view is doing, so do the right thing for your situation.
Don't depend on, you know, framework capabilities that are a bit more generic but from which
you might suffer because we can't know whether you're going to run into some of the artifacts
that we talked about. Or you can set a layer. This is kind of a
good thing to do in general, especially for animations. If it's a complex view and you
want to fade the view, just set a hardware layer on the object during the animation,
and then when you do an alpha fade on that, if you set the alpha property or animate the
alpha property, then we can take care of that very fast. We create a texture map and we
just fade the alpha on that texture directly. And another related thing to mention is this
new method that was introduced probably in API level 16, which is has overlapping rendering.
We like method names that are really long, preferably if they actually go past the character
wrap at the end of the screen. This one didn't quite make it. This one we use under the hood
to figure out if we can draw optimally in this view. If there is no overlapping rendering
in a view, we know we can draw each rendering operation separately with alpha, and we'll
take care of that very optimally, as opposed to you know we don't know whether these rendering
operation overlap or not so we'll just draw them all into the buffer and then composite
them back. So if you have a custom view, you can consider overriding this method and telling
us that you do not have overlapping rendering, and we're going to be able to handle alpha
operations faster. >>Romain Guy: We have only five minutes left
so we're going to start talking a little faster. Now let's talk about canvas. Here is an issue
I see in a lot of applications and I see a lot of confused people online asking about
this behavior. So imagine you have a canvas and that canvas is 640 pixels wide, 400 pixels
high and you call canvas that get width and canvas that get height. So I see people think
it's going to give them the size of the window, I see people who think it's going to give
them the size of the view, and the answer is both. So if we have this canvas in a window,
let's say you're on a Nexus 4 or Nexus 7 and the window is about 1280 pixels by 800 and
inside you have a view that's 600 by 300, and you call get width and get height, in
the window you're going to get the size of the view as the answer. So you call canvas
that get width and canvas that get height and you're getting the size of the view.
In software, you're going to get the size of the window.
The point here is do not use the dimensions of the canvas as the dimensions of your view.
You can just use view the get width and view the get height. There's no reason why you
should use the width and height of the canvas pretty much ever and you can think about it
in a different way. If you were to rely on one of those two values to be exact, what
would happen if instead of giving you the canvas of the window we would give you the
canvas of a bitmap, because we're taking a screen shot or we're taking a drawing cache.
Then you would get a completely arbitrary value. So be very careful if you're using
those methods your code. Now let's talk a little bit about clipping.
I mentioned that we're introducing super (indiscernible) clipping, so if you're doing rotations and
clipping, be very careful about the order of operations.
So here, for instance, we're simply going to draw a rotated bitmap and we want to clip
that bitmap just in case. So we first clip with a rectangle, then we rotate by minus
30 degrees, then draw the bitmap. So this is what's going to happen. You have your canvas.
We apply the clip rect, then we rotate the canvas, we draw our bitmap and this is the
result. As you can see the clip rect remained a rectangle
throughout the drawing operations. So we can do it very efficiently.
Now, if you do it the other way around, if you rotate first, then use your clip rect
and then you draw, here's what's going to happen. We're going to rotate the canvas,
we're going to apply the clip rect, we're going to draw, and the result is the same
because we're not intersecting -- we're not going outside of the bounds of the clip rect.
But now that we are back nonrotated, you can see that our rectangle is not a rectangle
anymore. It's actually some sort of a diamond. So to apply this affect, what we have to do
under the hood is we have to use something on the GPU called the stencil buffer, it's
some sort of a mask, we have to clear that mask, we have to render the rotated rectangle
that (indiscernible) the clip inside it, and then when we draw the bitmap, for each pixel
we have to test the pixel of the bitmap against the content of the stencil buffer.
GPUs are very good at doing that kind of stuff, so if you do it once per frame, it's okay,
but if you have a lot of very expensive drawing commands or if you change the clip rect or
if you keep rotating in different ways, it's going to be extremely expensive.
And again, remember that you can end up with a nonrectanglar clip that is just setting
a rotation on the view if you do a 3D flip or just a rotation on the Z axis.
Let's imagine you have a view that's 640 pixels wide by 400 pixels high, and we invalidate
a small region of the view. So we co-invalidate, we specify this little rectangle here inside,
and we ask for the clip bounds on the canvas in the draw method. The clip bounds are going
to be different if you are hardware accelerated or if you are not. So with hardware acceleration,
the clip bounds will always be the size of the view because we record a (indiscernible).
So the clip bounds don't matter at record time. We just want to see all the drawing
comments that you want to execute. When we're going to replay the display list,
then we're going to apply the dirty bounds. In software, however, we're going to give
you the exact clip bounds that you set using invalidate.
>>Chet Haase: We are not going to talk about reordering barriers. We will be posting these
slides online. That is, in fact, the talk. There's some links
to other information on related topics. Parleys.com has a lot of recorded talks that you can watch.
And there were performance case studies you can check out online. Our blogs post all of
this kind of information, or watch us on G+ where we also talk about this stuff.
Thank you.