Thursday, March 29, 2012

Android NDK loading resources and PNGs

After following the nice code sample from http://androgeek.info/?p=275 about how to load a PNG file by accessing the Android APK file directly using only the NDK, everything seemed awesome.

It uses libzip and libpng to manually load the APK, then load a png file right from the APK itself without using any Java or the Android SDK.

Why would you want to do this?  Well, aside from liking complicated stuff, it is useful for us because now we can put all of our "portable" resource loading code in c/c++ files so it is more easily moved to iOS, windows, whatever, without being bound to using only the Android SDK, thus having to recode loading logic over and over again for each target deployment architecture.

Everything worked well, until something didn't go quite right.  Some of my textures were corrupted and worse, some actually crashed my app!

It took 4 days to track down what exactly was happening.

Common problems usually indicate I was doing something wrong with opengl and the textures themselves...

* All my textures were perfect powers of two (64x64, 128x128, etc)
* All my textures were 32-bit RGBA files
* I fixed a warning and error from the given sample code to correctly read the libzip'd apk file

So, it was time to dig deeper with what was going on.

First, I noticed that my glGenTextures calls were giving me very large opengl texture ids so I thought this was a huge problem as it was not continuous numbers, and in C, any time you have generating wild numbers, it usually means a bad pointer somewhere.  I spent about a day analyzing and referencing code and started to realize that the generated numbers were always the same (this usually rules out a memory corruption issue if the results are *always* the same).

I noticed that the generated opengl texture ids always followed this pattern on my Droid RAZR:
100271
315638026
534244737
1505553200
-1563003837
...
etc.

Yet, on my Android emulator, the texture id's were:
1
2
3
4
5
...
etc

I had found this thread on google groups that I thought was my problem since it sounded really similar, but it didn't actually help.

Knowing that glGenTextures expects an *unsigned* integer as the data type, I realized that the negative number was an overflowed signed integer and actually nothing to worry about.  In my debugging print code I needed to change "%d" to "%u" to see the actual unsigned value.

Since these numbers repeated on every subsequent run of the program, I decided to test the idea that maybe my code had nothing at all to do with the "corrupt" large texture ids.  Indeed, I made a program that literally only called glGenTextures about 100 times right at startup time and did nothing else.  The result?  The exact same large numbers showed up in my device, and was "normal" on the emulator, as expected.  This left me with the conclusion that whatever logic is in my Motorola device in terms of picking a unique texture ID that this was normal - next.

Yet, this didn't answer my question as to why I was seeing an app crash occur and seemingly only when loading a certain texture or two.  My first order of business was to rule out if my other code was at fault, or was it somehow related to ONLY those sets of textures (somehow). Strangely, dozens of tests indicated that it was only the problem of 3 specific textures being loaded as I added lots of additional textures, even loaded some textures multiple times, then finally ONLY loading one of those 3 problematic textures.  The app would only crash at loading any of the 3.

Frustrated, I ended up putting a ton of extra debugging code all over my apps trying to track down what in the world was wrong.  All indicators were pointing to the idea that a problem was with either libzip or libpng since libc.so itself was crashing in a call to a libpng function -- great, time to track down potential bugs in those libraries.

Now, let me put a disclaimer up here, it is almost never a good idea to assume a mature code library has a bug in it; especially one this severe, yet here I am with a consistent crash always calling a specific line of code that libpng was using leaving me to believe this could be the case.

First, I downloaded the most recent version of each library and replaced the old versions that were provided in the linked post above.  I was now using libzip 0.10.1 and libpng 1.5.9.  This had promise since each of those had a bit of fixes/enhancements along the way.  I was hopeful that this would magically fix my problems.

Well, it did - sort of.

Whatever wizardry was happening internally in those libraries helped indicate that libpng was crashing specifically on this line:

png_read_info(png_ptr, info_ptr);

Great, all this work only to help reinforce the idea that libpng was broken for my special case.  Yet, I still couldn't rule out that libzip somehow corrupted my image data while decompressing my APK so libpng might not be at fault trying to load corrupted data.  Worse, all error checking came up empty for all libzip and libpng calls!  I even checked my opengl calls for errors using glError -- NOTHING!

