Search Unity

Massive Font.CacheFontForText CPU spike using default GUI

Discussion in 'Immediate Mode GUI (IMGUI)' started by invicticide, Feb 7, 2014.

  1. invicticide

    invicticide

    Joined:
    Nov 15, 2009
    Posts:
    109
    The first time I draw anything using the default GUI (no custom fonts, skins, or styles) I get a *huge* CPU spike in Font.CacheFontForText:

    $profiler-wat.png

    I found a UnityAnswers thread that suggests that this can happen with dynamic fonts when attempting to render glyphs that aren't currently in the font map, and that using a static font is one way to resolve the issue.

    However, since I'm using the default GUI font, I don't have access to any import properties, because it's not an asset in my project.

    Is there anything I can do about this?
     
  2. hellaFont

    hellaFont

    Joined:
    Jun 6, 2013
    Posts:
    4
    I believe the default font is arial, you could just import an arial font into your project and use that.
     
  3. Trof_Sivart

    Trof_Sivart

    Joined:
    Dec 15, 2013
    Posts:
    8
    Just solved a similar issue to what you're having. In my game, I was using multiple GUIstyles to display powerup icons. When there was no font attached to them (default was none), whenever I got my first powerup I would get about 800ms lag, then it would be fine for the rest of the playthrough. My solution was to assign a font to each of the GUIstyles, and it got rid of the lag. Hopefully this information is helpful to you :)
     
  4. droido

    droido

    Joined:
    Jan 27, 2014
    Posts:
    9
    got mine fixed with Rendering Type - OS Default
    previously it was Smooth.
     
  5. MrEsquire

    MrEsquire

    Joined:
    Nov 5, 2013
    Posts:
    2,712
    I seem to be having this issue with Unity 4.6 Beta 20, not sure whats causing it..

    On iOS Mobile 50percent CPU spike;
    Canvas.SendWillRenderCanvases()
     
  6. Andy-Block

    Andy-Block

    Joined:
    Aug 10, 2012
    Posts:
    10
    I'm also seeing this on b20 on Android. 150ms spike on a Galaxy SII
     
  7. DennG

    DennG

    Joined:
    Dec 5, 2013
    Posts:
    158
    I'm also facing the same problem. I'm downloading 4.6 RC1 right now to check if the problem has been solved.

    Edit:
    Working fine with RC1. Good job unity team.
     
    Last edited: Nov 8, 2014
    MrEsquire likes this.
  8. TheWulo

    TheWulo

    Joined:
    Nov 16, 2014
    Posts:
    5
    I have the same problem.

    Even after setting the .tff font rendering mode into OS Default it still gives me spikes.

    I'm using 5.0.0b13, so probably I will need to wait for the same patch that solved the case for DennG.
     
  9. DennG

    DennG

    Joined:
    Dec 5, 2013
    Posts:
    158
    The problem is still there. But it's much better than before.
    I hope that the UI team is working on this performance issue...
     
  10. jay-jang-archiact

    jay-jang-archiact

    Joined:
    Jul 21, 2014
    Posts:
    1
    When will this get fix!

    I made a static font instead of dynamic one, and bug is fixed, but I can't have it bold.
     
  11. BMayne

    BMayne

    Joined:
    Aug 4, 2014
    Posts:
    186
    Hey there,

    Try changing your text to not using Best Fit and go to your font file and don't make it dynamic. Unity is trying to rebuild your font into a texture every time a letter is added or removed. This has a huge overhead as it's baking a texture behind the scenes.

    Regards,
     
    rytalfo and MGGDev like this.
  12. IVxIV

    IVxIV

    Joined:
    Nov 13, 2013
    Posts:
    16
    For anyone else who encounters this same issue, here is another solution you could consider. I ran into the same problem as a result of using custom GUIStyle objects for rendering in my OnGUI calls, some of which referenced custom dynamic fonts (this is the source of the problem - dynamic font glyphs get cached to an internal font cache texture in Unity).

    To avoid taking the big hit all at once when Font.CacheFontForText() is first called by means of an OnGUI call, inside an Awake() call for my UI code I spin up a coroutine to asynchronously precache each of my dynamic fonts' glyph data, making repeated calls to Font.RequestCharactersInTexture(). This has worked nicely so far for my relatively simple UI needs, and avoids having one giant blocking stall where the GUI font cache is populated all at once.

    Here is some sample code to illustrate the basic approach I used:

    Code (CSharp):
    1.  
    2. public class MyGUIClass : MonoBehaviour
    3. {
    4.     private static readonly string kPrecacheFontGlyphsString= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}|\\:;\"'<>,.?/ ";
    5.  
    6.     // utility struct used in font caching
    7.     struct CacheFont
    8.     {
    9.         public Font theFont;
    10.         public int size;
    11.         public FontStyle style;
    12.     };
    13.  
    14.     void Awake()
    15.     {
    16.         // gather custom fonts that we will be using
    17.         CacheFont[] myCustomFonts= RetrieveMyCustomFonts();
    18.  
    19.         if (null != myCustomFonts)
    20.         {
    21.             for (int fontIndex= 0; fontIndex < myCustomFonts.Length; ++fontIndex)
    22.             {
    23.                 StartCoroutine(PrecacheFontGlyphs(
    24.                     myCustomFonts[fontIndex].theFont,
    25.                     myCustomFonts[fontIndex].fontSize,
    26.                     myCustomFonts[fontIndex].fontStyle,
    27.                     kPrecacheFontGlyphsString));
    28.             }
    29.         }
    30.      
    31.         return;
    32.     }
    33.  
    34.     // Precache the font glyphs for the given font data.
    35.     // Intended to run asynchronously inside of a coroutine.
    36.     IEnumerator PrecacheFontGlyphs(Font theFont, int fontSize, FontStyle style, string glyphs)
    37.     {
    38.         for (int index= 0; (index < glyphs.Length); ++index)
    39.         {
    40.             theFont.RequestCharactersInTexture(
    41.                glyphs[index].ToString(),
    42.                fontSize,
    43.                style);
    44.             yield return null;
    45.         }
    46.      
    47.         yield break;
    48.     }
    49.  
    50.  
    51.     void OnGUI()
    52.     {
    53.         // now that dynamic font glyphs have been precached,
    54.         // there won’t be a terrible hitch the first time this is called
    55.     }
    56. };
    57.  
     
    Last edited: Dec 24, 2014
  13. wangunity

    wangunity

    Joined:
    Sep 25, 2013
    Posts:
    12
    do you have any idea when come to NGUI project ?
     
  14. IVxIV

    IVxIV

    Joined:
    Nov 13, 2013
    Posts:
    16
    No, but if you needed something like this for NGUI (assuming it doesn't already have it built-in), then it should be straightforward to add.
     
  15. MrEsquire

    MrEsquire

    Joined:
    Nov 5, 2013
    Posts:
    2,712
    I think this issue still exists with the new GUI and Unity 4.6.2p1
     
  16. Icaro-Malta

    Icaro-Malta

    Joined:
    Jan 21, 2013
    Posts:
    5
  17. MrEsquire

    MrEsquire

    Joined:
    Nov 5, 2013
    Posts:
    2,712
  18. nevilleshane

    nevilleshane

    Joined:
    Aug 19, 2013
    Posts:
    4
    I fixed this by unchecking 'Incl. Font Data' on the font file.

    Switching the actual font from Dynamic also worked, but it caused some additional logistics headaches.
     
  19. Chintao

    Chintao

    Joined:
    Jun 26, 2014
    Posts:
    54
    Thank you! I had the same problem with my game and i just disabled Best Fit for all of my text elements and it's working ALOT better now! I still have the font dynamic cause without it looked washed out.

    again thank you!
     
  20. Tomer-Barkan

    Tomer-Barkan

    Joined:
    Jul 31, 2012
    Posts:
    150
    I'm now getting this spike every minute or so, and the spike is 1500 miliseconds... :(
    This started when upgrading to 5.2.1. When I build with my other PC that still has 5.1.1 this issues do not occur, and a bunch of other errors and warnings as well.

    upload_2015-10-5_21-12-35.png
     
    BernieZQ likes this.
  21. TriBolt72

    TriBolt72

    Joined:
    Jan 27, 2015
    Posts:
    7
    I'm having issues with this as well...
     
  22. rcabot

    rcabot

    Joined:
    Oct 24, 2014
    Posts:
    6
    Just a heads up, I solved this with a really silly workaround.
    If you don't disable and reenable your UI components, and instead move them off the screen so they're not seen, unity won't recache your font textures:
    Code (CSharp):
    1. private void ShowRightPane(bool shown)
    2.     {
    3.         RightPane.GetComponent<RectTransform>()
    4.             .transform.localScale = shown ? new Vector3(1f,1f,1f) : new Vector3(1f, -1f, 1f);
    5.     }
    Not particularly scalable, but good enough for a small project, and it completely removed the horrible spike I was having.
    So if you don't absolutely need to, try just not disabling the game objects your UI Text sits on!
     
    radiantboy and anishhuey like this.
  23. Prometheusheir

    Prometheusheir

    Joined:
    Aug 15, 2015
    Posts:
    20
    a436t4ataf and Exustus like this.
  24. InteractiveGames

    InteractiveGames

    Joined:
    Mar 12, 2016
    Posts:
    1
    I am using Localization so it's very difficult to not use Best Fit with 10 different languages. Some language texts are very long for the same sentence.

    Changed rendering mode to "OS Default" gave some performance. But still it has some spikes.
     
  25. Johannski

    Johannski

    Joined:
    Jan 25, 2014
    Posts:
    826
    With Text Mesh Pro you don't have to cache those gigantic textures. This will get unitys default at some point. In the meantime you can download it here for free: https://www.assetstore.unity3d.com/en/#!/content/84126
    I really recommend switching. It will make your life easier, believe me :)
     
  26. Glurth

    Glurth

    Joined:
    Dec 29, 2014
    Posts:
    109
    If anyone else runs across the same mistake I made:

    I was following the example provided on this page: https://docs.unity3d.com/ScriptReference/Font.RequestCharactersInTexture.html

    This gave me problems when using large strings:
    Code (CSharp):
    1. void Update()
    2.     {
    3.         // Keep requesting our characters each frame, so Unity will make sure that they stay in the font when regenerating the font texture.
    4.         font.RequestCharactersInTexture(str);
    5.     }
    6.  
    Massive delays when I passed a few paragraphs of text to this function! Obviously, there is no need to pass ALL the characters in the string, only the unique ones. But even computing THAT takes time, so I just passed in this string, everytime:
    Code (CSharp):
    1. string fontGlyphsString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}|\\:;\"'<>,.?/ ";
    2. font.RequestCharactersInTexture(fontGlyphsString);
    3.  
    The improvement in performance was immediate.

    In hindsight, this is an obvious fix. But, sometimes, those examples put me in a "box" that can be hard to see "outside of". Figured I'd post this here, in case I'm not the only one.
     
    radiantboy and a436t4ataf like this.
  27. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    is there any way to get the contents of the existing fontGlyphsString ? Since I use different languages etc I want to get it first then reapply it to make sure it covers every character I need..
     
  28. Glurth

    Glurth

    Joined:
    Dec 29, 2014
    Posts:
    109
    Not sure I understand; I entered that string manually in the code. I'd expect you should be able to simply modify it to include whatever characters you wish. You could also create a separate one for each language you will use, and call RequestCharactersInTexture using the string for whatever language is selected by the user/current build.

    That said, I'm not sure whether or not you can enter non-roman alphabet characters (arabic, chinese, etc..) in code, I've never had to try that.

    Worst case: you can take your text data, and run it through a function that will extract all the unique characters it contains, and store those results into your fontGlyphsString. This will, obviously, take some processing time, but should only need to be run once, during startup.
     
  29. radiantboy

    radiantboy

    Joined:
    Nov 21, 2012
    Posts:
    1,633
    Thanks yes, I came to same conclusion. Actually I reduced CacheFontForText from 3800 calls down to 220 by turning off items in my list that weren't visible and only turning them on as I need them. This and a few other things got my slowdown from 1600ms to just 20ms.

    I used this amazing script which will help anyone having listview slow down issues
    https://github.com/sarbian/KSPUIHel...xtensions/Utilities/UI_ScrollRectOcclusion.cs