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:
@@ -53,6 +53,7 @@ typedef struct {
|
|||||||
**********************/
|
**********************/
|
||||||
static void lv_refr_join_area(void);
|
static void lv_refr_join_area(void);
|
||||||
static void refr_invalid_areas(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(const lv_area_t * area_p);
|
||||||
static void refr_area_part(lv_draw_ctx_t * draw_ctx);
|
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);
|
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();
|
lv_refr_join_area();
|
||||||
|
refr_sync_areas();
|
||||||
refr_invalid_areas();
|
refr_invalid_areas();
|
||||||
|
|
||||||
/*If refresh happened ...*/
|
/*If refresh happened ...*/
|
||||||
if(disp_refr->inv_p != 0) {
|
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*/
|
/*Clean up*/
|
||||||
lv_memset_00(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
|
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));
|
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
|
* Refresh the joined areas
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -186,6 +186,8 @@ lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver)
|
|||||||
|
|
||||||
disp->inv_en_cnt = 1;
|
disp->inv_en_cnt = 1;
|
||||||
|
|
||||||
|
_lv_ll_init(&disp->sync_areas, sizeof(lv_area_t));
|
||||||
|
|
||||||
lv_disp_t * disp_def_tmp = disp_def;
|
lv_disp_t * disp_def_tmp = disp_def;
|
||||||
disp_def = disp; /*Temporarily change the default screen to create the default screens on the
|
disp_def = disp; /*Temporarily change the default screen to create the default screens on the
|
||||||
new display*/
|
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_remove(&LV_GC_ROOT(_lv_disp_ll), disp);
|
||||||
|
_lv_ll_clear(&disp->sync_areas);
|
||||||
if(disp->refr_timer) lv_timer_del(disp->refr_timer);
|
if(disp->refr_timer) lv_timer_del(disp->refr_timer);
|
||||||
lv_mem_free(disp);
|
lv_mem_free(disp);
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ extern "C" {
|
|||||||
#include "../misc/lv_area.h"
|
#include "../misc/lv_area.h"
|
||||||
#include "../misc/lv_ll.h"
|
#include "../misc/lv_ll.h"
|
||||||
#include "../misc/lv_timer.h"
|
#include "../misc/lv_timer.h"
|
||||||
|
#include "../misc/lv_ll.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
@@ -187,6 +188,9 @@ typedef struct _lv_disp_t {
|
|||||||
uint16_t inv_p;
|
uint16_t inv_p;
|
||||||
int32_t inv_en_cnt;
|
int32_t inv_en_cnt;
|
||||||
|
|
||||||
|
/** Double buffer sync areas */
|
||||||
|
lv_ll_t sync_areas;
|
||||||
|
|
||||||
/*Miscellaneous data*/
|
/*Miscellaneous data*/
|
||||||
uint32_t last_activity_time; /**< Last time when there was activity on this display*/
|
uint32_t last_activity_time; /**< Last time when there was activity on this display*/
|
||||||
} lv_disp_t;
|
} lv_disp_t;
|
||||||
|
|||||||
@@ -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;
|
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
|
* Join two areas into a third which involves the other two
|
||||||
* @param res_p pointer to an area, the result will be stored here
|
* @param res_p pointer to an area, the result will be stored here
|
||||||
|
|||||||
@@ -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);
|
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
|
* Join two areas into a third which involves the other two
|
||||||
* @param res_p pointer to an area, the result will be stored here
|
* @param res_p pointer to an area, the result will be stored here
|
||||||
|
|||||||
Reference in New Issue
Block a user