Well, the next step was to dive in to those library's source code and start tracking stuff down, the last resort I guess was to find the problem in one of these libraries and fix it myself and give the respective team a patch!

One of my testing tangents included making a stand alone C project to test very specific sets of code to completely rule out any Android Java JNI mysticism causing problems.  Everything was pointing to only those textures being a problem in the libpng loading code.  So I ran the libpng test program on those 3 test pngs and the only thing that sounded remotely threatening was:

Files aaaa.png and pngout.png are different
Was aaaa.png written with the same maximum IDAT chunk size (8192 bytes), filtering heuristic (libpng default), compression level (zlib default), and zlib version (1.2.5)?


It certainly sounded menacing and I was convinced that somehow my textures were corrupted.  I ended up regenerating those textures using the latest version of Gimp (2.6.11 at the time) -- plugged those in to my app in excitement; and they still crashed.

Strangely, the libpng test program generated pngout.png which was seemingly the same exact copy of my original texture, but a different filesize; something was different, and different was good at this point.  For giggles, I put that pngout.png file in to my Android's APK res/raw folder in place of my original texture and much to my surprise the texture loaded!!!  Ah-hah, the answer finally!

Unfortunately, things weren't over yet, the texture loaded completely corrupted.  Everything was misaligned, the colors were wrong and the texture was completely garbled with random colors all over it.  Basically, I've gone nowhere.

3 days in and I'm still struggling with what is wrong here.  I'm still baffled, why is it that my 10 other textures work fine, yet these 3 don't want to work at all?!  I decided to write my own texture loading function from scratch to ensure I understood exactly every step of logic that was taking place.  The same result happened.  I did all sorts of strange additional tests, hex dumps, you name it.  I was at the end of my rope on this one, I was getting angry.

Finally, I saw an interesting article somewhere that said I needed to rename my png files to mp3 files.  I thought to myself, that's the dumbest thing I ever heard - why would I ever want to do that?!  Well, if you're programming for Android, you might be crazy enough to hear this out.

Apparently, during the APK packing process *SOME* of your png textures are automagically compressed and others are not.  Renaming your png files tells the apk packing process to not mangle/compress that file, and my happy 3 textures were some of the lucky selected ones for this process; which completely broke the texture loading code.  In a move of desperation, I did rename my files to mp3 to verify this was the case, and they worked perfectly.  I was enraged and ecstatic at the same time.

On the bright side I got a better understanding of both libpng and libzip (we got a little familiar with each other, if you know what I mean).

Alright, so the magic bullet answer is that I need to rename all my PNG files to MP3 (test.png.mp3 if you will) or simply dump them all in to a game data file of sorts (probably the better idea).

So, I make a quick resource packing program (using zlib) and I find out that during the APK packing process again, my GZ files are also modified!!!

I quickly found this link explaining it a little bit.

I wonder what other goodies are in store for me to discover by accident!?

Hope this article is found by anyone else stuck in my situation and save you all a couple days of madness.

Friday, March 2, 2012

Awe at software development houses of yesteryear

After making another small game, I can't help but be in total stupor over how... easy software houses have it today.  I mean, seriously, back in the day when I was playing things like Nintendo way more often than I should have been, I never even thought about how much time was invested in to making all those games I played.

Back then, it's not like game dev houses could just send out a patch to fix things over the Internet.  When products were shipped, they were shipped for real!  Imagine, this had to be the final product, no going back; we're about to manufacture millions of cartridges and customers will get their hands on all our hard work, period, no going back.  Dev houses had to be pretty damn sure that what they were packaging up was 99.99% perfect.

Ok, a company probably could have done a product recall if the game was horrendously broken, but honestly, we just "shipped" Bubble Zing yesterday, and I already found 2 or 3 small bugs that should be fixed!  Talk about maddening for the devs of yesteryear!  Now granted, we made that game in 3 weeks with a really small team, but still... it is mind boggling to know that these guys did it, and did it well is stupendous... well, for the most part... games like Action 52 did exist, and that was... well... a broken down mess incurring the wrath of gamers lucky enough to play it.

