[mutter] native: Add headless mode using surfaceless EGL context



commit 634c948fc62a9a3b6604354d22cbaf28515b42eb
Author: Jonas Ã…dahl <jadahl gmail com>
Date:   Tue Feb 9 14:32:55 2021 +0100

    native: Add headless mode using surfaceless EGL context
    
    This eliminates the need for any render node or device nodes, thus can
    be used without any graphics hardware available at all, or with a
    graphics driver without any render node available.
    
    The surfaceless mode currently requires EGL_KHR_no_config_context to
    configure the initial EGL display.
    
    This also means we can enable the native backend tests in CI, as it
    should work without any additional privileges.
    
    Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1698>

 src/backends/native/meta-backend-native.c  |  12 ++-
 src/backends/native/meta-onscreen-native.c |  21 +++++
 src/backends/native/meta-renderer-native.c | 123 +++++++++++++++++++++++------
 src/backends/native/meta-renderer-native.h |   1 +
 src/backends/native/meta-udev.c            |  13 +--
 5 files changed, 130 insertions(+), 40 deletions(-)
---
diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c
index 65000cb529..f0c2fadaa6 100644
--- a/src/backends/native/meta-backend-native.c
+++ b/src/backends/native/meta-backend-native.c
@@ -144,6 +144,12 @@ maybe_disable_screen_cast_dma_bufs (MetaBackendNative *native)
   };
 
   primary_gpu = meta_renderer_native_get_primary_gpu (renderer_native);
+  if (!primary_gpu)
+    {
+      g_message ("Disabling DMA buffer screen sharing (surfaceless)");
+      goto disable_dma_bufs;
+    }
+
   kms_device = meta_gpu_kms_get_kms_device (primary_gpu);
   driver_name = meta_kms_device_get_driver_name (kms_device);
 
@@ -157,6 +163,7 @@ maybe_disable_screen_cast_dma_bufs (MetaBackendNative *native)
   g_message ("Disabling DMA buffer screen sharing for driver '%s'.",
              driver_name);
 
+disable_dma_bufs:
   meta_screen_cast_disable_dma_bufs (screen_cast);
 }
 #endif /* HAVE_REMOTE_DESKTOP */
