From 1cdc64cdda59fd6ff84e0fd6da9b61c5451cb9e5 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 29 Jul 2022 10:13:54 +0800 Subject: [PATCH] Implement extended frame synchronization * src/xterm.c (x_atom_refs): New atom _NET_WM_FRAME_TIMINGS. (x_sync_update_finish, x_sync_update_begin): New frame. (x_update_begin, x_update_end, XTframe_up_to_date): Begin and end frames accordingly if extended frame synchronization is enabled. (handle_one_xevent): Ignore timing and frame drawn events. * src/xterm.h (struct x_display_info): New atom. (FRAME_X_COUNTER_VALUE): New macro. --- src/xterm.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++------ src/xterm.h | 10 +++-- 2 files changed, 116 insertions(+), 17 deletions(-) diff --git a/src/xterm.c b/src/xterm.c index e7222d35b3a..acdac92d94a 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -998,6 +998,7 @@ static const struct x_atom_ref x_atom_refs[] = 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_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) ATOM_REFS_INIT ("_NET_WM_USER_TIME_WINDOW", Xatom_net_wm_user_time_window) ATOM_REFS_INIT ("_NET_CLIENT_LIST_STACKING", Xatom_net_client_list_stacking) @@ -6595,6 +6596,82 @@ x_set_frame_alpha (struct frame *f) Starting and ending an update ***********************************************************************/ +#if defined HAVE_XSYNC && !defined USE_GTK +/* Tell the compositing manager to postpone updates of F until a frame + has finished drawing. */ + +static void +x_sync_update_begin (struct frame *f) +{ + XSyncValue value, add; + Bool overflow; + + if (FRAME_X_EXTENDED_COUNTER (f) == None) + return; + + value = FRAME_X_COUNTER_VALUE (f); + + /* Since a frame is already in progress, there is no point in + continuing. */ + if (XSyncValueLow32 (value) % 2) + return; + + /* Since Emacs needs a non-urgent redraw, ensure that value % 4 == + 0. */ + if (XSyncValueLow32 (value) % 4 == 2) + XSyncIntToValue (&add, 3); + else + XSyncIntToValue (&add, 1); + + XSyncValueAdd (&FRAME_X_COUNTER_VALUE (f), + value, add, &overflow); + + if (XSyncValueLow32 (FRAME_X_COUNTER_VALUE (f)) % 4 != 1) + emacs_abort (); + + if (overflow) + XSyncIntToValue (&FRAME_X_COUNTER_VALUE (f), 1); + + XSyncSetCounter (FRAME_X_DISPLAY (f), + FRAME_X_EXTENDED_COUNTER (f), + FRAME_X_COUNTER_VALUE (f)); +} + +/* Tell the compositing manager that FRAME has been drawn and can be + updated. */ + +static void +x_sync_update_finish (struct frame *f) +{ + XSyncValue value, add; + Bool overflow; + + if (FRAME_X_EXTENDED_COUNTER (f) == None) + return; + + if (FRAME_X_OUTPUT (f)->ext_sync_end_pending_p) + return; + + value = FRAME_X_COUNTER_VALUE (f); + + if (!(XSyncValueLow32 (value) % 2)) + return; + + XSyncIntToValue (&add, 1); + XSyncValueAdd (&FRAME_X_COUNTER_VALUE (f), + value, add, &overflow); + + if (overflow) + XSyncIntToValue (&FRAME_X_COUNTER_VALUE (f), 0); + + XSyncSetCounter (FRAME_X_DISPLAY (f), + FRAME_X_EXTENDED_COUNTER (f), + FRAME_X_COUNTER_VALUE (f)); + + /* TODO: implement sync fences. */ +} +#endif + /* Start an update of frame F. This function is installed as a hook for update_begin, i.e. it is called when update_begin is called. This function is called prior to calls to gui_update_window_begin for @@ -6604,7 +6681,11 @@ x_set_frame_alpha (struct frame *f) static void x_update_begin (struct frame *f) { +#if defined HAVE_XSYNC && !defined USE_GTK + x_sync_update_begin (f); +#else /* Nothing to do. */ +#endif } /* Draw a vertical window border from (x,y0) to (x,y1) */ @@ -6750,17 +6831,17 @@ x_update_end (struct frame *f) #ifdef USE_CAIRO if (!FRAME_X_DOUBLE_BUFFERED_P (f) && FRAME_CR_CONTEXT (f)) - { - block_input (); - cairo_surface_flush (cairo_get_target (FRAME_CR_CONTEXT (f))); - unblock_input (); - } + cairo_surface_flush (cairo_get_target (FRAME_CR_CONTEXT (f))); #endif -#ifndef XFlush - block_input (); - XFlush (FRAME_X_DISPLAY (f)); - unblock_input (); + /* If double buffering is disabled, finish the update here. + Otherwise, finish the update when the back buffer is next + displayed. */ +#if defined HAVE_XSYNC && !defined USE_GTK +#ifdef HAVE_XDBE + if (!FRAME_X_DOUBLE_BUFFERED_P (f)) +#endif + x_sync_update_finish (f); #endif } @@ -6788,6 +6869,11 @@ XTframe_up_to_date (struct frame *f) if (!buffer_flipping_blocked_p () && FRAME_X_NEED_BUFFER_FLIP (f)) show_back_buffer (f); + +#if defined HAVE_XSYNC && !defined USE_GTK + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + x_sync_update_finish (f); +#endif #endif #ifdef HAVE_XSYNC @@ -6804,14 +6890,14 @@ XTframe_up_to_date (struct frame *f) if (FRAME_X_OUTPUT (f)->ext_sync_end_pending_p && FRAME_X_EXTENDED_COUNTER (f) != None) { - current = FRAME_X_OUTPUT (f)->current_extended_counter_value; + current = FRAME_X_COUNTER_VALUE (f); if (XSyncValueLow32 (current) % 2) XSyncIntToValue (&add, 1); else XSyncIntToValue (&add, 2); - XSyncValueAdd (&FRAME_X_OUTPUT (f)->current_extended_counter_value, + XSyncValueAdd (&FRAME_X_COUNTER_VALUE (f), current, add, &overflow_p); if (overflow_p) @@ -6819,7 +6905,7 @@ XTframe_up_to_date (struct frame *f) XSyncSetCounter (FRAME_X_DISPLAY (f), FRAME_X_EXTENDED_COUNTER (f), - FRAME_X_OUTPUT (f)->current_extended_counter_value); + FRAME_X_COUNTER_VALUE (f)); FRAME_X_OUTPUT (f)->ext_sync_end_pending_p = false; } @@ -16861,8 +16947,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, } else if (event->xclient.data.l[4] == 1) { - XSyncIntsToValue (&FRAME_X_OUTPUT (f)->current_extended_counter_value, - event->xclient.data.l[2], event->xclient.data.l[3]); + XSyncIntsToValue (&FRAME_X_COUNTER_VALUE (f), + event->xclient.data.l[2], + event->xclient.data.l[3]); FRAME_X_OUTPUT (f)->ext_sync_end_pending_p = true; } @@ -16979,6 +17066,14 @@ handle_one_xevent (struct x_display_info *dpyinfo, goto done; } +#if defined HAVE_XSYNC && !defined USE_GTK + /* These messages are sent by the compositing manager after a + frame is drawn under extended synchronization. */ + if (event->xclient.message_type == dpyinfo->Xatom_net_wm_frame_drawn + || event->xclient.message_type == dpyinfo->Xatom_net_wm_frame_timings) + goto done; +#endif + xft_settings_event (dpyinfo, event); f = any; diff --git a/src/xterm.h b/src/xterm.h index b9e7b094e31..3e237158e7e 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -614,7 +614,7 @@ 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_user_time, + 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; @@ -1211,8 +1211,12 @@ extern void x_mark_frame_dirty (struct frame *f); #endif #ifdef HAVE_XSYNC -#define FRAME_X_BASIC_COUNTER(f) FRAME_X_OUTPUT (f)->basic_frame_counter -#define FRAME_X_EXTENDED_COUNTER(f) FRAME_X_OUTPUT (f)->extended_frame_counter +#define FRAME_X_BASIC_COUNTER(f) \ + FRAME_X_OUTPUT (f)->basic_frame_counter +#define FRAME_X_EXTENDED_COUNTER(f) \ + FRAME_X_OUTPUT (f)->extended_frame_counter +#define FRAME_X_COUNTER_VALUE(f) \ + FRAME_X_OUTPUT (f)->current_extended_counter_value #endif /* This is the Colormap which frame F uses. */ -- 2.39.5