[gnome-remote-desktop] Introduce an EGL thread



commit 722ceaf0120ffad593d082c2757743411477759a
Author: Jonas Ådahl <jadahl gmail com>
Date:   Fri Oct 8 11:42:45 2021 +0200

    Introduce an EGL thread
    
    It does nothing more than up a thread with a main loop, but will be used
    to download DMA buffers to CPU memory.

 meson.build             |   2 +
 src/grd-egl-thread.c    | 211 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/grd-egl-thread.h    |  32 ++++++++
 src/meson.build         |   5 ++
 tests/egl-thread-test.c |  39 +++++++++
 tests/meson.build       |  16 ++++
 6 files changed, 305 insertions(+)
---
diff --git a/meson.build b/meson.build
index 06fbe1c..7914eeb 100644
--- a/meson.build
+++ b/meson.build
@@ -8,6 +8,7 @@ freerdp_req = '>= 2.3.0'
 fuse_req = '>= 3.9.1'
 nvenc_req = '>= 11'
 xkbcommon_req = '>= 1.0.0'
+epoxy_req = '>= 1.4'
 
 gnome = import('gnome')
 i18n  = import('i18n')
@@ -22,6 +23,7 @@ pipewire_dep = dependency('libpipewire-0.3', version: '>= 0.3.0')
 systemd_dep = dependency('systemd', required: get_option('systemd'))
 libsecret_dep = dependency('libsecret-1')
 libnotify_dep = dependency('libnotify')
+epoxy_dep = dependency('epoxy')
 
 have_rdp = get_option('rdp')
 have_vnc = get_option('vnc')
