shared-module/busdisplay: merge overlapping dirty rectangles - #11189
shared-module/busdisplay: merge overlapping dirty rectangles#11189lynt-smitka wants to merge 2 commits into
Conversation
Merge dirty rectangles whose bounding box is smaller than the two areas summed, so shared pixels are not sent twice. displayio does not merge them today, so it sends the overlap once per rectangle. Merging never sends more pixels than before. Fixes adafruit#10687
tannewt
left a comment
There was a problem hiding this comment.
What do you think about having Group do this instead? It'll have more context and work for all different display types.
I agree, that will be more versatile. I'll see where it would be best to place it (shared-module/displayio/area.c?) and try to move it there. |
Move the overlap merge from busdisplay into shared code: displayio_area_array_merge_overlapping() in area.c does the greedy fuse, displayio_display_core_merge_refresh_areas() clips and collects the list (raw-list fallback past DISPLAYIO_MAX_MERGE_AREAS), and busdisplay, framebufferio and epaperdisplay all use it.
|
Moved the merge into shared displayio code (area.c + display_core), used by busdisplay, framebufferio and epaperdisplay. Group could still win a different case - combining a moved group's areas at the source (they touch rather than overlap, so list-level merging can't). But that's a bigger change. Should I try to implement it in this PR or make a new one? |
tannewt
left a comment
There was a problem hiding this comment.
Please do it in group to prevent duplication. The comment has a link to where I'd do it.
| // Merge overlapping dirty rectangles so shared pixels are computed and sent once | ||
| // (see displayio_display_core_merge_refresh_areas). | ||
| displayio_area_t merged[DISPLAYIO_MAX_MERGE_AREAS]; | ||
| size_t merged_count; |
There was a problem hiding this comment.
This needs to be repeated because the areas are const by this time. Instead do it here:
circuitpython/shared-module/displayio/Group.c
Lines 438 to 471 in 43a7b49
Addresses #10687: when dirty rectangles overlap, displayio resends the shared pixels once per rectangle (and draws them as separate, visible passes). A changing text label is the common trigger.
@tannewt proposed two options in the issue - a quick heuristic (sum the dirty areas; if they exceed the screen, do a full refresh) or the more complex rectangle merge. This PR implements merge. The heuristic only helps when the summed dirty area exceeds the whole screen; it does nothing when the change covers only part of the screen - the common case, e.g. a label that updates but doesn't fill the display. Merge helps there too, and when the change does fill the screen it matches the heuristic (the union is then the whole screen anyway).
Measured on a PicoPad (ST7789 SPI)
What it does
Before each refresh, the clipped dirty areas are copied into a small scratch array and any pair whose bounding box is smaller than the two summed is fused; non-overlapping rectangles stay separate. The motivating case is a changing text label: it dirties a whole-label rectangle plus a nested per-glyph rectangle per character, sending those pixels twice - merge collapses them into one.
MAX_MERGE_AREAS(16); above that, refreshes the raw list unmerged.Fixes #10687