Whats the best caching crate out there for GUI systems?

If I were to create a GUI library out there (that has caching capabilities), what would be the best caching crate out there?


For example with egui, see if I move the slider, everything has to update, even the Click each year button (where the red arrow is pointing to) which is a waste of CPU usage. What I wanted to do instead is if I move the slider, the slider only gets updated/redrawn, all the other elements don't redraw and remain static/cached.

I am looking at lru


But I am not too sure if this is a good crate to even use for such situations?

Depending on the specific details of the application, doing this kind of bookkeeping for caching could end up being more work overall than just redrawing what's visible.

The example given in Could ImGUI be the future of GUIs? (greggman.com) is quoted below.

So for example you've got a UI like Microsoft Excel. It has 75 toolbar buttons and a spreadsheet showing 300 cells. The input cursor is in cell E7 and it's blinking. If you go back to Windows 3.0 (and earlier) the CPU is drawing pixels (GPUs didn't exist). The GUI system determines that some tiny area only the size of the cursor itself needs to be redrawn and only redraws those pixels directly into screen memory. Similarly if type a letter the system can figure out only cell E7 was updated only cell E7 needs to be redrawn.

On a 1993-1994 computer that was important. The computer could not draw the entire screen at 60fps.

So, that's the best case for a traditional scenegraph based object oriented retained mode GUI

Note that the system still had to walk a giant portion of the GUI scenegraph to compute what the smallest area of affect is. That might not be as much work as re-drawing every pixel but it is lots of work none the less

And for a datapoint in practice, I can run the egui_demo_app (all example windows open) with the wgpu backend on modern hardware at over 1,500 frames per second. Caching would not improve this; my monitor doesn't have a 1500 Hz refresh rate. Enabling Vsync to render only frames that can actually be displayed is good enough. The 1% of CPU utilization and 6% GPU utilization with this setup doesn't feel all that wasteful. There's enough headroom for business logic.

(Yeah, ok, a 100% perf boost to 3,000 fps would be an improvement, but it would have far less impact than a 100% improvement from 30 fps to 60 fps.)

That said, maybe you are running the app on 1993-1994 computer and would benefit from not writing every pixel on every frame. In this case, you might look into what they are doing with sixtyfps. For egui specifically, there is an open discussion for avoiding unnecessary re-rendering in Cache bounding boxes for "senses" · Discussion #724 · emilk/egui · GitHub.

how come iced, druid and even Bevy (once they implement their new UI) especially has caching capabilities?

I see where you are coming from, I know that it is highly optimised, hell it is even more optimized than GTK and uses even less CPU resources which is great and any day I would use egui over GTK. I even tried their demo and it was great, I loved it.

However the problem is that I am more concerned about power consumption on a battery operated device. If somebody heavily uses a software that uses egui and the software is used all day, then it will have a significant impact on battery compared to using something that supports caching capabilities. Especially if they are just making menus, and buttons and stuff.

Looks very interesting, in regards to sixtyfps, is it backend agnostic and does it give you freedom to code the way you want to or does it lock you into some kind of framework like iced and Bevy does? Addtionally is it immediate or retained or a combination of both (which is what iced is)?

Interesting mate, the guy metentions he will implement such a feature, do you happen to know if it has been implemented yet?

We can save the bounding boxes of widgets during rendering in lists (or maybe a binary tree) for each sense type. So in the next frame, we could cheaply check if the GUI will respond to user input and skip all rendering if not needed.

I am interested in implementing it in the future

Both iced and druid cache layouts. There is some benefit to this, since layout can be very expensive if the application designer is not careful. In particular, their layouts form part of the basic scene graph (like the one quoted above). Any updates to a deeply nested layout may end up affecting the layout of ancestor and sibling nodes in the graph. Caching can absolutely make sense for some applications with these libraries.

(It is worth mentioning that iced uses the Elm architecture, which shares some similarities with React. React helped popularize the Virtual DOM concept, which is also a lot like the "caching" you are asking about. The Virtual DOM is also the GUI's scene graph in practice. iced doesn't have a Virtual DOM, with the exception of iced_web.)

bevy_ui is tightly coupled with Bevy's ECS, which acts as the GUI's scene graph. That's about as close as I am aware of any caching in this library. I don't have a whole lot of experience with this one, but I have used Bevy, and I know that components can form a hierarchy with children positioned relative to parents, which is what bevy_ui relies on. Apparently, it uses flexbox for layout but I don't know how it works.

In short, these libraries all have scene graphs for the GUI elements which persist from frame-to-frame. At least in part; in iced the layout cache persists but the views do not.

However, and this is where things get fuzzy, iced does not cache any of the element contents (text, images, widget state). That's the application's job; the application owns the widget state. The application is effectively the cache. egui takes the same approach. Your application owns the text, images, and widget state contents, it is effectively also a cache. Both crates redraw the entire GUI every frame (this is why iced_web uses a Virtual DOM). In fact, iced rebuilds the views twice on every update, because reasons. The biggest difference is that egui also recomputes the layout while drawing. (This is what the linked egui discussion wants to implement.)

Bevy, being a game engine, also redraws the entire GUI on every frame.

I guess the real question is what do you really want to cache? The devil is in the details and making a claim that one crate caches and another does not is more than a little deceiving. Sometimes there are multiple levels of caching hidden from the user (iced layout cache + dodrio Virtual DOM with iced_web) and sometimes the "cache" is just the application-owned state.

I forgot to mention in my first response that the "1% CPU and 6% GPU utilization" was with the "Continuous Mode" enabled in egui_demo_app. When enabling "Reactive Mode", the utilization was 0% for both hardware resources until the cursor is moved.

I don't think GTK is hardware accelerated. At least, not in the same sense that iced and egui are with their wgpu backends. These GUI crates are basically doing the same work as a game, as far as drawing goes.

On a mobile device (assuming you mean an Android phone or tablet) you don't have a cursor, so you don't have to worry about spurious events that are unrelated to your application, like mouse movement. If the user is literally swiping their finger for 8 continuous hours, then yes, you will have to be concerned about things like overdraw.

But it is still not clear if rasterized pixels are the only things you want to cache. GPUs can be more efficient than CPUs with chores like rasterization. And of course, caching has its own problems (and especially, at what point does bookkeeping for a cache become more expensive than computing the result?)

My understanding with sixtyfps is that it is fully retained. I haven't used it (and I don't have any interest in it, personally) but it's an option that is available.

It has not been implemented. I don't really think it's a top priority because native desktop apps are its primary focus. And we basically have infinite resources there.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.