Looking for info on starting point for partial rendering of widgets

I’m looking to improve rendering performance by looking at input events, determining the specific change, and only re-rendering for the specific area that the change applies to. For example, if I move a slider slightly, the re-render would only be for the knob of the slider and the region of the slider background which pertains to the [old value, new value] range.

I’m pretty confident that this will not be easy (though I do see the current logic is already sort of implemented in terms of invalidated render regions). I’m looking to give it a try anyway for some widgets which are causing performance issues when responding to events. I’d rather modify existing widgets than make my own new, optimized widgets, so I can make use of their existing features.

This probably won’t go anywhere since this sort of update is tough when the code isn’t already set up in this way, but might be useful for future similar discussions/requests.

I’ve got a few questions:

  1. Is there already some widget that has this sort of behavior? (can probably ignore the rest of the questions if so)
  2. I’m unfamiliar with the codebase - broadly speaking, what systems (if any) would need changes to allow: (1) diffing widget state, (2) permitting an object to refresh without invalidating its whole bounds (header/source file names would be helpful)
  3. Is it at all feasible to try to implement this on a per-widget basis, or is there some global assumption about rendering that would be invalidated by this, requiring a larger refactor?
  4. If (3) is feasible, how difficult would you expect this change to be if made on a per-widget basis, from 1 (< 10 LoC change) to 10 (refactor the whole event/render systems)
  5. Aside from the obvious (ie, “just optimize the widget/usage in general”), what might I have missed?

I’ve also attempted to optimize this but deferred because there are only a few corner cases when it could be applied. For example a slider’s background could be different in pressed state too. And usually these “locally changing” parts are not that big to be worth the hassle.

There is one exception: lv_arc which is performance hungry to update so I implemented an invalidate by angle mechanism to update only the really changed parts.

It’s already done here. If there is no change, no invalidation should happen.

I think it’d make the most sense as the widget knows what happen internally. For example slider invalidation on pressing happens here. So it can be replaced by a smart lv_obj_invalidate_area call.

As you can see it’s a per widget optimization and not that complicated. I’d say 100-200 LoC per widget.

I’m not sure what’s the point of such optimization. These interactive widgets are usually small and simple. Can you mention a few cases when it could mean significant performance boost?

Ty for all the info

For example a slider’s background could be different in pressed state too. And usually these “locally changing” parts are not that big to be worth the hassle.

Fair point. In my case I can ignore potential differences for my specific application, can definitely understand not having already implemented for all widgets

I think it’d make the most sense as the widget knows what happen internally.

I agree, just wasn’t sure if there was an expectation that a widget should redraw its entire display region for each render (if there was such an expectation it could require more high-level changes). Wanted to confirm the general scope of necessary changes

For example slider invalidation on pressing happens here. So it can be replaced by a smart lv_obj_invalidate_area call.

Thank you, this and the arc rendering will be useful references

I’m not sure what’s the point of such optimization. These interactive widgets are usually small and simple. Can you mention a few cases when it could mean significant performance boost?

Cases where the widget is not small (eg >150^2 px) and rendered with low bandwidth. I’ve got a 320x480 4-wire SPI (40MHz) display and am getting around 20fps while interacting with a slider which fills up about half the screen (styled to take up half the width and almost the full height)

When I first started my UI with my own code (no LVGL) I was having similar performance issues until I changed it to diff state and only render what needed changing. Redrawn area drastically reduced (eg 100x5) compared to the full slider size (100x300)

After that it felt like 60fps and there were no tearing issues to be seen even without a busy/tearing pin. With debug rects enabled in LVGL I can see the whole widget is redrawn each frame while interacting. Not really a fair comparison since LVGL does so much more than my basic impl (and there are many more widgets where this approach isn’t really tenable) but the perf improvements were noticeable where it mattered for my case

I see your points. I think the first step could be to identify which widgets can improved this way. Below I grouped all the widgets. What do you think?

  • Already implemented
    • arc
    • spinner
    • table
  • Could be interesting
    • bar
    • btnmatrix
    • calendar
    • chart
    • colorwheel
    • keyboard
    • line
    • meter
    • slider
  • Not interesting
    • animimg
    • btn
    • canvas
    • checkbox
    • dropdown
    • img
    • imgbtn
    • label
    • led
    • menu
    • msgbox
    • roller
    • spinbox
    • span
    • switch
    • tabview
    • textarea
    • tileview
    • win
    • list

I think that grouping makes sense and have no changes to suggest for it

Great! Are you interested in giving it a try? I think lv_bar could be a good starting point for making experiments.

I do plan on giving it a shot locally, though I’m currently focused on other matters

Great! Feel free to send a PR if there is something to discuss.