What a fantastic show! I'm so glad I got to see them again in such a small venue. The new album, Synthetica, came out last week, and it's very good.
Along with the usual set of minor improvements, this version runs as an iOS application. As all of the 3D modules in XScreenSaver are written against the OpenGL 1.3 specification, and as the iPhone only supports OpenGL ES 1.1 and newer, this was something of a big deal.
I accomplished this by implementing most of the OpenGL 1.3 API in terms of the OpenGL ES 1.1 API.
It's not in the app store yet. If you have Xcode, please build the "XScreenSaver-iOS" target and check it out in the emulator or on your own hardware. Please let me know how it works for you! (Update: It is in the app store now.)
I wrote this because you are all idiots.
Specifically, if you were involved in the OpenGL specification between 2003 and today, you are an idiot.
Allow me to explain.
Let's say you have a well-specified system that is in wide use (a language, a library API, whatever) and because of changes in some substrate (operating systems, hardware, whatever) you find that you need to add a new way of doing things to it.
The way you do this is, you add new features to the specification and you clearly document the version in which those features become supported.
If there are old features that you would like to discourage the use of, then you mark them as obsolete -- but you do not remove them because thou shalt not break working code.
If you don't agree with that, then please, get out of the software industry right now. Find another line of work. Please.
Your users have code that works. Maybe the new APIs would serve them better. Maybe things would be so much more efficient if they updated their code to use the new API. Or maybe it doesn't matter to them and they just want working code to continue to be working code. At least until such a time as they need the new features, or new efficiency. Remember the First Rule of Optimization: DON'T.
You may see where I'm going with this.
OpenGL was invented at SGI in 1992, and it served the world well for a decade. Generally the API worked like this: you'd position your lights and observer; you'd translate and rotate to where your object was to go; and then you'd say glBegin, specify your vertexes and normals, and then glEnd. The library would then take that polygon and render it. This was refered to as "immediate mode". To speed things up, you could also store these polygons in a list that could be replayed later, more efficiently.
So in 2003, OpenGL ES came along and deleted 80% of the language. They eliminated "immediate mode" in favor of a syntactically very different -- yet functionally equivalent -- way of doing things: instead of calling glBegin and glVertex, you are expected to put all of your vertexes and normals into an array and then call glDrawArrays on it, to draw it in one fell swoop. (This API already existed, but they made it be the only way to do it.)
People defend this decision by saying that they "had" to do it, because the fixed function pipeline is terribly inefficient on modern GPUs or some such nonsense. These people don't know what they're talking about, because the contour of the API has absolutely fuck-all to do with what goes over the wire.
"We had to destroy the village to save it."
Their claim seems to be that glBegin/glVertex had to be removed from the API, because to do otherwise would impact the performance of the whole system, by, I don't know, forcing GPU manufacturers to add new features to their chips or something.
This is nonsense, and I have an existence proof.
Because I've implemented the OpenGL 1.3 API in terms of the OpenGL ES 1.1 API, and it works fine. I didn't have to install a new GPU in my iPhone to do it.
I did it all by myself, in about three days.
Not me and my team. Not ten years of committees working on hundred-page specifications. Just me. Just to prove a point.
So screw you guys.
There is no sensible reason that something very like the code that I just wrote could not have been included in the OpenGL ES API and library. If people didn't use the old parts of the API, it just wouldn't be linked in. No harm. No bloat. That's how libraries work! But if someone did use it, their legacy code could continue to function. That's how supporting your customers works!
If they really felt the need to go all "Second System Syndrome" and just start over, they shouldn't have pretended that OpenGL ES is still OpenGL. They should have named it something else, like, I don't know, DirectX.
To make this work, I wrote a version of the glBegin API that remembers your vertexes and then calls glDrawArrays at the end. Then to make display lists work, I wrapped each OpenGL function to let them be stored in a list. A side effect of this is that the generated glDrawArrays call is what gets stored in the list rather than the glVertex calls.
Then I implemented all the other crap that was missing too, like, "oh, we decided you don't need GL_QUADS, go rewrite your code to work with triangles instead." Jerks.
The code is in hacks/glx/jwzgles.c. If you want to use it to port your own legacy code, just include jwzgles.h. Let me know if it works!
The timeline went something like this:
In early 2010, I thought about porting XScreenSaver to the iPhone. I spent an hour on it and discovered that iPhones don't support OpenGL 1.3. I did a lot of swearing, threw my hands up in disgust and walked away.
Then about six months later, I thought, "maybe I'll just update the code to use some subset of the OpenGL ES API that also works on 5-year-old desktop computers. It was hard to answer the question of, "what is that API", because the OpenGL specifications are a nightmarish mess. I tried to answer the question, "Can I write a keyboard macro or Perl script that will munge my old code into a form that uses the new API?" The answer turned out to be, "hell no". So I threw my hands up in disgust and walked away.
Then about six months later, I thought, "Well, how hard could this be", and I spent a couple hours trying to generate a complete list of the OpenGL 1.3 functions that do not exist in OpenGL ES 1.1; and then the subset of those that are actually used by XScreenSaver. I made a header file. It was really long. I threw my hands up in disgust and walked away.
Then two weeks ago, I had a really bad week at work and needed a distraction, so I sat down and pounded out the code in three days.
As with all things, the first 90% took the first 90% of the time, and then the second 90% took the second 90% of the time.
I had the basics working right away -- I'd say 2/3rds of the OpenGL screen savers worked out of the box. A few more fiddly bits, like figuring out what parts of the texture API are no longer supported, took another few days.
The vast majority of the time was the next two weeks of dealing with the ancillary non-OpenGL-related stuff: building the iPhone user interface, and making sure all the savers reacted sensibly to orientation changes.
There are a few things I couldn't figure out how to implement:
- Sphere-mapped textures for environmental reflection: OpenGL ES doesn't have glTexGeni GL_SPHERE_MAP, and I don't know how to fake it, so the Flying Toasters aren't shiny.
There's glTexImage1D and I'm not sure how to simulate that with glTexImage2D.Oh duh, it's just an Nx1 2D texture.
There's no glPolygonMode with GL_LINE, so I don't see an easy way to implement wireframe objects with hidden surface removal. Maybe rendering them twice with glPolygonOffset?
Several of the hacks used GLUtesselator to decompose complex shapes into triangles, and I didn't implement that. I could probably port the code from GLU, but it's a huge piece of code and sounds like a pain in the ass, so I punted.
Some iOS-related questions for the Lazyweb:
- Is there any sane way to have the dialog that pops up when you hit "About" have a clickable URL in it? (My understanding is that putting a UIWebView there would not be considered sane.) Same question for the "Settings" pages: I'd like to have clickable URLs embedded in the free-text of the description field. (Update: I did it with UIWebView. It was an unconscionable hassle.)
Right now when a hack wants an image to display, it alterates between a screen-shot of the scrolling list view of the app, and colorbars. Is there any way to get a screenshot of the phone's "Finder" or whatever it's called: the page with all the application icons on it, that was visible before this app started? It also would be nice if we were able to load random images from the phone's Photo Gallery and use those. I gather that's possible with ALAssetsLibrary, but it sounds kind of like a pain in the ass. Can someone show me how I'd select a photo at random and get it back as a UIImage? Update: David Phillip Oster has the goods!
Anyway, there it is. I hope you enjoy it!
In order to debug my XScreenSaver port, I had to tithe Apple the usual $107 in order to have the "privilege" of debugging software that I wrote on hardware that I own.
If you let your "developer" membership lapse, they lock out all of your old apps. Even if those apps have already been approved, and haven't been modified in years, they stop letting people download them. It would cost them exactly nothing to have just left it there. They had already approved that exact binary, and on my dime. They disabled it purely for extortionary purposes. Fuck you, Apple. You are dicks.
But once I renewed, it came back to life, so if you don't have a copy already, get it while you can because it will presumably vanish again in 12 months when I next find that I have no other reason to pay them.
ImageLoaderMachO::parseLoadCmds() + 54
ImageLoaderMachOCompressed::instantiateFromFile(...) + 296
ImageLoaderMachO::instantiateFromFile(...) + 302
dyld::load(char const*, dyld::LoadContext const&) + 224
dlopen + 742
dlopen + 42
_CFBundleDlfcnLoadBundle + 106
_CFBundleLoadExecutableAndReturnError + 370
-[NSBundle loadAndReturnError:] + 904
-[NSBundle load] + 16
-[NSBundle principalClass] + 40
That's my call to [NSBundle principalClass]. The bundle exists; I see the pathname it's loading, and it's the same one it was successfully loading and executing under Xcode, on this same hardware.
I'm pretty stumped on how to solve this one. There's nothing like a Heisenbug that depends on whether the debugger is running.
I've turned on the various malloc environment variables inside Xcode and saw nothing. (Not surprising, though; that trick never works.)
The new band with Rebecca and Ryan from Stripmall Architecture and Halou. Stripmall are playing next Sat at Independent. You should go.