slint-ui / slint

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
https://slint.dev
Other
17.54k stars 600 forks source link

Very slow build times w/ embedded fonts? #2100

Closed A0lson closed 1 year ago

A0lson commented 1 year ago

As an early first project, I made a simple "clock" that simply contains two text boxes (with differing font sizes) using a single embedded font. I'm using the software renderer so I can target the Linux framebuffer without a full X11 environment or other libraries.

I see the generated code contains 184 code_point items and 552 BitmapGlyphs.

It takes about 90 seconds to build. Is this to be expected? This also becomes problematic as Microsoft Code frequently runs "cargo check" which triggers build.rs seemingly every time.

Since I'm not changing the font or the UI all that often, it'd be nice to only pay the build cost once, but not sure how. (Moving the UI to a lib crate didn't help either).

tronical commented 1 year ago

Yes, I think that is kind of expected or hard to avoid as-such. In the environment that you're describing, you wouldn't really need this feature though, as you have a file-system and Slint could load the .ttf fonts directly from disk at run-time. The same goes for the embedded image textures. I think that you can simply remove the use of EmbedResourcesKind::EmbedForSoftwareRenderer in your build.rs. If you're using Slint from git that should work, and we plan to make a new release soon that should have the necessary feature for that to work. Could you give it a try?

ogoffart commented 1 year ago

Code frequently runs "cargo check" which triggers build.rs seemingly every time

I wonder why it re-runs build.rs though. It shouldn't rerun it if UI files have not changed.

Also, what is the slow part? Is it the rust compilation or the build.rs itself? If the later, one thing that could be done is ensure that slint-build is built in release mode.

Otherwise we would also need to profile the run of build.rs to find out what takes time. Maybe there is low hanging fruits for optimizations. If it is the font generation, we could perhaps cache the font data in a separate rust file that wouldn't be generated all the time

tronical commented 1 year ago

If the later, one thing that could be done is ensure that slint-build is built in release mode.

Yeah, it would be interesting to know what the impact on that is in your case, @A0lson . I think you could add this to your Cargo.toml:

[profile.dev.build_override]
opt-level = 3

More generally, could you try to see what cargo build --timings reports? (see also https://doc.rust-lang.org/cargo/reference/timings.html#reporting-build-timings )

A0lson commented 1 year ago

Thanks for the suggestions. The build override setting didn't seem to help. I tried adding the build_override to both my Cargo.toml as well as base/api/rs/build/Cargo.toml, but it didn't seem to have much effect.

Even building in release mode has about the same timings.

Unit Total Codegen
1. clock_ui v0.1.0 build script (run) 86.1s
2. clock_ui v0.1.0 16.8s 1.1s (6%)
3. clock v0.1.0 bin "clock" 1.9s
real    1m45.228s                                                                                                                                                                                                                                                                                                                                                                                                                          
user    0m56.463s                                                                                                                                                                                                                                                                                                                                                                                                                          
sys     0m53.048s   

I think what I didn't realize before is that this problem may be somewhat self-inflicted as I am using 350px and 250px fonts, which is probably creating large per-character bitmaps.

Reducing the fonts to 35px and 25px results in :

  Unit Total Codegen
1. clock_ui v0.1.0 build script (run) 1.9s
2. clock v0.1.0 bin "clock" 1.8s
3. clock_ui v0.1.0 1.2s 0.5s (39%)
real    0m5.446s
user    0m7.824s
sys     0m1.443s

I tried out the systemfonts feature and it works great. Compilation times are down to just a few seconds with the large fonts and it works normally. I did have to make a small change as the Rust feature wasn't visible at the top level:

diff --git a/api/rs/slint/Cargo.toml b/api/rs/slint/Cargo.toml
index 89fd7eb7f..3f2b7a4e8 100644
--- a/api/rs/slint/Cargo.toml
+++ b/api/rs/slint/Cargo.toml
@@ -56,6 +56,10 @@ log = ["dep:log"]
 ## **Safety** : You must ensure that there is only one single thread that call into the Slint API
 unsafe-single-threaded = ["i-slint-core/unsafe-single-threaded"]

+## Enable this feature to allow the software-renderer to use system fonts,
+## instead of requiring them to be embedded
+systemfonts = ["i-slint-core/systemfonts"]
  Unit Total Codegen Features
1. clock v0.1.0 bin "clock" 5.2s  
2. clock_ui v0.1.0 1.8s 1.2s (65%)
real    0m8.024s
user    0m11.880s
sys     0m0.930s

Thanks!

tronical commented 1 year ago

Thanks!

Regarding the feature, perhaps we should unconditionally enable it if std is available. Olivier, what do you think?

ogoffart commented 1 year ago

It should be included by default when using the winit backend with the sw renderer.

I don't think we should enable it otherwise because it would bloat for everyone for something that use the GL backend only.
But we should have a way to enable the feature from the slint crate.

tronical commented 1 year ago

Fair point. @A0lson what features are you currently enabling in the slint crate in your Cargo.toml?

A0lson commented 1 year ago

I was building with features = ["std", "compat-0-3-0"] (before enabling systemfonts)

tronical commented 1 year ago

Thanks.

@ogoffart i think this highlights the situation well. By default we include the software renderer in the API and when using it in an std environment it is practical useless for text (when you have a file system the bitmap fonts really don’t make sense). Dependency wise the only dependency this adds over the GL backend example is fontdue, which in turn depends on ttf-parser (ok, we do, too) and hashbrown (that’s new).

I’m tempted to replace the use of fontdue with crossfont though, as winit uses that, too, when enabling Wayland client side decorations (which IMO we should).

IMO the software renderer should provide text out of the box on Linux etc, without having to enable an additional feature.

A0lson commented 1 year ago

Feel free to close this issue when you see fit, the systemfonts feature is working great for me.

tronical commented 1 year ago

We couldn't enable system fonts by default, but we've now added a software-renderer-systemfonts feature to the crate.