How to implement VSYNC and double buffering?

I implemented VSYNC as explained here:
https://docs.lvgl.io/master/porting/display.html?highlight=vsync#other-options
Not sure if I did it right, it also needed a lv_task_handler call after the _lv_disp_refr_timer call, otherwise I couldn’t click buttons, they were not animated anymore.

Now I’m trying to implement double buffering with 2 full framebuffers. As a start, I tried to set full_refresh and direct_mode to 1, and used the framebuffers as buf1 and buf2 for driver->draw_buf. This was flickering and I probably can’t use it anyway, because I need to write alternating to the 2 framebuffers, while the other is displayed (I’m using a STM32H7 microcontroller with integrated LCD controller).

Not sure if this works, but I think the one way to do it is the following: First update framebuffer1 with the help of the dirty area list, while framebuffer2 is displayed. Then swap the buffers, and while framebuffer1 is displayed, update framebuffer2. But merge the dirty area list of the previous framebuffer1 step with the framebuffer2 dirty area list and use this merged list to update framebuffer2. This way framebuffer2 is up to date with all changes, without requiring to copy much. Then again with framebuffer1, but with the merged lists from the last framebuffer2 update (without the merge from the previous update) and the new framebuffer1 update.

Usually the dirty lists will be very similar, like one button changed, and maybe the outline is a big bigger or smaller between the 2 framebuffers, when there is some animation effect. So this should be pretty fast, and the only overhead of for example a 60 frames animation is essential to draw the animation part once more, and the dirty area merge, but there are probably only a handful of dirty areas for each step anyway, so should be really fast.

How would I implement this? Can I do this in a custom display driver, or do I need to change the LVGL source code? And I’m wondering if not someone has already done something for a fast VSYNC and double buffer implementation, looks like general a requirement you often have for high performance displays with 60 Hz update rate, if you want to avoid a tearing effect.

This method really works and it was implemented in v7, but was removed from v8. However it turned out that it’s really useful for people, so I added it back in v9.

v9 is still under development but you can try it our from the master branch. The changes in the drivers are summarized here.

If you do:

lv_disp_set_draw_buffers(disp, buf1, buf2, screen_size_in_bytes, LV_DISP_RENDER_MODE_DIRECT);

LVGL will do all the dirty area copying between the buffers automatically.

Looks good. When is the release for v9 planned? I need this for production, so maybe not a good idea to use the master version. Can I backport just this double buffer changes to v8 easily? Also I need it for LVGL within Zephyr.

I estimate the release date of v9 to the end of this summer. So a little bit later… :frowning:

Basically, you need to do what you have described in the first comment. See the docs about it here.

Thanks, end of summer is not too distant, I’ll wait for the next version. Would be nice if there would be a concept for the display driver for waiting for vsync as well in it, without manually removing timers etc., and for switching the framebuffer addresses for the controller, then it would be super easy for me (and others with similar architectures) to use it.

But what is important is that there is no read of write access to the currently displayed framebuffer. With the STM32H757, sometimes we see glitches, were parts of the screen is shifted horizontally a few hundred pixels for a frame. We suspect this happens, when the ARM core is accessing the same memory the display peripheral wants to read, because it only happens when we write to the framebuffer (currently only one framebuffer is used). If it is different addresses, then the SDRAM should manage it, because it has multiple internal buffers for fast interleave access. Of course, wouldn’t be a problem with the 2 dirty area lists as I described. I guess ideal would be 2 SDRAM chips (the microcontroller supports this), but it is not our hardware.

We would like to iron out the main architectural changes of v9 first. After that we can look into how to make VSYNC triggering simpler. I agree that it would be useful for many users.

I had the same problem with stm32f7 family in my case .Frame buffers (16bit) are in external SDRAM.
After deep research this forum I reworked driver and got working driver. It’s not very fast but works with no tears effect who is very ugly for me :(.
Please look at (maybe it helps You)
tft.c (24.7 KB)
tft.h (592 Bytes)

1 Like

Thanks, I’ll try it. But there are lots of commented code, unused code etc., do you have a final version?

And what is the state of LVGL, I didn’t follow the progress, is it maybe meanwhile implemented in the library itself, as planned for version 9?