Stress testing the Beta UI - Text

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).

public class TextTest : MonoBehaviour {

    public GameObject txt;
    public Transform panel;

    public Text counter;

    List<Text> textList;

    void Start () {
        textList = new List<Text>();
    }
  
  
    void Update () {
        GameObject goT = Instantiate(txt) as GameObject;
        goT.transform.parent = panel;
        goT.transform.position = panel.position + (Vector3)(Random.insideUnitCircle * 200f);

        textList.Add(goT.GetComponent<Text>());

        for (int i = 0; i < textList.Count; i++)
        {
            if (textList[i].text.Length > 99) textList[i].text = "";
            textList[i].text += (char)(Random.Range(65,122));          
        }

        counter.text = textList.Count.ToString();
        Debug.Log(counter.text);
    }
}

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.

65535 seems the limit of an unsigned int.

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.

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.

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.

[quote=“Arowx, post:4, topic: 548403, username:Arowx”]
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.
[/quote]No, that’s not right. 65536 indices can store a maximum of 10922 quads (assuming six indices per quad, i.e. triangle list).

[quote]
In theory Unity could batch each character and just reuse them once
[/quote]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.

[quote]
Another test without shadow and it fails at 334 test strings so the shadow probably re-builds the same character again!
[/quote]Yes - AFAIK shadow adds a second copy of each glyph, tinted black and slightly offset.

[quote]
Or Unity’s Text system can handle about 33400 characters before failure.
[/quote]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.

Good point updated code to build each string up to 100 characters and count the total characters.

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public class TextTest : MonoBehaviour {

    public GameObject txt;
    public Transform panel;

    public Text counter;

    List<Text> textList;

    void Start () {
        textList = new List<Text>();
    }
  
  
    void Update () {
        int c = 0;
        GameObject goT = Instantiate(txt) as GameObject;
        goT.transform.parent = panel;
        goT.transform.position = panel.position + (Vector3)(Random.insideUnitCircle * 200f);

        textList.Add(goT.GetComponent<Text>());

        for (int i = 0; i < textList.Count; i++)
        {
            if (textList[i].text.Length < 100) textList[i].text += (char)(Random.Range(65,122));
            c += textList[i].text.Length;
        }

        counter.text = textList.Count.ToString();
        Debug.Log(c);
    }
}

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

Unity runs on many more platforms than just D3D9 and D3D11.

I have been loading 200+ buttons and hundreds of textfields…all dynamic…all day long with no lag or issues. OS X here.

@ippdev Good point could you please test my code on OS X?
Code from this test checks the Text limit. Stress testing the Beta UI - Text - Unity Engine - Unity Discussions

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#.

I tried this on OS X, and the count got to 1258 (alphabet + full stop in text field, so 30 characters) before it failed. The log showed 123385 as the last number before also showing an error message (“Failed to generate mesh for Canvas”).

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.

Which details are you looking for? Here’s a snapshot of the default overview: http://con.structive.net/TextProfile.jpg

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.

I’ve expanded as much as it would fit: http://con.structive.net/TextProfile2.jpg
Is there a way to save the actual profile data?

@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.

New test how many updating text fields can the UI handle before you go below a specific FPS.

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public class TextTest : MonoBehaviour {

    public GameObject btn;
    public Transform panel;

    public Text counter;

    List<Text> controlList;

    public int fpsUnder60counter = 0;

    public float fpsLimit;

    void Start()
    {
        fpsLimit = 1f / fpsLimit;
        controlList = new List<Text>();
    }

    float lastDeltaTime;
    int c = 0;


    void Update()
    {     
        if (fpsUnder60counter < 10)
        {

            c = 0;

            GameObject goT = Instantiate(btn) as GameObject;
            goT.transform.parent = panel;
            goT.transform.position = panel.position + (Vector3)(Random.insideUnitCircle * 300f);

            controlList.Add(goT.GetComponent<Text>());

            for (int i = 0; i < controlList.Count; i++)
            {
                if (controlList[i].text.Length < 100) controlList[i].text += (char)(Random.Range(65, 122));
                c += controlList[i].text.Length;
            }

            counter.text = controlList.Count.ToString();
        }

        lastDeltaTime = Time.deltaTime;

        if (lastDeltaTime > fpsLimit)
        {
            fpsUnder60counter++;         
            Debug.Log("characters:" + c.ToString() + " controls:" + controlList.Count + " fps " + 1f / lastDeltaTime);
        }
     
        counter.text = "characters:"+c.ToString()+" controls:"+controlList.Count;

    }
}

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.

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… :slight_smile:

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.

2 Likes