Blame SOURCES/0006-For-NVIDIA-proprietary-drivers-implement-sync-events.patch

821e46
From 062fd3cc6bf40990f748af9244728060f5330109 Mon Sep 17 00:00:00 2001
821e46
From: "Owen W. Taylor" <otaylor@fishsoup.net>
821e46
Date: Thu, 11 Feb 2016 17:15:13 -0500
821e46
Subject: [PATCH 6/7] For NVIDIA proprietary drivers, implement sync events
821e46
 with a thread
821e46
821e46
It's a good guess that the buffer swap will occur at the next vblank,
821e46
so use glXWaitVideoSync in a separate thread to deliver a sync event
821e46
rather than just letting the client block when frame drawing, which
821e46
can signficantly change app logic as compared to the INTEL_swap_event
821e46
case.
821e46
---
821e46
 cogl/cogl-private.h           |   3 +
821e46
 cogl/winsys/cogl-winsys-glx.c | 294 ++++++++++++++++++++++++++++++++++++++++--
821e46
 examples/cogl-crate.c         |   3 +
821e46
 3 files changed, 289 insertions(+), 11 deletions(-)
821e46
821e46
diff -up cogl-1.18.2/cogl/cogl-private.h.threaded-sync-events cogl-1.18.2/cogl/cogl-private.h
821e46
--- cogl-1.18.2/cogl/cogl-private.h.threaded-sync-events	2014-07-02 19:31:31.000000000 -0400
821e46
+++ cogl-1.18.2/cogl/cogl-private.h	2016-06-30 15:44:39.823352236 -0400
821e46
@@ -77,6 +77,9 @@ typedef enum
821e46
   COGL_PRIVATE_FEATURE_GL_PROGRAMMABLE,
821e46
   COGL_PRIVATE_FEATURE_GL_EMBEDDED,
821e46
   COGL_PRIVATE_FEATURE_GL_WEB,
821e46
+  /* This is currently only implemented for GLX, but isn't actually
821e46
+   * that winsys dependent */
821e46
+  COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT,
821e46
 
821e46
   COGL_N_PRIVATE_FEATURES
821e46
 } CoglPrivateFeature;
821e46
diff -up cogl-1.18.2/cogl/winsys/cogl-winsys-glx.c.threaded-sync-events cogl-1.18.2/cogl/winsys/cogl-winsys-glx.c
821e46
--- cogl-1.18.2/cogl/winsys/cogl-winsys-glx.c.threaded-sync-events	2016-06-30 15:44:39.822352220 -0400
821e46
+++ cogl-1.18.2/cogl/winsys/cogl-winsys-glx.c	2016-06-30 15:48:22.371948425 -0400
821e46
@@ -65,12 +65,16 @@
821e46
 #include <sys/types.h>
821e46
 #include <sys/stat.h>
821e46
 #include <sys/time.h>
821e46
+#include <errno.h>
821e46
 #include <fcntl.h>
821e46
 #include <time.h>
821e46
+#include <unistd.h>
821e46
 
821e46
 #include <GL/glx.h>
821e46
 #include <X11/Xlib.h>
821e46
 
821e46
+#include <glib.h>
821e46
+
821e46
 /* This is a relatively new extension */
821e46
 #ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV
821e46
 #define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7
821e46
@@ -100,6 +104,14 @@ typedef struct _CoglOnscreenGLX
821e46
   CoglBool pending_sync_notify;
821e46
   CoglBool pending_complete_notify;
821e46
   CoglBool pending_resize_notify;
821e46
+
821e46
+  GThread *swap_wait_thread;
821e46
+  GQueue *swap_wait_queue;
821e46
+  GCond swap_wait_cond;
821e46
+  GMutex swap_wait_mutex;
821e46
+  int swap_wait_pipe[2];
821e46
+  GLXContext swap_wait_context;
821e46
+  CoglBool closing_down;
821e46
 } CoglOnscreenGLX;
821e46
 
821e46
 typedef struct _CoglPixmapTextureEyeGLX
