Skip to content

Commit

Permalink
feature(wayland): add double buffering
Browse files Browse the repository at this point in the history
Add double buffering to Wayland driver: one buffer is updated while
another one is being displayed.

This doubles the memory requirement, but lowers the possibility to
encounter a busy buffer when LVGL core is ready to draw.

Signed-off-by: Francesco Valla <[email protected]>
  • Loading branch information
WallaceIT committed Nov 12, 2022
1 parent 829e0ff commit b0a389a
Showing 1 changed file with 100 additions and 29 deletions.
129 changes: 100 additions & 29 deletions wayland/wayland.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ struct buffer_hdl
int size;
struct wl_buffer *wl_buffer;
bool busy;
lv_area_t last_area;
bool is_first_flush;
};

struct buffer_allocator
Expand All @@ -141,7 +143,9 @@ struct graphic_object
int width;
int height;

struct buffer_hdl buffer;
struct buffer_hdl *buffer;
unsigned int num_buffers;
unsigned int active_buffer;

struct input input;
};
Expand Down Expand Up @@ -1115,7 +1119,7 @@ static const struct wl_shell_surface_listener shell_surface_listener = {
static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
{
struct window *window = (struct window *)data;
struct buffer_hdl *buffer = &window->body->buffer;
struct buffer_hdl *buffer = &window->body->buffer[0]; // FIXME

xdg_surface_ack_configure(xdg_surface, serial);

Expand Down Expand Up @@ -1416,7 +1420,7 @@ static bool deinitialize_buffer(struct window *window, struct buffer_hdl *buffer
}

static struct graphic_object * create_graphic_obj(struct application *app, struct window *window,
enum object_type type,
enum object_type type, unsigned int num_buffers,
struct graphic_object *parent)
{
struct graphic_object *obj;
Expand All @@ -1432,12 +1436,22 @@ static struct graphic_object * create_graphic_obj(struct application *app, struc

obj->window = window;
obj->type = type;
obj->num_buffers = num_buffers;

obj->buffer = lv_malloc(sizeof(struct buffer_hdl) * obj->num_buffers);
LV_ASSERT_MALLOC(obj->buffer);
if (!obj->buffer)
{
goto err_free;
}

lv_memset(obj->buffer, 0x00, sizeof(struct buffer_hdl));

obj->surface = wl_compositor_create_surface(app->compositor);
if (!obj->surface)
{
LV_LOG_ERROR("cannot create surface for graphic object");
goto err_free;
goto err_free_buf;
}

obj->surface_configured = true;
Expand All @@ -1448,6 +1462,9 @@ static struct graphic_object * create_graphic_obj(struct application *app, struc
err_destroy_surface:
wl_surface_destroy(obj->surface);

err_free_buf:
lv_free(obj->buffer);

err_free:
lv_free(obj);

Expand All @@ -1464,6 +1481,8 @@ static void destroy_graphic_obj(struct graphic_object * obj)

wl_surface_destroy(obj->surface);

lv_free(obj->buffer);

lv_free(obj);
}

Expand All @@ -1472,7 +1491,7 @@ static bool create_decoration(struct window *window,
struct graphic_object * decoration,
int window_width, int window_height)
{
struct buffer_hdl * buffer = &decoration->buffer;
struct buffer_hdl * buffer = &decoration->buffer[0];
int x, y;

switch (decoration->type)
Expand Down Expand Up @@ -1608,7 +1627,7 @@ static bool create_decoration(struct window *window,
static bool attach_decoration(struct window *window, struct graphic_object * decoration,
struct graphic_object * parent)
{
struct buffer_hdl * buffer = &decoration->buffer;
struct buffer_hdl * buffer = &decoration->buffer[0];
int pos_x, pos_y;
int x, y;

Expand Down Expand Up @@ -1691,42 +1710,50 @@ static void detach_decoration(struct window *window,

static bool resize_window(struct window *window, int width, int height)
{
struct buffer_hdl *buffer = &window->body->buffer;
unsigned int b;

LV_LOG_TRACE("resize window %dx%d", width, height);

// De-initialize previous buffers
if (buffer->busy)
// Detach active buffer
if (window->body->buffer[window->body->active_buffer].busy)
{
LV_LOG_WARN("Deinitializing busy window buffer...");
wl_surface_attach(window->body->surface, NULL, 0, 0);
wl_surface_commit(window->body->surface);
buffer->busy = false;
window->body->buffer[window->body->active_buffer].busy = false;
}

if (!deinitialize_buffer(window, buffer))
for (b = 0; b < window->body->num_buffers; b++)
{
LV_LOG_ERROR("failed to deinitialize window buffer");
return false;
if (!deinitialize_buffer(window, &window->body->buffer[b]))
{
LV_LOG_ERROR("failed to deinitialize window buffer");
}
}

#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
int b;
for (b = 0; b < NUM_DECORATIONS; b++)
{
if (window->decoration[b] != NULL)
{
detach_decoration(window, window->decoration[b]);
deinitialize_buffer(window, &window->decoration[b]->buffer);
deinitialize_buffer(window, &window->decoration[b]->buffer[0]);
}
}
#endif

// Initialize backing buffer
if (!initialize_buffer(window, buffer, width, height))
// Initialize backing buffers
for (b = 0; b < window->body->num_buffers; b++)
{
LV_LOG_ERROR("failed to initialize window buffer");
return false;
if (!initialize_buffer(window, &window->body->buffer[b], width, height))
{
LV_LOG_ERROR("failed to initialize window buffer");
for (b--; b >= 0; b--)
{
deinitialize_buffer(window, &window->body->buffer[b]);
}
return false;
}
}

window->width = width;
Expand Down Expand Up @@ -1790,13 +1817,16 @@ static struct window *create_window(struct application *app, int width, int heig
}

// Create wayland buffer and surface
window->body = create_graphic_obj(app, window, OBJECT_WINDOW, NULL);
window->body = create_graphic_obj(app, window, OBJECT_WINDOW, 2, NULL);
if (!window->body)
{
LV_LOG_ERROR("cannot create window body");
goto err_deinit_allocator;
}

// Start drawing into buffer 0, letting the driver believing buffer 1 is active
window->body->active_buffer = 1;

// Create shell surface
if (0)
{
Expand Down Expand Up @@ -1859,7 +1889,7 @@ static struct window *create_window(struct application *app, int width, int heig
int d;
for (d = 0; d < NUM_DECORATIONS; d++)
{
window->decoration[d] = create_graphic_obj(app, window, (FIRST_DECORATION+d), window->body);
window->decoration[d] = create_graphic_obj(app, window, (FIRST_DECORATION+d), 1, window->body);
if (!window->decoration[d])
{
LV_LOG_ERROR("Failed to create decoration %d", d);
Expand Down Expand Up @@ -1937,14 +1967,18 @@ static void destroy_window(struct window *window)
{
if (window->decoration[b])
{
deinitialize_buffer(window, &window->decoration[b]->buffer);
deinitialize_buffer(window, &window->decoration[b]->buffer[0]);
destroy_graphic_obj(window->decoration[b]);
window->decoration[b] = NULL;
}
}
#endif

deinitialize_buffer(window, &window->body->buffer);
for (b = 0; b < window->body->num_buffers; b++)
{
deinitialize_buffer(window, &window->body->buffer[b]);
}

destroy_graphic_obj(window->body);

deinitialize_allocator(&window->allocator);
Expand All @@ -1953,7 +1987,8 @@ static void destroy_window(struct window *window)
static void _lv_wayland_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
{
struct window *window = disp_drv->user_data;
struct buffer_hdl *buffer = &window->body->buffer;
struct buffer_hdl *buffer = &window->body->buffer[window->body->active_buffer ^ 1];
struct buffer_hdl *active_buffer = &window->body->buffer[window->body->active_buffer];

const lv_coord_t hres = (disp_drv->rotated == 0) ? (disp_drv->hor_res) : (disp_drv->ver_res);
const lv_coord_t vres = (disp_drv->rotated == 0) ? (disp_drv->ver_res) : (disp_drv->hor_res);
Expand Down Expand Up @@ -1986,9 +2021,29 @@ static void _lv_wayland_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv
{
LV_LOG_WARN("skip flush since wayland backing buffer is busy");
lv_disp_flush_ready(disp_drv);
lv_memset(&buffer->last_area, 0x00, sizeof(lv_area_t));
return;
}


// Copy previous invalidated area from active buffer
if (buffer->is_first_flush) // TODO: check if new area is not a superset of the last one
{
int32_t bytes_pre_pixel = window->bits_per_pixel / 8;
int32_t x1 = active_buffer->last_area.x1;
int32_t x2 = LV_MIN(active_buffer->last_area.x2, (disp_drv->hor_res - 1));
int32_t y1 = active_buffer->last_area.y1;
int32_t y2 = LV_MIN(active_buffer->last_area.y2, (disp_drv->ver_res - 1));
int32_t act_w = x2 - x1 + 1;

for (int y = y1; y <= y2; y++)
{
unsigned long offset = ((y * disp_drv->hor_res + x1) * bytes_pre_pixel);
lv_memcpy((uint8_t *)buffer->base + offset, active_buffer->base + offset, act_w * bytes_pre_pixel);
}

buffer->is_first_flush = false;
}

if (LV_COLOR_DEPTH == window->bits_per_pixel && LV_COLOR_DEPTH != 1 && LV_COLOR_DEPTH != 8)
{
int32_t bytes_pre_pixel = window->bits_per_pixel / 8;
Expand Down Expand Up @@ -2031,6 +2086,8 @@ static void _lv_wayland_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv
}
}

lv_memcpy(&buffer->last_area, area, sizeof(lv_area_t));

wl_surface_damage(window->body->surface, area->x1, area->y1,
(area->x2 - area->x1 + 1), (area->y2 - area->y1 + 1));

Expand All @@ -2042,6 +2099,10 @@ static void _lv_wayland_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv
}
buffer->busy = true;
window->flush_pending = true;

// Flip buffers
active_buffer->is_first_flush = true;
window->body->active_buffer ^= 1;
}

lv_disp_flush_ready(disp_drv);
Expand Down Expand Up @@ -2109,14 +2170,24 @@ static void _lv_wayland_handle_output(void)
}
else if (window->resize_pending)
{
bool do_resize = !window->body->buffer.busy;
bool do_resize = true;
unsigned int d;

// Do not resize until all buffers have been released
for (d = 0; d < window->body->num_buffers; d++)
{
if (window->body->buffer[d].busy)
{
do_resize = false;
break;
}
}
#if LV_WAYLAND_CLIENT_SIDE_DECORATIONS
if (!window->application->opt_disable_decorations && !window->fullscreen)
if (do_resize && !window->application->opt_disable_decorations && !window->fullscreen)
{
int d;
for (d = 0; d < NUM_DECORATIONS; d++)
{
if ((window->decoration[d] != NULL) && (window->decoration[d]->buffer.busy))
if ((window->decoration[d] != NULL) && (window->decoration[d]->buffer[0].busy))
{
do_resize = false;
break;
Expand Down

0 comments on commit b0a389a

Please sign in to comment.