Raxo

Besides study­ing for final exams and getting stabbed, which seems a bit of a self-fulfill­ing proph­esy at this time, I’ve been getting some work done on Raxo, my soft­ware modeler and raster­iz­ing renderer.

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

Raxo uses AsmJit for Raxo’s equiv­a­lent of vertex shaders, raster­iz­ing loops, the image buffer postprocessing/filtering, and maybe more. AsmJit gener­ates actual executable func­tions from calls to its API at runtime, then allows them to be called from your C/C++ code. Why is this useful? It allows condi­tional branch­ing (if, while, and for state­ments) to be elim­i­nated from inner loops—the parts of the soft­ware that are executed millions of times every second, 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 rendered, but not all of them are on. A non-JIT compiled renderer would need to check through each light to see if the program­mer has turned on the light, every time the light­ing equa­tion needs to be calcu­lated. That could be once per vertex or even once every pixel! Raxo, however, can recom­pile its render­ing func­tion so that it gener­ates light­ing equa­tion code only for the lights that are on, elim­i­nat­ing wasted CPU cycles eval­u­at­ing those ifs and mispre­dict­ing branches.

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.

Having AsmJit prevents me from having to write a lot of parts of the library in C++ template metapro­gram­ming. Using templates would allow condi­tion­als to be eval­u­ated and opti­mized out as the code is compiled. But then Raxo would be extremely diffi­cult to under­stand and use, not to mention state changes would be impos­si­ble. How would you like to use a renderer that you have to make a new instance of from another class, just to turn on another light in the scene?

So, I avoided hard­core C++ templates, even if it meant having to gener­ate assem­bly code. After all, I can abstract assem­bly gener­a­tion through my own API, but it’s very hard to hide an ugly template API. See Anti-Grain Geom­e­try if you want to see an exam­ple, as the API for it entails using nested templates para­me­ters to gener­ate an entire 2D graph­ics pipeline (filters, trans­forms, geom­e­try trans­for­ma­tion, etc.). Though I must say, AGG is blaz­ing fast, accu­rate, and extremely high-qual­ity. It is defi­nitely a library that inspired me to write Raxo.

Raxo is also currently using Pixel­Toaster as a float­ing-point color frame­buffer (get this: Raxo—and my final project for the Stuyvesant course—renders to single preci­sion float­ing point pixel buffers, for preci­sion and dynamic range beyond those displayable with a computer screen). In addi­tion, a sepa­rate file format, SPRx (Single Preci­sion Raxo err… some­thing) was devel­oped based on Indus­trial Light & Magic’s OpenEXR HDR image format. In compar­i­son, SPRx is signif­i­cantly simpli­fied because EXR was so much more than I needed.