0
Files
src/ui/gl/gl_implementation.cc
Marc Treib 6b8a107585 Revert "Fix explicitly requesting D3D11 WARP by the command line."
This reverts commit b5ce6f437f.

Reason for revert: Flakily broke many content_browsertests on Fuchsia, see crbug.com/406167287

Original change's description:
> Fix explicitly requesting D3D11 WARP by the command line.
>
> The --use-angle=d3d11-warp flag was not working without also enabling
> the AllowD3D11WarpFallback feature, the flags for warp and swiftshader
> would both be added to the command line.
>
> Clean up logic for setting software GL command line arguments from the
> browser process and respect the requested GL implementation if one is
> provided by command line.
>
> Bug: 402163834
> Change-Id: I8fdea87be0ce957df6fd33cf3d8f0aed3891c4da
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6377726
> Reviewed-by: Zhenyao Mo <zmo@chromium.org>
> Commit-Queue: Geoff Lang <geofflang@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1436956}

Bug: 402163834, 406167287
Change-Id: I00fccd86edfdfee027c93811dd172fa033a52dcb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6387706
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Owners-Override: Marc Treib <treib@chromium.org>
Commit-Queue: Marc Treib <treib@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1437457}
2025-03-25 06:32:31 -07:00

487 lines
17 KiB
C++

