blob: 4d3aa8eb3c3726a663d1974d2d1eebd76aed57ec [file] [log] [blame]
// Copyright 2016 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 "gpu/ipc/service/gpu_init.h"
#include <string>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "build/chromecast_buildflags.h"
#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "gpu/command_buffer/service/service_utils.h"
#include "gpu/config/gpu_driver_bug_list.h"
#include "gpu/config/gpu_driver_bug_workaround_type.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_info_collector.h"
#include "gpu/config/gpu_switches.h"
#include "gpu/config/gpu_switching.h"
#include "gpu/config/gpu_util.h"
#include "gpu/ipc/service/gpu_watchdog_thread.h"
#include "gpu/ipc/service/gpu_watchdog_thread_v2.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/switches.h"
#include "ui/gl/buildflags.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface.h"
#include "ui/gl/gl_switches.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/init/gl_factory.h"
#if defined(OS_MAC)
#include <GLES2/gl2.h>
#endif
#if defined(USE_OZONE)
#include "ui/base/ui_base_features.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/surface_factory_ozone.h"
#endif
#if defined(OS_WIN)
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "ui/gl/direct_composition_surface_win.h"
#include "ui/gl/gl_surface_egl.h"
#endif
#if defined(OS_ANDROID)
#include "base/android/android_image_reader_compat.h"
#include "ui/gl/android/android_surface_control_compat.h"
#endif
#if BUILDFLAG(ENABLE_VULKAN)
#include "gpu/vulkan/init/vulkan_factory.h"
#include "gpu/vulkan/vulkan_implementation.h"
#include "gpu/vulkan/vulkan_instance.h"
#include "gpu/vulkan/vulkan_util.h"
#endif
namespace gpu {
namespace {
bool CollectGraphicsInfo(GPUInfo* gpu_info) {
DCHECK(gpu_info);
TRACE_EVENT0("gpu,startup", "Collect Graphics Info");
base::TimeTicks before_collect_context_graphics_info = base::TimeTicks::Now();
bool success = CollectContextGraphicsInfo(gpu_info);
if (!success)
LOG(ERROR) << "CollectGraphicsInfo failed.";
if (success) {
base::TimeDelta collect_context_time =
base::TimeTicks::Now() - before_collect_context_graphics_info;
UMA_HISTOGRAM_TIMES("GPU.CollectContextGraphicsInfo", collect_context_time);
}
return success;
}
void InitializePlatformOverlaySettings(GPUInfo* gpu_info,
const GpuFeatureInfo& gpu_feature_info) {
#if defined(OS_WIN)
// This has to be called after a context is created, active GPU is identified,
// and GPU driver bug workarounds are computed again. Otherwise the workaround
// |disable_direct_composition| may not be correctly applied.
// Also, this has to be called after falling back to SwiftShader decision is
// finalized because this function depends on GL is ANGLE's GLES or not.
if (gpu_feature_info.IsWorkaroundEnabled(
gpu::ENABLE_BGRA8_OVERLAYS_WITH_YUV_OVERLAY_SUPPORT)) {
gl::DirectCompositionSurfaceWin::EnableBGRA8OverlaysWithYUVOverlaySupport();
}
if (gpu_feature_info.IsWorkaroundEnabled(gpu::FORCE_NV12_OVERLAY_SUPPORT)) {
gl::DirectCompositionSurfaceWin::ForceNV12OverlaySupport();
}
DCHECK(gpu_info);
CollectHardwareOverlayInfo(&gpu_info->overlay_info);
#elif defined(OS_ANDROID)
if (gpu_info->gpu.vendor_string == "Qualcomm")
gl::SurfaceControl::EnableQualcommUBWC();
#endif
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !BUILDFLAG(IS_CHROMECAST)
bool CanAccessNvidiaDeviceFile() {
bool res = true;
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::WILL_BLOCK);
if (access("/dev/nvidiactl", R_OK) != 0) {
DVLOG(1) << "NVIDIA device file /dev/nvidiactl access denied";
res = false;
}
return res;
}
#endif // OS_LINUX && !OS_CHROMEOS && !BUILDFLAG(IS_CHROMECAST)
class GpuWatchdogInit {
public:
GpuWatchdogInit() = default;
~GpuWatchdogInit() {
if (watchdog_ptr_)
watchdog_ptr_->OnInitComplete();
}
void SetGpuWatchdogPtr(GpuWatchdogThread* ptr) { watchdog_ptr_ = ptr; }
private:
GpuWatchdogThread* watchdog_ptr_ = nullptr;
};
// TODO(https://crbug.com/1095744): We currently do not handle
// VK_ERROR_DEVICE_LOST in in-process-gpu.
void DisableInProcessGpuVulkan(GpuFeatureInfo* gpu_feature_info,
GpuPreferences* gpu_preferences) {
if (gpu_feature_info->status_values[GPU_FEATURE_TYPE_VULKAN] ==
kGpuFeatureStatusEnabled) {
LOG(ERROR) << "Vulkan not supported with in process gpu";
gpu_preferences->use_vulkan = VulkanImplementationName::kNone;
gpu_feature_info->status_values[GPU_FEATURE_TYPE_VULKAN] =
kGpuFeatureStatusDisabled;
if (gpu_preferences->gr_context_type == GrContextType::kVulkan)
gpu_preferences->gr_context_type = GrContextType::kGL;
}
}
#if BUILDFLAG(ENABLE_VULKAN)
bool MatchGLRenderer(const GPUInfo& gpu_info, const std::string& patterns) {
auto pattern_strings = base::SplitString(patterns, "|", base::TRIM_WHITESPACE,
base::SPLIT_WANT_ALL);
for (const auto& pattern : pattern_strings) {
if (base::MatchPattern(gpu_info.gl_renderer, pattern))
return true;
}
return false;
}
#endif // !BUILDFLAG(ENABLE_VULKAN)
} // namespace
GpuInit::GpuInit() = default;
GpuInit::~GpuInit() {
StopForceDiscreteGPU();
}
bool GpuInit::InitializeAndStartSandbox(base::CommandLine* command_line,
const GpuPreferences& gpu_preferences) {
gpu_preferences_ = gpu_preferences;
// Blocklist decisions based on basic GPUInfo may not be final. It might
// need more context based GPUInfo. In such situations, switching to
// SwiftShader needs to wait until creating a context.
bool needs_more_info = true;
#if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMECAST)
needs_more_info = false;
if (!PopGPUInfoCache(&gpu_info_)) {
CollectBasicGraphicsInfo(command_line, &gpu_info_);
}
#if defined(OS_WIN)
IntelGpuSeriesType intel_gpu_series_type = GetIntelGpuSeriesType(
gpu_info_.active_gpu().vendor_id, gpu_info_.active_gpu().device_id);
UMA_HISTOGRAM_ENUMERATION("GPU.IntelGpuSeriesType", intel_gpu_series_type);
#endif // OS_WIN
// Set keys for crash logging based on preliminary gpu info, in case we
// crash during feature collection.
SetKeysForCrashLogging(gpu_info_);
#if defined(SUBPIXEL_FONT_RENDERING_DISABLED)
gpu_info_.subpixel_font_rendering = false;
#else
gpu_info_.subpixel_font_rendering = true;
#endif
if (gpu_preferences_.enable_perf_data_collection) {
// This is only enabled on the info collection GPU process.
DevicePerfInfo device_perf_info;
CollectDevicePerfInfo(&device_perf_info, /*in_browser_process=*/false);
device_perf_info_ = device_perf_info;
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
if (gpu_info_.gpu.vendor_id == 0x10de && // NVIDIA
gpu_info_.gpu.driver_vendor == "NVIDIA" && !CanAccessNvidiaDeviceFile())
return false;
#endif
if (!PopGpuFeatureInfoCache(&gpu_feature_info_)) {
// Compute blocklist and driver bug workaround decisions based on basic GPU
// info.
gpu_feature_info_ = ComputeGpuFeatureInfo(gpu_info_, gpu_preferences_,
command_line, &needs_more_info);
}
#endif // !OS_ANDROID && !BUILDFLAG(IS_CHROMECAST)
gpu_info_.in_process_gpu = false;
gl_use_swiftshader_ = false;
// GL bindings may have already been initialized, specifically on MacOSX.
bool gl_initialized = gl::GetGLImplementation() != gl::kGLImplementationNone;
if (!gl_initialized) {
// If GL has already been initialized, then it's too late to select GPU.
if (SwitchableGPUsSupported(gpu_info_, *command_line)) {
InitializeSwitchableGPUs(
gpu_feature_info_.enabled_gpu_driver_bug_workarounds);
}
} else if (gl::GetGLImplementation() == gl::kGLImplementationSwiftShaderGL &&
command_line->GetSwitchValueASCII(switches::kUseGL) !=
gl::kGLImplementationSwiftShaderName) {
gl_use_swiftshader_ = true;
}
bool enable_watchdog = !gpu_preferences_.disable_gpu_watchdog &&
!command_line->HasSwitch(switches::kHeadless) &&
!gl_use_swiftshader_;
// Disable the watchdog in debug builds because they tend to only be run by
// developers who will not appreciate the watchdog killing the GPU process.
#ifndef NDEBUG
enable_watchdog = false;
#endif
// watchdog_init will call watchdog OnInitComplete() at the end of this
// function.
GpuWatchdogInit watchdog_init;
bool delayed_watchdog_enable = false;
#if defined(OS_CHROMEOS)
// Don't start watchdog immediately, to allow developers to switch to VT2 on
// startup.
delayed_watchdog_enable = true;
#endif
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// PreSandbox is mainly for resource handling and not related to the GPU
// driver, it doesn't need the GPU watchdog. The loadLibrary may take long
// time that killing and restarting the GPU process will not help.
if (gpu_preferences_.gpu_sandbox_start_early) {
// The sandbox will be started earlier than usual (i.e. before GL) so
// execute the pre-sandbox steps now.
sandbox_helper_->PreSandboxStartup();
}
#else
// For some reasons MacOSX's VideoToolbox might crash when called after
// initializing GL, see crbug.com/1047643 and crbug.com/871280. On other
// operating systems like Windows and Android the pre-sandbox steps have
// always been executed before initializing GL so keep it this way.
sandbox_helper_->PreSandboxStartup();
#endif
// Start the GPU watchdog only after anything that is expected to be time
// consuming has completed, otherwise the process is liable to be aborted.
if (enable_watchdog && !delayed_watchdog_enable) {
if (base::FeatureList::IsEnabled(features::kGpuWatchdogV2)) {
watchdog_thread_ = GpuWatchdogThreadImplV2::Create(
gpu_preferences_.watchdog_starts_backgrounded);
watchdog_init.SetGpuWatchdogPtr(watchdog_thread_.get());
} else {
watchdog_thread_ = GpuWatchdogThreadImplV1::Create(
gpu_preferences_.watchdog_starts_backgrounded);
}
#if defined(OS_WIN)
// This is a workaround for an occasional deadlock between watchdog and
// current thread. Watchdog hangs at thread initialization in
// __acrt_thread_attach() and current thread in std::setlocale(...)
// (during InitializeGLOneOff()). Source of the deadlock looks like an old
// UCRT bug that was supposed to be fixed in 10.0.10586 release of UCRT,
// but we might have come accross a not-yet-covered scenario.
// References:
// https://bugs.python.org/issue26624
// http://stackoverflow.com/questions/35572792/setlocale-stuck-on-windows
auto watchdog_started = watchdog_thread_->WaitUntilThreadStarted();
DCHECK(watchdog_started);
#endif // OS_WIN
}
bool attempted_startsandbox = false;
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// On Chrome OS ARM Mali, GPU driver userspace creates threads when
// initializing a GL context, so start the sandbox early.
// TODO(zmo): Need to collect OS version before this.
if (gpu_preferences_.gpu_sandbox_start_early) {
gpu_info_.sandboxed = sandbox_helper_->EnsureSandboxInitialized(
watchdog_thread_.get(), &gpu_info_, gpu_preferences_);
attempted_startsandbox = true;
}
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
base::TimeTicks before_initialize_one_off = base::TimeTicks::Now();
#if defined(USE_OZONE)
// Initialize Ozone GPU after the watchdog in case it hangs. The sandbox
// may also have started at this point.
std::vector<gfx::BufferFormat> supported_buffer_formats_for_texturing;
if (features::IsUsingOzonePlatform()) {
ui::OzonePlatform::InitParams params;
params.single_process = false;
ui::OzonePlatform::InitializeForGPU(params);
// We need to get supported formats before sandboxing to avoid an known
// issue which breaks the camera preview. (b/166850715)
supported_buffer_formats_for_texturing =
ui::OzonePlatform::GetInstance()
->GetSurfaceFactoryOzone()
->GetSupportedFormatsForTexturing();
}
#endif
if (!gl_use_swiftshader_) {
gl_use_swiftshader_ = EnableSwiftShaderIfNeeded(
command_line, gpu_feature_info_,
gpu_preferences_.disable_software_rasterizer, needs_more_info);
}
if (gl_initialized && gl_use_swiftshader_ &&
gl::GetGLImplementation() != gl::kGLImplementationSwiftShaderGL) {
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
VLOG(1) << "Quit GPU process launch to fallback to SwiftShader cleanly "
<< "on Linux";
return false;
#else
gl::init::ShutdownGL(true);
gl_initialized = false;
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
}
if (!gl_initialized) {
// Pause watchdog. LoadLibrary in GLBindings may take long time.
if (watchdog_thread_)
watchdog_thread_->PauseWatchdog();
gl_initialized = gl::init::InitializeStaticGLBindingsOneOff();
if (!gl_initialized) {
VLOG(1) << "gl::init::InitializeStaticGLBindingsOneOff failed";
return false;
}
if (watchdog_thread_)
watchdog_thread_->ResumeWatchdog();
if (gl::GetGLImplementation() != gl::kGLImplementationDisabled) {
gl_initialized =
gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ false);
if (!gl_initialized) {
VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed";
return false;
}
}
}
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// The ContentSandboxHelper is currently the only one implementation of
// GpuSandboxHelper and it has no dependency. Except on Linux where
// VaapiWrapper checks the GL implementation to determine which display
// to use. So call PreSandboxStartup after GL initialization. But make
// sure the watchdog is paused as loadLibrary may take a long time and
// restarting the GPU process will not help.
if (!attempted_startsandbox) {
if (watchdog_thread_)
watchdog_thread_->PauseWatchdog();
// The sandbox is not started yet.
sandbox_helper_->PreSandboxStartup();
if (watchdog_thread_)
watchdog_thread_->ResumeWatchdog();
}
#endif
// On MacOS, the default texture target for native GpuMemoryBuffers is
// GL_TEXTURE_RECTANGLE_ARB. This is due to CGL's requirements for creating
// a GL surface. However, when ANGLE is used on top of SwiftShader or Metal,
// it's necessary to use GL_TEXTURE_2D instead.
// TODO(crbug.com/1056312): The proper behavior is to check the config
// parameter set by the EGL_ANGLE_iosurface_client_buffer extension
#if defined(OS_MAC)
if (gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE &&
(gl::GetANGLEImplementation() == gl::ANGLEImplementation::kSwiftShader ||
gl::GetANGLEImplementation() == gl::ANGLEImplementation::kMetal)) {
SetMacOSSpecificTextureTarget(GL_TEXTURE_2D);
}
#endif // defined(OS_MAC)
bool gl_disabled = gl::GetGLImplementation() == gl::kGLImplementationDisabled;
// Compute passthrough decoder status before ComputeGpuFeatureInfo below.
// Do this after GL is initialized so extensions can be queried.
gpu_info_.passthrough_cmd_decoder =
gles2::UsePassthroughCommandDecoder(command_line) &&
gles2::PassthroughCommandDecoderSupported();
// We need to collect GL strings (VENDOR, RENDERER) for blacklisting purposes.
if (!gl_disabled) {
if (!gl_use_swiftshader_) {
if (!CollectGraphicsInfo(&gpu_info_))
return false;
SetKeysForCrashLogging(gpu_info_);
gpu_feature_info_ = ComputeGpuFeatureInfo(gpu_info_, gpu_preferences_,
command_line, nullptr);
gl_use_swiftshader_ = EnableSwiftShaderIfNeeded(
command_line, gpu_feature_info_,
gpu_preferences_.disable_software_rasterizer, false);
if (gl_use_swiftshader_) {
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
VLOG(1) << "Quit GPU process launch to fallback to SwiftShader cleanly "
<< "on Linux";
return false;
#else
gl::init::ShutdownGL(true);
watchdog_thread_ = nullptr;
watchdog_init.SetGpuWatchdogPtr(nullptr);
if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ true)) {
VLOG(1)
<< "gl::init::InitializeGLNoExtensionsOneOff with SwiftShader "
<< "failed";
return false;
}
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
}
} else { // gl_use_swiftshader_ == true
switch (gpu_preferences_.use_vulkan) {
case VulkanImplementationName::kNative: {
// Collect GPU info, so we can use blocklist to disable vulkan if it
// is needed.
GPUInfo gpu_info;
if (!CollectGraphicsInfo(&gpu_info))
return false;
auto gpu_feature_info = ComputeGpuFeatureInfo(
gpu_info, gpu_preferences_, command_line, nullptr);
gpu_feature_info_.status_values[GPU_FEATURE_TYPE_VULKAN] =
gpu_feature_info.status_values[GPU_FEATURE_TYPE_VULKAN];
break;
}
case VulkanImplementationName::kForcedNative:
case VulkanImplementationName::kSwiftshader:
gpu_feature_info_.status_values[GPU_FEATURE_TYPE_VULKAN] =
kGpuFeatureStatusEnabled;
break;
case VulkanImplementationName::kNone:
gpu_feature_info_.status_values[GPU_FEATURE_TYPE_VULKAN] =
kGpuFeatureStatusDisabled;
break;
}
}
}
if (gpu_feature_info_.status_values[GPU_FEATURE_TYPE_VULKAN] !=
kGpuFeatureStatusEnabled ||
!InitializeVulkan()) {
gpu_preferences_.use_vulkan = VulkanImplementationName::kNone;
gpu_feature_info_.status_values[GPU_FEATURE_TYPE_VULKAN] =
kGpuFeatureStatusDisabled;
if (gpu_preferences_.gr_context_type == GrContextType::kVulkan) {
#if defined(OS_FUCHSIA)
// Fuchsia uses ANGLE for GL which requires Vulkan, so don't fall
// back to GL if Vulkan init fails.
LOG(FATAL) << "Vulkan initialization failed";
#endif
gpu_preferences_.gr_context_type = GrContextType::kGL;
}
} else {
// TODO(https://crbug.com/1095744): It would be better to cleanly tear
// down and recreate the VkDevice on VK_ERROR_DEVICE_LOST. Until that
// happens, we will exit_on_context_lost to ensure there are no leaks.
gpu_feature_info_.enabled_gpu_driver_bug_workarounds.push_back(
EXIT_ON_CONTEXT_LOST);
}
// Collect GPU process info
if (!gl_disabled) {
if (!CollectGpuExtraInfo(&gpu_extra_info_, gpu_preferences))
return false;
}
if (!gl_disabled) {
if (!gpu_feature_info_.disabled_extensions.empty()) {
gl::init::SetDisabledExtensionsPlatform(
gpu_feature_info_.disabled_extensions);
}
if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) {
VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed";
return false;
}
default_offscreen_surface_ =
gl::init::CreateOffscreenGLSurface(gfx::Size());
if (!default_offscreen_surface_) {
VLOG(1) << "gl::init::CreateOffscreenGLSurface failed";
return false;
}
}
InitializePlatformOverlaySettings(&gpu_info_, gpu_feature_info_);
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Driver may create a compatibility profile context when collect graphics
// information on Linux platform. Try to collect graphics information
// based on core profile context after disabling platform extensions.
if (!gl_disabled && !gl_use_swiftshader_) {
if (!CollectGraphicsInfo(&gpu_info_))
return false;
SetKeysForCrashLogging(gpu_info_);
gpu_feature_info_ = ComputeGpuFeatureInfo(gpu_info_, gpu_preferences_,
command_line, nullptr);
gl_use_swiftshader_ = EnableSwiftShaderIfNeeded(
command_line, gpu_feature_info_,
gpu_preferences_.disable_software_rasterizer, false);
if (gl_use_swiftshader_) {
VLOG(1) << "Quit GPU process launch to fallback to SwiftShader cleanly "
<< "on Linux";
return false;
}
}
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
if (gl_use_swiftshader_) {
AdjustInfoToSwiftShader();
}
if (kGpuFeatureStatusEnabled !=
gpu_feature_info_
.status_values[GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE]) {
gpu_preferences_.disable_accelerated_video_decode = true;
}
base::TimeDelta initialize_one_off_time =
base::TimeTicks::Now() - before_initialize_one_off;
UMA_HISTOGRAM_MEDIUM_TIMES("GPU.InitializeOneOffMediumTime",
initialize_one_off_time);
// Software GL is expected to run slowly, so disable the watchdog
// in that case.
// In SwiftShader case, the implementation is actually EGLGLES2.
if (!gl_use_swiftshader_ && command_line->HasSwitch(switches::kUseGL)) {
std::string use_gl = command_line->GetSwitchValueASCII(switches::kUseGL);
if (use_gl == gl::kGLImplementationSwiftShaderName ||
use_gl == gl::kGLImplementationSwiftShaderForWebGLName) {
gl_use_swiftshader_ = true;
}
}
if (gl_use_swiftshader_ ||
gl::GetGLImplementation() == gl::GetSoftwareGLImplementation()) {
gpu_info_.software_rendering = true;
watchdog_thread_ = nullptr;
watchdog_init.SetGpuWatchdogPtr(nullptr);
} else if (gl_disabled) {
watchdog_thread_ = nullptr;
watchdog_init.SetGpuWatchdogPtr(nullptr);
} else if (enable_watchdog && delayed_watchdog_enable) {
if (base::FeatureList::IsEnabled(features::kGpuWatchdogV2)) {
watchdog_thread_ = GpuWatchdogThreadImplV2::Create(
gpu_preferences_.watchdog_starts_backgrounded);
watchdog_init.SetGpuWatchdogPtr(watchdog_thread_.get());
} else {
watchdog_thread_ = GpuWatchdogThreadImplV1::Create(
gpu_preferences_.watchdog_starts_backgrounded);
}
}
UMA_HISTOGRAM_ENUMERATION("GPU.GLImplementation", gl::GetGLImplementation());
if (!gpu_info_.sandboxed && !attempted_startsandbox) {
gpu_info_.sandboxed = sandbox_helper_->EnsureSandboxInitialized(
watchdog_thread_.get(), &gpu_info_, gpu_preferences_);
}
UMA_HISTOGRAM_BOOLEAN("GPU.Sandbox.InitializedSuccessfully",
gpu_info_.sandboxed);
init_successful_ = true;
#if defined(USE_OZONE)
if (features::IsUsingOzonePlatform()) {
ui::OzonePlatform::GetInstance()->AfterSandboxEntry();
gpu_feature_info_.supported_buffer_formats_for_allocation_and_texturing =
std::move(supported_buffer_formats_for_texturing);
}
#endif
if (!watchdog_thread_)
watchdog_init.SetGpuWatchdogPtr(nullptr);
#if defined(OS_WIN)
if (gpu_feature_info_.IsWorkaroundEnabled(DISABLE_DECODE_SWAP_CHAIN))
gl::DirectCompositionSurfaceWin::DisableDecodeSwapChain();
#endif
return true;
}
#if defined(OS_ANDROID)
void GpuInit::InitializeInProcess(base::CommandLine* command_line,
const GpuPreferences& gpu_preferences) {
gpu_preferences_ = gpu_preferences;
init_successful_ = true;
DCHECK(!EnableSwiftShaderIfNeeded(
command_line, gpu_feature_info_,
gpu_preferences_.disable_software_rasterizer, false));
InitializeGLThreadSafe(command_line, gpu_preferences_, &gpu_info_,
&gpu_feature_info_);
DisableInProcessGpuVulkan(&gpu_feature_info_, &gpu_preferences_);
default_offscreen_surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
UMA_HISTOGRAM_ENUMERATION("GPU.GLImplementation", gl::GetGLImplementation());
}
#else
void GpuInit::InitializeInProcess(base::CommandLine* command_line,
const GpuPreferences& gpu_preferences) {
gpu_preferences_ = gpu_preferences;
init_successful_ = true;
#if defined(USE_OZONE)
if (features::IsUsingOzonePlatform()) {
ui::OzonePlatform::InitParams params;
params.single_process = true;
ui::OzonePlatform::InitializeForGPU(params);
}
#endif
bool needs_more_info = true;
#if !BUILDFLAG(IS_CHROMECAST)
needs_more_info = false;
if (!PopGPUInfoCache(&gpu_info_)) {
CollectBasicGraphicsInfo(command_line, &gpu_info_);
}
#if defined(SUBPIXEL_FONT_RENDERING_DISABLED)
gpu_info_.subpixel_font_rendering = false;
#else
gpu_info_.subpixel_font_rendering = true;
#endif
if (!PopGpuFeatureInfoCache(&gpu_feature_info_)) {
gpu_feature_info_ = ComputeGpuFeatureInfo(gpu_info_, gpu_preferences_,
command_line, &needs_more_info);
}
if (SwitchableGPUsSupported(gpu_info_, *command_line)) {
InitializeSwitchableGPUs(
gpu_feature_info_.enabled_gpu_driver_bug_workarounds);
}
#endif // !BUILDFLAG(IS_CHROMECAST)
gl_use_swiftshader_ = EnableSwiftShaderIfNeeded(
command_line, gpu_feature_info_,
gpu_preferences_.disable_software_rasterizer, needs_more_info);
if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ true)) {
VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed";
return;
}
bool gl_disabled = gl::GetGLImplementation() == gl::kGLImplementationDisabled;
if (!gl_disabled && !gl_use_swiftshader_) {
CollectContextGraphicsInfo(&gpu_info_);
gpu_feature_info_ = ComputeGpuFeatureInfo(gpu_info_, gpu_preferences_,
command_line, nullptr);
gl_use_swiftshader_ = EnableSwiftShaderIfNeeded(
command_line, gpu_feature_info_,
gpu_preferences_.disable_software_rasterizer, false);
if (gl_use_swiftshader_) {
gl::init::ShutdownGL(true);
if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ true)) {
VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed "
<< "with SwiftShader";
return;
}
}
}
if (!gl_disabled) {
if (!gpu_feature_info_.disabled_extensions.empty()) {
gl::init::SetDisabledExtensionsPlatform(
gpu_feature_info_.disabled_extensions);
}
if (!gl::init::InitializeExtensionSettingsOneOffPlatform()) {
VLOG(1) << "gl::init::InitializeExtensionSettingsOneOffPlatform failed";
}
default_offscreen_surface_ =
gl::init::CreateOffscreenGLSurface(gfx::Size());
if (!default_offscreen_surface_) {
VLOG(1) << "gl::init::CreateOffscreenGLSurface failed";
}
}
InitializePlatformOverlaySettings(&gpu_info_, gpu_feature_info_);
#if defined(OS_LINUX) || defined(OS_CHROMEOS)
// Driver may create a compatibility profile context when collect graphics
// information on Linux platform. Try to collect graphics information
// based on core profile context after disabling platform extensions.
if (!gl_disabled && !gl_use_swiftshader_) {
CollectContextGraphicsInfo(&gpu_info_);
gpu_feature_info_ = ComputeGpuFeatureInfo(gpu_info_, gpu_preferences_,
command_line, nullptr);
gl_use_swiftshader_ = EnableSwiftShaderIfNeeded(
command_line, gpu_feature_info_,
gpu_preferences_.disable_software_rasterizer, false);
if (gl_use_swiftshader_) {
gl::init::ShutdownGL(true);
if (!gl::init::InitializeGLNoExtensionsOneOff(/*init_bindings*/ true)) {
VLOG(1) << "gl::init::InitializeGLNoExtensionsOneOff failed "
<< "with SwiftShader";
return;
}
}
}
#endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
if (gl_use_swiftshader_) {
AdjustInfoToSwiftShader();
}
#if defined(USE_OZONE)
if (features::IsUsingOzonePlatform()) {
const std::vector<gfx::BufferFormat>
supported_buffer_formats_for_texturing =
ui::OzonePlatform::GetInstance()
->GetSurfaceFactoryOzone()
->GetSupportedFormatsForTexturing();
gpu_feature_info_.supported_buffer_formats_for_allocation_and_texturing =
std::move(supported_buffer_formats_for_texturing);
}
#endif
DisableInProcessGpuVulkan(&gpu_feature_info_, &gpu_preferences_);
#if defined(OS_WIN)
if (gpu_feature_info_.IsWorkaroundEnabled(DISABLE_DECODE_SWAP_CHAIN))
gl::DirectCompositionSurfaceWin::DisableDecodeSwapChain();
#endif
UMA_HISTOGRAM_ENUMERATION("GPU.GLImplementation", gl::GetGLImplementation());
}
#endif // OS_ANDROID
void GpuInit::AdjustInfoToSwiftShader() {
gpu_info_for_hardware_gpu_ = gpu_info_;
gpu_info_.passthrough_cmd_decoder = false;
gpu_feature_info_for_hardware_gpu_ = gpu_feature_info_;
gpu_feature_info_ = ComputeGpuFeatureInfoForSwiftShader();
CollectContextGraphicsInfo(&gpu_info_);
DCHECK_EQ(gpu_info_.passthrough_cmd_decoder, false);
}
scoped_refptr<gl::GLSurface> GpuInit::TakeDefaultOffscreenSurface() {
return std::move(default_offscreen_surface_);
}
#if BUILDFLAG(ENABLE_VULKAN)
bool GpuInit::InitializeVulkan() {
DCHECK_EQ(gpu_feature_info_.status_values[GPU_FEATURE_TYPE_VULKAN],
kGpuFeatureStatusEnabled);
DCHECK_NE(gpu_preferences_.use_vulkan, VulkanImplementationName::kNone);
bool vulkan_use_swiftshader =
gpu_preferences_.use_vulkan == VulkanImplementationName::kSwiftshader;
bool forced_native =
gpu_preferences_.use_vulkan == VulkanImplementationName::kForcedNative;
bool use_swiftshader = gl_use_swiftshader_ || vulkan_use_swiftshader;
const bool enforce_protected_memory =
gpu_preferences_.enforce_vulkan_protected_memory;
vulkan_implementation_ = CreateVulkanImplementation(
vulkan_use_swiftshader,
enforce_protected_memory ? true : false /* allow_protected_memory */,
enforce_protected_memory);
if (!vulkan_implementation_ ||
!vulkan_implementation_->InitializeVulkanInstance(
!gpu_preferences_.disable_vulkan_surface)) {
DLOG(ERROR) << "Failed to create and initialize Vulkan implementation.";
vulkan_implementation_ = nullptr;
CHECK(!gpu_preferences_.disable_vulkan_fallback_to_gl_for_testing);
}
// Vulkan info is no longer collected in gpu/config/gpu_info_collector_win.cc
// Histogram GPU.SupportsVulkan and GPU.VulkanVersion were marked as expired.
// TODO(magchen): Add back these two histograms here and re-enable them in
// histograms.xml when we start Vulkan finch on Windows.
if (!vulkan_use_swiftshader) {
const bool supports_vulkan = !!vulkan_implementation_;
uint32_t vulkan_version = 0;
if (supports_vulkan) {
const auto& vulkan_info =
vulkan_implementation_->GetVulkanInstance()->vulkan_info();
vulkan_version = vulkan_info.used_api_version;
}
}
if (!vulkan_implementation_)
return false;
auto disable_patterns = base::GetFieldTrialParamValueByFeature(
features::kVulkan, "disable_by_gl_renderer");
if (MatchGLRenderer(gpu_info_, disable_patterns))
return false;
auto enable_patterns = base::GetFieldTrialParamValueByFeature(
features::kVulkan, "force_enable_by_gl_renderer");
forced_native |= MatchGLRenderer(gpu_info_, enable_patterns);
if (!use_swiftshader && !forced_native &&
!CheckVulkanCompabilities(
vulkan_implementation_->GetVulkanInstance()->vulkan_info(),
gpu_info_)) {
vulkan_implementation_.reset();
return false;
}
gpu_info_.vulkan_info =
vulkan_implementation_->GetVulkanInstance()->vulkan_info();
return true;
}
#else // BUILDFLAG(ENABLE_VULKAN)
bool GpuInit::InitializeVulkan() {
return false;
}
#endif // !BUILDFLAG(ENABLE_VULKAN)
} // namespace gpu