Android SurfaceView optimisation

Well, I have been busy. My awesome map-based game Caravan has been spinning around a bit, but for a good reason.

In august, I went to XDADevCon in Miami. It was amazing. A big thanks to EVERYONE I met there. One of the things I picked up there was an Oppo Find 5 phone. At the time pretty much the best phone on the  market, bar none. The best screen, the best speakers, the best camera. And at developer prices :) Plus, the Oppo guys are really onet he right track; custom ROM support out of thebox, their own recovery; it truly is a developer’s company with an unbeatable product at an insanely low price. You can tell their marketing worked on me :)

But there’s the thing. This  was my first high res phone. It’s awfully strange having a phone in your hand with a 5 inch screen with the same resolution as a full HD tv. So of course I HAD to try my own programs and games on it. Which worked perfectly … except that my game, Ringi, was a bit choppy.

Now, you”d think that the Qualcom 800 and Adreno 320 would be enough to push the pixels of a not-too-intensive game, right? Wrong.

After some checking, it turns out that the game was fill-rate bound: I could not compute all the pixels necessary to run my game smoothly on 1080p. Now it worked perfectly on my old HTC Desire HD at 800×480. But now it was chugging a bit, and not smooth at all.

So, I spent a lot of time optimising it. I also worked on my great map-based game Caravan, ensuring it worked well at these high resolutions, but my main aim was to get Ringi working butter smoothly, so I could integrate all I would learn into Caravan. And people are playing Ringi, so I owed them a great experience on their new phones, too.

So, what did I do?

Basically, three things. Keep in mind, I didn’t immediately know what was causing the choppiness, so I attacked the most likely culprits first and went on from there.

First thing I went after was my input. I had a hunch that sleeping the input thread for a bit was a cause for the input not being so smooth (a common practice to prevent input flooding; especially older android versions have this problem). I’d come across a post by Robert Green at http://www.rbgrn.net/content/342-using-input-pipelines-your-android-game .This looked like something promising and didn”t involve sleeping/blocking my game so much. So, after implementing this and modifying it and my game heavily for multitouch and all Input events (this involved adding more field to the InputObject), my game did run better.But not enough: I was still getting some hickups.

My thinking then was that I was seeing too much garbage collection. Object creation and release does cause immediate GC on Android, and creates micro stuttering when it triggers. An d one of the leading causes of this object creation/cleanup? Strings. And I used a lot. I  created a lot of strings in my draw calls, too, converting integers to strings for my score, time, the floating point scores which occured every time you hit a flowerblock and then floated towards the score. A lot of these strings were created and cleaned up every draw call.

So I got rid of all string creation during runtime. Well, kinda: I loaded it all up at the front for a one-time creation hit. Seeing as I was keeping track of all numbers (score, time, points etc) as int’s anyway, I came up with the perfect solution. At the very start of the game, before any movement, I create a large string array of 10.000 strings which I fill with the Strings for the numbers zero to 9.999. BAM, one-time GC hit, but I never have to create a string anymore. Any time I need to draw a String, I just call the String Array using the tracking int as an index, and I get the corresponding String! No creation or destruction penalty, and all it took was a call to myStringArray[scoreInt] :)

This got rid of pretty much ALL my GC calls during gameplay and made the game a lot smoother. And I don’t have to screw around with StringBuilder either, which causes less GC but still wouldn”t eliminate it. The only cost? The String Array uses up a bit of memory … but if you can’t trade 100 to 200kb for NO garbage collection calls, you”re doing something wrong.

However, my game was running smoother, but not yet silky smooth. And this is when I realised I was maxing out my pixel fillrate. One of the reasons was that I had to re-draw my game’s background every drawcall. I had experimented with a workaround to this, by making my SurfaceView transparent and on top of a Layout which had my game background as it’s background. But that meant I HAD to set my SurfaceView’s ZOrder to the top. This worked great, and made my game run very well … bu had the drawback that I could not have ANY other drawing over my SurfaceView. A textbox on top? Not visible. Any widget, anything which got drawn on top would not be visible.

My big regret is that this approach does solve all my problems. Not only that, but I could draw everything pixel perfect at 1080p with no slowdown.

Now, were I to re-do Ringo from scratch, I would design around that problem by doing ALL drawing in the SurfaceView. But that would be a LOT of work to retro-fit Ringi in that fashion. So I did the only thing possible: I restricted the size of my SurfaceView and scaled it.

Basically, if the resolution gets to big, I now intercept the SurfaceView’s “onSurfaceChanged” call, check if the resolution is too high and if it is, I tell the SurfaceView that it is smaller than it actually is by intercepting and scaling it’s reported width and height. It is that simple; that one basic change didn’t even need any other code changes, except for scaling the position of where I draw my touch input (I draw a circle around each finger when you touch the area’s which allow you to rotate the rings).

