| // 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_skia.h" | |
| #include <GLES/gl.h> | |
| #include <utility> | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/fml/memory/ref_ptr.h" | |
| #include "flutter/shell/platform/android/android_egl_surface.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 | |
| AndroidSurfaceGLSkia::AndroidSurfaceGLSkia( | |
| const std::shared_ptr<AndroidContext>& android_context, | |
| const std::shared_ptr<PlatformViewAndroidJNI>& jni_facade) | |
| : AndroidSurface(android_context), | |
| native_window_(nullptr), | |
| onscreen_surface_(nullptr), | |
| offscreen_surface_(nullptr) { | |
| // Acquire the offscreen surface. | |
| offscreen_surface_ = GLContextPtr()->CreateOffscreenSurface(); | |
| if (!offscreen_surface_->IsValid()) { | |
| offscreen_surface_ = nullptr; | |
| } | |
| } | |
| AndroidSurfaceGLSkia::~AndroidSurfaceGLSkia() = default; | |
| void AndroidSurfaceGLSkia::TeardownOnScreenContext() { | |
| // When the onscreen surface is destroyed, the context and the surface | |
| // instance should be deleted. Issue: | |
| // https://github.com/flutter/flutter/issues/64414 | |
| GLContextPtr()->ClearCurrent(); | |
| onscreen_surface_ = nullptr; | |
| } | |
| bool AndroidSurfaceGLSkia::IsValid() const { | |
| return offscreen_surface_ && GLContextPtr()->IsValid(); | |
| } | |
| std::unique_ptr<Surface> AndroidSurfaceGLSkia::CreateGPUSurface( | |
| GrDirectContext* gr_context) { | |
| if (gr_context) { | |
| return std::make_unique<GPUSurfaceGLSkia>(sk_ref_sp(gr_context), this, | |
| true); | |
| } else { | |
| sk_sp<GrDirectContext> main_skia_context = | |
| GLContextPtr()->GetMainSkiaContext(); | |
| if (!main_skia_context) { | |
| main_skia_context = GPUSurfaceGLSkia::MakeGLContext(this); | |
| GLContextPtr()->SetMainSkiaContext(main_skia_context); | |
| } | |
| return std::make_unique<GPUSurfaceGLSkia>(main_skia_context, this, true); | |
| } | |
| } | |
| bool AndroidSurfaceGLSkia::OnScreenSurfaceResize(const SkISize& size) { | |
| FML_DCHECK(IsValid()); | |
| FML_DCHECK(onscreen_surface_); | |
| FML_DCHECK(native_window_); | |
| if (size == onscreen_surface_->GetSize()) { | |
| return true; | |
| } | |
| GLContextPtr()->ClearCurrent(); | |
| // Ensure the destructor is called since it destroys the `EGLSurface` before | |
| // creating a new onscreen surface. | |
| onscreen_surface_ = nullptr; | |
| onscreen_surface_ = GLContextPtr()->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 AndroidSurfaceGLSkia::ResourceContextMakeCurrent() { | |
| FML_DCHECK(IsValid()); | |
| auto status = offscreen_surface_->MakeCurrent(); | |
| return status != AndroidEGLSurfaceMakeCurrentStatus::kFailure; | |
| } | |
| bool AndroidSurfaceGLSkia::ResourceContextClearCurrent() { | |
| FML_DCHECK(IsValid()); | |
| EGLBoolean result = eglMakeCurrent(eglGetCurrentDisplay(), EGL_NO_SURFACE, | |
| EGL_NO_SURFACE, EGL_NO_CONTEXT); | |
| return result == EGL_TRUE; | |
| } | |
| bool AndroidSurfaceGLSkia::SetNativeWindow( | |
| fml::RefPtr<AndroidNativeWindow> 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_ = GLContextPtr()->CreateOnscreenSurface(window); | |
| if (!onscreen_surface_->IsValid()) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::unique_ptr<GLContextResult> AndroidSurfaceGLSkia::GLContextMakeCurrent() { | |
| FML_DCHECK(IsValid()); | |
| FML_DCHECK(onscreen_surface_); | |
| auto status = onscreen_surface_->MakeCurrent(); | |
| auto default_context_result = std::make_unique<GLContextDefaultResult>( | |
| status != AndroidEGLSurfaceMakeCurrentStatus::kFailure); | |
| return std::move(default_context_result); | |
| } | |
| bool AndroidSurfaceGLSkia::GLContextClearCurrent() { | |
| FML_DCHECK(IsValid()); | |
| return GLContextPtr()->ClearCurrent(); | |
| } | |
| SurfaceFrame::FramebufferInfo AndroidSurfaceGLSkia::GLContextFramebufferInfo() | |
| const { | |
| FML_DCHECK(IsValid()); | |
| SurfaceFrame::FramebufferInfo res; | |
| res.supports_readback = true; | |
| res.supports_partial_repaint = onscreen_surface_->SupportsPartialRepaint(); | |
| res.existing_damage = onscreen_surface_->InitialDamage(); | |
| // Some devices (Pixel2 XL) needs EGL_KHR_partial_update rect aligned to 4, | |
| // otherwise there are glitches | |
| // (https://github.com/flutter/flutter/issues/97482#) | |
| // Larger alignment might also be beneficial for tile base renderers. | |
| res.horizontal_clip_alignment = 32; | |
| res.vertical_clip_alignment = 32; | |
| return res; | |
| } | |
| void AndroidSurfaceGLSkia::GLContextSetDamageRegion( | |
| const std::optional<SkIRect>& region) { | |
| FML_DCHECK(IsValid()); | |
| onscreen_surface_->SetDamageRegion(region); | |
| } | |
| bool AndroidSurfaceGLSkia::GLContextPresent(const GLPresentInfo& present_info) { | |
| FML_DCHECK(IsValid()); | |
| FML_DCHECK(onscreen_surface_); | |
| if (present_info.presentation_time) { | |
| onscreen_surface_->SetPresentationTime(*present_info.presentation_time); | |
| } | |
| return onscreen_surface_->SwapBuffers(present_info.frame_damage); | |
| } | |
| GLFBOInfo AndroidSurfaceGLSkia::GLContextFBO(GLFrameInfo frame_info) const { | |
| FML_DCHECK(IsValid()); | |
| // The default window bound framebuffer on Android. | |
| return GLFBOInfo{ | |
| .fbo_id = 0, | |
| .partial_repaint_enabled = onscreen_surface_->SupportsPartialRepaint(), | |
| .existing_damage = onscreen_surface_->InitialDamage(), | |
| }; | |
| } | |
| // |GPUSurfaceGLDelegate| | |
| sk_sp<const GrGLInterface> AndroidSurfaceGLSkia::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<const char*>(glGetString(GL_RENDERER)); | |
| if (gl_renderer && strncmp(gl_renderer, kEmulatorRendererPrefix, | |
| strlen(kEmulatorRendererPrefix)) == 0) { | |
| EGLContext new_context = GLContextPtr()->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); | |
| [[maybe_unused]] 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(); | |
| } | |
| AndroidContextGLSkia* AndroidSurfaceGLSkia::GLContextPtr() const { | |
| return reinterpret_cast<AndroidContextGLSkia*>(android_context_.get()); | |
| } | |
| std::unique_ptr<Surface> AndroidSurfaceGLSkia::CreateSnapshotSurface() { | |
| if (!onscreen_surface_ || !onscreen_surface_->IsValid()) { | |
| onscreen_surface_ = GLContextPtr()->CreatePbufferSurface(); | |
| } | |
| sk_sp<GrDirectContext> main_skia_context = | |
| GLContextPtr()->GetMainSkiaContext(); | |
| if (!main_skia_context) { | |
| main_skia_context = GPUSurfaceGLSkia::MakeGLContext(this); | |
| GLContextPtr()->SetMainSkiaContext(main_skia_context); | |
| } | |
| return std::make_unique<GPUSurfaceGLSkia>(main_skia_context, this, true); | |
| } | |
| } // namespace flutter |