[cogl/cogl-1.16] kms: add public API to override the default configuration of outputs



commit bd3dce4af1b7b10f85dcf83d3367eb7819c1e287
Author: Giovanni Campagna <gcampagn redhat com>
Date:   Tue Jul 30 17:20:01 2013 +0200

    kms: add public API to override the default configuration of outputs
    
    Add API to allow complex applications using the KMS backend
    to go almost straight to direct configuration (which is not possible
    because Cogl needs to be in charge of buffers and FB objects).
    
    https://bugzilla.gnome.org/show_bug.cgi?id=705837
    
    Reviewed-by: Robert Bragg <robert linux intel com>
    
    (cherry picked from commit 52fb8e1c33d8c83c731c05cee767928fdd5991d7)

 cogl/cogl-kms-display.h           |   39 ++++++
 cogl/winsys/cogl-winsys-egl-kms.c |  232 +++++++++++++++++++++++++++++++-----
 2 files changed, 238 insertions(+), 33 deletions(-)
---
diff --git a/cogl/cogl-kms-display.h b/cogl/cogl-kms-display.h
index d8bfd9e..cfa33ea 100644
--- a/cogl/cogl-kms-display.h
+++ b/cogl/cogl-kms-display.h
@@ -32,6 +32,8 @@
 #include <cogl/cogl-types.h>
 #include <cogl/cogl-display.h>
 
+#include <xf86drmMode.h>
+
 COGL_BEGIN_DECLS
 
 /**
@@ -54,5 +56,42 @@ COGL_BEGIN_DECLS
 void
 cogl_kms_display_queue_modes_reset (CoglDisplay *display);
 
+typedef struct {
+  uint32_t id;
+  uint32_t x, y;
+  drmModeModeInfo mode;
+
+  uint32_t *connectors;
+  uint32_t  count;
+} CoglKmsCrtc;
+
+/**
+ * cogl_kms_display_set_layout:
+ * @onscreen: a #CoglDisplay
+ * @width: the framebuffer width
+ * @height: the framebuffer height
+ * @crtcs: the array of #CoglKmsCrtc structure with the desired CRTC layout
+ *
+ * Configures @display to use a framebuffer sized @width x @height, covering
+ * the CRTCS in @crtcs.
+ * @width and @height must be within the driver framebuffer limits, and @crtcs
+ * must be valid KMS API IDs.
+ *
+ * Calling this function overrides the automatic mode setting done by Cogl,
+ * and for this reason must be called before the first call to cogl_onscreen_swap_buffers().
+ *
+ * If you want to restore the default behaviour, you can call this function
+ * with @width and @height set to -1.
+ *
+ * Stability: unstable
+ */
+CoglBool
+cogl_kms_display_set_layout (CoglDisplay *display,
+                             int width,
+                             int height,
+                             CoglKmsCrtc **crtcs,
+                             int n_crtcs,
+                             CoglError **error);
+
 COGL_END_DECLS
 #endif /* __COGL_KMS_DISPLAY_H__ */
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index 1c9cafb..a6cf3c2 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -79,9 +79,13 @@ typedef struct _CoglOutputKMS
 typedef struct _CoglDisplayKMS
 {
   GList *outputs;
+  GList *crtcs;
+
   int width, height;
   CoglBool pending_set_crtc;
   struct gbm_surface *dummy_gbm_surface;
+
+  CoglOnscreen *onscreen;
 } CoglDisplayKMS;
 
 typedef struct _CoglFlipKMS
