Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Improving TMPro performance for Switch?

Discussion in 'UGUI & TextMesh Pro' started by aaversa, Jul 1, 2019.

  1. aaversa

    aaversa

    Joined:
    Sep 5, 2016
    Posts:
    41
    I'm the developer of Tangledeep, a PC game ported to Switch earlier this year. I'm currently doing an optimization pass on the game as we have some spiky frame drops despite it being a relatively simple-looking 2D game.

    One of the biggest performance issues we're having is when Unity redraws the canvas that our game log resides on. The game log (or combat log) is an always-on UI element that resides on its own canvas. Most game events are written to the log, so it updates at least once per second. According to Unity's deep profiler, this is the source of some substantial delays (4ms in deep profiling mode, one of the highest in the game!)

    You can see most of the usage in this screenshot, as a result of TMPro's Rebuild() function.

    https://cdn.discordapp.com/attachments/160444359815331841/594699050515693578/unknown.png

    To describe how we're using TextMeshProUGUI for the log:

    1. The log resides in a "Screen Space - Overlay" canvas. Non-pixel perfect, no raycaster.
    2. Canvas Scaler is being used on the canvas.
    3. The log content is within a ScrollRect which uses a Mask. The content itself is a single TMProUGUI.
    4. We are NOT using raycasting on the canvas or log objects, nor are we using Best Fit. However we use Rich Text as there is a ton of color and <sprites> being used throughout the log.
    5. I have tried removing the ScrollRect component but it does not seem to make a significant CPU difference in the problem function above.
    6. The average string size is about 500-600 characters.

    Is there anything else we can do to try and reduce the CPU usage here? Thanks in advance.
     
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    The ScrollRect is typically the source of excessive processing / re-layout as any RectTransform using Stretch mode can end up triggering full re-layout due to rounding errors.

    Layout components like Content Size Fitters can trigger full re-layout when the text changes where it might not be necessary. For instance, perhaps the RectTransform is initially set to a given size but could then be static onward. Here we are trying to avoid full re-layout when the text changes provided our initial RectTransform is of the correct size.

    Unless the text is changing each frame, there should not be any processing even when scrolling the text up or down.

    If a full re-layout of a text object that contains 500 - 600 characters take 4ms in Deep Profile, this means you will be at less than 1ms or even perhaps below 0.5 in non-dev / profiled builds. So that should be fine. It think it is unnecessary processing / re-layout that needs to be avoided.
     
  3. aaversa

    aaversa

    Joined:
    Sep 5, 2016
    Posts:
    41
    Thanks for your prompt response. On any modern PC this really isn't an issue, but on Switch the CPU is much, much slower. We're seeing noticeable spikes and stutters on that console, and the biggest source of these (according to the deep profiler) is TextMeshPro - that's why I'm trying to dig in further to see what else we can do.

    I tried removing the ScrollRect entirely. This had basically no impact on the performance at all. The structure of the object is now as follows;

    * Canvas object
    ** Container object with Image (the background of the log) and a log script
    *** An empty game object
    **** A TMProUGUI object

    I've confirmed that NONE of these objects are changing position at all. Best Fit is not being used, there are no ContentSizeFitters, etc. The ONLY thing changing is the .text field of the TMPro. An example of content is as follows:


    <color=yellow>Mirai</color> gains <color=#40b843>3</color> Stamina and <color=#40b843>3</color> Energy!
    <color=yellow>Your</color> <color=#0cffe6>Regen Flask</color> heals <color=#0cffe6>yourself</color> for <color=#40b843>31</color> HP!
    <color=yellow>Mirai</color> gains <color=#40b843>3</color> Stamina and <color=#40b843>3</color> Energy!
    <color=yellow>Your</color> <color=#0cffe6>Regen Flask</color> heals <color=#0cffe6>yourself</color> for <color=#40b843>31</color> HP!
    <color=yellow>Mirai</color> gains <color=#40b843>3</color> Stamina and <color=#40b843>3</color> Energy!
    <color=yellow>Your</color> <color=#0cffe6>Regen Flask</color> heals <color=#0cffe6>yourself</color> for <color=#40b843>31</color> HP!
    <color=yellow>Mirai</color> gains <color=#40b843>3</color> Stamina and <color=#40b843>3</color> Energy!
    <color=yellow>Your</color> <color=#0cffe6>Regen Flask</color> heals <color=#0cffe6>yourself</color> for <color=#40b843>31</color> HP!
    <color=yellow>Mirai</color> gains <color=#40b843>3</color> Stamina and <color=#40b843>3</color> Energy!


    Profiling the frame that the text was changed, I see once again that the entire "WillRenderCanvases" call takes about 5ms, and of that, the TMPro ReBuild method is 3.8ms.

    Edit: As an update, I wrote a script that just generated random numbers for the log instead. That completely eliminated the CPU time (down to about 0.1ms) from Rebuild. I can only assume then that rich text is the problem...
     
    Last edited: Jul 1, 2019
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I'll run some tests with and without the Rich Text on the sample text you provided above and provide feedback shortly thereafter.
     
  5. AdventureproGames

    AdventureproGames

    Joined:
    Feb 4, 2015
    Posts:
    5
    Any update on those test results?
     
  6. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I am still looking into this.

    Should be able to provide an update in the next few days.
     
  7. AdventureproGames

    AdventureproGames

    Joined:
    Feb 4, 2015
    Posts:
    5
    Hey Stephan, any word? Sorry to be a pain about this but if switching colors often in TMP is going to be an unavoidable performance hitch, that's something that matters for future projects. I worked with Andrew on the Switch port for Tangledeep, and I have another project in the works that uses a lot of ^s type nomenclature to change colors and styles mid message. Is rich text something we should generally avoid?
     
  8. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Getting pulled in all directions.

    I did make some performance improvements (related to parsing / not specific to rich text).

    I still need to look into the specific of this report as the parsing of the color tag for example should not impact performance by that much.

    I should be able to get back to this in a few days. Check back with me on Friday (if I have not provided an update by then).
     
  9. AdventureproGames

    AdventureproGames

    Joined:
    Feb 4, 2015
    Posts:
    5
    Been there :)

    Thanks for looking into it, will check back soon.
     
  10. aaversa

    aaversa

    Joined:
    Sep 5, 2016
    Posts:
    41
    Beep boop. Any updates?
     
  11. aaversa

    aaversa

    Joined:
    Sep 5, 2016
    Posts:
    41
    @Stephan_B Are there any updates on this issue? This is currently the #1 performance killer for our game on Switch.
     
  12. aaversa

    aaversa

    Joined:
    Sep 5, 2016
    Posts:
    41
    I'm trying this with the latest version of TMPro in Unity 2019.3. I've created a StringBuilder that passing the following text into TMPro.

    4
    Your Regen Flask heals yourself for 31 HP!
    Mirai gains 3 Stamina and 3 Energy!
    Your Regen Flask heals yourself for 31 HP!
    Mirai gains 3 Stamina and 3 Energy!
    Your Regen Flask heals yourself for 31 HP!
    Mirai gains 3 Stamina and 3 Energy!
    Your Regen Flask heals yourself for 31 HP!
    Mirai gains 3 Stamina and 3 Energy!

    No rich text tags. No content size fitters. Just a single TMPro on a single canvas. No raycasting, word wrap, auto fitting, etc.

    I have a function that writes this text when I press the spacebar. I hit spacebar a few times and look at the latest frame. According to the regular, non-deep profiler, this takes 1.28ms on an overclocked 9900K, within the Canvas.SendWillRenderCanvases() function.
     
  13. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Can you provide me with a simple scene that would allow me to verify the above?

    P.S. Be sure to test the above in the latest 2.1.0-preview.4 of the TMP package (if that is not the version you are currently using).