Google I/O 2011: Bringing C and C++ Games to Android


Uploaded by GoogleDevelopers on 12.05.2011

Transcript:
Galpin: Hello, and welcome
to Bringing C and C++ Games to Android.
I'm Dan Galpin. I'm a developer advocate
on the Android team working with game developers.
So about this talk--
um, we're gonna expect
that you guys are C and C++ developers.
If you don't know C and C++, you probably shouldn't be here.
Hopefully, you've gotten some Android background,
because we're not gonna cover all the basics here.
This is considered a higher-level talk.
Um, game development-- the development experience
is awesome.
We are not gonna teach you
how to become a game developer here, either.
And, however, we are going to expect
that you may or may not have ever touched the NDK
or, in that case, JNI.
So our agenda for today is going to be programming Android
and C and C++,
using the NDK, bringing your game to Android,
troubleshooting tips and tricks, and tongue twisters
and best practices.
So to start off,
um, Programming Android in C and C++.
Um, and I'm gonna be joined, by the way,
by Ian Ni-Lewis, who's also a developer advocate here
on the Android team doing game development--
uh, helping game-- helping out game developers.
So to start off with,
the NDK provides support
for C and C++ development.
NDK is the Native Development Kit,
and the idea is people write code
in other languages than Java.
[inhales sharply] I know. It's kind of shocking.
And sometimes-- sometimes this code works,
and they want to reuse it,
so we--so we've made the NDK partially because
there's tons and tons of great code out there,
whether it be open-source code,
whether it be code that you've developed,
that you're gonna want to reuse.
Also, as it turns out,
Java is not the best language for everything.
There are times
when you're doing things that are really math-sensitive.
There are times in which you are doing things
that are seriously hard-core
where you're gonna want to accelerate things,
and you're gonna want to take advantage of things like VFP.
You're gonna want to take advantage of things like NEON,
okay, for--for supercool stuff.
And of course, I have heard that there are other platforms
that people write games for other than Android.
And--I know. It's shocking. [laughter]
Galpin: Um, but you might want to port those entire games over,
and you want to--a method of doing that
that allows you to get high performance
and consistent quality.
So let--let me let Ian take it away from here,
and he's gonna talk about what's in the box.
Ni-Lewis: I haven't been this embarrassed
since I, uh, started teaching class at DigiPen
and thought that it started a week after it actually did.
I found out that all my students wanted to kill me.
Uh, I apologize for being late, but yes,
um, as Dan was saying, the, uh, the NDK
is a way to program C and C++ in Android.
How many of you have used the NDK?
Okay, awesome.
The rest of you, you know, bless your lucky stars.
Um... Galpin: [laughs]
Ni-Lewis: The NDK was actually kind of difficult to use
in the past.
It's gotten a lot better this year,
and we're actually pretty excited
about the changes that we've made.
Here's what the NDK is.
It starts out with a toolchain,
and if you're familiar with, uh, Linux programming
or Mac programming, this sort of toolchain
is not gonna seem at all out of place.
It's basically GCC and--and Make and Build
and all these nice things that you're used to.
It also has a build system.
Uh, you're not actually required to use this
as a--as an NDKr5.
And, at first glance, it seems a little overengineered,
but it's based on the same platform build system
that we use for Android.
And it makes it really, really easy
to write modular code
and--and build up an application of multiple shared libraries
in C++.
And I'm told that,
if you try and do the whole toolchain from scratch yourself,
it's considerably more difficult.
So you might want to at least start with the made files
that we've got for you.
Um, and there's also a set of headers and libraries,
so libraries that interface with the underlying system.
Now this is a really important point.
In fact, it's actually the most important piece of the NDK,
and you might be asking yourself, "Why?"
In fact, if you're like me, you're probably saying,
"Well, wait a second, I heard Android's Linux,
"and Linux has always been C++,
so why do I even need this stuff," right?
Well, it turns out that,
although Android is based on Linux,
Android processes
have a very important difference,
um, and that's the--you know, we've always had the Dalvik VM,
and every Android process loads the Dalvik VM.
Application code is expected to run in the VM,
over the top of this set of frameworks,
um, that is the Android SDK, in Java.
Now what the NDK is trying to do
is let you get some code down here
under--underneath that layer,
uh, where only C and C++ code
have--have been written before.
And the problem with this
is that, for a long time, the Android operating system
has been on, uh, able to rely on this framework layer
for backwards and forward compatibility.
So the problem with just sticking some code down there,
which you know, you know, you can do--
you're not dumb,
you can--you can compile C code on Linux--
but if you do it without the NDK,
you're gonna have a real problem here,
because the set of system libraries that we talk to,
uh, that--that implement the Android system
in C and C++ have never been stabilized.
There's never been a reason for them to be stable.
We change them every time we revise the operating system.
We're continually making them better
and switching things around
based on the needs of the underlying OS.
So what we needed with the NDK more than anything else,
uh, was to create
a stable ABI,
and with that stable ABI,
then we could finally get the framework classes
out of the way and let your application code
live in both Java land or Dalvik land
and C and C++ land.
With first revision of the NDK,
we--we launched some basic libraries.
Basically, the things
that we thought were gonna be most interesting
to high-performance, uh, programmers--
you know, obviously, the basic stuff
like--like libcs, math libraries, uh, and OpenGL.
Now with Froyo, we also introduced a library
called libjnigraphics
which let you talk directly to the 2-D rendering layers,
uh, in a bitmap layer, as well as the 3-D OpenGL stuff,
and with Gingerbread,
we actually made a concerted effort
to make the NDK,
and--and the Android system in general,
more attractive to game programmers,
so we introduced some new input models.
Um, we used libEGL,
which is a--the standard way
of getting OpenGL contacts.
Um, we introduced OpenSL,
so finally, you didn't have to go back to Java
just to do sound.
And we introduced a library called libandroid,
which allows you to access some of the SDK framework
from C++.
Uh, most notably, it's now possible, in Gingerbread,
to write, uh, an activity
completely in C++,
and it's, uh, it's also possible,
in Gingerbread
to access the AssetManager from C++,
which, if you ever tried to access the AssetManager
from--from C in previous versions of the NDK,
it was incredibly difficult.
So we're excited to see those changes.
Well, now that we've talked a little about what the NDK is,
we should probably make it really clear what it's not.
This is not the end of Dalvik.
Dalvik is still a great way to program Android,
and it will always be the--the premier way
of programming Android.
You always have, uh, SDKs written in Dalvik first.
But...
it does give you a way to--to write C--
C and C++ code if you've got an, uh, an investment in that.
It's not always gonna be higher performance.
Dalvik is a fast virtual machine,
and not everything is gonna benefit from NEON instructions
or from hand-tuned assembly code.
So it's not always gonna be the right choice for every game.
Is it the right choice for your game?
That's gonna depend on a lot of things.
Keep this in mind--
Dalvik is the premier development environment
for Android.
It's a productive development environment.
It has garbage collection and lots of nice libraries.
But on the C and C++ side,
uh, lots of programmers know C and C++
that don't know a Dalvik language.
Uh, it can be faster,
and it can leverage existing libraries
like open-source libraries that have been written in C and C++
and are available for free.
So it may or may not be the right choice for you.
Fortunately, you don't have to make
an all-or-nothing choice.
And we'll talk a little bit about how Java and C and C++
can coexist.
By the way, I do apologize for my voice.
I was way too enthusiastic yesterday.
Whoo! Google I/O!
Um...
Let's talk about how to use the NDK.
All right, first off, you're gonna have to install it.
This is a really simple step unless you're on Windows,
in which case, you're kind of screwed.
[laughs] No, I--so on any system,
you're--you're gonna want to make sure
you've got Java and Ant up to date.
I don't know, on my MacBook Pro,
I had to do, like, a-a MacPort to get for these things.
Um, but on Windows,
you're almost certainly gonna want to download Cygwin.
Um, that's not-- that's not a restriction
we put on regular Android programming,
but, you know, the build scripts for the--for the NDK
just have never been ported for Bash.
Um, it is actually possible
to build without Cygwin.
Um, I'll mention a couple of ways
in a bit on how to do that,
but it's highly recommended.
So then there's a sort of a nice flow that we go through.
You set up a project.
Um, you write some makefiles.
You're gonna write some interface classes
and glue them up with some JNI magic.
Um, and then you're gonna go
into sort of your build, deploy, and debug cycle
that--that characterizes any software project.
Let's drill down on these a little bit.
All right, to create the project,
there's actually several ways you can do this.
The one on the slide is the out-of-the-box way.
You run Android create project,
and that gets you, as I'm sure you're aware,
an Android project.
There's also a couple of third-party projects
that I want to mention just because they're so cool.
There's a product called vs-android
on Google code that, uh, will create an Android project
in MSBuild, so a lot of game developers
use Visual Studio.
If you like that sort of thing, um, that's a possibility.
There's also a project called Sequoyah
that's part of the Eclipse project.
If you load that as an Eclipse extension,
it will actually help you create, uh, an NDK project
using the confer to C++ project wizard
in, uh, in Eclipse.
Can you tell I'm a V-- a Visual Studio guy,
'cause I just said "wizard"?
Do they even call it that in Eclipse? I don't even know.
Um, but it's gonna create the-- either way,
you're gonna--you're gonna get a few different files created.
There's--there's an XML file that Ant uses.
There's a couple of properties files
that you usually don't need to care about.
Um, and you are also actually gonna have to create
a couple of files yourself, unless you use
that Sequoyah, uh, project that I talked about earlier.
Um, it's two makefiles.
These are actually fairly standard makefiles
in--in the sense that they used new make syntax,
but they fit into that fancy build system
that I mentioned earlier.
So they don't require nearly as much work as a normal makefile.
The first makefile is called Android.mk.
It's mandatory.
The build system expects to find this,
and if it doesn't, it will complain.
And it just contains the names
of the files that you're compiling
and any flag overrides that you want to do,
so, you know, you want to set C flags for your project,
fine, you can do it here.
There's also a somewhat optional makefile called application.mk.
Um, the idea is you might have several modules
in your application, you know, several different libraries.
Each one gets its own Android.mk,
and then the application.mk pulls it all together.
But the application makefile
also does some-- some other important things.
It's a--it's a place to set your global debug flag settings,
um, and more--more importantly than anything else,
it's a place to set up your STL.
And one of the great new features
that we've finally been able to add in NDKr5
is real STL support,
including exceptions,
uh, that you get with just this one line of code
in your makefile.
Now you need to write an interface,
because remember that, unless you happen to be targeting
the 4% of devices that run Gingerbread right now,
you're probably gonna want to start out in Java
and then call in to C++.
So you write a Java interface
that's gonna wrap your C++ in something that looks like Java,
and to do that, you use the native keyword.
This is actually really easy.
You just decide which functions are gonna be, uh,
implemented in C++,
and then you use the native keyword
to do--um, to define those functions
inside your Java file.
After that, you just need to generate stubs
in your C and C++ code
which you can do with a variety of tools.
Eclipse has a tool for this.
There's also a command line tool in the JDK called javah,
and it just generates a file that looks a little like this.
Remember that--oh, darn, these little slides.
Uh, remember we had two public native void--
or public data functions in our sample code.
If we passed the class with these functions in them
to javah, it's gonna generate code that looks like this.
Basically, you have a couple of JNI export functions
and... the, um,
the names of the functions are a concatenation
of the class path and the class name
and the function name, all separated by underscores.
And the parameters to those functions
are transformed into JNI types
that--that are in C--
you know, jint, for instance.
Uh, and then there's a couple of extra parameters.
One's a pointer to a JNI environment,
which is sort of the object
that you use to talk to the Java system from C++,
and there's also gonna be a handle to your object.
Or, if the function is static in Java,
there's gonna be a handle to your class.
We'll show you how to use that in just a second.
Now you might be thinking to yourself,
that looks really easy, and it is.
Just by giving the function the proper name,
the Java compiler and the--and the Runtime
know exactly how to find it in your c--C and C++ code.
And they find it with a name lookup
in--on a vtable that's sort of automatically generated for you.
But... there are times
that you might want to do this manually.
For instance, let's say that you've got some functions
that you know you want to implement in C,
but you don't know exactly
what the implementation is until Runtime.
A good example of this would be
if you've got some really heavy-duty math library,
and you've got an implementation that uses NEON
and then another implementation that doesn't.
Well, not all CPUs have NEON,
so you might want to delay the binding of that function
until you know what your instruction set is going to be.
If you wanted to do something like that,
it's also actually fairly easy.
You just make your own function table.
Um, it's just an array of structures
that has the name of the function.
It has this cryptic little string.
That's just telling you what the function signature is.
In this case, uh, two integers, uh, or the function
takes two integers and returns void.
If you don't want to memorize
this particular bit of gobbledygook,
you can use a, uh, tool called javap
to generate that, or, once again,
Eclipse has a menu item that does this for you.
Um...
and then finally, the last thing in that structure
is just the point of the function.
Once you've created that table,
when you're ready to register up your--your interface,
you just call a function called register natives,
and you pass in a pointer of that table that you created.
And that way, you could actually have
multiple different implementations
of the same functions inside the same library
being hooked up at Runtime to your Java code.
Of course, it's cool that you can call C++ code from Java.
It's also kind of nice to be able to go the other way.
That's not super--I'm not gonna tell you that's easy,
because it's not one line of code,
but it is pretty straightforward.
And this is where we use that class or, uh, object handle
that we talked about earlier.
Um... using object handle,
you can get a handle to its class.
Using class handle,
you can get a handle to a method,
and once you've got a handle to a method,
you can use a function on that JNI environment,
uh, one of the call method
family of functions
that differ by return type.
And--and that allows you to actually make a call
from C and C++ back into Java.
Excuse me. Mm.
So that's all the coding.
Now we get to build, and that's pretty normal.
If you've done any Java or--excuse me--
any, uh, Android programming before,
you probably know the second two steps.
It's the first step that's a little different.
You run ndk-build, which is a shell script
that just launches all those makefiles,
stitches everything together,
copies everything to the right directories,
and makes sure
that all of the C and C++ code that you've compiled
is where the packager expects to find it.
Then run Ant to package it, run Ant deploy or--
I'm sorry--Ant install to deploy it,
and we're ready to debug.
Now this is actually the most important, cool thing
that we've done in the past year with Androids.
We actually made it possible to run a real instance of GDB
and do multi-threaded debugging
on Android devices.
The NDK GDB script is a shell script
that does a whole lot of heavy lifting.
It starts by sanity checking your setup and making sure
that you can actually debug the code that you think you can,
um, and then it's gonna start your application
in an instance of GDB server.
The GDB server is this tiny little thing
that just communicates
with a real GDB across, in this case, a named pipe.
Why is it a named pipe instead of a socket?
So we don't have to ask for internet privileges to debug.
So GDB connects to this, uh, named pipe on your host.
It thinks there's a socket on the host,
because it's been, um, aliased.
And once GDB is running, you can attach to that
with any tool that supports GDB--
I know of at least three.
Eclipse supports GDB.
Um, Visual Studio supports GDB
if you buy an add-on called WinGDB.
Xcode, which I've never used, but I hear it's cool,
also, uh, it supports GDB.
So you can use these IDEs
or you can use the, uh, command line interface
to GDB, as well.
Now I'm just about to turn it back over to Dan,
and we're gonna talk about some tips and tricks,
and it's really actually the most interesting part
of the talk, but before we do,
I want to leave you with one tip of my own,
just from experience debugging.
Make sure that, when you start debugging the NDK,
you have a Gingerbread device.
No matter what you're targeting,
the debug support pre-Gingerbread
was not phenomenal.
Gingerbread changes all that, and it allows you
to actually debug multithreaded applications
and have a hope of actually figuring out
what's wrong with your code.
So remember to do that,
and then I'm gonna turn this over to Dan
for some ideas on how you can bring your games to Android.
Galpin: Thanks, Ian. [applause]
Galpin: Um, so this part of the talk
is subtitled "Saving Ian's Voice."
And, uh, but, uh, it's really good--
it's really great to have him here to talk about,
uh, a lot of the stuff that's going on in--in the NDK.
So this part of the talk, we're gonna be talking about
actually bringing your game to Android
and what that actually involves.
So let's first talk about what devices
have been accessing Android market.
This is basically the freshest data.
It's a 2-week period. We published it just this week.
And as you can see, it's really exciting.
We've had great uptake for Android 2.2.
65.9% of devices checking into market
are now running Froyo,
and 94.7%
are running Android 2.1 or higher.
So this is fantastic,
because it means that you can take advantage
of many of the latest features of Android
without having to eliminate much of the market at all.
But NativeActivity is just starting.
Right now, we're at 4.3%,
so for non-tablet-specific titles,
things that are not going to require Honeycomb,
we want to be able to take advantage of NativeActivity
when it's useful, and I'll give you an example.
Um, how many people here have heard about the Xperia PLAY?
Okay. Cool device.
Um, it has a touch pad on it, and that touch pad
is only accessible right now through NativeActivity.
So a lot of people are in a position
where they want to write an application that's compatible
that can take advantage of those touch pads,
but they still want to hit the rest of the market.
So let's talk about how to do that.
So we can do something
called platform-specific resources,
and we can define boolean values there.
And this is how that works.
We create resources in a values--
in a values-v9 directory.
Inside of there, we create two values,
one called, in this case, "atleastGingerbread"
and the other called "notGingerbread."
From there, we can just go into the manifest.
Now we enable one activity,
if atleastGingerbread is true,
and one activity if notGingerbread is true.
Voil�, We have an application
that now can run NativeActivity on Gingerbread
and will run whatever standard activity on everything else.
Now there are some things you'll want to do in your native code
to take advantage of this.
Oh, let me actually show you--
there's what I was gonna show you there.
Um, there's some things you're gonna want to do
to take advantage of this.
One is you're gonna want to create stub interfaces
to your game engine.
Obviously, you do not want to HardCode the dependencies
to NativeActivity in your game engine
and include the whole thing twice.
It's gonna make the download a lot larger.
It also means maintenance can potentially be larger.
So what we're going to do
is we're going to create some stub libraries.
We'll talk about that really fancy build system
we had before.
This is what the Android.make looks like now.
As you see, it just contains a pointer
to three other libraries we want to build.
There's the application.makefile.
It actually lists each one of those libraries.
Inside of the individual directories,
We also contain and have other makefiles.
In the Android.make, we have this one,
and in the Native Android.make, we have this one.
This one, of course, only refers to libraries that are in Froyo,
and this one refers to libraries that are actually available
in Gingerbread and beyond.
And what's important
is both of them use the same local shared library,
and that's where the primary amount of our code
will actually sit.
So we do have one problem,
and that is Android does not support
dynamically loaded libraries that are automatically linked.
We don't do dependency checking.
So we have to have a way of loading that other library
before NativeActivity kicks off.
The good news
is that we actually provide a method for doing that.
We can extend NativeActivity.
So all we're doing in our-- in NativeActivity
is creating a static member
that loads our primary dynamic library.
Now in our standard JNI-based version,
we simply load them in order.
And that's it.
With this, you can actually have two--two different activities
that share the same native code path
but target different devices.
Now let's talk about what you need to do
in order to write a game.
Well, you need to deal with the Android Lifecycle.
You're gonna deal with-- you're gonna have to have--
deal with Input, Graphics, Sound, and, of course, Assets.
And the reason I want to bring up the activity lifecycle
is it is so important.
The framework does a lot to help you with the activity lifecycle,
and I'm sure many of you have seen a diagram like this.
Someone comes in front of your activity.
It's visible. It get shut down. It comes to the foreground.
It gets restarted.
You know, all this stuff happens,
and there's always one line that has been missing
from this diagram, and that's this one.
Okay, and that happens when something in your process
actually changes the configuration.
So you're at--so look at all the different ways
your application can go through this chart.
But here's what's interesting--
There's only one in which your process actually gets killed.
Most of the time,
your native process is never going to get killed.
Your library is not going to get reloaded,
and this means that you actually have to be a little bit smart,
because Android tries to keep your process around
for as long as possible, so even if you finish your activity,
your native process is going to continue running.
It's gonna continue to get a bunch of CPU cycles.
What does this mean?
It means if you actually continue that running,
it's gonna be bad.
Now visible activities are given high priority,
so it's not the end of the world,
but it is something that is very important to note.
So what does this mean?
Be careful with static initializers.
Your library will not be unloaded
as long as your process is active.
So this means that when your activity restarts,
you need to be able to manually reinitialize anything
that you might be relying on static initializers to do.
So in general, don't use them.
Uh, also, be careful with threads,
because threads are tied to your process.
They'll continue to run even if your activity has ended.
And what that means is that people will be using the device,
and they'll find that it's slow and terrible,
and they'll look in the battery life meter,
and they'll realize that your application is the one
that's sucking up all the battery life
and then they'll go on Android Market
and they'll give you a one-star review and say that you suck.
I mean, they might do that anyways,
but at least let's not give them a reason to do it.
Um, other things to note
is that Gameplay should stop during onPause,
but your application might still be visible,
and I'll tell you, the most standard way that can happen
is if you're using In-app Billing,
you'll bring up--it'll--it'll actually bring up an intent
that'll cause an activity to show up not full screen.
Well, your application is technically paused,
but still visible,
so it might make sense to do some more stuff in onStop
than you were doing in onPause.
Still save all your state,
but maybe you don't want to trash
all of your GL variables in onPause in that case.
Finally, in onDestroy,
you want to make sure all your native code is cleaned up.
And a lot of people are like,
"Well, onDestroy never gets called, right?"
I mean--but it does.
There are definite ways in which it can get called,
especially if you're not handling orientation changes
or changes in--in, uh, in devices,
like, when a keyboard is attached or something like that,
and it tries to restart your activity.
So you've got two choices-- you can either handle
all those--all those configuration changes,
or you can be really, really vigilant about this--
one of the two.
So let's talk a bit-- just go quickly
about all the things you've got to handle.
You've got to handle input, okay?
Key handling is important.
A lot of devices have capacitive buttons,
and what this means is that we actually madea change
in Android 2 that said, instead of just relying on onKeyDown,
we actually went, and you have to get a cycle
of onKeyDown and onKeyUp.
This prevents you
from accidentally swiping into a button
and clicking it when you don't mean to a lot of the time.
It doesn't mean that it doesn't happen, but it helps.
Another thing to do is, if you're gonna do things
like handle key repeats, use the actual functions
that are built into the framework
so you--so you get the same key repeat counts
and the same behavior you would expect
from an Android application.
Touch and Multitouch handling is similar.
We have things like GestureDetector.
Use those so that your application behaves
like a non-native application whenever possible,
when it makes sense.
And then finally and probably most importantly--
dealing with Multitouch.
Now most devices now support Multitouch,
and most devices can actually track distinct points,
but you can query to find out whether or not that's true
by checking this variable--
"FEATURE_TOUCHSCREEN_ MULTITOUCH_ DISTINCT."
You can also use it as a filter in Android Market.
If you want to make sure your device
supports true 2-finger multitouch, you can.
We also add a feature in Gingerbread called Jazz Hands,
which allows you to see
if you application supports 5-finger multitouch.
So these are all--all things that you can do
to help configure your why to make sense for your user.
Now even if you don't support distinct multitouch,
you can do things, like have virtual D-pads.
You just want to put them in opposite corners of the screen
so that they will never intersect.
It is cool to handle things like trackballs.
For the devices that have them, I love it when it's an option.
And, of course, um, everyone's gonna want to use things
like sensors, you know.
When people are gonna want to take advantage
of the Xceleron or the Gyro. You just want to make sure
that it's disabled when you're not using it.
You're also gonna want to make sure to "Androidify" your game,
and what I mean is don't break the hard buttons.
So, you know, "Back" should do something.
There's nothing worse than when you're on the title screen
of a game, you hit the "Back" button and nothing happens,
and just, you know, the Pease Electric goes off
to let you know that you're hitting the button,
but the game is not responding or doing something.
Make sure it does something.
Uh, you know, Menu could-- should probably do something,
and if someone accidentally swipes the Home key,
there's nothing worse than when the game goes away.
So the result--and volume buttons are very similar.
We don't want to eat those messages.
You--you actually have to return them to the framework,
and you want to set the volume control stream.
And then again,
don't let the hard buttons bake--break your game.
You want to save your state. It's very, very important.
You know, these buttons do things that are relevant.
OpenGL--Let's talk very, very quickly about this.
The coolest thing about OpenGL and Android
is that you can actually create your surface inside of Dalvik
and take advantage of it directly from within the NDK,
and that's awesome, because, actually,
it's a lot of work
to create OpenGL stuff--
OpenGL context compatibly across Android.
And GLSurfaceView--if you ever look at the source code,
and, of course, what's awesome is that you can,
um, actually contains a whole bunch of stuff
that you don't want to have to rewrite
that works around a lot of issues
that can possibly happen in the GL driver.
So I definitely recommend using it.
Now it's not the most efficient way to multitask,
because it's going to automatically release
your context during onPause
if you call its onPause state during the onPause.
That's when it does the work for you.
You might want to call it during onStop
if you know you're gonna be using a In-app Billing--
one thing to think about.
But also with, um, Honeycomb,
you can actually say,
"I want it to preserve my state," and it will.
So we added that as a feature in Android 3.0.
And when your context is lost,
you're gonna have to do a lot of work.
You're gonna have to reload your resources--
things like textures, buffer optics, and shaders,
and your names don't even persist.
So these are all things that are important to remember
when you're using OpenGL.
The marketing way of describing this
is that Android encourages innovation in hardware.
[laughter] Galpin: [laughs]
What this means is that you have a problem as a developer.
You've got to deal with potentially different kinds
of proprietary texture compression formats,
and if you want the best quality, you will,
because your-- your other solutions
are not all that great.
You can use uncompressed textures,
but what that means
is that you are going to get a lower frame rate,
because you're gonna be using more memory bandwidth.
You can use ETC1,
but ETC1 doesn't compress all data very well.
It's mostly good for data
that does not have sharp color transitions
and it does not support Alpha.
You can use Multi-APK,
which, as we announced earlier, will be available soon,
or you can put textures on an asset server,
detect support at Runtime,
and downtoad-- download to each device.
In the previous session before this one,
we talked about how to secure-- securely deliver assets
from an asset server in order to avoid people leeching bandwidth.
So if you want to know about that,
there's some information on that.
So some--some small tips about drawing for performance.
Draw order is important, especially if you're not on a--
on a tile-based renderer,
or a-a specific type
of tile-based renderer in this case.
So definitely draw front to back.
It really helps.
VBOs are critical for performance.
You want to avoid unnecessary state changes.
And finally, you need to use power of 2 sized textures,
because if you don't,
it can either be very slow,
or it's just going to take an enormous amount of memory,
because it's just going to scale your texture up
to the next power of 2 sized textures
automatically inside the driver.
Or it's just not gonna work.
Um, and then, of course, writing compatible shaders,
and this--this slide is actually contributed by my friend Kim
over at Sony Ericsson, because he--they've done a lot of work
on this, but, uh, it's very, very important
to query for the number of attributes, and the varying,
and the uniforms that are supported
when you're writing your shaders.
And just because they're there
doesn't mean they're actually going to perform well,
but if it's not there
and you try to write a shader that does that,
it's going to crash your application, and that's not fun.
Uh, and of course, you want to avoid things like conditions
and discard as much as possible,
'cause it's really going to slow down the performance
of your shaders.
Mobile shaders are just in the beginning on their lifecycle,
and they are going to do very, very cool things,
but right now, we have to be a little cautious
when we write them.
Quickly into sound--
Android includes a bunch of options for sound.
It can be a little confusing,
so let's go through them super quickly.
We have MediaPlayer,
which is really a full-track media solution.
We have SoundPool, which allows you to play sounds from memory,
and it's actually the only way
to play compressed audio from memory.
That is not yet supported in native OpenSL.
We also have, of course, AudioTrack,
which is something we introduced in--in...
Android 1.5... [inhales sharply] I think,
and, uh, it, uh, allows you
to play PCM audio only with minimal latency,
and it's really meant for applications
that are generating their own audio.
And finally, we have OpenSL, yes,
which does require Android 2.3 or higher,
but it combines the capabilities of MediaPlayer and AudioTrack,
and it allows you to do all of your audio from native code.
And it--and the interface is actually a little nicer
in terms of generating audio,
because it actually calls you back when it needs a buffer.
So it's a very, very straightforward interface
to use.
And I'm sure, going forward, it's gonna become
even more and more powerful and important.
So let's talk quickly about assets.
One of the things that people want to be able to do
is pull resources and assets out of their APK file.
And I went to the Android team, and I said, "All right,
"there are many ways we can do this.
"What is the way
that is most likely to still be compatible in the future?"
And they suggested this method.
So what we're going to do is get our APK file name,
uh, using this SourceDir.
Then we're going to use the AssetManager code.
And that AssetManager code allows us to get something
called an AssetFileDescriptor.
All an AssetFileDescriptor is
is a standard native file descriptor
that includes extra parameters for offsetting length.
We can read them, open the APK file from that offset,
and there we are.
We're reading our--our resources right out of native code
without any additional overhead,
which--and the overhead we're trying to avoid,
of course, is having to buffer things
between Dalvik and native code.
You can, of course, use a native Zip Utility
in a very similar way.
It's a little--it's a little less future-proof,
but it's probably going to work as well.
Um, and it's probably gonna be faster,
as long as you do things like cache the Zip directory.
And then finally, of course, you can use native AssetManager
on Gingerbread and beyond.
All right, my tongue twister slide--
"Troubleshooting, Tips, and Tricks:
Targeting tricky, trying tasks."
So Threading, Dalvik, and the NDK--
all context are per-thread.
You've got to be careful to check your threading.
If you take that same context
and reuse it across multiple threads,
bad things will happen, because the JNI context
tries to do smart stuff for you,
like clean up objects that you're not using anymore.
So you want to use one JNI context per thread,
or you want to take advantage of a game loop
that actually takes your messages,
threads them into another thread.
It's also useful if you're calling the framework,
'cause many of the framework classes don't like to be called
from anything other than the UI thread.
As far as performance, you can check for older hardware,
and there's multiple ways of doing that--
checking for ARM7, checking for support for OpenGL 2.
And then you can use Fallbacks.
You can use things like 16-bit textures.
Um, you can actually convert them on the fly
using BitmapFactory.
You can also use a smaller render target
using SurfaceHolder.setFixedSize.
Of course, there are other ways of doing that within OpenGL,
but this actually does it at the optimal place,
which is at the compositor.
Pixels are very, very expensive,
especially on tablets, so this really will help,
but if all else fails, we do have market filters,
such as Device Availability, which was announced today,
platform release, GL version, and instruction set.
And you can--you can filter on instruction set
by only building a library
for ARM5 or ARM7.
And here are just general tips.
Start small. Don't make me download a big file.
Use the backup/restore service,
can support multiple control schemes,
install to SD-- I'm not gonna go through these,
but you should all consider these, things like profiling.
You know, make the game render up and do better stuff,
if I--if it detects that it's running on a cool system.
Hooking into social networks is, of course, really important
for viral spreading of your application.
Building a live wallpaper is so cool
and so few applications do it.
But, like, like, isn't that awesome?
You get to own the user's home screen.
I mean, what other platform allows you to do that?
That's so awesome.
Um, analytics--definitely find out where your customers are.
Find out how they're using your title.
Like, this is awesome, awesome information.
Find out where--use--use it to do things like finding out
even where someone is dying in the middle of your game.
Like, you're like, "Hey,
"that level's way harder than I thought it should be.
What's going on?"
Or, "Only on this device people are having problems."
All that kind of stuff you can find out.
And, of course, In-app payments, we went through that
in the last presentation I just did.
We are now seeing amazing traction in app payments.
If you look, we just added
a new category in Market for top-grossing applications
and three out of the top five
are top-grossing only through In-app payments.
So some tools--I'm gonna throw these up here very quickly
so that when this slide-- when this is played back
and put on--on, uh, on the web, you'll be able to get these,
but this is some of the stuff we've talked about,
and, of course, we have some performance tools.
Each one of the chips at manufacturers
has now come out with performance tools,
and they can be very useful.
Some of them require a routed device.
Now we've got a very short period of time for Q&A.
So, uh, I went through those a little fast at the end,
so if you have a question, this is the time to stop me on it.
And, uh, thank you very much.
[applause]
Galpin: And I recommend everyone
stay around in the room after this,
because, um, there's gonna be something special
for all of you, so... [chuckles]
Free cake? That sounds good to me.
All right, next.
man: Hi, uh, I work on the open source port of Freeciv,
if I can just give a quick shout-out.
Galpin: Awesome. man: So it's--it's open source.
It's, I think, one of the largest NDK applications
you can--you can freely get the source to.
Um, there's a lot of terrible ideas in there,
but hopefully, there's one or two, uh, good ones.
Galpin: Cool. man: Uh, I had a quick question.
It was something I'd seen when--when loading a resource.
One--one of the goals of that project was to be able
to drop my app, even let it be killed and have it, like,
spring back as quickly as possible to where you left off.
Galpin: Right. man: And so I spent
a lot of time optimizing how I loaded my art assets.
Galpin: Mm-hmm.
man: I found something really counterintuitive.
It seems JPEGs load about three times faster than PNGs.
Is that, um... sensible? Galpin: Is that--
Is that based on the file size? 'Cause--
man: Uh, it--what I found was when benchmarking this
on a Samsung tab... Galpin: Mm-hmm.
man: was only about 1 millisecond per--per file
was taken up in file I/O. Galpin: Mm-hmm. Mm-hmm.
man: And that was effectively the same
whether PNG or JPEG.
Definitely the PNGs were larger. Galpin: Hmm.
man: The JPEGs were smaller.
It was the actual--apparently, it was the actual time
spent, like, converting from the compressed stream
into a bitmap.
Galpin: Hmm. Interesting. I-I haven't experienced that.
But, you know, I-I--
Ni-Lewis: PNGs aren't on the no-compress list, are they?
Galpin: No. Yeah, they're-- they're both--they're both
on the no-compress list, yeah. Ni-Lewis: Okay, I thought so.
Galpin: So that's-- the only thing
that would cause that normally is that--is that, you know,
in the zip file, uh, you know, things,resources
can be compressed, but both of those files
are automatically not compressed.
I'm--I'm obviously pretty surprised you're seeing that.
But I-I--you know, I don't actually know the answer.
That's a cool thing to note. man: Yeah, and--
Shift--even shifting that around, it didn't, like,
if--if I was on the SD card, the only thing
is if it was on the SD card, it would seem to take
about twice as long as if it was on flash memory.
Galpin: Yeah, see, that--that would--that would really make me
think that it actually was, on some level, file I/O.
man: Yeah. Galpin: It might be even, uh,
just--just because, uh, uh, that would be my first--
my first guess is it's entirely I/O bound, but, um--
man: Right. But, uh, three times, and I mean, you know,
these weren't very large files. Galpin: Yeah.
man: I mean, it was--it-- I don't know.
And it could also be--I know that it's the OMAP platform
and they have optimized JPEG decoder,
so maybe out floating some of that to a DSP.
Galpin: I don't think so, but that's, uh, that's it.
I think--I think it's-- Ni-Lewis: And it could be,
you know, loading it through and optimizing code paths.
There are two different code paths for it, but I-I--
Galpin: It could be loading it through--yeah.
man: Oh. Yeah, I did actually try compiling the libJPEG
and using, like, instead of loading it from, um, Java,
pushing it into a texture, and then pulling it up
through there. Galpin: Yeah.
man: I-I tried the libJPEG thing
and--and it even seemed like using just
a bog standard compiled libJPEG performed better than PNG,
but performed worse than--than pulling it up through Java.
Galpin: Oh, interesting. Cool. man: All right, well, thank you.
Galpin: No, that's interesting. That's good to know.
Ni-Lewis: It's a mystery. Galpin: It is.
man: Um, so question--
Uh, I have downloaded NDK using the JNI,
but I find out that debugging is extremely difficult.
So I saw that we can do the complete,
uh, Android application in C and C++ only,
without using Java.
Uh, so that makes the debugging much easier.
Can you show me, uh, some way in the future
we can support only C and C++ for entry application?
Ni-Lewis: Ging-- with Gingerbread,
you can support that,
but let me sort of lay things on the line for you.
The, um, the debugger for C and C++
is, uh, honestly, a little flaky.
Um, every single version gets so much better,
it's just amazing. man: Mm-hmm.
Ni-Lewis: Um, but it also depends a lot
on what you're using with it.
Um, so for instance, were-- were you using, like, stock GDB,
or were you using Eclipse or...
man: Uh, many use, uh, Eclipse, yes.
Ni-Lewis: Okay, so right now,
um, if you--if you use Eclipse,
and you either--there's-- there's instructions
on the internet for how to get it to work with--with any GDB,
or you can also, um, use NVIDIA's plug-in for Eclipse.
It sets up the debugger for you.
The--the--the only disadvantage to that
is your device has to be rooted, uh, right now.
Um, hopefully, they'll fix that.
Um, then actually, it's-- it's fairly easy to debug.
You can bounce back and forth between Java and, uh, JNI,
right--oh, and your-- and your C++.
Galpin: Yeah, there's--there's no--well, I was gonna say
the main answer is that there's really no reason
why--why you can't debug native code
that's being called from--from JNI.
It actually works--it works, as far as I can tell,
just as well as-- as fully native code.
Ni-Lewis: Yeah, and--and the bad news is
going to full-on C++ would not help anything,
because it's not the--it's not bouncing back and forth
between Java and C that's the problem.
It's just the debug layer, and C is--is a little flaky
with Eclipse.
man: Okay, thank you. Galpin: Mm-hmm?
man: Uh, I have a question regarding Google TV
in the NDK. Galpin: Mm-hmm.
man: And that, uh, after talking to the Google TV guys,
the NDK is not supported for, uh, Google TV.
Galpin: Mm-hmm. man: Is that ever going
to change and... Galpin: I think that's kind of--
I think that's kind of up to the Google TV team.
Um, as-- as a C and C++ developer,
I would love it to change, but, uh, but that's--
that's--that's up to them.
Ni-Lewis: Yeah, actually, you're not the first person
to answer--to ask this question.
Um, and I've worked closely with the--the Google TV team.
They--basically, they're not making any announcements.
Um, they haven't ruled it out one way or the other.
Um, but they--they have said on many occasions
that they understand
that it's a-a desire that a lot of people have.
man: Yeah, especially with regard
to, uh, some sort of, like,
Xbox live arcade style gameplay.
Galpin: Absolutely. No, it's totally--I'm there with you.
Ni-Lewis: We--we think that would be cool, too.
man: So--so with each version,
indicates you guys are really progressing...
[speaking indistinctly]
so good direction.
But the question is about accessing the MediaPlayer,
the video player back, actually, from native code, so...
Galpin: Yeah, so--so right now,
there's--there's no way to do it, uh, directly.
Um, I'd love to see that change,
but, uh, that's still the way things are,
so you still have to go back
and bounce back into, uh, into Dalvik to do that.
man: But that's on your road map, right?
Galpin: [laughs] man: [laughs]
Galpin: I would really like it to happen.
man: [chuckles]
man: Uh, first of all, let me say thank you
for multithreaded GDB and Gingerbread.
Uh, other--before, it was just a pain
having to call things on the main thread.
Galpin: Yeah. man: Um, I actually, um--
Galpin: Thank David-- thank David Turner
and the--and the--and the Dalvik
and the--and the Android, uh, Kernel team, actually.
They made that happen. They're both awesome.
man: I actually don't work on games, but I imported
an extremely large engineering app from C++,
um, and we needed, um, we were using the CrystaX before,
because we needed wide string. Galpin: Yeah.
man: And we needed, like, a full SDL,
but for Gingerbread, we've actually switched now
to using new--the SDL, but the problem is,
the, um, the one that's part of the NDK5
doesn't export any of the actually wstring functions.
Um, if you--it builds, but it doesn't link to wstring.
Uh, and I tried really, really hard,
and I looked around, and eventually, what I had to do
was I had to download the NDK source code
and change the build scripts to get wstring to export,
and it's like a meg and a half bigger,
and it's fine, um, but actually, my question is this...
Galpin: Yeah. man: Um, so I changed that.
Do I have maybe--I may need to ask a lawyer this,
but do I have, like, an affirmative GPL obligation?
'Cause I only changed the build scripts.
I didn't change GCC, but I'm using my own version, I guess,
now of the NDK, not the source-- not the one that was...
Galpin: Well, all of, I mean, I'm--I'm not an attorney.
man: Yeah. Galpin: But my understanding
is all of Android is licensed under a, uh, license
that doesn't force you to contribute your changes
to all of--all of-- man: Well, 'cause I didn't know
if GCC--yeah. Galpin: Yeah, if you're using
a GCC stuff, you know, it's interesting.
I don't actually know what the answer is--on that.
But I'm surprised the wide string stuff isn't working.
You should file that as a bug, because I just spoke to it.
man: Okay. Galpin: So that was one--
that was one of the desired changes.
Ni-Lewis: That--yeah, well, we-- I talked to David about that.
Galpin: Yeah. Ni-Lewis: Yeah, I think--
now he said that he actually couldn't get the command line,
that there were like two mutually exclusive options
where wide string didn't work. Galpin: Yeah.
man: Uh-huh, there are, and I actually had
to HardCode one of them. They're like boolean opposites.
Galpin: Yeah. Ni-Lewis: Okay.
man: And I went into one of the makefiles, and I figured,
yeah, and I basically had to manually override it.
Galpin: Right. Ni-Lewis: And this
is really good-- this is one of the things
that you should speak up, uh, on the forums or e-mail us about.
man: Okay. Galpin: Yeah.
Ni-Lewis: Because--because as you can see--
so really difficult build, uh, solution that ends up
making your code a mega and a half bigger.
Galpin: Yeah. man: Mm-hmm.
Ni-Lewis: And maybe isn't all that desirable by most people.
man: Yeah. Ni-Lewis: You know, you can
probably see why that choice was made.
man: Yeah, absolutely. Ni-Lewis: But if we've got more
feedback about that, more data points, you know,
that might be something we want to do--support in the future.
Galpin: Yeah, absolutely. man: Thanks.
man: Okay, quick one. Galpin: Mm-hmm.
man: Uh, any plans to access the camera API through the--
through native code? Galpin: [sighs] Yeah,
it would be--it's another-- it's another "nice to have."
I think, you know, I think ultimately, here's--
here's the issue.
I mean, the--the general-- the general thought
is if there isn't a strong performance reason,
for the most part, um, you know, the--the goal is to make it
something that you have to get through, uh, through Dalvik,
because that is the primary--
the primary platform for development for Android.
That being said, there's a lot of people
who want to do really cool stuff with the camera that,
if you had a native interface, you could do it a lot better.
So I-I think that's--I think that's an important one
to--to bring up, and--and again, keep requesting it.
Keep complaining about it. Hopefully, it will happen.
man: Thank you.
Galpin: All right, so that being said,
uh, I'd like to, uh, to mention,
uh, that, uh, how many of you guys here
are game developers or want to be game developers?
Okay, well...
Ni-Lewis: Everybody wants to be a game developer.
Galpin: Uh, well, yeah. Well, you know, it's--it's--it's--
it's a tough job.
Um, so we have something very special for you.
We've, uh, Sony Ericsson has come today,
and, uh, they are here
to make sure that every one of you
has the ability to develop really, really great games.
And so they want you to have a device
that has support for a modern GPU,
has the support for a lot of different controls.
They want to see what you can do with it.
So, without further ado, on your way out,
you will be getting a card,
and you will be able to exchange that card
for your own--very own Xperia PLAY.
[cheers and applause]
Galpin: So...
our--our only ask,
and it's that we want to make sure
you guys do really, really cool stuff with it.
If you do,
uh, and you're gonna want to go a couple things--
one is you're gonna want to go to sonyericsson.com/developer,
and--and they've written a lot of articles about
how to get to the special features of that device.
The second thing you're gonna want to do is,
if you do something cool, go to standout.sonyericsson.com,
and let them know that you've done something cool,
because they're always looking for people
who are doing cool stuff with the Xperia PLAY.
So, uh, with that, I hope you all enjoy it,
and I'm very excited
and a little jealous for you all.
And, uh, and there we have it.
Ni-Lewis: Thank you. [cheers and applause]
Galpin: Thank you.