821e46
@@ -885,6 +897,28 @@ update_winsys_features (CoglContext *con
821e46
                       COGL_FEATURE_ID_PRESENTATION_TIME,
821e46
                       TRUE);
821e46
     }
821e46
+  else
821e46
+    {
821e46
+      CoglGpuInfo *info = &context->gpu;
821e46
+      if (glx_display->have_vblank_counter &&
821e46
+	  info->vendor == COGL_GPU_INFO_VENDOR_NVIDIA)
821e46
+        {
821e46
+          COGL_FLAGS_SET (context->winsys_features,
821e46
+                          COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT, TRUE);
821e46
+          COGL_FLAGS_SET (context->winsys_features,
821e46
+                          COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE);
821e46
+          /* TODO: remove this deprecated feature */
821e46
+          COGL_FLAGS_SET (context->features,
821e46
+                          COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
821e46
+                          TRUE);
821e46
+          COGL_FLAGS_SET (context->features,
821e46
+                          COGL_FEATURE_ID_PRESENTATION_TIME,
821e46
+                          TRUE);
821e46
+          COGL_FLAGS_SET (context->private_features,
821e46
+                          COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT,
821e46
+                          TRUE);
821e46
+        }
821e46
+    }
821e46
 
821e46
   /* We'll manually handle queueing dirty events in response to
821e46
    * Expose events from X */
821e46
@@ -1481,7 +1515,8 @@ _cogl_winsys_onscreen_init (CoglOnscreen
821e46
     }
821e46
 
821e46
 #ifdef GLX_INTEL_swap_event
821e46
-  if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
821e46
+  if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT) &&
821e46
+      !_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT))
821e46
     {
821e46
       GLXDrawable drawable =
821e46
         glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin;
821e46
@@ -1524,6 +1559,31 @@ _cogl_winsys_onscreen_deinit (CoglOnscre
821e46
       xlib_onscreen->output = NULL;
821e46
     }
821e46
 
821e46
+  if (glx_onscreen->swap_wait_thread)
821e46
+    {
821e46
+      g_mutex_lock (&glx_onscreen->swap_wait_mutex);
821e46
+      glx_onscreen->closing_down = TRUE;
821e46
+      g_cond_signal (&glx_onscreen->swap_wait_cond);
821e46
+      g_mutex_unlock (&glx_onscreen->swap_wait_mutex);
821e46
+      g_thread_join (glx_onscreen->swap_wait_thread);
821e46
+      glx_onscreen->swap_wait_thread = NULL;
821e46
+
821e46
+      g_cond_clear (&glx_onscreen->swap_wait_cond);
821e46
+      g_mutex_clear (&glx_onscreen->swap_wait_mutex);
821e46
+
821e46
+      g_queue_free (glx_onscreen->swap_wait_queue);
821e46
+      glx_onscreen->swap_wait_queue = NULL;
821e46
+
821e46
+      _cogl_poll_renderer_remove_fd (context->display->renderer,
821e46
+                                     glx_onscreen->swap_wait_pipe[0]);
821e46
+      
821e46
+      close (glx_onscreen->swap_wait_pipe[0]);
821e46
+      close (glx_onscreen->swap_wait_pipe[1]);
821e46
+
821e46
+      glx_renderer->glXDestroyContext (xlib_renderer->xdpy,
821e46
+                                       glx_onscreen->swap_wait_context);
821e46
+    }
821e46
+
821e46
   _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state);
821e46
 
821e46
   drawable =
821e46
@@ -1759,6 +1819,199 @@ set_frame_info_output (CoglOnscreen *ons
821e46
     }
821e46
 }
821e46
 