// Copyright 2012 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_implementation.h"
#include <stddef.h>
#include <cstdlib>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "ui/gl/angle_implementation.h"
#include "ui/gl/buildflags.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_features.h"
#include "ui/gl/gl_gl_api_implementation.h"
#include "ui/gl/gl_version_info.h"
namespace gl {
bool GLImplementationParts::IsValid() const {
if (angle == ANGLEImplementation::kNone) {
return (gl != kGLImplementationEGLANGLE);
} else {
return (gl == kGLImplementationEGLANGLE);
}
}
bool GLImplementationParts::IsAllowed(
const std::vector<GLImplementationParts>& 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 "this"
// implementation is kDefault, and we see any ANGLE implementation in the
// list, then we allow "this" implementation, or vice versa, if "this" is
// any ANGLE implementation, and we see kDefault in the list, "this" is
// allowed.
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 == ANGLEImplementation::kDefault) {
return true;
} else if (angle == impl_iter.angle) {
return true;
}
} else if (gl == impl_iter.gl) {
return true;
}
}
return false;
}
std::string GLImplementationParts::ToString() const {
std::stringstream s;
s << "(gl=";
s << GLString();
s << ",angle=";
s << ANGLEString();
s << ")";
return s.str();
}
std::string GLImplementationParts::GLString() const {
switch (gl) {
case GLImplementation::kGLImplementationNone:
return "none";
case GLImplementation::kGLImplementationEGLGLES2:
return "egl-gles2";
case GLImplementation::kGLImplementationMockGL:
return "mock-gl";
case GLImplementation::kGLImplementationStubGL:
return "stub-gl";
case GLImplementation::kGLImplementationDisabled:
return "disabled";
case GLImplementation::kGLImplementationEGLANGLE:
return "egl-angle";
}
return "";
}
std::string GLImplementationParts::ANGLEString() const {
switch (angle) {
case ANGLEImplementation::kNone:
return "none";
case ANGLEImplementation::kD3D9:
return "d3d9";
case ANGLEImplementation::kD3D11:
return "d3d11";
case ANGLEImplementation::kOpenGL:
return "opengl";
case ANGLEImplementation::kOpenGLES:
return "opengles";
case ANGLEImplementation::kNull:
return "null";
case ANGLEImplementation::kVulkan:
return "vulkan";
case ANGLEImplementation::kSwiftShader:
return "swiftshader";
case ANGLEImplementation::kMetal:
return "metal";
case ANGLEImplementation::kD3D11Warp:
return "d3d11-warp";
case ANGLEImplementation::kDefault:
return "default";
}
return "";
}
namespace {
const struct {
const char* gl_name;
const char* angle_name;
GLImplementationParts implementation;
} kGLImplementationNamePairs[] = {
{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, kANGLEImplementationD3D11WarpName,
GLImplementationParts(ANGLEImplementation::kD3D11Warp)},
{kGLImplementationANGLEName, kANGLEImplementationD3D11WarpForWebGLName,
GLImplementationParts(ANGLEImplementation::kD3D11Warp)},
{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<base::NativeLibrary> LibraryArray;
GLImplementationParts g_gl_implementation =
GLImplementationParts(kGLImplementationNone);
LibraryArray* g_libraries;
GLGetProcAddressProc g_get_proc_address;
void CleanupNativeLibraries(void* due_to_fallback) {
if (g_libraries) {
#if BUILDFLAG(IS_MAC)
// Mac `NativeLibrary` is heap-allocated, so always unload to ensure they're
// freed.
for (auto* library : *g_libraries)
base::UnloadNativeLibrary(library);
#else
// 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);
}
#endif // BUILDFLAG(IS_MAC)
delete g_libraries;
g_libraries = nullptr;
}
}
gfx::ExtensionSet GetGLExtensionsFromCurrentContext(GLApi* api,
GLenum extensions_enum) {
const char* extensions =
reinterpret_cast<const char*>(api->glGetStringFn(extensions_enum));
return extensions ? gfx::MakeExtensionSet(extensions) : gfx::ExtensionSet();
}
} // namespace
EGLApi* g_current_egl_context;
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 GetSoftwareGLImplementation() {
#if BUILDFLAG(IS_WIN)
if (base::FeatureList::IsEnabled(features::kAllowD3D11WarpFallback)) {
return GLImplementationParts(ANGLEImplementation::kD3D11Warp);
}
#endif
return GLImplementationParts(ANGLEImplementation::kSwiftShader);
}
bool IsSoftwareGLImplementation(GLImplementationParts implementation) {
return implementation.angle == ANGLEImplementation::kSwiftShader ||
implementation.angle == ANGLEImplementation::kD3D11Warp;
}
GL_EXPORT bool IsSwiftShaderGLImplementation(
GLImplementationParts implementation) {
return implementation.angle == ANGLEImplementation::kSwiftShader;
}
void SetGLImplementationCommandLineSwitches(
const GLImplementationParts& implementation,
base::CommandLine* command_line) {
command_line->AppendSwitchASCII(
switches::kUseGL, gl::GetGLImplementationGLName(implementation));
command_line->AppendSwitchASCII(
switches::kUseANGLE, gl::GetGLImplementationANGLEName(implementation));
}
void SetSoftwareGLCommandLineSwitches(base::CommandLine* command_line) {
GLImplementationParts implementation = GetSoftwareGLImplementation();
SetGLImplementationCommandLineSwitches(implementation, command_line);
}
void SetSoftwareWebGLCommandLineSwitches(base::CommandLine* command_line) {
#if BUILDFLAG(IS_WIN)
if (base::FeatureList::IsEnabled(features::kAllowD3D11WarpFallback)) {
command_line->AppendSwitchASCII(switches::kUseGL,
kGLImplementationANGLEName);
command_line->AppendSwitchASCII(switches::kUseANGLE,
kANGLEImplementationD3D11WarpForWebGLName);
return;
}
#endif
command_line->AppendSwitchASCII(switches::kUseGL, kGLImplementationANGLEName);
command_line->AppendSwitchASCII(switches::kUseANGLE,
kANGLEImplementationSwiftShaderForWebGLName);
}
std::optional<GLImplementationParts>
GetRequestedGLImplementationFromCommandLine(
const base::CommandLine* command_line) {
bool overrideUseSoftwareGL =
command_line->HasSwitch(switches::kOverrideUseSoftwareGLForTests);
#if BUILDFLAG(IS_LINUX) || \
(BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
if (std::getenv("RUNNING_UNDER_RR")) {
// https://rr-project.org/ is a Linux-only record-and-replay debugger that
// is unhappy when things like GPU drivers write directly into the
// process's address space. Using swiftshader helps ensure that doesn't
// happen and keeps Chrome and linux-chromeos usable with rr.
overrideUseSoftwareGL = true;
}
#endif
if (overrideUseSoftwareGL) {
return GetSoftwareGLImplementation();
}
if (!command_line->HasSwitch(switches::kUseGL) &&
!command_line->HasSwitch(switches::kUseANGLE)) {
return std::nullopt;
}
std::string gl_name = command_line->GetSwitchValueASCII(switches::kUseGL);
std::string angle_name =
command_line->GetSwitchValueASCII(switches::kUseANGLE);
// If --use-angle was specified but --use-gl was not, assume --use-gl=angle
if (command_line->HasSwitch(switches::kUseANGLE) &&
!command_line->HasSwitch(switches::kUseGL)) {
gl_name = kGLImplementationANGLEName;
}
return GetNamedGLImplementation(gl_name, angle_name);
}
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;
}
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")
STDCALL 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<GLFunctionPointerType>(
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 SetNullDrawGLBindings(bool enabled) {
SetNullDrawGLBindingsEnabled(enabled);
}
bool HasInitializedNullDrawGLBindings() {
return GetNullDrawBindingsEnabled();
}
std::string FilterGLExtensionList(
const char* extensions,
const std::vector<std::string>& disabled_extensions) {
if (extensions == NULL)
return "";
std::vector<std::string_view> extension_vec = base::SplitStringPiece(
extensions, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
auto is_disabled = [&disabled_extensions](std::string_view ext) {
return base::Contains(disabled_extensions, ext);
};
std::erase_if(extension_vec, is_disabled);
return base::JoinString(extension_vec, " ");
}
DisableNullDrawGLBindings::DisableNullDrawGLBindings() {
initial_enabled_ = SetNullDrawGLBindingsEnabled(false);
}
DisableNullDrawGLBindings::~DisableNullDrawGLBindings() {
SetNullDrawGLBindingsEnabled(initial_enabled_);
}
GLWindowSystemBindingInfo::GLWindowSystemBindingInfo() = default;
GLWindowSystemBindingInfo::~GLWindowSystemBindingInfo() = default;
std::string GetGLExtensionsFromCurrentContext() {
return GetGLExtensionsFromCurrentContext(g_current_gl_context);
}
std::string GetGLExtensionsFromCurrentContext(GLApi* api) {
const char* extensions =
reinterpret_cast<const char*>(api->glGetStringFn(GL_EXTENSIONS));
return extensions ? std::string(extensions) : std::string();
}
gfx::ExtensionSet GetRequestableGLExtensionsFromCurrentContext() {
return GetRequestableGLExtensionsFromCurrentContext(g_current_gl_context);
}
gfx::ExtensionSet GetRequestableGLExtensionsFromCurrentContext(GLApi* api) {
return GetGLExtensionsFromCurrentContext(api,
GL_REQUESTABLE_EXTENSIONS_ANGLE);
}
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