feat(disp): add double buffered direct-mode efficient sync algorithm (v8.3) (#4497)

Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Brandon Holland
2023-08-29 11:23:50 -07:00
committed by GitHub
parent 1c5df6c665
commit 336f24127a
5 changed files with 174 additions and 1 deletions

View File

@@ -53,6 +53,7 @@ typedef struct {
**********************/
static void lv_refr_join_area(void);
static void refr_invalid_areas(void);
static void refr_sync_areas(void);
static void refr_area(const lv_area_t * area_p);
static void refr_area_part(lv_draw_ctx_t * draw_ctx);
static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
@@ -320,12 +321,24 @@ void _lv_disp_refr_timer(lv_timer_t * tmr)
}
lv_refr_join_area();
refr_sync_areas();
refr_invalid_areas();
/*If refresh happened ...*/
if(disp_refr->inv_p != 0) {
/*Copy invalid areas for sync next refresh*/
if(disp_refr->driver->direct_mode) {
uint16_t i;
for(i = 0; i < disp_refr->inv_p; i++) {
if(disp_refr->inv_area_joined[i])
continue;
lv_area_t * sync_area = _lv_ll_ins_tail(&disp_refr->sync_areas);
*sync_area = disp_refr->inv_areas[i];
}
}
/*Clean up*/
lv_memset_00(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
lv_memset_00(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
@@ -495,6 +508,78 @@ static void lv_refr_join_area(void)
}
}
/**
* Refresh the sync areas
*/
static void refr_sync_areas(void)
{
/*Do not sync if not direct mode*/
if(!disp_refr->driver->direct_mode) return;
/*Do not sync if not double buffered*/
if(disp_refr->driver->draw_buf->buf2 == NULL) return;
/*Do not sync if no sync areas*/
if(_lv_ll_is_empty(&disp_refr->sync_areas)) return;
/*The buffers are already swapped.
*So the active buffer is the off screen buffer where LVGL will render*/
void * buf_off_screen = disp_refr->driver->draw_buf->buf_act;
void * buf_on_screen = disp_refr->driver->draw_buf->buf_act == disp_refr->driver->draw_buf->buf1
? disp_refr->driver->draw_buf->buf2
: disp_refr->driver->draw_buf->buf1;
/*Get stride for buffer copy*/
lv_coord_t stride = lv_disp_get_hor_res(disp_refr);
/*Iterate through invalidated areas to see if sync area should be copied*/
lv_area_t res[4] = {0};
int8_t res_c, j;
uint32_t i;
lv_area_t * sync_area, *new_area, *next_area;
for(i = 0; i < disp_refr->inv_p; i++) {
/*Skip joined areas*/
if(disp_refr->inv_area_joined[i]) continue;
/*Iterate over sync areas*/
sync_area = _lv_ll_get_head(&disp_refr->sync_areas);
while(sync_area != NULL) {
/*Get next sync area*/
next_area = _lv_ll_get_next(&disp_refr->sync_areas, sync_area);
/*Remove intersect of redraw area from sync area and get remaining areas*/
res_c = _lv_area_diff(res, sync_area, &disp_refr->inv_areas[i]);
/*New sub areas created after removing intersect*/
if(res_c != -1) {
/*Replace old sync area with new areas*/
for(j = 0; j < res_c; j++) {
new_area = _lv_ll_ins_prev(&disp_refr->sync_areas, sync_area);
*new_area = res[j];
}
_lv_ll_remove(&disp_refr->sync_areas, sync_area);
lv_mem_free(sync_area);
}
/*Move on to next sync area*/
sync_area = next_area;
}
}
/*Copy sync areas (if any remaining)*/
for(sync_area = _lv_ll_get_head(&disp_refr->sync_areas); sync_area != NULL;
sync_area = _lv_ll_get_next(&disp_refr->sync_areas, sync_area)) {
disp_refr->driver->draw_ctx->buffer_copy(
disp_refr->driver->draw_ctx,
buf_off_screen, stride, sync_area,
buf_on_screen, stride, sync_area
);
}
/*Clear sync areas*/
_lv_ll_clear(&disp_refr->sync_areas);
}
/**
* Refresh the joined areas
*/

View File

@@ -186,6 +186,8 @@ lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver)
disp->inv_en_cnt = 1;
_lv_ll_init(&disp->sync_areas, sizeof(lv_area_t));
lv_disp_t * disp_def_tmp = disp_def;
disp_def = disp; /*Temporarily change the default screen to create the default screens on the
new display*/
@@ -316,6 +318,7 @@ void lv_disp_remove(lv_disp_t * disp)
}
_lv_ll_remove(&LV_GC_ROOT(_lv_disp_ll), disp);
_lv_ll_clear(&disp->sync_areas);
if(disp->refr_timer) lv_timer_del(disp->refr_timer);
lv_mem_free(disp);

