Dear Imgui is absolutely awesome, first time in my life where I'm actually enjoying writing UI code. AFAIK it was an intentional (and IMHO good) design decision to not tie the API to the C++ std library, this makes imgui more widely usable, for instance in code bases that also don't use the STL, exceptions or RTTI (and that's quite common in the game development world), it also makes it easier to write wrappers for other languages.
Check out what people have been doing with Dear Imgui:
Yup, there's no doubt that it's awesome that ImGui doesn't force you to use some particular library.
But for people already using STL and integrating ImGui with their codebase, using STL with ImGui may be somewhat difficult and that's why I wrote the article about things that worked for me. :)
I'm surprised immediate mode guis have taken such a long time to take hold. The separation of data and drawing in very elegant after having come from ui systems where your widgets essentially end up being data structures.
Immediate mode GUI still has a huge drawback - running UI code every frame. It's not a big deal with simple widgets, but with complicated UI, especially with some kind of automatic layout involved, it becomes a pain.
In Unity3d game engine in particular, IMGUI was originally used, and it was a huge performance drain. Now all the "real" UIs have been moved to a new, object-based system (with a LOT of caching and optimization), but IMGUI is still used for quick prototyping and debug UIs.
I think the arguments that "well, games redraw the HUD every frame and it's fine, so why is recreating the UI every frame a problem?" are misunderstanding the problem. HUDs usually don't have much layout to do: in CSS terms, everything is absolutely positioned. Designers usually create HUDs visually using Flash or similar, so the layout ends up being trivial since, in effect, a human has already manually calculated the positions of every widget. For a programmatically-created GUI, though, there is actually layout to perform, usually via a flexbox-like model but sometimes via a constraint solving system (which makes the time complexity go way up). For this to be efficient you need a way to cache the results of layout so that you only recompute them from frame to frame when needed. This caching is precisely what a retained mode UI is.
For those who think that layout is always fast enough to never be a performance problem, try writing a flexbox UI in your browser, then open the dev tools, resize the window (which will trigger a full relayout if you don't have any explicit widths in there) and see how long the layout takes. It often eats up the time budgets of multiple frames (and this is not just browsers being slow--the complexity is inherent).
This is not to say IMGUI is bad, mind you! It's a great solution for debug overlays, which is what it was originally designed for, because of how simple it is. Just don't overstate its benefits: retained mode UIs exist for a reason.
At a clock rate of 2 GHz, at least 64 million cycles will elapse during multiple frames (> 32 milliseconds) of layout. What makes flexbox layout so inherently complicated that computing it takes such a huge number of operations? Maybe we should come up with ways of specifying layout that take fewer operations to compute.
But how many times can you access the DRAM in that time?
One thing you have to keep in mind is that the more dynamic the scene graph, and the larger its memory footprint, the more frequent you'll miss the cache while traversing it.
It's possible to manually write optimized flex-box-like layout code, where the overall structure, if not exact position, is mostly static.
For example, if you always have one resizable panel to the side of two viewports, of equal width (as is the case in the editor I use atm), you can easily do `viewport_width = (total_width - panel_width) / 2` and be done.
Those operations, if done with integers, should be faster than a read from cache, or if done with floating-point numbers, (much) faster than going to DRAM in the even of a cache miss.
However, doing all of that by hand would be a pain, and it's hopeless in the face of user customization. SIMD is another resource that's completely unusable in the face of truly dynamic data, and a time sink to do manually.
What we're missing is automation of mostly-static UIs, generating specialized code and optimizing it into computations that run under a microsecond. For HTML, the hope would be JITs, but highly dynamic DOMs are still all over the place, so you're not going to have as many gains as a dedicated UI system, and it wouldn't be free for the user.
Yes, exactly! It seems like immediate-mode GUIs could actually lay out faster than retained-mode: you don't have a scene graph, and the layout code is just normal code that can be optimized by the compiler.
Layout code is always "normal code that can be optimized by the compiler". Why wouldn't it be?
I think you're referring to applying optimizations to static UI with constant data structures, but that can be done with retained mode too, and I doubt it actually is going to help.
When you consider that layout typically depends on fonts, which are hideously complicated, it's a lot worse than that. Text line breaking involves a lot of allocation, etc.
Also, layout is typically memory-bound. The arithmetic intensity is sadly fairly low.
I do agree with you, though. Sadly very few Web developers and designers care about performance of layout; all everyone seems to want is new features without any regard for performance...
Dear Imgui is really fast since it tries to avoid state changes as much as possible and generates render command lists with very few draw calls. I've seen a lot of UI systems that do one or even several draw calls per UI elements and end up with hundreds or thousands of draw calls per frame (checking how draw call batching is done is usually the first thing I do when evaluating a new UI library).
On average, Dear Imgui seems to generate one draw call per window, with just changing the scissor rect inbetween draw calls. It will be very hard to come up with a complex enough UI that this becomes a performance problem even on low-end GPUs.
Code does not need to be run every frame. You can decide when the gui state gets refreshed, for example only redraw the interface framebuffer on interaction. The linked ocornut imgui is pretty fast however, even without framebuffering. I found the overhead to be negligible (i am so confused by the arrangement of chars in this word), atleast on 5 years old low end x86 hd4000 hardware with gl3. i did not check on arm gles2 hardware though, but on every cheap modern labtop / desktop this will fly.
The only drawback i see is that the linked ocornut imgui is pretty minimalist in design and layout, not easily bendable and not well suited for an elaborate pixelised artistic game interface.
But since it got textured buttons already in, it might not so much work at all to wrap something up. In the current state it's super easy painless and quick to implement for small tools, and to get values on screen updated in realtime.
You making a blanket statement based on what Unity (one implementation with its own particular design/features/requirement) did. I've rarely heard of people having unsolvable performance issues with dear imgui. The layout features and other features are carefully designed with performances in mind.
It's an inevitable drawback of the entire concept of an immediate-mode GUI that gets recreated from scratch on every frame. There are major types of optimization which are simply impossible.
The vast majority of games use simple GUIs where it won't matter. But if you have a complex or large GUI, you will be eating up significantly more CPU cycles.
In my experience the main problem with performance in UI system is not layout, but draw calls and render state changes. There are some braindead UI solutions that build a hierarchical scene graph for rendering, with each node in the graph costing one draw call.
Once you have such a system it doesn't matter whether the higher level parts follow the immediate-mode or non-immediate-mode philosophy, or if the scene graph itself is rebuilt each frame or not, just the render state changes and draw-call overhead will kill performance as soon as you have a few hundred UI elements on screen.
Dear Imgui builds flat vertex buffers and a very simple command list where each command is basically 'please render this range of vertices with this scissor rect', and as I wrote above, generates about 1 draw call per window. Only a new clip-rect (e.g. a scrolling text frame) requires a new draw call. As an example, in my 8-bit emulator, the entire debugging UI, with all windows open (12 windows with a lot of scrollable text, buttons, lists etc...) costs 16 draw calls.
Just to take "simple" text rendering... When you have just ASCII text rendered with monospace font then it probably OK to do its full layout on each render run - that's really simple. But if you need to render, say, HN page then you'd better compute once and cache glyph positions. And other objects too that need calculations: gradients, tesselated shapes, etc.
Displaying a few hundreds widgets has absolutely negligible cost if the code is written properly. For large lists they can always be clipped and not submitted at all. With an immediate-mode GUI you can actually handle navigation of a list of one million items whereas more RMGUI would struggle because of all the intermediate data involved.
Also, the optimization bottleneck nowadays is mostly programmer/brain time, so having a ten-fold productivity improvements means you can actually create more/better tooling and that has very direct consequence on your software performance. The reality with toolkits like QT is that people write very little amount of tooling with it, because it is so tedious. The cost of that are lost opportunity in improving a game engine and its performances.
The vast majority of games are very complex GUIs because the entire game is a graphics interface with the user, not just some HUD or menus. Yet most of them run on an immediate mode API.
It's funny that you mention this because I remember trying to utilize DirectX RM (retained mode) for a game in late 90s. Even without taking the performance into account (one could say that the implementation was not very optimal) it was a major pain to do anything. One thing I remember vividly: our explosions were static meshes containing all frames of animation and were animated through toggling visibility of different nodes.
Luckily that game had been killed by publisher and I did not have to suffer through optimizing its performance.
I guess it would be possible for someone to add a layer on top of ImGui that would perform diffs on the UI model, similar to React.
Having said that, while ImGui is pretty simple, I still think there's a lot of implementation details that leak out when you're coding (the stuff with the double hashes for instance). I still prefer Qt for regular UI work, but ImGui is deliciously lightweight though.
> I guess it would be possible for someone to add a layer on top of ImGui that would perform diffs on the UI model, similar to React.
That would very likely be done at a loss, the processing/clipping of ImGui is sufficiently fast.
> I still think there's a lot of implementation details that leak out when you're coding (the stuff with the double hashes for instance)
Yes the id-stack system is an artefact of the entire architecture but it's pretty trivial, transparent and a cost that is immediately amortized once you understand the UI architecture. Double-hash is a feature, would you prefer an UI system where every single widget would requires an explicit ID? Or worse, an explicit storage (like most RMGUI). Every piece of software involve implementation details. The details of QT can get pretty opaque if you ask me.
I am bit annoyed with people just theorizing on performance issues that they haven't experienced themselves. I wish people where spending a tenth of the time looking at the cost of naively using std::string or containers.
How would this UI library work on an iOS OpenGL project? I would expect it to display fine, but maybe lining up the touches with the buttons could require some extra thought.
<StlIsTheWrongName>If writing "modern C++", why do you want to use a really old library, STL, why not instead use the "Standard Library"? </StlIsTheWrongName>
Do you not maintain threads, RegEx etc... ? At best STL can be reffered to as a subset of the Standard Library, which contains the algorithms and containers framework. I take Stroustrup's word over a maintainer with the intials STL, who has an interest to keep calling it by this name.
"Standard Library" sounds better and I'll probably use it instead of "STL" in the future, but I'm not sure that "STL" is not used anymore or have been discarded.
Check out what people have been doing with Dear Imgui:
- https://github.com/ocornut/imgui/issues/123
- https://github.com/ocornut/imgui/issues/539