[mutter] backends/native: Get rendering and swap timings during scanout



commit 1116b14f38ca3dfae93fcb5bf9fd55aa1e4e04c3
Author: Ivan Molodetskikh <yalterz gmail com>
Date:   Wed Jan 6 15:42:33 2021 +0300

    backends/native: Get rendering and swap timings during scanout
    
    Scanout doesn't go through the usual path of compositing and doing
    eglSwapBuffers, therefore it doesn't hit the timestamp query placed in
    that path. Instead, get the timings by binding the scanout buffer to an
    FBO and doing a timestamp query on the FBO.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1762>

 src/backends/native/meta-drm-buffer-gbm.c     | 117 ++++++++++++++++++++++++++
 src/backends/native/meta-drm-buffer-private.h |   4 +
 src/backends/native/meta-drm-buffer.c         |  21 +++++
 src/backends/native/meta-drm-buffer.h         |   8 ++
 src/backends/native/meta-onscreen-native.c    |  29 +++++++
 5 files changed, 179 insertions(+)
---
diff --git a/src/backends/native/meta-drm-buffer-gbm.c b/src/backends/native/meta-drm-buffer-gbm.c
index 5273cb520f..f011afaca1 100644
--- a/src/backends/native/meta-drm-buffer-gbm.c
+++ b/src/backends/native/meta-drm-buffer-gbm.c
@@ -192,6 +192,122 @@ meta_drm_buffer_gbm_new_take (MetaDeviceFile  *device_file,
   return buffer_gbm;
 }
 