If you're unsure about Action 52, check out James Rolfe's review of it on cinemassacre, it is pretty funny.  James, being the "angry video game nerd", can be a tad vulgar (just a little), so if bad words offend you, you might want to stay away from his review.

On a side note, I guess this would help explain why no version numbers were prevalent in old games, no such thing was important because what you got was it!  In rare cases, a second or third production run was accomplished and it was during this time a dev house could slip in a revision copy of the game, fixing a couple things, but still, we almost never saw any indication of what version number our copies were running.

So yea, one of our products just shipped and I'm already getting a laundry list of things to put in to a patch within the first day of release!

Thursday, March 1, 2012

Design to Product - Post-Production (17 / 17)

Now that the product has been released, it is time to look back, recollect on lessons learned, and already be buried in to the next adventure.

If you took a look at the game design from the previous section and took a look at the game video (or played the game itself ideally) you might notice quite a few discrepancies from what is described in the document as opposed to what actually was developed.  I cannot stress enough that this is what happens.  Throughout the articles I talked about stuff like requirements creep, play testing, and things like this.  Pretty much all of those things happened, we applied our creative process on to the original game design, and ended up with the product you [hopefully] saw.  Yes, we were bad and never updated the original document, but we also ended up preserving what we started with, and what the end result was.

During development, and using the design as a guideline, we also took a bit of time discussing what features were necessary as opposed to being acceptable for future updates.  One of the fairly easy features to figure as "something for later" is code optimizations.

The game needs to be playable, some minor speed enhancements are ok during development, but finer tuning the engine performance isn't required to get the product completed.  Thus, these things are "nice to haves".

We will also be tweaking some graphics, offering different qualities of game resources, and changing things like timing, based on customer feedback now that the initial product has been released.  We did some basic play testing while the product was under development, but we admit that we are a bit subjective with what is and is not "good" from an outsider's perspective.  Therefore, we will also not rule out "customer feedback" as a guiding principle in what we will still do to the product in to the future.

Of course, some of the blatant things we will work on is when the inevitable "omg this doesn't work on my phone/tablet" feedback comes in.  This is expected to happen and we just await the first complaints.  There really is no way to guarantee that our software will work on all of those device configurations short of us physically testing our product on all hundreds/thousands of them ourselves.  Yes, we can use the emulators, and we do, but this is different than using the actual devices.  For example, it is impossible to test application performance against an emulator if the emulator itself is at the mercy of the power of the computer in which it is run against... this complicates accuracy of the performance testing.

From here, we gaze out in to what we will accomplish with this product.  Minor feature corrections, product enhancements, and possibly even small new features will likely occur and we will be delighted to integrate those things as they happen.  Any major new thing will either warrant a re-release of the product in the form of "2.0" or possibly become a whole other product all together.

At the end of the day, version 1.0 shipped and now we can expect all those hundreds of pennies to come piling in over the months/years it is active in the market!  The product that went from "design to product" took just over 3 weeks to go from start to finish and only time will tell how much money we will gain or lose in the bigger picture from this endeavor.

Design to Product - Release (16 / 17)

Release time is around the corner, some things you might want to check over:

  • Take several screenshots of gameplay, use them for marketing purposes
  • Save local copies of everything, and I do mean everything, then back them up somewhere
  • Make sure the release version number is correct in the software and documentation
  • Do one last final general test of the product on your deployment platform

Once everyone on the team has given the thumbs up for release, it is time to package everything up, do one last sanity check over everything, and then publish the product!

Below you will find our original design, screenshots of gameplay, and a link in to the Android marketplace where our product, Bubble Zing, may be found.  This product was in development over the duration in which these series of blog articles were written and took approximately 3 weeks to go "from design to product".

Game Design:
Bubble Zing Design (pdf)

Screenshots:
Menu Screen

Gameplay

Gameplay

Oo, a heart!

Gameplay

Options
Video of Gameplay:


Bubble Zing:
https://market.android.com/details?id=com.eondev.bubblezing
http://slideme.org/application/bubble-zing

Design to Product - Software Testing (15 / 17)

When is a software product "finished"?  This all depends on who you're asking and in what aspect.  One of the crucial aspects of software houses is testing.  Unlike play testing where people have the potential to have fun with the product, software testing is mostly the opposite.

