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>
This commit is contained in:
Jonas Ådahl 2021-02-09 14:32:55 +01:00 committed by Marge Bot
parent ee4a0dae7c
commit 634c948fc6
5 changed files with 130 additions and 40 deletions

View File

@ -144,6 +144,12 @@ maybe_disable_screen_cast_dma_bufs (MetaBackendNative *native)
}; };
primary_gpu = meta_renderer_native_get_primary_gpu (renderer_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); kms_device = meta_gpu_kms_get_kms_device (primary_gpu);
driver_name = meta_kms_device_get_driver_name (kms_device); 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'.", g_message ("Disabling DMA buffer screen sharing for driver '%s'.",
driver_name); driver_name);
disable_dma_bufs:
meta_screen_cast_disable_dma_bufs (screen_cast); meta_screen_cast_disable_dma_bufs (screen_cast);
} }
#endif /* HAVE_REMOTE_DESKTOP */ #endif /* HAVE_REMOTE_DESKTOP */
@ -473,7 +480,7 @@ init_gpus (MetaBackendNative *native,
GList *l; GList *l;
devices = meta_udev_list_drm_devices (udev, error); devices = meta_udev_list_drm_devices (udev, error);
if (!devices) if (*error)
return FALSE; return FALSE;
for (l = devices; l; l = l->next) for (l = devices; l; l = l->next)
@ -498,7 +505,8 @@ init_gpus (MetaBackendNative *native,
g_list_free_full (devices, g_object_unref); 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, g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"No GPUs found"); "No GPUs found");

View File

@ -240,6 +240,9 @@ notify_view_crtc_presented (MetaRendererView *view,
case META_RENDERER_NATIVE_MODE_GBM: case META_RENDERER_NATIVE_MODE_GBM:
meta_onscreen_native_swap_drm_fb (onscreen); meta_onscreen_native_swap_drm_fb (onscreen);
break; break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
break;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE: case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
break; break;
@ -473,6 +476,9 @@ meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen,
meta_crtc_kms_assign_primary_plane (crtc_kms, buffer, kms_update); meta_crtc_kms_assign_primary_plane (crtc_kms, buffer, kms_update);
break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
break; break;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_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: case META_RENDERER_NATIVE_MODE_GBM:
break; break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
break;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_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); onscreen_native->gbm.next_fb = META_DRM_BUFFER (buffer_gbm);
break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
break; break;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE: case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
@ -1117,6 +1129,9 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen,
return; return;
} }
break; break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
break;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE: case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
if (meta_renderer_native_has_pending_mode_set (renderer_native)) 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; onscreen_native->gbm.surface = gbm_surface;
cogl_onscreen_egl_set_egl_surface (onscreen_egl, egl_surface); cogl_onscreen_egl_set_egl_surface (onscreen_egl, egl_surface);
break; break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
break;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE: case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
render_kms_device = render_kms_device =
@ -2091,6 +2109,9 @@ meta_onscreen_native_dispose (GObject *object)
g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy); g_clear_pointer (&onscreen_native->gbm.surface, gbm_surface_destroy);
break; break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
g_assert_not_reached ();
break;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE: case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
g_clear_object (&onscreen_native->egl.dumb_fb); g_clear_object (&onscreen_native->egl.dumb_fb);

View File

