Full screen contents not sent to disp_drv.flush_cb when one object is changed

Description

When i update one object’s value in a screen (eg a label’s text), the screen contents sent to the disp_drv.flush_cb do not contain the correct values (i.e, unchanged from before) for all objects in the rest of the screen. Now, i understand that lvgl is only supposed to send the changed part inside the callback, but in my case, i am using two buffers, with sizes equal to the display&screen size. In this case, the contents of the whole screen size are received (i verified this by putting break point in the “flush_cb” and looking at the values of area -> x1, y1, x2, y2 which correspond to 0,0 and height, width of full display). Since the received pixel data (inside the lv_color_t * color_p object in the callback) does not correctly reflect the existing states of the other objects in the screen, i am left with just the updated part being displayed on the screen.

Note that i am using transparency, and using lvgl as a OSD over live video.

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

Xilinx Zynq Arm 9, using Xilinx SDK

What do you experience?

As mentioned above, when i update one object’s value on the screen, i expect that to update, but everything else on the screen to remain the same. However, the flush_cb sends data for the FULL screen, and puts everything else to zero, apart from what was updated recently. This is unexptedted. I have been trying to findo something wrong on my side for several days but i cannot find it. I have read through the relevant documentation few times. I suspect this might have to do with some setting perhaps in lv_conf.h, but i cannot find it. If that is not the case, then it has to be a bug. However, i do not see the same issue on the simulator. I am attaching my flush_cb code below as well…initially i susptected it but i can’t see how that can be wrong.

What do you expect?

Correct values for the full screen inside the callback.

Code to reproduce

Here’s my display driver callback for reference. I am attaching this there as that is usually considered the suspect from other posts i read.

/**** Display Driver Callback *****/
void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{

    int32_t x, y;

    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
    uint pxCnt=0;
    for(y = area->y1; y <= area->y2; y++)
    {
        for(x = area->x1; x <= area->x2; x++)
        {
            //put_px(x, y, *color_p);
        	memcpy(&(vdmaFrameBuffer[pxCnt]), color_p, sizeof(lv_color_t));

        	pxCnt += sizeof(lv_color_t);
            color_p++;
        }
    }

	/*
	 * Flush the framebuffer memory range to ensure changes are written to the
	 * actual memory, and therefore accessible by the VDMA.
	 */
    Xil_DCacheFlushRange((unsigned int) vdmaFrameBuffer, 1024*768*4); 

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
}

I have verified using breakpoints that everytime, inside the callback, the values of x1 = y1 =0 and x2 and y2 correspond to the full screen width (1024) and height (768) respectively. That is, the data for the full screen is sent, not just the part to be updated.

Following are my settings in lv_conf.h (The ones i changed…rest everything is at default):

/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX          (1024)
#define LV_VER_RES_MAX          (768)

/* Color depth:
#define LV_COLOR_DEPTH     32

/* Swap the 2 bytes of RGB565 color.
 * Useful if the display has a 8 bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP   0

#define LV_COLOR_SCREEN_TRANSP    1

Following is how i initialize my display driver in lvgl_hal_init()

    /*-----------------------------
     * Create a buffer for drawing
     *----------------------------*/
	// See: https://docs.lvgl.io/v7/en/html/porting/display.html
    /*A static or global variable to store the buffers*/
    static lv_disp_buf_t disp_buf; 

    /*Static or global buffer(s). The second buffer is optional*/
    static lv_color_t buf_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];
    static lv_color_t buf_2[LV_HOR_RES_MAX * LV_VER_RES_MAX];

    /*Initialise `disp_buf` with the buffer(s) */
    lv_disp_buf_init(&disp_buf, buf_1, buf_2, LV_HOR_RES_MAX * LV_VER_RES_MAX);


    /*-----------------------------------
     * Register the display in LVGL
     *----------------------------------*/

    //lv_disp_drv_t disp_drv;                       /*Descriptor of a display driver*/ // Moved to .h file
    lv_disp_drv_init(&disp_drv);                    /*Basic initialization*/

    /*Set up the functions to access to your display*/

    /*Set the resolution of the display*/
    disp_drv.hor_res = LV_HOR_RES_MAX;
    disp_drv.ver_res = LV_VER_RES_MAX;

    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = my_flush_cb;

    /*Set a display buffer*/
    disp_drv.buffer = &disp_buf;



    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);

Screenshot and/or video

Following is how it is supposed to look (In this case, i am able to show this by manually re-loading the screen every time after changing any value on screen (using lv_disp_load_scr(screen)):

Following is how it actually looks:


Note that as the focus changes from one object in group to next, only those objects remain on screen and rest all disappears.

(p.s Pls ignore the noisy pink background…i do not have live video source connected in the background and it’s just “playing” un-initialized memory buffer in the background.)

If you are using two full-size screen buffers, I believe they are treated by LVGL as double buffering. In that case, you are expected to just be switching your display’s framebuffer address between them. I don’t think there should be a need to copy any data. However, I suspect that you have no choice but to copy because you are using LVGL as an overlay over an external video feed.

I am not entirely sure what the best solution is here; perhaps @kisvegabor can help.

You’re right in saying that just switching frame buffer address would be the best thing to do. Copying the frame every time, as in this case, is just wasteful. Eventually that’s how i want that to be, however for now i am trying to keep things simple and get the whole system up first.
However, for the purpose of this issue, it should not matter. Because if the passed frame buffer does not contain the correct pixel values, then whether i just change the framebuffer address or copy the pixels out to a different framebuffer, the result would be the same in terms of what appears on the screen (and what doesn’t).
Appreciate your help.
@kisvegabor Any comment on what might be happening here?

I have an update on this.

I did some more testing and actually found out that i CAN reproduce this in the Simulator. I was previously using the default display device setup on the (Visual Studio) simulator, as follows, which does NOT cause the issue :

    static lv_disp_buf_t disp_buf1;
    static lv_color_t buf1_1[LV_HOR_RES_MAX * 120];
    lv_disp_buf_init(&disp_buf1, buf1_1, NULL, LV_HOR_RES_MAX * 120);

When i change this to the following (as on my ARM9 device), the issue can be reproduced on the simulator, too:

    static lv_disp_buf_t disp_buf1;
    static lv_color_t buf1_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];
    static lv_color_t buf1_2[LV_HOR_RES_MAX * LV_VER_RES_MAX];
    lv_disp_buf_init(&disp_buf1, buf1_1, buf1_2, LV_HOR_RES_MAX * LV_VER_RES_MAX);

I went ahead and tried some more combinations, to narrow down on when this happens and when it doesn’t:

The following DOES NOT show the issue.

    static lv_disp_buf_t disp_buf1;
    static lv_color_t buf1_1[LV_HOR_RES_MAX * LV_VER_RES_MAX];
    lv_disp_buf_init(&disp_buf1, buf1_1, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX);

The following DOES show the issue:

    static lv_disp_buf_t disp_buf1;
    static lv_color_t buf1_1[LV_HOR_RES_MAX * 120];
    static lv_color_t buf1_2[LV_HOR_RES_MAX * 120];
    lv_disp_buf_init(&disp_buf1, buf1_1, buf1_2, LV_HOR_RES_MAX * LV_VER_RES_MAX);

So in conclusion, i can see that the issue happens:

  • When two buffers are used
  • Whether buffers are of partial or full screen height does NOT matter

So it looks more like a bug at this point. Hopeufully the information i have provided will be enough to reproduce this issue and get to the root-cause on the developers’ end. Please have a look. Thank you.