Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Stress testing the Beta UI - Text

Discussion in 'UGUI & TextMesh Pro' started by Arowx, Aug 26, 2014.

  1. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    OK I'm testing the performance of the new beta UI and I have managed to crash it with 170 dynamic text labels with shadows (see code below).

    Code (CSharp):
    1. public class TextTest : MonoBehaviour {
    2.  
    3.     public GameObject txt;
    4.     public Transform panel;
    5.  
    6.     public Text counter;
    7.  
    8.     List<Text> textList;
    9.  
    10.     void Start () {
    11.         textList = new List<Text>();
    12.     }
    13.  
    14.  
    15.     void Update () {
    16.         GameObject goT = Instantiate(txt) as GameObject;
    17.         goT.transform.parent = panel;
    18.         goT.transform.position = panel.position + (Vector3)(Random.insideUnitCircle * 200f);
    19.  
    20.         textList.Add(goT.GetComponent<Text>());
    21.  
    22.         for (int i = 0; i < textList.Count; i++)
    23.         {
    24.             if (textList[i].text.Length > 99) textList[i].text = "";
    25.             textList[i].text += (char)(Random.Range(65,122));          
    26.         }
    27.  
    28.         counter.text = textList.Count.ToString();
    29.         Debug.Log(counter.text);
    30.     }
    31. }
    To use the code set up a canvas, panel and 2 text fields with shadows, one to duplicate the other as a counter, add code and run.

    The code duplicates the text field randomly positions it then adds a random character to all of the text fields. It blanks text fields with more than 99 characters.

    I get 170 text fields then Error: Failed to generate mesh for Canvas as more than vertices 65535 are present.

    I have raised this as a bug.

    Using the profiler on the program FPS very quickly takes a nose dive.



    Edit Hi-lighted areas of GC Alloc, High Calls and Time, these areas are ripe for Optimisation.
     
    Last edited: Aug 26, 2014
  2. HappyLDE

    HappyLDE

    Joined:
    Apr 16, 2014
    Posts:
    56
    65535 seems the limit of an unsigned int.
     
  3. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,613
    unsigned short, and yes, IIRC Unity's index buffers are 16-bit.

    So let's see... at 170 text fields, you've got sum(1..99) + sum(1..70) = 7335 total characters, I think? Assuming an average English word length of 5.1 characters per word, that's a bit over 1400 words in one canvas.
     
  4. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Good point but the text fields use the same font, size, material and range of characters ASCII 65 to 122 or 57 characters. Only extend up to 99 characters in length and Unity fails when it hits 170 text strings so that's a maximum of 17,000 characters.

    In theory Unity could batch each character and just reuse them once, it appears to generate each character used.

    Another test without shadow and it fails at 334 test strings so the shadow probably re-builds the same character again!

    Or Unity's Text system can handle about 33400 characters before failure.

    This seems excessive but stress tests like this can be used to really optimise the UI components.

    Or if you have a game with simulated computer screens, maybe a 360 degree spherical array of them you might hit limits in the amount of text you can show on each one.
     
  5. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Stress testing the Button and generating a default button fails at 1022 buttons, but has a much more gradual fps deterioration. Same indexing error is generated, as each button has the text Button on it I'm guessing the Text field is the limiting factor.

    Yep 30 character button crashes at 398 buttons.

    A single text character button hits a limit at 1486 buttons.

    or 1816 buttons without any text component or don't go over a 42x42 grid of buttons.
     
    Last edited: Aug 26, 2014
  6. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,613
    No, that's not right. 65536 indices can store a maximum of 10922 quads (assuming six indices per quad, i.e. triangle list).

    No, it can't. The transform of each glyph is baked into the vertex positions; this is necessary to be able to submit all the glyphs in a single draw call. If you want to use multiple draw calls, you use multiple canvases, and your limits increase accordingly.

    Yes - AFAIK shadow adds a second copy of each glyph, tinted black and slightly offset.

    Um, you do realise that your test code isn't loading the system up with 99-character strings, right? Adding a new string each frame, and setting the length of each string X to (length(X)+1)%99, means you're getting gradually increasing string length... you've got much less than 33400 characters.
     
  7. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Good point updated code to build each string up to 100 characters and count the total characters.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4.  
    5. public class TextTest : MonoBehaviour {
    6.  
    7.     public GameObject txt;
    8.     public Transform panel;
    9.  
    10.     public Text counter;
    11.  
    12.     List<Text> textList;
    13.  
    14.     void Start () {
    15.         textList = new List<Text>();
    16.     }
    17.  
    18.  
    19.     void Update () {
    20.         int c = 0;
    21.         GameObject goT = Instantiate(txt) as GameObject;
    22.         goT.transform.parent = panel;
    23.         goT.transform.position = panel.position + (Vector3)(Random.insideUnitCircle * 200f);
    24.  
    25.         textList.Add(goT.GetComponent<Text>());
    26.  
    27.         for (int i = 0; i < textList.Count; i++)
    28.         {
    29.             if (textList[i].text.Length < 100) textList[i].text += (char)(Random.Range(65,122));
    30.             c += textList[i].text.Length;
    31.         }
    32.  
    33.         counter.text = textList.Count.ToString();
    34.         Debug.Log(c);
    35.     }
    36. }
    Last working loop hit 16122 characters, so it looks like the limit will be about 16,383 characters (based on 4 vertices and a 65535 limit to vertex indices).

    Instancing is a feature of DX11 and allows multiple instances of a mesh with variations on color, texture, position, scale and only uses 1 draw call - http://www.braynzarsoft.net/index.php?p=D3D11Instancing

    And also appears to be a feature fo DX9 - http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter03.html
     
  8. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,613
    Unity runs on many more platforms than just D3D9 and D3D11.
     
  9. ippdev

    ippdev

    Joined:
    Feb 7, 2010
    Posts:
    3,789
    I have been loading 200+ buttons and hundreds of textfields..all dynamic...all day long with no lag or issues. OS X here.
     
  10. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Last edited: Aug 26, 2014
  11. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Instancing is available on OpenGL as well as DirectX, it may not be available in older versions of OpenGL ES but with the fast refresh cycle in the mobile/tablet space this will be a vanishingly small audience.

    And even if not all platforms can use instancing I bet that letter vertices once generated for a specific font size can be re-used and cached.

    I saw this problem in NGUI and worked out how to improve the performance of it's text handling code as the code was in C#.
     
    Last edited: Aug 26, 2014
  12. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,033
  13. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    So it looks like OS X does not have the same hard vertex limit as Windows. Do you have the pro version as it would be interesting to see a deep profile view of this in action.
     
  14. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,033
  15. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    If you could run it in until it errors, then open up the same tabs as my deep profile view on the last available profile line, that would give us an idea if it has the same bottleneck.
     
  16. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,033
  17. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @orb I don't think there is in the UI but there appears to be a way to do it via scripting. My apologies you want the current frame to be the highest point before the crash.
     
  18. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    New test how many updating text fields can the UI handle before you go below a specific FPS.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System.Collections.Generic;
    4.  
    5. public class TextTest : MonoBehaviour {
    6.  
    7.     public GameObject btn;
    8.     public Transform panel;
    9.  
    10.     public Text counter;
    11.  
    12.     List<Text> controlList;
    13.  
    14.     public int fpsUnder60counter = 0;
    15.  
    16.     public float fpsLimit;
    17.  
    18.     void Start()
    19.     {
    20.         fpsLimit = 1f / fpsLimit;
    21.         controlList = new List<Text>();
    22.     }
    23.  
    24.     float lastDeltaTime;
    25.     int c = 0;
    26.  
    27.  
    28.     void Update()
    29.     {    
    30.         if (fpsUnder60counter < 10)
    31.         {
    32.  
    33.             c = 0;
    34.  
    35.             GameObject goT = Instantiate(btn) as GameObject;
    36.             goT.transform.parent = panel;
    37.             goT.transform.position = panel.position + (Vector3)(Random.insideUnitCircle * 300f);
    38.  
    39.             controlList.Add(goT.GetComponent<Text>());
    40.  
    41.             for (int i = 0; i < controlList.Count; i++)
    42.             {
    43.                 if (controlList[i].text.Length < 100) controlList[i].text += (char)(Random.Range(65, 122));
    44.                 c += controlList[i].text.Length;
    45.             }
    46.  
    47.             counter.text = controlList.Count.ToString();
    48.         }
    49.  
    50.         lastDeltaTime = Time.deltaTime;
    51.  
    52.         if (lastDeltaTime > fpsLimit)
    53.         {
    54.             fpsUnder60counter++;        
    55.             Debug.Log("characters:" + c.ToString() + " controls:" + controlList.Count + " fps " + 1f / lastDeltaTime);
    56.         }
    57.      
    58.         counter.text = "characters:"+c.ToString()+" controls:"+controlList.Count;
    59.  
    60.     }
    61. }
    This code creates new text controls (or any control with a text field) every frame and adds a character to the existing controls (up to 100 then resets them to empty "").

    But starts counting frames that drop below the desired FPS level and once 10 frames are under the threshold, stops adding new controls and characters.

    So how many text fields can I run at 60FP on my machine (in editor play mode)...

    24

    24 text controls being updated with new text before my FPS drops below 60. (19 in standalone build).

    90 text controls @ 30 fps.
     
    Last edited: Aug 27, 2014
  19. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    OK did a quick test to see how well the old textMesh compares and get about 20 controls before it goes under 60 but when I set the minimum FPS to 0 it just kept going with a gradual drop in performance as more controls were added but I got bored at 3000 controls with 100 characters and still with 20fps.

    Maybe Unity could compare and contrast their text rendering routines for possible optimisation?

    What if someone wants to write a real time trading app... :)

     
    Last edited: Aug 27, 2014
  20. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,613
    Font and text handling is the next major area they want to improve, they've said. I don't know how much of that is on the rendering end and how much is on the underlying data model stuff.
     
    shkar-noori and Arowx like this.
  21. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,181
    Both ends.
     
    hippocoder, Arowx and superpig like this.
  22. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,181
    And every corner and every hidey hole that bad text rendering hides in.
     
    hippocoder and Arowx like this.
  23. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Good to hear!
     
  24. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Just tested Beta 19* well I was hoping that some work had been done to improve performance but nothing that shows up in my tests.

    *There was no mention of any performance enhancements in the release notes.
     
  25. SilentWarrior

    SilentWarrior

    Joined:
    Aug 3, 2010
    Posts:
    107
    I am creating a GUI heavy app, and I am getting Error: Failed to generate mesh for Canvas as more than vertices 65535 are present over and over and over and over and over.

    Its getting really bad.

    If I have GConsole displaying the game logs, I can't get more than 5-6 logs out before the whole thing crumbles. I created a special data structure that works like a sliding window to keep at maximum 10 log entries and keep deleting the older ones, but, cmon, 10 log lines is an extremely small amount of text.

    Is there a way I can force unity to create a new mesh instead of reusing the one I have? I am creating Text instances every 100 characters, but it seems to not make any difference at all.

    My app isn't a game per se, so, I don't mind the extra draw calls from more meshes/materials.

    Is there a way to force it?
     
  26. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Well they should be able to get around the 65535 limit if they split the mesh into two at the cost of an extra draw call.

    And if they cache the letters they should get a massive improvement in dynamic text performance.

    PS Raise a bug and once they start getting the features locked down then should put some effort into optimisations.
     
    MaxEden likes this.
  27. ortin

    ortin

    Joined:
    Jan 13, 2013
    Posts:
    221
    Place every line into separate canvas.
    Also, just curious, why are your log entries so huge?
     
  28. SilentWarrior

    SilentWarrior

    Joined:
    Aug 3, 2010
    Posts:
    107
    They are not.

    They are around 10 lines or so, before it all goes beserk.
     
  29. phil-Unity

    phil-Unity

    Unity UI Lead Developer Unity Technologies

    Joined:
    Nov 23, 2012
    Posts:
    1,226
    we already have bugs relating to this. the "optimisation" (its more of a batching change that would take longer to process) likely wont happen for v1 as it would touch alot of the backend and the last thing we want is to have a ton of new bugs come in related to it causing a huge release delay. That being said as ortin said just split them into seperate canvas's then they will each generate their own mesh.
     
  30. ortin

    ortin

    Joined:
    Jan 13, 2013
    Posts:
    221
    Strange. 1 letter takes 4 vertices (+16 for outline, +4 for shadow and +36 for both effects, but I doubt you use them for log), so if 10 lines go beyond 65k limit it means 6,5k per line -> 1625 symbols in line.
     
    Last edited: Sep 27, 2014
  31. SilentWarrior

    SilentWarrior

    Joined:
    Aug 3, 2010
    Posts:
    107
    Well. I dont use any outline or shadow. But I do have an app with a big UI. I have 3 chat panels (on tabs so only 1 is shown at a time) that can take some lines too.

    I am not too sure where I am using all my vertices but my UI isnt extremely simple, it aint extremely complex either.

    How can I force it to use a different canvas? I did try putting it as its own canvas but I was running into the same problem at exactly the same spot.
     
  32. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,613
    Are you doing something like putting your entire chat history into a single UI label, or something?
     
  33. SilentWarrior

    SilentWarrior

    Joined:
    Aug 3, 2010
    Posts:
    107
    SlidingCappedList is a linked list of maximum elements, in this case 10. If over limit, disconnects the first element and sets it to the second one, and then sends it to the delegate, in this case Destroy;

    Code (CSharp):
    1. SlidingCappedList<GameObject> textList = new SlidingCappedList<GameObject> (10, Destroy);
    2.  
    3. void OnOutput (string line)
    4.         {  
    5.                 int maximumCharactersPer = 100;
    6.                 Text t = curText;
    7.                 bool needCutting = (t != null) && t.text.Length + line.Length >= maximumCharactersPer;
    8.                 if (t == null || needCutting) {
    9.                         var tgo = (GameObject)Instantiate (textPrefab);
    10.                         tgo.transform.parent = textParent;
    11.                         textList.Add (tgo);
    12.                         t = tgo.GetComponent<Text> ();
    13.                         if (needCutting) {
    14.                                 int remaining = maximumCharactersPer - t.text.Length;
    15.                                 int endOfFirstHalfLen = remaining - 1;
    16.                                 string firstHalf = line.Substring (0, endOfFirstHalfLen);
    17.                                 string secondHalf = line.Substring (endOfFirstHalfLen - 1, line.Length - endOfFirstHalfLen);
    18.                                 curText.text += '\n' + firstHalf;
    19.                                 t.text = secondHalf;
    20.                         } else
    21.                                 t.text = "";
    22.                         curText = t;
    23.                 } else {
    24.                         t.text += '\n' + line;
    25.                 }
    26. }
     
  34. sennin31

    sennin31

    Joined:
    Oct 17, 2012
    Posts:
    8
    Hello,

    I have a similar bug here, using the 4.6.0b20 version of Unity, and the new uGUI.

    I try to instanciate several Button by script, on the same Canvas.

    Each button had an Image as background, tiled, with the "fill center option" checked (to do a repeated background).

    When my app runs, I've got this error message :

    Failed to generate mesh for Canvas as more than vertices 65535 are present

    with only two buttons instanciated, no more.

    The same issue occurs if I manually drag two buttons in the same canvas, in the editor, without playing the scene.

    So my only solution is to separate each buttons on separate canvas?

    Thanks,
    Sennin.
     
  35. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,613
    Separating the buttons onto different canvases is probably the best workaround for now.

    However, it's pretty weird that you'd be running into that limit with just two buttons on your canvas - it's probably worth filing a bug report, as per the instructions in this forum.
     
  36. sennin31

    sennin31

    Joined:
    Oct 17, 2012
    Posts:
    8
    Hello,

    Yep a bug report is on this way, thanks!

    Sennin
     
  37. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Just tried b21 as it mentions in the release notes

    As I thought this would prevent my stress test from failing when I dynamically add more text fields to a canvas but I still get the same error stating that I hit the 64k limits.
     
  38. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,181
    Are you adding 64k verts on a single UI element? That's the upper limit for a UI element as we can't index more than that.
     
  39. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    No multiple text fields being added dynamically to a single canvas, check the bug report mentioned above.
     
  40. eheimburg

    eheimburg

    Joined:
    Apr 1, 2009
    Posts:
    34
    Is there a workaround to this issue? I can't find a way to display a smallish (30k) text file to players. A single Text can't handle it, so I give each paragraph its own Text, and a mere 170 paragraphs (mostly single lines) blows the 65535 limit.

    Putting the Text objects in their own Canvases doesn't seem to change that, maybe I'm missing something there. (They're all children of a single Vertical Layout Group so they can be scrolled, so the parent Canvas is presumably the one blowing the limit.)

    This is a pretty critical requirement, as it will also apply to e.g. the game's chat log. In that instance, I really do need to have each line be its own game object so that players can click them individually. And of course I'll need to be able to display more than 170 lines of chat.
     
  41. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Try removing/disabling the root Canvas.

    3d text does not have this limit.
     
  42. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    Another optimisation would be to render the paragraphs to a texture then just position the textures in the UI as an Image. This would only be valid for static text and if you do it within Unity you need Unity Pro to render to texture.

    Hint Hint it could also be a great optimisation for the UI team to apply to get around the canvas vertex limitation.