821e46
+static gpointer
821e46
+threaded_swap_wait (gpointer data)
821e46
+{
821e46
+  CoglOnscreen *onscreen = data;
821e46
+
821e46
+  CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
821e46
+
821e46
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
821e46
+  CoglContext *context = framebuffer->context;
821e46
+  CoglDisplay *display = context->display;
821e46
+  CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer);
821e46
+  CoglGLXDisplay *glx_display = display->winsys;
821e46
+  CoglGLXRenderer *glx_renderer = display->renderer->winsys;
821e46
+  GLXDrawable dummy_drawable;
821e46
+
821e46
+  if (glx_display->dummy_glxwin)
821e46
+    dummy_drawable = glx_display->dummy_glxwin;
821e46
+  else
821e46
+    dummy_drawable = glx_display->dummy_xwin;
821e46
+
821e46
+  glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
821e46
+                                       dummy_drawable,
821e46
+                                       dummy_drawable,
821e46
+                                       glx_onscreen->swap_wait_context);
821e46
+
821e46
+  g_mutex_lock (&glx_onscreen->swap_wait_mutex);
821e46
+
821e46
+  while (TRUE)
821e46
+    {
821e46
+      gpointer queue_element;
821e46
+      uint32_t vblank_counter;
821e46
+
821e46
+      while (!glx_onscreen->closing_down && glx_onscreen->swap_wait_queue->length == 0)
821e46
+         g_cond_wait (&glx_onscreen->swap_wait_cond, &glx_onscreen->swap_wait_mutex);
821e46
+
821e46
+      if (glx_onscreen->closing_down)
821e46
+         break;
821e46
+
821e46
+      queue_element = g_queue_pop_tail (glx_onscreen->swap_wait_queue);
821e46
+      vblank_counter = GPOINTER_TO_UINT(queue_element);
821e46
+
821e46
+      g_mutex_unlock (&glx_onscreen->swap_wait_mutex);
821e46
+      glx_renderer->glXWaitVideoSync (2,
821e46
+                                      (vblank_counter + 1) % 2,
821e46
+                                      &vblank_counter);
821e46
+      g_mutex_lock (&glx_onscreen->swap_wait_mutex);
821e46
+
821e46
+      if (!glx_onscreen->closing_down)
821e46
+         {
821e46
+           int bytes_written = 0;
821e46
+
821e46
+           union {
821e46
+             char bytes[8];
821e46
+             int64_t presentation_time;
821e46
+           } u;
821e46
+
821e46
+           u.presentation_time = get_monotonic_time_ns ();
821e46
+
821e46
+           while (bytes_written < 8)
821e46
+             {
821e46
+               int res = write (glx_onscreen->swap_wait_pipe[1], u.bytes + bytes_written, 8 - bytes_written);
821e46
+               if (res == -1)
821e46
+                 {
821e46
+                   if (errno != EINTR)
821e46
+                     g_error ("Error writing to swap notification pipe: %s\n",
821e46
+                              g_strerror (errno));
821e46
+                 }
821e46
+               else
821e46
+                 {
821e46
+                   bytes_written += res;
821e46
+                 }
821e46
+             }
821e46
+         }
821e46
+    }
821e46
+
821e46
+  g_mutex_unlock (&glx_onscreen->swap_wait_mutex);
821e46
+
821e46
+  glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy,
821e46
+                                       None,
821e46
+                                       None,
821e46
+                                       NULL);
821e46
+
821e46
+  return NULL;
821e46
+}
821e46
+
821e46
+static int64_t
821e46
+threaded_swap_wait_pipe_prepare (void *user_data)
821e46
+{
821e46
+  return -1;
821e46
+}
821e46
+
821e46
+static void
821e46
+threaded_swap_wait_pipe_dispatch (void *user_data, int revents)
821e46
+{
821e46
+  CoglOnscreen *onscreen = user_data;
821e46
+  CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
821e46
+
821e46
+  CoglFrameInfo *info;
821e46
+
821e46
+  if ((revents & COGL_POLL_FD_EVENT_IN))
821e46
+    {
821e46
+      int bytes_read = 0;
821e46
+
821e46
+      union {
821e46
+         char bytes[8];
821e46
+         int64_t presentation_time;
821e46
+      } u;
821e46
+
821e46
+      while (bytes_read < 8)
821e46
+         {
821e46
+           int res = read (glx_onscreen->swap_wait_pipe[0], u.bytes + bytes_read, 8 - bytes_read);
821e46
+           if (res == -1)
821e46
+             {
821e46
+               if (errno != EINTR)
821e46
+                 g_error ("Error reading from swap notification pipe: %s\n",
821e46
+                          g_strerror (errno));
821e46
+             }
821e46
+           else
821e46
+             {
821e46
+               bytes_read += res;
821e46
+             }
821e46
+         }
821e46
+
821e46
+      set_sync_pending (onscreen);
821e46
+      set_complete_pending (onscreen);
821e46
+
821e46
+      info = g_queue_peek_head (&onscreen->pending_frame_infos);
821e46
+      info->presentation_time = u.presentation_time;
821e46
+    }
821e46
+}
821e46
+
821e46
+static void
821e46
+start_threaded_swap_wait (CoglOnscreen *onscreen,
821e46
+                           uint32_t      vblank_counter)
821e46
+{
821e46
+  CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
821e46
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
821e46
+  CoglContext *context = framebuffer->context;
821e46
+
821e46
+  if (glx_onscreen->swap_wait_thread == NULL)
821e46
+    {
821e46
+      CoglDisplay *display = context->display;
821e46
+      CoglGLXRenderer *glx_renderer = display->renderer->winsys;
821e46
+      CoglGLXDisplay *glx_display = display->winsys;
821e46
+      CoglOnscreenXlib *xlib_onscreen = onscreen->winsys;
821e46
+      CoglXlibRenderer *xlib_renderer =
821e46
+        _cogl_xlib_renderer_get_data (display->renderer);
821e46
+
821e46
+      GLXDrawable drawable =
821e46
+        glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin;
821e46
+      int i;
821e46
+
821e46
+      ensure_ust_type (display->renderer, drawable);
821e46
+      
821e46
+      if ((pipe (glx_onscreen->swap_wait_pipe) == -1))
821e46
+        g_error ("Couldn't create pipe for swap notification: %s\n",
821e46
+                 g_strerror (errno));
821e46
+
821e46
+      for (i = 0; i < 2; i++)
821e46
+	{
821e46
+	  if (fcntl(glx_onscreen->swap_wait_pipe[i], F_SETFD,
821e46
+		    fcntl(glx_onscreen->swap_wait_pipe[i], F_GETFD, 0) | FD_CLOEXEC) == -1)
821e46
+	    g_error ("Couldn't set swap notification pipe CLOEXEC: %s\n",
821e46
+		     g_strerror (errno));
821e46
+	}
821e46
+
821e46
+      _cogl_poll_renderer_add_fd (display->renderer,
821e46
+                                  glx_onscreen->swap_wait_pipe[0],
821e46
+                                  COGL_POLL_FD_EVENT_IN,
821e46
+                                  threaded_swap_wait_pipe_prepare,
821e46
+                                  threaded_swap_wait_pipe_dispatch,
821e46
+                                  onscreen);
821e46
+
821e46
+      glx_onscreen->swap_wait_queue = g_queue_new ();
821e46
+      g_mutex_init (&glx_onscreen->swap_wait_mutex);
821e46
+      g_cond_init (&glx_onscreen->swap_wait_cond);
821e46
+      glx_onscreen->swap_wait_context =
821e46
+         glx_renderer->glXCreateNewContext (xlib_renderer->xdpy,
821e46
+                                            glx_display->fbconfig,
821e46
+                                            GLX_RGBA_TYPE,
821e46
+                                            glx_display->glx_context,
821e46
+                                            True);
821e46
+      glx_onscreen->swap_wait_thread = g_thread_new ("cogl_glx_swap_wait",
821e46
+                                                     threaded_swap_wait,
821e46
+                                                     onscreen);
821e46
+    }
821e46
+
821e46
+  g_mutex_lock (&glx_onscreen->swap_wait_mutex);
821e46
+  g_queue_push_head (glx_onscreen->swap_wait_queue, GUINT_TO_POINTER(vblank_counter));
821e46
+  g_cond_signal (&glx_onscreen->swap_wait_cond);
821e46
+  g_mutex_unlock (&glx_onscreen->swap_wait_mutex);
821e46
+}
821e46
+
821e46
 static void
