lvgl icon indicating copy to clipboard operation
lvgl copied to clipboard

feat(display): add globle function lv_display_get_render_state

Open spbgzh opened this issue 1 year ago • 7 comments

Description of the feature or fix

When I change screen really fast, I need to avoid ERROR Asserted at expression: !disp->rendering_in_progress (Invalidate area is not allowed during rendering.), I need to check rendering_in_progress state, but disp its in lv_display_private.h, if I want to access it without c hearder "lvgl/src/display/lv_display_private.h", it will show error: invalid use of incomplete. so in this case I suggest to set a globle function 'lv_display_get_render_state' to get rendering_in_progress state

/**
 * Get display render state
 * @param disp              pointer to a display
 * @return                  state of rendering_in_progress
 */
uint32_t lv_display_get_render_state(lv_display_t * disp);
uint32_t lv_display_get_render_state(lv_display_t * disp)
{
    return disp->rendering_in_progress;
}

spbgzh avatar Jun 30 '24 00:06 spbgzh

If you don't consider this function, you can add a check for d->rendering_in_progress in the lv_screen_load_anim function to prevent errors.

spbgzh avatar Jun 30 '24 05:06 spbgzh

@spbgzh This problem is usually caused by users using the lvgl API across threads. For example, lv_timer_handler works in the main loop, but lv_screen_xxx is called in another thread or in an interrupt function, which causes the lvgl's core data to be changed during reading. An error occurred that caused the thread to be unsafe.

So please use lvgl single thread, or lock lvgl thread, refer to: https://docs.lvgl.io/master/porting/os.html

FASTSHIFT avatar Jun 30 '24 11:06 FASTSHIFT

@FASTSHIFT In fact, I did not use threads. I used the serial port to control the screen switching. When the screen switching is not completed, the serial port sends the command to switch the screen, which will cause the lvgl system to crash. For this reason, I added a check at the top of the serial port control command to determine whether the screen rendering is completed.

static uint8_t perform_screen_transition(lv_screen_to_t *transition)
{
    if (disp->rendering_in_progress)
    {
        return 0;
    }
    if (transition == NULL || transition->next == NULL)
        return 0;
    if (transition->next->screen == NULL && transition->next->init != NULL)
        transition->next->init();
    if (transition->next->screen != NULL)
    {
        lv_screen_load_anim(transition->next->screen, transition->anim, transition->speed, transition->delay, false);
        node = *(transition->next);
        return 1;
    }
    return 0;
}

spbgzh avatar Jun 30 '24 12:06 spbgzh

This is my serial port log, where 01 means the operation is successful, and 00 means the operation fails. You can see that the process of controlling the switching screen includes the failed instructions.

[20:35:04.446] 01 A0 20 [20:35:04.597] 00 A0 20 [20:35:04.748] 01 A0 20 [20:35:04.946] 00 A0 20 [20:35:05.097] 01 A0 20 [20:35:05.246] 01 A0 20 [20:35:05.395] 00 A0 20 [20:35:05.596] 01 A0 20 [20:35:05.746] 01 A0 20 [20:35:08.596] 01 A0 20 [20:35:08.747] 01 A0 20 [20:35:08.898] 00 A0 20 [20:35:09.048] 00 A0 20 [20:35:09.197] 01 A0 20 [20:35:09.300] 00 A0 20 [20:35:09.447] 00 A0 20 [20:35:09.599] 01 A0 20

spbgzh avatar Jun 30 '24 12:06 spbgzh

@spbgzh In a bare metal environment, the main loop and the serial interrupt can also be considered two threads, where the interrupt handler takes precedence over the main loop (because the interrupt handler can interrupt the main loop at any time). There are many shared resources inside LVGL, such as heap and global variables, which do not need to be locked in a single-threaded environment because functions do not interrupt each other's execution.

If multi-threading is used, it will cause problems, for example, lv_timer_handler parses an obj to do the preparation work before rendering, but because of the interrupt, the user deletes the obj directly in the interrupt callback function. When the interrupt exits, the main loop accesses the data of the obj that has just been deleted, which leads to execution errors.

Adding the rendering_in_progress flag check is just a means to detect the problem, bypassing the error does not really solve the problem, because there are still many shared resources that are not protected.

The correct solution is to set a flag in the serial port interrupt callback to identify the page to jump to. Check the flag in the LVGL main loop (lv_timer is recommended), call lv_screen_load_anim to load the page based on the flag, and clear the flag when the call is complete, so there are no thread-safety issues.

FASTSHIFT avatar Jun 30 '24 13:06 FASTSHIFT

I added a new code comment to illustrate: https://github.com/lvgl/lvgl/pull/6450

FASTSHIFT avatar Jun 30 '24 14:06 FASTSHIFT

I think adding lv_display_get_render_state would foster bad habits, like work-arounding multi threading issue.

kisvegabor avatar Jul 01 '24 13:07 kisvegabor

We need some feedback on this pull request.

Now we mark this as "stale" because there was no activity here for 14 days.

Remove the "stale" label or comment else this will be closed in 7 days.

lvgl-bot avatar Jul 18 '24 01:07 lvgl-bot

We need some feedback on this pull request.

Now we mark this as "stale" because there was no activity here for 14 days.

Remove the "stale" label or comment else this will be closed in 7 days.

lvgl-bot avatar Aug 14 '24 01:08 lvgl-bot

Closing in light of this. Thank you for taking your time to contribute anyway! :+1:

kisvegabor avatar Aug 22 '24 06:08 kisvegabor