910e62b5创建于 1月15日历史提交
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gl/gl_display.h"

#include <string>
#include <type_traits>
#include <vector>

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/debug/crash_logging.h"
#include "base/export_template.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/synchronization/atomic_flag.h"
#include "base/system/sys_info.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "gl_display.h"
#include "gl_switches.h"
#include "ui/base/ozone_buildflags.h"
#include "ui/gl/angle_platform_impl.h"
#include "ui/gl/egl_util.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context_egl.h"
#include "ui/gl/gl_display_egl_util.h"
#include "ui/gl/gl_features.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gpu_switching_manager.h"

#if BUILDFLAG(IS_ANDROID)
#include "base/android/android_info.h"
#endif

using ui::GetLastEGLErrorString;

namespace gl {

namespace {

base::AtomicFlag* GetANGLEDebugLayerFlag() {
  static base::AtomicFlag* const flag = new base::AtomicFlag();
  return flag;
}

std::vector<const char*> GetAttribArrayFromStringVector(
    const std::vector<std::string>& strings) {
  std::vector<const char*> attribs;
  for (const std::string& item : strings) {
    attribs.push_back(item.c_str());
  }
  attribs.push_back(nullptr);
  return attribs;
}

EGLDisplay GetPlatformANGLEDisplay(
    EGLNativeDisplayType display,
    EGLenum platform_type,
    const std::vector<std::string>& enabled_features,
    const std::vector<std::string>& disabled_features,
    const std::vector<EGLAttrib>& extra_display_attribs) {
  TRACE_EVENT("gpu,startup", "gl_display::GetPlatformANGLEDisplay");
  std::vector<EGLAttrib> display_attribs(extra_display_attribs);

  display_attribs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
  display_attribs.push_back(static_cast<EGLAttrib>(platform_type));

  if (platform_type == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) {
    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
    if (command_line->HasSwitch(switches::kUseAdapterLuid)) {
      // If the LUID is specified, the format is <high part>,<low part>. Split
      // and add them to the EGL_ANGLE_platform_angle_d3d_luid ext attributes.
      std::string luid =
          command_line->GetSwitchValueASCII(switches::kUseAdapterLuid);
      size_t comma = luid.find(',');
      if (comma != std::string::npos) {
        int32_t high;
        uint32_t low;
        if (!base::StringToInt(luid.substr(0, comma), &high) ||
            !base::StringToUint(luid.substr(comma + 1), &low))
          return EGL_NO_DISPLAY;

        display_attribs.push_back(EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE);
        display_attribs.push_back(high);

        display_attribs.push_back(EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE);
        display_attribs.push_back(low);
      }
    }
  }

  GLDisplayEglUtil::GetInstance()->GetPlatformExtraDisplayAttribs(
      platform_type, &display_attribs);

  std::vector<const char*> enabled_features_attribs =
      GetAttribArrayFromStringVector(enabled_features);
  std::vector<const char*> disabled_features_attribs =
      GetAttribArrayFromStringVector(disabled_features);
  if (g_driver_egl.client_ext.b_EGL_ANGLE_feature_control) {
    if (!enabled_features_attribs.empty()) {
      display_attribs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
      display_attribs.push_back(
          reinterpret_cast<EGLAttrib>(enabled_features_attribs.data()));
    }
    if (!disabled_features_attribs.empty()) {
      display_attribs.push_back(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE);
      display_attribs.push_back(
          reinterpret_cast<EGLAttrib>(disabled_features_attribs.data()));
    }
  }
  // TODO(dbehr) Add an attrib to Angle to pass EGL platform.

  if (g_driver_egl.client_ext.b_EGL_ANGLE_display_power_preference) {
    GpuPreference pref =
        GLSurface::AdjustGpuPreference(GpuPreference::kDefault);
    switch (pref) {
      case GpuPreference::kDefault:
        // Don't request any GPU, let ANGLE and the native driver decide.
        break;
      case GpuPreference::kLowPower:
        display_attribs.push_back(EGL_POWER_PREFERENCE_ANGLE);
        display_attribs.push_back(EGL_LOW_POWER_ANGLE);
        break;
      case GpuPreference::kHighPerformance:
        display_attribs.push_back(EGL_POWER_PREFERENCE_ANGLE);
        display_attribs.push_back(EGL_HIGH_POWER_ANGLE);
        break;
      default:
        NOTREACHED();
    }
  }

  display_attribs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE);
  display_attribs.push_back(GetANGLEDebugLayerFlag()->IsSet() ? EGL_TRUE
                                                              : EGL_FALSE);