821e46
 _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
821e46
                                    const int *user_rectangles,
821e46
@@ -2002,19 +2255,38 @@ _cogl_winsys_onscreen_swap_buffers_with_
821e46
 
821e46
   if (framebuffer->config.swap_throttled)
821e46
     {
821e46
-      uint32_t end_frame_vsync_counter = 0;
821e46
-
821e46
       have_counter = glx_display->have_vblank_counter;
821e46
 
821e46
-      /* If the swap_region API is also being used then we need to track
821e46
-       * the vsync counter for each swap request so we can manually
821e46
-       * throttle swap_region requests. */
821e46
-      if (have_counter)
821e46
-        end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context);
821e46
-
821e46
-      if (!glx_renderer->glXSwapInterval)
821e46
+      if (glx_renderer->glXSwapInterval)
821e46
         {
821e46
-          CoglBool can_wait = glx_display->can_vblank_wait;
821e46
+          if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_THREADED_SWAP_WAIT))
821e46
+            {
821e46
+	      /* If we didn't wait for the GPU here, then it's easy to get the case
821e46
+	       * where there is a VBlank between the point where we get the vsync counter
821e46
+	       * and the point where the GPU is ready to actually perform the glXSwapBuffers(),
821e46
+	       * and the swap wait terminates at the first VBlank rather than the one
821e46
+	       * where the swap buffers happens. Calling glFinish() here makes this a
821e46
+	       * rare race since the GPU is already ready to swap when we call glXSwapBuffers().
821e46
+	       * The glFinish() also prevents any serious damage if the rare race happens,
821e46
+	       * since it will wait for the preceding glXSwapBuffers() and prevent us from
821e46
+	       * getting premanently ahead. (For NVIDIA drivers, glFinish() after glXSwapBuffers()
821e46
+	       * waits for the buffer swap to happen.)
821e46
+	       */
821e46
+              _cogl_winsys_wait_for_gpu (onscreen);
821e46
+              start_threaded_swap_wait (onscreen, _cogl_winsys_get_vsync_counter (context));
821e46
+            }
821e46
+        }
821e46
+      else
821e46
+        {
821e46
+          CoglBool can_wait = have_counter || glx_display->can_vblank_wait;
821e46
+
821e46
+          uint32_t end_frame_vsync_counter = 0;
821e46
+
821e46
+          /* If the swap_region API is also being used then we need to track
821e46
+           * the vsync counter for each swap request so we can manually
821e46
+           * throttle swap_region requests. */
821e46
+          if (have_counter)
821e46
+            end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context);
821e46
 
