Wed, 26 Feb 2014
I took a computer graphics class for the winter 2013 semester, in which I learned how to program in C++ with the OpenGL (and GLU, and GLUT) library. The most fun part, which I unfortunately did not have enough time for, was my final project in which I could draw pretty much whatever I wanted.
After finals were over, and Christmas came and went, I began diving into OpenGL for Android. Android does not use OpenGL per se, it uses the OpenGL ES library.
When I was porting open source apps that uses the Simple Directmedia Library, some of them had had OpenGL hooks and I had skipped them for porting to Android.
Initially I kind of dove in at too high a level. While the Android example apps used OpenGL ES 2 and so forth, most of the code I was looking at was more geared toward OpenGL ES 1 if anything. So I rewrote Android's hello-gl2 app to target OpenGL ES 1, not OpenGL ES 2. I also made sure it had the C++ values exported properly.
I decided to revisit those open source SDL apps with OpenGL that I had passed over previously. The first I looked at was Pipe Walker. It had a minimal number of OpenGL calls, and I ported it without much of a problem.
One thing I did was install the OpenGL ES library on my Linux desktop, and then target my desktop for the program, but pointing to the OpenGL ES library, not OpenGL. Once I got that working, porting it to Android was less of a hassle.
Then I looked at Jigzo, an open source jigsaw puzzle app that used SDL. It had a few more OpenGL calls, but was still fairly simple. So I ported that over. Again, I rewrote the desktop app to use the OpenGL ES library on my desktop, then I ported it to Android.
I then noticed the app Anagramarama which used SDL. It didn't have OpenGL calls, but I just noticed it while looking through open source SDL apps. So I ported that to Android as well. It's really designed for a standard monitor, so I made it tablet only - it does not work with phones well in its current form.
Pipe Walker and Jigzo used minimal OpenGL calls, so hand-porting it to Android was easy enough. But as I looked at apps with more code, hand-porting all the OpenGL stuff looked like more work. So I began looking how to automate this.
One solution was regal. It's Github page says regal is "a user-space OpenGL layer for OpenGL 2.x, 3.x, 4.x, Core contexts and ES 2.0. Regal implements OpenGL loading, emulation for ES and Core contexts and tools for debugging". Cool! I grabbed it and compiled the dreamtorus example app right on my Android. Excellent.
Then I looked at the size of libdreamtorus.so. About 20 megs! To figure out the total of what my Android app would be I would have to take that and then add on the size of the rest of the Android app. A 20 meg dynamically linked shared object library is not big for an average desktop or server, but it is for an app on a smartphone.
Pipe Walker hand ported myself had come out to less than 3 megs all told. Jigzo even with its Jigsaw puzzles was less than 6 megs in total. Yet just the regal library itself would be 20 megs on my device, never mind the rest of the app.
If I wanted to continue with regal I'd probably want to work on trimming that library size down. I don't think regal had much OpenGL 1 support either. I decided to look for other options.
Jamie Zawinski of Netscape fame had ported his XScreensaver app over to iOS, and had faced the rigamarole of all that OpenGL to OpenGL ES porting. Amazingly (to me), he was able to automate doing this within three days. Pretty much all of this work is done in a compatibility shim consisting of a file of C code and two header files.
As the file was within XScreensaver, I thought Xscreensaver would make a good first app to port with this method. But XScreensaver has a lot of libraries, a lot of dependencies, a lot of header files in code which themselves include other header files. I like to work on simple things when getting familiar with something, and then work my way up to the more complex stuff.
Like Jigzo and Anagramarama, I tried to find the simplest Xscreensaver to compile on my desktop, and rip it out to its own package, with as simple a Makefile as possible and as few dependencies as possible. The step after this would be to point to the OpenGL ES library, and then do the Android port. But it was slightly difficult to do, the XScreensaver apps had a lot of dependencies.
So I took a look at other screensaver packages. The Really Slick Screensaver package (rss-glx) contains the official Really Slick screensavers as well as some additional open source screensavers. They were much more easy to make simple standalone applications and Makefiles. The Sundancer one was simple enough that I hand-ported it from OpenGL to OpenGL ES, not even using jwz's GL -> GLES code. Once that was done, I worked on porting it to Android.
It was a little difficult, I never did a wallpaper on Android before, never mind a live wallpaper. I found some code that pointed to an EGLSurface as opposed to a Canvas for live wallpapers. Then I hooked that into the code I wrote which could do OpenGL ES 1 rendering on the native (C/C++) side of JNI. A little more banging on it and it worked as a live wallpaper. I tried some of the other rss-glx wallpapers but there were various problems. Then I went to work on the Hufo's Tunnel screensaver. It had a few more OpenGL calls than Sundancer, in a more complex manner, so I pulled in jwz's GL -> GLES code. It worked.
I wanted to have multiple screensavers in one app so I worked on it so that both would be in one app. I also wanted users to be able to send some of the flags that you could in the package. I put this in the wallpaper settings. The tunnel could be made smoother or coarser. The dancing sun could have sunbeams increased or decreased. Then I wanted to make sure the screensavers wouldn't interfere with each other or zombie instances of themselves - something I still have probably not totally fixed yet. I want to reduce state as much as possible, especially global, long-lasting state.
Sending command line flags to Android is a little different due to how the application lifecycle works. A threaded getopt routine is preferable. Luckily one exists, optlist, so I replaced getopt with it. Worked great.
The tunnel app uses the bzip2 library so I included that as well.
So I released the app with two live wallpapers - Sundancer and Hufo's tunnel. It had been unstable, but then I removed callbacks from the Runnable command when destroying the surface, and it became more stable. I QA'd it some and it was good. I published it. Hopefully the code is stable enough. I think it should be if someone is not purposefully trying to break it - hopefully.