# iPhone 2D projection matrix, FPU

iPhoneCore (Plasmacore for iPhone) is nearing completion!

Item 1: I had an interesting issue today. I’d just got my graphics all drawing when I noticed that my bitmap text was blurry.

Using a trick I’d learned in DirectX, I first tried subtracting 0.5 from each drawing coordinate to compensate for center-of-texel sampling like you have to do in DX. The text was instantly crisp. However:

I then thought I’d see if lines & points needed a 0.5 adjustment, so I drew some lines on the edges of the screen. This graphics test worked fine on the simulator, but on the actual iPhone another problem appeared: floating point error was messing up pixel precision! If I drew lines in screen columns 0, 1, 2, 3, and 319, then line 0 wouldn’t show up, lines 1 & 2 would both be in column 1, column2 would be skipped, and columns 3 and 319 would be fine.

I tried tweaking the orthographic projection matrix several different ways: glOrthof(0,319,479,0,…), glOrthof(0,320,480,0,…), glOrthof(0.5,319.5,479.5,0.5), and glOrthof(1,320,480,1). None of them were accurate for all pixel positions.

Finally, after hours of screwing around, I realized I could ditch the floating point-based pixel-to-unit OpenGL ES transformation matrix and do a better job of it myself using doubles instead of floats!

[Side bar: how many numbers on the number line between 1.0 and 2.0? Infinitely many! How much memory do you need to be able to represent any number between 1.0 and 2.0 with complete accuracy? An infinite amount! So computers use an approximation scheme referred to as “floating point numbers”. The original “float” type has 32 bits and works okay. The expanded “double” type uses double the amount of memory (64 bits) and has twice the accuracy. Computers originally only supported integer arithmetic and did floating point calculations in software, so for a while floats and doubles were much slower than integers. These days most computers have built-in floating point math (via FPU’s = Floating Point Units).]

I set the OpenGL transformation matrix to be the identity matrix. Then OpenGL expects coordinates in the range -1.0..1.0, so I made the following 2D transformation matrix of doubles:

[ 1.0/160.0, 0.0, -1.0;

0.0, -1.0/240.0, 1.0;

0.0, 0.0, 1.0 ]

I was then able to transform pixel coordinates (0..319,0..479) with pixel-perfect accuracy! Not only that, but it clarified my bitmap text drawing to where I didn’t need any -0.5 translation – in this case, it think the original benefit of -0.5 was more from compensating for floating point error more than it was from compensating for any sort of texel sampling setting.

Item 2: With my bitmap text working I did some integer versus double speed calculations on the iPhone hardware. Adding 1 to an integer sum a million times (in the Slag virtual machine) took 0.60 seconds whereas adding 1.0 to a double sum took 0.68 seconds. Not bad – it’s totally awesome that doubles aren’t hella slow or anything!

Awesome! We use a lot of doubles so I’m relieved there’s no major performance hit.

Great work!

Jacob

awesome! makes me want to get a mac to play with iphone development 🙂

I haven’t got my Iphone dev environment yet but from Intel experience I would query whether the slowdown might come from memory access and register loading. I would be interested to see the comparison with adding two 256 x 256 arrays together.

Hey abe, this may be a noob question, but how were u able to impose your 3X3 matrix as the projection matrix? the only functions i see are translate, rotate, and scale. I can create a M3dMatrix, but then how would I impose it as the projection? without calling glOrthof(float, float, float, float).

I rolled my own – made an array that contained all the matrix values and then wrote custom matrix multiplication routines; I do all the transforms myself before sending vertices to OpenGL.

However, after this post I figured out a solution that got rid of the imprecision in glOrthof: using the fixed-precision orthox methods got rid of the error – so, for example:

glOrthox( 0, 320<<16, 480<<16, 0, -1<<16, 1<<16 );