#TextMeshPro render perf issue

1 messages · Page 1 of 1 (latest)

rose verge
#

Even if I limit the update rate to 0.5s, this is still a silly high overhead...

night orbit
#

I'm not an expert on TMPro except at being constantly disappointed with it. I just think your maybe going beyond it's limits (as your undesirable results imply).

shy hound
#

I disagree with This doesn't seem to be caused by layout dirtying, but simply by the text rendering, and according to what I read here and there, 0.03 ms per updated TMP component is a "normal" perf figure...
Every update of a canvas causes everything to be redrawn. You have 125 * 0.03ms (assuming your info is correct)= ~3.75ms worth of text updates alone. Probably a few other things too in your canvas. This performance seem reasonable to me for a canvas of this size.

The workaround could be to make all the rows a separate canvas with 5 text elements. I'm also not a UI pro, but last few times I helped people with huge canvasses this fixed it.

rose verge
#

The canvas only has the table, which is a single vertical layout group, each row has a single layout element and all 5 cells are statically positioned with the RectTransform, no UI layout involved. You can see in the profiler that there is zero overhead from the Layout update. I've tried manually dirtying the same canvas but without the text changing, and there is zero perf overhead when I do that. I'm relatively confident that the overhead is caused by the TMP text rendering itself.
The 0.03ms figure I gave is just a rough 4 ms / 125 division, I didn't profile it (I don't think there is any to get such a figure).

#

But yeah, I will try implementing the table as just 5 TMP components, one for each column.

rose verge
#

sigh
Nope... There is a slight improvement by using only 5 components, I'm around 3.5 ms, yay !
Seems the overhead is directly proportional to the amount of characters to render. If I only populate each column with a single value, I'm around 0.2 ms. If I put all 25 values in each column, I'm at 3.5 ms...

shy hound
rose verge
#

Well, I think that's how things are...
I wanted to get my specific environment out of the equation, so I just tested a blank Unity 2019.4 project with nothing else but a canvas and 5 TMP objects that are assigned a random 150 char long string every update, and this is eating ~2.3 ms per frame.

shy hound
#

So just a screenshot.

rose verge
#

I've tried messing around a bit with the TMP settings and nothing made a difference, excepted Auto size, which nearly tripled the per-TMP instance cost

shy hound
rose verge
#

Can you disable that, that is really costly.
Doesn't make any perceivable difference. I've tried disabling basically everything.
And after a bit of messing around, I confirm it's directly proportional to the rendered char count, even the amount of separate TMP instances doesn't make a huge difference. 150 TMP instances rendering 1 char each perform roughly equally as 1 instance rendering 150 chars.

rose verge
#

Yes, I'm painfully aware about all the usual caveats with Unity UI stuff. This a separate problem entirely.

#

I can even reproduce it with the non-UI world space TMP component.

shy hound
#

Anyhow, I'm just trying to help. But that it doesn't make a difference is really weird. That should make a huge difference. Can you show a screenshot of the UI itself?

#

Maybe it has lots of overdraw.

rose verge
#

There is nothing in that test scene beside the bare minium to show a TMP component :

#

Just deleted the event system just in case, doesn't make a difference.

shy hound
#

So 0.65 ms for 1 TMP component?

#

Insanely weird. If I have energy I'll try to look it up when I get home from work.

rose verge
#

This is the script I'm attaching to the TMP GO :

using TMPro;
using UnityEngine;
using Random = System.Random;

public class RandomText : MonoBehaviour
{
    TextMeshProUGUI text;
    string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    char[] stringChars;
    Random random = new Random();

    public int charCount = 500;

    void Start() => text = GetComponent<TextMeshProUGUI>();

    void Update()
    {
        if (stringChars == null || stringChars.Length != charCount)
            stringChars = new char[charCount];

        for (int i = 0; i < stringChars.Length; i++)
        {
            if (i % 5 == 0)
                stringChars[i] = '\n';
            else
                stringChars[i] = chars[random.Next(chars.Length)];
        }

        text.text = new string(stringChars);
    }
}
#

I think it simply always been like that, it's just that the issue isn't immediately visible. If you have text changes once every 50 frames that produce a 10 ms spike, it doesn't visibly affect average (or even median) framerate, but it definitely will cause frame dropping and stutter. If you take a look at the TMP demo scene "Benchmark (Floating Text)", you can see that for 150 text objects that are mostly 1 char, the same significant render cost is visiable (0.5-1 ms depending on the animation state) :

rose verge
#

But yeah, that's insane. Just did a comparative test with OnGUI() :
For 1000 chars rendered / frame :

  • OnGUI : 0.24 ms
  • TMP : 2.71 ms
    -> ratio 1:11
    for 500 chars :
  • OnGUI : 0.15 ms
  • TMP : 1.35 ms
    -> ratio 1:9
    For 100 chars :
  • OnGUI : 0.07 ms
  • TMP : 0.37 ms
    -> ratio 1:5
    For 10 x 20 chars :
  • OnGUI : 0.20 ms
  • TMP : 0.82 ms
    -> ratio 1:4
    For 100 x 5 chars :
  • OnGUI : 1.73 ms
  • TMP : 3.63 ms
    -> ratio 1:2
rose verge
#

TextMeshPro render perf issue

rose verge
#

Just did some test with Unity 2021.3, performance is slightly better there, getting ~2ms for a 1000 char label.
Also tested with an UI Toolkit label in 2021.3 too, performance is a bit better than UGUI/TMP at around 1.5ms, but still nowhere near OnGUI()...

shy hound
#

Still seems weird UI updates are this slow.

#

How do the Unity cookie clickers garbage games give hundreds of UI text then that update thousands/millions of numbers in them?

#

And those work on mobile devices.

#

There is something wrong here in my opinion.

rose verge
#

It's not "UI updates". It's text rendering (ie, turning a string into a texture), which is only performed when the TMP.text property is set to a different value than it was before.

#

Hence why that perf issue isn't immediately obvious, because most text in an UI is usually static.
Even for a label representing a value that is always moving, you don't usually update it every frame (because that makes it unreadable), you update it every 500ms or 1s.
But I actually found a few complaints here and there of people having significant perf overhead in situations like mine, or in other use cases (for example RPG-style scrolling text, or chat windows with large strings).
Imagine for example that you have a "stats summary" UI with 100 labels updating every 5 seconds : it's not immediately an issue, but it means you're gonna have a massive frame time spike every 5 seconds.

rose verge
# shy hound There is something wrong here in my opinion.

Well, I think I've tested everything I could think of (various settings, in editor and built player comparison, various Unity/TMP versions) and results are consistent.
But maybe it's an issue on my machine, so if someone else want to test this, I'm interested.
It's quite easy, create a new Unity project, add a TMP text object, attach the script I posted above to the TMP text object, run, check the profiler.
With the script default value (500 chars), I'm seeing between 0.8 ms and 1.4 ms in UGUI.Rendering.UpdateBatches(), depending on the Unity and TMP version.

shy hound