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/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