LVGL Scale performance Issues

Hi, I’m currently building a visualization of a large Sensor group for a Alarm system. I need to be able to visualize 32 Sensors values at the same time.

I am trying to add a scale to my Bars to visualize a minimum and maximum Value. The Performance of my entire breaks down when doing that. I really don’t know why.

static void BarInsideFrameCreate(lv_obj_t *pParent, BarObject_t *pBarObject)
{
    static bool styleInitialized = false;

    const int16_t barWidth = 15;  // Adjust the width to fit text
    const int16_t barHeight = 140;  // Adjust the height for appearance clarity
    const int16_t textHeight = 20;

    // Define grid columns and rows for the bar layout
    static int32_t barColumns[] = {barWidth, barWidth, barWidth, LV_GRID_TEMPLATE_LAST};
    static int32_t barRows[] = {barHeight, textHeight, textHeight, textHeight, LV_GRID_TEMPLATE_LAST};

    lv_obj_set_style_pad_gap(pParent, 0, 0);
    lv_obj_set_grid_dsc_array(pParent, barColumns, barRows);
    lv_obj_set_size(pParent, barWidth, barHeight); // Parent size based on grid dimensions
    lv_obj_center(pParent); // Center the parent object

    static lv_style_t labelStyle;

    if(styleInitialized == false)
    {
        lv_style_init(&labelStyle);
        lv_style_set_text_color(&labelStyle, lv_color_make(0xFF, 0xFF, 0xFF)); // White text
        lv_style_set_text_font(&labelStyle, &lv_font_montserrat_16); // Font for labels
    }

    // lv_obj_add_style(pParent, &mainStyle, 0); // Apply main style to the parent bar

    // Create text fields inside the bar
    lv_obj_t *label1 = lv_label_create(pParent);
    lv_label_set_text(label1, "CO2"); // Add text
    lv_obj_set_grid_cell(label1, LV_GRID_ALIGN_CENTER, 0, 3,
                         LV_GRID_ALIGN_CENTER, 1, 1); // Position in cell (0, 0)
    lv_obj_add_style(label1, &labelStyle, 0); // Style for label1

    lv_obj_t *label2 = lv_label_create(pParent);
    lv_label_set_text(label2, "PPM"); // Add text
    lv_obj_set_grid_cell(label2, LV_GRID_ALIGN_CENTER, 0, 3,
                         LV_GRID_ALIGN_CENTER, 2, 1); // Position in cell (1, 0)
    lv_obj_add_style(label2, &labelStyle, 0); // Style for label2

    // Styles
    static lv_style_t mainStyle;
   
    if(styleInitialized == false)
    {
      lv_style_init(&mainStyle);
      lv_style_set_bg_color(&mainStyle, lv_color_make(0x00, 0x0, 0x0)); // Dark blue background
      lv_style_set_radius(&mainStyle, 5); // Rounded corners for the bar
    }

    lv_obj_t *pBar = screenAddBarToGrid(pParent, 1, 1, 0, 1);
    lv_obj_add_style(pBar, &mainStyle, LV_PART_MAIN); // Apply main style to the bar

    (void) screenAddScaleToBar(pParent); // Mega buggy...

    if(pBarObject!=NULL)
    {
        pBarObject->pParent = pParent;
        pBarObject->pBar = pBar;
        pBarObject->pLabel1 = label1;
        pBarObject->pLabel2 = label2;
    }
    styleInitialized = true; // Mark styles as initialized
}

