takkaO / OpenFontRender

TTF font render support library for microcomputer.
Other
112 stars 17 forks source link

OpenFontRenderer memory requirements of loadFont() function #47

Closed hnikolov closed 4 months ago

hnikolov commented 4 months ago

Hello, this issue is about the memory requirements for loadFont() function of OpenFontRenderer. I am using OpenFontRender v1.2 together with TFT_eSPI library (2.5.43). Using Arduino IDE 2.3.2, I can build the Noto_Font_Demo (https://github.com/takkaO/OpenFontRender/tree/master/examples/TFT_eSPI/Noto_Font_Demo) for Teensy 4.1 board.

When I build the Noto_Font_Demo, I see that the OpenFontRenderer loadFont() function uses ~60-70KB of memory, regardless of the size of the font data to be loaded. This is (at least relatively) large amount of memory for an embedded system. Is such ~60-70KB memory usage of the loadFont() function to be expected? Can the memory usage be optimized/reduced?

OpenFontRenderer: Noto_Font_Demo with loading the NotoSans_Bold font

Memory Usage on Teensy 4.1: FLASH: code:158312, data:45244, headers:8408 free for files:7914500 RAM1: variables: 20128, code: 155608, padding: 8232 free for local variables: 340320 RAM2: variables:12416 free for malloc/new:511872

OpenFontRenderer: Noto_Font_Demo without loading the NotoSans_Bold font

Memory Usage on Teensy 4.1: FLASH: code:103240, data:27472, headers:8548 free for files:7987204 RAM1: variables: 13984, code: 100536, padding: 30536 free for local variables: 379232 RAM2: variables:12416 free for malloc/new:511872

Thank you for the support!

takkaO commented 4 months ago

@hnikolov

Please tell me where parameters does "memory" refer to? (RAM or FLASH?) Also, can you provide a sample code to measure the memory usage of the loadFont function?

Thank you.

hnikolov commented 4 months ago

@takkaO

Hello. I am referring to the RAM memory. I build the Noto_Font_Demo code: (https://github.com/takkaO/OpenFontRender/tree/master/examples/TFT_eSPI/Noto_Font_Demo) for Teensy 4.1 board. I use the Arduino GUI. The last output message from the compilation in the Arduino 'Output' console/window is the "memory statistics":

image

Then, I comment lines 85 till 88 in the Arduino sketch file (Noto_Font_Demo.ino):

84 // Load the font and check it can be read OK 85 // if (ofr.loadFont(TTF_FONT, sizeof(TTF_FONT))) { 86 // Serial.println("Initialise error"); 87 // return; 88 // }

And build the demo again. The output info as as follows:

image

So, if I add the 'free for local variables' with the "padding" in both cases, I get 348_552 Bytes and 409_768 Bytes respectively. The difference when I subtract the two numbers is the effect of not calling ofr.loadFont() function, i.e, 61_216 Bytes.

Hope my explanation clarifies. Thank you!

P.S. I just noticed that also the code part of RAM1 differs with 55_072 Bytes between the two versions.

takkaO commented 4 months ago

@hnikolov

I have investigated the results under several conditions.


Memory Usage on Teensy 4.1: Faster
  FLASH: code:158312, data:45244, headers:8408   free for files:7914500
   RAM1: variables:20128, code:155608, padding:8232   free for local variables:340320
   RAM2: variables:12416  free for malloc/new:511872

Total Memory: 348552


Memory Usage on Teensy 4.1: Faster no load
  FLASH: code:103240, data:27472, headers:8548   free for files:7987204
   RAM1: variables:13984, code:100536, padding:30536   free for local variables:379232
   RAM2: variables:12416  free for malloc/new:511872

Total Memory: 409768


Memory Usage on Teensy 4.1: Smallest
  FLASH: code:80228, data:36776, headers:8944   free for files:8000516
   RAM1: variables:11424, code:77760, padding:20544   free for local variables:414560
   RAM2: variables:12416  free for malloc/new:511872

Total Memory: 435104


Memory Usage on Teensy 4.1: Smallest no load
  FLASH: code:33492, data:19004, headers:8940   free for files:8065028
   RAM1: variables:5280, code:31024, padding:1744   free for local variables:486240
   RAM2: variables:12416  free for malloc/new:511872

Total Memory: 487984


And loadFont function is implemented as below.

FT_Error OpenFontRender::loadFont(enum OFR::LoadFontFrom from) {
    FT_Face face;
    FT_Error error;
    FontDataInfo info = {from, _debug_level};

    if (g_NeedInitialize) {
        error = FT_Init_FreeType(&g_FtLibrary);
        if (error) {
            //debugPrintf((_debug_level & OFR_ERROR), "FT_Init_FreeType error: 0x%02X\n", error);
            return error;
        }
        g_NeedInitialize = false;
    }

    // 現在の引数は適当
    error = FTC_Manager_New(g_FtLibrary, _cache.max_faces, _cache.max_sizes, _cache.max_bytes, &ftc_face_requester, &info, &_ftc_manager);
    if (error) {
        debugPrintf((_debug_level & OFR_ERROR), "FTC_Manager_New error: 0x%02X\n", error);
        return error;
    }

    error = FTC_Manager_LookupFace(_ftc_manager, &_face_id, &face);
    if (error) {
        debugPrintf((_debug_level & OFR_ERROR), "FTC_Manager_LookupFace error: 0x%02X\n", error);
        return error;
    }

    error = FTC_CMapCache_New(_ftc_manager, &_ftc_cmap_cache);
    if (error) {
        debugPrintf((_debug_level & OFR_ERROR), "FTC_CMapCache_New error: 0x%02X\n", error);
        return error;
    }

    error = FTC_ImageCache_New(_ftc_manager, &_ftc_image_cache);
    if (error) {
        debugPrintf((_debug_level & OFR_ERROR), "FTC_ImageCache_New error: 0x%02X\n", error);
        return error;
    }

    if (FT_HAS_VERTICAL(face) == 0) {
        // Current font does NOT support vertical layout
        _flags.support_vertical = false;
    } else {
        _flags.support_vertical = true;
    }

    return FT_Err_Ok;
}

As you can see from the code, most of the contents of the loadFont function are calls to FreeType library code. I tried commenting out some of the factors (debugPrint, etc.), but that had little effect.

All I can say at present is that the build option optimization should be set to Smallest to save memory. However, changing the version of the FreeType library may solve this problem.

Thank you.

hnikolov commented 4 months ago

@takkaO

Thank you very much for this analysis! I quickly checked as well and indeed, most memory is used by function FT_Init_FreeType(), followed by functions FTC_Manager_New and FTC_ImageCache_New().

I guess this issue can be closed now. All the best! :)