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 <yshuiv7@gmail.com>
This commit is contained in:
parent
28cf35e43b
commit
72a977eed5
|
@ -33,9 +33,11 @@ typedef struct _xrender_data {
|
||||||
/// The painting target, it is either the root or the overlay
|
/// The painting target, it is either the root or the overlay
|
||||||
xcb_render_picture_t target;
|
xcb_render_picture_t target;
|
||||||
/// A back buffer
|
/// 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
|
/// 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.
|
/// The original root window content, usually the wallpaper.
|
||||||
/// We save it so we don't loss the wallpaper when we paint over
|
/// We save it so we don't loss the wallpaper when we paint over
|
||||||
/// it.
|
/// 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
|
// Detect if the region is empty before painting
|
||||||
if (pixman_region32_not_empty(®_tmp)) {
|
if (pixman_region32_not_empty(®_tmp)) {
|
||||||
x_set_picture_clip_region(ps->c, xd->back, 0, 0, ®_tmp);
|
x_set_picture_clip_region(ps->c, xd->back[xd->curr_back], 0, 0,
|
||||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER,
|
®_tmp);
|
||||||
wd->shadow_pict, alpha_pict, xd->back, 0, 0, 0,
|
xcb_render_composite(
|
||||||
0, dst_x + w->shadow_dx, dst_y + w->shadow_dy,
|
ps->c, XCB_RENDER_PICT_OP_OVER, wd->shadow_pict, alpha_pict,
|
||||||
w->shadow_width, w->shadow_height);
|
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(®_tmp);
|
||||||
pixman_region32_fini(&shadow_reg);
|
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
|
// sure we get everything into the buffer
|
||||||
x_clear_picture_clip_region(ps->c, wd->rendered_pict);
|
x_clear_picture_clip_region(ps->c, wd->rendered_pict);
|
||||||
|
|
||||||
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, op, wd->rendered_pict, alpha_pict, xd->back, 0, 0, 0,
|
xcb_render_composite(ps->c, op, wd->rendered_pict, alpha_pict, xd->back[xd->curr_back],
|
||||||
0, dst_x, dst_y, w->widthb, w->heightb);
|
0, 0, 0, 0, dst_x, dst_y, w->widthb, w->heightb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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
|
// The multipass blur implemented here is not correct, but this is what old
|
||||||
// compton did anyway. XXX
|
// 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)];
|
auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)];
|
||||||
int current = 0;
|
int current = 0;
|
||||||
int src_x = reg->x1, src_y = reg->y1;
|
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 {
|
} else {
|
||||||
// This is the last pass, and this is also not the first
|
// This is the last pass, and this is also not the first
|
||||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict,
|
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict,
|
||||||
alpha_pict, xd->back, 0, 0, 0, 0, reg->x1,
|
alpha_pict, xd->back[xd->curr_back], 0, 0, 0,
|
||||||
reg->y1, width, height);
|
0, reg->x1, reg->y1, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset filter
|
// 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
|
// There is only 1 pass
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict,
|
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]);
|
xcb_render_free_picture(ps->c, tmp_picture[0]);
|
||||||
|
@ -425,10 +429,15 @@ static void *init(session_t *ps) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
xd->back_pixmap = x_create_pixmap(ps->c, pictfmt->depth, ps->root, ps->root_width,
|
// We might need to do double buffering for vsync
|
||||||
ps->root_height);
|
int pixmap_needed = ps->o.vsync ? 2 : 1;
|
||||||
xd->back = x_create_picture_with_pictfmt_and_pixmap(ps->c, pictfmt,
|
for (int i = 0; i < pixmap_needed; i++) {
|
||||||
xd->back_pixmap, 0, NULL);
|
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);
|
xcb_pixmap_t root_pixmap = x_get_root_back_pixmap(ps);
|
||||||
if (root_pixmap == XCB_NONE) {
|
if (root_pixmap == XCB_NONE) {
|
||||||
|
@ -440,7 +449,8 @@ static void *init(session_t *ps) {
|
||||||
|
|
||||||
if (ps->present_exists) {
|
if (ps->present_exists) {
|
||||||
auto eid = xcb_generate_id(ps->c);
|
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,
|
ps->c, eid, xd->target_win,
|
||||||
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY));
|
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY));
|
||||||
if (e) {
|
if (e) {
|
||||||
|
@ -448,9 +458,11 @@ static void *init(session_t *ps) {
|
||||||
free(e);
|
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) {
|
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++) {
|
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;
|
struct _xrender_data *xd = backend_data;
|
||||||
if (ps->o.vsync != VSYNC_NONE && ps->present_exists && xd->present_in_progress) {
|
if (ps->o.vsync != VSYNC_NONE && ps->present_exists && xd->present_in_progress) {
|
||||||
// TODO don't block wait for present completion
|
// 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);
|
assert(pev->evtype == XCB_PRESENT_COMPLETE_NOTIFY);
|
||||||
//xcb_present_complete_notify_event_t *pcev = (void *)pev;
|
xcb_present_complete_notify_event_t *pcev = (void *)pev;
|
||||||
//log_trace("Present complete: %d %ld", pcev->mode, pcev->msc);
|
// 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);
|
free(pev);
|
||||||
|
|
||||||
xd->present_in_progress = false;
|
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)
|
// Paint the root pixmap (i.e. wallpaper)
|
||||||
// Limit the paint area
|
// 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,
|
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) {
|
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.
|
// 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
|
// To make sure rendering won't get stuck if user toggles vsync on the
|
||||||
// fly.
|
// fly.
|
||||||
xcb_present_pixmap(ps->c, xd->target_win, xd->back_pixmap, 0, XCB_NONE,
|
xcb_present_pixmap(ps->c, xd->target_win, xd->back_pixmap[xd->curr_back],
|
||||||
XCB_NONE, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, 0,
|
0, XCB_NONE, XCB_NONE, 0, 0, XCB_NONE, XCB_NONE,
|
||||||
0, 0, 0, 0, NULL);
|
XCB_NONE, 0, 0, 0, 0, 0, NULL);
|
||||||
xd->present_in_progress = true;
|
xd->present_in_progress = true;
|
||||||
} else {
|
} else {
|
||||||
// compose() sets clip region, so clear it first to make
|
// compose() sets clip region, so clear it first to make
|
||||||
// sure we update the whole screen.
|
// 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.
|
// TODO buffer-age-like optimization might be possible here.
|
||||||
// but that will require a different backend API
|
// but that will require a different backend API
|
||||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->back, XCB_NONE,
|
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||||
xd->target, 0, 0, 0, 0, 0, 0, ps->root_width,
|
xd->back[xd->curr_back], XCB_NONE, xd->target, 0, 0,
|
||||||
ps->root_height);
|
0, 0, 0, 0, ps->root_width, ps->root_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue