| // 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 <stddef.h> |
| |
| #include <string> |
| |
| #include "base/at_exit.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/memory/protected_memory.h" |
| #include "base/memory/protected_memory_cfi.h" |
| #include "base/stl_util.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 { |
| |
| namespace { |
| |
| const struct { |
| const char* name; |
| GLImplementation implementation; |
| } kGLImplementationNamePairs[] = { |
| {kGLImplementationDesktopName, kGLImplementationDesktopGL}, |
| {kGLImplementationSwiftShaderName, kGLImplementationSwiftShaderGL}, |
| #if defined(OS_MACOSX) |
| {kGLImplementationAppleName, kGLImplementationAppleGL}, |
| #endif |
| {kGLImplementationEGLName, kGLImplementationEGLGLES2}, |
| {kGLImplementationMockName, kGLImplementationMockGL}, |
| {kGLImplementationStubName, kGLImplementationStubGL}, |
| {kGLImplementationDisabledName, kGLImplementationDisabled}}; |
| |
| typedef std::vector<base::NativeLibrary> LibraryArray; |
| |
| GLImplementation g_gl_implementation = kGLImplementationNone; |
| LibraryArray* g_libraries; |
| // Place the function pointer for GetProcAddress in read-only memory after being |
| // resolved to prevent it being tampered with. See crbug.com/771365 for details. |
| PROTECTED_MEMORY_SECTION base::ProtectedMemory<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<bool*>(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<const char*>(api->glGetStringFn(extensions_enum)); |
| return extensions ? gfx::MakeExtensionSet(extensions) : gfx::ExtensionSet(); |
| } |
| |
| GLint num_extensions = 0; |
| api->glGetIntegervFn(num_extensions_enum, &num_extensions); |
| |
| std::vector<base::StringPiece> exts(num_extensions); |
| for (GLint i = 0; i < num_extensions; ++i) { |
| const char* extension = |
| reinterpret_cast<const char*>(api->glGetStringiFn(extensions_enum, i)); |
| DCHECK(extension != NULL); |
| exts[i] = extension; |
| } |
| return gfx::ExtensionSet(exts); |
| } |
| |
| } // namespace |
| |
| base::ThreadLocalPointer<CurrentGL>* 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 |
| |
| GLImplementation GetNamedGLImplementation(const std::string& name) { |
| for (size_t i = 0; i < base::size(kGLImplementationNamePairs); ++i) { |
| if (name == kGLImplementationNamePairs[i].name) |
| return kGLImplementationNamePairs[i].implementation; |
| } |
| |
| return kGLImplementationNone; |
| } |
| |
| GLImplementation GetSoftwareGLImplementation() { |
| return kGLImplementationSwiftShaderGL; |
| } |
| |
| const char* GetGLImplementationName(GLImplementation implementation) { |
| for (size_t i = 0; i < base::size(kGLImplementationNamePairs); ++i) { |
| if (implementation == kGLImplementationNamePairs[i].implementation) |
| return kGLImplementationNamePairs[i].name; |
| } |
| |
| return "unknown"; |
| } |
| |
| void SetGLImplementation(GLImplementation implementation) { |
| g_gl_implementation = implementation; |
| } |
| |
| GLImplementation GetGLImplementation() { |
| return g_gl_implementation; |
| } |
| |
| bool HasDesktopGLFeatures() { |
| return kGLImplementationDesktopGL == g_gl_implementation || |
| kGLImplementationDesktopGLCoreProfile == g_gl_implementation || |
| kGLImplementationAppleGL == g_gl_implementation; |
| } |
| |
| 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); |
| auto writer = base::AutoWritableMemory::Create(g_get_proc_address); |
| *g_get_proc_address = proc; |
| } |
| |
| GLFunctionPointerType GetGLProcAddress(const char* name) { |
| DCHECK(g_gl_implementation != kGLImplementationNone); |
| |
| if (g_libraries) { |
| for (size_t i = 0; i < g_libraries->size(); ++i) { |
| GLFunctionPointerType proc = reinterpret_cast<GLFunctionPointerType>( |
| base::GetFunctionPointerFromNativeLibrary((*g_libraries)[i], name)); |
| if (proc) |
| return proc; |
| } |
| } |
| if (*g_get_proc_address) { |
| GLFunctionPointerType proc = |
| base::UnsanitizedCfiCall(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<std::string>& disabled_extensions) { |
| if (extensions == NULL) |
| return ""; |
| |
| std::vector<base::StringPiece> extension_vec = base::SplitStringPiece( |
| extensions, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| |
| auto is_disabled = [&disabled_extensions](const base::StringPiece& ext) { |
| return base::ContainsValue(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<const char*>(api->glGetStringFn(GL_EXTENSIONS)); |
| return extensions ? std::string(extensions) : std::string(); |
| } |
| |
| GLint num_extensions = 0; |
| api->glGetIntegervFn(GL_NUM_EXTENSIONS, &num_extensions); |
| |
| std::vector<base::StringPiece> exts(num_extensions); |
| for (GLint i = 0; i < num_extensions; ++i) { |
| const char* extension = |
| reinterpret_cast<const char*>(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<const char*>(api->glGetStringFn(GL_VERSION)); |
| gfx::ExtensionSet extensions; |
| GLVersionInfo version_info(version_str, nullptr, 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; |
| } |
| |
| } // namespace gl |