There are several types of software testing, and this article isn't meant to be a definitive guide on how to perform this job, but I will give a quick run down of what it means and what we do.

Data Validation
During any testing, are the proper inputs handled?  This should be one of the first questions that arise.  Of course, what is good input data, if we can't throw some bad input data in there to spice things up.  Data validation is the process of testing software against good, bad, and unknown data.  What happens when that 0 or 1 input was actually a -1 as input?  Does the program blow up?  We won't know unless we test it!

Unit Testing
This is basically the smallest form of testing.  Do small units of logic perform as expected.  This usually means I test a specific function or method and if the resultant output from that logic is what is expected, I could say that this unit test passed -- it performed as expected.  This testing is more or less automatically performed by the programmer as code is written, or at least it should be!

Performance Testing
Does the software do what it is suppose to do, and do it fast enough?  We may make the next best game ever, but if it runs at 8 frames a second... that's probably bad.

Compatibility Testing
This can sometimes go overlooked (especially on Android phones!), does the software work on multiple different configurations?  Are there weird effects?  Specifically for Android, will the product work on a phone, and an Android tablet?


Acceptance Testing
This is like Software Quality Assurance or Quality Control.  Does the software deliver, on a high level, what the design is?  Think of this like a marriage of software testing with play testing.  Does the product allow the player to actually play the game as designed?  Does the code actually allow for the player to launch new marbles and perform all the functions needed to play?  If these are both yes, then it passed both the play test, and the software test, this the product can be accepted as the product.

For more information about testing, validation, methods to perform them, etc... really, we encourage you to look at the wikipedia article here: Software Testing.  The trick is to find what works for you and use that.

Design to Product - Play Testing (14 / 17)

We constantly do general "play testing" - or rather, a general form of playing the game and noting any discrepancies that happen.  This could be referred to as finding bugs, but really it isn't.  It serves as a way to see if the game is any fun, if the design is "good" and what may have been overseen as pitfalls earlier in the design stages of production.  Can things be improved? Does everything "feel" right?  Are there overlapping aspects?  Simply put, this form of testing finds design flaws not software flaws.

This method of testing happens all throughout the development of the product.  This is by no means the only form of testing (nor should it be), but instead gives us the ability to second guess some of our designs as it is being developed and explore some options before publishing the product - more on software testing coming later.

One of the fairly humorous aspects of this is that back in our article about the graphics design where we showed the proposed layout for the game, we had big black circles at the bottom of the screen above the ads that indicated where marbles would occupy.  These "slots" were to be used to launch marbles from.

During the development, we ended up shrinking those slots to about half that size so that the launcher graphic underneath could be seen more; since we made those graphics, may as well have people see them, right?  Well over dozens of play tests, we found that it was difficult to correctly touch and drag marbles from those slots and be able to actually play the game.  We ended up increasing their sizes back to the original layout sizes - taking up the entire height of the launcher.

The gameplay became much smoother and easier to drag marbles out, but the launcher graphic isn't seen easily, yet overall, the game is now smoother and the user should have an easier time actually playing the game; rather than seeing some eye candy.  Aesthetics versus function is the issue here, and we chose gameplay over showing off artwork.

This detail was really only found after performing a lot of play tests and from different people.  The general feedback was "we like this... but...".  This should be taken seriously and addressed during development.  Yes, the catch phrase of what users say can take a lot of extra time to implement.  Fortunately for us, the slot sizes in this case was easy to change because only two variables controlled the width and height of the slot which "magically" handled both the visual display, and the collision detection of the user input.  If these values were hardcoded, this could have been a fun hour or two adventure hunting down and "fixing" all the math that referenced the slot sizes.

When your friends say they want to be game testers, this is generally what they mean without actually knowing it.  They want to be play testers, not software testers.  Unfortunately for them, usually play testing is a team effort and isn't a dedicated job.  For example, this is why software publishers utilize beta tests, they literally outsource play testing to users and gather feedback about the design of the game in a distributed manner -- if they find actual software bugs as well, that's a nice side benefit, but in reality the primary purpose is to assess the value of the design of the product.  It is also a nice way to get the word out and serves as a nice marketing gimmick, but that's a whole other issue.