And the scaling isn’t even that drastic. My ondraw call now renders at about 1500×800 instead of 1920×1080 (bigger screens will scale a bit more) and that is auto-scaled to the screen by the surfaceview itself due to it’s height and width being set to “match_parent”.

And now Ringi runs butter smooth, with NO stutter and no GC. If you look hard, you MIGHT MAYBE be able to tell it is upscaled … but in exchange for a game which runs flawlessly  on high rez screens, that’s a trade I had to make, because otherwise the game would not be fun anymore.

And the best thing? These three techniques (input pipeline for multitouch, up-front String creation and what I learned about SurfaceViews) are directly  transferable to any other project/game.

Posted in android, programming | Tagged , , , | Leave a comment

Long time no see … of rivers, cartography and tiling.

Well, I’ve been away a while and haven’t been able to do as much with “Caravan” as I’d have liked. But I have started the work on some features, which you can see below.

First off, I’m getting closer to the cartographic look I want. Colour makes a huge difference and compared to my fractal terrain generation (which needs a lot of work, too … it doesn’t generate terrain with enough unique-ness yet) the processing doesn’t take too long. But it is a shame it can’t be hardware accelerated, as the blendmodes I use (like the LightingColorFilter, PorterDuffXfermode and ColorMatrixColorFilter) aren’t yet supported.

Slightly more cartographic

Still, it surprised me how fast you can process 10×10=100 tiles that way if you do it correctly.

One trick has to do with looping and bitmap pixels. It’s always faster to operate on a sinbgle dimensional array of pixeldata, an int[x times y] (or byte[x times y] or whatever) than on a two dimensional array of int[x][y].

So much so that it’s actually worth it to loop over your int[x][y] to fill a (previously created and re-used) int(x-times-y], and operate on that. And then use that to either fill an int[x][y] or use the int[xy] to fill a bitmap.

But be carefull: always loop first over the x and then the y. The fastest way to do this is to have the y in the outer loop and the x in the inner loop:

for (y=0 …)
for (x=0 …)
do stuff

The reason for this? Look it up :)

Anyway, I have done a lot of optimisation on the backend, too, as well as completely re-written the tilehandler and caching.

One really interesting thing I came across was when I tried to speed up my rendering mechanism when scrolling.
Instead of re-drawing all the tiles which where onscreen (even if they were cached), I wanted to use the picture which was on my canvas already, shift it by a tile and only draw the new line of tiles which had appeared. Kind of like:

bufferCanvas.draw(bufferCanvasBMP, posx+offset, posy+offset)
bufferCanvas.draw(either horizontal or vertical strip of new tiles to the left/right/top/bottom)
canvas.draw(bufferCanvas)

Which worked perfectly. If I was scrolling my map down or to the left. But as soon as I scrolled to the right or up, I got major corruption. Which turned out to be because in Android, drawing the canvas bitmap over itself doesn’t check to see which direction it is offsetting itself but always gets it’s data from the top/left and re-writes itself at the same time.
So scrolling and re-writing to the bitmap itself works if you’re replacing the pixel data from the left/top with the data from a bit to the right. But do that the otherway around and you’re trying to over write data with data which is already re-written!
Turns out, many implementations of such a bitmap copy-to-itself do a check to see in which direction you’re doing the offset. But Android doesn’t.
The solution to all this is to use the fast copy-canvas-bitmap-to-itself method if you’re scolling to the right or down, but to do a (slower) copy-canvas-to-another-canvas-then-copy-that-canvasbitmap-to-the-first when you’re scrolling/offsetting to the left or up.

As you can see, I’ve also done some work on river generation, but that deserves a post on it’s own. Especially since this map is per-pixel and fractal, making things difficult for me :)
And I’ve done some work on rendering points-of-interest on the map, like cities/huts/whatever. Because they’ll be changing during the course of the game (cities grow, resources/treasures get picked up), they need a seperate render pass and it would be handy if they only get rendered when they’re on the screen.

Turns out you run into an interesting problem depending on how you scroll over the border of your map (when you go from til 9 to tile 0 or from tile 0 to tile 9): it totally borks up your tests to see if the absolute world location of the point is within the tiles being drawn on screen!

So you need different parameters to test against (different if statements) when you cross over the ends of your tile-array, depending on your scrolling direction! This surprised the heck out of me :)

Posted in android, art, programming | Tagged , , , | Leave a comment

Update on the mapping progress

I’m well into getting my procedurally generated map looking cartographic.

I mentioned this before, and it might be a bit confusing to visually, so let’s just show once again that a picture is worth a thousand words:

Image

This is still very basic, but it shows the direction I want to go.

What I basically do is just load in my heightmapped tile, loop over a small block of it at a time and get the average height. My “height” is based on the green value of the pixel, where the greener it is, the lower the height. Based on the value, I put down a bitmap with the feature required and loop through the next block of pixels in the tile to find out which bitmap to put there.

