blob: 77c88c828ef695483683751a8431628b3b01781c [file] [log] [blame]
// 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