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. 4 5#include "content/gpu/gpu_child_thread.h" 6 7#include "base/bind.h" 8#include "base/lazy_instance.h" 9#include "base/threading/worker_pool.h" 10#include "build/build_config.h" 11#include "content/child/child_process.h" 12#include "content/child/thread_safe_sender.h" 13#include "content/common/gpu/gpu_messages.h" 14#include "content/gpu/gpu_watchdog_thread.h" 15#include "content/public/common/content_client.h" 16#include "content/public/common/content_switches.h" 17#include "gpu/config/gpu_info_collector.h" 18#include "ipc/ipc_channel_handle.h" 19#include "ipc/ipc_sync_message_filter.h" 20#include "ui/gl/gl_implementation.h" 21 22#if defined(USE_OZONE) 23#include "ui/ozone/public/gpu_platform_support.h" 24#include "ui/ozone/public/ozone_platform.h" 25#endif 26 27namespace content { 28namespace { 29 30static base::LazyInstance<scoped_refptr<ThreadSafeSender> > 31 g_thread_safe_sender = LAZY_INSTANCE_INITIALIZER; 32 33bool GpuProcessLogMessageHandler(int severity, 34 const char* file, int line, 35 size_t message_start, 36 const std::string& str) { 37 std::string header = str.substr(0, message_start); 38 std::string message = str.substr(message_start); 39 40 g_thread_safe_sender.Get()->Send(new GpuHostMsg_OnLogMessage( 41 severity, header, message)); 42 43 return false; 44} 45 46} // namespace 47 48GpuChildThread::GpuChildThread(GpuWatchdogThread* watchdog_thread, 49 bool dead_on_arrival, 50 const gpu::GPUInfo& gpu_info, 51 const DeferredMessages& deferred_messages) 52 : dead_on_arrival_(dead_on_arrival), 53 gpu_info_(gpu_info), 54 deferred_messages_(deferred_messages), 55 in_browser_process_(false) { 56 watchdog_thread_ = watchdog_thread; 57#if defined(OS_WIN) 58 target_services_ = NULL; 59#endif 60 g_thread_safe_sender.Get() = thread_safe_sender(); 61} 62 63GpuChildThread::GpuChildThread(const std::string& channel_id) 64 : ChildThread(Options(channel_id, false)), 65 dead_on_arrival_(false), 66 in_browser_process_(true) { 67#if defined(OS_WIN) 68 target_services_ = NULL; 69#endif 70 DCHECK( 71 CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) || 72 CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU)); 73#if !defined(OS_ANDROID) 74 // For single process and in-process GPU mode, we need to load and 75 // initialize the GL implementation and locate the GL entry points here. 76 // On Android, GLSurface::InitializeOneOff() is called from BrowserMainLoop 77 // before getting here. crbug.com/326295 78 if (!gfx::GLSurface::InitializeOneOff()) 79 VLOG(1) << "gfx::GLSurface::InitializeOneOff failed"; 80#endif 81 g_thread_safe_sender.Get() = thread_safe_sender(); 82} 83 84GpuChildThread::~GpuChildThread() { 85} 86 87void GpuChildThread::Shutdown() { 88 ChildThread::Shutdown(); 89 logging::SetLogMessageHandler(NULL); 90} 91 92void GpuChildThread::Init(const base::Time& process_start_time) { 93 process_start_time_ = process_start_time; 94} 95 96bool GpuChildThread::Send(IPC::Message* msg) { 97 // The GPU process must never send a synchronous IPC message to the browser 98 // process. This could result in deadlock. 99 DCHECK(!msg->is_sync()); 100 101 return ChildThread::Send(msg); 102} 103 104bool GpuChildThread::OnControlMessageReceived(const IPC::Message& msg) { 105 bool handled = true; 106 IPC_BEGIN_MESSAGE_MAP(GpuChildThread, msg) 107 IPC_MESSAGE_HANDLER(GpuMsg_Initialize, OnInitialize) 108 IPC_MESSAGE_HANDLER(GpuMsg_CollectGraphicsInfo, OnCollectGraphicsInfo) 109 IPC_MESSAGE_HANDLER(GpuMsg_GetVideoMemoryUsageStats, 110 OnGetVideoMemoryUsageStats) 111 IPC_MESSAGE_HANDLER(GpuMsg_Clean, OnClean) 112 IPC_MESSAGE_HANDLER(GpuMsg_Crash, OnCrash) 113 IPC_MESSAGE_HANDLER(GpuMsg_Hang, OnHang) 114 IPC_MESSAGE_HANDLER(GpuMsg_DisableWatchdog, OnDisableWatchdog) 115 IPC_MESSAGE_UNHANDLED(handled = false) 116 IPC_END_MESSAGE_MAP() 117 118 if (handled) 119 return true; 120 121#if defined(USE_OZONE) 122 if (ui::OzonePlatform::GetInstance() 123 ->GetGpuPlatformSupport() 124 ->OnMessageReceived(msg)) 125 return true; 126#endif 127 128 return gpu_channel_manager_.get() && 129 gpu_channel_manager_->OnMessageReceived(msg); 130} 131 132void GpuChildThread::OnInitialize() { 133 // Record initialization only after collecting the GPU info because that can 134 // take a significant amount of time. 135 gpu_info_.initialization_time = base::Time::Now() - process_start_time_; 136 Send(new GpuHostMsg_Initialized(!dead_on_arrival_, gpu_info_)); 137 while (!deferred_messages_.empty()) { 138 Send(deferred_messages_.front()); 139 deferred_messages_.pop(); 140 } 141 142 if (dead_on_arrival_) { 143 LOG(ERROR) << "Exiting GPU process due to errors during initialization"; 144 base::MessageLoop::current()->Quit(); 145 return; 146 } 147 148#if defined(OS_ANDROID) 149 base::PlatformThread::SetThreadPriority( 150 base::PlatformThread::CurrentHandle(), 151 base::kThreadPriority_Display); 152#endif 153 154 // We don't need to pipe log messages if we are running the GPU thread in 155 // the browser process. 156 if (!in_browser_process_) 157 logging::SetLogMessageHandler(GpuProcessLogMessageHandler); 158 159 // Defer creation of the render thread. This is to prevent it from handling 160 // IPC messages before the sandbox has been enabled and all other necessary 161 // initialization has succeeded. 162 gpu_channel_manager_.reset( 163 new GpuChannelManager(GetRouter(), 164 watchdog_thread_.get(), 165 ChildProcess::current()->io_message_loop_proxy(), 166 ChildProcess::current()->GetShutDownEvent(), 167 channel())); 168 169#if defined(USE_OZONE) 170 ui::OzonePlatform::GetInstance() 171 ->GetGpuPlatformSupport() 172 ->OnChannelEstablished(this); 173#endif 174} 175 176void GpuChildThread::StopWatchdog() { 177 if (watchdog_thread_.get()) { 178 watchdog_thread_->Stop(); 179 } 180} 181 182void GpuChildThread::OnCollectGraphicsInfo() { 183#if defined(OS_WIN) 184 // GPU full info collection should only happen on un-sandboxed GPU process 185 // or single process/in-process gpu mode on Windows. 186 CommandLine* command_line = CommandLine::ForCurrentProcess(); 187 DCHECK(command_line->HasSwitch(switches::kDisableGpuSandbox) || 188 in_browser_process_); 189#endif // OS_WIN 190 191 gpu::CollectInfoResult result = 192 gpu::CollectContextGraphicsInfo(&gpu_info_); 193 switch (result) { 194 case gpu::kCollectInfoFatalFailure: 195 LOG(ERROR) << "gpu::CollectGraphicsInfo failed (fatal)."; 196 // TODO(piman): can we signal overall failure? 197 break; 198 case gpu::kCollectInfoNonFatalFailure: 199 VLOG(1) << "gpu::CollectGraphicsInfo failed (non-fatal)."; 200 break; 201 case gpu::kCollectInfoNone: 202 NOTREACHED(); 203 break; 204 case gpu::kCollectInfoSuccess: 205 break; 206 } 207 GetContentClient()->SetGpuInfo(gpu_info_); 208 209#if defined(OS_WIN) 210 // This is slow, but it's the only thing the unsandboxed GPU process does, 211 // and GpuDataManager prevents us from sending multiple collecting requests, 212 // so it's OK to be blocking. 213 gpu::GetDxDiagnostics(&gpu_info_.dx_diagnostics); 214 gpu_info_.dx_diagnostics_info_state = gpu::kCollectInfoSuccess; 215#endif // OS_WIN 216 217 Send(new GpuHostMsg_GraphicsInfoCollected(gpu_info_)); 218 219#if defined(OS_WIN) 220 if (!in_browser_process_) { 221 // The unsandboxed GPU process fulfilled its duty. Rest in peace. 222 base::MessageLoop::current()->Quit(); 223 } 224#endif // OS_WIN 225} 226 227void GpuChildThread::OnGetVideoMemoryUsageStats() { 228 GPUVideoMemoryUsageStats video_memory_usage_stats; 229 if (gpu_channel_manager_) 230 gpu_channel_manager_->gpu_memory_manager()->GetVideoMemoryUsageStats( 231 &video_memory_usage_stats); 232 Send(new GpuHostMsg_VideoMemoryUsageStats(video_memory_usage_stats)); 233} 234 235void GpuChildThread::OnClean() { 236 VLOG(1) << "GPU: Removing all contexts"; 237 if (gpu_channel_manager_) 238 gpu_channel_manager_->LoseAllContexts(); 239} 240 241void GpuChildThread::OnCrash() { 242 VLOG(1) << "GPU: Simulating GPU crash"; 243 // Good bye, cruel world. 244 volatile int* it_s_the_end_of_the_world_as_we_know_it = NULL; 245 *it_s_the_end_of_the_world_as_we_know_it = 0xdead; 246} 247 248void GpuChildThread::OnHang() { 249 VLOG(1) << "GPU: Simulating GPU hang"; 250 for (;;) { 251 // Do not sleep here. The GPU watchdog timer tracks the amount of user 252 // time this thread is using and it doesn't use much while calling Sleep. 253 } 254} 255 256void GpuChildThread::OnDisableWatchdog() { 257 VLOG(1) << "GPU: Disabling watchdog thread"; 258 if (watchdog_thread_.get()) { 259 // Disarm the watchdog before shutting down the message loop. This prevents 260 // the future posting of tasks to the message loop. 261 if (watchdog_thread_->message_loop()) 262 watchdog_thread_->PostAcknowledge(); 263 // Prevent rearming. 264 watchdog_thread_->Stop(); 265 } 266} 267 268} // namespace content 269 270