821e46
           /* If we are going to wait for VBLANK manually, we not only
821e46
            * need to flush out pending drawing to the GPU before we
821e46
diff -up cogl-1.18.2/examples/cogl-crate.c.threaded-sync-events cogl-1.18.2/examples/cogl-crate.c
821e46
--- cogl-1.18.2/examples/cogl-crate.c.threaded-sync-events	2016-06-30 15:44:39.804351929 -0400
821e46
+++ cogl-1.18.2/examples/cogl-crate.c	2016-06-30 15:44:39.824352252 -0400
821e46
@@ -1,5 +1,6 @@
821e46
 #include <cogl/cogl.h>
821e46
 #include <cogl-pango/cogl-pango.h>
821e46
+#include <X11/Xlib.h>
821e46
 
821e46
 /* The state for this example... */
821e46
 typedef struct _Data
821e46
@@ -153,6 +154,8 @@ main (int argc, char **argv)
821e46
   float fovy, aspect, z_near, z_2d, z_far;
821e46
   CoglDepthState depth_state;
821e46
 
821e46
+  XInitThreads ();
821e46
+
821e46
   ctx = cogl_context_new (NULL, &error);
821e46
   if (!ctx) {
821e46
       fprintf (stderr, "Failed to create context: %s\n", error->message);