  display_attribs.push_back(EGL_NONE);

  // This is an EGL 1.5 function that we know ANGLE supports. It's used to pass
  // EGLAttribs (pointers) instead of EGLints into the display
  return eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
                               reinterpret_cast<void*>(display),
                               &display_attribs[0]);
}

EGLDisplay GetDisplayFromType(
    DisplayType display_type,
    EGLDisplayPlatform native_display,
    const std::vector<std::string>& enabled_angle_features,
    const std::vector<std::string>& disabled_angle_features,
    uint64_t system_device_id,
    DisplayKey display_key) {
  std::vector<EGLAttrib> extra_display_attribs;
  if (system_device_id != 0 &&
      g_driver_egl.client_ext.b_EGL_ANGLE_platform_angle_device_id) {
    uint32_t low_part = system_device_id & 0xffffffff;
    extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE);
    extra_display_attribs.push_back(low_part);

    uint32_t high_part = (system_device_id >> 32) & 0xffffffff;
    extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE);
    extra_display_attribs.push_back(high_part);
  }
  if (display_key != DisplayKey::kDefault) {
    extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DISPLAY_KEY_ANGLE);
    extra_display_attribs.push_back(static_cast<EGLint>(display_key));
  }
  EGLNativeDisplayType display = native_display.GetDisplay();
  switch (display_type) {
    case DEFAULT:
    case SWIFT_SHADER: {
      if (native_display.GetPlatform() != 0) {
        return eglGetPlatformDisplay(native_display.GetPlatform(),
                                     reinterpret_cast<void*>(display), nullptr);
      }
      return eglGetDisplay(display);
    }
    case ANGLE_D3D9:
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_D3D11:
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_D3D11_WARP:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(
          EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_D3D11_NULL:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(
          EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_OPENGL:
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_OPENGL_EGL:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_EGL_ANGLE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_OPENGL_NULL:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(
          EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_OPENGLES:
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE,
          enabled_angle_features, disabled_angle_features,
          extra_display_attribs);
    case ANGLE_OPENGLES_EGL:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_EGL_ANGLE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE,
          enabled_angle_features, disabled_angle_features,
          extra_display_attribs);
    case ANGLE_OPENGLES_NULL:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(
          EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE,
          enabled_angle_features, disabled_angle_features,
          extra_display_attribs);
    case ANGLE_NULL:
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_VULKAN:
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_VULKAN_NULL:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(
          EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_D3D11on12:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE);
      extra_display_attribs.push_back(EGL_TRUE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_SWIFTSHADER:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(
          EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE);
#if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_OZONE_X11)
      extra_display_attribs.push_back(
          EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE);
      extra_display_attribs.push_back(
          EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE);
#endif  // BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_OZONE_X11)
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_METAL:
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    case ANGLE_METAL_NULL:
      extra_display_attribs.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
      extra_display_attribs.push_back(
          EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE);
      return GetPlatformANGLEDisplay(
          display, EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE, enabled_angle_features,
          disabled_angle_features, extra_display_attribs);
    default:
      NOTREACHED();
  }
}

ANGLEImplementation GetANGLEImplementationFromDisplayType(
    DisplayType display_type) {
  switch (display_type) {
    case ANGLE_D3D9:
      return ANGLEImplementation::kD3D9;
    case ANGLE_D3D11:
    case ANGLE_D3D11_NULL:
    case ANGLE_D3D11on12:
      return ANGLEImplementation::kD3D11;
    case ANGLE_D3D11_WARP:
      return ANGLEImplementation::kD3D11Warp;
    case ANGLE_OPENGL:
    case ANGLE_OPENGL_EGL:
    case ANGLE_OPENGL_NULL:
      return ANGLEImplementation::kOpenGL;
    case ANGLE_OPENGLES:
    case ANGLE_OPENGLES_EGL:
    case ANGLE_OPENGLES_NULL:
      return ANGLEImplementation::kOpenGLES;
    case ANGLE_NULL:
      return ANGLEImplementation::kNull;
    case ANGLE_VULKAN:
    case ANGLE_VULKAN_NULL:
      return ANGLEImplementation::kVulkan;
    case ANGLE_SWIFTSHADER:
      return ANGLEImplementation::kSwiftShader;
    case ANGLE_METAL:
    case ANGLE_METAL_NULL:
      return ANGLEImplementation::kMetal;
    default:
      return ANGLEImplementation::kNone;
  }
}

const char* DisplayTypeString(DisplayType display_type) {
  switch (display_type) {
    case DEFAULT:
      return "Default";
    case SWIFT_SHADER:
      return "SwiftShader";
    case ANGLE_D3D9:
      return "D3D9";
    case ANGLE_D3D11:
      return "D3D11";
    case ANGLE_D3D11_WARP:
      return "D3D11Warp";
    case ANGLE_D3D11_NULL:
      return "D3D11Null";
    case ANGLE_OPENGL:
      return "OpenGL";
    case ANGLE_OPENGL_NULL:
      return "OpenGLNull";
    case ANGLE_OPENGLES:
      return "OpenGLES";
    case ANGLE_OPENGLES_NULL:
      return "OpenGLESNull";
    case ANGLE_NULL:
      return "Null";
    case ANGLE_VULKAN:
      return "Vulkan";
    case ANGLE_VULKAN_NULL:
      return "VulkanNull";
    case ANGLE_D3D11on12:
      return "D3D11on12";
    case ANGLE_SWIFTSHADER:
      return "SwANGLE";
    case ANGLE_OPENGL_EGL:
      return "OpenGLEGL";
    case ANGLE_OPENGLES_EGL:
      return "OpenGLESEGL";
    case ANGLE_METAL:
      return "Metal";
    case ANGLE_METAL_NULL:
      return "MetalNull";
    default:
      NOTREACHED();
  }
}

void SetEglDebugMessageControl() {
  static bool egl_debug_message_control_is_set = false;
  if (!egl_debug_message_control_is_set) {
    EGLAttrib controls[] = {
        EGL_DEBUG_MSG_CRITICAL_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_ERROR_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_WARN_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_INFO_KHR,
        EGL_TRUE,
        EGL_NONE,
        EGL_NONE,
    };

    eglDebugMessageControlKHR(&ui::LogEGLDebugMessage, controls);
  }
}

}  // namespace

