// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/shell/platform/android/android_surface_gl.h" #include #include #include "flutter/fml/logging.h" #include "flutter/fml/memory/ref_ptr.h" #include "flutter/shell/platform/android/android_shell_holder.h" 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, const AndroidSurface::Factory& surface_factory) : external_view_embedder_( std::make_unique(android_context, jni_facade, surface_factory)), android_context_( std::static_pointer_cast(android_context)), native_window_(nullptr), onscreen_surface_(nullptr), offscreen_surface_(nullptr) { // Acquire the offscreen surface. offscreen_surface_ = android_context_->CreateOffscreenSurface(); if (!offscreen_surface_->IsValid()) { offscreen_surface_ = nullptr; } } AndroidSurfaceGL::~AndroidSurfaceGL() = default; void AndroidSurfaceGL::TeardownOnScreenContext() { // When the onscreen surface is destroyed, the context and the surface // instance should be deleted. Issue: // https://github.com/flutter/flutter/issues/64414 android_context_->ClearCurrent(); onscreen_surface_ = nullptr; } bool AndroidSurfaceGL::IsValid() const { return offscreen_surface_ && android_context_->IsValid(); } std::unique_ptr AndroidSurfaceGL::CreateGPUSurface( GrDirectContext* gr_context) { if (gr_context) { return std::make_unique(sk_ref_sp(gr_context), this, true); } return std::make_unique(this, true); } bool AndroidSurfaceGL::OnScreenSurfaceResize(const SkISize& size) { FML_DCHECK(IsValid()); FML_DCHECK(onscreen_surface_); FML_DCHECK(native_window_); if (size == onscreen_surface_->GetSize()) { return true; } android_context_->ClearCurrent(); // Ensure the destructor is called since it destroys the `EGLSurface` before // creating a new onscreen surface. onscreen_surface_ = nullptr; onscreen_surface_ = android_context_->CreateOnscreenSurface(native_window_); if (!onscreen_surface_->IsValid()) { FML_LOG(ERROR) << "Unable to create EGL window surface on resize."; return false; } onscreen_surface_->MakeCurrent(); return true; } bool AndroidSurfaceGL::ResourceContextMakeCurrent() { FML_DCHECK(IsValid()); return offscreen_surface_->MakeCurrent(); } bool AndroidSurfaceGL::ResourceContextClearCurrent() { FML_DCHECK(IsValid()); return android_context_->ClearCurrent(); } bool AndroidSurfaceGL::SetNativeWindow( fml::RefPtr window) { FML_DCHECK(IsValid()); FML_DCHECK(window); native_window_ = window; // Ensure the destructor is called since it destroys the `EGLSurface` before // creating a new onscreen surface. onscreen_surface_ = nullptr; // Create the onscreen surface. onscreen_surface_ = android_context_->CreateOnscreenSurface(window); if (!onscreen_surface_->IsValid()) { return false; } return true; } std::unique_ptr AndroidSurfaceGL::GLContextMakeCurrent() { FML_DCHECK(IsValid()); FML_DCHECK(onscreen_surface_); auto default_context_result = std::make_unique( onscreen_surface_->MakeCurrent()); return std::move(default_context_result); } bool AndroidSurfaceGL::GLContextClearCurrent() { FML_DCHECK(IsValid()); return android_context_->ClearCurrent(); } bool AndroidSurfaceGL::GLContextPresent(uint32_t fbo_id) { FML_DCHECK(IsValid()); FML_DCHECK(onscreen_surface_); return onscreen_surface_->SwapBuffers(); } intptr_t AndroidSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const { FML_DCHECK(IsValid()); // The default window bound framebuffer on Android. return 0; } // |GPUSurfaceGLDelegate| 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