From 634c948fc62a9a3b6604354d22cbaf28515b42eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 9 Feb 2021 14:32:55 +0100 Subject: [PATCH] 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: --- 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 65000cb52..f0c2fadaa 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 4290eeeca..7c0fb5133 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 95d2ffd77..f48db9ab8 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,23 +1982,31 @@ 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)) + 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; + + 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; } - 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; - return TRUE; } diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h index dd19eb014..9475e1857 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 649c9df27..9a6bfe03b 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; }