The trickiest bit has turned out to be finding the best process to ensure that overlapping features are correctly overlapped without any lines of hills going through hills in front of them. In many cases this is very doable, but it turns out to not be as easy to organise where tiles are next to each other…that overlap still needs to be properly sorted out. And I need a better background colour than pure white :) Oh, and the underlying multi-fractal needs some work…and I’m still trying to get my coastlines to look like how I want them to look…but other than that things look good :)

But still, I’m quite happy with what I’ve got here: a cartographic map which tiles and wraps around in real time :)

Posted in android, art, programming | Tagged , , , , | Leave a comment

A little thing about (activity transition) animations

I recently tried to get some activity transistion animations going. You know,  sliding in the newly launched activity, that kind of thing.

What I wanted was to have my old activity just sit there whilst my new activity (a scroll) opened up horizontally from the centre of the screen. So I wrote my scaling animation xml and buggered around using overridePendingTransition. And completely failed to get anything to show.

Turns out, even if you’re just animating the x-scale property (android:fromXscale=”0″ and android:toXScale=”1″ with the scaling centered on the screen at android:XPivot=”50%”), you NEED  to supply the Y values in the xml, too! Just set them from “1” to “1” will do, but you do have to supply them.

After doing this, my animation works fine (even though it does look a little cheesy…I might have to work my 9patch a bit).

Posted in android, art, programming | Tagged , , , , | Leave a comment

EditTexts, keyboards and the mystery of nonmovement…

I’ve done some cool stuff with my mapmaking routines for my game, on which I’ll post later (screenshots included, of course … it’s really starting to resemble what I am aiming for!).

 

But in the meantime, let me tell you about Android EditTexts.

I have an EditText in a FrameLayout. The ET is placed to the bottom of the screen using layout_gravity=”bottom”. Which is all well and good, but when I tap the ET, I want the IME/softkeyboard to come up. Which it does … but it overlaps the keyboard. Which isn’t good :(

I spent a day or so trying to get the screen to “pan and scan” or whatever using

android:windowSoftInputMode=”stateHidden|adjustPan”

in the activity’s tags in the Manifest, all to no avail. adjustResize didn’t work, wrapping things in a ScrollView didn’t work … every time the keyboard came up, it just hid what was underneath it.

In the end, the solution was relatively simple, although it’s implications aren’t as nice: what was going on was that I had set my application to fullscreen; no Titlebar, no statusbar. I’d also set

getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);

to make use of the extra layout space.

After disabling that and just showing the statusbar, lo and behold! My EditText now will move up when the keyboard appears.

Nice waste of a day :( I hope this post helps others not waste that time!

 

Posted in android, programming | Tagged , , , , , | Leave a comment

Mountains are just big hills…

Well, I think I have found a nice way to make rivers happen, but in the meantime I want to get my terrain looking better. Playing around with different methods of setting up my fractals produce wildly different effects and looks to the landscape, but here’s a screenshot of where I am now:

Image

Remember, this is randomly generated terrain, AND it tiles seamlessly, wrapping the world, so you can travel around the globe in any direction :)

Anyway, after I get the rivers in, I’ll be doing some nifty shit(TM) to replace this with geographical features so it looks like one of those old timey maps :)

Posted in Uncategorized | Leave a comment

But now it has to look real! Or at least, interesting…

OK, so now I have a basic terrain to work with (bar some tweaking to the fractal pattern). But there’s a few things missing: all the things which make a terrain a map. I’m talking rivers, forest, plains and deserts…biomes!

Now, as I’ve said before, the gross terrain features are easy; the heightmap already gives me mountains, hills, coastlines and seas. Using some trickery with a random prevailing wind, I can kind of super-impose the mountain’s shade into a rainshadow, so I know where my deserts are. But one thing I’m missing are rivers!
So how do we get rivers? We do some water/erosion simulation. Basically we find some mountaintops and run water down the sides, carving the heightmap down where the river runs over it. Because this is meant for a game, we’re not gonna do full scale erosion/Navier-Stokes water simulations here; it’ll take to long and the player will have an empty phone battery by the time we’re done. So we’ll have to simplify things a bit.

Anyway, the very first step isn’t as obvious as you’d think; simply iterating around the whole heightmap finding the highest point will get you only one point of origin for a single river; what I want is rivers coming down from all the mountaintops, so somehow we have to iterate over all the tiles and find an AREA which defines a mountaintop and get the rivers to spring from those tops, carving a riverbed on the way down.

This would be simpler if I had the one heightmap, but we’re working on tiles and with Android’s limited memory, we’re gonna hafta do some clever iteration to get this all done seamlessly and end up with multiple mountaintops which might be spanning multiple tiles.

Hopefully next post will contain some more boring pictures resembling iso-lined geographic GIS data. It’s odd, but to get the stylistic display I want, I first have to generate a more complex set of tiles, which get uglier the further along the process goes, before I can pretty them up again…

Posted in android | Tagged , , , , , , , | Leave a comment