Correctly implement refresh synchronization fences

* configure.ac (HAVE_XSYNC): Also check for XSyncTriggerFence.

* src/xfns.c (Fx_create_frame): Create fences.
* src/xterm.c (x_atom_refs): New atom.
(x_sync_trigger_fence, x_sync_init_fences, x_sync_free_fences):
New functions.
(x_sync_update_finish): Trigger the appropriate fence.
(x_free_frame_resources): Free fences.
* src/xterm.h (struct x_display_info): New atom
`_NET_WM_SYNC_FENCES'.
(struct x_output): New field `sync_fences'.
This commit is contained in:
Po Lu 2022-08-04 15:37:54 +08:00
parent 3e60f7f3c3
commit 78129dcf53
4 changed files with 111 additions and 5 deletions

View file

@ -4675,6 +4675,10 @@ if test "${HAVE_X11}" = "yes"; then
AC_DEFINE([HAVE_XSYNC], [1],
[Define to 1 if the X Synchronization Extension is available.])
XSYNC_LIBS="-lXext"
OLDLIBS="$LIBS"
LIBS="-lXext $LIBS" # Set this temporarily for AC_CHECK_FUNC
AC_CHECK_FUNCS([XSyncTriggerFence]) # Check for version 3.1
LIBS="$OLDLIBS"
fi
fi
AC_SUBST([XSYNC_LIBS])

View file