View File

@@ -23,6 +23,7 @@ extern "C" {
#include "../misc/lv_area.h"
#include "../misc/lv_ll.h"
#include "../misc/lv_timer.h"
#include "../misc/lv_ll.h"
/*********************
* DEFINES
@@ -187,6 +188,9 @@ typedef struct _lv_disp_t {
uint16_t inv_p;
int32_t inv_en_cnt;
/** Double buffer sync areas */
lv_ll_t sync_areas;
/*Miscellaneous data*/
uint32_t last_activity_time; /**< Last time when there was activity on this display*/
} lv_disp_t;

View File

@@ -143,6 +143,78 @@ bool _lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area
return union_ok;
}
/**
* Get resulting sub areas after removing the common parts of two areas from the first area
* @param res_p pointer to an array of areas with a count of 4, the resulting areas will be stored here
* @param a1_p pointer to the first area
* @param a2_p pointer to the second area
* @return number of results or -1 if no intersect
*/
int8_t _lv_area_diff(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p)
{
/*Areas have no common parts*/
if(!_lv_area_is_on(a1_p, a2_p)) return -1;
/*No remaining areas after removing common parts*/
if(_lv_area_is_in(a1_p, a2_p, 0)) return 0;
/*Result counter*/
int8_t res_c = 0;
/*Get required information*/
lv_area_t n;
lv_coord_t a1_w = lv_area_get_width(a1_p) - 1;
lv_coord_t a1_h = lv_area_get_height(a1_p) - 1;
/*Compute top rectangle*/
lv_coord_t th = a2_p->y1 - a1_p->y1;
if(th > 0) {
n.x1 = a1_p->x1;
n.y1 = a1_p->y1;
n.x2 = a1_p->x2;
n.y2 = a1_p->y1 + th;
res_p[res_c++] = n;
}
/*Compute the bottom rectangle*/
lv_coord_t bh = a1_h - (a2_p->y2 - a1_p->y1);
if(bh > 0 && a2_p->y2 < a1_p->y2) {
n.x1 = a1_p->x1;
n.y1 = a2_p->y2;
n.x2 = a1_p->x2;
n.y2 = a2_p->y2 + bh;
res_p[res_c++] = n;
}
/*Compute side height*/
lv_coord_t y1 = a2_p->y1 > a1_p->y1 ? a2_p->y1 : a1_p->y1;
lv_coord_t y2 = a2_p->y2 < a1_p->y2 ? a2_p->y2 : a1_p->y2;
lv_coord_t sh = y2 - y1;
/*Compute the left rectangle*/
lv_coord_t lw = a2_p->x1 - a1_p->x1;
if(lw > 0 && sh > 0) {
n.x1 = a1_p->x1;
n.y1 = y1;
n.x2 = a1_p->x1 + lw;
n.y2 = y1 + sh;
res_p[res_c++] = n;
}
/*Compute the right rectangle*/
lv_coord_t rw = a1_w - (a2_p->x2 - a1_p->x1);
if(rw > 0) {
n.x1 = a2_p->x2;
n.y1 = y1;
n.x2 = a2_p->x2 + rw;
n.y2 = y1 + sh;
res_p[res_c++] = n;
}
//Return number of results
return res_c;
}
/**
* Join two areas into a third which involves the other two
* @param res_p pointer to an area, the result will be stored here

View File

@@ -177,6 +177,15 @@ void lv_area_move(lv_area_t * area, lv_coord_t x_ofs, lv_coord_t y_ofs);
*/
bool _lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p);
/**
* Get resulting sub areas after removing the common parts of two areas from the first area
* @param res_p pointer to an array of areas with a count of 4, the resulting areas will be stored here
* @param a1_p pointer to the first area
* @param a2_p pointer to the second area
* @return number of results (max 4) or -1 if no intersect
*/
int8_t _lv_area_diff(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p);
/**
* Join two areas into a third which involves the other two
* @param res_p pointer to an area, the result will be stored here