Performance Enhanced Version of SpriteText?

Performance Enhanced Version of SpriteText?

Postby Arowx » Tue Oct 04, 2011 6:47 pm

I've been working on a game using EZGUI for the HUD, it's a great help but...

Image

Just yesterday I started profiling and optimising the game and started noticing spikes in frame times and updates to SpriteText.Text was often taking over 2ms to update (a 60fps shooter has to loop in under 15ms).

Now I've dug into the code and tweaked a few low level performance blips, for example Rect.x/height/width was being accessed multiple times in the same method instead of being localised.

Vectors were being added v1+v2, just unrolling that to x1+x2, was faster there.

But SpriteText generates new meshes for each character from scratch every time you change the text, using a Dictornary lookup for each characters details to calculate the mesh, could it not just cache and reuse character meshes as it uses them in an array for super fast speed?

Or is there a faster version of SpriteText for in game HUD elements?
Arowx
 
Posts: 22
Joined: Fri Mar 18, 2011 11:05 am

Re: Performance Enhanced Version of SpriteText?

Postby Brady » Tue Oct 04, 2011 11:18 pm

There are definitely some optimizations which have been planned but not yet implemented. As for caching character meshes, I'm not certain what you're referring to here, whether you mean leaving actual vertices in place, or storing copies of the character quad data in a local array. The latter approach could be very wasteful in a different way, since it would require massive duplication of data for very many text objects, particularly in cases where you use large character sets (Mandarin, for example, could eat a TON of your game's memory in a case like this). This is also true in cases where the character values themselves are distant from one another, unless you used some means by which to translate non-sequential character codes into sequential array indices.

So it's sort of a balancing act. In the current implementation, it tends to assume that text doesn't change all that often, and when it does, it only changes on a small handful of objects. But if you're updating lots of on-screen text objects every frame, then some things like you describe might be a better trade-off, depending on the game.

I like your idea about having a separate version or codepath for something like HUD text. And the "Dynamic Length" checkbox is sort of an unimplemented placeholder feature that would serve a purpose quite similar to that.

In the meantime, however, if you don't need any of the specific features available in SpriteText such as word-wrapping, clipping, or color tagging, you might just try 3D Text and see how that does.
Brady
 
Posts: 5361
Joined: Tue Jul 06, 2010 11:33 pm

Re: Performance Enhanced Version of SpriteText?

Postby Arowx » Wed Oct 05, 2011 11:14 am

I'm new to this but this is how I think it works
Every character in a SpriteText is a single quad

1 Every time someone updates the text each characters mesh quad is calculated by dictionary lookup for that characters info width/height offset ect
2 The quads dimensions are then calculated
3 A mesh is created
4 it's UV coordinates set to the text for that character

2-4 is your inner loop for every character of every string
Now on the profiler my FPS counter update is taking 1.38ms (a 60fps game needs to loop in <16ms)
SpriteText
.setText() 0.98ms
.UpdateMesh() 0.86ms
.Layout_Single_Line() 0.80ms
.LayoutLine() 0.55ms
.BuildCharacter() 0.22ms (10 calls) "FPS: 000.0" *** Inner Loop here on down
Rect.get_x() 0.2ms (20 calls))
Other Rect.get_ calls 0.1ms (10 calls)

SpriteFont.GetSpriteChar() 0.13ms (10 calls)
Dictionary.TryGetValue() 0.11ms (10 calls)

Now I think you could flatten that inner loop by caching, ideally you would just store the created character mesh and if it has already been created copy it and reposition it or you could store the pre-calcualted character co-ords and build a new quad

To do this the fastest way would be an array lookup, you already know the characters in the font, so you store the min char value subtract this from your char value and then check the cache via an array large enough to cover the used character set, this array gives you the cache index of the character.

or

Code: Select all
cacheIndex = charLookup[char-minChar];

if (cacheIndex > 0)
charMeshCache[cacheindex];

//else build and cache character


Now this is just my view based on a limited look via the profiler of your code, so take it with a big pinch of salt, as I am new to this!
Arowx
 
Posts: 22
Joined: Fri Mar 18, 2011 11:05 am

Re: Performance Enhanced Version of SpriteText?

Postby Arowx » Wed Oct 05, 2011 12:00 pm

Just a quick commnet, it looks like the SpriteChar object would be ideal for caching as you could get it to store the character mesh or calculated vertices (prior to offset).

Then you could just replace your dictionary with a two array lookup and SpriteChar array and it should fly (in theory?!).
Arowx
 
Posts: 22
Joined: Fri Mar 18, 2011 11:05 am

Re: Performance Enhanced Version of SpriteText?

Postby Arowx » Wed Oct 05, 2011 2:03 pm

I think I've got the basics of this optimisation working...

