Raxo

Besides study­ing for final exams and get­ting stabbed, which seems a bit of a self-ful­fill­ing proph­esy at this time, I’ve been get­ting some work done on Raxo, my soft­ware mod­el­er and ras­ter­iz­ing ren­der­er.

Raxo is, as described, a way for me to learn about all the comp­sci stuff I’m inter­est­ed in: graph­ics, low-lev­el assem­bly gen­er­a­tion and opti­miza­tion, and design.

Raxo uses AsmJit for Raxo’s equiv­a­lent of ver­tex shaders, ras­ter­iz­ing loops, the image buffer postprocessing/filtering, and may­be more. AsmJit gen­er­ates actu­al exe­cutable func­tions from calls to its API at run­time, then allows them to be called from your C/C++ code. Why is this use­ful? It allows con­di­tion­al branch­ing (if, while, and for state­ments) to be elim­i­nat­ed from inner loops—the parts of the soft­ware that are exe­cut­ed mil­lions of times every sec­ond, and that need to be as fast as possible—whenever they can be deter­mined ahead of time.

For exam­ple, if you have a set of say, 8 lights in a scene to be ren­dered, but not all of them are on. A non-JIT com­piled ren­der­er would need to check through each light to see if the pro­gram­mer has turned on the light, every time the light­ing equa­tion needs to be cal­cu­lat­ed. That could be once per ver­tex or even once every pix­el! Raxo, how­ev­er, can recom­pile its ren­der­ing func­tion so that it gen­er­ates light­ing equa­tion code only for the lights that are on, elim­i­nat­ing wast­ed CPU cycles eval­u­at­ing those ifs and mis­pre­dict­ing branch­es.

This of course means that state changes are expen­sive. But then state changes are expen­sive with hard­ware too—much more expen­sive.

Hav­ing AsmJit pre­vents me from hav­ing to write a lot of parts of the library in C++ tem­plate metapro­gram­ming. Using tem­plates would allow con­di­tion­als to be eval­u­at­ed and opti­mized out as the code is com­piled. But then Raxo would be extreme­ly dif­fi­cult to under­stand and use, not to men­tion state changes would be impos­si­ble. How would you like to use a ren­der­er that you have to make a new instance of from anoth­er class, just to turn on anoth­er light in the scene?

So, I avoid­ed hard­core C++ tem­plates, even if it meant hav­ing to gen­er­ate assem­bly code. After all, I can abstract assem­bly gen­er­a­tion through my own API, but it’s very hard to hide an ugly tem­plate API. See Anti-Grain Geom­e­try if you want to see an exam­ple, as the API for it entails using nest­ed tem­plates para­me­ters to gen­er­ate an entire 2D graph­ics pipeline (fil­ters, trans­forms, geom­e­try trans­for­ma­tion, etc.). Though I must say, AGG is blaz­ing fast, accu­rate, and extreme­ly high-qual­i­ty. It is def­i­nite­ly a library that inspired me to write Raxo.

Raxo is also cur­rent­ly using Pix­el­Toast­er as a float­ing-point col­or frame­buffer (get this: Raxo—and my final project for the Stuyvesant course—renders to sin­gle pre­ci­sion float­ing point pix­el buffers, for pre­ci­sion and dynam­ic range beyond those dis­playable with a com­put­er screen). In addi­tion, a sep­a­rate file for­mat, SPRx (Sin­gle Pre­ci­sion Raxo err… some­thing) was devel­oped based on Indus­tri­al Light & Magic’s OpenEXR HDR image for­mat. In com­par­ison, SPRx is sig­nif­i­cant­ly sim­pli­fied because EXR was so much more than I need­ed.