Two screen sized buffers - switching in display driver

##Explanation and background
I have a custom board with a SAMA5D27 processor, 0.5G DDR, 800x480 TFT.

All is running OK (latest version of LittlevGL) and I am playing with the samples before cracking on with my own interface, but I’m having an issue “optimizing” the display driver.

If I use a basic memcpy from the active buffer to a third (cached) buffer it works fine, but takes 50ms to execute which is no use really.

So I’ve now set it up to use 2 full sized screen buffers and believe that one will be written to while the other is being displayed. So all I need to do is effecively pass the pointer of the active buffer to the underlying display routines every time flush is called - there’s a function in existence to display a “base buffer” from a cached area of memory and I should be able to simply pass a new buffer to this.

It works, only takes 10us to run, but I get horrible artefacts on areas of the screen that are being refreshed (e.g. the rotating circle of the lv_test_theme example on tab 2, and the blinking cursor on the same tab). It’s almost like the underlying update routines are actually writing to the buffer that’s being displaye.
If an action such as changing tabs is done on the screen, causing a whole screen scroll, and therefore the majority is being refreshed, it looks fine - so I know the buffers are being redrawn etc. And I’ve debugged it to ensure that “buf_act” is flipping each time flush is called.

Code to reproduce

	CACHE_ALIGNED_DDR static lv_color_t lcd_buf_1[SIZE_LCD_BUFFER];
	CACHE_ALIGNED_DDR static lv_color_t lcd_buf_2[SIZE_LCD_BUFFER];
	cache_clean_region(lcd_buf_1, sizeof(lcd_buf_1));
	cache_clean_region(lcd_buf_2, sizeof(lcd_buf_2));
	static lv_disp_buf_t disp_buf;
	lv_disp_buf_init(&disp_buf, lcd_buf_1, lcd_buf_2, SIZE_LCD_BUFFER);
	lv_disp_drv_t disp_drv;
	disp_drv.buffer = &disp_buf;
	disp_drv.flush_cb = lcd_disp_flush;
static void lcd_disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
	lcdc_show_base(disp_drv->buffer->buf_act, 32, 0);

Any suggestions of what I should be doing welcomed!

Screenshot and/or video

Here’s a screenshot - can’t think how to share a video at the moment!

Thanks for providing a detailed explanation.

Is this equivalent to the size of your display framebuffer in pixels (hor_res*ver_res)?

I assume 32 is the bits per pixel?

Sorry, yes: SIZE_LCD_BUFFER is 800x480.

And, yes, 32 bits per pixel (ARGB). The 0 in the function call is a rotation angle.

Hi Tim,

Could it be that you need to flush your cache just before

lcdc_show_base(disp_drv->buffer->buf_act, 32, 0);


Just a thought.



Sadly not - the lcdc_show_base function does that for me.

Are you sure that lcdc_show_base makes its job immediately? What if you add 100 ms delay before lv_disp_flush_ready?

After lv_disp_drv_register() you can use lv_disp_is_true_double_buf(disp) to check that you are really in true double buffered mode.

Ah okay, sorry not familiar with your platform so was just stabbing in the dark, I had a similar issue on a different platform which was cache related.
I also found that cache mode needed to be Write-Through to work correctly as opposed to Write-Back which also gave me similar artifacts. Although if you had it working correctly with the memcpy method I suspect the cache is probably configured correctly.
If I have any further thoughts I will post again.

Note that several revisions of Cortex-M7 based processors have an errata that causes Write-Through cache to store/load corrupt data, rendering it useless for a real application. The WT cache on that processor usually causes artifacts on the screen (at least in my experience).

If you are using Cortex-M7, it might be worthwhile to try disabling the cache temporarily.

Adding a delay makes no difference (it makes the artefacts slower lol).

lv_disp_is_true_double_buf(disp) is returning true, and adding a breakpoint in every time the flush routine is called shows the address for disp_drv->buffer->buf_act alternating too.

It’s an A5. Caching works fine, in principle, for the LCD - if I just have a single cached buffer and memcpy to it from the lvgl buffers it works well - just too slow to copy so many pixels at a sensible screen refresh rate.

My hunch is that it is a caching issue so may have to delve more into this

(sorry for not yet working out how to quote people - first time I’ve encountered this forum methodolgy and struggling a little!)

Thanks for the heads-up @embeddedt, I was/am using a Cortex-A9 (Xilinx Zynq). I can confirm that video memory buffer cached in write-through mode on this core works fine, but you get artifacts if you use write-back mode. I believe this is because my DMA controller doesn’t have the ability to read from the cache directly so write through is required to make sure the memory is up to date.