Use one global XSync fence

And only sync once per frame.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2018-12-30 07:41:31 +00:00
parent 80847dd3fa
commit 4bfed7f7e3
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
5 changed files with 59 additions and 55 deletions

View File

@ -70,6 +70,7 @@
#include <xcb/damage.h>
#include <xcb/randr.h>
#include <xcb/shape.h>
#include <xcb/sync.h>
#ifdef CONFIG_XINERAMA
#include <xcb/xinerama.h>
@ -421,6 +422,7 @@ typedef struct session {
void *backend_data;
/// libev mainloop
struct ev_loop *loop;
// === Display related ===
/// Display in use.
Display *dpy;
@ -466,6 +468,8 @@ typedef struct session {
// XXX should be in glx_session_t
glx_prog_main_t glx_prog_win;
#endif
/// Sync fence to sync draw operations
xcb_sync_fence_t sync_fence;
// === Operation related ===
/// Program options.

View File

@ -957,11 +957,6 @@ unmap_win(session_t *ps, win **_w) {
if (w->destroyed) return;
// One last synchronization
if (w->paint.pixmap && ps->o.xrender_sync_fence) {
x_fence_sync(ps, w->paint.pixmap);
}
// Set focus out
win_set_focused(ps, w, false);
@ -2770,6 +2765,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
xcb_prefetch_extension_data(ps->c, &xcb_randr_id);
xcb_prefetch_extension_data(ps->c, &xcb_xinerama_id);
xcb_prefetch_extension_data(ps->c, &xcb_present_id);
xcb_prefetch_extension_data(ps->c, &xcb_sync_id);
ext_info = xcb_get_extension_data(ps->c, &xcb_render_id);
if (!ext_info || !ext_info->present) {
@ -2880,17 +2876,33 @@ session_init(session_t *ps_old, int argc, char **argv) {
}
// Query X Sync
if (XSyncQueryExtension(ps->dpy, &ps->xsync_event, &ps->xsync_error)) {
// TODO: Fencing may require version >= 3.0?
int major_version_return = 0, minor_version_return = 0;
if (XSyncInitialize(ps->dpy, &major_version_return, &minor_version_return))
ext_info = xcb_get_extension_data(ps->c, &xcb_sync_id);
if (ext_info && ext_info->present) {
ps->xsync_error = ext_info->first_error;
ps->xsync_event = ext_info->first_event;
// Need X Sync 3.1 for fences
auto r = xcb_sync_initialize_reply(ps->c, xcb_sync_initialize(ps->c, 3, 1), NULL);
if (r) {
ps->xsync_exists = true;
free(r);
}
}
ps->sync_fence = XCB_NONE;
if (!ps->xsync_exists && ps->o.xrender_sync_fence) {
log_fatal("X Sync extension not found. No X Sync fence sync is "
log_error("XSync extension not found. No XSync fence sync is "
"possible. (xrender-sync-fence can't be enabled)");
exit(1);
ps->o.xrender_sync_fence = false;
}
if (ps->o.xrender_sync_fence) {
ps->sync_fence = xcb_generate_id(ps->c);
auto e = xcb_request_check(ps->c, xcb_sync_create_fence(ps->c, ps->root, ps->sync_fence, 0));
if (e) {
log_error("Failed to create a XSync fence. xrender-sync-fence will be disabled");
ps->o.xrender_sync_fence = false;
free(e);
}
}
// Query X RandR
@ -3188,6 +3200,11 @@ session_destroy(session_t *ps) {
ps->overlay = XCB_NONE;
}
if (ps->sync_fence) {
xcb_sync_destroy_fence(ps->c, ps->sync_fence);
ps->sync_fence = XCB_NONE;
}
// Free reg_win
if (ps->reg_win) {
xcb_destroy_window(ps->c, ps->reg_win);

View File

@ -167,7 +167,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
ps, xcb_composite_name_window_pixmap(ps->c, w->id, w->paint.pixmap));
}
Drawable draw = w->paint.pixmap;
xcb_drawable_t draw = w->paint.pixmap;
if (!draw)
draw = w->id;
@ -181,10 +181,6 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
ps, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
}
if (IsViewable == w->a.map_state && ps->o.xrender_sync_fence) {
x_fence_sync(ps, draw);
}
// GLX: Build texture
// Let glx_bind_pixmap() determine pixmap size, because if the user
// is resizing windows, the width and height we get may not be up-to-date,
@ -605,11 +601,6 @@ static bool win_build_shadow(session_t *ps, win *w, double opacity) {
assert(!w->shadow_paint.pict);
w->shadow_paint.pict = shadow_picture_argb;
// Sync it once and only once
if (ps->o.xrender_sync_fence) {
x_fence_sync(ps, w->shadow_paint.pixmap);
}
xcb_free_gc(ps->c, gc);
xcb_image_destroy(shadow_image);
xcb_free_pixmap(ps->c, shadow_pixmap);
@ -822,8 +813,17 @@ win_blur_background(session_t *ps, win *w, xcb_render_picture_t tgt_buffer,
/// region = ??
/// region_real = the damage region
void paint_all(session_t *ps, region_t *region, const region_t *region_real, win *const t) {
if (!region_real)
if (ps->o.xrender_sync_fence) {
if (!x_fence_sync(ps, ps->sync_fence)) {
log_error("x_fence_sync failed, xrender-sync-fence will be disabled from now on.");
xcb_sync_destroy_fence(ps->c, ps->sync_fence);
ps->o.xrender_sync_fence = false;
}
}
if (!region_real) {
region_real = region;
}
#ifdef DEBUG_REPAINT
static struct timespec last_paint = {0};
@ -1027,15 +1027,8 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
glFlush();
glXWaitX();
assert(ps->tgt_buffer.pixmap);
if (ps->o.xrender_sync_fence) {
x_fence_sync(ps, ps->tgt_buffer.pixmap);
}
paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height,
ps->depth, !ps->o.glx_no_rebind_pixmap);
// See #163
if (ps->o.xrender_sync_fence) {
x_fence_sync(ps, ps->tgt_buffer.pixmap);
}
if (ps->o.vsync_use_glfinish)
glFinish();
else

40
src/x.c
View File

@ -454,46 +454,36 @@ bool x_atom_is_background_prop(session_t *ps, xcb_atom_t atom) {
return false;
}
/**
* Free a XSync fence.
*/
static inline void
x_free_fence(session_t *ps, xcb_sync_fence_t *pfence) {
if (*pfence) {
xcb_sync_destroy_fence(ps->c, *pfence);
}
*pfence = XCB_NONE;
}
/**
* Synchronizes a X Render drawable to ensure all pending painting requests
* are completed.
*/
void x_fence_sync(session_t *ps, xcb_drawable_t d) {
x_sync(ps->c);
bool x_fence_sync(session_t *ps, xcb_sync_fence_t f) {
if (ps->xsync_exists) {
// TODO(richardgv): If everybody just follows the rules stated in X Sync
// prototype, we need only one fence per screen, but let's stay a bit
// cautious right now
xcb_sync_fence_t tmp_fence = xcb_generate_id(ps->c);
xcb_generic_error_t *e =
xcb_request_check(ps->c,
xcb_sync_create_fence(ps->c, d, tmp_fence, 0));
auto e = xcb_request_check(ps->c, xcb_sync_trigger_fence_checked(ps->c, f));
if (e) {
log_error("Failed to create a XSync fence for %#010x", d);
log_error("Failed to trigger the fence.");
free(e);
return;
return false;
}
e = xcb_request_check(ps->c, xcb_sync_trigger_fence(ps->c, tmp_fence));
e = xcb_request_check(ps->c, xcb_sync_await_fence_checked(ps->c, 1, &f));
if (e) {
log_error("Failed to trigger the fence");
log_error("Failed to await on a fence.");
free(e);
x_free_fence(ps, &tmp_fence);
return;
return false;
}
xcb_sync_await_fence(ps->c, 1, &tmp_fence);
x_free_fence(ps, &tmp_fence);
e = xcb_request_check(ps->c, xcb_sync_reset_fence_checked(ps->c, f));
if (e) {
log_error("Failed to reset the fence.");
free(e);
return false;
}
}
return true;
}

View File

@ -169,4 +169,4 @@ xcb_pixmap_t x_get_root_back_pixmap(session_t *ps);
/// root window background pixmap
bool x_atom_is_background_prop(session_t *ps, xcb_atom_t atom);
void x_fence_sync(session_t *, xcb_sync_fence_t);
bool x_fence_sync(session_t *, xcb_sync_fence_t);