Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Text shaping, font fallback, and iced_wgpu overhaul #1697

Merged
merged 50 commits into from
Feb 24, 2023
Merged

Conversation

hecrj
Copy link
Member

@hecrj hecrj commented Feb 10, 2023

This PR introduces support for text shaping and font fallback (#33), as well as it improves the architecture of iced_wgpu to get rid of bottlenecks that were present since the inception of the library (#41).

cosmic-text and glyphon

The wgpu_glyph dependency in iced_wgpu has been replaced with glyphon, a fairly new text renderer for wgpu that has a better-suited architecture for layering and caching.

Recently, glyphon landed integration with cosmic-text (grovesNL/glyphon#23), which provides advanced text shaping, layout, and rendering wrapped up into a simple abstraction. cosmic-text has been a long-time-missing piece in the Rust GUI ecosystem and the folks at System76 are breaking ground to make the ecosystem thrive!

What does this all mean? It means iced is now capable of rendering more complex scripts, automatically finding missing glyphs in your system fonts (no more tofu!), and selecting fonts based on their family.

For instance, we can now handle emoji 🥳:

image

iced_wgpu overhaul

Since the inception of the library, iced_wgpu has been limited by the (now gone) wgpu_glyph dependency. The main issue with wgpu_glyph is that it couples uploading new data to the GPU with recording draw calls. This architecture is forced by glyph-brush, an internal dependency originally designed with OpenGL in mind that deals with text and glyph caching.

The architecture of wgpu_glyph becomes a problem when combined with layering. If we want to draw multiple layers of text with other kinds of primitives between them then we have to issue multiple text draw calls (a depth buffer would break alpha blending). But since uploads and draw calls are coupled, we are forced to issue a complete new RenderPass for every layer of text!

And it gets worse! glyph-brush couples cache trimming with drawing as well. So not only we have to issue a whole new RenderPass and upload data between draw calls, but every layer overwrites the cache of the previous one. Not good.

glyphon has a completely different architecture. For starters, it has a first-class TextAtlas which represents the glyph cache. Then, it has a TextRenderer type with two different methods: prepare and render. prepare deals with preparing buffers and updating the TextAtlas, while render just issues a draw call.

This separation is great because it lets us completely redesign the architecture of iced_wgpu to split rendering into prepare and draw stages and reuse a RenderPass as much as possible (no uploads in between!). Furthermore, a TextAtlas can be shared with many TextRenderers (i.e. layers!) as needed.

Memory usage should also be considerably lower, since we are no longer forced to use a StagingBelt to upload between draw calls.

Deprecation of iced_glow and iced_glutin

As of now, glyphon only works with wgpu. In order to bring these improvements over to the iced_glow renderer, we would need to add glow support for glyphon (or fork it). This would take a considerable amount of effort.

As time goes on, maintaining iced_glow is becoming quite a headache. OpenGL is full of pitfalls and ensuring compatibility with older hardware while also taking advantage of more modern one is a pain.

Because of this, I have decided to deprecate iced_glow and iced_glutin. I am just removing the crates from the repository for now. But by all means, if someone is interested in maintaining them further I will gladly give you a repository in the iced-rs organization.

Instead of working on maintaining a renderer with a graphics API that is on its deathbed, I prefer to focus on implementing a proper software renderer. This should increase the compatibility of the library even further; allowing it to run not only in older hardware but also in environments with no GPUs (e.g. virtual machines).

Next steps

These changes are not ready for master yet! They will be merged into a new advanced-text branch for now.

First, it's very likely there are a bunch of bugs! While we may be able to draw complex scripts now, we are certainly not capable of editing them. The TextInput logic has not been changed.

Also, the vectorial text support (#1610) needs to be merged and integrated with these changes.

Finally, since removing iced_glow decreases the compatibility of the library considerably, I would like to have a software renderer before these new features can land in master.

@hecrj hecrj added improvement An internal improvement feature New feature or request text performance widget rendering wgpu labels Feb 10, 2023
@hecrj hecrj added this to the 1.0.0 milestone Feb 10, 2023
@thunderstorm010
Copy link
Contributor

Woo! 🎉

@@ -32,32 +34,21 @@ pub struct Text<'a, Font> {
pub enum Hit {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could remove this and just have the CharOffset(usize) as a struct to avoid the match in the impl, unless you plan on adding more variants back!

@@ -344,7 +344,7 @@ where
let size = {
let intrinsic = Size::new(
0.0,
f32::from(text_size + self.padding.vertical())
(f32::from(self.padding.vertical()) + text_size * 1.2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 1.2 value is used in a few different calculations; might it be better suited as a font::DEFAULT_LINE_HEIGHT or something const?

Copy link
Member Author

@hecrj hecrj Feb 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have plans to make this configurable somehow. Still figuring it out.

@liyixin123
Copy link

I am still experiencing tofu font issues when using iced::widget::Text. Will this change fix it?

@liyixin123
Copy link

I am still experiencing tofu font issues when using iced::widget::Text. Will this change fix it?

I didn't understand your previous message. After replacing widget::Text with advanced::widget::Text and enabling Advanced Shaping, it is now working properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants