// Copyright (c) 2012 The Chromium 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 "ui/gl/gl_implementation.h" #include #include #include "base/at_exit.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/containers/contains.h" #include "base/containers/cxx20_erase.h" #include "base/logging.h" #include "base/strings/string_piece.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "build/build_config.h" #include "ui/gl/buildflags.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_gl_api_implementation.h" #include "ui/gl/gl_version_info.h" namespace gl { ANGLEImplementation MakeANGLEImplementation( const GLImplementation gl_impl, const ANGLEImplementation angle_impl) { if (gl_impl == kGLImplementationEGLANGLE) { if (angle_impl == ANGLEImplementation::kNone) { return ANGLEImplementation::kDefault; } else { return angle_impl; } } else { return ANGLEImplementation::kNone; } } GLImplementationParts::GLImplementationParts( const ANGLEImplementation angle_impl) : gl(kGLImplementationEGLANGLE), angle(MakeANGLEImplementation(kGLImplementationEGLANGLE, angle_impl)) {} GLImplementationParts::GLImplementationParts(const GLImplementation gl_impl) : gl(gl_impl), angle(MakeANGLEImplementation(gl_impl, ANGLEImplementation::kDefault)) {} bool GLImplementationParts::IsValid() const { if (angle == ANGLEImplementation::kNone) { return (gl != kGLImplementationEGLANGLE); } else { return (gl == kGLImplementationEGLANGLE); } } bool GLImplementationParts::IsAllowed( const std::vector& allowed_impls) const { // Given a vector of GLImplementationParts, this function checks if "this" // GLImplementation is found in the list, with a special case where if the // list contains ANGLE/kDefault, "this" may be any ANGLE implementation. for (const GLImplementationParts& impl_iter : allowed_impls) { if (gl == kGLImplementationEGLANGLE && impl_iter.gl == kGLImplementationEGLANGLE) { if (impl_iter.angle == ANGLEImplementation::kDefault) { return true; } else if (angle == impl_iter.angle) { return true; } } else if (gl == impl_iter.gl) { return true; } } return false; } namespace { const struct { const char* gl_name; const char* angle_name; GLImplementationParts implementation; } kGLImplementationNamePairs[] = { {kGLImplementationDesktopName, kANGLEImplementationNoneName, GLImplementationParts(kGLImplementationDesktopGL)}, {kGLImplementationCoreProfileName, kANGLEImplementationNoneName, GLImplementationParts(kGLImplementationDesktopGLCoreProfile)}, {kGLImplementationSwiftShaderName, kANGLEImplementationNoneName, GLImplementationParts(kGLImplementationSwiftShaderGL)}, #if defined(OS_APPLE) {kGLImplementationAppleName, kANGLEImplementationNoneName, GLImplementationParts(kGLImplementationAppleGL)}, #endif {kGLImplementationEGLName, kANGLEImplementationNoneName, GLImplementationParts(kGLImplementationEGLGLES2)}, {kGLImplementationANGLEName, kANGLEImplementationNoneName, GLImplementationParts(ANGLEImplementation::kDefault)}, {kGLImplementationANGLEName, kANGLEImplementationDefaultName, GLImplementationParts(ANGLEImplementation::kDefault)}, {kGLImplementationANGLEName, kANGLEImplementationD3D9Name, GLImplementationParts(ANGLEImplementation::kD3D9)}, {kGLImplementationANGLEName, kANGLEImplementationD3D11Name, GLImplementationParts(ANGLEImplementation::kD3D11)}, {kGLImplementationANGLEName, kANGLEImplementationD3D11on12Name, GLImplementationParts(ANGLEImplementation::kD3D11)}, {kGLImplementationANGLEName, kANGLEImplementationD3D11NULLName, GLImplementationParts(ANGLEImplementation::kD3D11)}, {kGLImplementationANGLEName, kANGLEImplementationOpenGLName, GLImplementationParts(ANGLEImplementation::kOpenGL)}, {kGLImplementationANGLEName, kANGLEImplementationOpenGLEGLName, GLImplementationParts(ANGLEImplementation::kOpenGL)}, {kGLImplementationANGLEName, kANGLEImplementationOpenGLNULLName, GLImplementationParts(ANGLEImplementation::kOpenGL)}, {kGLImplementationANGLEName, kANGLEImplementationOpenGLESName, GLImplementationParts(ANGLEImplementation::kOpenGLES)}, {kGLImplementationANGLEName, kANGLEImplementationOpenGLESEGLName, GLImplementationParts(ANGLEImplementation::kOpenGLES)}, {kGLImplementationANGLEName, kANGLEImplementationOpenGLESNULLName, GLImplementationParts(ANGLEImplementation::kOpenGLES)}, {kGLImplementationANGLEName, kANGLEImplementationVulkanName, GLImplementationParts(ANGLEImplementation::kVulkan)}, {kGLImplementationANGLEName, kANGLEImplementationVulkanNULLName, GLImplementationParts(ANGLEImplementation::kVulkan)}, {kGLImplementationANGLEName, kANGLEImplementationMetalName, GLImplementationParts(ANGLEImplementation::kMetal)}, {kGLImplementationANGLEName, kANGLEImplementationMetalNULLName, GLImplementationParts(ANGLEImplementation::kMetal)}, {kGLImplementationANGLEName, kANGLEImplementationSwiftShaderName, GLImplementationParts(ANGLEImplementation::kSwiftShader)}, {kGLImplementationANGLEName, kANGLEImplementationSwiftShaderForWebGLName, GLImplementationParts(ANGLEImplementation::kSwiftShader)}, {kGLImplementationANGLEName, kANGLEImplementationNullName, GLImplementationParts(ANGLEImplementation::kNull)}, {kGLImplementationMockName, kANGLEImplementationNoneName, GLImplementationParts(kGLImplementationMockGL)}, {kGLImplementationStubName, kANGLEImplementationNoneName, GLImplementationParts(kGLImplementationStubGL)}, {kGLImplementationDisabledName, kANGLEImplementationNoneName, GLImplementationParts(kGLImplementationDisabled)}}; typedef std::vector LibraryArray; GLImplementationParts g_gl_implementation = GLImplementationParts(kGLImplementationNone); LibraryArray* g_libraries; GLGetProcAddressProc g_get_proc_address; void CleanupNativeLibraries(void* due_to_fallback) { if (g_libraries) { // We do not call base::UnloadNativeLibrary() for these libraries as // unloading libGL without closing X display is not allowed. See // https://crbug.com/250813 for details. // However, if we fallback to a software renderer (e.g., SwiftShader), // then the above concern becomes irrelevant. // During fallback from ANGLE to SwiftShader ANGLE library needs to // be unloaded, otherwise software SwiftShader loading will fail. See // https://crbug.com/760063 for details. // During fallback from VMware mesa to SwiftShader mesa libraries need // to be unloaded. See https://crbug.com/852537 for details. if (due_to_fallback && *static_cast(due_to_fallback)) { for (auto* library : *g_libraries) base::UnloadNativeLibrary(library); } delete g_libraries; g_libraries = nullptr; } } gfx::ExtensionSet GetGLExtensionsFromCurrentContext( GLApi* api, GLenum extensions_enum, GLenum num_extensions_enum) { if (WillUseGLGetStringForExtensions(api)) { const char* extensions = reinterpret_cast(api->glGetStringFn(extensions_enum)); return extensions ? gfx::MakeExtensionSet(extensions) : gfx::ExtensionSet(); } GLint num_extensions = 0; api->glGetIntegervFn(num_extensions_enum, &num_extensions); std::vector exts(num_extensions); for (GLint i = 0; i < num_extensions; ++i) { const char* extension = reinterpret_cast(api->glGetStringiFn(extensions_enum, i)); DCHECK(extension != NULL); exts[i] = extension; } return gfx::ExtensionSet(exts); } } // namespace base::ThreadLocalPointer* g_current_gl_context_tls = NULL; #if defined(USE_EGL) EGLApi* g_current_egl_context; #endif #if defined(OS_WIN) WGLApi* g_current_wgl_context; #endif #if defined(USE_GLX) GLXApi* g_current_glx_context; #endif GLImplementationParts GetNamedGLImplementation(const std::string& gl_name, const std::string& angle_name) { for (auto name_pair : kGLImplementationNamePairs) { if (gl_name == name_pair.gl_name && angle_name == name_pair.angle_name) return name_pair.implementation; } return GLImplementationParts(kGLImplementationNone); } GLImplementationParts GetLegacySoftwareGLImplementation() { return GLImplementationParts(kGLImplementationSwiftShaderGL); } GLImplementationParts GetSoftwareGLImplementation() { return GLImplementationParts(ANGLEImplementation::kSwiftShader); } bool IsSoftwareGLImplementation(GLImplementationParts implementation) { return (implementation == GetLegacySoftwareGLImplementation()) || (implementation == GetSoftwareGLImplementation()); } void SetSoftwareGLCommandLineSwitches(base::CommandLine* command_line, bool legacy_software_gl) { if (legacy_software_gl) { command_line->AppendSwitchASCII( switches::kUseGL, gl::GetGLImplementationGLName(gl::GetLegacySoftwareGLImplementation())); } else { GLImplementationParts implementation = GetSoftwareGLImplementation(); command_line->AppendSwitchASCII( switches::kUseGL, gl::GetGLImplementationGLName(implementation)); command_line->AppendSwitchASCII( switches::kUseANGLE, gl::GetGLImplementationANGLEName(implementation)); } } void SetSoftwareWebGLCommandLineSwitches(base::CommandLine* command_line, bool legacy_software_gl) { if (legacy_software_gl) { command_line->AppendSwitchASCII(switches::kUseGL, kGLImplementationSwiftShaderForWebGLName); } else { command_line->AppendSwitchASCII(switches::kUseGL, kGLImplementationANGLEName); command_line->AppendSwitchASCII( switches::kUseANGLE, kANGLEImplementationSwiftShaderForWebGLName); } } const char* GetGLImplementationGLName(GLImplementationParts implementation) { for (auto name_pair : kGLImplementationNamePairs) { if (implementation.gl == name_pair.implementation.gl && implementation.angle == name_pair.implementation.angle) return name_pair.gl_name; } return "unknown"; } const char* GetGLImplementationANGLEName(GLImplementationParts implementation) { for (auto name_pair : kGLImplementationNamePairs) { if (implementation.gl == name_pair.implementation.gl && implementation.angle == name_pair.implementation.angle) return name_pair.angle_name; } return "not defined"; } void SetGLImplementationParts(const GLImplementationParts& implementation) { DCHECK(implementation.IsValid()); g_gl_implementation = GLImplementationParts(implementation); } const GLImplementationParts& GetGLImplementationParts() { return g_gl_implementation; } void SetGLImplementation(GLImplementation implementation) { g_gl_implementation = GLImplementationParts(implementation); DCHECK(g_gl_implementation.IsValid()); } GLImplementation GetGLImplementation() { return g_gl_implementation.gl; } void SetANGLEImplementation(ANGLEImplementation implementation) { g_gl_implementation = GLImplementationParts(implementation); DCHECK(g_gl_implementation.IsValid()); } ANGLEImplementation GetANGLEImplementation() { return g_gl_implementation.angle; } bool HasDesktopGLFeatures() { return kGLImplementationDesktopGL == g_gl_implementation.gl || kGLImplementationDesktopGLCoreProfile == g_gl_implementation.gl || kGLImplementationAppleGL == g_gl_implementation.gl; } void AddGLNativeLibrary(base::NativeLibrary library) { DCHECK(library); if (!g_libraries) { g_libraries = new LibraryArray; base::AtExitManager::RegisterCallback(CleanupNativeLibraries, NULL); } g_libraries->push_back(library); } void UnloadGLNativeLibraries(bool due_to_fallback) { CleanupNativeLibraries(&due_to_fallback); } void SetGLGetProcAddressProc(GLGetProcAddressProc proc) { DCHECK(proc); g_get_proc_address = proc; } NO_SANITIZE("cfi-icall") GLFunctionPointerType GetGLProcAddress(const char* name) { DCHECK(g_gl_implementation.gl != kGLImplementationNone); if (g_libraries) { for (size_t i = 0; i < g_libraries->size(); ++i) { GLFunctionPointerType proc = reinterpret_cast( base::GetFunctionPointerFromNativeLibrary((*g_libraries)[i], name)); if (proc) return proc; } } if (g_get_proc_address) { GLFunctionPointerType proc = g_get_proc_address(name); if (proc) return proc; } return NULL; } void InitializeNullDrawGLBindings() { SetNullDrawGLBindingsEnabled(true); } bool HasInitializedNullDrawGLBindings() { return GetNullDrawBindingsEnabled(); } std::string FilterGLExtensionList( const char* extensions, const std::vector& disabled_extensions) { if (extensions == NULL) return ""; std::vector extension_vec = base::SplitStringPiece( extensions, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); auto is_disabled = [&disabled_extensions](const base::StringPiece& ext) { return base::Contains(disabled_extensions, ext); }; base::EraseIf(extension_vec, is_disabled); return base::JoinString(extension_vec, " "); } DisableNullDrawGLBindings::DisableNullDrawGLBindings() { initial_enabled_ = SetNullDrawGLBindingsEnabled(false); } DisableNullDrawGLBindings::~DisableNullDrawGLBindings() { SetNullDrawGLBindingsEnabled(initial_enabled_); } GLWindowSystemBindingInfo::GLWindowSystemBindingInfo() {} GLWindowSystemBindingInfo::~GLWindowSystemBindingInfo() {} std::string GetGLExtensionsFromCurrentContext() { return GetGLExtensionsFromCurrentContext(g_current_gl_context); } std::string GetGLExtensionsFromCurrentContext(GLApi* api) { if (WillUseGLGetStringForExtensions(api)) { const char* extensions = reinterpret_cast(api->glGetStringFn(GL_EXTENSIONS)); return extensions ? std::string(extensions) : std::string(); } GLint num_extensions = 0; api->glGetIntegervFn(GL_NUM_EXTENSIONS, &num_extensions); std::vector exts(num_extensions); for (GLint i = 0; i < num_extensions; ++i) { const char* extension = reinterpret_cast(api->glGetStringiFn(GL_EXTENSIONS, i)); DCHECK(extension != NULL); exts[i] = extension; } return base::JoinString(exts, " "); } gfx::ExtensionSet GetRequestableGLExtensionsFromCurrentContext() { return GetRequestableGLExtensionsFromCurrentContext(g_current_gl_context); } gfx::ExtensionSet GetRequestableGLExtensionsFromCurrentContext(GLApi* api) { return GetGLExtensionsFromCurrentContext(api, GL_REQUESTABLE_EXTENSIONS_ANGLE, GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE); } bool WillUseGLGetStringForExtensions() { return WillUseGLGetStringForExtensions(g_current_gl_context); } bool WillUseGLGetStringForExtensions(GLApi* api) { const char* version_str = reinterpret_cast(api->glGetStringFn(GL_VERSION)); const char* renderer_str = reinterpret_cast(api->glGetStringFn(GL_RENDERER)); gfx::ExtensionSet extensions; GLVersionInfo version_info(version_str, renderer_str, extensions); return version_info.is_es || version_info.major_version < 3; } base::NativeLibrary LoadLibraryAndPrintError( const base::FilePath::CharType* filename) { return LoadLibraryAndPrintError(base::FilePath(filename)); } base::NativeLibrary LoadLibraryAndPrintError(const base::FilePath& filename) { base::NativeLibraryLoadError error; base::NativeLibrary library = base::LoadNativeLibrary(filename, &error); if (!library) { LOG(ERROR) << "Failed to load " << filename.MaybeAsASCII() << ": " << error.ToString(); return NULL; } return library; } #if BUILDFLAG(USE_OPENGL_APITRACE) void TerminateFrame() { GetGLProcAddress("glFrameTerminatorGREMEDY")(); } #endif } // namespace gl