Home | History | Annotate | Download | only in gpu
      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