@@ -393,13 +397,13 @@ find_mirror_modes (drmModeModeInfo *modes0,
 
 static drmModeModeInfo builtin_1024x768 =
 {
-       63500,                  /* clock */
-       1024, 1072, 1176, 1328, 0,
-       768, 771, 775, 798, 0,
-       59920,
-       DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
-       0,
-       "1024x768"
+        63500,                        /* clock */
+        1024, 1072, 1176, 1328, 0,
+        768, 771, 775, 798, 0,
+        59920,
+        DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
+        0,
+        "1024x768"
 };
 
 static CoglBool
@@ -520,19 +524,71 @@ setup_crtc_modes (CoglDisplay *display, int fb_id)
   CoglRendererKMS *kms_renderer = egl_renderer->platform;
   GList *l;
 
-  for (l = kms_display->outputs; l; l = l->next)
+  for (l = kms_display->crtcs; l; l = l->next)
     {
-      CoglOutputKMS *output = l->data;
+      CoglKmsCrtc *crtc = l->data;
+
       int ret = drmModeSetCrtc (kms_renderer->fd,
-                                output->encoder->crtc_id,
-                                fb_id, 0, 0,
-                                &output->connector->connector_id, 1,
-                                &output->mode);
+                                crtc->id,
+                                fb_id, crtc->x, crtc->y,
+                                crtc->connectors, crtc->count,
+                                crtc->count ? &crtc->mode : NULL);
+      if (ret)
+        g_warning ("Failed to set crtc mode %s: %m", crtc->mode.name);
+    }
+}
+
+static void
+flip_all_crtcs (CoglDisplay *display, CoglFlipKMS *flip, int fb_id)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRendererEGL *egl_renderer = display->renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *l;
+
+  for (l = kms_display->crtcs; l; l = l->next)
+    {
+      CoglKmsCrtc *crtc = l->data;
+      int ret;
+
+      if (crtc->count == 0)
+        continue;
+
+      ret = drmModePageFlip (kms_renderer->fd,
+                             crtc->id, fb_id,
+                             DRM_MODE_PAGE_FLIP_EVENT, flip);
+
       if (ret)
-        g_warning ("Failed to set crtc mode %s: %m", output->mode.name);
+        {
+          g_warning ("Failed to flip: %m");
+          continue;
+        }
+
+      flip->pending++;
     }
 }
 
+static void
+crtc_free (CoglKmsCrtc *crtc)
+{
+  g_free (crtc->connectors);
+  g_slice_free (CoglKmsCrtc, crtc);
+}
+
+static CoglKmsCrtc *
+crtc_copy (CoglKmsCrtc *from)
+{
+  CoglKmsCrtc *new;
+
+  new = g_slice_new (CoglKmsCrtc);
+
+  *new = *from;
+  new->connectors = g_memdup (from->connectors, from->count * sizeof(uint32_t));
+
+  return new;
+}
+
 static CoglBool
 _cogl_winsys_egl_display_setup (CoglDisplay *display,
                                 CoglError **error)
@@ -544,6 +600,7 @@ _cogl_winsys_egl_display_setup (CoglDisplay *display,
   drmModeRes *resources;
   CoglOutputKMS *output0, *output1;
   CoglBool mirror;
+  CoglKmsCrtc *crtc0, *crtc1;
 
   kms_display = g_slice_new0 (CoglDisplayKMS);
   egl_display->platform = kms_display;
@@ -598,7 +655,33 @@ _cogl_winsys_egl_display_setup (CoglDisplay *display,
         }
     }
   else
-    output0->mode = output0->modes[0];
+    {
+      output0->mode = output0->modes[0];
+      output1 = NULL;
+    }
+
+  crtc0 = g_slice_new (CoglKmsCrtc);
+  crtc0->id = output0->encoder->crtc_id;
+  crtc0->x = 0;
+  crtc0->y = 0;
+  crtc0->mode = output0->mode;
+  crtc0->connectors = g_new (uint32_t, 1);
+  crtc0->connectors[0] = output0->connector->connector_id;
+  crtc0->count = 1;
+  kms_display->crtcs = g_list_prepend (kms_display->crtcs, crtc0);
+
+  if (output1)
+    {
+      crtc1 = g_slice_new (CoglKmsCrtc);
+      crtc1->id = output1->encoder->crtc_id;
+      crtc1->x = 0;
+      crtc1->y = 0;
+      crtc1->mode = output1->mode;
+      crtc1->connectors = g_new (uint32_t, 1);
+      crtc1->connectors[0] = output1->connector->connector_id;
+      crtc1->count = 1;
+      kms_display->crtcs = g_list_prepend (kms_display->crtcs, crtc1);
+    }
 
   kms_display->width = output0->mode.hdisplay;
   kms_display->height = output0->mode.vdisplay;
@@ -654,6 +737,8 @@ _cogl_winsys_egl_display_destroy (CoglDisplay *display)
   g_list_free (kms_display->outputs);
   kms_display->outputs = NULL;
 
+  g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free);
+
   g_slice_free (CoglDisplayKMS, egl_display->platform);
 }
 
@@ -742,7 +827,6 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
   CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
   uint32_t handle, stride;
   CoglFlipKMS *flip;
-  GList *l;
 
   /* If we already have a pending swap then block until it completes */
   while (kms_onscreen->next_fb_id != 0)
