emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master ee93a06b8b 1/3: Implement monitor refresh rate synchronization on


From: Po Lu
Subject: master ee93a06b8b 1/3: Implement monitor refresh rate synchronization on X
Date: Fri, 29 Jul 2022 05:27:49 -0400 (EDT)

branch: master
commit ee93a06b8b1922b31e12cfe60566779f185ddeba
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Implement monitor refresh rate synchronization on X
    
    * src/xfns.c (x_set_parent_frame, Fx_create_frame): Disable
    vsync on child and embedded frames.
    * src/xmenu.c (x_menu_show): Fix XMenu position calculation in
    child frames.
    
    * src/xterm.c (x_sync_is_frame_drawn_event)
    (x_sync_wait_for_frame_drawn_event): New functions.
    (x_sync_update_begin): Wait for frame to be drawn if not double
    buffered.
    (x_sync_update_finish): Set FRAME_X_WAITING_FOR_DRAW (f).
    (show_back_buffer): Wait for frame to be drawn before flipping
    buffers.
    (XTframe_up_to_date): Set FRAME_X_WAITING_FOR_DRAW if bumped.
    (handle_one_xevent): Handle frame drawn events.
    
    * src/xterm.h (struct x_output): New fields for frame dirtyness
    and vsync.
---
 src/xfns.c  | 21 +++++++++++++-
 src/xmenu.c | 23 +++++++++------
 src/xterm.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/xterm.h | 25 +++++++++++++++++
 4 files changed, 147 insertions(+), 15 deletions(-)

diff --git a/src/xfns.c b/src/xfns.c
index 076cd97875..579237068a 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -976,6 +976,16 @@ x_set_parent_frame (struct frame *f, Lisp_Object 
new_value, Lisp_Object old_valu
          gdk_x11_window_set_frame_sync_enabled (window, FALSE);
        }
 #endif
+
+#if defined HAVE_XSYNC && !defined USE_GTK
+      /* Frame synchronization can't be used in child frames since
+        they are not directly managed by the compositing manager.
+        Re-enabling vsync in former child frames also leads to
+        inconsistent display.  In addition, they can only be updated
+        outside of a toplevel frame.  */
+      FRAME_X_OUTPUT (f)->use_vsync_p = false;
+      FRAME_X_WAITING_FOR_DRAW (f) = false;
+#endif
       unblock_input ();
 
       fset_parent_frame (f, new_value);
@@ -5113,7 +5123,10 @@ This function is an internal primitive--use `make-frame' 
instead.  */)
     }
 
 #ifdef HAVE_XSYNC
-  if (dpyinfo->xsync_supported_p)
+  if (dpyinfo->xsync_supported_p
+      /* Frame synchronization isn't supported in child frames.  */
+      && NILP (parent_frame)
+      && !f->output_data.x->explicit_parent)
     {
 #ifndef HAVE_GTK3
       XSyncValue initial_value;
@@ -5149,6 +5162,12 @@ This function is an internal primitive--use `make-frame' 
instead.  */)
                       ((STRINGP (value)
                         && !strcmp (SSDATA (value), "extended")) ? 2 : 1));
 #endif
+
+#ifndef USE_GTK
+      if (FRAME_X_EXTENDED_COUNTER (f))
+       FRAME_X_OUTPUT (f)->use_vsync_p
+         = x_wm_supports (f, dpyinfo->Xatom_net_wm_frame_drawn);
+#endif
     }
 #endif
 
