Search Unity

TMP (TextMeshPro) massive CPU time for TMP.GenerateText on Android

Discussion in 'UGUI & TextMesh Pro' started by Mowtine, Oct 19, 2021.

  1. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19
    So this is the profiler on android when I open one of my tables with ~100 cards on it with ~7 TMP objects on each card:
    upload_2021-10-20_0-16-47.png
    This is obviously unacceptable for an android build, but I can't seem to find any information about why its so inflated and/or if there is any workaround other than having to create a complete second project for android which does not use TMP (and does not have any fancy text).

    If it matters, my TMP materials are:
    upload_2021-10-20_0-20-15.png
    None of the "Mobile" TMP materials are usable here because they either
    1. Don't allow the texture or
    2. Make the text just black and stop it being maskable.

    My Q here is:
    1. Why do I get 9K TMP.GenerateText calls? Could reducing that solve this problem?
    2. Is there any solution for this other than having to make a completely different code source for android?

    Thanks.
     
    dan_ginovker likes this.
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Are you using Text Auto Size on those text objects?

    Text Auto Size is very expensive as it requires multiple re-layout of the text to figure out the optimum point size. There are ways to optimize this if you are using Auto Size on all text objects.

    Are you using Layout Components on the objects?

    Similar to Text Auto Size, layout components can result in multiple layout passes. However, this is fine in most cases. However, using Layout components + Text Auto Size = lots of layout passes resulting in much more overhead.

    Are you using Dynamic or Static font assets?
     
    Last edited: Oct 19, 2021
  3. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19
    Yes! I am using Text Auto Size and using it on all text objects to fit with any resolution. How can I optimise this?

    Yes I am also using layout groups for many of the objects: The cards are in a scrollview, same with other components.

    The fonts I assume are static: I do not change them during runtime.

    Thank you so much for the quick response, this was a few days of trying to find what could be causing the constant crashes (since profiler doesn't show as TMP if it crashes while trying to render, realising it was the TMP took ages and then there didn't seem to be any documentation or even forum questions with proper answers as to why TMP might have such bad performance).

    How should I go about optimising this then? - Is there any way to keep auto-size and layout groups? Can I have the auto-size only occur once somehow? or have it be optimised somehow?

    Thanks.
     
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    See the following post which provides a detail answer on how to best use auto size.

    Following the above linked post recommendations, you should be able determine the optimum point size for all text objects or groups of text objects for any given resolution by only querying a single of few text objects as opposed to all of them. This should drastically reduce the number of layout passes from nearly 9K to perhaps a few dozen when using Layout Components.

    Please feel free to follow up in this thread with any additional questions about my recommendations from that post.
     
    freakrho likes this.
  5. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19
    Great, from the code sample in the previous discussion, what would be the UI.Text equivalent of TMP_Text..ForceMeshUpdate()? - If I want to do the same manual sizing for regular Text objects what would I use to auto-size it immediately?
     
  6. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    There is no equivalent for UI.Text. Is there a reason you are still using UI.Text in the project?

    Also keep in mind that UI.Text is considered legacy. Ie. in maintenance mode only whereas TMP is still on active development and the basis for all our future text in Unity. As such, I would recommend switching all text to TMP and continuing to provide feedback so I can keep improving it to make it even more powerful and flexible for all.
     
  7. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19
    Alright, I would strongly recommend putting some notice of this in the documentation somewhere: I've been working with unity for years and this is the first time I heard that UI.Text is legacy and TMPro is anything more than extra-fancy text.
     
  8. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Besides all the potential visual styling functionality which you describe as "extra-fancy text", TMP offers a substantial amount additional functionality over UI.Text which includes over 40 rich text tags, text layout with control over character, word, line and paragraph spacing, margins, justified text, text overflow modes like Ellipsis, Linked text, etc. Ability to use multiple fonts and sprites / emojis in the same text object, text styles, OpenType features such as kerning, diacritical marks, etc. and more. In short a massive set of additional features over UI.Text.

    Note that UI.Text will soon be label as "Text - Legacy" in a future 2022 Unity release and TMP shortly thereafter no longer be reference as "Text - TextMeshPro" and instead as just "Text". The underlying classes will not change.
     
    Last edited: Oct 21, 2021
  9. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19
    Cool I wasn't saying that TMP isn't awesome XD, I'd still add a note in the UI.Text documentation. Anyone using an earlier version won't have any idea that TMP should be default.
     
    Stephan_B likes this.
  10. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19
    While I have you here one more question:
    Is there a way to find the widest character with a specific font in TMP? My use case:
    I want to cache auto-sized sizes by the string length and rect size. This does not work if the cached auto size which is sized first has a shorter string size than any others of the same length (same letter count).
    So I would like to create the cache by using a "longest possible string" with that many letters, autosize that and use that value for all others.

    Second superior method:
    If getting the actual length of a string generated by TMP is a fast operation (much faster than auto-sizing) then a better solution would be to calculate the length of the string using the cached font size, and if it is longer than the length of the string for the one used in the cache, auto-size again and replace the cache.

    Would either of these be supported with TMP or do you have a better suggestion for such a system?

    Thanks.
     
  11. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    In theory, you should not have to use auto-size more than once and only on one text object or few text objects assuming that are using different point size in the sense of a title which typically uses larger point size vs the body of the text.

    The reasoning behind this is simply that from a design and typography point of view, you don't want to end up with a bunch of text objects all using different point sizes as this looks bad. So once you determine the optimum point size for your titles and body text, etc. you set that font size on all those objects which all have auto-size disabled.

    As per the post that I linked previously, the idea is to find the largest preferred width for all your titles for instance. Then once you know which one has the largest preferred width, you use auto-size to find that optimum point size. Once you have it, set that size on all other title text objects where auto size is disabled on all of them. The rinse and repeat for body text, etc. Even if a game contains 1,000 cards and lots of text, the point size of titles and body text should be the same for each group. So you only need to find that optimum point size once for each group.

    Since this only needs to be performed at startup when you have determined the resolution, it should never have to be done again unless the text is dynamic and subject to random changes or the resolution changes.

    Given the above, even if we came up with some alternative to auto-size, this process should not be needed more than once and performed only on a few group of objects like again titles, body text, etc.

    In terms of widest character, this can vary per font but is typically "W" but this can be checked by looking at the glyph text metrics and more specially which glyph has the largest advance value.
     
  12. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19
    Unfortunately this doesn't really fit my use-case well.
    In my game, it is a TCG with the following card designs:

    Casper Belt Fabricators.jpg Eshra.jpg Carrier Elankar.jpg

    Your Auto-size method works for the stats and types, but for example, for the name it does not work, because the design is such that the name covers the focus of the card and is not kept at the same size. Each name needs to be auto-sized individually which with 100 cards in a layout-group is not feasible on android.

    I also cannot cache for text character numbers, because in some cases (Like Carrier Elankar), a previously cached name with the same number of letters was smaller, causing it to have two lines (like Casper Belt Fabricators) but in a font too large to fit vertically.
    (Note, these are not the actual in-game images, but the design is almost identical)

    Secondly, within the game, I have smaller descriptions be much larger (to a max size) so that they are visible on Mobile. Verbose descriptions such as Casper Belt's one need to be auto-sized to be as large as possible but not cover the image. Same as the name, I cannot re-use the other description's auto-size for all of them.

    My current solution is to Auto-size the name for each card, and use shortcuts where possible for the other texts, but even autosizing one field 100 times causes a very noticeable delay. My two questions were the two workarounds I considered for this.

    Probably worse-case I need could make a lookup-table before-hand for widths and cards for the size but I would rather avoid a solution like that.
     
  13. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19

    Ok now I have one more issue:

    When using this auto-size method on TMP objects in a layout group, I want to auto-size when I enable them. The problem is when they are enabled, the layout group has not sized them yet, and they are treated as having a rect.width == 0 which creates inaccurate sizing.

    I cannot force update canvases each time this happens, so what is the work around using this method?
     
  14. Mowtine

    Mowtine

    Joined:
    Aug 17, 2014
    Posts:
    19
    For future devs, to answer some of these myself:
    1. I haven't found a faster solution to effectively resize the names, I have added a loading screen instead.

    2. To autosize on enable for layout groups, you need to delay the auto size to WaitForEndOfFrame().
     
    juanesmora_pl likes this.