GLDisplay::GLDisplay(uint64_t system_device_id,
                     DisplayKey display_key,
                     DisplayPlatform type)
    : system_device_id_(system_device_id),
      display_key_(display_key),
      type_(type) {}

GLDisplay::~GLDisplay() = default;

template <typename GLDisplayPlatform>
GLDisplayPlatform* GLDisplay::GetAs() {
  bool type_checked = false;
  switch (type_) {
    case NONE:
      NOTREACHED();

    case EGL:
      type_checked = std::is_same<GLDisplayPlatform, GLDisplayEGL>::value;
      break;
  }
  if (type_checked)
    return static_cast<GLDisplayPlatform*>(this);

  return nullptr;
}

template EXPORT_TEMPLATE_DEFINE(GL_EXPORT)
    GLDisplayEGL* GLDisplay::GetAs<GLDisplayEGL>();

GLDisplayEGL::EGLGpuSwitchingObserver::EGLGpuSwitchingObserver(
    EGLDisplay display)
    : display_(display) {
  DCHECK(display != EGL_NO_DISPLAY);
}

void GLDisplayEGL::EGLGpuSwitchingObserver::OnGpuSwitched() {
  eglHandleGPUSwitchANGLE(display_);
}

// Because on Apple platforms there is a member variable of a type (ObjCStorage)
// that is defined in gl_display_egl.mm, the constructor/destructor also have to
// be there. If making changes to this copy, be sure to adjust the other.
#if !BUILDFLAG(IS_APPLE)
GLDisplayEGL::GLDisplayEGL(uint64_t system_device_id, DisplayKey display_key)
    : GLDisplay(system_device_id, display_key, EGL) {
  ext = std::make_unique<DisplayExtensionsEGL>();
}

GLDisplayEGL::~GLDisplayEGL() = default;
#endif

EGLDisplay GLDisplayEGL::GetDisplay() const {
  return display_;
}

