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