new xrender: implement partial updates

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2019-07-26 02:21:35 +01:00
parent 5a861d5d6a
commit d37a4136ee
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
1 changed files with 44 additions and 32 deletions

View File

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