[content shell] hook up testRunner.dumpEditingCallbacks
[chromium-blink-merge.git] / content / gpu / gpu_main.cc
blob3020600ed34dc1785fc90d6a5dddc52e94b0651d
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <stdlib.h>
7 #if defined(OS_WIN)
8 #include <windows.h>
9 #endif
11 #include "base/debug/trace_event.h"
12 #include "base/message_loop.h"
13 #include "base/rand_util.h"
14 #include "base/string_number_conversions.h"
15 #include "base/stringprintf.h"
16 #include "base/threading/platform_thread.h"
17 #include "build/build_config.h"
18 #include "content/common/gpu/gpu_config.h"
19 #include "content/gpu/gpu_child_thread.h"
20 #include "content/gpu/gpu_info_collector.h"
21 #include "content/gpu/gpu_process.h"
22 #include "content/gpu/gpu_watchdog_thread.h"
23 #include "content/public/common/content_client.h"
24 #include "content/public/common/content_switches.h"
25 #include "content/public/common/gpu_switching_option.h"
26 #include "content/public/common/main_function_params.h"
27 #include "crypto/hmac.h"
28 #include "ui/gl/gl_implementation.h"
29 #include "ui/gl/gl_surface.h"
30 #include "ui/gl/gl_switches.h"
31 #include "ui/gl/gpu_switching_manager.h"
33 #if defined(OS_WIN)
34 #include "base/win/scoped_com_initializer.h"
35 #include "content/common/gpu/media/dxva_video_decode_accelerator.h"
36 #include "sandbox/win/src/sandbox.h"
37 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)
38 #include "content/common/gpu/media/omx_video_decode_accelerator.h"
39 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
40 #include "content/common/gpu/media/vaapi_video_decode_accelerator.h"
41 #endif
43 #if defined(USE_X11)
44 #include "ui/base/x/x11_util.h"
45 #endif
47 #if defined(OS_LINUX)
48 #include "content/public/common/sandbox_init.h"
49 #endif
51 const int kGpuTimeout = 10000;
53 namespace content {
54 namespace {
55 void WarmUpSandbox(const GPUInfo&, bool);
58 // Main function for starting the Gpu process.
59 int GpuMain(const MainFunctionParams& parameters) {
60 TRACE_EVENT0("gpu", "GpuMain");
62 base::Time start_time = base::Time::Now();
64 const CommandLine& command_line = parameters.command_line;
65 if (command_line.HasSwitch(switches::kGpuStartupDialog)) {
66 ChildProcess::WaitForDebugger("Gpu");
69 if (!command_line.HasSwitch(switches::kSingleProcess)) {
70 #if defined(OS_WIN)
71 // Prevent Windows from displaying a modal dialog on failures like not being
72 // able to load a DLL.
73 SetErrorMode(
74 SEM_FAILCRITICALERRORS |
75 SEM_NOGPFAULTERRORBOX |
76 SEM_NOOPENFILEERRORBOX);
77 #elif defined(USE_X11)
78 ui::SetDefaultX11ErrorHandlers();
79 #endif
82 if (command_line.HasSwitch(switches::kSupportsDualGpus) &&
83 command_line.HasSwitch(switches::kGpuSwitching)) {
84 std::string option = command_line.GetSwitchValueASCII(
85 switches::kGpuSwitching);
86 if (option == switches::kGpuSwitchingOptionNameForceDiscrete)
87 ui::GpuSwitchingManager::GetInstance()->ForceUseOfDiscreteGpu();
88 else if (option == switches::kGpuSwitchingOptionNameForceIntegrated)
89 ui::GpuSwitchingManager::GetInstance()->ForceUseOfIntegratedGpu();
92 // Initialization of the OpenGL bindings may fail, in which case we
93 // will need to tear down this process. However, we can not do so
94 // safely until the IPC channel is set up, because the detection of
95 // early return of a child process is implemented using an IPC
96 // channel error. If the IPC channel is not fully set up between the
97 // browser and GPU process, and the GPU process crashes or exits
98 // early, the browser process will never detect it. For this reason
99 // we defer tearing down the GPU process until receiving the
100 // GpuMsg_Initialize message from the browser.
101 bool dead_on_arrival = false;
103 MessageLoop::Type message_loop_type = MessageLoop::TYPE_IO;
104 #if defined(OS_WIN)
105 // Unless we're running on desktop GL, we don't need a UI message
106 // loop, so avoid its use to work around apparent problems with some
107 // third-party software.
108 if (command_line.HasSwitch(switches::kUseGL) &&
109 command_line.GetSwitchValueASCII(switches::kUseGL) ==
110 gfx::kGLImplementationDesktopName) {
111 message_loop_type = MessageLoop::TYPE_UI;
113 #elif defined(OS_LINUX)
114 message_loop_type = MessageLoop::TYPE_DEFAULT;
115 #endif
117 MessageLoop main_message_loop(message_loop_type);
118 base::PlatformThread::SetName("CrGpuMain");
120 // In addition to disabling the watchdog if the command line switch is
121 // present, disable the watchdog on valgrind because the code is expected
122 // to run slowly in that case.
123 bool enable_watchdog =
124 !CommandLine::ForCurrentProcess()->HasSwitch(
125 switches::kDisableGpuWatchdog) &&
126 !RunningOnValgrind();
128 // Disable the watchdog in debug builds because they tend to only be run by
129 // developers who will not appreciate the watchdog killing the GPU process.
130 #ifndef NDEBUG
131 enable_watchdog = false;
132 #endif
134 bool delayed_watchdog_enable = false;
136 #if defined(OS_CHROMEOS)
137 // Don't start watchdog immediately, to allow developers to switch to VT2 on
138 // startup.
139 delayed_watchdog_enable = true;
140 #endif
142 scoped_refptr<GpuWatchdogThread> watchdog_thread;
144 // Start the GPU watchdog only after anything that is expected to be time
145 // consuming has completed, otherwise the process is liable to be aborted.
146 if (enable_watchdog && !delayed_watchdog_enable) {
147 watchdog_thread = new GpuWatchdogThread(kGpuTimeout);
148 watchdog_thread->Start();
151 GPUInfo gpu_info;
152 // Get vendor_id, device_id, driver_version from browser process through
153 // commandline switches.
154 DCHECK(command_line.HasSwitch(switches::kGpuVendorID) &&
155 command_line.HasSwitch(switches::kGpuDeviceID) &&
156 command_line.HasSwitch(switches::kGpuDriverVersion));
157 bool success = base::HexStringToInt(
158 command_line.GetSwitchValueASCII(switches::kGpuVendorID),
159 reinterpret_cast<int*>(&(gpu_info.gpu.vendor_id)));
160 DCHECK(success);
161 success = base::HexStringToInt(
162 command_line.GetSwitchValueASCII(switches::kGpuDeviceID),
163 reinterpret_cast<int*>(&(gpu_info.gpu.device_id)));
164 DCHECK(success);
165 gpu_info.driver_vendor =
166 command_line.GetSwitchValueASCII(switches::kGpuDriverVendor);
167 gpu_info.driver_version =
168 command_line.GetSwitchValueASCII(switches::kGpuDriverVersion);
169 GetContentClient()->SetGpuInfo(gpu_info);
171 #if defined(OS_WIN)
172 // Asynchronously initialize DXVA while GL is being initialized because
173 // they both take tens of ms.
174 base::WaitableEvent dxva_initialized(true, false);
175 DXVAVideoDecodeAccelerator::PreSandboxInitialization(
176 base::Bind(&base::WaitableEvent::Signal,
177 base::Unretained(&dxva_initialized)));
178 #endif
180 // We need to track that information for the WarmUpSandbox function.
181 bool initialized_gl_context = false;
182 // Load and initialize the GL implementation and locate the GL entry points.
183 if (gfx::GLSurface::InitializeOneOff()) {
184 if (!gpu_info_collector::CollectContextGraphicsInfo(&gpu_info))
185 VLOG(1) << "gpu_info_collector::CollectGraphicsInfo failed";
186 GetContentClient()->SetGpuInfo(gpu_info);
188 // We know that CollectGraphicsInfo will initialize a GLContext.
189 initialized_gl_context = true;
191 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
192 if (gpu_info.gpu.vendor_id == 0x10de && // NVIDIA
193 gpu_info.driver_vendor == "NVIDIA") {
194 base::ThreadRestrictions::AssertIOAllowed();
195 if (access("/dev/nvidiactl", R_OK) != 0) {
196 VLOG(1) << "NVIDIA device file /dev/nvidiactl access denied";
197 gpu_info.gpu_accessible = false;
198 dead_on_arrival = true;
201 #endif // OS_CHROMEOS
202 } else {
203 VLOG(1) << "gfx::GLSurface::InitializeOneOff failed";
204 gpu_info.gpu_accessible = false;
205 gpu_info.finalized = true;
206 dead_on_arrival = true;
209 if (enable_watchdog && delayed_watchdog_enable) {
210 watchdog_thread = new GpuWatchdogThread(kGpuTimeout);
211 watchdog_thread->Start();
214 // OSMesa is expected to run very slowly, so disable the watchdog in that
215 // case.
216 if (enable_watchdog &&
217 gfx::GetGLImplementation() == gfx::kGLImplementationOSMesaGL) {
218 watchdog_thread->Stop();
220 watchdog_thread = NULL;
224 const bool should_initialize_gl_context = !initialized_gl_context &&
225 !dead_on_arrival;
226 // Warm up the current process before enabling the sandbox.
227 WarmUpSandbox(gpu_info, should_initialize_gl_context);
230 #if defined(OS_LINUX)
232 TRACE_EVENT0("gpu", "Initialize sandbox");
233 bool do_init_sandbox = true;
235 #if defined(OS_CHROMEOS) && defined(NDEBUG)
236 // On Chrome OS and when not on a debug build, initialize
237 // the GPU process' sandbox only for Intel GPUs.
238 do_init_sandbox = gpu_info.gpu.vendor_id == 0x8086; // Intel GPU.
239 #endif
241 if (do_init_sandbox) {
242 if (watchdog_thread.get())
243 watchdog_thread->Stop();
244 gpu_info.sandboxed = InitializeSandbox();
245 if (watchdog_thread.get())
246 watchdog_thread->Start();
249 #endif
251 #if defined(OS_WIN)
253 // DXVA initialization must have completed before the token is lowered.
254 TRACE_EVENT0("gpu", "Wait for DXVA initialization");
255 dxva_initialized.Wait();
259 TRACE_EVENT0("gpu", "Lower token");
260 // For windows, if the target_services interface is not zero, the process
261 // is sandboxed and we must call LowerToken() before rendering untrusted
262 // content.
263 sandbox::TargetServices* target_services =
264 parameters.sandbox_info->target_services;
265 if (target_services) {
266 target_services->LowerToken();
267 gpu_info.sandboxed = true;
270 #endif
272 GpuProcess gpu_process;
274 GpuChildThread* child_thread = new GpuChildThread(watchdog_thread.get(),
275 dead_on_arrival, gpu_info);
277 child_thread->Init(start_time);
279 gpu_process.set_main_thread(child_thread);
282 TRACE_EVENT0("gpu", "Run Message Loop");
283 main_message_loop.Run();
286 child_thread->StopWatchdog();
288 return 0;
291 namespace {
293 #if defined(OS_LINUX)
294 void CreateDummyGlContext() {
295 scoped_refptr<gfx::GLSurface> surface(
296 gfx::GLSurface::CreateOffscreenGLSurface(false, gfx::Size(1, 1)));
297 if (!surface.get()) {
298 VLOG(1) << "gfx::GLSurface::CreateOffscreenGLSurface failed";
299 return;
302 // On Linux, this is needed to make sure /dev/nvidiactl has
303 // been opened and its descriptor cached.
304 scoped_refptr<gfx::GLContext> context(
305 gfx::GLContext::CreateGLContext(NULL,
306 surface,
307 gfx::PreferDiscreteGpu));
308 if (!context.get()) {
309 VLOG(1) << "gfx::GLContext::CreateGLContext failed";
310 return;
313 // Similarly, this is needed for /dev/nvidia0.
314 if (context->MakeCurrent(surface)) {
315 context->ReleaseCurrent(surface.get());
316 } else {
317 VLOG(1) << "gfx::GLContext::MakeCurrent failed";
320 #endif
322 void WarmUpSandbox(const GPUInfo& gpu_info,
323 bool should_initialize_gl_context) {
325 TRACE_EVENT0("gpu", "Warm up rand");
326 // Warm up the random subsystem, which needs to be done pre-sandbox on all
327 // platforms.
328 (void) base::RandUint64();
331 TRACE_EVENT0("gpu", "Warm up HMAC");
332 // Warm up the crypto subsystem, which needs to done pre-sandbox on all
333 // platforms.
334 crypto::HMAC hmac(crypto::HMAC::SHA256);
335 unsigned char key = '\0';
336 bool ret = hmac.Init(&key, sizeof(key));
337 (void) ret;
340 #if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL)
341 OmxVideoDecodeAccelerator::PreSandboxInitialization();
342 #elif defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_FAMILY)
343 VaapiVideoDecodeAccelerator::PreSandboxInitialization();
344 #endif
346 #if defined(OS_LINUX)
347 // We special case Optimus since the vendor_id we see may not be Nvidia.
348 bool uses_nvidia_driver = (gpu_info.gpu.vendor_id == 0x10de && // NVIDIA.
349 gpu_info.driver_vendor == "NVIDIA") ||
350 gpu_info.optimus;
351 if (uses_nvidia_driver && should_initialize_gl_context) {
352 // We need this on Nvidia to pre-open /dev/nvidiactl and /dev/nvidia0.
353 CreateDummyGlContext();
355 #endif
357 #if defined(OS_WIN)
358 // Preload these DLL because the sandbox prevents them from loading.
359 LoadLibrary(L"setupapi.dll");
360 #endif
363 } // namespace.
365 } // namespace content