void GLDisplayEGL::Shutdown() {
  if (display_ == EGL_NO_DISPLAY)
    return;

  if (gpu_switching_observer_.get()) {
    ui::GpuSwitchingManager::GetInstance()->RemoveObserver(
        gpu_switching_observer_.get());
    gpu_switching_observer_.reset();
  }

  DCHECK(g_driver_egl.fn.eglGetProcAddressFn);
  angle::ResetPlatform(display_, g_driver_egl.fn.eglGetProcAddressFn);
  DCHECK(g_driver_egl.fn.eglTerminateFn);
  eglTerminate(display_);

  display_ = EGL_NO_DISPLAY;
  egl_surfaceless_context_supported_ = false;
  egl_context_priority_supported_ = false;
  egl_android_native_fence_sync_supported_ = false;

#if BUILDFLAG(IS_APPLE)
  CleanupMetalSharedEventStorage();
#endif
}

bool GLDisplayEGL::IsInitialized() const {
  return display_ != EGL_NO_DISPLAY;
}

void GLDisplayEGL::SetDisplay(EGLDisplay display) {
  display_ = display;
}

EGLDisplayPlatform GLDisplayEGL::GetNativeDisplay() const {
  return native_display_;
}

DisplayType GLDisplayEGL::GetDisplayType() const {
  return display_type_;
}

// static
GLDisplayEGL* GLDisplayEGL::GetDisplayForCurrentContext() {
  GLContext* context = GLContext::GetCurrent();
  return context ? context->GetGLDisplayEGL() : nullptr;
}

// static
void GLDisplayEGL::EnableANGLEDebugLayer() {
  GetANGLEDebugLayerFlag()->Set();
}

bool GLDisplayEGL::IsEGLSurfacelessContextSupported() {
  return egl_surfaceless_context_supported_;
}

bool GLDisplayEGL::IsEGLContextPrioritySupported() {
  return egl_context_priority_supported_;
}

bool GLDisplayEGL::IsAndroidNativeFenceSyncSupported() {
  return egl_android_native_fence_sync_supported_;
}

bool GLDisplayEGL::IsANGLEExternalContextAndSurfaceSupported() {
  return this->ext->b_EGL_ANGLE_external_context_and_surface;
}

bool GLDisplayEGL::Initialize(bool supports_angle,
                              std::vector<DisplayType> init_displays,
                              EGLDisplayPlatform native_display) {
  if (display_ != EGL_NO_DISPLAY)
    return true;

  if (!InitializeDisplay(supports_angle, init_displays, native_display,
                         /*existing_display=*/nullptr)) {
    return false;
  }
  InitializeCommon(/*for_testing=*/false);

  return true;
}

bool GLDisplayEGL::Initialize(GLDisplay* other_display) {
  DCHECK(other_display);
  DCHECK_EQ(display_, EGL_NO_DISPLAY);
  DCHECK_NE(display_key_, other_display->display_key());
  GLDisplayEGL* other_display_egl = other_display->GetAs<GLDisplayEGL>();
  if (other_display_egl == nullptr || !other_display_egl->IsInitialized()) {
    return false;
  }

  // Only allow initialization from a display from the same device.
  if (other_display_egl->system_device_id() != system_device_id_) {
    return false;
  }

  auto gl_implementation = GetGLImplementationParts();
  bool supports_angle = (gl_implementation.gl == kGLImplementationEGLANGLE);
  std::vector<DisplayType> init_displays;
  init_displays.push_back(other_display_egl->GetDisplayType());
  if (!InitializeDisplay(supports_angle, init_displays,
                         other_display_egl->GetNativeDisplay(),
                         other_display_egl)) {
    return false;
  }

  InitializeCommon(/*for_testing=*/false);

  return true;
}

void GLDisplayEGL::InitializeForTesting() {
  display_ = eglGetCurrentDisplay();
  ext->InitializeExtensionSettings(display_);
  InitializeCommon(/*for_testing=*/true);
}

bool GLDisplayEGL::InitializeExtensionSettings() {
  if (display_ == EGL_NO_DISPLAY)
    return false;
  ext->UpdateConditionalExtensionSettings(display_);
  return true;
}