+static gboolean
+meta_drm_buffer_gbm_fill_timings (MetaDrmBuffer  *buffer,
+                                  CoglFrameInfo  *info,
+                                  GError        **error)
+{
+  MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (buffer);
+  MetaBackend *backend = meta_get_backend ();
+  MetaEgl *egl = meta_backend_get_egl (backend);
+  ClutterBackend *clutter_backend =
+    meta_backend_get_clutter_backend (backend);
+  CoglContext *cogl_context =
+    clutter_backend_get_cogl_context (clutter_backend);
+  CoglDisplay *cogl_display = cogl_context->display;
+  CoglRenderer *cogl_renderer = cogl_display->renderer;
+  CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys;
+  EGLDisplay egl_display = cogl_renderer_egl->edpy;
+  EGLImageKHR egl_image;
+  CoglPixelFormat cogl_format;
+  CoglEglImageFlags flags;
+  g_autoptr (CoglOffscreen) cogl_fbo = NULL;
+  CoglTexture2D *cogl_tex;
+  uint32_t n_planes;
+  uint64_t *modifiers;
+  uint32_t *strides;
+  uint32_t *offsets;
+  uint32_t width;
+  uint32_t height;
+  uint32_t drm_format;
+  int *fds;
+  gboolean result;
+  int dmabuf_fd = -1;
+  uint32_t i;
+
+  dmabuf_fd = gbm_bo_get_fd (buffer_gbm->bo);
+  if (dmabuf_fd == -1)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to export buffer's DMA fd: %s",
+                   g_strerror (errno));
+      return FALSE;
+    }
+
+  drm_format = gbm_bo_get_format (buffer_gbm->bo);
+  result = meta_cogl_pixel_format_from_drm_format (drm_format,
+                                                   &cogl_format,
+                                                   NULL);
+  g_assert (result);
+
+  width = gbm_bo_get_width (buffer_gbm->bo);
+  height = gbm_bo_get_height (buffer_gbm->bo);
+  n_planes = gbm_bo_get_plane_count (buffer_gbm->bo);
+  fds = g_alloca (sizeof (int) * n_planes);
+  strides = g_alloca (sizeof (uint32_t) * n_planes);
+  offsets = g_alloca (sizeof (uint32_t) * n_planes);
+  modifiers = g_alloca (sizeof (uint64_t) * n_planes);
+
+  for (i = 0; i < n_planes; i++)
+    {
+      fds[i] = dmabuf_fd;
+      strides[i] = gbm_bo_get_stride_for_plane (buffer_gbm->bo, i);
+      offsets[i] = gbm_bo_get_offset (buffer_gbm->bo, i);
+      modifiers[i] = gbm_bo_get_modifier (buffer_gbm->bo);
+    }
+
+  egl_image = meta_egl_create_dmabuf_image (egl,
+                                            egl_display,
+                                            width,
+                                            height,
+                                            drm_format,
+                                            n_planes,
+                                            fds,
+                                            strides,
+                                            offsets,
+                                            modifiers,
+                                            error);
+  if (egl_image == EGL_NO_IMAGE_KHR)
+    goto out;
+
+  flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA;
+  cogl_tex = cogl_egl_texture_2d_new_from_image (cogl_context,
+                                                 width,
+                                                 height,
+                                                 cogl_format,
+                                                 egl_image,
+                                                 flags,
+                                                 error);
+
+  meta_egl_destroy_image (egl, egl_display, egl_image, NULL);
+
+  if (!cogl_tex)
+    goto out;
+
+  cogl_fbo = cogl_offscreen_new_with_texture (COGL_TEXTURE (cogl_tex));
+  cogl_object_unref (cogl_tex);
+
+  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_GET_GPU_TIME))
+    {
+      info->gpu_time_before_buffer_swap_ns =
+        cogl_context_get_gpu_time_ns (cogl_context);
+    }
+
+  info->cpu_time_before_buffer_swap_us = g_get_monotonic_time ();
+
+  /* Set up a timestamp query for when all rendering will be finished. */
+  if (cogl_has_feature (cogl_context, COGL_FEATURE_ID_TIMESTAMP_QUERY))
+    {
+      info->timestamp_query =
+        cogl_framebuffer_create_timestamp_query (COGL_FRAMEBUFFER (cogl_fbo));
+    }
+
+out:
+  close (dmabuf_fd);
+
+  return TRUE;
+}
+
 static gboolean
 meta_drm_buffer_gbm_blit_to_framebuffer (CoglScanout      *scanout,
                                          CoglFramebuffer  *framebuffer,
@@ -354,4 +470,5 @@ meta_drm_buffer_gbm_class_init (MetaDrmBufferGbmClass *klass)
   buffer_class->get_height = meta_drm_buffer_gbm_get_height;
   buffer_class->get_stride = meta_drm_buffer_gbm_get_stride;
   buffer_class->get_format = meta_drm_buffer_gbm_get_format;
+  buffer_class->fill_timings = meta_drm_buffer_gbm_fill_timings;
 }
diff --git a/src/backends/native/meta-drm-buffer-private.h b/src/backends/native/meta-drm-buffer-private.h
index 859d21fb93..a54ce7c31e 100644
--- a/src/backends/native/meta-drm-buffer-private.h
+++ b/src/backends/native/meta-drm-buffer-private.h
@@ -43,6 +43,10 @@ struct _MetaDrmBufferClass
   int (* get_height) (MetaDrmBuffer *buffer);
   int (* get_stride) (MetaDrmBuffer *buffer);
   uint32_t (* get_format) (MetaDrmBuffer *buffer);
+
+  gboolean (* fill_timings) (MetaDrmBuffer  *buffer,
+                             CoglFrameInfo  *info,
+                             GError        **error);
 };
 
 MetaDeviceFile * meta_drm_buffer_get_device_file (MetaDrmBuffer *buffer);
diff --git a/src/backends/native/meta-drm-buffer.c b/src/backends/native/meta-drm-buffer.c
index a1a24f0834..1da6220372 100644
--- a/src/backends/native/meta-drm-buffer.c
+++ b/src/backends/native/meta-drm-buffer.c
@@ -185,6 +185,27 @@ meta_drm_buffer_get_format (MetaDrmBuffer *buffer)
   return META_DRM_BUFFER_GET_CLASS (buffer)->get_format (buffer);
 }
 
