How do I get better performance from LVGL for a plot app


We are writing an app for a device that gathers waveform data that is 640x480 (yes, it’s hardcoded to that). We need to be able to plot this data as a set of lines (yes, 640 of them) at 60hz. We’re using an NXP IMXRT1064, and I’m able to get this performance with raw drawing without LVGL (using my own memory buffer and line drawing directly to it, using LCDIF single buffered, without even using PXP).

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

NXP IMRT1064 evk with a 640x480 lcd (not the one that came with it).

What LVGL version are you using?

We tried v7 (from github) and v6 something (whatever came with the NXP SDK).

What do you want to achieve?

60hz drawing of 640 lines.

What have you tried so far?

The chart widget can’t handle that much data with any decent performance. Using a canvas widget and our own fast line drawing algorithm (this takes 4ms to draw 640 lines directly to the buffer), in an lvgl task that runs every 16ms (60hz) never exits the main lvgl loop and takes 34ms to draw the screen.

Code to reproduce

Would need to provide a whole zip file. If you need it let me know.

Screenshot and/or video


How are the LVGL display buffers configured (e.g. where you call lv_disp_buf_init)?

SDK_ALIGN(static uint8_t s_frameBuffer[2][DEMO_FB_SIZE], DEMO_FB_ALIGN);
    lv_disp_buf_init(&disp_buf, s_frameBuffer[0], s_frameBuffer[1], LCD_WIDTH * LCD_HEIGHT);

I think it ends up in SDRAM noncached. This came from the default widgets demo from the SDK so I’m not totally sure.

Also, the canvas buffer is in SRAM_OC:

__attribute__((section(".bss.$SRAM_OC"), used))
static lv_color_t s_canvasMem[LV_HOR_RES_MAX][LV_VER_RES_MAX];

Is your plan to use a fullscreen canvas and then put more widgets on top of it? There might be a more optimal way of doing it if that is the case.

(cc @kisvegabor)

No, actually, only a portion of the screen will be the canvas and other widgets will be on top and/or on the left. We’re still not sure how much of the screen will be the canvas as we’re still in the design stages but 640 lines is still the goal we’re aiming at because sometimes our left nav won’t be visible and the plot window will take over the entire horizontal width of the screen (even though there will be a top nav).

Here is one example of what we are thinking:


The chart is really not suitable to display a large amount of data at high speed. Using canvas is much better.

Some thoughts:

  • If possible use the canvas with TRUE_COLOR color format instead of TRUE_COLOR_ALPHA because the former is much faster to draw.
  • Probably it’s much faster to use e.g. 2 pcs of 1/4 screen-sized buffer in internal RAM and copy the buffers into single frame buffers stored in external SRAM. It might have flickerings, neede to be checked.
  • Be sure you have enabled -O3 optimization and all MCU caching.

I’m actually using full screen sized buffers. Does switching to 1/4 size buffers make it more efficient? MCU caching can’t be used if we’re using DMA to get the data to the display. We’re already using LV_IMG_CF_TRUE_COLOR in the canvas.

So, after some more testing and SWO profiling, I’m finding that the MCU is doing most of its work in fill_normal. About 50% of the cpu is there and about 40% appears to be in “sleeping”, the other 10% is random other stuff. I’m wondering why there is so much sleeping. Also, I’m wondering if I might have to make a simpler canvas object that doesn’t base itself off img, which has all this shearing and transform stuff. I’m also wondering if it might be possible to make the canvas not do all this blending since nothing is going to be on top of it. (2.2 MB) Unfortunately, I am still unable to get lv_task_handler to return in any time under 27ms. No matter what I try. I’m attaching a zip of my last ditch effort to get 60fps out of lvgl with plotting to a canvas. If anyone can see anything I may be doing wrong, or any way I can make this work, please let me know. If I can’t get to 60fps we unfortunately won’t be able to use LVGL.

(a long shot in the dark)

If your graph has significant background portion that don’t change from one frame to another, you can try breaking the graph into a grid of canvases (e.g. 8 x 4 in your example) such that only the ones that changed, need to be rendered. Depending on the nature of your data, it may save you a significant update time.