mikke89 / RmlUi

RmlUi - The HTML/CSS User Interface library evolved
https://mikke89.github.io/RmlUiDoc/
MIT License
2.74k stars 299 forks source link

Heavy load on applying kerning with lots of characters #498

Closed PerikiyoXD closed 2 weeks ago

PerikiyoXD commented 1 year ago

imagen imagen

Statistics from VS

Nombre de la función CPU total [unidad, %] Propia CPU [unidad, %] Módulo Categoría
||||||||||||||||||||||||||| + Rml::FlexFormattingContext::Format 1605 (30,47 %) 0 (0,00 %) iiigamemodule
|||||||||||||||||||||||||||| + Rml::FlexFormattingContext::Format 1605 (30,47 %) 0 (0,00 %) iiigamemodule
||||||||||||||||||||||||||||| + Rml::FormattingContext::FormatIndependent 1599 (30,36 %) 0 (0,00 %) iiigamemodule
|||||||||||||||||||||||||||||| + Rml::BlockFormattingContext::Format 1599 (30,36 %) 0 (0,00 %) iiigamemodule
||||||||||||||||||||||||||||||| + Rml::BlockFormattingContext::FormatBlockContainerChild 1598 (30,34 %) 0 (0,00 %) iiigamemodule
|||||||||||||||||||||||||||||||| + Rml::BlockFormattingContext::FormatInlineBox 1598 (30,34 %) 0 (0,00 %) iiigamemodule
||||||||||||||||||||||||||||||||| + Rml::BlockContainer::AddInlineElement 1598 (30,34 %) 0 (0,00 %) iiigamemodule
|||||||||||||||||||||||||||||||||| + Rml::InlineContainer::AddInlineElement 1594 (30,26 %) 0 (0,00 %) iiigamemodule
||||||||||||||||||||||||||||||||||| + Rml::LineBox::AddBox 1591 (30,21 %) 0 (0,00 %) iiigamemodule
|||||||||||||||||||||||||||||||||||| + Rml::InlineLevelBox_Text::CreateFragment 1591 (30,21 %) 0 (0,00 %) iiigamemodule
||||||||||||||||||||||||||||||||||||| + Rml::ElementText::GenerateLine 1585 (30,09 %) 1 (0,02 %) iiigamemodule
|||||||||||||||||||||||||||||||||||||| + Rml::FontEngineInterfaceDefault::GetStringWidth 1321 (25,08 %) 0 (0,00 %) iiigamemodule
||||||||||||||||||||||||||||||||||||||| + Rml::FontFaceHandleDefault::GetStringWidth 1292 (24,53 %) 60 (1,14 %) iiigamemodule
|||||||||||||||||||||||||||||||||||||||| + Rml::FontFaceHandleDefault::GetOrAppendGlyph 906 (17,20 %) 239 (4,54 %) iiigamemodule
||||||||||||||||||||||||||||||||||||||||| + robin_hood::detail::Table\<1,80,enum Rml::Character,Rml::FontGlyph,robin_hood::hash\<enum Rml::Character,void>,std::equal_to\<enum Rml::Character> >::find 541 (10,27 %) 35 (0,66 %) iiigamemodule
|||||||||||||||||||||||||||||||||||||||||| + robin_hood::detail::Table\<1,80,enum Rml::Character,Rml::FontGlyph,robin_hood::hash\<enum Rml::Character,void>,std::equal_to\<enum Rml::Character> >::findIdx\<enum Rml::Character> 473 (8,98 %) 124 (2,35 %) iiigamemodule
||||||||||||||||||||||||||||||||||||||||||| + robin_hood::detail::Table\<1,80,enum Rml::Character,Rml::FontGlyph,robin_hood::hash\<enum Rml::Character,void>,std::equal_to\<enum Rml::Character> >::keyToIdx\<enum Rml::Character const &> 298 (5,66 %) 50 (0,95 %) iiigamemodule
|||||||||||||||||||||||||||||||||||||||||||| + robin_hood::hash\<enum Rml::Character,void>::operator() 224 (4,25 %) 11 (0,21 %) iiigamemodule
||||||||||||||||||||||||||||||||||||||||||||| + robin_hood::hash\<char32_t,void>::operator() 203 (3,85 %) 11 (0,21 %) iiigamemodule
|||||||||||||||||||||||||||||||||||||||||||||| + robin_hood::hash_int 137 (2,60 %) 65 (1,23 %) iiigamemodule

robin_hood stealin' those cycles?

It seems it's taking a lot of CPU usage when I'm rendering "Out of the screen", happens when I input long strings in the game chatbox and takes too much height.

If you need anything, don't hesitate on asking!

mikke89 commented 1 year ago

Hey, thanks for reporting. First of all, does this happen on every rendered frame, or only when changing the text?

