From 8b61e0b6cdc85448d33c3776cf5a01665a74f497 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 9 Jun 2020 15:43:44 +1200 Subject: [PATCH 1/4] Log EGL errors --- shell/platform/linux/fl_renderer.cc | 61 +++++++++++++++++++++--- shell/platform/linux/testing/mock_egl.cc | 4 ++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 891eb7f9909e3..5db82f9bc1e5b 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -16,6 +16,45 @@ typedef struct { G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT) +// Gets a string representation of the last EGL error. +static const gchar* get_egl_error() { + EGLint error = eglGetError(); + switch (error) { + case EGL_SUCCESS: + return "Success"; + case EGL_NOT_INITIALIZED: + return "Not Initialized"; + case EGL_BAD_ACCESS: + return "Bad Access"; + case EGL_BAD_ALLOC: + return "Bad Allocation"; + case EGL_BAD_ATTRIBUTE: + return "Bad Attribute"; + case EGL_BAD_CONTEXT: + return "Bad Context"; + case EGL_BAD_CONFIG: + return "Bad Configuration"; + case EGL_BAD_CURRENT_SURFACE: + return "Bad Current Surface"; + case EGL_BAD_DISPLAY: + return "Bad Display"; + case EGL_BAD_SURFACE: + return "Bad Surface"; + case EGL_BAD_MATCH: + return "Bad Match"; + case EGL_BAD_PARAMETER: + return "Bad Parameter"; + case EGL_BAD_NATIVE_PIXMAP: + return "Bad Native Pixmap"; + case EGL_BAD_NATIVE_WINDOW: + return "Bad Native Window"; + case EGL_CONTEXT_LOST: + return "Context Lost"; + default: + return "Unknown Error"; + } +} + // Default implementation for the start virtual method. // Provided so subclasses can chain up to here. static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) { @@ -52,25 +91,35 @@ static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) { if (!eglChooseConfig(priv->egl_display, attributes, &egl_config, 1, &n_config)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to choose EGL config"); + "Failed to choose EGL config: %s", get_egl_error()); return FALSE; } if (n_config == 0) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to find appropriate EGL config"); + "Failed to find appropriate EGL config: %s", get_egl_error()); return FALSE; } if (!eglBindAPI(EGL_OPENGL_ES_API)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to bind EGL OpenGL ES API"); + "Failed to bind EGL OpenGL ES API: %s", get_egl_error()); return FALSE; } priv->egl_surface = FL_RENDERER_GET_CLASS(self)->create_surface( self, priv->egl_display, egl_config); + if (priv->egl_surface == nullptr) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to create EGL surface: %s", get_egl_error()); + return FALSE; + } EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; priv->egl_context = eglCreateContext(priv->egl_display, egl_config, EGL_NO_CONTEXT, context_attributes); + if (priv->egl_context == nullptr) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to create EGL context: %s", get_egl_error()); + return FALSE; + } EGLint value; eglQueryContext(priv->egl_display, priv->egl_context, EGL_CONTEXT_CLIENT_VERSION, &value); @@ -99,7 +148,7 @@ gboolean fl_renderer_make_current(FlRenderer* self, GError** error) { if (!eglMakeCurrent(priv->egl_display, priv->egl_surface, priv->egl_surface, priv->egl_context)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to make EGL context current"); + "Failed to make EGL context current: %s", get_egl_error()); return FALSE; } @@ -113,7 +162,7 @@ gboolean fl_renderer_clear_current(FlRenderer* self, GError** error) { if (!eglMakeCurrent(priv->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to clear EGL context"); + "Failed to clear EGL context: %s", get_egl_error()); return FALSE; } @@ -131,7 +180,7 @@ gboolean fl_renderer_present(FlRenderer* self, GError** error) { if (!eglSwapBuffers(priv->egl_display, priv->egl_surface)) { g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to swap EGL buffers"); + "Failed to swap EGL buffers: %s", get_egl_error()); return FALSE; } diff --git a/shell/platform/linux/testing/mock_egl.cc b/shell/platform/linux/testing/mock_egl.cc index 53edb697b6f2f..12ce679e5dc23 100644 --- a/shell/platform/linux/testing/mock_egl.cc +++ b/shell/platform/linux/testing/mock_egl.cc @@ -34,6 +34,10 @@ EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) { return nullptr; } +EGLint eglGetError() { + return EGL_SUCCESS; +} + void (*eglGetProcAddress(const char* procname))(void) { return nullptr; } From 50c69aea0d6387d9aafdc951c9cbf31c9b3b5544 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 4 Jun 2020 16:39:54 +1200 Subject: [PATCH 2/4] Implement an EGL resource context for the Linux shell. --- shell/platform/linux/fl_engine.cc | 10 +++++++ shell/platform/linux/fl_renderer.cc | 34 ++++++++++++++++++++++++ shell/platform/linux/fl_renderer.h | 13 +++++++++ shell/platform/linux/testing/mock_egl.cc | 6 +++++ 4 files changed, 63 insertions(+) diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 6ca91892db0fa..c1fd69e48f706 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -108,6 +108,15 @@ static bool fl_engine_gl_present(void* user_data) { return result; } +static bool fl_engine_gl_make_resource_current(void* user_data) { + FlEngine* self = static_cast(user_data); + g_autoptr(GError) error = nullptr; + gboolean result = fl_renderer_make_resource_current(self->renderer, &error); + if (!result) + g_warning("%s", error->message); + return result; +} + // Called by the engine to determine if it is on the GTK thread. static bool fl_engine_runs_task_on_current_thread(void* user_data) { FlEngine* self = static_cast(user_data); @@ -222,6 +231,7 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { config.open_gl.clear_current = fl_engine_gl_clear_current; config.open_gl.fbo_callback = fl_engine_gl_get_fbo; config.open_gl.present = fl_engine_gl_present; + config.open_gl.make_resource_current = fl_engine_gl_make_resource_current; FlutterTaskRunnerDescription platform_task_runner = {}; platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription); diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 5db82f9bc1e5b..40b53ac67b91c 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -12,6 +12,9 @@ typedef struct { EGLDisplay egl_display; EGLSurface egl_surface; EGLContext egl_context; + + EGLSurface resource_surface; + EGLContext resource_context; } FlRendererPrivate; G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT) @@ -124,6 +127,23 @@ static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) { eglQueryContext(priv->egl_display, priv->egl_context, EGL_CONTEXT_CLIENT_VERSION, &value); + const EGLint shared_context_attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, + EGL_NONE}; + priv->resource_surface = eglCreatePbufferSurface( + priv->egl_display, egl_config, shared_context_attribs); + if (priv->resource_surface == nullptr) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to create EGL resource surface: %s", get_egl_error()); + return FALSE; + } + priv->resource_context = eglCreateContext( + priv->egl_display, egl_config, priv->egl_context, context_attributes); + if (priv->resource_context == nullptr) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to create EGL resource context: %s", get_egl_error()); + return FALSE; + } + return TRUE; } @@ -155,6 +175,20 @@ gboolean fl_renderer_make_current(FlRenderer* self, GError** error) { return TRUE; } +gboolean fl_renderer_make_resource_current(FlRenderer* self, GError** error) { + FlRendererPrivate* priv = + static_cast(fl_renderer_get_instance_private(self)); + + if (!eglMakeCurrent(priv->egl_display, priv->resource_surface, + priv->resource_surface, priv->resource_context)) { + g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to make EGL context current: %s", get_egl_error()); + return FALSE; + } + + return TRUE; +} + gboolean fl_renderer_clear_current(FlRenderer* self, GError** error) { FlRendererPrivate* priv = static_cast(fl_renderer_get_instance_private(self)); diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h index 58063f3473b15..edfafeadc6a24 100644 --- a/shell/platform/linux/fl_renderer.h +++ b/shell/platform/linux/fl_renderer.h @@ -80,6 +80,19 @@ void* fl_renderer_get_proc_address(FlRenderer* renderer, const char* name); */ gboolean fl_renderer_make_current(FlRenderer* renderer, GError** error); +/** + * fl_renderer_make_resource_current: + * @renderer: an #FlRenderer. + * @error: (allow-none): #GError location to store the error occurring, or %NULL + * to ignore. + * + * Makes the resource rendering context current. + * + * Returns %TRUE if successful. + */ +gboolean fl_renderer_make_resource_current(FlRenderer* renderer, + GError** error); + /** * fl_renderer_clear_current: * @renderer: an #FlRenderer. diff --git a/shell/platform/linux/testing/mock_egl.cc b/shell/platform/linux/testing/mock_egl.cc index 12ce679e5dc23..ab28fc9068492 100644 --- a/shell/platform/linux/testing/mock_egl.cc +++ b/shell/platform/linux/testing/mock_egl.cc @@ -23,6 +23,12 @@ EGLContext eglCreateContext(EGLDisplay dpy, return nullptr; } +EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, + EGLConfig config, + const EGLint* attrib_list) { + return nullptr; +} + EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, From bde0c798ec92ef531975a2a8b5ce61889918c198 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 10 Jun 2020 07:03:44 +1200 Subject: [PATCH 3/4] Don't make resource context creation fatal Fixes https://github.com/flutter/flutter/issues/54855 --- shell/platform/linux/fl_renderer.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 40b53ac67b91c..5ede864b9a288 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -132,15 +132,14 @@ static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) { priv->resource_surface = eglCreatePbufferSurface( priv->egl_display, egl_config, shared_context_attribs); if (priv->resource_surface == nullptr) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL resource surface: %s", get_egl_error()); - return FALSE; + g_warning(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to create EGL resource surface: %s", get_egl_error()); } priv->resource_context = eglCreateContext( priv->egl_display, egl_config, priv->egl_context, context_attributes); if (priv->resource_context == nullptr) { - g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL resource context: %s", get_egl_error()); + g_warning(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, + "Failed to create EGL resource context: %s", get_egl_error()); return FALSE; } From 4c6f8c1c482b434b158f5b1a6aeb4deb4aa82863 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 10 Jun 2020 10:42:13 +1200 Subject: [PATCH 4/4] Refactor out create_resource_surface --- shell/platform/linux/fl_renderer.cc | 51 ++++++++++++++++------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 5ede864b9a288..7b6da5f273648 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -58,18 +58,38 @@ static const gchar* get_egl_error() { } } +// Creates a resource surface. +static void create_resource_surface(FlRenderer* self, EGLConfig config) { + FlRendererPrivate* priv = + static_cast(fl_renderer_get_instance_private(self)); + + EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + const EGLint resource_context_attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, + EGL_NONE}; + priv->resource_surface = eglCreatePbufferSurface(priv->egl_display, config, + resource_context_attribs); + if (priv->resource_surface != nullptr) { + g_warning("Failed to create EGL resource surface: %s", get_egl_error()); + return; + } + + priv->resource_context = eglCreateContext( + priv->egl_display, config, priv->egl_context, context_attributes); + if (priv->resource_context == nullptr) + g_warning("Failed to create EGL resource context: %s", get_egl_error()); +} + // Default implementation for the start virtual method. // Provided so subclasses can chain up to here. static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) { FlRendererPrivate* priv = static_cast(fl_renderer_get_instance_private(self)); - // Note the use of EGL_DEFAULT_DISPLAY rather than sharing an existing display - // connection (e.g. an X11 connection from GTK). This is because this EGL - // display is going to be accessed by a thread from Flutter. In the case - // of GTK/X11 the display connection is not thread safe and this would cause - // a crash. - // + // Note the use of EGL_DEFAULT_DISPLAY rather than sharing an existing + // display connection (e.g. an X11 connection from GTK). This is because + // this EGL display is going to be accessed by a thread from Flutter. In the + // case of GTK/X11 the display connection is not thread safe and this would + // cause a crash. priv->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (!eglInitialize(priv->egl_display, nullptr, nullptr)) { @@ -123,26 +143,13 @@ static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) { "Failed to create EGL context: %s", get_egl_error()); return FALSE; } + + create_resource_surface(self, egl_config); + EGLint value; eglQueryContext(priv->egl_display, priv->egl_context, EGL_CONTEXT_CLIENT_VERSION, &value); - const EGLint shared_context_attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, - EGL_NONE}; - priv->resource_surface = eglCreatePbufferSurface( - priv->egl_display, egl_config, shared_context_attribs); - if (priv->resource_surface == nullptr) { - g_warning(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL resource surface: %s", get_egl_error()); - } - priv->resource_context = eglCreateContext( - priv->egl_display, egl_config, priv->egl_context, context_attributes); - if (priv->resource_context == nullptr) { - g_warning(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED, - "Failed to create EGL resource context: %s", get_egl_error()); - return FALSE; - } - return TRUE; }