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