lvgl / lv_drivers

TFT and touch pad drivers for LVGL embedded GUI library
https://docs.lvgl.io/master/porting/index.html
MIT License
291 stars 310 forks source link

The wayland driver has a bug. #244

Closed 904221150 closed 1 year ago

904221150 commented 1 year ago

My English is not good that some sentences may be expressed incorrectly. If the statement is not accurate, try translating Chinese into English. 我的英语不是很好,部分句子可能表述不对。如果表述不准确,可以试试翻译将中文翻译成英文。

When I was using wayland, I found a bug that made the animation play incomplete 当我使用wayland时,我发现一个bug,这个bug使动画播放不完整 1

The bug is caused by the following code 这个bug是由下面这部分代码产生

else if (buffer->busy)
    {
        LV_LOG_WARN("skip flush since wayland backing buffer is busy");
        printf("skip flush since wayland backing buffer is busy\n");
        lv_disp_flush_ready(disp_drv);
        return;
    }

The wl_buffer is always busy because it is submitted many times in a frame 这是由于一帧中多次提交wl_buffer,导致wl_buffer总是处于忙碌状态

I tried to change it 我试过修改

#define LV_INV_BUF_SIZE 1

This keeps lvgl constantly in full screen refresh, and even then wl_buffer is often busy. But the weston I'm using can play 1080p@30 videos, and it should play lvgl. 这使lvgl经常处于全屏刷新,但即使这样wl_buffer也经常busy。但是我用的weston可以播放1080p@30的视频,不应该播不了lvgl。

So I removed the code about buffer->busy, and then everything works, is this method feasible? 所以我删除了buffer->busy这部分代码,然后一切都正常了,这个方法可行吗?

kisvegabor commented 1 year ago

It seems the Wayland driver get popular. @WallaceIT @simplejack-src could you check this out?

WallaceIT commented 1 year ago

I need some time to investigate, maybe @simplejack-src has some ideas?

HR1025 commented 1 year ago

@WallaceIT

LVGL uses single thread to process UI related content to avoid data race, but now maybe the wayland flush take too much time.

In fact, I also encountered this problem. I think that's one of the reasons. https://github.com/lvgl/lv_drivers/pull/249

HR1025 commented 1 year ago

Except LVGL, I also run electron with wayland and it seems nothing unusual.

904221150 commented 1 year ago

@HR1025 the problem was not solved when my project added your code as well as "#define LV_INV_BUF_SIZE 1". In theory, using #define LV_INV_BUF_SIZE 1 should reduce the number of wayland flush,but wayland is still busy. so, I guess wayland may be the source of the problem.I think my wayland version is low, leads to lower communication efficiency of lvgl. My weston is 8.0.0 and wayland is 1.18.0. What is your Wayland version?I go to try.

我在使用”#define LV_INV_BUF_SIZE 1“的情况下,添加了你的代码,但没有解决这个问题。理论上讲使用了”#define LV_INV_BUF_SIZE 1“后,能减少wayland的刷新次数,而避免wayland刷新过多的问题,但在这种情况下wayland依旧经常忙碌。所以,我猜测wayland才可能是问题的源头,感觉有可能是wayland版本太低,导致lvgl的通信效率低。我的weston是8.0.0,wayland1.18.0,请问一下你的wayland版本是多少?我去试试。

HR1025 commented 1 year ago

@904221150 weston 10.0

Another possibility is the performance of your device. Render with gpu or cpu,which? Runing the demo and observe the system payload I think you can try.

HR1025 commented 1 year ago

@904221150 Could you try the following code?

static void _lv_wayland_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
    struct window *window = disp_drv->user_data;
    struct buffer_hdl *buffer = &window->body->buffer;

    const lv_coord_t hres = (disp_drv->rotated == 0) ? (disp_drv->hor_res) : (disp_drv->ver_res);
    const lv_coord_t vres = (disp_drv->rotated == 0) ? (disp_drv->ver_res) : (disp_drv->hor_res);

    /* If private data is not set, it means window has not been initialized */
    if (!window)
    {
        LV_LOG_ERROR("please intialize wayland display using lv_wayland_create_window()");
        return;
    }
    /* If window has been / is being closed, or is not visible, skip rendering */
    else if (window->closed || window->shall_close)
    {
        lv_disp_flush_ready(disp_drv);
        return;
    }
    /* Return if the area is out the screen */
    else if ((area->x2 < 0) || (area->y2 < 0) || (area->x1 > hres - 1) || (area->y1 > vres - 1))
    {
        lv_disp_flush_ready(disp_drv);
        return;
    }
    else if (window->resize_pending)
    {
        LV_LOG_TRACE("skip flush since resize is pending");
        lv_disp_flush_ready(disp_drv);
        return;
    }