+gboolean
+meta_drm_buffer_supports_fill_timings (MetaDrmBuffer *buffer)
+{
+  return META_DRM_BUFFER_GET_CLASS (buffer)->fill_timings != NULL;
+}
+
+gboolean
+meta_drm_buffer_fill_timings (MetaDrmBuffer  *buffer,
+                              CoglFrameInfo  *info,
+                              GError        **error)
+{
+  if (!meta_drm_buffer_supports_fill_timings (buffer))
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                           "Buffer doesn't support filling timing info");
+      return FALSE;
+    }
+
+  return META_DRM_BUFFER_GET_CLASS (buffer)->fill_timings (buffer, info, error);
+}
+
 static void
 meta_drm_buffer_get_property (GObject    *object,
                               guint       prop_id,
diff --git a/src/backends/native/meta-drm-buffer.h b/src/backends/native/meta-drm-buffer.h
index 1647d399e7..d321355911 100644
--- a/src/backends/native/meta-drm-buffer.h
+++ b/src/backends/native/meta-drm-buffer.h
@@ -26,6 +26,8 @@
 #include <glib-object.h>
 #include <stdint.h>
 
+#include "cogl/cogl.h"
+
 #define META_TYPE_DRM_BUFFER (meta_drm_buffer_get_type ())
 G_DECLARE_DERIVABLE_TYPE (MetaDrmBuffer,
                           meta_drm_buffer,
@@ -42,4 +44,10 @@ int meta_drm_buffer_get_stride (MetaDrmBuffer *buffer);
 
 uint32_t meta_drm_buffer_get_format (MetaDrmBuffer *buffer);
 
+gboolean meta_drm_buffer_supports_fill_timings (MetaDrmBuffer *buffer);
+
+gboolean meta_drm_buffer_fill_timings (MetaDrmBuffer  *buffer,
+                                       CoglFrameInfo  *info,
+                                       GError        **error);
+
 #endif /* META_DRM_BUFFER_H */
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index e37b86a08f..17dcac03f5 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -1222,6 +1222,8 @@ meta_onscreen_native_direct_scanout (CoglOnscreen   *onscreen,
     meta_backend_get_monitor_manager (backend);
   MetaPowerSave power_save_mode;
   ClutterFrame *frame = user_data;
+  MetaDrmBuffer *scanout_buffer;
+  GError *fill_timings_error = NULL;
   MetaKmsCrtc *kms_crtc;
   MetaKmsDevice *kms_device;
   MetaKmsUpdateFlag flags;
@@ -1255,6 +1257,33 @@ meta_onscreen_native_direct_scanout (CoglOnscreen   *onscreen,
 
   g_set_object (&onscreen_native->gbm.next_fb, META_DRM_BUFFER (scanout));
 
+  /* Try to get a measurement of GPU rendering time on the scanout buffer.
+   *
+   * The successful operation here adds ~0.4 ms to a ~0.1 ms total frame clock
+   * dispatch duration when displaying an unredirected client, thus
+   * unfortunately bringing it more in line with duration of the regular
+   * non-unredirected frame clock dispatch. However, measuring GPU rendering
+   * time is important for computing accurate max render time without
+   * underestimating. Also this operation should be optimizable by caching
+   * EGLImage for each buffer instead of re-creating it every time it's needed.
+   * This should also help all other cases which convert the buffer to a
+   * EGLImage.
+   */
+  if (META_IS_DRM_BUFFER (scanout))
+    {
+      scanout_buffer = META_DRM_BUFFER (scanout);
+      if (meta_drm_buffer_supports_fill_timings (scanout_buffer))
+        {
+          if (!meta_drm_buffer_fill_timings (scanout_buffer, frame_info,
+                                             &fill_timings_error))
+            {
+              g_warning ("Failed to fill timings for a scanout buffer: %s",
+                         fill_timings_error->message);
+              g_error_free (fill_timings_error);
+            }
+        }
+    }
+
   ensure_crtc_modes (onscreen);
   meta_onscreen_native_flip_crtc (onscreen,
                                   onscreen_native->view,


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