Home | History | Annotate | Download | only in client
      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/common/gpu/client/gpu_channel_host.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/debug/trace_event.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/message_loop/message_loop_proxy.h"
     13 #include "base/posix/eintr_wrapper.h"
     14 #include "base/threading/thread_restrictions.h"
     15 #include "content/common/gpu/client/command_buffer_proxy_impl.h"
     16 #include "content/common/gpu/gpu_messages.h"
     17 #include "gpu/command_buffer/common/mailbox.h"
     18 #include "ipc/ipc_sync_message_filter.h"
     19 #include "url/gurl.h"
     20 
     21 #if defined(OS_WIN)
     22 #include "content/public/common/sandbox_init.h"
     23 #endif
     24 
     25 using base::AutoLock;
     26 using base::MessageLoopProxy;
     27 
     28 namespace content {
     29 
     30 GpuListenerInfo::GpuListenerInfo() {}
     31 
     32 GpuListenerInfo::~GpuListenerInfo() {}
     33 
     34 // static
     35 scoped_refptr<GpuChannelHost> GpuChannelHost::Create(
     36     GpuChannelHostFactory* factory,
     37     int gpu_host_id,
     38     int client_id,
     39     const gpu::GPUInfo& gpu_info,
     40     const IPC::ChannelHandle& channel_handle) {
     41   DCHECK(factory->IsMainThread());
     42   scoped_refptr<GpuChannelHost> host = new GpuChannelHost(
     43       factory, gpu_host_id, client_id, gpu_info);
     44   host->Connect(channel_handle);
     45   return host;
     46 }
     47 
     48 GpuChannelHost::GpuChannelHost(GpuChannelHostFactory* factory,
     49                                int gpu_host_id,
     50                                int client_id,
     51                                const gpu::GPUInfo& gpu_info)
     52     : factory_(factory),
     53       client_id_(client_id),
     54       gpu_host_id_(gpu_host_id),
     55       gpu_info_(gpu_info) {
     56   next_transfer_buffer_id_.GetNext();
     57 }
     58 
     59 void GpuChannelHost::Connect(const IPC::ChannelHandle& channel_handle) {
     60   // Open a channel to the GPU process. We pass NULL as the main listener here
     61   // since we need to filter everything to route it to the right thread.
     62   scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy();
     63   channel_.reset(new IPC::SyncChannel(channel_handle,
     64                                       IPC::Channel::MODE_CLIENT,
     65                                       NULL,
     66                                       io_loop.get(),
     67                                       true,
     68                                       factory_->GetShutDownEvent()));
     69 
     70   sync_filter_ = new IPC::SyncMessageFilter(
     71       factory_->GetShutDownEvent());
     72 
     73   channel_->AddFilter(sync_filter_.get());
     74 
     75   channel_filter_ = new MessageFilter();
     76 
     77   // Install the filter last, because we intercept all leftover
     78   // messages.
     79   channel_->AddFilter(channel_filter_.get());
     80 }
     81 
     82 bool GpuChannelHost::Send(IPC::Message* msg) {
     83   // Callee takes ownership of message, regardless of whether Send is
     84   // successful. See IPC::Sender.
     85   scoped_ptr<IPC::Message> message(msg);
     86   // The GPU process never sends synchronous IPCs so clear the unblock flag to
     87   // preserve order.
     88   message->set_unblock(false);
     89 
     90   // Currently we need to choose between two different mechanisms for sending.
     91   // On the main thread we use the regular channel Send() method, on another
     92   // thread we use SyncMessageFilter. We also have to be careful interpreting
     93   // IsMainThread() since it might return false during shutdown,
     94   // impl we are actually calling from the main thread (discard message then).
     95   //
     96   // TODO: Can we just always use sync_filter_ since we setup the channel
     97   //       without a main listener?
     98   if (factory_->IsMainThread()) {
     99     // http://crbug.com/125264
    100     base::ThreadRestrictions::ScopedAllowWait allow_wait;
    101     return channel_->Send(message.release());
    102   } else if (base::MessageLoop::current()) {
    103     return sync_filter_->Send(message.release());
    104   }
    105 
    106   return false;
    107 }
    108 
    109 CommandBufferProxyImpl* GpuChannelHost::CreateViewCommandBuffer(
    110     int32 surface_id,
    111     CommandBufferProxyImpl* share_group,
    112     const std::string& allowed_extensions,
    113     const std::vector<int32>& attribs,
    114     const GURL& active_url,
    115     gfx::GpuPreference gpu_preference) {
    116   TRACE_EVENT1("gpu",
    117                "GpuChannelHost::CreateViewCommandBuffer",
    118                "surface_id",
    119                surface_id);
    120 
    121   GPUCreateCommandBufferConfig init_params;
    122   init_params.share_group_id =
    123       share_group ? share_group->GetRouteID() : MSG_ROUTING_NONE;
    124   init_params.allowed_extensions = allowed_extensions;
    125   init_params.attribs = attribs;
    126   init_params.active_url = active_url;
    127   init_params.gpu_preference = gpu_preference;
    128   int32 route_id = factory_->CreateViewCommandBuffer(surface_id, init_params);
    129   if (route_id == MSG_ROUTING_NONE)
    130     return NULL;
    131 
    132   CommandBufferProxyImpl* command_buffer =
    133       new CommandBufferProxyImpl(this, route_id);
    134   AddRoute(route_id, command_buffer->AsWeakPtr());
    135 
    136   AutoLock lock(context_lock_);
    137   proxies_[route_id] = command_buffer;
    138   return command_buffer;
    139 }
    140 
    141 CommandBufferProxyImpl* GpuChannelHost::CreateOffscreenCommandBuffer(
    142     const gfx::Size& size,
    143     CommandBufferProxyImpl* share_group,
    144     const std::string& allowed_extensions,
    145     const std::vector<int32>& attribs,
    146     const GURL& active_url,
    147     gfx::GpuPreference gpu_preference) {
    148   TRACE_EVENT0("gpu", "GpuChannelHost::CreateOffscreenCommandBuffer");
    149 
    150   GPUCreateCommandBufferConfig init_params;
    151   init_params.share_group_id =
    152       share_group ? share_group->GetRouteID() : MSG_ROUTING_NONE;
    153   init_params.allowed_extensions = allowed_extensions;
    154   init_params.attribs = attribs;
    155   init_params.active_url = active_url;
    156   init_params.gpu_preference = gpu_preference;
    157   int32 route_id;
    158   if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size,
    159                                                            init_params,
    160                                                            &route_id))) {
    161     return NULL;
    162   }
    163 
    164   if (route_id == MSG_ROUTING_NONE)
    165     return NULL;
    166 
    167   CommandBufferProxyImpl* command_buffer =
    168       new CommandBufferProxyImpl(this, route_id);
    169   AddRoute(route_id, command_buffer->AsWeakPtr());
    170 
    171   AutoLock lock(context_lock_);
    172   proxies_[route_id] = command_buffer;
    173   return command_buffer;
    174 }
    175 
    176 scoped_ptr<media::VideoDecodeAccelerator> GpuChannelHost::CreateVideoDecoder(
    177     int command_buffer_route_id,
    178     media::VideoCodecProfile profile,
    179     media::VideoDecodeAccelerator::Client* client) {
    180   AutoLock lock(context_lock_);
    181   ProxyMap::iterator it = proxies_.find(command_buffer_route_id);
    182   DCHECK(it != proxies_.end());
    183   CommandBufferProxyImpl* proxy = it->second;
    184   return proxy->CreateVideoDecoder(profile, client).Pass();
    185 }
    186 
    187 void GpuChannelHost::DestroyCommandBuffer(
    188     CommandBufferProxyImpl* command_buffer) {
    189   TRACE_EVENT0("gpu", "GpuChannelHost::DestroyCommandBuffer");
    190 
    191   int route_id = command_buffer->GetRouteID();
    192   Send(new GpuChannelMsg_DestroyCommandBuffer(route_id));
    193   RemoveRoute(route_id);
    194 
    195   AutoLock lock(context_lock_);
    196   proxies_.erase(route_id);
    197   delete command_buffer;
    198 }
    199 
    200 bool GpuChannelHost::CollectRenderingStatsForSurface(
    201     int surface_id, GpuRenderingStats* stats) {
    202   TRACE_EVENT0("gpu", "GpuChannelHost::CollectRenderingStats");
    203 
    204   return Send(new GpuChannelMsg_CollectRenderingStatsForSurface(surface_id,
    205                                                                 stats));
    206 }
    207 
    208 void GpuChannelHost::AddRoute(
    209     int route_id, base::WeakPtr<IPC::Listener> listener) {
    210   DCHECK(MessageLoopProxy::current().get());
    211 
    212   scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy();
    213   io_loop->PostTask(FROM_HERE,
    214                     base::Bind(&GpuChannelHost::MessageFilter::AddRoute,
    215                                channel_filter_.get(), route_id, listener,
    216                                MessageLoopProxy::current()));
    217 }
    218 
    219 void GpuChannelHost::RemoveRoute(int route_id) {
    220   scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy();
    221   io_loop->PostTask(FROM_HERE,
    222                     base::Bind(&GpuChannelHost::MessageFilter::RemoveRoute,
    223                                channel_filter_.get(), route_id));
    224 }
    225 
    226 base::SharedMemoryHandle GpuChannelHost::ShareToGpuProcess(
    227     base::SharedMemoryHandle source_handle) {
    228   if (IsLost())
    229     return base::SharedMemory::NULLHandle();
    230 
    231 #if defined(OS_WIN)
    232   // Windows needs to explicitly duplicate the handle out to another process.
    233   base::SharedMemoryHandle target_handle;
    234   if (!BrokerDuplicateHandle(source_handle,
    235                              channel_->peer_pid(),
    236                              &target_handle,
    237                              0,
    238                              DUPLICATE_SAME_ACCESS)) {
    239     return base::SharedMemory::NULLHandle();
    240   }
    241 
    242   return target_handle;
    243 #else
    244   int duped_handle = HANDLE_EINTR(dup(source_handle.fd));
    245   if (duped_handle < 0)
    246     return base::SharedMemory::NULLHandle();
    247 
    248   return base::FileDescriptor(duped_handle, true);
    249 #endif
    250 }
    251 
    252 bool GpuChannelHost::GenerateMailboxNames(unsigned num,
    253                                           std::vector<gpu::Mailbox>* names) {
    254   DCHECK(names->empty());
    255   TRACE_EVENT0("gpu", "GenerateMailboxName");
    256   size_t generate_count = channel_filter_->GetMailboxNames(num, names);
    257 
    258   if (names->size() < num) {
    259     std::vector<gpu::Mailbox> new_names;
    260     if (!Send(new GpuChannelMsg_GenerateMailboxNames(num - names->size(),
    261                                                      &new_names)))
    262       return false;
    263     names->insert(names->end(), new_names.begin(), new_names.end());
    264   }
    265 
    266   if (generate_count > 0)
    267     Send(new GpuChannelMsg_GenerateMailboxNamesAsync(generate_count));
    268 
    269   return true;
    270 }
    271 
    272 int32 GpuChannelHost::ReserveTransferBufferId() {
    273   return next_transfer_buffer_id_.GetNext();
    274 }
    275 
    276 GpuChannelHost::~GpuChannelHost() {
    277   // channel_ must be destroyed on the main thread.
    278   if (!factory_->IsMainThread())
    279     factory_->GetMainLoop()->DeleteSoon(FROM_HERE, channel_.release());
    280 }
    281 
    282 
    283 GpuChannelHost::MessageFilter::MessageFilter()
    284     : lost_(false),
    285       requested_mailboxes_(0) {
    286 }
    287 
    288 GpuChannelHost::MessageFilter::~MessageFilter() {}
    289 
    290 void GpuChannelHost::MessageFilter::AddRoute(
    291     int route_id,
    292     base::WeakPtr<IPC::Listener> listener,
    293     scoped_refptr<MessageLoopProxy> loop) {
    294   DCHECK(listeners_.find(route_id) == listeners_.end());
    295   GpuListenerInfo info;
    296   info.listener = listener;
    297   info.loop = loop;
    298   listeners_[route_id] = info;
    299 }
    300 
    301 void GpuChannelHost::MessageFilter::RemoveRoute(int route_id) {
    302   ListenerMap::iterator it = listeners_.find(route_id);
    303   if (it != listeners_.end())
    304     listeners_.erase(it);
    305 }
    306 
    307 bool GpuChannelHost::MessageFilter::OnMessageReceived(
    308     const IPC::Message& message) {
    309   // Never handle sync message replies or we will deadlock here.
    310   if (message.is_reply())
    311     return false;
    312 
    313   if (message.routing_id() == MSG_ROUTING_CONTROL)
    314     return OnControlMessageReceived(message);
    315 
    316   ListenerMap::iterator it = listeners_.find(message.routing_id());
    317 
    318   if (it != listeners_.end()) {
    319     const GpuListenerInfo& info = it->second;
    320     info.loop->PostTask(
    321         FROM_HERE,
    322         base::Bind(
    323             base::IgnoreResult(&IPC::Listener::OnMessageReceived),
    324             info.listener,
    325             message));
    326   }
    327 
    328   return true;
    329 }
    330 
    331 void GpuChannelHost::MessageFilter::OnChannelError() {
    332   // Set the lost state before signalling the proxies. That way, if they
    333   // themselves post a task to recreate the context, they will not try to re-use
    334   // this channel host.
    335   {
    336     AutoLock lock(lock_);
    337     lost_ = true;
    338   }
    339 
    340   // Inform all the proxies that an error has occurred. This will be reported
    341   // via OpenGL as a lost context.
    342   for (ListenerMap::iterator it = listeners_.begin();
    343        it != listeners_.end();
    344        it++) {
    345     const GpuListenerInfo& info = it->second;
    346     info.loop->PostTask(
    347         FROM_HERE,
    348         base::Bind(&IPC::Listener::OnChannelError, info.listener));
    349   }
    350 
    351   listeners_.clear();
    352 }
    353 
    354 bool GpuChannelHost::MessageFilter::IsLost() const {
    355   AutoLock lock(lock_);
    356   return lost_;
    357 }
    358 
    359 size_t GpuChannelHost::MessageFilter::GetMailboxNames(
    360     size_t num, std::vector<gpu::Mailbox>* names) {
    361   AutoLock lock(lock_);
    362   size_t count = std::min(num, mailbox_name_pool_.size());
    363   names->insert(names->begin(),
    364                 mailbox_name_pool_.end() - count,
    365                 mailbox_name_pool_.end());
    366   mailbox_name_pool_.erase(mailbox_name_pool_.end() - count,
    367                            mailbox_name_pool_.end());
    368 
    369   const size_t ideal_mailbox_pool_size = 100;
    370   size_t total = mailbox_name_pool_.size() + requested_mailboxes_;
    371   DCHECK_LE(total, ideal_mailbox_pool_size);
    372   if (total >= ideal_mailbox_pool_size / 2)
    373     return 0;
    374   size_t request = ideal_mailbox_pool_size - total;
    375   requested_mailboxes_ += request;
    376   return request;
    377 }
    378 
    379 bool GpuChannelHost::MessageFilter::OnControlMessageReceived(
    380     const IPC::Message& message) {
    381   bool handled = true;
    382 
    383   IPC_BEGIN_MESSAGE_MAP(GpuChannelHost::MessageFilter, message)
    384   IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNamesReply,
    385                       OnGenerateMailboxNamesReply)
    386   IPC_MESSAGE_UNHANDLED(handled = false)
    387   IPC_END_MESSAGE_MAP()
    388 
    389   DCHECK(handled);
    390   return handled;
    391 }
    392 
    393 void GpuChannelHost::MessageFilter::OnGenerateMailboxNamesReply(
    394     const std::vector<gpu::Mailbox>& names) {
    395   TRACE_EVENT0("gpu", "OnGenerateMailboxNamesReply");
    396   AutoLock lock(lock_);
    397   DCHECK_LE(names.size(), requested_mailboxes_);
    398   requested_mailboxes_ -= names.size();
    399   mailbox_name_pool_.insert(mailbox_name_pool_.end(),
    400                             names.begin(),
    401                             names.end());
    402 }
    403 
    404 
    405 }  // namespace content
    406