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/command_buffer_proxy_impl.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/debug/trace_event.h"
      9 #include "base/logging.h"
     10 #include "base/memory/shared_memory.h"
     11 #include "base/stl_util.h"
     12 #include "content/common/child_process_messages.h"
     13 #include "content/common/gpu/client/gpu_channel_host.h"
     14 #include "content/common/gpu/client/gpu_video_decode_accelerator_host.h"
     15 #include "content/common/gpu/gpu_messages.h"
     16 #include "content/common/view_messages.h"
     17 #include "gpu/command_buffer/common/cmd_buffer_common.h"
     18 #include "gpu/command_buffer/common/command_buffer_shared.h"
     19 #include "gpu/command_buffer/common/gpu_memory_allocation.h"
     20 #include "ui/gfx/size.h"
     21 
     22 namespace content {
     23 
     24 CommandBufferProxyImpl::CommandBufferProxyImpl(
     25     GpuChannelHost* channel,
     26     int route_id)
     27     : channel_(channel),
     28       route_id_(route_id),
     29       flush_count_(0),
     30       last_put_offset_(-1),
     31       next_signal_id_(0) {
     32 }
     33 
     34 CommandBufferProxyImpl::~CommandBufferProxyImpl() {
     35   FOR_EACH_OBSERVER(DeletionObserver,
     36                     deletion_observers_,
     37                     OnWillDeleteImpl());
     38 
     39   // Delete all the locally cached shared memory objects, closing the handle
     40   // in this process.
     41   for (TransferBufferMap::iterator it = transfer_buffers_.begin();
     42        it != transfer_buffers_.end();
     43        ++it) {
     44     delete it->second.shared_memory;
     45     it->second.shared_memory = NULL;
     46   }
     47 }
     48 
     49 bool CommandBufferProxyImpl::OnMessageReceived(const IPC::Message& message) {
     50   bool handled = true;
     51   IPC_BEGIN_MESSAGE_MAP(CommandBufferProxyImpl, message)
     52     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_Destroyed, OnDestroyed);
     53     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_EchoAck, OnEchoAck);
     54     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_ConsoleMsg, OnConsoleMessage);
     55     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SetMemoryAllocation,
     56                         OnSetMemoryAllocation);
     57     IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalSyncPointAck,
     58                         OnSignalSyncPointAck);
     59     IPC_MESSAGE_UNHANDLED(handled = false)
     60   IPC_END_MESSAGE_MAP()
     61 
     62   DCHECK(handled);
     63   return handled;
     64 }
     65 
     66 void CommandBufferProxyImpl::OnChannelError() {
     67   OnDestroyed(gpu::error::kUnknown);
     68 }
     69 
     70 void CommandBufferProxyImpl::OnDestroyed(gpu::error::ContextLostReason reason) {
     71   // Prevent any further messages from being sent.
     72   channel_ = NULL;
     73 
     74   // When the client sees that the context is lost, they should delete this
     75   // CommandBufferProxyImpl and create a new one.
     76   last_state_.error = gpu::error::kLostContext;
     77   last_state_.context_lost_reason = reason;
     78 
     79   if (!channel_error_callback_.is_null()) {
     80     channel_error_callback_.Run();
     81     // Avoid calling the error callback more than once.
     82     channel_error_callback_.Reset();
     83   }
     84 }
     85 
     86 void CommandBufferProxyImpl::OnEchoAck() {
     87   DCHECK(!echo_tasks_.empty());
     88   base::Closure callback = echo_tasks_.front();
     89   echo_tasks_.pop();
     90   callback.Run();
     91 }
     92 
     93 void CommandBufferProxyImpl::OnConsoleMessage(
     94     const GPUCommandBufferConsoleMessage& message) {
     95   if (!console_message_callback_.is_null()) {
     96     console_message_callback_.Run(message.message, message.id);
     97   }
     98 }
     99 
    100 void CommandBufferProxyImpl::SetMemoryAllocationChangedCallback(
    101     const MemoryAllocationChangedCallback& callback) {
    102   if (last_state_.error != gpu::error::kNoError)
    103     return;
    104 
    105   memory_allocation_changed_callback_ = callback;
    106   Send(new GpuCommandBufferMsg_SetClientHasMemoryAllocationChangedCallback(
    107       route_id_, !memory_allocation_changed_callback_.is_null()));
    108 }
    109 
    110 void CommandBufferProxyImpl::AddDeletionObserver(DeletionObserver* observer) {
    111   deletion_observers_.AddObserver(observer);
    112 }
    113 
    114 void CommandBufferProxyImpl::RemoveDeletionObserver(
    115     DeletionObserver* observer) {
    116   deletion_observers_.RemoveObserver(observer);
    117 }
    118 
    119 void CommandBufferProxyImpl::OnSetMemoryAllocation(
    120     const gpu::MemoryAllocation& allocation) {
    121   if (!memory_allocation_changed_callback_.is_null())
    122     memory_allocation_changed_callback_.Run(allocation);
    123 }
    124 
    125 void CommandBufferProxyImpl::OnSignalSyncPointAck(uint32 id) {
    126   SignalTaskMap::iterator it = signal_tasks_.find(id);
    127   DCHECK(it != signal_tasks_.end());
    128   base::Closure callback = it->second;
    129   signal_tasks_.erase(it);
    130   callback.Run();
    131 }
    132 
    133 void CommandBufferProxyImpl::SetChannelErrorCallback(
    134     const base::Closure& callback) {
    135   channel_error_callback_ = callback;
    136 }
    137 
    138 bool CommandBufferProxyImpl::Initialize() {
    139   shared_state_shm_.reset(channel_->factory()->AllocateSharedMemory(
    140       sizeof(*shared_state())).release());
    141   if (!shared_state_shm_)
    142     return false;
    143 
    144   if (!shared_state_shm_->Map(sizeof(*shared_state())))
    145     return false;
    146 
    147   shared_state()->Initialize();
    148 
    149   // This handle is owned by the GPU process and must be passed to it or it
    150   // will leak. In otherwords, do not early out on error between here and the
    151   // sending of the Initialize IPC below.
    152   base::SharedMemoryHandle handle =
    153       channel_->ShareToGpuProcess(shared_state_shm_->handle());
    154   if (!base::SharedMemory::IsHandleValid(handle))
    155     return false;
    156 
    157   bool result;
    158   if (!Send(new GpuCommandBufferMsg_Initialize(
    159       route_id_, handle, &result, &capabilities_))) {
    160     LOG(ERROR) << "Could not send GpuCommandBufferMsg_Initialize.";
    161     return false;
    162   }
    163 
    164   if (!result) {
    165     LOG(ERROR) << "Failed to initialize command buffer service.";
    166     return false;
    167   }
    168 
    169   capabilities_.map_image = true;
    170 
    171   return true;
    172 }
    173 
    174 gpu::CommandBuffer::State CommandBufferProxyImpl::GetState() {
    175   // Send will flag state with lost context if IPC fails.
    176   if (last_state_.error == gpu::error::kNoError) {
    177     gpu::CommandBuffer::State state;
    178     if (Send(new GpuCommandBufferMsg_GetState(route_id_, &state)))
    179       OnUpdateState(state);
    180   }
    181 
    182   TryUpdateState();
    183   return last_state_;
    184 }
    185 
    186 gpu::CommandBuffer::State CommandBufferProxyImpl::GetLastState() {
    187   return last_state_;
    188 }
    189 
    190 int32 CommandBufferProxyImpl::GetLastToken() {
    191   TryUpdateState();
    192   return last_state_.token;
    193 }
    194 
    195 void CommandBufferProxyImpl::Flush(int32 put_offset) {
    196   if (last_state_.error != gpu::error::kNoError)
    197     return;
    198 
    199   TRACE_EVENT1("gpu",
    200                "CommandBufferProxyImpl::Flush",
    201                "put_offset",
    202                put_offset);
    203 
    204   if (last_put_offset_ == put_offset)
    205     return;
    206 
    207   last_put_offset_ = put_offset;
    208 
    209   Send(new GpuCommandBufferMsg_AsyncFlush(route_id_,
    210                                           put_offset,
    211                                           ++flush_count_));
    212 }
    213 
    214 void CommandBufferProxyImpl::SetLatencyInfo(
    215     const ui::LatencyInfo& latency_info) {
    216   if (last_state_.error != gpu::error::kNoError)
    217     return;
    218   Send(new GpuCommandBufferMsg_SetLatencyInfo(route_id_, latency_info));
    219 }
    220 
    221 gpu::CommandBuffer::State CommandBufferProxyImpl::FlushSync(
    222     int32 put_offset,
    223     int32 last_known_get) {
    224   TRACE_EVENT1("gpu", "CommandBufferProxyImpl::FlushSync", "put_offset",
    225                put_offset);
    226   Flush(put_offset);
    227   TryUpdateState();
    228   if (last_known_get == last_state_.get_offset) {
    229     // Send will flag state with lost context if IPC fails.
    230     if (last_state_.error == gpu::error::kNoError) {
    231       gpu::CommandBuffer::State state;
    232       if (Send(new GpuCommandBufferMsg_GetStateFast(route_id_,
    233                                                     &state)))
    234         OnUpdateState(state);
    235     }
    236     TryUpdateState();
    237   }
    238 
    239   return last_state_;
    240 }
    241 
    242 void CommandBufferProxyImpl::SetGetBuffer(int32 shm_id) {
    243   if (last_state_.error != gpu::error::kNoError)
    244     return;
    245 
    246   Send(new GpuCommandBufferMsg_SetGetBuffer(route_id_, shm_id));
    247   last_put_offset_ = -1;
    248 }
    249 
    250 void CommandBufferProxyImpl::SetGetOffset(int32 get_offset) {
    251   // Not implemented in proxy.
    252   NOTREACHED();
    253 }
    254 
    255 gpu::Buffer CommandBufferProxyImpl::CreateTransferBuffer(size_t size,
    256                                                          int32* id) {
    257   *id = -1;
    258 
    259   if (last_state_.error != gpu::error::kNoError)
    260     return gpu::Buffer();
    261 
    262   int32 new_id = channel_->ReserveTransferBufferId();
    263   DCHECK(transfer_buffers_.find(new_id) == transfer_buffers_.end());
    264 
    265   scoped_ptr<base::SharedMemory> shared_memory(
    266       channel_->factory()->AllocateSharedMemory(size));
    267   if (!shared_memory)
    268     return gpu::Buffer();
    269 
    270   DCHECK(!shared_memory->memory());
    271   if (!shared_memory->Map(size))
    272     return gpu::Buffer();
    273 
    274   // This handle is owned by the GPU process and must be passed to it or it
    275   // will leak. In otherwords, do not early out on error between here and the
    276   // sending of the RegisterTransferBuffer IPC below.
    277   base::SharedMemoryHandle handle =
    278       channel_->ShareToGpuProcess(shared_memory->handle());
    279   if (!base::SharedMemory::IsHandleValid(handle))
    280     return gpu::Buffer();
    281 
    282   if (!Send(new GpuCommandBufferMsg_RegisterTransferBuffer(route_id_,
    283                                                            new_id,
    284                                                            handle,
    285                                                            size))) {
    286     return gpu::Buffer();
    287   }
    288 
    289   *id = new_id;
    290   gpu::Buffer buffer;
    291   buffer.ptr = shared_memory->memory();
    292   buffer.size = size;
    293   buffer.shared_memory = shared_memory.release();
    294   transfer_buffers_[new_id] = buffer;
    295 
    296   return buffer;
    297 }
    298 
    299 void CommandBufferProxyImpl::DestroyTransferBuffer(int32 id) {
    300   if (last_state_.error != gpu::error::kNoError)
    301     return;
    302 
    303   // Remove the transfer buffer from the client side cache.
    304   TransferBufferMap::iterator it = transfer_buffers_.find(id);
    305   if (it != transfer_buffers_.end()) {
    306     delete it->second.shared_memory;
    307     transfer_buffers_.erase(it);
    308   }
    309 
    310   Send(new GpuCommandBufferMsg_DestroyTransferBuffer(route_id_, id));
    311 }
    312 
    313 gpu::Buffer CommandBufferProxyImpl::GetTransferBuffer(int32 id) {
    314   if (last_state_.error != gpu::error::kNoError)
    315     return gpu::Buffer();
    316 
    317   // Check local cache to see if there is already a client side shared memory
    318   // object for this id.
    319   TransferBufferMap::iterator it = transfer_buffers_.find(id);
    320   if (it != transfer_buffers_.end()) {
    321     return it->second;
    322   }
    323 
    324   // Assuming we are in the renderer process, the service is responsible for
    325   // duplicating the handle. This might not be true for NaCl.
    326   base::SharedMemoryHandle handle = base::SharedMemoryHandle();
    327   uint32 size;
    328   if (!Send(new GpuCommandBufferMsg_GetTransferBuffer(route_id_,
    329                                                       id,
    330                                                       &handle,
    331                                                       &size))) {
    332     return gpu::Buffer();
    333   }
    334 
    335   // Cache the transfer buffer shared memory object client side.
    336   scoped_ptr<base::SharedMemory> shared_memory(
    337       new base::SharedMemory(handle, false));
    338 
    339   // Map the shared memory on demand.
    340   if (!shared_memory->memory()) {
    341     if (!shared_memory->Map(size))
    342       return gpu::Buffer();
    343   }
    344 
    345   gpu::Buffer buffer;
    346   buffer.ptr = shared_memory->memory();
    347   buffer.size = size;
    348   buffer.shared_memory = shared_memory.release();
    349   transfer_buffers_[id] = buffer;
    350 
    351   return buffer;
    352 }
    353 
    354 void CommandBufferProxyImpl::SetToken(int32 token) {
    355   // Not implemented in proxy.
    356   NOTREACHED();
    357 }
    358 
    359 void CommandBufferProxyImpl::SetParseError(
    360     gpu::error::Error error) {
    361   // Not implemented in proxy.
    362   NOTREACHED();
    363 }
    364 
    365 void CommandBufferProxyImpl::SetContextLostReason(
    366     gpu::error::ContextLostReason reason) {
    367   // Not implemented in proxy.
    368   NOTREACHED();
    369 }
    370 
    371 gpu::Capabilities CommandBufferProxyImpl::GetCapabilities() {
    372   return capabilities_;
    373 }
    374 
    375 gfx::GpuMemoryBuffer* CommandBufferProxyImpl::CreateGpuMemoryBuffer(
    376     size_t width,
    377     size_t height,
    378     unsigned internalformat,
    379     int32* id) {
    380   *id = -1;
    381 
    382   if (last_state_.error != gpu::error::kNoError)
    383     return NULL;
    384 
    385   int32 new_id = channel_->ReserveGpuMemoryBufferId();
    386   DCHECK(gpu_memory_buffers_.find(new_id) == gpu_memory_buffers_.end());
    387 
    388   scoped_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer(
    389       channel_->factory()->AllocateGpuMemoryBuffer(width,
    390                                                    height,
    391                                                    internalformat));
    392   if (!gpu_memory_buffer)
    393     return NULL;
    394 
    395   DCHECK(GpuChannelHost::IsValidGpuMemoryBuffer(
    396              gpu_memory_buffer->GetHandle()));
    397 
    398   // This handle is owned by the GPU process and must be passed to it or it
    399   // will leak. In otherwords, do not early out on error between here and the
    400   // sending of the RegisterGpuMemoryBuffer IPC below.
    401   gfx::GpuMemoryBufferHandle handle =
    402       channel_->ShareGpuMemoryBufferToGpuProcess(
    403           gpu_memory_buffer->GetHandle());
    404 
    405   if (!Send(new GpuCommandBufferMsg_RegisterGpuMemoryBuffer(
    406                 route_id_,
    407                 new_id,
    408                 handle,
    409                 width,
    410                 height,
    411                 internalformat))) {
    412     return NULL;
    413   }
    414 
    415   *id = new_id;
    416   gpu_memory_buffers_[new_id] = gpu_memory_buffer.release();
    417   return gpu_memory_buffers_[new_id];
    418 }
    419 
    420 void CommandBufferProxyImpl::DestroyGpuMemoryBuffer(int32 id) {
    421   if (last_state_.error != gpu::error::kNoError)
    422     return;
    423 
    424   // Remove the gpu memory buffer from the client side cache.
    425   GpuMemoryBufferMap::iterator it = gpu_memory_buffers_.find(id);
    426   if (it != gpu_memory_buffers_.end()) {
    427     delete it->second;
    428     gpu_memory_buffers_.erase(it);
    429   }
    430 
    431   Send(new GpuCommandBufferMsg_DestroyGpuMemoryBuffer(route_id_, id));
    432 }
    433 
    434 int CommandBufferProxyImpl::GetRouteID() const {
    435   return route_id_;
    436 }
    437 
    438 void CommandBufferProxyImpl::Echo(const base::Closure& callback) {
    439   if (last_state_.error != gpu::error::kNoError) {
    440     return;
    441   }
    442 
    443   if (!Send(new GpuCommandBufferMsg_Echo(
    444            route_id_, GpuCommandBufferMsg_EchoAck(route_id_)))) {
    445     return;
    446   }
    447 
    448   echo_tasks_.push(callback);
    449 }
    450 
    451 uint32 CommandBufferProxyImpl::InsertSyncPoint() {
    452   if (last_state_.error != gpu::error::kNoError)
    453     return 0;
    454 
    455   uint32 sync_point = 0;
    456   Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_, &sync_point));
    457   return sync_point;
    458 }
    459 
    460 void CommandBufferProxyImpl::SignalSyncPoint(uint32 sync_point,
    461                                              const base::Closure& callback) {
    462   if (last_state_.error != gpu::error::kNoError)
    463     return;
    464 
    465   uint32 signal_id = next_signal_id_++;
    466   if (!Send(new GpuCommandBufferMsg_SignalSyncPoint(route_id_,
    467                                                     sync_point,
    468                                                     signal_id))) {
    469     return;
    470   }
    471 
    472   signal_tasks_.insert(std::make_pair(signal_id, callback));
    473 }
    474 
    475 void CommandBufferProxyImpl::SignalQuery(uint32 query,
    476                                          const base::Closure& callback) {
    477   if (last_state_.error != gpu::error::kNoError)
    478     return;
    479 
    480   // Signal identifiers are hidden, so nobody outside of this class will see
    481   // them. (And thus, they cannot save them.) The IDs themselves only last
    482   // until the callback is invoked, which will happen as soon as the GPU
    483   // catches upwith the command buffer.
    484   // A malicious caller trying to create a collision by making next_signal_id
    485   // would have to make calls at an astounding rate (300B/s) and even if they
    486   // could do that, all they would do is to prevent some callbacks from getting
    487   // called, leading to stalled threads and/or memory leaks.
    488   uint32 signal_id = next_signal_id_++;
    489   if (!Send(new GpuCommandBufferMsg_SignalQuery(route_id_,
    490                                                 query,
    491                                                 signal_id))) {
    492     return;
    493   }
    494 
    495   signal_tasks_.insert(std::make_pair(signal_id, callback));
    496 }
    497 
    498 void CommandBufferProxyImpl::SetSurfaceVisible(bool visible) {
    499   if (last_state_.error != gpu::error::kNoError)
    500     return;
    501 
    502   Send(new GpuCommandBufferMsg_SetSurfaceVisible(route_id_, visible));
    503 }
    504 
    505 void CommandBufferProxyImpl::SendManagedMemoryStats(
    506     const gpu::ManagedMemoryStats& stats) {
    507   if (last_state_.error != gpu::error::kNoError)
    508     return;
    509 
    510   Send(new GpuCommandBufferMsg_SendClientManagedMemoryStats(route_id_,
    511                                                             stats));
    512 }
    513 
    514 bool CommandBufferProxyImpl::GenerateMailboxNames(
    515     unsigned num,
    516     std::vector<gpu::Mailbox>* names) {
    517   if (last_state_.error != gpu::error::kNoError)
    518     return false;
    519 
    520   return channel_->GenerateMailboxNames(num, names);
    521 }
    522 
    523 bool CommandBufferProxyImpl::ProduceFrontBuffer(const gpu::Mailbox& mailbox) {
    524   if (last_state_.error != gpu::error::kNoError)
    525     return false;
    526 
    527   return Send(new GpuCommandBufferMsg_ProduceFrontBuffer(route_id_, mailbox));
    528 }
    529 
    530 scoped_ptr<media::VideoDecodeAccelerator>
    531 CommandBufferProxyImpl::CreateVideoDecoder(
    532     media::VideoCodecProfile profile,
    533     media::VideoDecodeAccelerator::Client* client) {
    534   int decoder_route_id;
    535   scoped_ptr<media::VideoDecodeAccelerator> vda;
    536   if (!Send(new GpuCommandBufferMsg_CreateVideoDecoder(route_id_, profile,
    537                                                        &decoder_route_id))) {
    538     LOG(ERROR) << "Send(GpuCommandBufferMsg_CreateVideoDecoder) failed";
    539     return vda.Pass();
    540   }
    541 
    542   if (decoder_route_id < 0) {
    543     DLOG(ERROR) << "Failed to Initialize GPU decoder on profile: " << profile;
    544     return vda.Pass();
    545   }
    546 
    547   GpuVideoDecodeAcceleratorHost* decoder_host =
    548       new GpuVideoDecodeAcceleratorHost(channel_, decoder_route_id, client,
    549                                         this);
    550   vda.reset(decoder_host);
    551   return vda.Pass();
    552 }
    553 
    554 gpu::error::Error CommandBufferProxyImpl::GetLastError() {
    555   return last_state_.error;
    556 }
    557 
    558 bool CommandBufferProxyImpl::Send(IPC::Message* msg) {
    559   // Caller should not intentionally send a message if the context is lost.
    560   DCHECK(last_state_.error == gpu::error::kNoError);
    561 
    562   if (channel_) {
    563     if (channel_->Send(msg)) {
    564       return true;
    565     } else {
    566       // Flag the command buffer as lost. Defer deleting the channel until
    567       // OnChannelError is called after returning to the message loop in case
    568       // it is referenced elsewhere.
    569       last_state_.error = gpu::error::kLostContext;
    570       return false;
    571     }
    572   }
    573 
    574   // Callee takes ownership of message, regardless of whether Send is
    575   // successful. See IPC::Sender.
    576   delete msg;
    577   return false;
    578 }
    579 
    580 void CommandBufferProxyImpl::OnUpdateState(
    581     const gpu::CommandBuffer::State& state) {
    582   // Handle wraparound. It works as long as we don't have more than 2B state
    583   // updates in flight across which reordering occurs.
    584   if (state.generation - last_state_.generation < 0x80000000U)
    585     last_state_ = state;
    586 }
    587 
    588 void CommandBufferProxyImpl::SetOnConsoleMessageCallback(
    589     const GpuConsoleMessageCallback& callback) {
    590   console_message_callback_ = callback;
    591 }
    592 
    593 void CommandBufferProxyImpl::TryUpdateState() {
    594   if (last_state_.error == gpu::error::kNoError)
    595     shared_state()->Read(&last_state_);
    596 }
    597 
    598 }  // namespace content
    599