@@ -473,7 +480,7 @@ init_gpus (MetaBackendNative  *native,
   GList *l;
 
   devices = meta_udev_list_drm_devices (udev, error);
-  if (!devices)
+  if (*error)
     return FALSE;
 
   for (l = devices; l; l = l->next)
@@ -498,7 +505,8 @@ init_gpus (MetaBackendNative  *native,
 
   g_list_free_full (devices, g_object_unref);
 
-  if (g_list_length (meta_backend_get_gpus (backend)) == 0)
+  if (!native->is_headless &&
+      g_list_length (meta_backend_get_gpus (backend)) == 0)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
                    "No GPUs found");
diff --git a/src/backends/native/meta-onscreen-native.c b/src/backends/native/meta-onscreen-native.c
index 4290eeeca8..7c0fb51333 100644
--- a/src/backends/native/meta-onscreen-native.c
+++ b/src/backends/native/meta-onscreen-native.c
@@ -240,6 +240,9 @@ notify_view_crtc_presented (MetaRendererView *view,
     case META_RENDERER_NATIVE_MODE_GBM:
       meta_onscreen_native_swap_drm_fb (onscreen);
       break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      g_assert_not_reached ();
+      break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       break;
@@ -473,6 +476,9 @@ meta_onscreen_native_flip_crtc (CoglOnscreen                *onscreen,
 
       meta_crtc_kms_assign_primary_plane (crtc_kms, buffer, kms_update);
 
+      break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      g_assert_not_reached ();
       break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
@@ -511,6 +517,9 @@ meta_onscreen_native_set_crtc_mode (CoglOnscreen              *onscreen,
     {
     case META_RENDERER_NATIVE_MODE_GBM:
       break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      g_assert_not_reached ();
+      break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       {
@@ -1050,6 +1059,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen  *onscreen,
 
       onscreen_native->gbm.next_fb = META_DRM_BUFFER (buffer_gbm);
 
+      break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      g_assert_not_reached ();
       break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
@@ -1117,6 +1129,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen  *onscreen,
           return;
         }
       break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      g_assert_not_reached ();
+      break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       if (meta_renderer_native_has_pending_mode_set (renderer_native))
@@ -1736,6 +1751,9 @@ meta_onscreen_native_allocate (CoglFramebuffer  *framebuffer,
       onscreen_native->gbm.surface = gbm_surface;
       cogl_onscreen_egl_set_egl_surface (onscreen_egl, egl_surface);
       break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      g_assert_not_reached ();
+      break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       render_kms_device =
@@ -2091,6 +2109,9 @@ meta_onscreen_native_dispose (GObject *object)
 
       g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy);
       break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      g_assert_not_reached ();
+      break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       g_clear_object (&onscreen_native->egl.dumb_fb);
diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
index 95d2ffd771..f48db9ab8b 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -206,13 +206,14 @@ meta_renderer_native_connect (CoglRenderer *cogl_renderer,
                               GError      **error)
 {
   CoglRendererEGL *cogl_renderer_egl;
-  MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data;
-  MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms);
+  MetaRendererNative *renderer_native = cogl_renderer->custom_winsys_user_data;
+  MetaGpuKms *gpu_kms;
   MetaRendererNativeGpuData *renderer_gpu_data;
 
   cogl_renderer->winsys = g_new0 (CoglRendererEGL, 1);
   cogl_renderer_egl = cogl_renderer->winsys;
 
+  gpu_kms = meta_renderer_native_get_primary_gpu (renderer_native);
   renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
                                                          gpu_kms);
 
@@ -246,6 +247,10 @@ meta_renderer_native_add_egl_config_attributes (CoglDisplay                 *cog
       attributes[i++] = EGL_SURFACE_TYPE;
       attributes[i++] = EGL_WINDOW_BIT;
       break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      attributes[i++] = EGL_SURFACE_TYPE;
+      attributes[i++] = EGL_PBUFFER_BIT;
+      break;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       attributes[i++] = EGL_SURFACE_TYPE;
@@ -326,6 +331,9 @@ meta_renderer_native_choose_egl_config (CoglDisplay  *cogl_display,
                                                 GBM_FORMAT_XRGB8888,
                                                 out_config,
                                                 error);
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
+      *out_config = EGL_NO_CONFIG_KHR;
+      return TRUE;
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
       return meta_egl_choose_first_config (egl,
@@ -778,10 +786,11 @@ meta_renderer_native_create_dma_buf (CoglRenderer  *cogl_renderer,
         return dmabuf_handle;
       }
       break;
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
 #ifdef HAVE_EGL_DEVICE
     case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
-      break;
 #endif
+      break;
     }
 
   g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN,
@@ -933,26 +942,17 @@ get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer)
 }
 
 static CoglRenderer *
-create_cogl_renderer_for_gpu (MetaGpuKms *gpu_kms)
+meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer)
 {
   CoglRenderer *cogl_renderer;
 
   cogl_renderer = cogl_renderer_new ();
   cogl_renderer_set_custom_winsys (cogl_renderer,
                                    get_native_cogl_winsys_vtable,
-                                   gpu_kms);
-
+                                   renderer);
   return cogl_renderer;
 }
 
-static CoglRenderer *
-meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer)
-{
-  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
-
-  return create_cogl_renderer_for_gpu (renderer_native->primary_gpu_kms);
-}
-
 static MetaMonitorTransform
 calculate_view_transform (MetaMonitorManager *monitor_manager,
                           MetaLogicalMonitor *logical_monitor,
@@ -1244,6 +1244,7 @@ create_secondary_egl_config (MetaEgl               *egl,
   switch (mode)
     {
     case META_RENDERER_NATIVE_MODE_GBM:
+    case META_RENDERER_NATIVE_MODE_SURFACELESS:
       return choose_egl_config_from_gbm_format (egl,
                                                 egl_display,
                                                 attributes,
@@ -1490,6 +1491,65 @@ create_renderer_gpu_data_gbm (MetaRendererNative  *renderer_native,
   return renderer_gpu_data;
 }
 
+static EGLDisplay
+init_surfaceless_egl_display (MetaRendererNative  *renderer_native,
+                              GError             **error)
+{
+  MetaEgl *egl = meta_renderer_native_get_egl (renderer_native);
+  EGLDisplay egl_display;
+
+  if (!meta_egl_has_extensions (egl, EGL_NO_DISPLAY, NULL,
+                                "EGL_MESA_platform_surfaceless",
+                                NULL))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Missing EGL platform required for surfaceless context: "
+                   "EGL_MESA_platform_surfaceless");
+      return EGL_NO_DISPLAY;
+    }
+
+  egl_display = meta_egl_get_platform_display (egl,
+                                               EGL_PLATFORM_SURFACELESS_MESA,
+                                               EGL_DEFAULT_DISPLAY,
+                                               NULL, error);
+  if (egl_display == EGL_NO_DISPLAY)
+    return EGL_NO_DISPLAY;
+
+  if (!meta_egl_initialize (egl, egl_display, error))
+    return EGL_NO_DISPLAY;
+
+  if (!meta_egl_has_extensions (egl, egl_display, NULL,
+                                "EGL_KHR_no_config_context",
+                                NULL))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Missing EGL extension required for surfaceless context: "
+                   "EGL_KHR_no_config_context");
+      return EGL_NO_DISPLAY;
+    }
+
+  return egl_display;
+}
+
+static MetaRendererNativeGpuData *
+create_renderer_gpu_data_surfaceless (MetaRendererNative  *renderer_native,
+                                      GError             **error)
+{
+  MetaRendererNativeGpuData *renderer_gpu_data;
+  EGLDisplay egl_display;
+
+  egl_display = init_surfaceless_egl_display (renderer_native, error);
+  if (egl_display == EGL_NO_DISPLAY)
+    return NULL;
+
+  renderer_gpu_data = meta_create_renderer_native_gpu_data (NULL);
+  renderer_gpu_data->renderer_native = renderer_native;
+  renderer_gpu_data->mode = META_RENDERER_NATIVE_MODE_SURFACELESS;
+  renderer_gpu_data->egl_display = egl_display;
+
+  return renderer_gpu_data;
+}
+
 #ifdef HAVE_EGL_DEVICE
 static const char *
 get_drm_device_file (MetaEgl     *egl,
@@ -1692,6 +1752,9 @@ meta_renderer_native_create_renderer_gpu_data (MetaRendererNative  *renderer_nat
   GError *egl_device_error = NULL;
 #endif
 
+  if (!gpu_kms)
+    return create_renderer_gpu_data_surfaceless (renderer_native, error);
+
 #ifdef HAVE_EGL_DEVICE
   /* Try to initialize the EGLDevice backend first. Whenever we use a
    * non-NVIDIA GPU, the EGLDevice enumeration function won't find a match, and
@@ -1919,22 +1982,30 @@ meta_renderer_native_initable_init (GInitable     *initable,
   GList *l;
 
   gpus = meta_backend_get_gpus (backend);
-  for (l = gpus; l; l = l->next)
+  if (gpus)
     {
-      MetaGpuKms *gpu_kms = META_GPU_KMS (l->data);
+      for (l = gpus; l; l = l->next)
+        {
+          MetaGpuKms *gpu_kms = META_GPU_KMS (l->data);
 
-      if (!create_renderer_gpu_data (renderer_native, gpu_kms, error))
-        return FALSE;
-    }
+          if (!create_renderer_gpu_data (renderer_native, gpu_kms, error))
+            return FALSE;
+        }
 
-  renderer_native->primary_gpu_kms = choose_primary_gpu (backend,
-                                                         renderer_native,
-                                                         error);
-  if (!renderer_native->primary_gpu_kms)
-    return FALSE;
+      renderer_native->primary_gpu_kms = choose_primary_gpu (backend,
+                                                             renderer_native,
+                                                             error);
+      if (!renderer_native->primary_gpu_kms)
+        return FALSE;
 
-  if (meta_gpu_kms_requires_modifiers (renderer_native->primary_gpu_kms))
-    renderer_native->use_modifiers = TRUE;
+      if (meta_gpu_kms_requires_modifiers (renderer_native->primary_gpu_kms))
+        renderer_native->use_modifiers = TRUE;
+    }
+  else
+    {
+      if (!create_renderer_gpu_data (renderer_native, NULL, error))
+        return FALSE;
+    }
 
   return TRUE;
 }
diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h
index dd19eb0144..9475e1857f 100644
--- a/src/backends/native/meta-renderer-native.h
+++ b/src/backends/native/meta-renderer-native.h
@@ -41,6 +41,7 @@ G_DECLARE_FINAL_TYPE (MetaRendererNative, meta_renderer_native,
 typedef enum _MetaRendererNativeMode
 {
   META_RENDERER_NATIVE_MODE_GBM,
+  META_RENDERER_NATIVE_MODE_SURFACELESS,
 #ifdef HAVE_EGL_DEVICE
   META_RENDERER_NATIVE_MODE_EGL_DEVICE
 #endif
diff --git a/src/backends/native/meta-udev.c b/src/backends/native/meta-udev.c
index 649c9df27e..9a6bfe03ba 100644
--- a/src/backends/native/meta-udev.c
+++ b/src/backends/native/meta-udev.c
@@ -159,11 +159,7 @@ meta_udev_list_drm_devices (MetaUdev  *udev,
 
   devices = g_udev_enumerator_execute (enumerator);
   if (!devices)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                   "No drm devices found");
-      return FALSE;
-    }
+    return NULL;
 
   for (l = devices; l;)
     {
@@ -179,13 +175,6 @@ meta_udev_list_drm_devices (MetaUdev  *udev,
       l = l_next;
     }
 
-  if (!devices)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "No DRM devices found");
-      return NULL;
-    }
-
   return devices;
 }
 


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