From 72a977eed55fdee8ab5aea364e680863d228d100 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 24 Feb 2019 23:53:46 +0000 Subject: [PATCH] Allow double buffering in the new xrender backend If PresentPixmap choose to use the Flip mode, we cannot reuse the same buffer for rendering for the next frame. Add double buffering for that case. Signed-off-by: Yuxuan Shui --- src/backend/xrender.c | 84 ++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/src/backend/xrender.c b/src/backend/xrender.c index 4e1c5d5..8bc7ee1 100644 --- a/src/backend/xrender.c +++ b/src/backend/xrender.c @@ -33,9 +33,11 @@ typedef struct _xrender_data { /// The painting target, it is either the root or the overlay xcb_render_picture_t target; /// A back buffer - xcb_render_picture_t back; + xcb_render_picture_t back[2]; + /// The back buffer we should be painting into + int curr_back; /// The corresponding pixmap to the back buffer - xcb_pixmap_t back_pixmap; + xcb_pixmap_t back_pixmap[2]; /// The original root window content, usually the wallpaper. /// We save it so we don't loss the wallpaper when we paint over /// it. @@ -147,11 +149,12 @@ static void compose(void *backend_data, session_t *ps, win *w, void *win_data, i // Detect if the region is empty before painting if (pixman_region32_not_empty(®_tmp)) { - x_set_picture_clip_region(ps->c, xd->back, 0, 0, ®_tmp); - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, - wd->shadow_pict, alpha_pict, xd->back, 0, 0, 0, - 0, dst_x + w->shadow_dx, dst_y + w->shadow_dy, - w->shadow_width, w->shadow_height); + x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0, + ®_tmp); + xcb_render_composite( + ps->c, XCB_RENDER_PICT_OP_OVER, wd->shadow_pict, alpha_pict, + xd->back[xd->curr_back], 0, 0, 0, 0, dst_x + w->shadow_dx, + dst_y + w->shadow_dy, w->shadow_width, w->shadow_height); } pixman_region32_fini(®_tmp); pixman_region32_fini(&shadow_reg); @@ -161,9 +164,9 @@ static void compose(void *backend_data, session_t *ps, win *w, void *win_data, i // sure we get everything into the buffer x_clear_picture_clip_region(ps->c, wd->rendered_pict); - x_set_picture_clip_region(ps->c, xd->back, 0, 0, reg_paint); - xcb_render_composite(ps->c, op, wd->rendered_pict, alpha_pict, xd->back, 0, 0, 0, - 0, dst_x, dst_y, w->widthb, w->heightb); + x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0, reg_paint); + xcb_render_composite(ps->c, op, wd->rendered_pict, alpha_pict, xd->back[xd->curr_back], + 0, 0, 0, 0, dst_x, dst_y, w->widthb, w->heightb); } static bool @@ -196,7 +199,7 @@ blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_pain // The multipass blur implemented here is not correct, but this is what old // compton did anyway. XXX - xcb_render_picture_t src_pict = xd->back, dst_pict = tmp_picture[0]; + xcb_render_picture_t src_pict = xd->back[xd->curr_back], dst_pict = tmp_picture[0]; auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)]; int current = 0; int src_x = reg->x1, src_y = reg->y1; @@ -225,8 +228,8 @@ blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_pain } else { // This is the last pass, and this is also not the first xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, - alpha_pict, xd->back, 0, 0, 0, 0, reg->x1, - reg->y1, width, height); + alpha_pict, xd->back[xd->curr_back], 0, 0, 0, + 0, reg->x1, reg->y1, width, height); } // reset filter @@ -243,7 +246,8 @@ blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_pain // There is only 1 pass if (i == 1) { xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, - xd->back, 0, 0, 0, 0, reg->x1, reg->y1, width, height); + xd->back[xd->curr_back], 0, 0, 0, 0, reg->x1, + reg->y1, width, height); } xcb_render_free_picture(ps->c, tmp_picture[0]); @@ -425,10 +429,15 @@ static void *init(session_t *ps) { abort(); } - xd->back_pixmap = x_create_pixmap(ps->c, pictfmt->depth, ps->root, ps->root_width, - ps->root_height); - xd->back = x_create_picture_with_pictfmt_and_pixmap(ps->c, pictfmt, - xd->back_pixmap, 0, NULL); + // We might need to do double buffering for vsync + int pixmap_needed = ps->o.vsync ? 2 : 1; + for (int i = 0; i < pixmap_needed; i++) { + xd->back_pixmap[i] = x_create_pixmap(ps->c, pictfmt->depth, ps->root, + ps->root_width, ps->root_height); + xd->back[i] = x_create_picture_with_pictfmt_and_pixmap( + ps->c, pictfmt, xd->back_pixmap[i], 0, NULL); + } + xd->curr_back = 0; xcb_pixmap_t root_pixmap = x_get_root_back_pixmap(ps); if (root_pixmap == XCB_NONE) { @@ -440,7 +449,8 @@ static void *init(session_t *ps) { if (ps->present_exists) { auto eid = xcb_generate_id(ps->c); - auto e = xcb_request_check(ps->c, xcb_present_select_input_checked( + auto e = + xcb_request_check(ps->c, xcb_present_select_input_checked( ps->c, eid, xd->target_win, XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY)); if (e) { @@ -448,9 +458,11 @@ static void *init(session_t *ps) { free(e); } - xd->present_event = xcb_register_for_special_xge(ps->c, &xcb_present_id, eid, NULL); + xd->present_event = + xcb_register_for_special_xge(ps->c, &xcb_present_id, eid, NULL); if (!xd->present_event) { - log_error("Cannot register for special XGE, vsync will be disabled"); + log_error("Cannot register for special XGE, vsync will be " + "disabled"); } } for (int i = 0; ps->o.blur_kerns[i]; i++) { @@ -480,10 +492,15 @@ static void prepare(void *backend_data, session_t *ps, const region_t *reg_paint struct _xrender_data *xd = backend_data; if (ps->o.vsync != VSYNC_NONE && ps->present_exists && xd->present_in_progress) { // TODO don't block wait for present completion - xcb_present_generic_event_t *pev = (void *)xcb_wait_for_special_event(ps->c, xd->present_event); + xcb_present_generic_event_t *pev = + (void *)xcb_wait_for_special_event(ps->c, xd->present_event); assert(pev->evtype == XCB_PRESENT_COMPLETE_NOTIFY); - //xcb_present_complete_notify_event_t *pcev = (void *)pev; - //log_trace("Present complete: %d %ld", pcev->mode, pcev->msc); + xcb_present_complete_notify_event_t *pcev = (void *)pev; + // log_trace("Present complete: %d %ld", pcev->mode, pcev->msc); + if (pcev->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) { + // We cannot use the pixmap we used anymore + xd->curr_back = 1 - xd->curr_back; + } free(pev); xd->present_in_progress = false; @@ -491,10 +508,11 @@ static void prepare(void *backend_data, session_t *ps, const region_t *reg_paint // Paint the root pixmap (i.e. wallpaper) // Limit the paint area - x_set_picture_clip_region(ps->c, xd->back, 0, 0, reg_paint); + x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0, reg_paint); xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->root_pict, XCB_NONE, - xd->back, 0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height); + xd->back[xd->curr_back], 0, 0, 0, 0, 0, 0, ps->root_width, + ps->root_height); } static void present(void *backend_data, session_t *ps) { @@ -504,20 +522,20 @@ static void present(void *backend_data, session_t *ps) { // Only reset the fence when we are sure we will trigger it again. // To make sure rendering won't get stuck if user toggles vsync on the // fly. - xcb_present_pixmap(ps->c, xd->target_win, xd->back_pixmap, 0, XCB_NONE, - XCB_NONE, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, 0, - 0, 0, 0, 0, NULL); + xcb_present_pixmap(ps->c, xd->target_win, xd->back_pixmap[xd->curr_back], + 0, XCB_NONE, XCB_NONE, 0, 0, XCB_NONE, XCB_NONE, + XCB_NONE, 0, 0, 0, 0, 0, NULL); xd->present_in_progress = true; } else { // compose() sets clip region, so clear it first to make // sure we update the whole screen. - x_clear_picture_clip_region(ps->c, xd->back); + x_clear_picture_clip_region(ps->c, xd->back[xd->curr_back]); // TODO buffer-age-like optimization might be possible here. // but that will require a different backend API - xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->back, XCB_NONE, - xd->target, 0, 0, 0, 0, 0, 0, ps->root_width, - ps->root_height); + xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, + xd->back[xd->curr_back], XCB_NONE, xd->target, 0, 0, + 0, 0, 0, 0, ps->root_width, ps->root_height); } }