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..27392495dab6f 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) @@ -55,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)) { @@ -120,6 +143,9 @@ 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); @@ -155,6 +181,23 @@ 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 (priv->resource_surface == nullptr || priv->resource_context == nullptr) + return FALSE; + + 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,