LVGL render and display flush on different threads

Is there a common practice for how to split the processing of LVGL rendering + pushing pixels to the display on different threads? (i.e. render thread and flush thread)

It seems like LVGL won’t push the next frame until lv_display_flush_ready is called, which can’t be safely invoked in a separate thread

I saw lv_mutex_* but wouldn’t this cause a deadlock if I called that in the flush thread? Or will lv_timer_*handler no-wait and immediately return when flush is pending to avoid that deadlock?

With FreeRTOS this ended up just being:

TaskHandle_t hFlushTask;
struct {
  lv_display_t* disp;
  lv_area_t area;
  unsigned char* data;
} FlushInfo;

void FlushTask(void*) {
  while (true) {
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

    uint32_t w = lv_area_get_width(&FlushInfo.area);
    uint32_t h = lv_area_get_height(&FlushInfo.area);
    lv_draw_sw_rgb565_swap(FlushInfo.data, w*h);
    display->pushImage(FlushInfo.area.x1, FlushInfo.area.y1, w, h, (uint16_t*)FlushInfo.data);
    lv_display_flush_ready(FlushInfo.disp);
  }
}
void setup() {
  ...

  xTaskCreate(FlushTask, "FlushTask", 4096, nullptr, 1, &hFlushTask);

  lv_display_set_flush_cb(lvDisplay, [](lv_display_t* disp, const lv_area_t* area, unsigned char* data) {
    FlushInfo = {
      .disp = disp,
      .area = *area,
      .data = data
    };

    xTaskNotifyGive(hFlushTask);
  });

  ...
}

(With double-buffering, of course)

Hi,

Can you share the complete code snippit showing how you deal with the double buffering and multiple tasks?

I am struggling with this, and since I am using an ESP32 with 2 cores I would like to use both cores in parallel to see if I can increase the frame rate. It sounds like you have mastered this.

Many thanks in advance.

Hi
Interesting, I currently use 2 buffers (although I think not correctly) and I would like to know how to refresh both in order to try to increase the frame rate.

The best way to increase frame rate is to use a multi lane connection to the display. The best that is out there for MCU’s is going to be MIPI which is a super fast multi lane connection, this is only available on a small number of MCU’s. The next best is going to be either RGB or I8080 depending on the MCU you are using. RGB requires a large amount of memory because of the display not having any GRAM. While you can use this kind of a display with an MCU that has SPIRAM it is not going to give the best in terms of performance. SPIRAM is slow and transferring data in and out of it is really not all that fantastic speed wise. I8080 is better suited for a lot of low cost MCU’s because the IC’s are going to have GRAM. This allows a smaller buffer to be used when transferring.

Besides the connection using DMA memory and double buffering is the way to go. This allows the user code to run at the same time as a buffer is transferring. So that means while one buffer is being sent the second one is being filled. DMA allocation from SRAM is going to be leaps and bounds faster than having to allocate the memory from SPIRAM. This is due to SPIRAM’s speed being divided by 2 if a buffer is being filled and transferred at the same time. When using an I8080 connection is is going to be best to make the buffers small enough to fit into SRAM. You will have a small performance hit for having a buffer size that is less than 1/10th the side of the display but no where near the same kind of a performance hit as having to use SPIRAM.

Take away’s

I8080
double buffering
DMA transfers

If you can get an MCU that has those things and also has a 2D gfx accelerator to help speed up rendering you will see some fairly high frame rates