From 2342a35172e1b66cb1ec0b416f1369f1c736e08b Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 17 Sep 2020 15:15:14 -0700 Subject: [PATCH] Workaround for an Android emulator EGL bug that can cause inaccurate GL version strings Some versions of the Android emulator EGL implementation will only update the GL_VERSION string when the process calls eglMakeCurrent on an EGLContext for the first time. If you select a GLES2 context, then a GLES1 context, and then the original GLES2 context, the version string will not be updated by the second eglMakeCurrent(GLES2) call. So if a GLES1 context was previously current, then when the engine makes its GLES2 context current Skia will continue to see the GLES1 version string and will fail to build a GrGLInterface. This workaround checks for the emulator GL renderer and creates a new context to force a version string update before making the GrGLInterface. See internal bug b/168748787 Fixes https://github.com/flutter/flutter/issues/63663 --- shell/gpu/gpu_surface_gl_delegate.h | 2 +- shell/platform/android/android_context_gl.cc | 8 ++++ shell/platform/android/android_context_gl.h | 7 ++++ shell/platform/android/android_surface_gl.cc | 40 ++++++++++++++++++++ shell/platform/android/android_surface_gl.h | 3 ++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index 8f2c3df04006f..e35be2c6232bb 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -57,7 +57,7 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // flushed. virtual SkMatrix GLContextSurfaceTransformation() const; - sk_sp GetGLInterface() const; + virtual sk_sp GetGLInterface() const; // TODO(chinmaygarde): The presence of this method is to work around the fact // that not all platforms can accept a custom GL proc table. Migrate all diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index 91e2628e1fc1b..1a17a9a8f0444 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -248,4 +248,12 @@ bool AndroidContextGL::ClearCurrent() { return true; } +EGLContext AndroidContextGL::CreateNewContext() const { + bool success; + EGLContext context; + std::tie(success, context) = + CreateContext(environment_->Display(), config_, EGL_NO_CONTEXT); + return success ? context : EGL_NO_CONTEXT; +} + } // namespace flutter diff --git a/shell/platform/android/android_context_gl.h b/shell/platform/android/android_context_gl.h index dc9fc3212303e..a0e4f99e81489 100644 --- a/shell/platform/android/android_context_gl.h +++ b/shell/platform/android/android_context_gl.h @@ -110,6 +110,13 @@ class AndroidContextGL : public AndroidContext { /// bool ClearCurrent(); + //---------------------------------------------------------------------------- + /// @brief Create a new EGLContext using the same EGLConfig. + /// + /// @return The EGLContext. + /// + EGLContext CreateNewContext() const; + private: fml::RefPtr environment_; EGLConfig config_; diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index 642fd4f0c65cb..c9a9a130c59c4 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -4,6 +4,7 @@ #include "flutter/shell/platform/android/android_surface_gl.h" +#include #include #include "flutter/fml/logging.h" @@ -12,6 +13,12 @@ namespace flutter { +namespace { +// GL renderer string prefix used by the Android emulator GLES implementation. +constexpr char kEmulatorRendererPrefix[] = + "Android Emulator OpenGL ES Translator"; +} // anonymous namespace + AndroidSurfaceGL::AndroidSurfaceGL( std::shared_ptr android_context, std::shared_ptr jni_facade, @@ -133,4 +140,37 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return external_view_embedder_.get(); } +// |GPUSurfaceGLDelegate| +sk_sp AndroidSurfaceGL::GetGLInterface() const { + // This is a workaround for a bug in the Android emulator EGL/GLES + // implementation. Some versions of the emulator will not update the + // GL version string when the process switches to a new EGL context + // unless the EGL context is being made current for the first time. + // The inaccurate version string will be rejected by Skia when it + // tries to build the GrGLInterface. Flutter can work around this + // by creating a new context, making it current to force an update + // of the version, and then reverting to the previous context. + const char* gl_renderer = + reinterpret_cast(glGetString(GL_RENDERER)); + if (gl_renderer && strncmp(gl_renderer, kEmulatorRendererPrefix, + strlen(kEmulatorRendererPrefix)) == 0) { + EGLContext new_context = android_context_->CreateNewContext(); + if (new_context != EGL_NO_CONTEXT) { + EGLContext old_context = eglGetCurrentContext(); + EGLDisplay display = eglGetCurrentDisplay(); + EGLSurface draw_surface = eglGetCurrentSurface(EGL_DRAW); + EGLSurface read_surface = eglGetCurrentSurface(EGL_READ); + EGLBoolean result = + eglMakeCurrent(display, draw_surface, read_surface, new_context); + FML_DCHECK(result == EGL_TRUE); + result = eglMakeCurrent(display, draw_surface, read_surface, old_context); + FML_DCHECK(result == EGL_TRUE); + result = eglDestroyContext(display, new_context); + FML_DCHECK(result == EGL_TRUE); + } + } + + return GPUSurfaceGLDelegate::GetGLInterface(); +} + } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index 8af382a521e27..771fbfea41a80 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -65,6 +65,9 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + sk_sp GetGLInterface() const override; + private: const std::unique_ptr external_view_embedder_; const std::shared_ptr android_context_;