CarbonUI and EVE Online: Incursion 1.6
With this release, we are introducing the new CarbonUI framework into EVE. You shouldn't see much difference - with this round of changes we have fixed the underlying code, not the user interface itself. This step was made possible by the UI corification that happened last year. This project was a big engineering undertaking that you can read more about below, to pave the way for taking the EVE UI into the future. We appreciate your help with spotting anything unexpected, and we'll be monitoring all the usual channels carefully.
The UI in EVE is somewhat dated by now, and so was the underlying code that handled the rendering. The old UI rendering used fixed-function rendering in Direct3D - basically a D3D7 approach. D3D9 still supports this approach, but it can be a performance bottleneck, and limits you to what the fixed-function feature set offers - no fancy shaders, for example.
We've wanted to overhaul the UI in EVE for a long time now for several reasons:
- Improved usability
- Improved look
- Improved performance
The UI framework itself held us back - as it has grown over the years it had become cumbersome to use and we'd reached some dead ends in trying to improve its performance. Not enough attention had been given to the framework itself in the rush to add new features to the UI.
We considered several approaches to revamping the UI framework, but at the end of the day we realized that the EVE UI code base was far too big to consider rewriting the whole UI with some new paradigms. Whatever approach we would take for a new UI framework would have to involve minimal reworking of EVE UI code.
So, CarbonUI is an evolution of the existing UI framework, with a completely new rendering layer. We've strived to change as little as we can in the high-level interface, so the UI programmer, working in Python doesn't see much change - and more importantly, those ~200K lines of UI related code continue to work. That being said, there was a lot of code modified, either for it to work with the new rendering layer or simply because it sorely needed refactoring. The end result is that there are not many UI files that we haven't touched to some extent.
Some key features:
- UI scene can be rendered in different contexts:
- UI objects can be rendered to a texture that is then applied to a 3D object. The UI then supports picking in 3D, allowing interaction with UI in the 3D scene.
A development screenshot showing the same user interface rendered both in 2D and on the screen 3D object. Click images to enlarge.
- UI can also be rendered as a collection of 2D primitives in a 3D scene, allowing for depth effects and perspective rendering.
- Regular 2D rendering as a 2D overlay
- All Python code and UI objects are the same for these different contexts - the scene can be moved from one context to another on the fly.
- Performance improvements over old UI framework
- Textures are automatically gathered in a texture atlas, giving much more efficient rendering without burdening UI artists
- Clipping is done in pixel shaders
- Transformations done in vertex shaders
- Pixel shaders premultiply alpha so opaque, transparent and additive blending is handled with one D3D blend mode
- This results in drastically fewer state changes - the whole UI is usually drawn with less than 10 draw calls, rather than the several hundred draw calls we had before.
- Alignment completely separated from rendering
- Alignment now done in Python - easier maintenance
- Optimizing rendering does not risk breaking alignment
- Rendering is all done with shaders, opening up the possibilities for doing cool stuff in pixel shaders.
- All primitives support simple effects such as drop shadow, glow and blur.
Test tools used by UI programmers to try out the effects and animations for user interface elements built into CarbonUI. Click images to enlarge.
- New animation library written in python, making it easy to create more dynamic UI.
What do we mean by alignment? It's how the UI elements are laid out - all UI elements have some coordinates on the screen, but the UI programmer doesn't have to place each element at a given x-y coordinate. For example, if she wants a button centered at the bottom of a window, the button gets a CENTERBOTTOM alignment. If the window changes size, the UI framework recalculates the coordinates for the button to keep it centered at the bottom.
Moving the alignment out of C++ into Python has been a mixed blessing. It is certainly faster in C++ and we've been struggling somewhat to make up for slower alignment in Python. On the other hand, the way this was implemented before meant that changing any alignment rules or adding new ones was more difficult. This was due to two reasons:
- The rendering and alignment of UI elements was intertwined - changing alignment would risk breaking rendering and vice versa
- Turnaround time for testing changes in Python is much shorter than testing for C++ changes
Rather than trying to optimize the Python code for alignment to run faster, we found ways to reduce the work needed to achieve the same results. Alignment used to be done every frame while traversing down the UI hierarchy - this was replaced by caching the results and only doing alignment when any relevant properties changed.
Having a clearer separation of alignment and rendering has made it easier to optimize each part of the puzzle.
Why should I care?
When EVE was launched some 8 years ago, the UI looked pretty much on par with other games. Times have changed, but our UI hasn't really. Even though projects such as CarbonUI are difficult to pull through, they are absolutely essential if we ever want to move beyond what the current technology allows for. Even though players won't see much visual difference once we launch CarbonUI on to TQ, we can promise you that our UI designers are already hearing a lot more of "sure, no problem" when they deliver their regular batch of crazy ideas to their fellow programmers, ultimately resulting in UI that's fittingly awesome for our lovely little space game.
What about performance?
We'll take a more detailed look at the difference in performance with CarbonUI in a future devblog!