kmssink: wait for page flip or vblank
authorVíctor Manuel Jáquez Leal <vjaquez@igalia.com>
Sat, 20 Feb 2016 22:13:54 +0000 (23:13 +0100)
committerVíctor Manuel Jáquez Leal <vjaquez@igalia.com>
Mon, 11 Apr 2016 17:57:48 +0000 (19:57 +0200)
This patch requests for drmModePageFlip() for the used CRTC, if the kernel
module suppports async page flip. If it does not, the element requests for a
vblank event. A GstPoll waits for the event to happen.

https://bugzilla.gnome.org/show_bug.cgi?id=761059

sys/kms/gstkmssink.c
sys/kms/gstkmssink.h

index 66777e3..ec75e24 100644 (file)
@@ -261,6 +261,7 @@ get_drm_caps (GstKMSSink * self)
   gint ret;
   guint64 has_dumb_buffer;
   guint64 has_prime;
+  guint64 has_async_page_flip;
 
   has_dumb_buffer = 0;
   ret = drmGetCap (self->fd, DRM_CAP_DUMB_BUFFER, &has_dumb_buffer);
@@ -278,8 +279,16 @@ get_drm_caps (GstKMSSink * self)
   else
     self->has_prime_import = (gboolean) (has_prime & DRM_PRIME_CAP_IMPORT);
 
-  GST_INFO_OBJECT (self, "prime import (%s)",
-      self->has_prime_import ? "✓" : "✗");
+  has_async_page_flip = 0;
+  ret = drmGetCap (self->fd, DRM_CAP_ASYNC_PAGE_FLIP, &has_async_page_flip);
+  if (ret)
+    GST_WARNING_OBJECT (self, "could not get async page flip capability");
+  else
+    self->has_async_page_flip = (gboolean) has_async_page_flip;
+
+  GST_INFO_OBJECT (self, "prime import (%s) / async page flip (%s)",
+      self->has_prime_import ? "✓" : "✗",
+      self->has_async_page_flip ? "✓" : "✗");
 
   return TRUE;
 }
@@ -392,6 +401,7 @@ gst_kms_sink_start (GstBaseSink * bsink)
 
   self->hdisplay = crtc->mode.hdisplay;
   self->vdisplay = crtc->mode.vdisplay;
+  self->buffer_id = crtc->buffer_id;
 
   self->mm_width = conn->mmWidth;
   self->mm_height = conn->mmHeight;
@@ -399,6 +409,10 @@ gst_kms_sink_start (GstBaseSink * bsink)
   GST_INFO_OBJECT (self, "display size: pixels = %dx%d / millimeters = %dx%d",
       self->hdisplay, self->vdisplay, self->mm_width, self->mm_height);
 
+  self->pollfd.fd = self->fd;
+  gst_poll_add_fd (self->poll, &self->pollfd);
+  gst_poll_fd_ctl_read (self->poll, &self->pollfd, TRUE);
+
   ret = TRUE;
 
 bail:
@@ -472,6 +486,10 @@ gst_kms_sink_stop (GstBaseSink * bsink)
   gst_object_replace ((GstObject **) & self->pool, NULL);
   gst_object_replace ((GstObject **) & self->allocator, NULL);
 
+  gst_poll_remove_fd (self->poll, &self->pollfd);
+  gst_poll_restart (self->poll);
+  gst_poll_fd_init (&self->pollfd);
+
   if (self->fd >= 0) {
     drmClose (self->fd);
     self->fd = -1;
@@ -748,6 +766,78 @@ gst_kms_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
   }
 }
 