#if (LV_COLOR_DEPTH == BYTES_PER_PIXEL * 8)              /* memory align */ \
    && !(LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8)     /* not RGB332 */
    int32_t bytes_pre_pixel = BYTES_PER_PIXEL;
    int32_t x1 = area->x1, x2 = area->x2 <= disp_drv->hor_res - 1 ? area->x2 : disp_drv->hor_res - 1;
    int32_t y1 = area->y1, y2 = area->y2 <= disp_drv->ver_res - 1 ? area->y2 : disp_drv->ver_res - 1;
    int32_t act_w = x2 - x1 + 1;

    for (int y = y1; y <= y2; y++)
    {
        lv_memcpy((uint8_t *)buffer->base + ((y * disp_drv->hor_res + x1) * bytes_pre_pixel), color_p, act_w * bytes_pre_pixel);
        color_p += act_w;
    }
#else
#if !(LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8)
#error "Unsupport LV_COLOR_DEPTH, support LV_COLOR_DEPTH: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)"
#endif
    int32_t x;
    int32_t y;
    for (y = area->y1; y <= area->y2 && y < disp_drv->ver_res; y++)
    {
        for (x = area->x1; x <= area->x2 && x < disp_drv->hor_res; x++)
        {
            int offset = (y * disp_drv->hor_res) + x;
            uint8_t * const buf = (uint8_t *)buffer->base + offset;
            *buf = ((0x07 * color_p->ch.red)   << 5) |
                ((0x07 * color_p->ch.green) << 2) |
                ((0x03 * color_p->ch.blue)  << 0);
            color_p++;
        }
    }
#endif

    wl_surface_damage(window->body->surface, area->x1, area->y1,
                    (area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1));

    if (buffer->busy)
    {
        LV_LOG_WARN("skip flush since wayland backing buffer is busy");
        lv_disp_flush_ready(disp_drv);
        return;
    }

    if (lv_disp_flush_is_last(disp_drv))
    {
        if (window->body->surface_configured) {
           wl_surface_attach(window->body->surface, buffer->wl_buffer, 0, 0);
           wl_surface_commit(window->body->surface);
        }
        buffer->busy = true;
        window->flush_pending = true;
    }

    lv_disp_flush_ready(disp_drv);
}

Maybe this will solve your problem, Thanks.

904221150 commented 1 year ago

@HR1025 I tried your new method to check buffer->busy after wl_surface_damage. "skip flush" error reporting has also almost disappeared. This method is similar to removing the “buffer->busy”,which also causes the screen to tear. But this method is better than deleting “buffer->busy”, which has a better display. 我试了你的新方法,在wl_surface_damage后检查buffer->busy,“skip flush”报错也几乎消失了,但和我注删除“buffer->busy”类似,也会出现画面撕裂情况,但这种方法的显示效果好于删除“buffer->busy”。

In addition, I also checked the cpu usage. The good news is that the cpu performance is confirmed: 1080P frames cost 50ms. The bad news is that _lv_wayland_flush cost 10ms in 1080P frames.It seems that in addition to the wayland problem, I have to look at the opengl acceleration lvgl, which adds another puzzle. 此外,我也检查了cpu占用情况,好消息是确认了cpu性能:处理1080P的帧需要花费50ms,坏消息是1080P的帧_lv_wayland_flush花费10ms,大概率cpu性能不足了。看来除了wayland问题,我还得看opengl加速lvgl了,又加多一个难题了。

HR1025 commented 1 year ago

Do you use the standard memory operation interface? Add below define to lv_conf.h.

#define LV_STDLIB_INCLUDE <stdlib.h>
#define LV_MALLOC       malloc
#define LV_REALLOC      realloc
#define LV_FREE         free
#define LV_MEMSET       memset
#define LV_MEMCPY       memcpy

