D2X-XL Worklog
Notes on features and problems from the development of D2X-XL, newest first.
A New Renderer |
|
History After having mostly gotten rid of the lag plaguing D2X-XL for so long, it was time to take a more general look at its rendering engine, which didn't really cope too well with the huge new levels DLE-XP and D2X-XL had made possible, or with displaying large amounts of objects at a time. Actually D2X's render code had just been a quick hack, threading the most basic OpenGL calls into the initially software based renderer to make it work with OpenGL hardware acceleration. There had been no tweaks, no code or data rearrangements, nothing to make it utilize OpenGL at least halfway efficiently. For standard Descent levels and visuals that wasn't a requirement anyway, D2X threw the polygons fast enough at the graphics card. Not so for D2X-XL with its huge levels, high poly count models, new effects and new lighting methods. In certain situations, D2X-XL slowed down to a slide show even on powerful systems. Considerations I had been thinking about how I could change that for quite a while already, and at some point of time I even asked myself why I should keep 'polishing a turd' instead of rewriting the renderer altogether. Well, there were several answers to this question: I didn't have the time for a complete rewrite, I probably didn't even have the skill (though that mostly boils down to having enough time), and I liked the 'turd'. So I set out to do something about what I had already. Objects Step one was to optimize the model renderer. The current model data will be converted to a more manageable format on the fly when first rendering it, and
during subsequent render calls, the optimized model will be used in more efficient lighting and rendering routines. This brought a 50% speed increase and allowed
to multi-thread certain CPU intensive tasks, because they weren't executed inline during rendering, but ahead of it, like the lighting. Not bad.
Geometry The second step was to rewrite the static geometry renderer. This was a pretty complex task and took quite some time to accomplish. First thing I did was to split routines doing general stuff (like computing vertex color and light) off the legacy renderer so that I could easily reuse them in the new render path. Next I devised an efficient data structure for the level geometry and separated the various render processes from each other, executing them sequentially instead of inline during rendering (primarily the lighting). This again allows to execute CPU intensive tasks ahead of the rendering, putting them into multiple threads if the hardware suggests it. It also allows for implementing rendering optimiziations like only setting the depth buffer in an initial render pass, reducing overdraw in subsequent passes (overwriting a further away pixel with a closer one). That optimization made it possible to sort the faces due to criteria minimizing state switches (like using a different texture, or shader program) during rendering. Finally, more efficient render calls could be used here, too. Depending on the area rendered, these optimizations can bring up to 70% speed increase (the more faces and textures, the greater the improvement). Transparency During the work on the new render path I found out that rendering all transparent faces back to front after having rendered all opaque geometry had become
mandatory. Fortunately I had already implemented that as an option. You can imagine how great my relief was that I didn't have to write and test that code too,
because it already was all there and working. Lighting A nice side effect of all the work I had spent on this was that I could improve the appearence of OpenGL style lighting. It was a bug at first that brought me there: Depending on player position, certain faces appeared brighter or darker. When investigating the issue, I found out that this always happened in the direct vicinity of faces emitting light, and depended on whether that face or its neighbours got rendered first. Light emitting faces received a 'brightness boost' in the form of a specular highlight and some shine. This looked pretty good, so I just extended this treatment to every face. Voilà: Nicer, more vibrant lighting and no more brightness changes. Conclusion The complexity of the new render path brought a few bugs I didn't catch right away, but hopefully they are a thing of the past as I write this. So enjoy
the speedier rendering particularly where you need it: In big mines with lots of objects flying around (probably at you |




).