// InitializeDisplay is necessary because the static binding code
// needs a full Display init before it can query the Display extensions.
bool GLDisplayEGL::InitializeDisplay(bool supports_angle,
                                     std::vector<DisplayType> init_displays,
                                     EGLDisplayPlatform native_display,
                                     gl::GLDisplayEGL* existing_display) {
  TRACE_EVENT("gpu,startup", "gl::GLDisplayEGL::InitializeDisplay");
  if (display_ != EGL_NO_DISPLAY)
    return true;

  native_display_ = native_display;

  bool supports_egl_debug = g_driver_egl.client_ext.b_EGL_KHR_debug;
  if (supports_egl_debug) {
    SetEglDebugMessageControl();
  }

  if (g_driver_egl.client_ext.b_EGL_ANGLE_no_error &&
      !features::IsANGLEValidationEnabled()) {
    eglSetValidationEnabledANGLE(EGL_FALSE);
  }

  std::vector<std::string> enabled_angle_features;
  std::vector<std::string> disabled_angle_features;
  features::GetANGLEFeaturesFromCommandLineAndFinch(
      base::CommandLine::ForCurrentProcess(), enabled_angle_features,
      disabled_angle_features);

  for (size_t disp_index = 0; disp_index < init_displays.size(); ++disp_index) {
    DisplayType display_type = init_displays[disp_index];
    EGLDisplay display = GetDisplayFromType(
        display_type, native_display, enabled_angle_features,
        disabled_angle_features, system_device_id_, display_key_);
    if (display == EGL_NO_DISPLAY) {
      // Assume this is not an error, so don't verbosely report it;
      // simply try the next display type.
      continue;
    }

    if (!existing_display) {
      // Init ANGLE platform now that we have the global display.
      if (supports_angle) {
        if (!angle::InitializePlatform(display,
                                       g_driver_egl.fn.eglGetProcAddressFn)) {
          LOG(ERROR) << "ANGLE Platform initialization failed.";
        }

        SetANGLEImplementation(
            GetANGLEImplementationFromDisplayType(display_type));
      }

      // The platform may need to unset its platform specific display env in
      // case of vulkan if the platform doesn't support Vulkan surface.
      std::optional<base::ScopedEnvironmentVariableOverride> unset_display;
      if (display_type == ANGLE_VULKAN) {
        unset_display = GLDisplayEglUtil::GetInstance()
                            ->MaybeGetScopedDisplayUnsetForVulkan();
      }
    }

    {
      TRACE_EVENT("gpu,startup", "eglInitializeFn display");
      if (!eglInitialize(display, nullptr, nullptr)) {
        bool is_last = disp_index == init_displays.size() - 1;

        LOG(ERROR) << "eglInitialize " << DisplayTypeString(display_type)
                   << " failed with error " << GetLastEGLErrorString()
                   << (is_last ? "" : ", trying next display type");
        continue;
      }
    }

    if (!existing_display) {
      std::ostringstream display_type_string;
      auto gl_implementation = GetGLImplementationParts();
      display_type_string << GetGLImplementationGLName(gl_implementation);
      if (gl_implementation.gl == kGLImplementationEGLANGLE) {
        display_type_string << ":" << DisplayTypeString(display_type);
      }

      static auto* egl_display_type_key = base::debug::AllocateCrashKeyString(
          "egl-display-type", base::debug::CrashKeySize::Size32);
      base::debug::SetCrashKeyString(egl_display_type_key,
                                     display_type_string.str());

      UMA_HISTOGRAM_ENUMERATION("GPU.EGLDisplayType", display_type,
                                DISPLAY_TYPE_MAX);
    }
    display_ = display;
    display_type_ = display_type;
    if (!existing_display) {
      ext->InitializeExtensionSettings(display);
    } else {
      type_ = existing_display->type();
      ext =
          std::make_unique<DisplayExtensionsEGL>(*existing_display->ext.get());
    }
    return true;
  }

  LOG(ERROR) << "Initialization of all EGL display types failed.";

  return false;
}