diff --git a/src/grd-egl-thread.c b/src/grd-egl-thread.c
new file mode 100644
index 0000000..5abff48
--- /dev/null
+++ b/src/grd-egl-thread.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 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 "grd-egl-thread.h"
+
+#include <epoxy/egl.h>
+#include <epoxy/gl.h>
+#include <gio/gio.h>
+
+struct _GrdEglThread
+{
+  GThread *thread;
+  GMutex mutex;
+  GCond cond;
+
+  GAsyncQueue *task_queue;
+
+  struct
+  {
+    gboolean initialized;
+    GError *error;
+
+    GMainContext *main_context;
+    GMainLoop *main_loop;
+
+    EGLDisplay egl_display;
+    EGLContext egl_context;
+  } impl;
+};
+
+static gboolean
+grd_egl_init_in_impl (GrdEglThread  *egl_thread,
+                      GError       **error)
+{
+  EGLDisplay egl_display;
+  EGLint major, minor;
+  EGLContext egl_context;
+
+  if (!epoxy_has_egl_extension (NULL, "EGL_EXT_platform_base"))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Missing extension 'EGL_EXT_platform_base'");
+      return FALSE;
+    }
+
+  egl_display = eglGetPlatformDisplayEXT (EGL_PLATFORM_SURFACELESS_MESA, NULL, NULL);
+  if (egl_display == EGL_NO_DISPLAY)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to get EGL display");
+      return FALSE;
+    }
+
+  if (!eglInitialize (egl_display, &major, &minor))
+    {
+      eglTerminate (egl_display);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to initialize EGL display");
+      return FALSE;
+    }
+
+  if (!eglBindAPI (EGL_OPENGL_ES_API))
+    {
+      eglTerminate (egl_display);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to bind OpenGL ES API");
+      return FALSE;
+    }
+
+  if (!epoxy_has_egl_extension (egl_display, "EGL_MESA_platform_surfaceless"))
+    {
+      eglTerminate (egl_display);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   "Missing extension 'EGL_MESA_platform_surfaceless'");
+      return FALSE;
+    }
+
+  if (!epoxy_has_egl_extension (egl_display, "EGL_MESA_configless_context"))
+    {
+      eglTerminate (egl_display);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                   "Missing extension 'EGL_MESA_platform_surfaceless'");
+      return FALSE;
+    }
+
+  egl_context = eglCreateContext (egl_display, EGL_NO_CONFIG_KHR, NULL, NULL);
+  if (egl_context == EGL_NO_CONTEXT)
+    {
+      eglTerminate (egl_display);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Failed to create EGL context");
+      return FALSE;
+    }
+
+  egl_thread->impl.egl_display = egl_display;
+  egl_thread->impl.egl_context = egl_context;
+
+  return TRUE;
+}
+
+static gpointer
+grd_egl_thread_func (gpointer user_data)
+{
+  GrdEglThread *egl_thread = user_data;
+  GError *error = NULL;
+
+  egl_thread->impl.main_context = g_main_context_new ();
+
+  if (!grd_egl_init_in_impl (egl_thread, &error))
+    {
+      g_propagate_error (&egl_thread->impl.error, error);
+      g_mutex_lock (&egl_thread->mutex);
+      egl_thread->impl.initialized = TRUE;
+      g_cond_signal (&egl_thread->cond);
+      g_mutex_unlock (&egl_thread->mutex);
+      return NULL;
+    }
+
+  egl_thread->impl.main_loop = g_main_loop_new (egl_thread->impl.main_context,
+                                                FALSE);
+  g_mutex_lock (&egl_thread->mutex);
+  egl_thread->impl.initialized = TRUE;
+  g_cond_signal (&egl_thread->cond);
+  g_mutex_unlock (&egl_thread->mutex);
+
+  g_main_loop_run (egl_thread->impl.main_loop);
+
+  g_clear_pointer (&egl_thread->impl.main_loop, g_main_loop_unref);
+
+  eglDestroyContext (egl_thread->impl.egl_display,
+                     egl_thread->impl.egl_context);
+  eglTerminate (egl_thread->impl.egl_display);
+
+  return NULL;
+}
+
+GrdEglThread *
+grd_egl_thread_new (GError **error)
+{
+  GrdEglThread *egl_thread;
+
+  egl_thread = g_new0 (GrdEglThread, 1);
+  g_mutex_init (&egl_thread->mutex);
+  g_cond_init (&egl_thread->cond);
+
+  g_mutex_lock (&egl_thread->mutex);
+
+  egl_thread->thread = g_thread_new ("GRD EGL thread", grd_egl_thread_func, egl_thread);
+
+  while (!egl_thread->impl.initialized)
+    g_cond_wait (&egl_thread->cond, &egl_thread->mutex);
+  g_mutex_unlock (&egl_thread->mutex);
+
+  if (egl_thread->impl.error)
+    {
+      g_propagate_error (error, egl_thread->impl.error);
+      grd_egl_thread_free (egl_thread);
+      return NULL;
+    }
+
+  return egl_thread;
+}
+
+static gboolean
+quit_main_loop_in_impl (gpointer user_data)
+{
+  GrdEglThread *egl_thread = user_data;
+
+  if (egl_thread->impl.main_loop)
+    g_main_loop_quit (egl_thread->impl.main_loop);
+
+  return G_SOURCE_REMOVE;
+}
+
+void
+grd_egl_thread_free (GrdEglThread *egl_thread)
+{
+  GSource *source;
+
+  source = g_idle_source_new ();
+  g_source_set_callback (source, quit_main_loop_in_impl, egl_thread, NULL);
+  g_source_attach (source, egl_thread->impl.main_context);
+  g_source_unref (source);
+
+  g_thread_join (egl_thread->thread);
+  g_mutex_clear (&egl_thread->mutex);
+  g_cond_clear (&egl_thread->cond);
+
+  g_clear_pointer (&egl_thread->impl.main_context, g_main_context_unref);
+
+  g_free (egl_thread);
+}
diff --git a/src/grd-egl-thread.h b/src/grd-egl-thread.h
new file mode 100644
index 0000000..0dabf1e
--- /dev/null
+++ b/src/grd-egl-thread.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 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.
+ *
+ */
+
+#ifndef GRD_EGL_THREAD_H
+#define GRD_EGL_THREAD_H
+
+#include <glib.h>
+
+typedef struct _GrdEglThread GrdEglThread;
+
+GrdEglThread * grd_egl_thread_new (GError **error);
+
+void grd_egl_thread_free (GrdEglThread *egl_thread);
+
+#endif /* GRD_EGL_THREAD_H */
diff --git a/src/meson.build b/src/meson.build
index 2fe3923..a65a62b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,3 +1,5 @@
+src_includepath = include_directories('.')
+
 deps = [
   cairo_dep,
   glib_dep,
@@ -6,6 +8,7 @@ deps = [
   pipewire_dep,
   libsecret_dep,
   libnotify_dep,
+  epoxy_dep,
 ]
 
 daemon_sources = files([
@@ -13,6 +16,8 @@ daemon_sources = files([
   'grd-clipboard.h',
   'grd-context.c',
   'grd-context.h',
+  'grd-egl-thread.c',
+  'grd-egl-thread.h',
   'grd-daemon.c',
   'grd-daemon.h',
   'grd-damage-utils.c',
diff --git a/tests/egl-thread-test.c b/tests/egl-thread-test.c
new file mode 100644
index 0000000..b6d387d
--- /dev/null
+++ b/tests/egl-thread-test.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 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 "grd-egl-thread.h"
+
+int
+main (int    argc,
+      char **argv)
+{
+  GError *error = NULL;
+  GrdEglThread *egl_thread;
+
+  egl_thread = grd_egl_thread_new (&error);
+  if (!egl_thread)
+    g_error ("Failed to start EGL thread: %s", error->message);
+
+  grd_egl_thread_free (egl_thread);
+
+  return EXIT_SUCCESS;
+}
diff --git a/tests/meson.build b/tests/meson.build
index 978ae23..a82c261 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -20,3 +20,19 @@ if have_vnc
     timeout: 10,
   )
 endif
+
+egl_thread_test = executable(
+  'egl-thread-test',
+  sources: [
+    'egl-thread-test.c',
+    '../src/grd-egl-thread.c',
+    '../src/grd-egl-thread.h',
+  ],
+  dependencies: deps,
+  include_directories: [
+    src_includepath,
+    configinc,
+  ],
+)
+
+test('egl-thread', egl_thread_test)


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