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

Issue with removing fonts from fallbackFontAssetTable (memory leak)

Discussion in 'UGUI & TextMesh Pro' started by lyha, Jan 26, 2021.

  1. lyha

    lyha

    Joined:
    Feb 28, 2016
    Posts:
    15
    First of all – many thanks for the level of support you are giving here, it's awesome.
    Thanks to existing threads and responses Stephan_B is giving this Autumn I've created a custom system that loads system fonts for TMP to handle different languages.
    I've to return to it due to find issues and noticed that some of the memory is not freed up. Checking on iOS with xCode.
    The shortened logic looks like that:
    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine;
    3. using System.Linq;
    4. using System.Globalization;
    5.  
    6. namespace FirstWords.Utilities
    7. {
    8.     public class SystemFontFallback : MonoBehaviour
    9.     {
    10.         [SerializeField]
    11.         private TMP_FontAsset fontToEnhance;
    12.  
    13.         Font fontObj;
    14.         TMP_FontAsset fontAsset;
    15.         CultureInfo cultureInfo = CultureInfo.InvariantCulture;
    16.  
    17.         public void Start()
    18.         {
    19.  
    20.             string[] fontPaths = Font.GetPathsToOSFonts();
    21.  
    22.             string fontPath = null;
    23.             switch (Application.systemLanguage)
    24.             {
    25.                 case SystemLanguage.Japanese:
    26.                     fontPath = fontPaths.First(s => Contains(s, "HiraginoMaruGothProN.ttc"));
    27.                     break;
    28.                 case SystemLanguage.Chinese:
    29.                     fontPath = fontPaths.First(s => Contains(s, "PingFang.ttc"));
    30.                     break;
    31.                 default:
    32.                     break;
    33.             }
    34.  
    35.             fontObj = new Font(fontPath);
    36.             fontAsset = TMP_FontAsset.CreateFontAsset(fontObj, 50, 6, UnityEngine.TextCore.LowLevel.GlyphRenderMode.SDFAA, 512, 512, AtlasPopulationMode.Dynamic);
    37.             fontToEnhance.fallbackFontAssetTable.Add(fontAsset);
    38.         }
    39.  
    40.         private bool Contains(string path, string part)
    41.         {
    42.             return cultureInfo.CompareInfo.IndexOf(path, part, CompareOptions.IgnoreCase) >= 0;
    43.         }
    44.  
    45.         private void OnDestroy()
    46.         {
    47.             fontToEnhance?.fallbackFontAssetTable?.Remove(fontAsset);
    48.             fontAsset?.ClearFontAssetData(true);
    49.             Destroy(fontAsset);
    50.             Destroy(fontObj);
    51.             fontAsset = null;
    52.             fontObj = null;
    53.         }
    54.     }
    55. }
    56.  
    A bit of context — this script is run on a specific scene load and all resources need to be cleaned up after the scene is unloaded.
    I've addressed my issue here because it looks like the fallbackFontAssetTable is not cleared correctly — at least memory leak is not present when I commenting lines 36-37 (creating TMP_FontAsset and adding a fallback).

    I assume that maybe the issue is connected with a fact that I'm referencing the TMP_FontAsset in resources (not in folder Resources), but it's already created in memory and removing a fallback font is not affecting it.

    I'm using:
    Unity 2018.4.26
    TMP 1.5.1
    Addressables 1.15.1


    UPD: Just made a build with commented line 37 (adding a fallback) and the issue is present. So the issue is with fontAsset. What is the correct way of freeing it up?
     
    Last edited: Jan 26, 2021
    ejx61s likes this.
  2. lyha

    lyha

    Joined:
    Feb 28, 2016
    Posts:
    15
    So I hurried with creating a thread, I found a solution:
    Code (CSharp):
    1. FontEngine.DestroyFontEngine();
    It cleans up the memory from created font asset.
     
  3. lyha

    lyha

    Joined:
    Feb 28, 2016
    Posts:
    15
    As a bonus, to not being punished, let me share my investigations about fonts on iOS/Android.
    I wanted to prepare this data and cover with script, but I'm to lazy to change the code :)
    The next list is mainly gathered for my needs (bold fonts), so I may miss some thin fonts in case already has what I need. Please note that the Chinese font on iOS is huge, it took about 100Mb in memory. Care.

    Android
        Arabic
    /system/fonts/NotoNaskhArabicUI-Bold.ttf
    /system/fonts/NotoNaskhArabic-Bold.ttf.
    /system/fonts/SECNaskhArabicUI-Bold.ttf
    /system/fonts/SECNaskhArabic-Bold.ttf

    Chinese +Japanese
    /system/fonts/NotoSansCJK-Regular.ttc
    /system/fonts/SECCJK-Regular.ttc
    /system/fonts/NotoSerifCJK-Regular.ttc

    Hindi
    /system/fonts/NotoSansDevanagari-Bold.otf
    NotoSansDevanagari-Medium.otf
    /system/fonts/NotoSansDevanagari-Regular.otf
    /system/fonts/SECDevanagari-Bold.otf
    There are also fonts with similar names but with UI suffix in name - they are not matched my criterias.

    Russian
    /system/fonts/Roboto-Bold.ttf
    /system/fonts/SECRobotoCondensed-Bold.ttf

    Turkish + Vietnamese
    /system/fonts/Roboto-Bold.ttf
    /system/fonts/SECRobotoCondensed-Bold.ttf
    /system/fonts/SECTamilUI-Bold.otf
    /system/fonts/SECFallback.ttf


    iOS
        Arabic
    /System/Library/Fonts/CoreAddition/ArialBold.ttf
    /System/Library/Fonts/Core/TimesNewRomanBold.ttf
    /System/Library/Fonts/Core/TimesNewRoman.ttf
    /System/Library/Fonts/CoreAddition/Arial.ttf
    /System/Library/Fonts/AppFonts/AlNile.ttc
    /System/Library/Fonts/Core/CourierNew.ttf
    /System/Library/Fonts/Core/GeezaPro.ttc
    /System/Library/Fonts/AppFonts/Mishafi.ttf

    Chinese
    ([B]huge[/B]!) /System/Library/Fonts/LanguageSupport/PingFang.ttc

    Japanese
    /System/Library/Fonts/Core/AppleSDGothicNeo.ttc
    Apple SD Gothic Neo Heavy
    Apple SD GothicNeo ExtraBold
    /System/Library/Fonts/CoreUI/HiraginoMaruGothProN.ttc
    Hiragino Maru Gothic ProN W4
    (huge!) /System/Library/Fonts/LanguageSupport/PingFang.ttc

    Hindi
    /System/Library/Fonts/AppFonts/DevanagariSangamMN.ttc
    Devanagari Sangam MN
    Devanagari Sangam MN Bold
    /System/Library/Fonts/Core/Kohinoor.ttc
    Kohinoor Devanagari Bold
    Kohinoor Devanagari Light
    Kohinoor Devanagari Medium
    Kohinoor Devanagari Regular
    Kohinoor Devanagari Semibold

    Russian
    /System/Library/Fonts/CoreAddition/ArialBold.ttf
    /System/Library/Fonts/Core/AvenirNext.ttc
    Avenir Next Bold
    /System/Library/Fonts/CoreAddition/VerdanaBold.ttf
    /System/Library/Fonts/Core/Courier.ttc
    /System/Library/Fonts/Core/HelveticaNeueExtras.ttc

    Turkish + Vietnamese
    /System/Library/Fonts/CoreAddition/ArialBold.ttf
    /System/Library/Fonts/Core/AvenirNext.ttc
    Avenir Next Bold
    /System/Library/Fonts/CoreAddition/VerdanaBold.ttf
    /System/Library/Fonts/Core/Courier.ttc
    /System/Library/Fonts/Core/HelveticaNeueExtras.ttc
     
    ejx61s likes this.
  4. Stephan_B

    Stephan_B

    Unity Technologies

    Joined:
    Feb 26, 2017
    Posts:
    6,588
    Thank you for the thread and sharing of this information.

    I wanted to give you and others a heads up on some changes that I recently made to the FontEngine which are related to this thread.

    The first change is to improve memory overhead when creating font assets from font objects which currently take 3X more memory than it should. This is due to the font object returning an instance of the font data which then needs to be cached by the FontEngine. Once this change lands the memory overhead will be 1X as it should be.

    This change has not landed yet and I aim to backport it all the way back to 2018.4.

    A related change is the addition of two new utility functions which are:

    Code (csharp):
    1.  
    2. /// <summary>
    3. /// Unloads current font face and removes it from the cache.
    4. /// </summary>
    5. /// <returns>A value of zero (0) if the font face was successfully unloaded and removed from the cache.</returns>
    6. public static FontEngineError UnloadFontFace()
    7.  
    8. /// <summary>
    9. /// Unloads all currently loaded font faces and removes them from the cache.
    10. /// </summary>
    11. /// <returns>A value of zero (0) if the font faces were successfully unloaded and removed from the cache.</returns>
    12. public static FontEngineError UnloadAllFontFaces()
    13.  
    These two functions will be very useful when iterating over multiple fonts such as system fonts when searching for fonts that contain certain glyphs.

    These changes will also include the ability to select the face index of potential font faces contained in TrueType Collections (TTC) font files. This new functionality will require a new version of the TMP package to expose this new functionality.

    Here is one of the new overloads that will be available to load a specific font file and face index.

    Code (csharp):
    1.  
    2. /// <summary>
    3. /// Loads the font file from the Unity font's internal font data. Note the Unity font must be set to Dynamic with Include Font Data enabled.
    4. /// </summary>
    5. /// <param name="font">The font from which to load the data.</param>
    6. /// <param name="pointSize">The point size used to scale the font face.</param>
    7. /// <param name="faceIndex">The face index of the font face to load. When the font file is a TrueType collection (.TTC), this specifies the face index of the font face to load. If the font file is a TrueType Font (.TTF) or OpenType Font (.OTF) file, the face index is always 0.</param>
    8. /// <returns>A value of zero (0) if the font face was loaded successfully.</returns>
    9. public static FontEngineError LoadFontFace(Font font, int pointSize, int faceIndex)
    10.  
    11. /// <summary>
    12. /// Get the font face(s) and style(s) for the currently loaded font.
    13. /// </summary>
    14. /// <returns>Array containing the names of the font faces and styles.</returns>
    15. public static string[] GetFontFaces()
    16.  
     
    ejx61s and lyha like this.