diff --git a/src/xmenu.c b/src/xmenu.c
index e5e24b87d1..3be0fb1876 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -2536,6 +2536,9 @@ Lisp_Object
 x_menu_show (struct frame *f, int x, int y, int menuflags,
             Lisp_Object title, const char **error_name)
 {
+#ifdef HAVE_X_WINDOWS
+  Window dummy_window;
+#endif
   Window root;
   XMenu *menu;
   int pane, selidx, lpane, status;
@@ -2584,20 +2587,22 @@ x_menu_show (struct frame *f, int x, int y, int 
menuflags,
   inhibit_garbage_collection ();
 
 #ifdef HAVE_X_WINDOWS
-  {
-    /* Adjust coordinates to relative to the outer (window manager) window. */
-    int left_off, top_off;
+  XTranslateCoordinates (FRAME_X_DISPLAY (f),
 
-    x_real_pos_and_offsets (f, &left_off, NULL, &top_off, NULL,
-                            NULL, NULL, NULL, NULL, NULL);
+                         /* From-window, to-window.  */
+                         FRAME_X_WINDOW (f),
+                         FRAME_DISPLAY_INFO (f)->root_window,
 
-    x += left_off;
-    y += top_off;
-  }
-#endif /* HAVE_X_WINDOWS */
+                         /* From-position, to-position.  */
+                         x, y, &x, &y,
 
+                         /* Child of win.  */
+                         &dummy_window);
+#else
+  /* MSDOS without X support.  */
   x += f->left_pos;
   y += f->top_pos;
+#endif
 
   /* Create all the necessary panes and their items.  */
   maxwidth = maxlines = lines = i = 0;
diff --git a/src/xterm.c b/src/xterm.c
index e9db4b364f..d3ffd432dd 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -6597,6 +6597,43 @@ x_set_frame_alpha (struct frame *f)
  ***********************************************************************/
 
 #if defined HAVE_XSYNC && !defined USE_GTK
+static Bool
+x_sync_is_frame_drawn_event (Display *dpy, XEvent *event,
+                            XPointer user_data)
+{
+  struct frame *f;
+  struct x_display_info *dpyinfo;
+
+  f = (struct frame *) user_data;
+  dpyinfo = FRAME_DISPLAY_INFO (f);
+
+  if (event->type == ClientMessage
+      && (event->xclient.message_type
+         == dpyinfo->Xatom_net_wm_frame_drawn)
+      && event->xclient.window == FRAME_OUTER_WINDOW (f))
+    return True;
+
+  return False;
+}
+
+/* Wait for the compositing manager to finish drawing the last frame.
+   If the compositing manager has already drawn everything, do
+   nothing.  */
+
+static void
+x_sync_wait_for_frame_drawn_event (struct frame *f)
+{
+  XEvent event;
+
+  if (!FRAME_X_WAITING_FOR_DRAW (f))
+    return;
+
+  /* Wait for the frame drawn message to arrive.  */
+  XIfEvent (FRAME_X_DISPLAY (f), &event,
+           x_sync_is_frame_drawn_event, (XPointer) f);
+  FRAME_X_WAITING_FOR_DRAW (f) = false;
+}
+
 /* Tell the compositing manager to postpone updates of F until a frame
    has finished drawing.  */
 
@@ -6616,6 +6653,15 @@ x_sync_update_begin (struct frame *f)
   if (XSyncValueLow32 (value) % 2)
     return;
 
+  /* Wait for a pending frame draw event if the last frame has not yet
+     been drawn if F isn't double buffered.  (In double buffered
+     frames, this happens before buffer flipping).  */
+
+#ifdef HAVE_XDBE
+  if (!FRAME_X_DOUBLE_BUFFERED_P (f))
+#endif
+    x_sync_wait_for_frame_drawn_event (f);
+
   /* Since Emacs needs a non-urgent redraw, ensure that value % 4 ==
      0.  */
   if (XSyncValueLow32 (value) % 4 == 2)
@@ -6668,7 +6714,20 @@ x_sync_update_finish (struct frame *f)
                   FRAME_X_EXTENDED_COUNTER (f),
                   FRAME_X_COUNTER_VALUE (f));
 
-  /* TODO: implement sync fences.  */
+  /* 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;
+}
+
+/* Handle a _NET_WM_FRAME_DRAWN message from the compositor.  */
+
+static void
+x_sync_handle_frame_drawn (struct x_display_info *dpyinfo,
+                          XEvent *message, struct frame *f)
+{
+  if (FRAME_OUTER_WINDOW (f) == message->xclient.window)
+    FRAME_X_WAITING_FOR_DRAW (f) = false;
 }
 #endif
 
@@ -6775,16 +6834,26 @@ x_draw_window_divider (struct window *w, int x0, int 
x1, int y0, int y1)
 static void
 show_back_buffer (struct frame *f)
 {
+  XdbeSwapInfo swap_info;
+#ifdef USE_CAIRO
+  cairo_t *cr;
+#endif
+
   block_input ();
 
   if (FRAME_X_DOUBLE_BUFFERED_P (f))
     {
+#if defined HAVE_XSYNC && !defined USE_GTK
+      /* Wait for drawing of the previous frame to complete before
+        displaying this new frame.  */
+      x_sync_wait_for_frame_drawn_event (f);
+#endif
+
 #ifdef USE_CAIRO
-      cairo_t *cr = FRAME_CR_CONTEXT (f);
+      cr = FRAME_CR_CONTEXT (f);
       if (cr)
        cairo_surface_flush (cairo_get_target (cr));
 #endif
-      XdbeSwapInfo swap_info;
       memset (&swap_info, 0, sizeof (swap_info));
       swap_info.swap_window = FRAME_X_WINDOW (f);
       swap_info.swap_action = XdbeCopied;
@@ -6911,6 +6980,11 @@ XTframe_up_to_date (struct frame *f)
                       FRAME_X_COUNTER_VALUE (f));
 
       FRAME_X_OUTPUT (f)->ext_sync_end_pending_p = false;
+
+#ifndef USE_GTK
+      if (FRAME_OUTPUT_DATA (f)->use_vsync_p)
+       FRAME_X_WAITING_FOR_DRAW (f) = true;
+#endif
     }
 #else
   if (FRAME_X_OUTPUT (f)->xg_sync_end_pending_p)
@@ -17072,8 +17146,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #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)
+       if (event->xclient.message_type
+           == dpyinfo->Xatom_net_wm_frame_drawn)
+         {
+           if (any)
+             x_sync_handle_frame_drawn (dpyinfo, (XEvent *) event, any);
+
+           goto done;
+         }
+
+       if (event->xclient.message_type
+           == dpyinfo->Xatom_net_wm_frame_timings)
          goto done;
 #endif
 
diff --git a/src/xterm.h b/src/xterm.h
index 3e237158e7..1163dd5cd1 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -1026,16 +1026,39 @@ struct x_output
 #endif
 
 #ifdef HAVE_XSYNC
+  /* The "basic frame counter" used for resize synchronization.  */
   XSyncCounter basic_frame_counter;
+
+  /* The "extended frame counter" used for frame synchronization.  */
   XSyncCounter extended_frame_counter;
+
+  /* The pending value of the basic counter.  */
   XSyncValue pending_basic_counter_value;
+
+  /* The current value of the extended counter.  */
   XSyncValue current_extended_counter_value;
 
+  /* Whether or not basic resize synchronization is in progress.  */
   bool_bf sync_end_pending_p : 1;
+
+  /* Whether or not extended resize synchronization is in
+     progress.  */
   bool_bf ext_sync_end_pending_p : 1;
+
 #ifdef HAVE_GTK3
+  /* Whether or not GDK resize synchronization is in progress.  */
   bool_bf xg_sync_end_pending_p : 1;
 #endif
+
+  /* Whether or Emacs is waiting for the compositing manager to draw a
+     frame.  */
+  bool_bf waiting_for_frame_p : 1;
+
+#ifndef USE_GTK
+  /* Whether or not Emacs should wait for the compositing manager to
+     draw frames before starting a new frame.  */
+  bool_bf use_vsync_p : 1;
+#endif
 #endif
 
   /* Relief GCs, colors etc.  */
@@ -1215,6 +1238,8 @@ extern void x_mark_frame_dirty (struct frame *f);
   FRAME_X_OUTPUT (f)->basic_frame_counter
 #define FRAME_X_EXTENDED_COUNTER(f)            \
   FRAME_X_OUTPUT (f)->extended_frame_counter
+#define FRAME_X_WAITING_FOR_DRAW(f)            \
+  FRAME_X_OUTPUT (f)->waiting_for_frame_p
 #define FRAME_X_COUNTER_VALUE(f)               \
   FRAME_X_OUTPUT (f)->current_extended_counter_value
 #endif



reply via email to

[Prev in Thread] Current Thread [Next in Thread]