Code: Select all
private void createCharMapArray() {

      // create a character lookup array from the charMap dictionary

      int[] index = new int[charMap.Count];

      charMap.Keys.CopyTo(index, 0); //.ToArray();

      minMapIndex = int.MaxValue;

      maxMapIndex = int.MinValue;

      foreach (int i in index) {

         if (minMapIndex > i) minMapIndex = i;

         if (maxMapIndex < i) maxMapIndex = i;

      }

      Debug.Log("min:"+minMapIndex);

      Debug.Log("max:"+maxMapIndex);

      int size = maxMapIndex - minMapIndex + 1;

      Debug.Log("size:"+size);

      
      charMapArray = new int[size];

      for (int i = 0; i < size; i++) {
         charMapArray[i] = -1;
      }

      foreach (int i in index) {

         Debug.Log("index:"+i);

         charMap.TryGetValue(i, out charMapArray[i-minMapIndex]);   

      }

   }


Is the cornerstone as it allows you to replace the dictionary lookup with an array lookup!

UnityEngine.Rect is extremely slow keeps popping up in the profiler so if you replace it with local variables it rockets along.

Hmm and it's what the Unity GUI uses to run so fast! ;0)

Got a test string that cycles every frame (just increments +1 along a character string) and I've got a 20 character SpriteText updating in 1.21ms

Code: Select all
using UnityEngine;
using System.Collections;

public class TestText : MonoBehaviour {
   
   public SpriteText text;
   
   public string storyline = "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG! 013456789";
      
   private int i = 0;
   private int l = 20;
   private int sl;
   
   void Start() {
      sl = storyline.Length;   
   }
   
   // Update is called once per frame
   void Update () {
      i++;
      l = 20;
      
      if (i >= storyline.Length) i = 0;
      if (sl - i < l) l = sl - i;
      text.Text = storyline.Substring(i,l);
   }
}


The GetKerning is popping up in the profiler and uses another dictionary lookup so if I replace that should trim it down a bit (0.30ms there)

BuildCharacter() is being called 20 times and now takes 0.03ms ;o) sweet!
Arowx
 
Posts: 22
Joined: Fri Mar 18, 2011 11:05 am

Re: Performance Enhanced Version of SpriteText?

Postby Arowx » Wed Oct 05, 2011 2:31 pm

Wow this is going well I've got RemoveUnsupportedCharacters down to 0.01ms using the ArrayMap lookup and converting the StringBuilder to a char array! ;0)

Down to 0.75ms for TextText.Update()
UpdateMesh() 0.51ms *
ProcessString() 0.18ms

* Still uses a dictionary lookup.
Arowx
 
Posts: 22
Joined: Fri Mar 18, 2011 11:05 am

Re: Performance Enhanced Version of SpriteText?

Postby Brady » Wed Oct 05, 2011 4:29 pm

Nice, sounds like some good optimizations. If possible, could you to PM me with all the specific modifications and I can see about integrating them so you don't have to modify future releases? I'm just a bit concerned about using arrays for non-Latin character sets, particularly a typical use case of Mandarin, which can have so many characters that the font definition file alone is over 3MB.
Brady
 
Posts: 5361
Joined: Tue Jul 06, 2010 11:33 pm

Re: Performance Enhanced Version of SpriteText?

Postby Arowx » Wed Oct 05, 2011 6:23 pm

Will do they will need some tidying up and regression testing as I'm focusing on just optimising for a simple hud with text updates so there will be code paths I'm not considering/testing.

Good point on the mandarin but if they use it as sprite text that's a massive texture, and don't they have a lower sized character set for computers!

As I use a min/maxed lookup table you would only have to factor in the memory footprint of the lookup array as the character array has not changed, it's the kerning arrays that could be the big hit.

Now just tried it in my game and it's really blew away the previous performance, the profiler is showing under over 60 fps for the simpler scenes!

What I have is a bit on the rough side and I think you could get a lot more speed out of the SpriteText ProcessString() if you just cycled through it a character at a time as opposed to using multiple String.IndexOf() calls, and you could probably just use a simple switch case statement to act on specific characters.

You might want to consider removing the Dictionaries after the ArrayLookups have been created but do a Mandarin performance test first.

Will PM you in a mo!
Arowx
 
Posts: 22
Joined: Fri Mar 18, 2011 11:05 am

Re: Performance Enhanced Version of SpriteText?

Postby Arowx » Wed Oct 05, 2011 7:22 pm

Just updated my game with on Kongregate it's running lots faster!
Don't belive me try it here -> http://www.kongregate.com/games/Arowx/energy-island-conquest-beta2
Arowx
 
Posts: 22
Joined: Fri Mar 18, 2011 11:05 am

Re: Performance Enhanced Version of SpriteText?

Postby MadMac » Mon Feb 06, 2012 12:38 pm

Hello,
is this still valid ?
Can you explain what to do exactly ?
thank you very much.
MadMac
 
Posts: 47
Joined: Tue Sep 28, 2010 11:03 am

Next

Return to EZ GUI General

Who is online

Users browsing this forum: valamun and 8 guests

cron