chromium/android_webview/browser/gfx/scoped_app_gl_state_restore_impl_angle.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "android_webview/browser/gfx/scoped_app_gl_state_restore_impl_angle.h"

#include <EGL/egl.h>
#include <GLES2/gl2.h>

#include "base/android/build_info.h"
#include "base/native_library.h"
#include "base/threading/thread_restrictions.h"
#include "ui/gl/gl_context.h"

namespace android_webview {
namespace {
namespace os {

// TODO(penghuang): remove this typedef when egl headers are updated to 1.5.
typedef __eglMustCastToProperFunctionPointerType(
    EGLAPIENTRYP PFNEGLGETPROCADDRESSPROC)(const char* procname);
typedef EGLContext(EGLAPIENTRYP PFNEGLGETCURRENTCONTEXTPROC)(void);

PFNEGLGETPROCADDRESSPROC eglGetProcAddressFn = nullptr;
PFNGLGETBOOLEANVPROC glGetBooleanvFn = nullptr;
PFNGLGETINTEGERVPROC glGetIntegervFn = nullptr;
PFNGLGETERRORPROC glGetErrorFn = nullptr;

#if DCHECK_IS_ON()
PFNEGLGETCURRENTCONTEXTPROC eglGetCurrentContextFn = nullptr;
#endif

template <typename T>
void AssignProc(T& fn, const char* name) {
  fn = reinterpret_cast<T>(eglGetProcAddressFn(name));
  CHECK(fn) << "Failed to get " << name;
}

void InitializeGLBindings() {
  if (eglGetProcAddressFn)
    return;

  base::NativeLibraryLoadError error;
  base::FilePath filename("libEGL.so");
  base::NativeLibrary egl_library = base::LoadNativeLibrary(filename, &error);
  CHECK(egl_library) << "Failed to load " << filename.MaybeAsASCII() << ": "
                     << error.ToString();

  eglGetProcAddressFn = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(
      base::GetFunctionPointerFromNativeLibrary(egl_library,
                                                "eglGetProcAddress"));
  CHECK(eglGetProcAddressFn) << "Failed to get eglGetProcAddress.";

  AssignProc(glGetBooleanvFn, "glGetBooleanv");
  AssignProc(glGetIntegervFn, "glGetIntegerv");
  AssignProc(glGetErrorFn, "glGetError");

#if DCHECK_IS_ON()
  AssignProc(eglGetCurrentContextFn, "eglGetCurrentContext");
#endif
}

bool ClearGLErrors(bool warn, const char* msg) {
  bool no_error = true;
  GLenum error;
#if DCHECK_IS_ON()
  DCHECK(eglGetCurrentContextFn());
#endif
  while ((error = glGetErrorFn()) != GL_NO_ERROR) {
    DLOG_IF(WARNING, warn) << error << " " << msg;
    no_error = false;
  }

  return no_error;
}

}  // namespace os

}  // namespace

namespace internal {

ScopedAppGLStateRestoreImplAngle::ScopedAppGLStateRestoreImplAngle(
    ScopedAppGLStateRestore::CallMode mode,
    bool save_restore) {
  os::InitializeGLBindings();

  os::ClearGLErrors(true, "Incoming GLError");

#if DCHECK_IS_ON()
  egl_context_ = os::eglGetCurrentContextFn();
  DCHECK_NE(egl_context_, EGL_NO_CONTEXT) << " no native context is current.";
#endif

  if (mode == ScopedAppGLStateRestore::MODE_DRAW &&
      base::android::BuildInfo::GetInstance()->sdk_int() ==
          base::android::SDK_VERSION_S) {
    GLint red_bits = 0;
    GLint green_bits = 0;
    GLint blue_bits = 0;
    GLint alpha_bits = 0;
    os::glGetIntegervFn(GL_RED_BITS, &red_bits);
    os::glGetIntegervFn(GL_GREEN_BITS, &green_bits);
    os::glGetIntegervFn(GL_BLUE_BITS, &blue_bits);
    os::glGetIntegervFn(GL_ALPHA_BITS, &alpha_bits);
    skip_draw_ =
        red_bits == 8 && green_bits == 0 && blue_bits == 0 && alpha_bits == 0;
  }

  // Query |stencil_state_| with native GL API.
  // Android should have made a native EGL context current, so we can call GL
  // directly.
  os::glGetBooleanvFn(GL_STENCIL_TEST, &stencil_state_.stencil_test_enabled);
  os::glGetIntegervFn(GL_STENCIL_FUNC, &stencil_state_.stencil_front_func);
  os::glGetIntegervFn(GL_STENCIL_VALUE_MASK,
                      &stencil_state_.stencil_front_mask);
  os::glGetIntegervFn(GL_STENCIL_REF, &stencil_state_.stencil_front_ref);
  os::glGetIntegervFn(GL_STENCIL_BACK_FUNC, &stencil_state_.stencil_back_func);
  os::glGetIntegervFn(GL_STENCIL_BACK_VALUE_MASK,
                      &stencil_state_.stencil_back_mask);
  os::glGetIntegervFn(GL_STENCIL_BACK_REF, &stencil_state_.stencil_back_ref);
  os::glGetIntegervFn(GL_STENCIL_CLEAR_VALUE, &stencil_state_.stencil_clear);
  os::glGetIntegervFn(GL_STENCIL_WRITEMASK,
                      &stencil_state_.stencil_front_writemask);
  os::glGetIntegervFn(GL_STENCIL_BACK_WRITEMASK,
                      &stencil_state_.stencil_back_writemask);
  os::glGetIntegervFn(GL_STENCIL_FAIL, &stencil_state_.stencil_front_fail_op);
  os::glGetIntegervFn(GL_STENCIL_PASS_DEPTH_FAIL,
                      &stencil_state_.stencil_front_z_fail_op);
  os::glGetIntegervFn(GL_STENCIL_PASS_DEPTH_PASS,
                      &stencil_state_.stencil_front_z_pass_op);
  os::glGetIntegervFn(GL_STENCIL_BACK_FAIL,
                      &stencil_state_.stencil_back_fail_op);
  os::glGetIntegervFn(GL_STENCIL_BACK_PASS_DEPTH_FAIL,
                      &stencil_state_.stencil_back_z_fail_op);
  os::glGetIntegervFn(GL_STENCIL_BACK_PASS_DEPTH_PASS,
                      &stencil_state_.stencil_back_z_pass_op);
  // ANGLE can wrap current native FBO to an EGLSurface which will be used
  // later, so with that EGLSurface as render target, the framebuffer binding is
  // always 0.
  framebuffer_binding_ext_ = 0;

  os::ClearGLErrors(false, nullptr);
}

ScopedAppGLStateRestoreImplAngle::~ScopedAppGLStateRestoreImplAngle() {
#if DCHECK_IS_ON()
  DCHECK_EQ(egl_context_, os::eglGetCurrentContextFn())
      << " the native context is changed.";
#endif

  // Do not leak GLError out of chromium.
  os::ClearGLErrors(true, "Chromium GLError");
}

}  // namespace internal
}  // namespace android_webview