I just attempted to write up a simple Minesweeper game with Iced. No bells or whistles, but it works:
https://github.com/veniamin-ilmer/minesweeper
On one hand, I find it pretty cool I built a clear cross platform GUI with actual functionality, within only 200 lines of code.
On the other hand, I can’t help but see how inefficient it seems. If I ever need to modify any of the objects, I need to redraw all of them. The structure of the view
method makes me think it is very difficult for Iced to maintain a “virtual DOM” to only make delta changes.
Am I wrong? Is there a more efficient way to do this in Iced?
Edit: I just found this - https://github.com/iced-rs/iced/pull/1284
As the library matures, the need for some kind of persistent widget data (see #553) between view calls becomes more apparent (e.g. incremental rendering, animations, accessibility, etc.).
If we are going to end up having persistent widget data anyways… There is no reason to have impure, stateful widgets anymore!
So it seems like Iced plans to have an internal “persistent widget storage”, which in abstracted away from the user. But it is quite unclear to me how they would accomplish this, considering the view
method doesn’t provide an ID for any of its objects, so it would not be easy for Iced to determine the difference between updates.
As long as you store state in your application’s struct so that there’s no need to calculate and allocate in the view, then the view is quick to generate. You can also use lazy widgets that will avoid being re-recreated unless a given input has changed. Have you benchmarked to compare? You’d be surprised how efficient it is compared to heavier frameworks like GTK.
mmstick, it is always good to hear from you. You frequently provide a huge depth of knowledge in your comments.
I come from a windows background. If I want to make a memory efficient GUI, I always used native windows GUI libraries. All other frameworks have always seemed like they took up much more memory and CPU. This always annoyed me.
My little Minesweeper game is taking 78 MB of memory (with --release).
At the same time, Excel is only 2.5 MB, Notepad++ is only 1.9 MB.
Recently I found out that a lot of memory and CPU is used up simply to communicate with the GPU. I am confused about this… Does Excel not use the GPU?
I am sorry, I feel like I am starting to rant here. This memory issue has been annoying me for a while, and I have not heard anyone provide a clear reason why these complicated apps seems to take up much less memory than any simple cross-compatible app I build.
The application crashes when I start it, perhaps due to wgpu. After switching to
iced = { git = "https://github.com/pop-os/iced" }
, I’m able to run it and see 7.7 MB memory consumption. We use a software-based renderer by default. Keep in mind though that Windows also doesn’t accurately report memory usage, and in Rust we have everything statically-linked into the binary. And there’s a lot of stuff being embedded and cached in memory at the moment as these libraries are being developed.
Last time I checked, GTK could do laziness well where it matters (lists /trees), but admittedly that was some time ago.
I’m mainly referring to how GTK is a very high level toolkit with many layers of abstractions and a number of complex functionalities built-in by default. Whereas iced in comparison is a low level library where you have to bring your own toolkit, or do it raw.
You might be surprised by how heavy some of the more established GUI toolkits are, which you don’t see because the abstractions are hidden from view in dozens of event loops running asynchronously in the background on the same local thread.
Iced widgets are collectively compiled down to a single state machine that pushes messages to a streamlined singular event loop. Commands and subscriptions are spawned on background thread(s). So the API by design keeps the UI thread free to focus on the UI.
The Elm model gives you freedom to manage all of your memory in a central location efficiently. So you can easily render a complex responsive UI at 240 FPS even if you’re using software rendering. How fast it renders is going to depend on how you cache and reuse your data.
You can pass data by reference to widgets so they’re drawn without allocating. You can load images on a background thread and cache them in your app struct so they only need to be decoded once. You can use lazy widgets, and even watch window dimensions to render most things in a single pass. Allocations are unfortunately required when you’re attaching multiple elements to a container, but that can change once Rust’s allocator API is stabilized.
iced does also internally cache some things itself between frames so they don’t have to be re-drawn. And it’ll surely get more sophisticated in this over time. Especially once damage-tracking is fully implemented.
And if you want a higher level abstraction with platform integrations built in, libcosmic is coming around the corner as a high level platform toolkit for making COSMIC applications with iced that’s tightly integrated with the COSMIC ecosystem. So you don’t have to worry about how you’re going to render client-side decorations, integrate with window manager protocols, implement theme support, configuration APIs, accessibility, etc.
The main issue I’ve come across experimenting with Rust UI frameworks is that none of them seem to have a decent multiline / rich text field story. Iced and the like are nice for simple apps but if you need to e.g. input Markdown and render it nicely all in one field you’re out in the wilderness.
That’s been resolved by cosmic-text, which is being used by iced now. The cosmic-text crate provides a text editing widget, too.
Indeed, a lacking multiline text is what stopped me from trying to use it as well. But with the minesweeper example, I thought “it’s just a bunch of buttons, surely this is simple enough for me to build?”
But no, the button widget doesn’t support right clicks or double clicks, which limits the functionality I can build into minesweeper.
Overall, I love how simple Iced code ends up being, which makes me think about contributing to this project. Only issue I have with it is this seeming inefficiency.
Have you looked at Bevy before? There is a popular Rust YouTuber that posts videos of his Bevy projects. Definitely look there for inspiration.
Bevy is awesome, but I don’t think it’s really designed to be used as a retained-mode UI framework. I know it’s on their roadmap to support traditional UIs (or at least was at one point), but as it is right now, it’s primarily useful for making games which require regular re-renders of the entire screen.
Care to say who they are?
Tantandev