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 |