+static void
+sync_handler (gint fd, guint frame, guint sec, guint usec, gpointer data)
+{
+  gboolean *waiting;
+
+  waiting = data;
+  *waiting = FALSE;
+}
+
+static gboolean
+gst_kms_sink_sync (GstKMSSink * self)
+{
+  gint ret;
+  gboolean waiting;
+  drmEventContext evctxt = {
+    .version = DRM_EVENT_CONTEXT_VERSION,
+    .page_flip_handler = sync_handler,
+    .vblank_handler = sync_handler,
+  };
+  drmVBlank vbl = {
+    .request = {
+          .type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
+          .sequence = 1,
+          .signal = (gulong) & waiting,
+        },
+  };
+
+  waiting = TRUE;
+  if (!self->has_async_page_flip) {
+    ret = drmWaitVBlank (self->fd, &vbl);
+    if (ret)
+      goto vblank_failed;
+  } else {
+    ret = drmModePageFlip (self->fd, self->crtc_id, self->buffer_id,
+        DRM_MODE_PAGE_FLIP_EVENT, &waiting);
+    if (ret)
+      goto pageflip_failed;
+  }
+
+  while (waiting) {
+    do {
+      ret = gst_poll_wait (self->poll, 3 * GST_SECOND);
+    } while (ret == -1 && (errno == EAGAIN || errno == EINTR));
+
+    ret = drmHandleEvent (self->fd, &evctxt);
+    if (ret)
+      goto event_failed;
+  }
+
+  return TRUE;
+
+  /* ERRORS */
+vblank_failed:
+  {
+    GST_WARNING_OBJECT (self, "drmWaitVBlank failed: %s (%d)", strerror (-ret),
+        ret);
+    return FALSE;
+  }
+pageflip_failed:
+  {
+    GST_WARNING_OBJECT (self, "drmModePageFlip failed: %s (%d)",
+        strerror (-ret), ret);
+    return FALSE;
+  }
+event_failed:
+  {
+    GST_ERROR_OBJECT (self, "drmHandleEvent failed: %s (%d)", strerror (-ret),
+        ret);
+    return FALSE;
+  }
+}
+
 static GstMemory *
 get_cached_kmsmem (GstMemory * mem)
 {
@@ -961,9 +1051,12 @@ gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
   GstVideoRectangle src = { 0, };
   GstVideoRectangle dst = { 0, };
   GstVideoRectangle result;
+  GstFlowReturn res;
 
   self = GST_KMS_SINK (vsink);
 
+  res = GST_FLOW_ERROR;
+
   buffer = gst_kms_sink_get_input_buffer (self, buf);
   if (!buffer)
     return GST_FLOW_ERROR;
@@ -999,19 +1092,24 @@ gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
       result.x, result.y, result.w, result.h,
       /* source/cropping coordinates are given in Q16 */
       src.x << 16, src.y << 16, src.w << 16, src.h << 16);
-
-  gst_buffer_unref (buffer);
-
   if (ret)
     goto set_plane_failed;
 
-  return GST_FLOW_OK;
+  /* Wait for the previous frame to complete redraw */
+  if (!gst_kms_sink_sync (self))
+    goto bail;
+
+  res = GST_FLOW_OK;
+
+bail:
+  gst_buffer_unref (buffer);
+  return res;
 
   /* ERRORS */
 buffer_invalid:
   {
     GST_ERROR_OBJECT (self, "invalid buffer: it doesn't have a fb id");
-    return GST_FLOW_ERROR;
+    goto bail;
   }
 set_plane_failed:
   {
@@ -1021,7 +1119,7 @@ set_plane_failed:
         dst.h);
     GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
         (NULL), ("drmModeSetPlane failed: %s (%d)", strerror (-ret), ret));
-    return GST_FLOW_ERROR;
+    goto bail;
   }
 }
 
@@ -1074,6 +1172,7 @@ gst_kms_sink_finalize (GObject * object)
 
   sink = GST_KMS_SINK (object);
   g_clear_pointer (&sink->devname, g_free);
+  gst_poll_free (sink->poll);
 }
 
 static void
@@ -1081,6 +1180,8 @@ gst_kms_sink_init (GstKMSSink * sink)
 {
   sink->fd = -1;
   sink->conn_id = -1;
+  gst_poll_fd_init (&sink->pollfd);
+  sink->poll = gst_poll_new (TRUE);
   gst_video_info_init (&sink->vinfo);
 }
 
index 90700b4..1f9c0e1 100644 (file)
@@ -53,10 +53,13 @@ struct _GstKMSSink {
   gint crtc_id;
   gint plane_id;
 
+  /* crtc data */
   guint16 hdisplay, vdisplay;
+  guint32 buffer_id;
 
   /* capabilities */
   gboolean has_prime_import;
+  gboolean has_async_page_flip;
 
   GstVideoInfo vinfo;
   GstCaps *allowed_caps;
@@ -66,6 +69,9 @@ struct _GstKMSSink {
   gchar *devname;
 
   guint32 mm_width, mm_height;
+
+  GstPoll *poll;
+  GstPollFD pollfd;
 };
 
 struct _GstKMSSinkClass {