Support for lv_obj_(begin|end)_update

Hi,

a very common pattern, when working with UIs, is the ability to signal the underlying library that some changes are going to be performed but that the library has to wait before considering to trigger a real refresh.

From the looks of it, the refresh relies on lv_obj_invalidate so I was thinking that having a bit flag in the obj struct to identify if the obj is being updated (using begin / end _update methods) checked by the lv_obj_invalidate would do more than half of the job.
Triggering the actual refresh may be a bit trickier, while the current architecture relies on the fact that the children is contained in the parent with the above mentioned approach the parent would have to trigger the refresh for the children objs but I am fairly sure there are various approaches for this.

I am currently building an interface with many items built at runtime while the screen is visible and I don’t want to set it to hidden and then back to visible to refresh it because makes to code tricky.

I am willing to look into it but before getting down to a road that can be really tricky, do you foresee any breaking with this approach?

Forgot to mention, I am using lvgl with micropython v1.11. I don’t think this is going to have any impact on the above mentioned approach but just in case better to highlight it :slight_smile:.

Thanks!

Have you read the code for lv_obj_invalidate? At the end of the day it simply adds the object’s area to the list of areas that need to be redrawn. The areas will not actually be redrawn until the display refresh task runs (after LV_DISP_DEF_REFR_PERIOD milliseconds).

Is there a specific reason why you are trying to do this? Is it running too slowly right now? Maybe there is a better solution.

Could you describe step by step how do you imagine this feature? E.g.

  1. I create an object on … when …
  2. LittlevGL sends a signal to…
  3. If I see the signal I do …
  4. Etc.

@kisvegabor

The basic idea is to let lvgl know that it has to do not perform any graphical operation the objects because they are being updated.

After further thinking I think that it’s not really easily implementable, especially because there is no compositor or backbuffering for the graphical elements (and it would take too much memory to have them, especially the back-buffering of the graphical items).

@embeddedt
Yes, I read it now :smiley:

I reduced the code to this just for a test, in lv_conf I turned off the shadowing (but not the antialiasing)

articles = [
    'title1','title2','title3','title4','title5','title1','title2','title3','title4','title5',
    'title1','title2','title3','title4','title5','title1','title2','title3','title4','title5'
]

new_scr = lv.obj()
lv.scr_load(new_scr)

last_obj = None
counter = 0

start_time = utime.ticks_ms()
for title in articles: 
    counter += 1
    label = lv.label(new_scr)
    label.set_size(240, 10)
    label.set_text('{}) {}'.format(counter, title))
    label.set_align(lv.label.ALIGN.LEFT)
    label.set_x(0)
    label.set_y(counter * 16)
    last_obj = label
end_time = utime.ticks_ms()

print('Execution time {}ms'.format(end_time - start_time))

The initialization code is pretty much the one from the example so I am not posting it.

But after further investigation (because I totally don’t have experience with micropython) I discovered that issue was coming from
(’{}) {}’.format(counter, title)

Any form of string aggregation, with format or concatenations, are SUPER slow: the total execution time of the above code is roughly 900ms and to make 20 of them the format takes almost 800ms of the 900ms.
Without the format, displaying only the string, the execution time goes to 110ms/120ms
If the screen is displayed after they are created it goes down to ~20ms

Sorry for the post, I wasn’t aware that micropython was that slow in concatenating strings or, in general, in working with strings.

Thanks for your time!

Maybe @amirgon can shed some light on this.

This workaround allowed the execution time to go down considerably

...
articles_numbers = [str(i) for i in range(1, len(articles) + 1)]

last_obj = None
counter = 0

for title in articles: 
    label = lv.label(new_scr)
    label.set_text(''.join([articles_numbers[counter], ') ', title]))
    label.set_size(240, 10)
    label.set_y(counter * 16)
...

Potentially the articles_numbers can be re-initialized only if the amount of articles to display is greater than the previous time but this code was just to test my build of micropython so not really worth spending time on it to improve the performance but I had some nice learnings!

Indeed would be interesting to know which is the best practice, avoid the join double the performances

If performance is important for you in Micropython, you should really have look at this article.

Micropython also supports a Native/Viper code emitter that can generate machine code from Micropython code (although not completely compliant), and can significantly improve performance.
It’s worth noting that ESP32 gained native code emitter support very recently, it wasn’t even merged to master yet.

If you find some built-in micropython function which is very inefficient (such as the the format method, according to @d.albano experience), I recommend opening an issue on upstream Micropython about this.

@amigron yes, I saw that yesterday they merged some code into a branch and I was planning to test it with my branch so I want to give it a try.
But the slowness, from what I saw, applies to format, % and the concatenation of strings.

I will investigate a bit more before opening the issue, thanks!