LVGL in order to be able to run in an environment like MCU , implement a set of memory operation interfaces. Their efficiency is far lower than the native interface of Linux.

I think _lv_wayland_flush don't need so much time to process.

ghost commented 1 year ago

Apologies for the late response, life has dealt me a low hand on a bad turn. The root cause of this issue (and similar ones), comes down to the fact that the wayland driver only allocates a single backing buffer (per window/decoration/etc.). When this is busy (i.e. submitted to the wayland compositor), the driver is unable to continue drawing until this buffer becomes free (i.e. the wayland compositor returns the buffer).

This is less then ideal, and a more verbose solution is to have the driver allocate backing buffers on demand (thus allowing more the one buffer to be in flight between the client and compositor). I had started this transition (see #250) but unfortunately, it doesn't look like I'll be able to complete it (at least not in the near future). I've posted my WIP up so anyone can continue the crusade.

All the best, simplejack

kisvegabor commented 1 year ago

Thank you, for looking into it.

It seems the Wayland driver is getting popular and it'd be great to have good support for it.

How broken it is in it's current state? Is it working with some settings?

904221150 commented 1 year ago

@HR1025 I tried a similar change in lvgl8.3, but the speed didn't change, presumably my platform was underperforming. 我在lvgl8.3尝试了下类似的的修改,但速度没有变化,这大概是我的平台性能不足了。

904221150 commented 1 year ago

@kisvegabor Thank you for your reply. I will try again to solve this problem. 我也再尝试一下,看能不能解决这个问题

904221150 commented 1 year ago

@kisvegabor I've added some logs that make it easy to see what's going on in lvgl wayland 我添加了些log,通过这些log可以很容易看出lvgl目前的情况

wayland_flush time:0
wayland_flush time:0
wayland_flush time:0
wayland_flush time:0
wayland_flush time:0
start:5362, refr_invalid_areas time:3, elaps:3
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
start:5394, refr_invalid_areas time:1, elaps:1
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
start:5424, refr_invalid_areas time:2, elaps:2
wayland_flush time:0
wayland_flush time:0
wayland_flush time:0
start:5455, refr_invalid_areas time:1, elaps:1
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
start:5485, refr_invalid_areas time:1, elaps:1
wayland_flush time:0
wayland_flush time:0
wayland_flush time:0
wayland_flush time:0
wayland_flush time:0
start:5515, refr_invalid_areas time:3, elaps:3
skip flush since wayland backing buffer is busy
skip flush since wayland backing buffer is busy
start:5547, refr_invalid_areas time:1, elaps:1

wayland_flush time:%d //_lv_wayland_flush runs properly skip flush since wayland backing buffer is busy //_lv_wayland_flush error start:%d, refr_invalid_areas time:%d, elaps:%d //Every frame

I don't think the current solution will make wayland work properly. My remedy is not recommended. It doesn't completely solve the problem. 我觉得目前没有方法能使wayland正常工作,我的补救方法并不推荐拿来用,这种方法并不能完全解决这个问题。

kisvegabor commented 1 year ago

I don't know much about Wayland, but can't we simply poll a "busy flag" in case of "skip flush since wayland backing buffer is busy" while the backing buffer becomes available?

WallaceIT commented 1 year ago

Hi,

I found some time to work on the Wayland driver. Can you please try the version available in draft PR #253 ?

The code there adds a second buffer to the Wayland driver, so one buffer is updated while another one is being displayed. In my tests, this eliminates the "busy buffer" issue.

Please note that the PR is still in draft state!

kisvegabor commented 1 year ago

I'd be happy to try it out, but I'm not sure if there is a way to test it on conventional Linux Mint?

WallaceIT commented 1 year ago

Hi @kisvegabor

You should be able to test the Wayland driver using Weston, that is available in Ubuntu (and thus also in Linux Mint, I think).

HR1025 commented 1 year ago

In raw Linux environment, weston can be cross compile.

Although this is complex, it is still achievable. In fact, that's what I did.

If your system is based on Debian (UbuntuDeepin and maybe others), the command below can solve your dependencies:

# device input
sudo apt install libxkbcommon-dev
# wayland dependencies
sudo apt install libwayland-dev
sudo apt install wayland-protocols libwayland-bin
# weston
sudo apt install weston

Maybe there are other dependencies, but I don't remember very clearly.

You can simply run weston, it will run a virtual window. And use https://github.com/lvgl/lv_port_pc_eclipse to test.

Good Luck

@kisvegabor

904221150 commented 1 year ago

@WallaceIT I tried to add your code into my project, but a "Segmentation fault" occurred at run time. I need to see if I made the wrong transplant. I'm also going to take a second to look at [#250], which I missed earlier 我试试着将你的代码添加进我的工程,但运行时发生了“Segmentation fault”。我得看一下是否我移植错误了。我也要再花点时间看下 [#250 ],我之前居然看漏了

WallaceIT commented 1 year ago

@904221150 any error printed before the Segmentation fault? Can you share some more details on your configuation?

904221150 commented 1 year ago

@WallaceIT Probably because of XDG_SHELL and WL_SHELL, I found some changes in XDG_SHELL, but I used WL_SHELL, WL_SHELL has no similar changes.In addition, it seems that my weston and wayland do not have wayland-xdg-shell-client-protocol.h, but both have xdg-shell-client-protocol.h. Maybe the version is too low 可能是因为XDG_SHELL和WL_SHELL的原因,我发现XDG_SHELL里进行了部分修改,但我使用了WL_SHELL,WL_SHELL没有类似的修改。另外我的weston和wayland似乎没有wayland-xdg-shell-client-protocol.h,但都有xdg-shell-client-protocol.h。可能真的版本太低

symfund commented 1 year ago

See my earlier reported bug #225

symfund commented 1 year ago

@WallaceIT Probably because of XDG_SHELL and WL_SHELL, I found some changes in XDG_SHELL, but I used WL_SHELL, WL_SHELL has no similar changes.In addition, it seems that my weston and wayland do not have wayland-xdg-shell-client-protocol.h, but both have xdg-shell-client-protocol.h. Maybe the version is too low 可能是因为XDG_SHELL和WL_SHELL的原因,我发现XDG_SHELL里进行了部分修改,但我使用了WL_SHELL,WL_SHELL没有类似的修改。另外我的weston和wayland似乎没有wayland-xdg-shell-client-protocol.h,但都有xdg-shell-client-protocol.h。可能真的版本太低

You can use wayland-scanner to generate the file xdg-shell-client-protocol.h https://github.com/symfund/lvgl-port-ma35d1/blob/master/wayland-protocol-code-generator.png

904221150 commented 1 year ago

I found another solution. I added SDL to weston and then wrote an opengles lvgl display driver. Although the environment is more, but this method can display normally. 我找到了另外一个解决办法。我在weston的基础上又加上了SDL,然后又写了个opengles的lvgl显示驱动。虽然环境好像加多了个,但这种方法能够正常显示。 @kisvegabor Does lvgl add an opengles driver later? On lvgl v8, my app has reached 100% occupancy on single-core a55@1.2Ghz. The reason I used the wayland driver was to use weston's full screen rotation to reduce single-core consumption.But even using weaton, the single-core occupancy is still high, so I use opengles for rgb565 to argb, 720p to 1080p and rotation.If lvgl want to reduce cpu consumption, it's a good idea to use opengles. lvgl后续是否要加个opengles的驱动?在lvgl v8上我的app在单核a55@1.2Ghz上已经达到100%的占用率,我之所以使用wayland驱动其实是想用weston的全屏旋转,这样可以减少单核的消耗。但即使用上weaton,单核占用依旧下不来,所以便使用opengles来将rgb565转argb,720p放大到1080p还有旋转。如果lvgl想要减少cpu消耗,使用opengles进行处理是个不错的办法。

kisvegabor commented 1 year ago

Does lvgl add an opengles driver later? On lvgl v8, my app has reached 100% occupancy on single-core a55@1.2Ghz. The reason I used the wayland driver was to use weston's full screen rotation to reduce single-core consumption.But even using weaton, the single-core occupancy is still high, so I use opengles for rgb565 to argb, 720p to 1080p and rotation.If lvgl want to reduce cpu consumption, it's a good idea to use opengles.

Yes, it'd be great to have an OpenGLES drawing engine. In v9 the rendering architecture will be improved and I think it will be easier to add and OpenGLES backend.