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

[WIP] Replace the current text rendering engine with fontdue. #28

Closed
wants to merge 4 commits into from
Closed

[WIP] Replace the current text rendering engine with fontdue. #28

wants to merge 4 commits into from

Conversation

hasenbanck
Copy link
Contributor

@hasenbanck hasenbanck commented Oct 15, 2020

This PR is currently WIP.

The reasons why to change to fontdue are simple:

  • One of the fastest renderer / layout engine.
  • Very few dependencies.
  • Also pure Rust.

Performance against the master branch:

demo_app_minimal        time:   [167.56 us 168.24 us 168.97 us]
                        change: [-11.383% -10.846% -10.287%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  1 (1.00%) low mild
  3 (3.00%) high mild
  1 (1.00%) high severe

demo_app_full           time:   [383.38 us 384.16 us 385.01 us]
                        change: [-29.759% -28.513% -27.261%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  2 (2.00%) high mild
  3 (3.00%) high severe

label                   time:   [55.027 us 55.867 us 57.003 us]
                        change: [-8.4554% -5.9631% -3.5809%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe

This PR replaces the glyph rendering by rusttype and the custom layout engine by egui.

TODO

Bugs

  • Font align wrong
  • Some glyphs are missing with certain TTF files (Issue 1 Issue 2)
  • Word wrap after hard line brake is broken (Issue 3)

Features

  • Hyperlink underline
  • Text cursor
  • Optimize culling

Others

  • Wait for new ttf-parser and fontdue release with the fixes needed.

The reasons why to change to fontdue are simple:
 * One of the fastest renderer / layout engine.
 * Very free dependencies.
 * Also pure Rust.

This PR replaces the glyph rendering by rusttype
and the custom layout engine by egui.
@emilk
Copy link
Owner

emilk commented Oct 15, 2020

Interesting! It would certainly be nice if we could have less code in Egui.

A quick test showed slightly larger binaries (in particular the .wasm size which critical), but not enough to worry me. The benchmark certainly looks promising!

@emilk
Copy link
Owner

emilk commented Oct 15, 2020

A bit of a headsup:

On thing that I plan to add to Egui in the coming week or two is to be able to continue text on the same line, and then wrap it. This is so that one can mix different widgets etc to e.g. make a markdown viewer. For instance, maybe we first add a button:

[THIS IS A BUTTON]

And then add text continuing on the same line:

[THIS IS A BUTTON] followed by some text
which wraps at the appropriate maximum
width.

This would be the work of egui::Layout but it means the font needs some sort of argument like "start the text at a certain offset".

@emilk
Copy link
Owner

emilk commented Oct 15, 2020

Btw, I feel like I should explain the choice of the word "galley": Egui does widget layout using egui::Layout, so I wanted a word that didn't clash with it. So I thought "galley" would be appropriate: https://rhollick.wordpress.com/2010/09/20/galleys/

I'm not married to "galley", but it would be nice to avoid using the same word for different things. egui::Layout could perhaps also be renamed (WidgetPlacer? I don't know).

@hasenbanck
Copy link
Contributor Author

I will keep this branch rebased to master, so that merge should be easy once deemed ready and merge worthy.

This would be the work of egui::Layout but it means the font needs some sort of argument like "start the text at a certain offset".

What you describe is pretty advance and would most likely need an extension to the upstream project, so for now I would ask you to push back that change for the short term. It's essentially text flow around a keep out zone (it's not even line based).

I'm not married to "galley", but it would be nice to avoid using the same word for different things. egui::Layout could perhaps also be renamed (WidgetPlacer? I don't know).

We could also change the "layout" for the glyphs into something like "typeset" or "typograph".

@emilk
Copy link
Owner

emilk commented Oct 15, 2020

What you describe is pretty advance and would most likely need an extension to the upstream project, so for now I would ask you to push back that change for the short term. It's essentially text flow around a keep out zone (it's not even line based).

It's not quite that advanced – I don't need the text to wrap around an arbitrary keep-out-zone, just the ability to ask for a text layout where the first line wraps at a different maximum width from the following lines. Basically I'm talking about first-line indentation. If fontdue doesn't support it we should open an issue for it, as it really is key to implementing things like changing font or text color in the middle of a text, or embedding a small image in a text.

