/* * Copyright (C) 2016-2021 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/native/meta-render-device-egl-stream.h" #include "backends/meta-backend-private.h" struct _MetaRenderDeviceEglStream { MetaRenderDevice parent; EGLDeviceEXT egl_device; }; static GInitableIface *initable_parent_iface; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (MetaRenderDeviceEglStream, meta_render_device_egl_stream, META_TYPE_RENDER_DEVICE, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) static EGLDisplay get_egl_device_display (MetaRenderDevice *render_device, EGLDeviceEXT egl_device, GError **error) { MetaBackend *backend = meta_render_device_get_backend (render_device); MetaEgl *egl = meta_backend_get_egl (backend); MetaDeviceFile *device_file = meta_render_device_get_device_file (render_device); int kms_fd = meta_device_file_get_fd (device_file); EGLint platform_attribs[] = { EGL_DRM_MASTER_FD_EXT, kms_fd, EGL_NONE }; return meta_egl_get_platform_display (egl, EGL_PLATFORM_DEVICE_EXT, (void *) egl_device, platform_attribs, error); } static int count_mode_setting_devices (MetaBackend *backend) { return g_list_length (meta_backend_get_gpus (backend)); } static const char * get_drm_device_file (MetaEgl *egl, EGLDeviceEXT device, GError **error) { if (!meta_egl_egl_device_has_extensions (egl, device, NULL, "EGL_EXT_device_drm", NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing required EGLDevice extension EGL_EXT_device_drm"); return NULL; } return meta_egl_query_device_string (egl, device, EGL_DRM_DEVICE_FILE_EXT, error); } static EGLDeviceEXT find_egl_device (MetaRenderDevice *render_device, GError **error) { MetaBackend *backend = meta_render_device_get_backend (render_device); MetaEgl *egl = meta_backend_get_egl (backend); g_autofree const char **missing_extensions = NULL; MetaDeviceFile *device_file = meta_render_device_get_device_file (render_device); EGLint num_devices; g_autofree EGLDeviceEXT *devices = NULL; const char *device_file_path; EGLDeviceEXT device; EGLint i; if (!meta_egl_has_extensions (egl, EGL_NO_DISPLAY, &missing_extensions, "EGL_EXT_device_base", NULL)) { g_autofree char *missing_extensions_str = NULL; missing_extensions_str = g_strjoinv (", ", (char **) missing_extensions); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing EGL extensions required for EGLDevice renderer: %s", missing_extensions_str); return EGL_NO_DEVICE_EXT; } if (!meta_egl_query_devices (egl, 0, NULL, &num_devices, error)) return EGL_NO_DEVICE_EXT; devices = g_new0 (EGLDeviceEXT, num_devices); if (!meta_egl_query_devices (egl, num_devices, devices, &num_devices, error)) return EGL_NO_DEVICE_EXT; device_file_path = meta_device_file_get_path (device_file); device = EGL_NO_DEVICE_EXT; for (i = 0; i < num_devices; i++) { const char *egl_device_drm_path; g_clear_error (error); egl_device_drm_path = get_drm_device_file (egl, devices[i], error); if (!egl_device_drm_path) continue; if (g_str_equal (egl_device_drm_path, device_file_path)) { device = devices[i]; break; } } if (device == EGL_NO_DEVICE_EXT) { if (!*error) g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to find matching EGLDeviceEXT"); return EGL_NO_DEVICE_EXT; } return device; } static gboolean meta_render_device_egl_stream_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaRenderDevice *render_device = META_RENDER_DEVICE (initable); MetaRenderDeviceEglStream *render_device_egl_stream = META_RENDER_DEVICE_EGL_STREAM (initable); MetaBackend *backend = meta_render_device_get_backend (render_device); EGLDeviceEXT egl_device; EGLDisplay egl_display; g_autofree const char **missing_extensions = NULL; if (count_mode_setting_devices (backend) != 1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EGLDevice currently only works with single GPU systems"); return FALSE; } egl_device = find_egl_device (render_device, error); if (egl_device == EGL_NO_DEVICE_EXT) return FALSE; render_device_egl_stream->egl_device = egl_device; if (!initable_parent_iface->init (initable, cancellable, error)) return FALSE; egl_display = meta_render_device_get_egl_display (render_device); if (egl_display == EGL_NO_DISPLAY) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EGLStream render device requires an EGL display"); return FALSE; } return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_parent_iface = g_type_interface_peek_parent (initable_iface); initable_iface->init = meta_render_device_egl_stream_initable_init; } static EGLDisplay meta_render_device_egl_stream_create_egl_display (MetaRenderDevice *render_device, GError **error) { MetaRenderDeviceEglStream *render_device_egl_stream = META_RENDER_DEVICE_EGL_STREAM (render_device); EGLDeviceEXT egl_device = render_device_egl_stream->egl_device; MetaBackend *backend = meta_render_device_get_backend (render_device); MetaEgl *egl = meta_backend_get_egl (backend); EGLDisplay egl_display; g_autofree const char **missing_extensions = NULL; egl_display = get_egl_device_display (render_device, egl_device, error); if (egl_display == EGL_NO_DISPLAY) return EGL_NO_DISPLAY; if (!meta_egl_initialize (egl, egl_display, error)) { meta_egl_terminate (egl, egl_display, NULL); return EGL_NO_DISPLAY; } if (!meta_egl_has_extensions (egl, egl_display, &missing_extensions, "EGL_NV_output_drm_flip_event", "EGL_EXT_output_base", "EGL_EXT_output_drm", "EGL_KHR_stream", "EGL_KHR_stream_producer_eglsurface", "EGL_EXT_stream_consumer_egloutput", "EGL_EXT_stream_acquire_mode", NULL)) { g_autofree char *missing_extensions_str = NULL; meta_egl_terminate (egl, egl_display, NULL); missing_extensions_str = g_strjoinv (", ", (char **) missing_extensions); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing EGL extensions required for EGLDevice renderer: %s", missing_extensions_str); meta_egl_terminate (egl, egl_display, NULL); return EGL_NO_DISPLAY; } return egl_display; } static void meta_render_device_egl_stream_class_init (MetaRenderDeviceEglStreamClass *klass) { MetaRenderDeviceClass *render_device_class = META_RENDER_DEVICE_CLASS (klass); render_device_class->create_egl_display = meta_render_device_egl_stream_create_egl_display; } static void meta_render_device_egl_stream_init (MetaRenderDeviceEglStream *render_device_egl_stream) { } MetaRenderDeviceEglStream * meta_render_device_egl_stream_new (MetaBackend *backend, MetaDeviceFile *device_file, GError **error) { return g_initable_new (META_TYPE_RENDER_DEVICE_EGL_STREAM, NULL, error, "backend", backend, "device-file", device_file, NULL); }