Also saying there's no memory allocation is really misleading. There's PLENTY of memory allocation, per frame, you're just not explicitly doing it yourself. It's actually much worse than an RMGUI in this regard, because at least with an RMGUI you get the allocations over with once. With an IMGUI you're allocating things all the time. They're probably much smaller allocations, but lots of small allocations does not make for good performance.
One final note, the Unity 3D example always gets used. If you've ever written a plugin for unity or a custom editor, you're very familiar with the fact that it's editor gui system is extremely limiting and kind of sucks. I mean, it's an example, but once you're past the basics it's kind of a bad example.
Electron is a catastrophic of hog of resources and performance. Electron for mobile game UI would be a disaster!
It works very well in the context where you already have fast graphics and an update loop, and you're already expecting to redraw the whole screen every frame. It does not really suit more complex, text-heavy UIs where you're rendering thousands of glyphs with proper kerning and ligatures and anti-aliasing, etc, and want the result of that hard work to be retained in the framebuffer unless it absolutely needs to change.
I think immediate mode GUI libraries can get around this issue by still caching and reusing between frames. Conrod does this by still having the state in the background although you are programming to an immediate mode API:
https://docs.rs/conrod/latest/conrod/guide/chapter_1/index.h...
That said it's a good middle ground. Use whatever API you prefer over a well optimized implementation.
The immediate mode renderer is great for toy programs. Similar to how you could reproduce 'look here is how simple it is to write hello world and compute the millionth digit of PI' in a new esoteric language...
Occlusion, hit-testing, state changes, scrolling/animations even in the simplest forms will fall over. Infact, that's why we have every major browser move their touch and animation systems into a layer based compositor system (into a separate thread / process).
The author also grossly misses their own example of 'how a spreadsheet with many rows and columns will update faster using ImgUI' and how Instagram styled apps will fare better ImgUi.
A retained mode renderer will use virtual scrollers, efficient culling of nodes for both display and hit-testing (scale to billions of virtual cells) and more importantly help a team of people coordinate their work and get things done.
We are no longer in the 90s.
Function call tree has some advantages. Functions are wonderfully composable and flexible.
React is such a revolution because it translates ImGUI usage into whatever insane retained mode API is at the bottom of the stack, through intermediate representation of virtual dom.
Presto. You have flexibility and composability of ImGUI without disadvantages of keeping your gui tree only in your function calls, and you can use it without ability to directly control how the GUI is drawn in the engine.
A good IMGUI will do this too (e.g. Dear IMGUI already does)
Sure, if you're on an embedded platform, somewhere a JIT can't run, or if you're doing something with real-time rendering requirements (and honestly modern React Suspense should even make that feasible), you may want to use something lower-level. But most people won't need to do this.
(I've written systems like this for Cocoa and Win32, and it never turned out to be necessary to do anything other than just regenerate the entire UI any time anything changed. The update runs at 30Hz or 60Hz, and when anything is changing, the UI gets regenerated a lot! - but so what? Most of the time, the UI doesn't get regenerated at all. Then something happens, and the code spends 2 seconds getting absolutely hammered continuously, malloc malloc malloc malloc malloc, god help us all... and then, once again, nothing. The operator puts their fingers to one side and stares at the result with their eyes. Repeat.)
Immediate vs retained is a simple case of budgeting against cpu usage or memory usage, and it should be considered in that light. (immediate uses more processing, retained uses more memory)
Many of authors most significant criticisms on retained GUIs are implementation considerations. GUI frameworks exist that solve his key criticisms of complexity and are pleasant to work with.
Criticisms that target core architecture of retained GUI I don't consider to be valuable design goals, at least in settings where I work on GUIs. e.g. memory usage.
Alot of things are glossed over that remain challenges in both, e.g. layout management.
HTML is an interesting example. First iteration of HTML was essentially immediate mode if you think about a single client/server interaction as a GUI update cycle. Server sends draws to client browser and client browser sends back to server user selections. There is no retained state on gui side. Now with programmatic access to DOM, ability to attach events to DOM elements from client side it is now a retained GUI. Seems to be where things evolve to naturally.
The GUI framework I use nearly daily is retained and very pleasant to work with in terms of ease of belting out screens & readability/maintainability of code. The simplicity comes with compromises though as there are limits on GUI idioms that can be expressed. Occasionally run into those boundaries and resulting GUI does look a little plain and unexciting, but for something that is substantially about data entry its fine.
The idea is that a button is a UI element that _blocks_ until an event is fired. You can then compose elements in time like:
button "hey" >> label "clicked"
which is a program that displays a button, you click it, the button goes away and the text "clicked appears"Or you can compose programs in space:
(button "hey" <> label "not clicked")
this is a program that displays both a button, and a label at the same time.Now, by combining both space and time, we can create a program that changes the label when clicking as follows:
program = (button "toggle" <> label "off") >> (button "toggle" <> label "on") >> program
This is an application that toggles a label on and off when you press a button. (Note that the definition is recursive)As the author points out, HTML has a poor design (eg. if you want to have a 1000x1000 cell table, you have to have 10^6 actual cells - that's a lot of tds or whatever to parse).
Modern OO GUI frameworks don't do this - they say something like:
cellRenderer.draw(target, row,col,position,size)
No creation of objects required. Of course since it's so easy to create OO programs a lot of code isn't great... and then others copy that code and so it goes.
Seems like we keep re-creating software b/c we haven't taken the time to look at what exists and only then decide on what to keep and what to change. "This is too complex - I'll re-write it!". 10 years later: "We added all the features that the existing software had and now the new one... is just as slow... but we did sell a lot of conference tickets and books so... totally worth it."
When I was 20 I also thought I knew better so I get it.
A balance may be letting people define it either way, so that manually written ui still can have auto-layout yet intuitive code (following control flow primitives), whilst allowing generated retained uis to be manually editable -- perhaps even allowing one to then embed one within the other, a boon for scripted interfaces that perhaps have people of various levels of experience producing ui elements, such as a musician with little experience being able to add a simple visualiser in an immediate manner to a deeply retained daw gui.
Of course, there's a lot here that is implementation, and some criticism either way can be optimised out. Immediate mode can still cache its rendering, we've had optimised blitting since the early days, and is only usually a problem with complex ui. Retained would get fewer cache misses if we weren't allocating madly across the heap and took a more disciplined approach allocating to contiguous memory -- which is almost entirely a language/api problem (in my experience) that can also happen with immediate but we typically don't see since it's often done in a more procedural style that is allocating to some pool.
Other api elements, such as handling lists etc aren't really a differentiation between retained and immediate, those can be made in either.
For me, I often find that the ability to write out prototype ui code in an immediate style in very quick and satisfying (exactly what I want in prototyping), however once I start to expand upon a ui, I find it best to over time refactor towards a retained style, since by then I will typically have some templates for what ui elements look like, and so I just have to pass a string and function pointer to fill in the template.
Can't see why we can't have nice things and let both coexist...
> You have to write lots of code to manage the the creation and destruction of GUI objects. [..]
> The creation and destruction problem leads to slow unresponsive UIs [..]
> You have to marshal your data into and out of the widgets. [..]
My biggest pain point with the retained mode GUIs I worked with was none of the issues mentioned above. It was always the centralized GUI thread and the consequential synchronization complications. I don't know if this is an inherent problem of retained mode GUI frameworks and if there are some that don't force all widgets into a single thread. If not, this alone is a reason to for me to find immediate mode interesting.
My hope was that with an immediate mode framework I could just show a progress bar on top of the existing GUI right from the worker thread. I don't know enough about immediate mode to say if this is really possible. It would simplify a lot of my code though.
We moved away from WM_PAINT for a reason.
if (ImGUI::Button("Click Me")) {
IWasClickedSoDoSomething();
}
This forces Button to be stateless, which limits the possible quality of implementation. If you mouse-down on a button and the button changes before you mouse-up, it shouldn’t register as a click. Similarly, if you mouse-down on a button, drag to the button, and mouse-up, it shouldn’t be a click. Implementing this in a fully immediate-mode GUI with this interface is either impossible or requires gross hacks.I would call that a dirty hack, no to mention being implicitly stateful.
"More research into ImGUI style UIs could lead to huge gains in productivity."
Don't tell this cat that the research on this stuff goes back >40 years and that the introductory chapter of any book on computer graphics would have talked about all of this. Not like it would help him - he hasn't read anything about it, didn't even do a cursory Google search. Sheesh. A low, low bar.
Nowadays I do a hybrid approach, so I have use NanoGUI and create my own "live data" "retained mode" controls. Now I have either the best of both worlds or the worst of both worlds. I think the best:
Pros: - I don't have to bother with data binding. As the control is passed a pointer to the actual memory for he value, it can "go get it" when rendering, rather than my application setting its state. - I still have classes to represent elements and state, so its conceptually simple to build controls on controls. I found this difficult with Imgui.
Cons: - renders 100% full speed, but I am working on way to speed up and slow down render loop depending on user activity, so that when sitting idle, the cpu is not burning.
I may have misunderstood you, tho :)
Sometimes you need to traverse the hierarchy to figure out where things will be placed. Before traversing it for render. If your hierarchy is implicit in a call graph, you have to either duplicate your calls, or implement some kind of backend retained system so you can defer rendering until after layout.
Beyond the absolute simplest of toy UIs, immediate mode doesn't work in my opinion.
As a Unity developer, I love immediate mode GUI for debugging. But I would never in my right mind attempt to use it for actual in-game GUI. Project I'm working on right now is not incredibly complicated, it's just a typical mobile match3 game. But a typical screen here has: (1) background that has to be scaled over all screen, eveloping it around while keeping aspect ratio, (2) frame that has to be scaled to screen without keeping aspect ratio, (3) a "window" background that has to be scaled somewhat to screen (with margin), being enveloped by it, (4) interactive elements, that have to be scaled down from the "safe area" (so that there are no button under the iPhone bevel), (5) match3 game field that has to be scaled according to physical sizes of the screen, (6) pixel-perfect elements that have to be chosen according to pixel size of the screen (1x, 2x and 3x options) and scaled appropriately.
So, no, immediate GUI is definitely not the solution here.
> Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes:
> - Doesn't look fancy, doesn't animate.
> - Limited layout features, intricate layouts are typically crafted in code.
It may not replace retained mode GUI toolkits, but it can certainly make the life of devs easier. If all you need is to quickly hack together an internal tool, or some quick debugging interface, keep ImGui in mind.
I believe Rebol's GUI support is even easier to use than ImGUI, but of course it can't be embedded and used in the same way as ImGUI either. I wonder if non Red projects could possibly hook into Red's system once Red/System gets closer to C level performance?
I think this is a misrepresentation of how fast scrolling is usually implemented.
For fast scrolling, you render the page (which is larger than the viewport = "what fits on the monitor") ONCE onto a GPU texture, and then all scolling happens on the GPU side (the CPU just tells the GPU the offsets into that texture).
Immediate mode has to recreate the texture every frame, instead of once for multiple frames. So "It might use more CPU" is quite certainly true.
Assume a 4k x 2k display at 60 FPS.
Compute the throughput needed (Bytes per second) to draw an RGB framebuffer.
That is: 8M pixels x 3 Bytes x 60 fps = 1.44 GB/s
Note how we haven't done any computation yet to decide what the colours should be, this is just the CPU effort to do IO to tell the GPU about the new colours to show.
This would incur significant CPU usage, and your device would get hot quickly. In contrast, if you let the GPU scroll, you have to send two floats (for X and Y offset) per frame, and the GPU just does a hardware-parallelised lookup.
This is why we have GPUs, and why scrolling immediate-mode would make your device burning hot while a GPU does the task with minimal energy usage.
Two floats? You are using a memory that is a CPU memory, a big chunk of memory. That does not exist in the GPU. In the GPU the memory is distributed so it can be used in parallel.
Immediate GUI exist because of GPUs, because with GPUs you can draw frames fast. If you look at Imgui code it uses GPU drawing for everything. In fact it uses only two drawing functions copying small rectangles in the screen.
It is drawing a single big chunk of memory what is extremely slow, and you need to do that before you do offsetting.
And if you work with variable data, like a treeview, you need to allocate for a finite amount of memory in the GPU buffers.
One of the big advantages of drawing for every frame is that you only need to draw what is visible. And do it in real time, with no perceived lag.
For a text editor I use I only draw at any given time 1/10.000 of what is actually there. In fact, you don't need to draw textures at all with the GPU, Apple or Microsoft or Google does not do that for drawing text because it is extremely slow generating this texture and it is not flexible.
Those companies in the bleeding edge, they draw directly in the screen from curves like bezier approximations.
That's a really well-favored set of trade offs for many cases where you need smooth scrolling.
People don't do that stuff in shaders or CUDA, because they were neither designed for it, nor is it fast, nor is it pleasant to code.
ImGUI exposes that to their API users: you have to re-render and check for clicks on every frame. The code looks like React, (but not as optimized, it re-renders every frame!), and normally you have to keep state by yourself. Code example: [1].
Retained Mode is closer to the DOM, Cocoa or WPF: you create objects and there's an abstraction between the API and the renderer: they get re-rendered every frame for you. Componentes normally have events and state by themselves. Sometimes there's a visual editor too.
The main difference is the API. One is lower level than the other. In practice, the APIs aren't that different, except when it comes to event handling.
They don't re-upload the whole scene to the GPU every frame, they don't recreate objects all the time. Immediate mode was only used in, like, the GL 1.x times.
An immediate mode GUI is defined by writing an update function that draws the UI, passing in data and conditionally running callbacks for interactive elements like buttons. There is no model saved in memory, rather the structure of the UI is implied through the code path taken by the update function. The UI library uses this function to both draw the UI and handle user interactions.
In 3D you use hardware acceleration that is 100-200 times more energy efficient than drawing in the CPU. But you loose flexibility.
It is actually way cheaper to actually clear the screen and redraw it again each 1/60 of a second than to complicate the design of the drawing.
If you make it complex, the GPU could not draw it, because the GPU is not flexible, so only the CPU could draw it.
With GPUs, those tricks do not make sense at all.
In the past, there was a lot of optimization for things like drawing windows on the screen, things like circular buffers, "happy ideas" everywhere that made it efficient in the CPU at the expense of complexity, that the CPU could handle.
Imagine solving a mathematical equation. With matrices we fill a table with zeros that represents the elements of the table that do not exist. This is inefficient, but with a matrix a machine can solve it automatically without "thinking".
Immediate Gui uses the same concept, it draws without caring for last frames or states, making it way simpler.
Immediate doesn't mean just redrawing everything on every frame, immediate means what's described in the article — not keeping state. WR aggressively relies on kept state to optimize rendering of each frame as much as possible.
That is how we used to do it on 8 bit and 16 bit platforms, before frameworks like Turbo Vision, GEM and Workbench came into the scene.
1. The GUI needs to be overlaid on the game image (OpenGL/DirectX). This is difficult with traditional GUIs like QT.
2. The GUI needs to be updated in sync with the game, again, it's difficult to integrate traditional GUIs event loops into the game loop, especially with stuff like double/triple buffering.
3. The GUI needs to be as fast as possible, games are severely CPU bound.
A retained mode GUI is typically easier to use, convenience is not why people use immediate mode GUIs.
It's worth pointing out that the immediate/retained split doesn't apply only to the GUI - there are retained mode graphical APIs - DirectX used to have one. They are only used in low-demand games, they sacrifice a lot of speed for the convenience of using a retained mode.
1. It is very possible to write a retained-mode GUI in a graphics API like DirectX or OpenGL. In fact, a retained GUI would typically wipe immediate GUIs in terms of performance in this context. In immediate mode, the GUI's vertex buffers need to be completely reconstructed from scratch every single frame, which is slow, CPU bound, and cannot be (easily) parallelized. It's like reconstructing the game world every frame -- that would be ludicrous for any non-trivial game.
2. I don't think there would be that much of a difference between the two UI models, since data updates can be dispatched from the event loop. It would be faster, too, because only UI components that need updating could be redrawn. This is far faster than updating the entire UI every single frame.
3. As mentioned earlier, immediate mode GUIs are going to be a lot slower than retained mode, when implemented properly. Immediate mode GUIs put most of the work on the CPU instead of offloading most of the work to the GPU like in the retained model.
I think developers that are using immediate mode GUIs are doing so because of their ease of use. I think retained mode is typically harder for a game developer to conceptualize because immediate mode is conceptually similar to a game loop. Also, I don't know of any free & open source retained mode GUIs for DirectX and OpenGL and the like.
Also, DirectX at least (and probably OpenGL) encourages a retained-like model for general rendering. The only way to get decent performance is to re-use vertex buffers between frames and only update them when something changes.
(Every UI I worked on actually did redraw everything every frame anyway. It's really not a big deal. Your average GPU can draw a monstrous amount of stuff, something quite ridiculous, and any sensible game UI that's actually usable will struggle to get anywhere near that limit.)
And since writing a full retained GUI is not exactly trivial, people just wrote mini-GUIs using immediate mode.
When I talked about retained mode APIs, I was thinking about scene graphs, where you say "addMesh" or "addSphere" and then just call "renderFrame". I'm aware that most game engines implement their own scene graph anyway, but it's game specific, not some generic one provided by the OpenGL/DX API.
IMGUI is easy to implement so its the easiest to put in a custom game engine.
IMGUI is not particularly fast in practice. Unity's certainly isn't. However, they are simple, which approximates performance for small cases and it also lets you write your own tuned implementation.
Traditionally screen orientations for games were pretty simple so doing layouts in a single pass was feasible. These days, when you want to fit your game on every platform you need to do a lot of layout work. At that point you're just making an incomplete retained gui implementation.
> It's worth pointing out that the immediate/retained split doesn't apply only to the GUI
Indeed. Modern game engines, and in particular almost all 3D games, use what could be described as a “retained mode” system for the rendering of the game itself — the scene graph — so it is interesting that the UI doesn't always match that.
A lot of modern games actually put the UI in with the scene graph of the game itself (IE: Unity's new GUI system)
Internal tools tend to be IMGUI, Winforms, or WPF from what I've seen. Essentially whatever the original author is most productive in. No need to be super performant because your only users are other devs with really high end systems and rarely an actual need for a constant 30/60fps.
Your engine likely already has a graph structure, event system, and input handling. The core pieces of a retained mode UI system are already there.
That's not actually that hard to do. You just need to split the UI on layers.
Here is for example HTML/CSS is rendered below and on top of 3D scene: https://sciter.com/sciter-and-directx/
GUIs are multi-process, multi-system, multi-clock, multi-network entities, or at least they have the potential to be. Immediate Mode GUIs are almost non-scalable by design.
Imagine a multi-system asynchronous AR collaboration environment. Now imagine that as an Immediate Mode GUI. If we had enough horsepower to do that, we'd be doing something far better with it.