| // 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/system/sys_info.h" |
| #include "build/build_config.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_implementation.h" |
| #include "ui/gl/gl_surface.h" |
| |
| #if BUILDFLAG(IS_OZONE) |
| #include "ui/ozone/buildflags.h" |
| #endif // BUILDFLAG(IS_OZONE) |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include "base/android/build_info.h" |
| #endif |
| |
| // From ANGLE's egl/eglext.h. |
| |
| #ifndef EGL_ANGLE_platform_angle |
| #define EGL_ANGLE_platform_angle 1 |
| #define EGL_PLATFORM_ANGLE_ANGLE 0x3202 |
| #define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203 |
| #define EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE 0x3204 |
| #define EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE 0x3205 |
| #define EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE 0x3206 |
| #define EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE 0x3451 |
| #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE 0x3209 |
| #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_EGL_ANGLE 0x348E |
| #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE 0x320A |
| #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE 0x345E |
| #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE 0x3487 |
| #define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348F |
| #endif /* EGL_ANGLE_platform_angle */ |
| |
| #ifndef EGL_ANGLE_platform_angle_d3d |
| #define EGL_ANGLE_platform_angle_d3d 1 |
| #define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207 |
| #define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208 |
| #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE 0x320B |
| #define EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_REFERENCE_ANGLE 0x320C |
| #endif /* EGL_ANGLE_platform_angle_d3d */ |
| |
| #ifndef EGL_ANGLE_platform_angle_d3d_luid |
| #define EGL_ANGLE_platform_angle_d3d_luid 1 |
| #define EGL_PLATFORM_ANGLE_D3D_LUID_HIGH_ANGLE 0x34A0 |
| #define EGL_PLATFORM_ANGLE_D3D_LUID_LOW_ANGLE 0x34A1 |
| #endif /* EGL_ANGLE_platform_angle_d3d_luid */ |
| |
| #ifndef EGL_ANGLE_platform_angle_d3d11on12 |
| #define EGL_ANGLE_platform_angle_d3d11on12 1 |
| #define EGL_PLATFORM_ANGLE_D3D11ON12_ANGLE 0x3488 |
| #endif /* EGL_ANGLE_platform_angle_d3d11on12 */ |
| |
| #ifndef EGL_ANGLE_platform_angle_opengl |
| #define EGL_ANGLE_platform_angle_opengl 1 |
| #define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320D |
| #define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320E |
| #endif /* EGL_ANGLE_platform_angle_opengl */ |
| |
| #ifndef EGL_ANGLE_platform_angle_null |
| #define EGL_ANGLE_platform_angle_null 1 |
| #define EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE 0x33AE |
| #endif /* EGL_ANGLE_platform_angle_null */ |
| |
| #ifndef EGL_ANGLE_platform_angle_vulkan |
| #define EGL_ANGLE_platform_angle_vulkan 1 |
| #define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450 |
| #define EGL_PLATFORM_VULKAN_DISPLAY_MODE_HEADLESS_ANGLE 0x34A5 |
| #endif /* EGL_ANGLE_platform_angle_vulkan */ |
| |
| #ifndef EGL_ANGLE_platform_angle_metal |
| #define EGL_ANGLE_platform_angle_metal 1 |
| #define EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE 0x3489 |
| #endif /* EGL_ANGLE_platform_angle_metal */ |
| |
| #ifndef EGL_ANGLE_x11_visual |
| #define EGL_ANGLE_x11_visual 1 |
| #define EGL_X11_VISUAL_ID_ANGLE 0x33A3 |
| #endif /* EGL_ANGLE_x11_visual */ |
| |
| #ifndef EGL_ANGLE_direct_composition |
| #define EGL_ANGLE_direct_composition 1 |
| #define EGL_DIRECT_COMPOSITION_ANGLE 0x33A5 |
| #endif /* EGL_ANGLE_direct_composition */ |
| |
| #ifndef EGL_ANGLE_display_robust_resource_initialization |
| #define EGL_ANGLE_display_robust_resource_initialization 1 |
| #define EGL_DISPLAY_ROBUST_RESOURCE_INITIALIZATION_ANGLE 0x3453 |
| #endif /* EGL_ANGLE_display_robust_resource_initialization */ |
| |
| #ifndef EGL_ANGLE_display_power_preference |
| #define EGL_ANGLE_display_power_preference 1 |
| #define EGL_POWER_PREFERENCE_ANGLE 0x3482 |
| #define EGL_LOW_POWER_ANGLE 0x0001 |
| #define EGL_HIGH_POWER_ANGLE 0x0002 |
| #endif /* EGL_ANGLE_power_preference */ |
| |
| #ifndef EGL_ANGLE_platform_angle_device_id |
| #define EGL_ANGLE_platform_angle_device_id |
| #define EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE 0x34D6 |
| #define EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE 0x34D7 |
| #define EGL_PLATFORM_ANGLE_DISPLAY_KEY_ANGLE 0x34DC |
| #endif /* EGL_ANGLE_platform_angle_device_id */ |
| |
| // From ANGLE's egl/eglext.h. |
| #ifndef EGL_ANGLE_feature_control |
| #define EGL_ANGLE_feature_control 1 |
| #define EGL_FEATURE_NAME_ANGLE 0x3460 |
| #define EGL_FEATURE_CATEGORY_ANGLE 0x3461 |
| #define EGL_FEATURE_DESCRIPTION_ANGLE 0x3462 |
| #define EGL_FEATURE_BUG_ANGLE 0x3463 |
| #define EGL_FEATURE_STATUS_ANGLE 0x3464 |
| #define EGL_FEATURE_COUNT_ANGLE 0x3465 |
| #define EGL_FEATURE_OVERRIDES_ENABLED_ANGLE 0x3466 |
| #define EGL_FEATURE_OVERRIDES_DISABLED_ANGLE 0x3467 |
| #define EGL_FEATURE_ALL_DISABLED_ANGLE 0x3469 |
| #endif /* EGL_ANGLE_feature_control */ |
| |
| using ui::GetLastEGLErrorString; |
| |
| namespace gl { |
| |
| namespace { |
| |
| 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; |
| } |
| |
| std::vector<std::string> GetStringVectorFromCommandLine( |
| const base::CommandLine* command_line, |
| const char switch_name[]) { |
| std::string command_string = command_line->GetSwitchValueASCII(switch_name); |
| return base::SplitString(command_string, ", ;", base::TRIM_WHITESPACE, |
| base::SPLIT_WANT_NONEMPTY); |
| } |
| |
| 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) { |
| 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_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, |
| bool disable_all_angle_features, |
| uint64_t system_device_id, |
| DisplayKey display_key) { |
| std::vector<EGLAttrib> extra_display_attribs; |
| if (disable_all_angle_features) { |
| extra_display_attribs.push_back(EGL_FEATURE_ALL_DISABLED_ANGLE); |
| extra_display_attribs.push_back(EGL_TRUE); |
| } |
| 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_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_OZONE) |
| #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(OZONE_PLATFORM_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(OZONE_PLATFORM_X11) |
| #endif // BUILDFLAG(IS_OZONE) |
| 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(); |
| return EGL_NO_DISPLAY; |
| } |
| } |
| |
| 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_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_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(); |
| return "Err"; |
| } |
| } |
| |
| const char* GetDebugMessageTypeString(EGLint source) { |
| switch (source) { |
| case EGL_DEBUG_MSG_CRITICAL_KHR: |
| return "Critical"; |
| case EGL_DEBUG_MSG_ERROR_KHR: |
| return "Error"; |
| case EGL_DEBUG_MSG_WARN_KHR: |
| return "Warning"; |
| case EGL_DEBUG_MSG_INFO_KHR: |
| return "Info"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| void EGLAPIENTRY LogEGLDebugMessage(EGLenum error, |
| const char* command, |
| EGLint message_type, |
| EGLLabelKHR thread_label, |
| EGLLabelKHR object_label, |
| const char* message) { |
| std::string formatted_message = std::string("EGL Driver message (") + |
| GetDebugMessageTypeString(message_type) + |
| ") " + command + ": " + message; |
| |
| // Assume that all labels that have been set are strings |
| if (thread_label) { |
| formatted_message += " thread: "; |
| formatted_message += static_cast<const char*>(thread_label); |
| } |
| if (object_label) { |
| formatted_message += " object: "; |
| formatted_message += static_cast<const char*>(object_label); |
| } |
| |
| if (message_type == EGL_DEBUG_MSG_CRITICAL_KHR || |
| message_type == EGL_DEBUG_MSG_ERROR_KHR) { |
| LOG(ERROR) << formatted_message; |
| } else { |
| DVLOG(1) << formatted_message; |
| } |
| } |
| |
| 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(&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(); |
| break; |
| |
| case EGL: |
| #if defined(USE_EGL) |
| type_checked = std::is_same<GLDisplayPlatform, GLDisplayEGL>::value; |
| #endif // defined(USE_EGL) |
| break; |
| } |
| if (type_checked) |
| return static_cast<GLDisplayPlatform*>(this); |
| |
| return nullptr; |
| } |
| |
| #if defined(USE_EGL) |
| template EXPORT_TEMPLATE_DEFINE(GL_EXPORT) |
| GLDisplayEGL* GLDisplay::GetAs<GLDisplayEGL>(); |
| #endif // defined(USE_EGL) |
| |
| #if defined(USE_EGL) |
| GLDisplayEGL::EGLGpuSwitchingObserver::EGLGpuSwitchingObserver( |
| EGLDisplay display) |
| : display_(display) { |
| DCHECK(display != EGL_NO_DISPLAY); |
| } |
| |
| void GLDisplayEGL::EGLGpuSwitchingObserver::OnGpuSwitched( |
| GpuPreference active_gpu_heuristic) { |
| eglHandleGPUSwitchANGLE(display_); |
| } |
| |
| GLDisplayEGL::GLDisplayEGL(uint64_t system_device_id, DisplayKey display_key) |
| : GLDisplay(system_device_id, display_key, EGL), display_(EGL_NO_DISPLAY) { |
| ext = std::make_unique<DisplayExtensionsEGL>(); |
| } |
| |
| GLDisplayEGL::~GLDisplayEGL() = default; |
| |
| 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(); |
| } |
| |
| angle::ResetPlatform(display_); |
| 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) |
| CleanupMetalSharedEvent(); |
| #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; |
| } |
| |
| 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) { |
| 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(); |
| } |
| |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| |
| std::vector<std::string> enabled_angle_features = |
| GetStringVectorFromCommandLine(command_line, |
| switches::kEnableANGLEFeatures); |
| std::vector<std::string> disabled_angle_features = |
| GetStringVectorFromCommandLine(command_line, |
| switches::kDisableANGLEFeatures); |
| |
| bool disable_all_angle_features = |
| command_line->HasSwitch(switches::kDisableGpuDriverBugWorkarounds); |
| |
| 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, disable_all_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)) { |
| 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. |
| absl::optional<base::ScopedEnvironmentVariableOverride> unset_display; |
| if (display_type == ANGLE_VULKAN) { |
| unset_display = GLDisplayEglUtil::GetInstance() |
| ->MaybeGetScopedDisplayUnsetForVulkan(); |
| } |
| } |
| |
| 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) { |
| // 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(https://crbug.com/1086781): Once this is fixed at the |
| // Android level, update the heuristic to trust the reported extension from |
| // that version onward. |
| 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::BuildInfo::GetInstance()->sdk_int() >= |
| base::android::SDK_VERSION_NOUGAT && |
| g_driver_egl.fn.eglDupNativeFenceFDANDROIDFn && |
| base::SysInfo::GetAndroidHardwareEGL() != "swiftshader" && |
| base::SysInfo::GetAndroidHardwareEGL() != "emulation") { |
| egl_android_native_fence_sync_supported_ = true; |
| } |
| #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()); |
| } |
| } |
| } |
| #endif // defined(USE_EGL) |
| |
| } // namespace gl |