@ -5157,6 +5157,10 @@ This function is an internal primitive--use `make-frame' instead. */)
(unsigned char *) &counters,
((STRINGP (value)
&& !strcmp (SSDATA (value), "extended")) ? 2 : 1));
#if defined HAVE_XSYNCTRIGGERFENCE && !defined USE_GTK
x_sync_init_fences (f);
#endif
#endif
}
#endif

View file

@ -997,6 +997,7 @@ static const struct x_atom_ref x_atom_refs[] =
ATOM_REFS_INIT ("_NET_WORKAREA", Xatom_net_workarea)
ATOM_REFS_INIT ("_NET_WM_SYNC_REQUEST", Xatom_net_wm_sync_request)
ATOM_REFS_INIT ("_NET_WM_SYNC_REQUEST_COUNTER", Xatom_net_wm_sync_request_counter)
ATOM_REFS_INIT ("_NET_WM_SYNC_FENCES", Xatom_net_wm_sync_fences)
ATOM_REFS_INIT ("_NET_WM_FRAME_DRAWN", Xatom_net_wm_frame_drawn)
ATOM_REFS_INIT ("_NET_WM_FRAME_TIMINGS", Xatom_net_wm_frame_timings)
ATOM_REFS_INIT ("_NET_WM_USER_TIME", Xatom_net_wm_user_time)
@ -6812,6 +6813,85 @@ x_sync_update_begin (struct frame *f)
FRAME_X_COUNTER_VALUE (f));
}
#ifdef HAVE_XSYNCTRIGGERFENCE
/* Trigger the sync fence for counter VALUE immediately before a frame
finishes. */
static void
x_sync_trigger_fence (struct frame *f, XSyncValue value)
{
uint64_t n, low, high, idx;
/* Sync fences aren't supported by the X server. */
if (FRAME_DISPLAY_INFO (f)->xsync_major < 3
|| (FRAME_DISPLAY_INFO (f)->xsync_major == 3
&& FRAME_DISPLAY_INFO (f)->xsync_minor < 1))
return;
low = XSyncValueLow32 (value);
high = XSyncValueHigh32 (value);
n = low | (high << 32);
idx = (n / 4) % 2;
#ifdef FRAME_DEBUG
fprintf (stderr, "Triggering synchonization fence: %lu\n", idx);
#endif
XSyncTriggerFence (FRAME_X_DISPLAY (f),
FRAME_X_OUTPUT (f)->sync_fences[idx]);
}
/* Initialize the sync fences on F. */
void
x_sync_init_fences (struct frame *f)
{
struct x_output *output;
struct x_display_info *dpyinfo;
output = FRAME_X_OUTPUT (f);
dpyinfo = FRAME_DISPLAY_INFO (f);
/* Sync fences aren't supported by the X server. */
if (dpyinfo->xsync_major < 3
|| (dpyinfo->xsync_major == 3
&& dpyinfo->xsync_minor < 1))
return;
output->sync_fences[0]
= XSyncCreateFence (FRAME_X_DISPLAY (f),
/* The drawable given below is only used to
determine the screen on which the fence is
created. */
FRAME_X_WINDOW (f),
False);
output->sync_fences[1]
= XSyncCreateFence (FRAME_X_DISPLAY (f),
FRAME_X_WINDOW (f),
False);
XChangeProperty (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
dpyinfo->Xatom_net_wm_sync_fences, XA_CARDINAL,
32, PropModeReplace,
(unsigned char *) &output->sync_fences, 1);
}
static void
x_sync_free_fences (struct frame *f)
{
if (FRAME_X_OUTPUT (f)->sync_fences[0] != None)
XSyncDestroyFence (FRAME_X_DISPLAY (f),
FRAME_X_OUTPUT (f)->sync_fences[0]);
if (FRAME_X_OUTPUT (f)->sync_fences[1] != None)
XSyncDestroyFence (FRAME_X_DISPLAY (f),
FRAME_X_OUTPUT (f)->sync_fences[1]);
}
#endif
/* Tell the compositing manager that FRAME has been drawn and can be
updated. */
@ -6844,12 +6924,15 @@ x_sync_update_finish (struct frame *f)
if (overflow)
XSyncIntToValue (&FRAME_X_COUNTER_VALUE (f), 0);
/* Trigger any sync fences if necessary. */
#ifdef HAVE_XSYNCTRIGGERFENCE
x_sync_trigger_fence (f, FRAME_X_COUNTER_VALUE (f));
#endif
XSyncSetCounter (FRAME_X_DISPLAY (f),
FRAME_X_EXTENDED_COUNTER (f),
FRAME_X_COUNTER_VALUE (f));
/* FIXME: this leads to freezes if the compositing manager crashes
in the meantime. */
if (FRAME_OUTPUT_DATA (f)->use_vsync_p)
FRAME_X_WAITING_FOR_DRAW (f) = true;
}
@ -26282,6 +26365,11 @@ x_free_frame_resources (struct frame *f)
XFreeCursor (FRAME_X_DISPLAY (f), f->output_data.x->bottom_edge_cursor);
if (f->output_data.x->bottom_left_corner_cursor != 0)
XFreeCursor (FRAME_X_DISPLAY (f), f->output_data.x->bottom_left_corner_cursor);
/* Free sync fences. */
#if defined HAVE_XSYNCTRIGGERFENCE && !defined USE_GTK
x_sync_free_fences (f);
#endif
}
#ifdef HAVE_GTK3

View file

@ -614,9 +614,9 @@ struct x_display_info
Xatom_net_wm_state_shaded, Xatom_net_frame_extents, Xatom_net_current_desktop,
Xatom_net_workarea, Xatom_net_wm_opaque_region, Xatom_net_wm_ping,
Xatom_net_wm_sync_request, Xatom_net_wm_sync_request_counter,
Xatom_net_wm_frame_drawn, Xatom_net_wm_frame_timings, Xatom_net_wm_user_time,
Xatom_net_wm_user_time_window, Xatom_net_client_list_stacking,
Xatom_net_wm_pid;
Xatom_net_wm_sync_fences, Xatom_net_wm_frame_drawn, Xatom_net_wm_frame_timings,
Xatom_net_wm_user_time, Xatom_net_wm_user_time_window,
Xatom_net_client_list_stacking, Xatom_net_wm_pid;
/* XSettings atoms and windows. */
Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr;
@ -1077,6 +1077,13 @@ struct x_output
/* A temporary time used to calculate that value. */
uint64_t temp_frame_time;
#ifdef HAVE_XSYNCTRIGGERFENCE
/* An array of two sync fences that are triggered in order after a
frame completes. Not initialized if the XSync extension is too
old to support sync fences. */
XSyncFence sync_fences[2];
#endif
#endif
#endif
@ -1516,6 +1523,9 @@ extern void x_make_frame_invisible (struct frame *);
extern void x_iconify_frame (struct frame *);
extern void x_free_frame_resources (struct frame *);
extern void x_wm_set_size_hint (struct frame *, long, bool);
#if defined HAVE_XSYNCTRIGGERFENCE && !defined USE_GTK
extern void x_sync_init_fences (struct frame *);
#endif
extern void x_delete_terminal (struct terminal *);
extern Cursor x_create_font_cursor (struct x_display_info *, int);