diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index 0b9f82a..5810513 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -28,15 +28,14 @@ typedef struct _xrender_data { /// If vsync is enabled and supported by the current system bool vsync; xcb_visualid_t default_visual; - /// The idle fence for the present extension - xcb_sync_fence_t idle_fence; - /// The target window + /// Target window xcb_window_t target_win; - /// The painting target, it is either the root or the overlay + /// Painting target, it is either the root or the overlay xcb_render_picture_t target; - /// A back buffer - xcb_render_picture_t back[2]; - /// Age of each back buffer + /// Back buffers. Double buffer, with 1 for temporary render use + xcb_render_picture_t back[3]; + /// The back buffer that is for temporary use + /// Age of each back buffer. int buffer_age[2]; /// The back buffer we should be painting into int curr_back; @@ -104,9 +103,9 @@ static void compose(backend_t *base, void *img_data, int dst_x, int dst_y, // sure we get everything into the buffer x_clear_picture_clip_region(base->c, img->pict); - x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, ®); - xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back], - 0, 0, 0, 0, to_i16_checked(dst_x), to_i16_checked(dst_y), + x_set_picture_clip_region(base->c, xd->back[2], 0, 0, ®); + xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0, 0, 0, 0, + to_i16_checked(dst_x), to_i16_checked(dst_y), to_u16_checked(img->ewidth), to_u16_checked(img->eheight)); pixman_region32_fini(®); } @@ -114,10 +113,10 @@ static void compose(backend_t *base, void *img_data, int dst_x, int dst_y, static void fill(backend_t *base, struct color c, const region_t *clip) { struct _xrender_data *xd = (void *)base; const rect_t *extent = pixman_region32_extents((region_t *)clip); - x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, clip); + x_set_picture_clip_region(base->c, xd->back[2], 0, 0, clip); // color is in X fixed point representation xcb_render_fill_rectangles( - base->c, XCB_RENDER_PICT_OP_OVER, xd->back[xd->curr_back], + base->c, XCB_RENDER_PICT_OP_OVER, xd->back[2], (xcb_render_color_t){.red = (uint16_t)(c.red * 0xffff), .green = (uint16_t)(c.green * 0xffff), .blue = (uint16_t)(c.blue * 0xffff), @@ -177,7 +176,7 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip); pixman_region32_fini(&clip); - xcb_render_picture_t src_pict = xd->back[xd->curr_back], dst_pict = tmp_picture[0]; + xcb_render_picture_t src_pict = xd->back[2], dst_pict = tmp_picture[0]; auto alpha_pict = xd->alpha_pict[(int)(opacity * MAX_ALPHA)]; int current = 0; x_set_picture_clip_region(c, src_pict, 0, 0, ®_op_resized); @@ -212,11 +211,11 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, XCB_NONE, dst_pict, 0, 0, 0, 0, 0, 0, width_resized, height_resized); } else { - x_set_picture_clip_region(c, xd->back[xd->curr_back], 0, 0, ®_op); + x_set_picture_clip_region(c, xd->back[2], 0, 0, ®_op); // This is the last pass, and we are doing more than 1 pass xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, - alpha_pict, xd->back[xd->curr_back], 0, 0, 0, - 0, to_i16_checked(extent_resized->x1), + alpha_pict, xd->back[2], 0, 0, 0, 0, + to_i16_checked(extent_resized->x1), to_i16_checked(extent_resized->y1), width_resized, height_resized); } @@ -232,10 +231,10 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, // There is only 1 pass if (i == 1) { - x_set_picture_clip_region(c, xd->back[xd->curr_back], 0, 0, ®_op); + x_set_picture_clip_region(c, xd->back[2], 0, 0, ®_op); xcb_render_composite( - c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, - xd->back[xd->curr_back], 0, 0, 0, 0, to_i16_checked(extent_resized->x1), + c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict, xd->back[2], 0, 0, + 0, 0, to_i16_checked(extent_resized->x1), to_i16_checked(extent_resized->y1), width_resized, height_resized); } @@ -301,10 +300,25 @@ static void deinit(backend_t *backend_data) { free(xd); } -static void present(backend_t *base, const region_t *region attr_unused) { +static void present(backend_t *base, const region_t *region) { struct _xrender_data *xd = (void *)base; + const rect_t *extent = pixman_region32_extents((region_t *)region); + int16_t orig_x = to_i16_checked(extent->x1), orig_y = to_i16_checked(extent->y1); + uint16_t region_width = to_u16_checked(extent->x2 - extent->x1), + region_height = to_u16_checked(extent->y2 - extent->y1); + + // compose() sets clip region on the back buffer, so clear it first + x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]); + + // limit the region of update + x_set_picture_clip_region(base->c, xd->back[2], 0, 0, region); if (xd->vsync) { + // Update the back buffer first, then present + xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2], + XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0, + 0, orig_x, orig_y, region_width, region_height); + // Make sure we got reply from PresentPixmap before waiting for events, // to avoid deadlock auto e = xcb_request_check( @@ -342,16 +356,13 @@ static void present(backend_t *base, const region_t *region attr_unused) { } free(pev); } else { - // compose() sets clip region, so clear it first to make - // sure we update the whole screen. - x_clear_picture_clip_region(xd->base.c, xd->back[xd->curr_back]); + // No vsync needed, draw into the target picture directly + xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2], + XCB_NONE, xd->target, orig_x, orig_y, 0, 0, orig_x, + orig_y, region_width, region_height); - // TODO buffer-age-like optimization might be possible here. - // but that will require a different backend API - xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, - xd->back[xd->curr_back], XCB_NONE, xd->target, 0, 0, - 0, 0, 0, 0, to_u16_checked(xd->target_width), - to_u16_checked(xd->target_height)); + // Only the target picture really holds the screen content, and its + // content is always up to date. So buffer age is always 1. xd->buffer_age[xd->curr_back] = 1; } } @@ -589,9 +600,10 @@ backend_t *backend_xrender_init(session_t *ps) { xd->vsync = false; } - // We might need to do double buffering for vsync - int pixmap_needed = xd->vsync ? 2 : 1; - for (int i = 0; i < pixmap_needed; i++) { + // We might need to do double buffering for vsync, and buffer 0 and 1 are for + // double buffering. + int first_buffer_index = xd->vsync ? 0 : 2; + for (int i = first_buffer_index; i < 3; i++) { xd->back_pixmap[i] = x_create_pixmap(ps->c, pictfmt->depth, ps->root, to_u16_checked(ps->root_width), to_u16_checked(ps->root_height));