void GLDisplayEGL::InitializeCommon(bool for_testing) {
  TRACE_EVENT("gpu,startup", "gl::GLDisplayEGL::InitializeCommon");
  // According to https://source.android.com/compatibility/android-cdd.html the
  // EGL_IMG_context_priority extension is mandatory for Virtual Reality High
  // Performance support, but due to a bug in Android Nougat the extension
  // isn't being reported even when it's present. As a fallback, check if other
  // related extensions that were added for VR support are present, and assume
  // that this implies context priority is also supported. See also:
  // https://github.com/googlevr/gvr-android-sdk/issues/330
  egl_context_priority_supported_ =
      ext->b_EGL_IMG_context_priority ||
      (ext->b_EGL_ANDROID_front_buffer_auto_refresh &&
       ext->b_EGL_ANDROID_create_native_client_buffer);

  // Check if SurfacelessEGL is supported.
  egl_surfaceless_context_supported_ = ext->b_EGL_KHR_surfaceless_context;

  // TODO(oetuaho@nvidia.com): Surfaceless is disabled on Android as a temporary
  // workaround, since code written for Android WebView takes different paths
  // based on whether GL surface objects have underlying EGL surface handles,
  // conflicting with the use of surfaceless. ANGLE can still expose surfacelss
  // because it is emulated with pbuffers if native support is not present. See
  // https://crbug.com/382349.

#if BUILDFLAG(IS_ANDROID)
  // Use the WebGL compatibility extension for detecting ANGLE. ANGLE always
  // exposes it.
  bool is_angle = ext->b_EGL_ANGLE_create_context_webgl_compatibility;
  if (!is_angle) {
    egl_surfaceless_context_supported_ = false;
  }
#endif  // BUILDFLAG(IS_ANDROID)

  if (egl_surfaceless_context_supported_) {
    // EGL_KHR_surfaceless_context is supported but ensure
    // GL_OES_surfaceless_context is also supported. We need a current context
    // to query for supported GL extensions.
    scoped_refptr<GLSurface> surface =
        new SurfacelessEGL(this, gfx::Size(1, 1));
    scoped_refptr<GLContext> context = InitializeGLContext(
        new GLContextEGL(nullptr), surface.get(), GLContextAttribs());
    if (!context || !context->MakeCurrent(surface.get()))
      egl_surfaceless_context_supported_ = false;

    // Ensure context supports GL_OES_surfaceless_context.
    if (egl_surfaceless_context_supported_) {
      egl_surfaceless_context_supported_ =
          context->HasExtension("GL_OES_surfaceless_context");
      context->ReleaseCurrent(surface.get());
    }
  }

  // The native fence sync extension is a bit complicated. It's reported as
  // present for ChromeOS, but Android currently doesn't report this extension
  // even when it's present, and older devices and Android emulator may export
  // a useless wrapper function. See crbug.com/775707 for details. In short, if
  // the symbol is present and we're on Android N or newer and we are not on
  // Android emulator, assume that it's usable even if the extension wasn't
  // reported. TODO(crbug.com/40132708): Once this is fixed at the
  // Android level, update the heuristic to trust the reported extension from
  // that version onward.
  // LINT.IfChange(AndroidSurfaceControlCondition)
  egl_android_native_fence_sync_supported_ =
      ext->b_EGL_ANDROID_native_fence_sync;
#if BUILDFLAG(IS_ANDROID)
  if (!egl_android_native_fence_sync_supported_ &&
      base::android::android_info::sdk_int() >=
          base::android::android_info::SDK_VERSION_NOUGAT &&
      g_driver_egl.fn.eglDupNativeFenceFDANDROIDFn &&
      base::SysInfo::GetAndroidHardwareEGL() != "swiftshader" &&
      base::SysInfo::GetAndroidHardwareEGL() != "emulation") {
    egl_android_native_fence_sync_supported_ = true;
  }
  UMA_HISTOGRAM_BOOLEAN("GPU.Android.HasEGLDupNativeFenceFunction",
                        !!g_driver_egl.fn.eglDupNativeFenceFDANDROIDFn);

  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDisableAndroidNativeFenceSyncForTesting)) {
    egl_android_native_fence_sync_supported_ = false;
  }
  // LINT.ThenChange(//gpu/config/gpu_finch_features.cc:AndroidSurfaceControlCondition)
#endif  // BUILDFLAG(IS_ANDROID)

  if (!for_testing) {
    if (ext->b_EGL_ANGLE_power_preference) {
      gpu_switching_observer_ =
          std::make_unique<EGLGpuSwitchingObserver>(display_);
      ui::GpuSwitchingManager::GetInstance()->AddObserver(
          gpu_switching_observer_.get());
    }
  }

#if BUILDFLAG(IS_APPLE)
  InitMetalSharedEventStorage();
#endif
}

}  // namespace gl