blob: 12e100f6f917d4ddd778a23fd72d0ae6e481d2f7 [file] [log] [blame]
// Copyright 2016 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/init/gl_factory.h"
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gl/gl_share_group.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/gl_version_info.h"
#include "ui/gl/init/gl_initializer.h"
#if BUILDFLAG(IS_OZONE)
#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
namespace gl {
namespace init {
namespace {
bool g_is_angle_enabled = true;
bool ShouldFallbackToSoftwareGL() {
const base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
std::string requested_implementation_gl_name =
cmd->GetSwitchValueASCII(switches::kUseGL);
if (cmd->HasSwitch(switches::kUseGL) &&
requested_implementation_gl_name == "any") {
return true;
} else {
return false;
}
}
GLImplementationParts GetRequestedGLImplementation(
bool* fallback_to_software_gl) {
const base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
std::string requested_implementation_gl_name =
cmd->GetSwitchValueASCII(switches::kUseGL);
// If --use-angle was specified but --use-gl was not, assume --use-gl=angle
if (cmd->HasSwitch(switches::kUseANGLE) &&
!cmd->HasSwitch(switches::kUseGL)) {
requested_implementation_gl_name = kGLImplementationANGLEName;
}
if (requested_implementation_gl_name == kGLImplementationDisabledName) {
return GLImplementationParts(kGLImplementationDisabled);
}
std::vector<GLImplementationParts> allowed_impls =
GetAllowedGLImplementations();
if (GetGlWorkarounds().disable_es3gl_context_for_testing) {
GLVersionInfo::DisableES3ForTesting();
}
// If the passthrough command decoder is enabled, put ANGLE first if allowed
if (g_is_angle_enabled && UsePassthroughCommandDecoder(cmd)) {
std::vector<GLImplementationParts> angle_impls = {};
bool software_gl_allowed = false;
auto iter = allowed_impls.begin();
while (iter != allowed_impls.end()) {
if ((*iter) == GetSoftwareGLImplementation()) {
software_gl_allowed = true;
allowed_impls.erase(iter);
} else if (iter->gl == kGLImplementationEGLANGLE) {
angle_impls.emplace_back(*iter);
allowed_impls.erase(iter);
} else {
iter++;
}
}
allowed_impls.insert(allowed_impls.begin(), angle_impls.begin(),
angle_impls.end());
// Insert software implementations at the end, after all other hardware
// implementations
if (software_gl_allowed) {
allowed_impls.emplace_back(GetSoftwareGLImplementation());
}
}
if (allowed_impls.empty()) {
LOG(ERROR) << "List of allowed GL implementations is empty.";
return GLImplementationParts(kGLImplementationNone);
}
*fallback_to_software_gl = false;
absl::optional<GLImplementationParts> impl_from_cmdline =
GetRequestedGLImplementationFromCommandLine(cmd, fallback_to_software_gl);
// The default implementation is always the first one in list.
if (!impl_from_cmdline)
return allowed_impls[0];
if (IsSoftwareGLImplementation(*impl_from_cmdline))
return *impl_from_cmdline;
if (impl_from_cmdline->IsAllowed(allowed_impls))
return *impl_from_cmdline;
std::vector<std::string> allowed_impl_strs;
for (const auto& allowed_impl : allowed_impls) {
allowed_impl_strs.push_back(allowed_impl.ToString());
}
LOG(ERROR) << "Requested GL implementation " << impl_from_cmdline->ToString()
<< " not found in allowed implementations: ["
<< base::JoinString(allowed_impl_strs, ",") << "].";
return GLImplementationParts(kGLImplementationNone);
}
GLDisplay* InitializeGLOneOffPlatformHelper(bool init_extensions,
gl::GpuPreference gpu_preference) {
TRACE_EVENT1("gpu,startup", "gl::init::InitializeGLOneOffPlatformHelper",
"init_extensions", init_extensions);
bool fallback_to_software_gl = ShouldFallbackToSoftwareGL();
const base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
bool disable_gl_drawing = cmd->HasSwitch(switches::kDisableGLDrawingForTests);
return InitializeGLOneOffPlatformImplementation(
fallback_to_software_gl, disable_gl_drawing, init_extensions,
gpu_preference);
}
} // namespace
GLDisplay* InitializeGLOneOff(gl::GpuPreference gpu_preference) {
TRACE_EVENT0("gpu,startup", "gl::init::InitializeOneOff");
if (!InitializeStaticGLBindingsOneOff())
return nullptr;
if (GetGLImplementation() == kGLImplementationDisabled) {
return GetDefaultDisplayEGL();
}
return InitializeGLOneOffPlatformHelper(true, gpu_preference);
}
GLDisplay* InitializeGLNoExtensionsOneOff(bool init_bindings,
gl::GpuPreference gpu_preference) {
TRACE_EVENT1("gpu,startup", "gl::init::InitializeNoExtensionsOneOff",
"init_bindings", init_bindings);
if (init_bindings) {
if (!InitializeStaticGLBindingsOneOff())
return nullptr;
if (GetGLImplementation() == kGLImplementationDisabled) {
return GetDefaultDisplayEGL();
}
}
return InitializeGLOneOffPlatformHelper(false, gpu_preference);
}
bool InitializeStaticGLBindingsOneOff() {
DCHECK_EQ(kGLImplementationNone, GetGLImplementation());
bool fallback_to_software_gl = false;
GLImplementationParts impl =
GetRequestedGLImplementation(&fallback_to_software_gl);
if (impl.gl == kGLImplementationDisabled) {
SetGLImplementation(kGLImplementationDisabled);
return true;
} else if (impl.gl == kGLImplementationNone) {
return false;
}
return InitializeStaticGLBindingsImplementation(impl,
fallback_to_software_gl);
}
bool InitializeStaticGLBindingsImplementation(GLImplementationParts impl,
bool fallback_to_software_gl) {
if (IsSoftwareGLImplementation(impl))
fallback_to_software_gl = false;
bool initialized = InitializeStaticGLBindings(impl);
if (!initialized && fallback_to_software_gl) {
ShutdownGL(nullptr, /*due_to_fallback*/ true);
initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation());
}
if (!initialized) {
ShutdownGL(nullptr, /*due_to_fallback*/ false);
return false;
}
return true;
}
GLDisplay* InitializeGLOneOffPlatformImplementation(
bool fallback_to_software_gl,
bool disable_gl_drawing,
bool init_extensions,
gl::GpuPreference gpu_preference) {
if (IsSoftwareGLImplementation(GetGLImplementationParts()))
fallback_to_software_gl = false;
GLDisplay* display = InitializeGLOneOffPlatform(gpu_preference);
bool initialized = !!display;
if (!initialized) {
DVLOG(1) << "Initialization failed. Attempting to initialize default "
"GLDisplayEGL.";
RemoveGpuPreferenceEGL(gpu_preference);
display = InitializeGLOneOffPlatform(gl::GpuPreference::kDefault);
initialized = !!display;
}
if (!initialized && fallback_to_software_gl) {
ShutdownGL(nullptr, /*due_to_fallback=*/true);
if (InitializeStaticGLBindings(GetSoftwareGLImplementation())) {
display = InitializeGLOneOffPlatform(gpu_preference);
initialized = !!display;
}
}
if (initialized && init_extensions) {
initialized = InitializeExtensionSettingsOneOffPlatform(display);
}
if (!initialized) {
ShutdownGL(display, false);
return nullptr;
}
DVLOG(1) << "Using " << GetGLImplementationGLName(GetGLImplementationParts())
<< " GL implementation.";
SetNullDrawGLBindings(disable_gl_drawing);
return display;
}
GLDisplay* GetOrInitializeGLOneOffPlatformImplementation(
bool fallback_to_software_gl,
bool disable_gl_drawing,
bool init_extensions,
gl::GpuPreference gpu_preference) {
gl::GLDisplay* display = gl::GetDisplay(gpu_preference);
DCHECK(display);
if (display->IsInitialized()) {
return display;
}
display = gl::init::InitializeGLOneOffPlatformImplementation(
/*fallback_to_software_gl=*/false, /*disable_gl_drawing=*/false,
/*init_extensions=*/true,
/*gpu_preference=*/gpu_preference);
return display;
}
void ShutdownGL(GLDisplay* display, bool due_to_fallback) {
ShutdownGLPlatform(display);
UnloadGLNativeLibraries(due_to_fallback);
SetGLImplementation(kGLImplementationNone);
}
scoped_refptr<GLSurface> CreateOffscreenGLSurface(gl::GLDisplay* display,
const gfx::Size& size) {
return CreateOffscreenGLSurfaceWithFormat(display, size, GLSurfaceFormat());
}
void DisableANGLE() {
DCHECK_NE(GetGLImplementation(), kGLImplementationEGLANGLE);
g_is_angle_enabled = false;
}
} // namespace init
} // namespace gl