lv_obj_t *screenAddScaleToBar(lv_obj_t *pBar)
{
  static bool styleInitialized = false;

  lv_obj_t *pScale = lv_scale_create(pBar);
  lv_obj_set_size(pScale, lv_obj_get_width(pBar), lv_obj_get_height(pBar));
  lv_scale_set_mode(pScale, LV_SCALE_MODE_VERTICAL_LEFT);
  lv_obj_align(pScale, LV_ALIGN_CENTER, 0, 0);

  lv_scale_set_total_tick_count(pScale, 100);
  lv_scale_set_major_tick_every(pScale, 10);

  lv_obj_set_style_length(pScale, 20, LV_PART_INDICATOR);
  lv_scale_set_range(pScale, 0, 100);

  static const char *custom_labels[3] = {"", "", NULL};
  lv_scale_set_text_src(pScale, custom_labels);

  static lv_style_t indicatorStyle;
  if (styleInitialized == false)
  {
    lv_style_init(&indicatorStyle);

    ////////////////////////////
    // Major tick properties
    ////////////////////////////
    lv_style_set_line_color(&indicatorStyle, lv_color_hex(0xFF0000));
    lv_style_set_width(&indicatorStyle, 5U);      // Tick length
    lv_style_set_line_width(&indicatorStyle, 0U); // Tick width

  }

  lv_obj_add_style(pScale, &indicatorStyle, LV_PART_INDICATOR);

  static lv_style_t mainLineStyle;
  if (styleInitialized == false)
  {
    lv_style_init(&mainLineStyle);

    ////////////////////////////
    // Main line properties
    ////////////////////////////

    lv_style_set_line_color(&mainLineStyle, lv_color_hex(0xFF0000));
    lv_style_set_line_width(&mainLineStyle, 0U); // Tick width
  }

  lv_obj_add_style(pScale, &mainLineStyle, LV_PART_MAIN);
  lv_obj_center(pScale);
  
  ////////////////////////////
  // Add a section
  ////////////////////////////

  static lv_style_t section_label_style;
  if (styleInitialized == false)
  {
    lv_style_init(&section_label_style);

    /////////////////////////
    // Label style properties
    /////////////////////////

    lv_style_set_text_font(&section_label_style, LV_FONT_DEFAULT);
    lv_style_set_text_color(&section_label_style, lv_color_hex(0xFFFFFF));
    lv_style_set_text_letter_space(&section_label_style, 10);
    lv_style_set_text_opa(&section_label_style, LV_OPA_50);

    lv_style_set_line_color(&section_label_style, lv_color_hex(0xFFFFFF));
    lv_style_set_width(&section_label_style, 20U);     // Tick length
    lv_style_set_line_width(&section_label_style, 2U); // Tick width

    lv_style_set_width(&section_label_style, 10U); // Tick length
  }

  /////////////////////////
  // Configure section styles
  /////////////////////////

  lv_scale_section_t *section = lv_scale_add_section(pScale);
  lv_scale_section_set_range(section, 1, 10);
  lv_scale_section_set_style(section, LV_PART_INDICATOR, &section_label_style);

  lv_scale_section_t *section_2 = lv_scale_add_section(pScale);
  lv_scale_section_set_range(section_2, 85, 95);
  lv_scale_section_set_style(section_2, LV_PART_INDICATOR, &section_label_style);

  styleInitialized = true; // Mark styles as initialized

  return pScale;
}

What am I missing here?

Best regards

If you add scales with 100 ticks to that many bars it’s a lot of small draw tasks to handle.
Just to check, does it get faster if you have only 10 minor ticks per scale?

An other idea: check if LV_USE_ASSERT_OBJ and LV_USE_ASSERT_MEM_INTEGRITY are disabled.

Also, what kind of MCU do you use? If possible please upload a working simulator project to GitHub so that we can see it together and potentially run som profilers.

Because of the sheer number of scales you are using you may want to opt to render the scales yourself. It will eliminate quite a bit in the way of overhead of you do that. The scale widget in LVGL is written so that it is able to render a slew of different scale types so there is going to be quite a bit in the way of conditional checks that are done each time rendering takes place to know what kind of a scale to render.

What you are rendering is really not all that complex so it wouldn’t be too terribly hard to do.

Replacing scales with images is also an option.

It looks like the scale gets rendered if a value is changed in the widget. Does this really need to be done? Having it render the whole scale over every time the indicator value changes seems a bit excessive especially if the indicator has only moved a very small amount. Rendering only the ticks that may have changed as a result of the indicator moving would be the thing to do. as an example.

Say on a 0-100 scale you have the value set to 50 and then the value changes to 60. Rendering all of the ticks over again for the entire scale would be an excessive thing to do considering the only ticks that need to be updates are the ones from 50 to 60. The same thing would be true if setting the value to 50 when it was 60. only 10% of the ticks would need to be rendered. That would be a significant reduction in work being done that’s for sure.

Using Images could be quite difficult because of the Memory constraints for my Device. Thanks for the Input tough.

Everything is running on a stm32u5gj-dk2.

This is the Project.
CANLineX2Graphics-main.zip (10.0 KB)
You’ll have to link it to the Simulator yourself.:wink:

Thanks a lot for offering help.

This is a very interessting input. I might know to little about the inner workings of LVGL. A lot of thinks seem to run in the Background. My Scale is a child object of my Bar so things are getting updated in the Background a Million times without knowing it… Do you have some refference how I can do selective rendering?

Cheers