browser_gpu_channel_host_factory.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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/browser/gpu/browser_gpu_channel_host_factory.h" 6 7#include "base/bind.h" 8#include "base/debug/trace_event.h" 9#include "base/synchronization/waitable_event.h" 10#include "base/threading/thread_restrictions.h" 11#include "content/browser/gpu/gpu_data_manager_impl.h" 12#include "content/browser/gpu/gpu_process_host.h" 13#include "content/browser/gpu/gpu_surface_tracker.h" 14#include "content/common/child_process_host_impl.h" 15#include "content/common/gpu/client/gpu_memory_buffer_impl.h" 16#include "content/common/gpu/gpu_messages.h" 17#include "content/public/browser/browser_thread.h" 18#include "content/public/browser/gpu_data_manager.h" 19#include "content/public/common/content_client.h" 20#include "ipc/ipc_channel_handle.h" 21#include "ipc/ipc_forwarding_message_filter.h" 22#include "ipc/message_filter.h" 23 24namespace content { 25 26BrowserGpuChannelHostFactory* BrowserGpuChannelHostFactory::instance_ = NULL; 27 28struct BrowserGpuChannelHostFactory::CreateRequest { 29 CreateRequest() 30 : event(true, false), gpu_host_id(0), route_id(MSG_ROUTING_NONE) {} 31 ~CreateRequest() {} 32 base::WaitableEvent event; 33 int gpu_host_id; 34 int32 route_id; 35 bool succeeded; 36}; 37 38class BrowserGpuChannelHostFactory::EstablishRequest 39 : public base::RefCountedThreadSafe<EstablishRequest> { 40 public: 41 static scoped_refptr<EstablishRequest> Create(CauseForGpuLaunch cause, 42 int gpu_client_id, 43 int gpu_host_id); 44 void Wait(); 45 void Cancel(); 46 47 int gpu_host_id() { return gpu_host_id_; } 48 IPC::ChannelHandle& channel_handle() { return channel_handle_; } 49 gpu::GPUInfo gpu_info() { return gpu_info_; } 50 51 private: 52 friend class base::RefCountedThreadSafe<EstablishRequest>; 53 explicit EstablishRequest(CauseForGpuLaunch cause, 54 int gpu_client_id, 55 int gpu_host_id); 56 ~EstablishRequest() {} 57 void EstablishOnIO(); 58 void OnEstablishedOnIO(const IPC::ChannelHandle& channel_handle, 59 const gpu::GPUInfo& gpu_info); 60 void FinishOnIO(); 61 void FinishOnMain(); 62 63 base::WaitableEvent event_; 64 CauseForGpuLaunch cause_for_gpu_launch_; 65 const int gpu_client_id_; 66 int gpu_host_id_; 67 bool reused_gpu_process_; 68 IPC::ChannelHandle channel_handle_; 69 gpu::GPUInfo gpu_info_; 70 bool finished_; 71 scoped_refptr<base::MessageLoopProxy> main_loop_; 72}; 73 74scoped_refptr<BrowserGpuChannelHostFactory::EstablishRequest> 75BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause, 76 int gpu_client_id, 77 int gpu_host_id) { 78 scoped_refptr<EstablishRequest> establish_request = 79 new EstablishRequest(cause, gpu_client_id, gpu_host_id); 80 scoped_refptr<base::MessageLoopProxy> loop = 81 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); 82 // PostTask outside the constructor to ensure at least one reference exists. 83 loop->PostTask( 84 FROM_HERE, 85 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO, 86 establish_request)); 87 return establish_request; 88} 89 90BrowserGpuChannelHostFactory::EstablishRequest::EstablishRequest( 91 CauseForGpuLaunch cause, 92 int gpu_client_id, 93 int gpu_host_id) 94 : event_(false, false), 95 cause_for_gpu_launch_(cause), 96 gpu_client_id_(gpu_client_id), 97 gpu_host_id_(gpu_host_id), 98 reused_gpu_process_(false), 99 finished_(false), 100 main_loop_(base::MessageLoopProxy::current()) { 101} 102 103void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() { 104 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 105 if (!host) { 106 host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED, 107 cause_for_gpu_launch_); 108 if (!host) { 109 LOG(ERROR) << "Failed to launch GPU process."; 110 FinishOnIO(); 111 return; 112 } 113 gpu_host_id_ = host->host_id(); 114 reused_gpu_process_ = false; 115 } else { 116 if (reused_gpu_process_) { 117 // We come here if we retried to establish the channel because of a 118 // failure in ChannelEstablishedOnIO, but we ended up with the same 119 // process ID, meaning the failure was not because of a channel error, 120 // but another reason. So fail now. 121 LOG(ERROR) << "Failed to create channel."; 122 FinishOnIO(); 123 return; 124 } 125 reused_gpu_process_ = true; 126 } 127 128 host->EstablishGpuChannel( 129 gpu_client_id_, 130 true, 131 base::Bind( 132 &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO, 133 this)); 134} 135 136void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO( 137 const IPC::ChannelHandle& channel_handle, 138 const gpu::GPUInfo& gpu_info) { 139 if (channel_handle.name.empty() && reused_gpu_process_) { 140 // We failed after re-using the GPU process, but it may have died in the 141 // mean time. Retry to have a chance to create a fresh GPU process. 142 DVLOG(1) << "Failed to create channel on existing GPU process. Trying to " 143 "restart GPU process."; 144 EstablishOnIO(); 145 } else { 146 channel_handle_ = channel_handle; 147 gpu_info_ = gpu_info; 148 FinishOnIO(); 149 } 150} 151 152void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() { 153 event_.Signal(); 154 main_loop_->PostTask( 155 FROM_HERE, 156 base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain, 157 this)); 158} 159 160void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() { 161 if (!finished_) { 162 BrowserGpuChannelHostFactory* factory = 163 BrowserGpuChannelHostFactory::instance(); 164 factory->GpuChannelEstablished(); 165 finished_ = true; 166 } 167} 168 169void BrowserGpuChannelHostFactory::EstablishRequest::Wait() { 170 DCHECK(main_loop_->BelongsToCurrentThread()); 171 { 172 // We're blocking the UI thread, which is generally undesirable. 173 // In this case we need to wait for this before we can show any UI 174 // /anyway/, so it won't cause additional jank. 175 // TODO(piman): Make this asynchronous (http://crbug.com/125248). 176 TRACE_EVENT0("browser", 177 "BrowserGpuChannelHostFactory::EstablishGpuChannelSync"); 178 base::ThreadRestrictions::ScopedAllowWait allow_wait; 179 event_.Wait(); 180 } 181 FinishOnMain(); 182} 183 184void BrowserGpuChannelHostFactory::EstablishRequest::Cancel() { 185 DCHECK(main_loop_->BelongsToCurrentThread()); 186 finished_ = true; 187} 188 189bool BrowserGpuChannelHostFactory::CanUseForTesting() { 190 return GpuDataManager::GetInstance()->GpuAccessAllowed(NULL); 191} 192 193void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) { 194 DCHECK(!instance_); 195 instance_ = new BrowserGpuChannelHostFactory(); 196 if (establish_gpu_channel) { 197 instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP, 198 base::Closure()); 199 } 200} 201 202void BrowserGpuChannelHostFactory::Terminate() { 203 DCHECK(instance_); 204 delete instance_; 205 instance_ = NULL; 206} 207 208BrowserGpuChannelHostFactory::BrowserGpuChannelHostFactory() 209 : gpu_client_id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()), 210 shutdown_event_(new base::WaitableEvent(true, false)), 211 gpu_host_id_(0) { 212} 213 214BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() { 215 DCHECK(IsMainThread()); 216 if (pending_request_) 217 pending_request_->Cancel(); 218 for (size_t n = 0; n < established_callbacks_.size(); n++) 219 established_callbacks_[n].Run(); 220 shutdown_event_->Signal(); 221} 222 223bool BrowserGpuChannelHostFactory::IsMainThread() { 224 return BrowserThread::CurrentlyOn(BrowserThread::UI); 225} 226 227base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() { 228 return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI); 229} 230 231scoped_refptr<base::MessageLoopProxy> 232BrowserGpuChannelHostFactory::GetIOLoopProxy() { 233 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); 234} 235 236scoped_ptr<base::SharedMemory> 237BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) { 238 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory()); 239 if (!shm->CreateAnonymous(size)) 240 return scoped_ptr<base::SharedMemory>(); 241 return shm.Pass(); 242} 243 244void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO( 245 CreateRequest* request, 246 int32 surface_id, 247 const GPUCreateCommandBufferConfig& init_params) { 248 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 249 if (!host) { 250 request->event.Signal(); 251 return; 252 } 253 254 gfx::GLSurfaceHandle surface = 255 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id); 256 257 host->CreateViewCommandBuffer( 258 surface, 259 surface_id, 260 gpu_client_id_, 261 init_params, 262 request->route_id, 263 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO, 264 request)); 265} 266 267// static 268void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO( 269 CreateRequest* request, bool succeeded) { 270 request->succeeded = succeeded; 271 request->event.Signal(); 272} 273 274bool BrowserGpuChannelHostFactory::CreateViewCommandBuffer( 275 int32 surface_id, 276 const GPUCreateCommandBufferConfig& init_params, 277 int32 route_id) { 278 CreateRequest request; 279 request.route_id = route_id; 280 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind( 281 &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO, 282 base::Unretained(this), 283 &request, 284 surface_id, 285 init_params)); 286 // We're blocking the UI thread, which is generally undesirable. 287 // In this case we need to wait for this before we can show any UI /anyway/, 288 // so it won't cause additional jank. 289 // TODO(piman): Make this asynchronous (http://crbug.com/125248). 290 TRACE_EVENT0("browser", 291 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer"); 292 base::ThreadRestrictions::ScopedAllowWait allow_wait; 293 request.event.Wait(); 294 return request.succeeded; 295} 296 297void BrowserGpuChannelHostFactory::CreateImageOnIO( 298 gfx::PluginWindowHandle window, 299 int32 image_id, 300 const CreateImageCallback& callback) { 301 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 302 if (!host) { 303 ImageCreatedOnIO(callback, gfx::Size()); 304 return; 305 } 306 307 host->CreateImage( 308 window, 309 gpu_client_id_, 310 image_id, 311 base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO, callback)); 312} 313 314// static 315void BrowserGpuChannelHostFactory::ImageCreatedOnIO( 316 const CreateImageCallback& callback, const gfx::Size size) { 317 BrowserThread::PostTask( 318 BrowserThread::UI, 319 FROM_HERE, 320 base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated, 321 callback, size)); 322} 323 324// static 325void BrowserGpuChannelHostFactory::OnImageCreated( 326 const CreateImageCallback& callback, const gfx::Size size) { 327 callback.Run(size); 328} 329 330void BrowserGpuChannelHostFactory::CreateImage( 331 gfx::PluginWindowHandle window, 332 int32 image_id, 333 const CreateImageCallback& callback) { 334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 335 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind( 336 &BrowserGpuChannelHostFactory::CreateImageOnIO, 337 base::Unretained(this), 338 window, 339 image_id, 340 callback)); 341} 342 343void BrowserGpuChannelHostFactory::DeleteImageOnIO( 344 int32 image_id, int32 sync_point) { 345 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 346 if (!host) { 347 return; 348 } 349 350 host->DeleteImage(gpu_client_id_, image_id, sync_point); 351} 352 353void BrowserGpuChannelHostFactory::DeleteImage( 354 int32 image_id, int32 sync_point) { 355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 356 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind( 357 &BrowserGpuChannelHostFactory::DeleteImageOnIO, 358 base::Unretained(this), 359 image_id, 360 sync_point)); 361} 362 363GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync( 364 CauseForGpuLaunch cause_for_gpu_launch) { 365 EstablishGpuChannel(cause_for_gpu_launch, base::Closure()); 366 367 if (pending_request_) 368 pending_request_->Wait(); 369 370 return gpu_channel_.get(); 371} 372 373void BrowserGpuChannelHostFactory::EstablishGpuChannel( 374 CauseForGpuLaunch cause_for_gpu_launch, 375 const base::Closure& callback) { 376 if (gpu_channel_.get() && gpu_channel_->IsLost()) { 377 DCHECK(!pending_request_); 378 // Recreate the channel if it has been lost. 379 gpu_channel_ = NULL; 380 } 381 382 if (!gpu_channel_ && !pending_request_) { 383 // We should only get here if the context was lost. 384 pending_request_ = EstablishRequest::Create( 385 cause_for_gpu_launch, gpu_client_id_, gpu_host_id_); 386 } 387 388 if (!callback.is_null()) { 389 if (gpu_channel_) 390 callback.Run(); 391 else 392 established_callbacks_.push_back(callback); 393 } 394} 395 396GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() { 397 if (gpu_channel_ && !gpu_channel_->IsLost()) 398 return gpu_channel_; 399 400 return NULL; 401} 402 403void BrowserGpuChannelHostFactory::GpuChannelEstablished() { 404 DCHECK(IsMainThread()); 405 DCHECK(pending_request_); 406 if (pending_request_->channel_handle().name.empty()) { 407 DCHECK(!gpu_channel_); 408 } else { 409 GetContentClient()->SetGpuInfo(pending_request_->gpu_info()); 410 gpu_channel_ = GpuChannelHost::Create(this, 411 pending_request_->gpu_info(), 412 pending_request_->channel_handle(), 413 shutdown_event_.get()); 414 } 415 gpu_host_id_ = pending_request_->gpu_host_id(); 416 pending_request_ = NULL; 417 418 for (size_t n = 0; n < established_callbacks_.size(); n++) 419 established_callbacks_[n].Run(); 420 421 established_callbacks_.clear(); 422} 423 424scoped_ptr<gfx::GpuMemoryBuffer> 425BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(size_t width, 426 size_t height, 427 unsigned internalformat, 428 unsigned usage) { 429 if (!GpuMemoryBufferImpl::IsFormatValid(internalformat) || 430 !GpuMemoryBufferImpl::IsUsageValid(usage)) 431 return scoped_ptr<gfx::GpuMemoryBuffer>(); 432 433 return GpuMemoryBufferImpl::Create(gfx::Size(width, height), 434 internalformat, 435 usage).PassAs<gfx::GpuMemoryBuffer>(); 436} 437 438// static 439void BrowserGpuChannelHostFactory::AddFilterOnIO( 440 int host_id, 441 scoped_refptr<IPC::MessageFilter> filter) { 442 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 443 444 GpuProcessHost* host = GpuProcessHost::FromID(host_id); 445 if (host) 446 host->AddFilter(filter.get()); 447} 448 449void BrowserGpuChannelHostFactory::SetHandlerForControlMessages( 450 const uint32* message_ids, 451 size_t num_messages, 452 const base::Callback<void(const IPC::Message&)>& handler, 453 base::TaskRunner* target_task_runner) { 454 DCHECK(gpu_host_id_) 455 << "Do not call" 456 << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()" 457 << " until the GpuProcessHost has been set up."; 458 459 scoped_refptr<IPC::ForwardingMessageFilter> filter = 460 new IPC::ForwardingMessageFilter(message_ids, 461 num_messages, 462 target_task_runner); 463 filter->AddRoute(MSG_ROUTING_CONTROL, handler); 464 465 GetIOLoopProxy()->PostTask( 466 FROM_HERE, 467 base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO, 468 gpu_host_id_, 469 filter)); 470} 471 472} // namespace content 473