How much text are we talking about here? It would be very helpful if you could post the text and the font. Even better with a sample document which reproduces this behavior.

Looks like you are using flexbox, is that two nested ones I'm seeing there? Did you try to experiment a bit with their performance considerations, as listed in the documentation here?

PerikiyoXD commented 1 year ago

This happens everytime the document needs to be updated (Using T to open the chatbox textinput, typing in the textinput, updating the text...), so "when changing the text" is more like it.

I've probably not read the "performance concerns". I'll investigate further the link you provided.

This is the chat.rml we use: chat.zip

PerikiyoXD commented 1 year ago

Also I've been struggling to make a static height chatbox, so probably using other CSS rules for the chatbox might remove this problem. I'm fine having a static width chatbox, or even resizable with a drag element or some sort of command.

PerikiyoXD commented 1 year ago

imagen When I use that aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa multiple times, it'd lag the chatbox. It's expanding vertically if the messages are long so it always shows N last chatlines.

mikke89 commented 1 year ago

I did some investigations and measurements. I have some questions, comments, and suggestions:

  1. Are you running in debug or release mode by the way? I seem to get a lot more inlining than you. And normally there should not be any noticeable lag with the little text shown in the screenshot.
  2. Or is there text above what is shown, and the window is scrolled down? If you keep the history forever then it will get slower and slower, so I suggest removing old history if you don't already do that.
  3. I don't really see any reason for using flexbox in this case. With these properties, the flexbox has to essentially format every element twice. Instead, normal block layout should be more than sufficient to achieve this layout, and you'd get double the speed with this simple change. Feel free to ask if you need some help with that.
  4. From my measurements, this has little to do with kerning. For comparison, glyph lookup is ~12%, while kerning lookup is ~3%.
  5. I tested with your font as well to rule out any issues there. In fact, that one seems to be slightly faster than our built-in font, but nothing standing out.
  6. I'm getting a load-update-render time of about 500µs for your chatbox with more text than in your screenshot. This shouldn't really be noticeable.
  7. Edit: I tested with very long aaa... words (125 characters), and I see a lot of time is spent trying to break up the word here. It's a lot faster if you break up the long word. We could improve the time complexity of this algorithm to better handle long words, but this also seems like a very artificial case to optimize for. Perhaps an area that could be looked into though for more realistic cases too.
PerikiyoXD commented 1 year ago

I'm sorry for the delay in my response.

  1. Debugging. I will try to compile in "Release" mode and make a comparison.
  2. The chat implements a LIFO buffer for Rml lines. I don't think this is a problem. I assume RmlUi can handle at least 100 backbuffer lines successfully.
  3. I agree to explore other ways to achieve an "adaptable" chatbox. Please provide me with some ideas and examples of how we can create a nice chatbox without relying on "flex."
  4. Probably, the issue here is the lack of optimization in some glyph-related functions (probably the algorithm that separates words when they are too long) when using flexbox, rather than kerning as the title suggests.
  5. Great!
  6. Hopefully, with a simplified version, it will work even faster.
  7. This case came up when I was trying to create an anti-spam system for the game. Basically, I'm considering long words and phrases in the chat, even copy-pasting (up to 512 characters). It's not very common, but it shows that some uncommon events can reveal areas for improvement.
mikke89 commented 1 year ago

I see. It doesn't really make sense to report library performance issues in debug mode, that depends entirely on your specific environment. You probably want to look into how to speedup your debug builds first.

I've made a note for this specific case of long words with word breaking, it's not a top priority for me right now though.

I agree to explore other ways to achieve an "adaptable" chatbox. Please provide me with some ideas and examples of how we can create a nice chatbox without relying on "flex."

Of course, this depends entirely on your needs. But based on your screenshot, set display: block for each new chat entry. If you want the chat box to grow with the content, you can simply wrap all the chats inside another block box. Here is a basic prototype to get the idea across.

<style>
  div { display: block; }
  .outer {
    background: #0003;
    margin 5px;
}
</style>

<body>
  <div class="outer">
    <div>user a: message</div>
    <div>user b: message</div>
  </div>
  <input type="text" class="chat_entry"/>
</body>

If you want a fixed height, just set a height on the .outer class. Or maybe you want a max-height instead. If you want to have a scrollable box, you can set overflow: auto. Then you'll also have to scroll it to the bottom through the API every time a new chat message is added if you want it to stick to the bottom, unless the user has already scrolled up. You can also add <handle>s to make the chat box resizable.

mikke89 commented 2 weeks ago

I am doing some cleanup of old issues. I am closing this one as it is a bit too unfocused. We did identify one performance issue here, so I appreciate the issue being reported, thanks! For now I have made a note of this issue, while it's not a top priority, hopefully we'll get around to it.