How to speed up drawing with lvgl 8?


Drawing is really slow, switch page will cost 1.5s. How to speed up?

I touch tabs on the screen to switch page, flush_cb will be called several times, i print every call of flush_cb and its’ call time by clock() (time unit: ms).
In the below log we can see: there is nearly 1.4 second between two flush_cb, and i don’t know what lvgl is doing this time.

I (58340) ####: tabs clicked time: 56730
I (58480) ####: flush_cb start time: 56870
I (58480) EDDIY: flush_cb area rect: 34 x:1 y:31 w:68 h:85;
I (58480) ####: flush_cb end time: 56870
I (58490) ####: flush_cb start time: 56880
I (58490) EDDIY: flush_cb area rect: 35 x:1 y:116 w:68 h:85;
I (58500) ####: flush_cb end time: 56890
I (58510) ####: flush_cb start time: 56900
I (58510) EDDIY: flush_cb area rect: 36 x:1 y:201 w:68 h:85;
I (58520) ####: flush_cb end time: 56910
I (58530) ####: flush_cb start time: 56920
I (58530) EDDIY: flush_cb area rect: 37 x:1 y:286 w:68 h:85;
I (58530) ####: flush_cb end time: 56920
I (58540) ####: flush_cb start time: 56930
I (58540) EDDIY: flush_cb area rect: 38 x:1 y:371 w:68 h:85;
I (58550) ####: flush_cb end time: 56940
I (58560) ####: flush_cb start time: 56950
I (58560) EDDIY: flush_cb area rect: 39 x:1 y:672 w:68 h:85;
I (58570) ####: flush_cb end time: 56960
I (59920) ####: flush_cb start time: 58310
I (59920) EDDIY: flush_cb area rect: 40 x:70 y:30 w:954 h:728;
I (59920) ####: flush_cb end time: 58310

I have empty the flush_cb, set_px_cb code to avoid the influence from hardware.

What MCU/Processor/Board and compiler are you using?


What LVGL version are you using?


What do you want to achieve?

Speed up drawing.

What have you tried so far?


Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

void app_main() {
  int display_pixel_size = display_width * display_height;

  lv_color_t* buf1 = (lv_color_t*)heap_caps_malloc(
    display_pixel_size * sizeof(lv_color_t) / 2, MALLOC_CAP_SPIRAM);
  assert(buf1 != NULL);

  static lv_color_t* buf2 = NULL;

  static lv_disp_draw_buf_t disp_buf;

  /* Initialize the working buffer depending on the selected display.
   * NOTE: buf2 == NULL when using monochrome displays. */
  lv_disp_draw_buf_init(&disp_buf, buf1, buf2, display_pixel_size);

  disp_drv.hor_res  = display_width;
  disp_drv.ver_res  = display_height;
  disp_drv.flush_cb = disp_driver_flush;

  ESP_LOGI(TAG, "address of disp_drv: %p", &disp_drv);

  /* When using a monochrome display we need to register the callbacks:
   * - rounder_cb
   * - set_px_cb */
  disp_drv.rounder_cb = disp_driver_rounder;
  disp_drv.set_px_cb  = disp_driver_set_px;
  disp_drv.set_px_cb = disp_driver_set_px;

  disp_drv.draw_buf = &disp_buf;

  /* Register an input device when enabled on the menuconfig */
  lv_indev_drv_t indev_drv;
  indev_drv.read_cb = touch_driver_read;
  indev_drv.type    = LV_INDEV_TYPE_POINTER;

  /* Create and start a periodic timer interrupt to call lv_tick_inc */
  const esp_timer_create_args_t periodic_timer_args = {
    .callback = &lv_tick_task, .name = "lvgl_tick_periodic_timer"};

    esp_timer_create(&periodic_timer_args, &lvgl_tick_periodic_timer));
                                           LV_TICK_PERIOD_MS * 1000));

  // start_application

  /* Force screen refresh */

  while (1) {
    /* Delay 1 tick (assumes FreeRTOS tick is 10ms */


    // ESP_LOGI(TAG, "lv_task_handler, time: %ld.", clock());

    /* Try to take the semaphore, call lvgl related function on success */
    if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {

  /* A task should NEVER return */

static void lv_tick_task(void* arg) {

Screenshot and/or video


Current hardware

  • MCU: ESP32 (8M PSRAM & 16M Flash)
  • COLOR_DEPTH: 8, also tested 1
  • Display: 1024*758
  • Screen: EPaper (Because i empty the flush_cb, so this will not effect)
  • LVGL Version: v8.2

I have not very much experience with the ESP32, but here it goes:

It seems you use the external SPI RAM. I don’t think this is an good idea, as this kind of RAM is much slower than the internal RAM.
You set up the lvgl working buffer the size of the full frame / 2. I think most of the time 40 rows would be enough. So it would easily fit into internal RAM.

The first six areas which are transferred to display are relatively small, but according the times stamps, only two flushes are within the same tick. The other four are starting on one tick and ends at the next tick.
I would have expected that the start and end of flush are mostly within the same tick.

Do you have any other task running (maybe with higher priority?) and with some busy loop (polling something)?

I don’t think the timing problems are caused by lvgl. It’s something around your code.

1 Like

Thanks a lot, we testing it.

Did you manage to speed it up?

Yeah, we found the problem is set_px_cb, it effect so much, i removed it then the time cost reduce to 400ms.

Here is the result we tested:

  1. set_px_cb has a great impact on performance, which will increase the time consumption by 2.0~2.5 times
  2. Task priority has a great impact on performance, set the priority as high as possible
  3. The number of Buffer rows has a little effect on performance. It can balance high performance and low memory usage
  4. Memory location has little effect on performance
  5. Double Buffer or single Buffer have no effect on performance at all
  6. lv_tick_task has no effect on performance at all
  7. ESP_LOGX has little impact on performance and can be ignored