browser_gpu_channel_host_factory.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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 next_create_gpu_memory_buffer_request_id_(0) { 213} 214 215BrowserGpuChannelHostFactory::~BrowserGpuChannelHostFactory() { 216 DCHECK(IsMainThread()); 217 if (pending_request_) 218 pending_request_->Cancel(); 219 for (size_t n = 0; n < established_callbacks_.size(); n++) 220 established_callbacks_[n].Run(); 221 shutdown_event_->Signal(); 222} 223 224bool BrowserGpuChannelHostFactory::IsMainThread() { 225 return BrowserThread::CurrentlyOn(BrowserThread::UI); 226} 227 228base::MessageLoop* BrowserGpuChannelHostFactory::GetMainLoop() { 229 return BrowserThread::UnsafeGetMessageLoopForThread(BrowserThread::UI); 230} 231 232scoped_refptr<base::MessageLoopProxy> 233BrowserGpuChannelHostFactory::GetIOLoopProxy() { 234 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); 235} 236 237scoped_ptr<base::SharedMemory> 238BrowserGpuChannelHostFactory::AllocateSharedMemory(size_t size) { 239 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory()); 240 if (!shm->CreateAnonymous(size)) 241 return scoped_ptr<base::SharedMemory>(); 242 return shm.Pass(); 243} 244 245void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO( 246 CreateRequest* request, 247 int32 surface_id, 248 const GPUCreateCommandBufferConfig& init_params) { 249 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 250 if (!host) { 251 request->event.Signal(); 252 return; 253 } 254 255 gfx::GLSurfaceHandle surface = 256 GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id); 257 258 host->CreateViewCommandBuffer( 259 surface, 260 surface_id, 261 gpu_client_id_, 262 init_params, 263 request->route_id, 264 base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO, 265 request)); 266} 267 268// static 269void BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO( 270 CreateRequest* request, bool succeeded) { 271 request->succeeded = succeeded; 272 request->event.Signal(); 273} 274 275bool BrowserGpuChannelHostFactory::CreateViewCommandBuffer( 276 int32 surface_id, 277 const GPUCreateCommandBufferConfig& init_params, 278 int32 route_id) { 279 CreateRequest request; 280 request.route_id = route_id; 281 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind( 282 &BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO, 283 base::Unretained(this), 284 &request, 285 surface_id, 286 init_params)); 287 // We're blocking the UI thread, which is generally undesirable. 288 // In this case we need to wait for this before we can show any UI /anyway/, 289 // so it won't cause additional jank. 290 // TODO(piman): Make this asynchronous (http://crbug.com/125248). 291 TRACE_EVENT0("browser", 292 "BrowserGpuChannelHostFactory::CreateViewCommandBuffer"); 293 base::ThreadRestrictions::ScopedAllowWait allow_wait; 294 request.event.Wait(); 295 return request.succeeded; 296} 297 298void BrowserGpuChannelHostFactory::CreateImageOnIO( 299 gfx::PluginWindowHandle window, 300 int32 image_id, 301 const CreateImageCallback& callback) { 302 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 303 if (!host) { 304 ImageCreatedOnIO(callback, gfx::Size()); 305 return; 306 } 307 308 host->CreateImage( 309 window, 310 gpu_client_id_, 311 image_id, 312 base::Bind(&BrowserGpuChannelHostFactory::ImageCreatedOnIO, callback)); 313} 314 315// static 316void BrowserGpuChannelHostFactory::ImageCreatedOnIO( 317 const CreateImageCallback& callback, const gfx::Size size) { 318 BrowserThread::PostTask( 319 BrowserThread::UI, 320 FROM_HERE, 321 base::Bind(&BrowserGpuChannelHostFactory::OnImageCreated, 322 callback, size)); 323} 324 325// static 326void BrowserGpuChannelHostFactory::OnImageCreated( 327 const CreateImageCallback& callback, const gfx::Size size) { 328 callback.Run(size); 329} 330 331void BrowserGpuChannelHostFactory::CreateImage( 332 gfx::PluginWindowHandle window, 333 int32 image_id, 334 const CreateImageCallback& callback) { 335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 336 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind( 337 &BrowserGpuChannelHostFactory::CreateImageOnIO, 338 base::Unretained(this), 339 window, 340 image_id, 341 callback)); 342} 343 344void BrowserGpuChannelHostFactory::DeleteImageOnIO( 345 int32 image_id, int32 sync_point) { 346 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 347 if (!host) { 348 return; 349 } 350 351 host->DeleteImage(gpu_client_id_, image_id, sync_point); 352} 353 354void BrowserGpuChannelHostFactory::DeleteImage( 355 int32 image_id, int32 sync_point) { 356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 357 GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind( 358 &BrowserGpuChannelHostFactory::DeleteImageOnIO, 359 base::Unretained(this), 360 image_id, 361 sync_point)); 362} 363 364GpuChannelHost* BrowserGpuChannelHostFactory::EstablishGpuChannelSync( 365 CauseForGpuLaunch cause_for_gpu_launch) { 366 EstablishGpuChannel(cause_for_gpu_launch, base::Closure()); 367 368 if (pending_request_) 369 pending_request_->Wait(); 370 371 return gpu_channel_.get(); 372} 373 374void BrowserGpuChannelHostFactory::EstablishGpuChannel( 375 CauseForGpuLaunch cause_for_gpu_launch, 376 const base::Closure& callback) { 377 if (gpu_channel_.get() && gpu_channel_->IsLost()) { 378 DCHECK(!pending_request_); 379 // Recreate the channel if it has been lost. 380 gpu_channel_ = NULL; 381 } 382 383 if (!gpu_channel_ && !pending_request_) { 384 // We should only get here if the context was lost. 385 pending_request_ = EstablishRequest::Create( 386 cause_for_gpu_launch, gpu_client_id_, gpu_host_id_); 387 } 388 389 if (!callback.is_null()) { 390 if (gpu_channel_) 391 callback.Run(); 392 else 393 established_callbacks_.push_back(callback); 394 } 395} 396 397GpuChannelHost* BrowserGpuChannelHostFactory::GetGpuChannel() { 398 if (gpu_channel_ && !gpu_channel_->IsLost()) 399 return gpu_channel_; 400 401 return NULL; 402} 403 404void BrowserGpuChannelHostFactory::GpuChannelEstablished() { 405 DCHECK(IsMainThread()); 406 DCHECK(pending_request_); 407 if (pending_request_->channel_handle().name.empty()) { 408 DCHECK(!gpu_channel_); 409 } else { 410 GetContentClient()->SetGpuInfo(pending_request_->gpu_info()); 411 gpu_channel_ = GpuChannelHost::Create(this, 412 pending_request_->gpu_info(), 413 pending_request_->channel_handle(), 414 shutdown_event_.get()); 415 } 416 gpu_host_id_ = pending_request_->gpu_host_id(); 417 pending_request_ = NULL; 418 419 for (size_t n = 0; n < established_callbacks_.size(); n++) 420 established_callbacks_[n].Run(); 421 422 established_callbacks_.clear(); 423} 424 425scoped_ptr<gfx::GpuMemoryBuffer> 426BrowserGpuChannelHostFactory::AllocateGpuMemoryBuffer(size_t width, 427 size_t height, 428 unsigned internalformat, 429 unsigned usage) { 430 if (!GpuMemoryBufferImpl::IsFormatValid(internalformat) || 431 !GpuMemoryBufferImpl::IsUsageValid(usage)) 432 return scoped_ptr<gfx::GpuMemoryBuffer>(); 433 434 return GpuMemoryBufferImpl::Create(gfx::Size(width, height), 435 internalformat, 436 usage).PassAs<gfx::GpuMemoryBuffer>(); 437} 438 439// static 440void BrowserGpuChannelHostFactory::AddFilterOnIO( 441 int host_id, 442 scoped_refptr<IPC::MessageFilter> filter) { 443 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 444 445 GpuProcessHost* host = GpuProcessHost::FromID(host_id); 446 if (host) 447 host->AddFilter(filter.get()); 448} 449 450void BrowserGpuChannelHostFactory::SetHandlerForControlMessages( 451 const uint32* message_ids, 452 size_t num_messages, 453 const base::Callback<void(const IPC::Message&)>& handler, 454 base::TaskRunner* target_task_runner) { 455 DCHECK(gpu_host_id_) 456 << "Do not call" 457 << " BrowserGpuChannelHostFactory::SetHandlerForControlMessages()" 458 << " until the GpuProcessHost has been set up."; 459 460 scoped_refptr<IPC::ForwardingMessageFilter> filter = 461 new IPC::ForwardingMessageFilter(message_ids, 462 num_messages, 463 target_task_runner); 464 filter->AddRoute(MSG_ROUTING_CONTROL, handler); 465 466 GetIOLoopProxy()->PostTask( 467 FROM_HERE, 468 base::Bind(&BrowserGpuChannelHostFactory::AddFilterOnIO, 469 gpu_host_id_, 470 filter)); 471} 472 473void BrowserGpuChannelHostFactory::CreateGpuMemoryBuffer( 474 const gfx::GpuMemoryBufferHandle& handle, 475 const gfx::Size& size, 476 unsigned internalformat, 477 unsigned usage, 478 const CreateGpuMemoryBufferCallback& callback) { 479 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 480 uint32 request_id = next_create_gpu_memory_buffer_request_id_++; 481 create_gpu_memory_buffer_requests_[request_id] = callback; 482 GetIOLoopProxy()->PostTask( 483 FROM_HERE, 484 base::Bind(&BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO, 485 base::Unretained(this), 486 handle, 487 size, 488 internalformat, 489 usage, 490 request_id)); 491} 492 493void BrowserGpuChannelHostFactory::DestroyGpuMemoryBuffer( 494 const gfx::GpuMemoryBufferHandle& handle, 495 int32 sync_point) { 496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 497 GetIOLoopProxy()->PostTask( 498 FROM_HERE, 499 base::Bind(&BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO, 500 base::Unretained(this), 501 handle, 502 sync_point)); 503} 504 505void BrowserGpuChannelHostFactory::CreateGpuMemoryBufferOnIO( 506 const gfx::GpuMemoryBufferHandle& handle, 507 const gfx::Size& size, 508 unsigned internalformat, 509 unsigned usage, 510 uint32 request_id) { 511 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 512 if (!host) { 513 GpuMemoryBufferCreatedOnIO(request_id, gfx::GpuMemoryBufferHandle()); 514 return; 515 } 516 517 host->CreateGpuMemoryBuffer( 518 handle, 519 size, 520 internalformat, 521 usage, 522 base::Bind(&BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO, 523 base::Unretained(this), 524 request_id)); 525} 526 527void BrowserGpuChannelHostFactory::GpuMemoryBufferCreatedOnIO( 528 uint32 request_id, 529 const gfx::GpuMemoryBufferHandle& handle) { 530 BrowserThread::PostTask( 531 BrowserThread::UI, 532 FROM_HERE, 533 base::Bind(&BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated, 534 base::Unretained(this), 535 request_id, 536 handle)); 537} 538 539void BrowserGpuChannelHostFactory::OnGpuMemoryBufferCreated( 540 uint32 request_id, 541 const gfx::GpuMemoryBufferHandle& handle) { 542 CreateGpuMemoryBufferCallbackMap::iterator iter = 543 create_gpu_memory_buffer_requests_.find(request_id); 544 DCHECK(iter != create_gpu_memory_buffer_requests_.end()); 545 iter->second.Run(handle); 546 create_gpu_memory_buffer_requests_.erase(iter); 547} 548 549void BrowserGpuChannelHostFactory::DestroyGpuMemoryBufferOnIO( 550 const gfx::GpuMemoryBufferHandle& handle, 551 int32 sync_point) { 552 GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); 553 if (!host) 554 return; 555 556 host->DestroyGpuMemoryBuffer(handle, sync_point); 557} 558 559} // namespace content 560