@@ -781,7 +865,7 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
     }
 
   /* If this is the first framebuffer to be presented then we now setup the
-   * crtc modes... */
+   * crtc modes, else we flip from the previous buffer */
   if (kms_display->pending_set_crtc)
     {
       setup_crtc_modes (context->display, kms_onscreen->next_fb_id);
@@ -791,22 +875,7 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
   flip = g_slice_new0 (CoglFlipKMS);
   flip->onscreen = onscreen;
 
-  for (l = kms_display->outputs; l; l = l->next)
-    {
-      CoglOutputKMS *output = l->data;
-
-      if (drmModePageFlip (kms_renderer->fd,
-                           output->encoder->crtc_id,
-                           kms_onscreen->next_fb_id,
-                           DRM_MODE_PAGE_FLIP_EVENT,
-                           flip))
-        {
-          g_warning ("Failed to flip: %m");
-          continue;
-        }
-
-      flip->pending++;
-    }
+  flip_all_crtcs (context->display, flip, kms_onscreen->next_fb_id);
 
   if (flip->pending == 0)
     {
@@ -859,6 +928,16 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
 
   _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
 
+  if (kms_display->onscreen)
+    {
+      _cogl_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                       "Cannot have multiple onscreens in the KMS platform");
+      return FALSE;
+    }
+
+  kms_display->onscreen = onscreen;
+
   onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
   egl_onscreen = onscreen->winsys;
 
@@ -906,6 +985,9 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
 {
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
   CoglContext *context = framebuffer->context;
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
   CoglRenderer *renderer = context->display->renderer;
   CoglRendererEGL *egl_renderer = renderer->winsys;
   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
@@ -915,6 +997,8 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
   if (egl_onscreen == NULL)
     return;
 
+  kms_display->onscreen = NULL;
+
   kms_onscreen = egl_onscreen->platform;
 
   /* flip state takes a reference on the onscreen so there should
@@ -1009,3 +1093,85 @@ cogl_kms_display_queue_modes_reset (CoglDisplay *display)
       kms_display->pending_set_crtc = TRUE;
     }
 }
+
+CoglBool
+cogl_kms_display_set_layout (CoglDisplay *display,
+                             int width,
+                             int height,
+                             CoglKmsCrtc **crtcs,
+                             int n_crtcs,
+                             CoglError **error)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *crtc_list;
+  int i;
+
+  if ((width != kms_display->width ||
+       height != kms_display->height) &&
+      kms_display->onscreen)
+    {
+      CoglOnscreenEGL *egl_onscreen = kms_display->onscreen->winsys;
+      CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+      struct gbm_surface *new_surface;
+      EGLSurface new_egl_surface;
+
+      /* Need to drop the GBM surface and create a new one */
+
+      new_surface = gbm_surface_create (kms_renderer->gbm,
+                                        width, height,
+                                        GBM_BO_FORMAT_XRGB8888,
+                                        GBM_BO_USE_SCANOUT |
+                                        GBM_BO_USE_RENDERING);
+
+      if (!new_surface)
+        {
+          _cogl_set_error (error, COGL_WINSYS_ERROR,
+                           COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                           "Failed to allocate new surface");
+          return FALSE;
+        }
+
+      new_egl_surface =
+        eglCreateWindowSurface (egl_renderer->edpy,
+                                egl_display->egl_config,
+                                (NativeWindowType) new_surface,
+                                NULL);
+      if (new_egl_surface == EGL_NO_SURFACE)
+        {
+          _cogl_set_error (error, COGL_WINSYS_ERROR,
+                           COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                           "Failed to allocate new surface");
+          gbm_surface_destroy (new_surface);
+          return FALSE;
+        }
+
+      eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface);
+      gbm_surface_destroy (kms_onscreen->surface);
+
+      kms_onscreen->surface = new_surface;
+      egl_onscreen->egl_surface = new_egl_surface;
+
+      _cogl_framebuffer_winsys_update_size (COGL_FRAMEBUFFER (kms_display->onscreen), width, height);
+    }
+
+  kms_display->width = width;
+  kms_display->height = height;
+
+  g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free);
+
+  crtc_list = NULL;
+  for (i = 0; i < n_crtcs; i++)
+    {
+      crtc_list = g_list_prepend (crtc_list, crtc_copy (crtcs[i]));
+    }
+  crtc_list = g_list_reverse (crtc_list);
+  kms_display->crtcs = crtc_list;
+
+  kms_display->pending_set_crtc = TRUE;
+
+  return TRUE;
+}


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