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