@ -206,13 +206,14 @@ meta_renderer_native_connect (CoglRenderer *cogl_renderer,
GError **error) GError **error)
{ {
CoglRendererEGL *cogl_renderer_egl; CoglRendererEGL *cogl_renderer_egl;
MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data; MetaRendererNative *renderer_native = cogl_renderer->custom_winsys_user_data;
MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms); MetaGpuKms *gpu_kms;
MetaRendererNativeGpuData *renderer_gpu_data; MetaRendererNativeGpuData *renderer_gpu_data;
cogl_renderer->winsys = g_new0 (CoglRendererEGL, 1); cogl_renderer->winsys = g_new0 (CoglRendererEGL, 1);
cogl_renderer_egl = cogl_renderer->winsys; 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, renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native,
gpu_kms); gpu_kms);
@ -246,6 +247,10 @@ meta_renderer_native_add_egl_config_attributes (CoglDisplay *cog
attributes[i++] = EGL_SURFACE_TYPE; attributes[i++] = EGL_SURFACE_TYPE;
attributes[i++] = EGL_WINDOW_BIT; attributes[i++] = EGL_WINDOW_BIT;
break; break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
attributes[i++] = EGL_SURFACE_TYPE;
attributes[i++] = EGL_PBUFFER_BIT;
break;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE: case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
attributes[i++] = EGL_SURFACE_TYPE; attributes[i++] = EGL_SURFACE_TYPE;
@ -326,6 +331,9 @@ meta_renderer_native_choose_egl_config (CoglDisplay *cogl_display,
GBM_FORMAT_XRGB8888, GBM_FORMAT_XRGB8888,
out_config, out_config,
error); error);
case META_RENDERER_NATIVE_MODE_SURFACELESS:
*out_config = EGL_NO_CONFIG_KHR;
return TRUE;
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE: case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
return meta_egl_choose_first_config (egl, return meta_egl_choose_first_config (egl,
@ -778,10 +786,11 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer,
return dmabuf_handle; return dmabuf_handle;
} }
break; break;
case META_RENDERER_NATIVE_MODE_SURFACELESS:
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
case META_RENDERER_NATIVE_MODE_EGL_DEVICE: case META_RENDERER_NATIVE_MODE_EGL_DEVICE:
break;
#endif #endif
break;
} }
g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN, 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 * static CoglRenderer *
create_cogl_renderer_for_gpu (MetaGpuKms *gpu_kms) meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer)
{ {
CoglRenderer *cogl_renderer; CoglRenderer *cogl_renderer;
cogl_renderer = cogl_renderer_new (); cogl_renderer = cogl_renderer_new ();
cogl_renderer_set_custom_winsys (cogl_renderer, cogl_renderer_set_custom_winsys (cogl_renderer,
get_native_cogl_winsys_vtable, get_native_cogl_winsys_vtable,
gpu_kms); renderer);
return cogl_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 static MetaMonitorTransform
calculate_view_transform (MetaMonitorManager *monitor_manager, calculate_view_transform (MetaMonitorManager *monitor_manager,
MetaLogicalMonitor *logical_monitor, MetaLogicalMonitor *logical_monitor,
@ -1244,6 +1244,7 @@ create_secondary_egl_config (MetaEgl *egl,
switch (mode) switch (mode)
{ {
case META_RENDERER_NATIVE_MODE_GBM: case META_RENDERER_NATIVE_MODE_GBM:
case META_RENDERER_NATIVE_MODE_SURFACELESS:
return choose_egl_config_from_gbm_format (egl, return choose_egl_config_from_gbm_format (egl,
egl_display, egl_display,
attributes, attributes,
@ -1490,6 +1491,65 @@ create_renderer_gpu_data_gbm (MetaRendererNative *renderer_native,
return renderer_gpu_data; 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 #ifdef HAVE_EGL_DEVICE
static const char * static const char *
get_drm_device_file (MetaEgl *egl, 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; GError *egl_device_error = NULL;
#endif #endif
if (!gpu_kms)
return create_renderer_gpu_data_surfaceless (renderer_native, error);
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
/* Try to initialize the EGLDevice backend first. Whenever we use a /* Try to initialize the EGLDevice backend first. Whenever we use a
* non-NVIDIA GPU, the EGLDevice enumeration function won't find a match, and * non-NVIDIA GPU, the EGLDevice enumeration function won't find a match, and
@ -1919,6 +1982,8 @@ meta_renderer_native_initable_init (GInitable *initable,
GList *l; GList *l;
gpus = meta_backend_get_gpus (backend); gpus = meta_backend_get_gpus (backend);
if (gpus)
{
for (l = gpus; l; l = l->next) for (l = gpus; l; l = l->next)
{ {
MetaGpuKms *gpu_kms = META_GPU_KMS (l->data); MetaGpuKms *gpu_kms = META_GPU_KMS (l->data);
@ -1935,6 +2000,12 @@ meta_renderer_native_initable_init (GInitable *initable,
if (meta_gpu_kms_requires_modifiers (renderer_native->primary_gpu_kms)) if (meta_gpu_kms_requires_modifiers (renderer_native->primary_gpu_kms))
renderer_native->use_modifiers = TRUE; renderer_native->use_modifiers = TRUE;
}
else
{
if (!create_renderer_gpu_data (renderer_native, NULL, error))
return FALSE;
}
return TRUE; return TRUE;
} }

View File

@ -41,6 +41,7 @@ G_DECLARE_FINAL_TYPE (MetaRendererNative, meta_renderer_native,
typedef enum _MetaRendererNativeMode typedef enum _MetaRendererNativeMode
{ {
META_RENDERER_NATIVE_MODE_GBM, META_RENDERER_NATIVE_MODE_GBM,
META_RENDERER_NATIVE_MODE_SURFACELESS,
#ifdef HAVE_EGL_DEVICE #ifdef HAVE_EGL_DEVICE
META_RENDERER_NATIVE_MODE_EGL_DEVICE META_RENDERER_NATIVE_MODE_EGL_DEVICE
#endif #endif

View File

@ -159,11 +159,7 @@ meta_udev_list_drm_devices (MetaUdev *udev,
devices = g_udev_enumerator_execute (enumerator); devices = g_udev_enumerator_execute (enumerator);
if (!devices) if (!devices)
{ return NULL;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"No drm devices found");
return FALSE;
}
for (l = devices; l;) for (l = devices; l;)
{ {
@ -179,13 +175,6 @@ meta_udev_list_drm_devices (MetaUdev *udev,
l = l_next; l = l_next;
} }
if (!devices)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"No DRM devices found");
return NULL;
}
return devices; return devices;
} }