| Index: gpu/ipc/service/gpu_init.cc
|
| diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9dcc1c230afeeab996141085b6382d5b9ffd2513
|
| --- /dev/null
|
| +++ b/gpu/ipc/service/gpu_init.cc
|
| @@ -0,0 +1,352 @@
|
| +// 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 "base/lazy_instance.h"
|
| +#include "base/metrics/histogram.h"
|
| +#include "base/metrics/statistics_recorder.h"
|
| +#include "base/power_monitor/power_monitor.h"
|
| +#include "base/rand_util.h"
|
| +#include "base/run_loop.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
|
| +#include "base/threading/platform_thread.h"
|
| +#include "base/trace_event/trace_event.h"
|
| +#include "build/build_config.h"
|
| +#include "gpu/command_buffer/service/gpu_switches.h"
|
| +#include "gpu/config/gpu_driver_bug_workaround_type.h"
|
| +#include "gpu/config/gpu_info_collector.h"
|
| +#include "gpu/config/gpu_switches.h"
|
| +#include "gpu/config/gpu_util.h"
|
| +#include "gpu/ipc/common/gpu_memory_buffer_support.h"
|
| +#include "gpu/ipc/service/gpu_config.h"
|
| +#include "gpu/ipc/service/gpu_init_delegate.h"
|
| +#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
|
| +#include "gpu/ipc/service/gpu_watchdog_thread.h"
|
| +#include "ui/events/platform/platform_event_source.h"
|
| +#include "ui/gl/gl_context.h"
|
| +#include "ui/gl/gl_implementation.h"
|
| +#include "ui/gl/gl_surface.h"
|
| +#include "ui/gl/gl_switches.h"
|
| +#include "ui/gl/gpu_switching_manager.h"
|
| +#include "ui/gl/init/gl_factory.h"
|
| +
|
| +#if defined(OS_WIN)
|
| +#include <windows.h>
|
| +#include <dwmapi.h>
|
| +#endif
|
| +
|
| +#if defined(USE_X11)
|
| +#include "ui/base/x/x11_util.h" // nogncheck
|
| +#endif
|
| +
|
| +namespace gpu {
|
| +
|
| +namespace {
|
| +
|
| +base::LazyInstance<std::vector<GpuInitLogMessage>> deferred_messages =
|
| + LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +bool GpuProcessLogMessageHandler(int severity,
|
| + const char* file,
|
| + int line,
|
| + size_t message_start,
|
| + const std::string& str) {
|
| + std::string header = str.substr(0, message_start);
|
| + std::string message = str.substr(message_start);
|
| + deferred_messages.Get().push_back({severity, header, message});
|
| + return false;
|
| +}
|
| +
|
| +void GetGpuInfoFromCommandLine(gpu::GPUInfo* gpu_info,
|
| + const base::CommandLine& command_line) {
|
| + if (!command_line.HasSwitch(switches::kGpuVendorID) ||
|
| + !command_line.HasSwitch(switches::kGpuDeviceID) ||
|
| + !command_line.HasSwitch(switches::kGpuDriverVersion))
|
| + return;
|
| + bool success = base::HexStringToUInt(
|
| + command_line.GetSwitchValueASCII(switches::kGpuVendorID),
|
| + &gpu_info->gpu.vendor_id);
|
| + DCHECK(success);
|
| + success = base::HexStringToUInt(
|
| + command_line.GetSwitchValueASCII(switches::kGpuDeviceID),
|
| + &gpu_info->gpu.device_id);
|
| + DCHECK(success);
|
| + gpu_info->driver_vendor =
|
| + command_line.GetSwitchValueASCII(switches::kGpuDriverVendor);
|
| + gpu_info->driver_version =
|
| + command_line.GetSwitchValueASCII(switches::kGpuDriverVersion);
|
| + gpu_info->driver_date =
|
| + command_line.GetSwitchValueASCII(switches::kGpuDriverDate);
|
| + gpu::ParseSecondaryGpuDevicesFromCommandLine(command_line, gpu_info);
|
| +
|
| + // Set active gpu device.
|
| + if (command_line.HasSwitch(switches::kGpuActiveVendorID) &&
|
| + command_line.HasSwitch(switches::kGpuActiveDeviceID)) {
|
| + uint32_t active_vendor_id = 0;
|
| + uint32_t active_device_id = 0;
|
| + success = base::HexStringToUInt(
|
| + command_line.GetSwitchValueASCII(switches::kGpuActiveVendorID),
|
| + &active_vendor_id);
|
| + DCHECK(success);
|
| + success = base::HexStringToUInt(
|
| + command_line.GetSwitchValueASCII(switches::kGpuActiveDeviceID),
|
| + &active_device_id);
|
| + DCHECK(success);
|
| + if (gpu_info->gpu.vendor_id == active_vendor_id &&
|
| + gpu_info->gpu.device_id == active_device_id) {
|
| + gpu_info->gpu.active = true;
|
| + } else {
|
| + for (size_t i = 0; i < gpu_info->secondary_gpus.size(); ++i) {
|
| + if (gpu_info->secondary_gpus[i].vendor_id == active_vendor_id &&
|
| + gpu_info->secondary_gpus[i].device_id == active_device_id) {
|
| + gpu_info->secondary_gpus[i].active = true;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +#if !defined(OS_MACOSX)
|
| +bool CollectGraphicsInfo(gpu::GPUInfo* gpu_info) {
|
| + TRACE_EVENT0("gpu,startup", "Collect Graphics Info");
|
| +
|
| + bool res = true;
|
| + gpu::CollectInfoResult result = gpu::CollectContextGraphicsInfo(gpu_info);
|
| + switch (result) {
|
| + case gpu::kCollectInfoFatalFailure:
|
| + LOG(ERROR) << "gpu::CollectGraphicsInfo failed (fatal).";
|
| + res = false;
|
| + break;
|
| + case gpu::kCollectInfoNonFatalFailure:
|
| + DVLOG(1) << "gpu::CollectGraphicsInfo failed (non-fatal).";
|
| + break;
|
| + case gpu::kCollectInfoNone:
|
| + NOTREACHED();
|
| + break;
|
| + case gpu::kCollectInfoSuccess:
|
| + break;
|
| + }
|
| + return res;
|
| +}
|
| +#endif // !defined(OS_MACOSX)
|
| +
|
| +#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
|
| +bool CanAccessNvidiaDeviceFile() {
|
| + bool res = true;
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| + if (access("/dev/nvidiactl", R_OK) != 0) {
|
| + DVLOG(1) << "NVIDIA device file /dev/nvidiactl access denied";
|
| + res = false;
|
| + }
|
| + return res;
|
| +}
|
| +#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
|
| +
|
| +} // namespace
|
| +
|
| +GpuInit::GpuInit(GpuInitDelegate* delegate,
|
| + const base::CommandLine& command_line)
|
| + : delegate_(delegate) {
|
| + if (command_line.HasSwitch(switches::kGpuStartupDialog)) {
|
| + delegate_->WaitForDebugger();
|
| + }
|
| +
|
| + base::Time start_time = base::Time::Now();
|
| +
|
| +#if defined(OS_WIN)
|
| + // Prevent Windows from displaying a modal dialog on failures like not being
|
| + // able to load a DLL.
|
| + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
|
| + SEM_NOOPENFILEERRORBOX);
|
| +#elif defined(USE_X11)
|
| + ui::SetDefaultX11ErrorHandlers();
|
| +#endif // defined(USE_X11)
|
| +
|
| + logging::SetLogMessageHandler(GpuProcessLogMessageHandler);
|
| +
|
| + if (command_line.HasSwitch(switches::kSupportsDualGpus)) {
|
| + std::string types =
|
| + command_line.GetSwitchValueASCII(switches::kGpuDriverBugWorkarounds);
|
| + std::set<int> workarounds;
|
| + gpu::StringToFeatureSet(types, &workarounds);
|
| + if (workarounds.count(gpu::FORCE_DISCRETE_GPU) == 1)
|
| + ui::GpuSwitchingManager::GetInstance()->ForceUseOfDiscreteGpu();
|
| + else if (workarounds.count(gpu::FORCE_INTEGRATED_GPU) == 1)
|
| + ui::GpuSwitchingManager::GetInstance()->ForceUseOfIntegratedGpu();
|
| + }
|
| +
|
| + // Initialization of the OpenGL bindings may fail, in which case we
|
| + // will need to tear down this process. However, we can not do so
|
| + // safely until the IPC channel is set up, because the detection of
|
| + // early return of a child process is implemented using an IPC
|
| + // channel error. If the IPC channel is not fully set up between the
|
| + // browser and GPU process, and the GPU process crashes or exits
|
| + // early, the browser process will never detect it. For this reason
|
| + // we defer tearing down the GPU process until receiving the
|
| + // GpuMsg_Initialize message from the browser.
|
| + bool dead_on_arrival = false;
|
| +
|
| + delegate_->InitializeMessageLoop();
|
| + base::PlatformThread::SetName("CrGpuMain");
|
| +
|
| + // In addition to disabling the watchdog if the command line switch is
|
| + // present, disable the watchdog on valgrind because the code is expected
|
| + // to run slowly in that case.
|
| + bool enable_watchdog =
|
| + !command_line.HasSwitch(switches::kDisableGpuWatchdog) &&
|
| + !RunningOnValgrind();
|
| +
|
| +// 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
|
| +
|
| + 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
|
| +
|
| + // 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) {
|
| + watchdog_thread_ = gpu::GpuWatchdogThread::Create();
|
| + delegate_->OnGpuWatchdogThreadCreated(watchdog_thread_.get());
|
| + }
|
| +
|
| + // Initializes StatisticsRecorder which tracks UMA histograms.
|
| + base::StatisticsRecorder::Initialize();
|
| +
|
| + gpu::GPUInfo gpu_info;
|
| + // Get vendor_id, device_id, driver_version from browser process through
|
| + // commandline switches.
|
| + GetGpuInfoFromCommandLine(&gpu_info, command_line);
|
| + gpu_info.in_process_gpu = false;
|
| + delegate_->OnGpuInfoUpdate(gpu_info);
|
| +
|
| + delegate_->PreSandboxInitialization();
|
| +
|
| +#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
|
| + // Set thread priority before sandbox initialization.
|
| + base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY);
|
| +#endif
|
| +
|
| + // Warm up sandbox.
|
| + {
|
| + TRACE_EVENT0("gpu", "Warm up rand");
|
| + // Warm up the random subsystem, which needs to be done pre-sandbox on all
|
| + // platforms.
|
| + (void)base::RandUint64();
|
| + }
|
| + delegate_->WarmUpSandbox();
|
| +
|
| + bool initialized_sandbox = false;
|
| + if (delegate_->ShouldStartSandboxEarly()) {
|
| + gpu_info.sandboxed = delegate_->StartSandbox();
|
| + initialized_sandbox = true;
|
| + }
|
| +
|
| + base::TimeTicks before_initialize_one_off = base::TimeTicks::Now();
|
| +
|
| + // Determine if we need to initialize GL here or it has already been done.
|
| + bool should_initialize_gl = delegate_->ShouldInitializeGL();
|
| +
|
| + // Load and initialize the GL implementation and locate the GL entry points.
|
| + bool gl_initialized = should_initialize_gl ? gl::init::InitializeGLOneOff()
|
| + : gl::GetGLImplementation() !=
|
| + gl::kGLImplementationNone;
|
| + if (gl_initialized) {
|
| + // We need to collect GL strings (VENDOR, RENDERER) for blacklisting
|
| + // purposes. However, on Mac we don't actually use them. As documented in
|
| + // crbug.com/222934, due to some driver issues, glGetString could take
|
| + // multiple seconds to finish, which in turn cause the GPU process to
|
| + // crash.
|
| + // By skipping the following code on Mac, we don't really lose anything,
|
| + // because the basic GPU information is passed down from browser process
|
| + // and we already registered them through SetGpuInfo() above.
|
| + base::TimeTicks before_collect_context_graphics_info =
|
| + base::TimeTicks::Now();
|
| +#if !defined(OS_MACOSX)
|
| + if (!CollectGraphicsInfo(&gpu_info))
|
| + dead_on_arrival = true;
|
| + delegate_->OnGpuInfoUpdate(gpu_info);
|
| +
|
| + // Recompute gpu driver bug workarounds.
|
| + // This is necessary on systems where vendor_id/device_id aren't available
|
| + // (Chrome OS, Android) or where workarounds may be dependent on GL_VENDOR
|
| + // and GL_RENDERER strings which are lazily computed (Linux).
|
| + if (!command_line.HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) {
|
| + // TODO: this can not affect disabled extensions, since they're already
|
| + // initialized in the bindings. This should be moved before bindings
|
| + // initialization. However, populating GPUInfo fully works only on
|
| + // Android. Other platforms would need the bindings to query GL strings.
|
| + gpu::ApplyGpuDriverBugWorkarounds(
|
| + gpu_info, const_cast<base::CommandLine*>(&command_line));
|
| + }
|
| +#endif // !defined(OS_MACOSX)
|
| +
|
| +#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
|
| + if (gpu_info.gpu.vendor_id == 0x10de && // NVIDIA
|
| + gpu_info.driver_vendor == "NVIDIA" && !CanAccessNvidiaDeviceFile())
|
| + dead_on_arrival = true;
|
| +#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
|
| +
|
| + base::TimeDelta collect_context_time =
|
| + base::TimeTicks::Now() - before_collect_context_graphics_info;
|
| + UMA_HISTOGRAM_TIMES("GPU.CollectContextGraphicsInfo", collect_context_time);
|
| + } else { // gl_initialized
|
| + VLOG(1) << "gl::init::InitializeGLOneOff failed";
|
| + dead_on_arrival = true;
|
| + }
|
| +
|
| + base::TimeDelta initialize_one_off_time =
|
| + base::TimeTicks::Now() - before_initialize_one_off;
|
| + UMA_HISTOGRAM_MEDIUM_TIMES("GPU.InitializeOneOffMediumTime",
|
| + initialize_one_off_time);
|
| +
|
| + if (enable_watchdog && delayed_watchdog_enable) {
|
| + watchdog_thread_ = gpu::GpuWatchdogThread::Create();
|
| + delegate_->OnGpuWatchdogThreadCreated(watchdog_thread_.get());
|
| + }
|
| +
|
| + // OSMesa is expected to run very slowly, so disable the watchdog in that
|
| + // case.
|
| + if (enable_watchdog &&
|
| + gl::GetGLImplementation() == gl::kGLImplementationOSMesaGL) {
|
| + watchdog_thread_->Stop();
|
| + watchdog_thread_ = nullptr;
|
| + delegate_->OnGpuWatchdogThreadDestroyed();
|
| + }
|
| +
|
| + if (!initialized_sandbox)
|
| + gpu_info.sandboxed = delegate_->StartSandbox();
|
| + delegate_->OnGpuInfoUpdate(gpu_info);
|
| +
|
| + logging::SetLogMessageHandler(nullptr);
|
| +
|
| + if (gpu::GetNativeGpuMemoryBufferType() != gfx::EMPTY_BUFFER)
|
| + gpu_memory_buffer_factory_ =
|
| + gpu::GpuMemoryBufferFactory::CreateNativeType();
|
| +
|
| + delegate_->Initialize(start_time, dead_on_arrival,
|
| + std::move(deferred_messages.Get()),
|
| + gpu_memory_buffer_factory_.get());
|
| +
|
| + if (watchdog_thread_ && base::PowerMonitor::Get())
|
| + watchdog_thread_->AddPowerObserver();
|
| +}
|
| +
|
| +GpuInit::~GpuInit() {
|
| + if (watchdog_thread_)
|
| + watchdog_thread_->Stop();
|
| +}
|
| +
|
| +} // namespace gpu
|
|
|