@hasenbanck
Copy link
Contributor Author

But your example with the button is not line based, it's pixel based. The text of the first line might start on the same height of the button, but the text after that has it's own line height, which might not match the height / margin of the button. And once you want to embed images in text, you need keep out zones and the text flowing around it.

If you want it to be line based, then a workaround would be to add whitespace before sending the text to the layout engine.

It's already possible with fontdue to mix multiple styles in a typeset:

    let styles = &[&TextStyle::new("Hello ", 35.0, 0), &TextStyle::new("world!", 40.0, 0)];
    layout.layout_horizontal(fonts, styles, &settings, &mut output);

Complex shaping is on the roadmap.

What I'm essentially asking for: Please dont create a moving target right now, since otherwise this PR would stay in perpetual flux. It's defentily possible to extend the upstream projects, since the author is definitly interrested in having more users.

@emilk
Copy link
Owner

emilk commented Oct 15, 2020

Issue submitted: mooman219/fontdue#40

@emilk
Copy link
Owner

emilk commented Oct 15, 2020

What I'm essentially asking for: Please dont create a moving target right now, since otherwise this PR would stay in perpetual flux.

I feel your concern, but I won't switch text layout engine unless it supports what I need it to support :) That's why I flagged this now: it is something I want very soon, and if it is impossible in fontdue, then you should not waste time with this PR! However, I assume it will be fairly easy to add to fontdue.

Mixing multiple font styles using fontdue is not very attractive to me as it is not immediate mode.


Just to illustrate the sort of code I'd like to be able to write (details will vary):

ui.horizontal_wrapped(|ui|{
    ui.label("This example has ");
    ui.hyperlink("a hyperlink", "https://github.com/emilk/gui");
    ui.label(" and ");
    ui.monospace("some monospace text with a tooltip").on_hover_text("I told you");
    ui.label(". It is automatically wrapped at some max width.");
});

This example has a hyperlink and some monospace
text with a tooltip. It is automatically wrapped
at some max width.

This produces proper result with HiDPI screens.
The font sizes have also been adjusted, so that
the final result matches the old sizes.
@emilk
Copy link
Owner

emilk commented Oct 21, 2020

Another thing I'd like to add to Egui one day is the ability to opt-in to mesh-based font rendering instead of texture based. This is so that Egui could be used in a 3D world without having blurry text as you get close to it (think VR).

Do you think Fontdue would make that easier or harder, or no different?

@hasenbanck
Copy link
Contributor Author

Another thing I'd like to add to Egui one day is the ability to opt-in to mesh-based font rendering instead of texture based. This is so that Egui could be used in a 3D world without having blurry text as you get close to it (think VR).

Do you think Fontdue would make that easier or harder, or no different?

You don't want to render your UI text as 3D meshes. That would most likely destroy your performance.

For VR and resolution independent applications you most likely want to implement SDF (Signed distance field) based font rendering. I know that the author of fontdue did play with the idea to support those, since they are essentialy still bitmap (with more information). But I don't know the current roadmap.

@emilk
Copy link
Owner

emilk commented Oct 21, 2020

I actually do know what I want! I want a simple renderer so that Egui is easy to integrate into any engine. Luckily, rendering triangles is something most computers are really good at.

Vector based fonts has downsides (especially for aliasing), but so does SDF based fonts (lack of sharp edges). An SDF approach would also pose problems for how to have a single shader that works for all the Egui primitives. It opens up a big can of worms.

Anyway, my current idea would be to pre-tesselate each character using e.g. lyon (just like the current code pre-rasterizes each character), but otherwise keep the text layout algorithm mostly the same. This is not something I will get to soon, but I want to keep my options open.

@emilk
Copy link
Owner

emilk commented Dec 10, 2020

I'm gonna close this - I'm not sure fontdue is the right fit for Egui, because it is still uncertain exactly what Egui needs. I appreciate the effort, but Egui is evolving fast, and it is helpful to me to keep things simple for now, which means rolling my own text layout.

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

Successfully merging this pull request may close these issues.

2 participants