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.
5 #include "content/common/gpu/client/command_buffer_proxy_impl.h"
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/client/gpu_video_encode_accelerator_host.h"
16 #include "content/common/gpu/gpu_messages.h"
17 #include "content/common/view_messages.h"
18 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
19 #include "gpu/command_buffer/common/cmd_buffer_common.h"
20 #include "gpu/command_buffer/common/command_buffer_shared.h"
21 #include "gpu/command_buffer/common/gpu_memory_allocation.h"
22 #include "gpu/command_buffer/service/image_factory.h"
23 #include "ui/gfx/size.h"
24 #include "ui/gl/gl_bindings.h"
28 CommandBufferProxyImpl::CommandBufferProxyImpl(
29 GpuChannelHost* channel,
38 CommandBufferProxyImpl::~CommandBufferProxyImpl() {
39 FOR_EACH_OBSERVER(DeletionObserver,
44 bool CommandBufferProxyImpl::OnMessageReceived(const IPC::Message& message) {
46 IPC_BEGIN_MESSAGE_MAP(CommandBufferProxyImpl, message)
47 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_Destroyed, OnDestroyed);
48 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_ConsoleMsg, OnConsoleMessage);
49 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SetMemoryAllocation,
50 OnSetMemoryAllocation);
51 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalSyncPointAck,
52 OnSignalSyncPointAck);
53 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SwapBuffersCompleted,
54 OnSwapBuffersCompleted);
55 IPC_MESSAGE_UNHANDLED(handled = false)
62 void CommandBufferProxyImpl::OnChannelError() {
63 OnDestroyed(gpu::error::kUnknown);
66 void CommandBufferProxyImpl::OnDestroyed(gpu::error::ContextLostReason reason) {
67 // Prevent any further messages from being sent.
70 // When the client sees that the context is lost, they should delete this
71 // CommandBufferProxyImpl and create a new one.
72 last_state_.error = gpu::error::kLostContext;
73 last_state_.context_lost_reason = reason;
75 if (!channel_error_callback_.is_null()) {
76 channel_error_callback_.Run();
77 // Avoid calling the error callback more than once.
78 channel_error_callback_.Reset();
82 void CommandBufferProxyImpl::OnConsoleMessage(
83 const GPUCommandBufferConsoleMessage& message) {
84 if (!console_message_callback_.is_null()) {
85 console_message_callback_.Run(message.message, message.id);
89 void CommandBufferProxyImpl::SetMemoryAllocationChangedCallback(
90 const MemoryAllocationChangedCallback& callback) {
91 if (last_state_.error != gpu::error::kNoError)
94 memory_allocation_changed_callback_ = callback;
95 Send(new GpuCommandBufferMsg_SetClientHasMemoryAllocationChangedCallback(
96 route_id_, !memory_allocation_changed_callback_.is_null()));
99 void CommandBufferProxyImpl::AddDeletionObserver(DeletionObserver* observer) {
100 deletion_observers_.AddObserver(observer);
103 void CommandBufferProxyImpl::RemoveDeletionObserver(
104 DeletionObserver* observer) {
105 deletion_observers_.RemoveObserver(observer);
108 void CommandBufferProxyImpl::OnSetMemoryAllocation(
109 const gpu::MemoryAllocation& allocation) {
110 if (!memory_allocation_changed_callback_.is_null())
111 memory_allocation_changed_callback_.Run(allocation);
114 void CommandBufferProxyImpl::OnSignalSyncPointAck(uint32 id) {
115 SignalTaskMap::iterator it = signal_tasks_.find(id);
116 DCHECK(it != signal_tasks_.end());
117 base::Closure callback = it->second;
118 signal_tasks_.erase(it);
122 void CommandBufferProxyImpl::SetChannelErrorCallback(
123 const base::Closure& callback) {
124 channel_error_callback_ = callback;
127 bool CommandBufferProxyImpl::Initialize() {
128 TRACE_EVENT0("gpu", "CommandBufferProxyImpl::Initialize");
129 shared_state_shm_.reset(channel_->factory()->AllocateSharedMemory(
130 sizeof(*shared_state())).release());
131 if (!shared_state_shm_)
134 if (!shared_state_shm_->Map(sizeof(*shared_state())))
137 shared_state()->Initialize();
139 // This handle is owned by the GPU process and must be passed to it or it
140 // will leak. In otherwords, do not early out on error between here and the
141 // sending of the Initialize IPC below.
142 base::SharedMemoryHandle handle =
143 channel_->ShareToGpuProcess(shared_state_shm_->handle());
144 if (!base::SharedMemory::IsHandleValid(handle))
148 if (!Send(new GpuCommandBufferMsg_Initialize(
149 route_id_, handle, &result, &capabilities_))) {
150 LOG(ERROR) << "Could not send GpuCommandBufferMsg_Initialize.";
155 LOG(ERROR) << "Failed to initialize command buffer service.";
159 capabilities_.image = true;
164 gpu::CommandBuffer::State CommandBufferProxyImpl::GetLastState() {
168 int32 CommandBufferProxyImpl::GetLastToken() {
170 return last_state_.token;
173 void CommandBufferProxyImpl::Flush(int32 put_offset) {
174 if (last_state_.error != gpu::error::kNoError)
178 "CommandBufferProxyImpl::Flush",
182 if (last_put_offset_ == put_offset)
185 last_put_offset_ = put_offset;
187 Send(new GpuCommandBufferMsg_AsyncFlush(route_id_,
191 latency_info_.clear();
194 void CommandBufferProxyImpl::SetLatencyInfo(
195 const std::vector<ui::LatencyInfo>& latency_info) {
196 for (size_t i = 0; i < latency_info.size(); i++)
197 latency_info_.push_back(latency_info[i]);
200 void CommandBufferProxyImpl::SetSwapBuffersCompletionCallback(
201 const SwapBuffersCompletionCallback& callback) {
202 swap_buffers_completion_callback_ = callback;
205 void CommandBufferProxyImpl::WaitForTokenInRange(int32 start, int32 end) {
207 "CommandBufferProxyImpl::WaitForToken",
213 if (!InRange(start, end, last_state_.token) &&
214 last_state_.error == gpu::error::kNoError) {
215 gpu::CommandBuffer::State state;
216 if (Send(new GpuCommandBufferMsg_WaitForTokenInRange(
217 route_id_, start, end, &state)))
218 OnUpdateState(state);
220 DCHECK(InRange(start, end, last_state_.token) ||
221 last_state_.error != gpu::error::kNoError);
224 void CommandBufferProxyImpl::WaitForGetOffsetInRange(int32 start, int32 end) {
226 "CommandBufferProxyImpl::WaitForGetOffset",
232 if (!InRange(start, end, last_state_.get_offset) &&
233 last_state_.error == gpu::error::kNoError) {
234 gpu::CommandBuffer::State state;
235 if (Send(new GpuCommandBufferMsg_WaitForGetOffsetInRange(
236 route_id_, start, end, &state)))
237 OnUpdateState(state);
239 DCHECK(InRange(start, end, last_state_.get_offset) ||
240 last_state_.error != gpu::error::kNoError);
243 void CommandBufferProxyImpl::SetGetBuffer(int32 shm_id) {
244 if (last_state_.error != gpu::error::kNoError)
247 Send(new GpuCommandBufferMsg_SetGetBuffer(route_id_, shm_id));
248 last_put_offset_ = -1;
251 scoped_refptr<gpu::Buffer> CommandBufferProxyImpl::CreateTransferBuffer(
256 if (last_state_.error != gpu::error::kNoError)
259 int32 new_id = channel_->ReserveTransferBufferId();
261 scoped_ptr<base::SharedMemory> shared_memory(
262 channel_->factory()->AllocateSharedMemory(size));
266 DCHECK(!shared_memory->memory());
267 if (!shared_memory->Map(size))
270 // This handle is owned by the GPU process and must be passed to it or it
271 // will leak. In otherwords, do not early out on error between here and the
272 // sending of the RegisterTransferBuffer IPC below.
273 base::SharedMemoryHandle handle =
274 channel_->ShareToGpuProcess(shared_memory->handle());
275 if (!base::SharedMemory::IsHandleValid(handle))
278 if (!Send(new GpuCommandBufferMsg_RegisterTransferBuffer(route_id_,
286 scoped_refptr<gpu::Buffer> buffer(
287 gpu::MakeBufferFromSharedMemory(shared_memory.Pass(), size));
291 void CommandBufferProxyImpl::DestroyTransferBuffer(int32 id) {
292 if (last_state_.error != gpu::error::kNoError)
295 Send(new GpuCommandBufferMsg_DestroyTransferBuffer(route_id_, id));
298 gpu::Capabilities CommandBufferProxyImpl::GetCapabilities() {
299 return capabilities_;
302 int32_t CommandBufferProxyImpl::CreateImage(ClientBuffer buffer,
305 unsigned internalformat) {
306 if (last_state_.error != gpu::error::kNoError)
309 int32 new_id = channel_->ReserveImageId();
311 gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager =
312 channel_->gpu_memory_buffer_manager();
313 gfx::GpuMemoryBuffer* gpu_memory_buffer =
314 gpu_memory_buffer_manager->GpuMemoryBufferFromClientBuffer(buffer);
315 DCHECK(gpu_memory_buffer);
317 // This handle is owned by the GPU process and must be passed to it or it
318 // will leak. In otherwords, do not early out on error between here and the
319 // sending of the CreateImage IPC below.
320 bool requires_sync_point = false;
321 gfx::GpuMemoryBufferHandle handle =
322 channel_->ShareGpuMemoryBufferToGpuProcess(gpu_memory_buffer->GetHandle(),
323 &requires_sync_point);
325 DCHECK(gpu::ImageFactory::IsImageFormatCompatibleWithGpuMemoryBufferFormat(
326 internalformat, gpu_memory_buffer->GetFormat()));
327 if (!Send(new GpuCommandBufferMsg_CreateImage(route_id_,
330 gfx::Size(width, height),
331 gpu_memory_buffer->GetFormat(),
336 if (requires_sync_point) {
337 gpu_memory_buffer_manager->SetDestructionSyncPoint(gpu_memory_buffer,
344 void CommandBufferProxyImpl::DestroyImage(int32 id) {
345 if (last_state_.error != gpu::error::kNoError)
348 Send(new GpuCommandBufferMsg_DestroyImage(route_id_, id));
351 int32_t CommandBufferProxyImpl::CreateGpuMemoryBufferImage(
354 unsigned internalformat,
356 scoped_ptr<gfx::GpuMemoryBuffer> buffer(
357 channel_->gpu_memory_buffer_manager()->AllocateGpuMemoryBuffer(
358 gfx::Size(width, height),
359 gpu::ImageFactory::ImageFormatToGpuMemoryBufferFormat(internalformat),
360 gpu::ImageFactory::ImageUsageToGpuMemoryBufferUsage(usage)));
364 return CreateImage(buffer->AsClientBuffer(), width, height, internalformat);
367 int CommandBufferProxyImpl::GetRouteID() const {
371 uint32 CommandBufferProxyImpl::CreateStreamTexture(uint32 texture_id) {
372 if (last_state_.error != gpu::error::kNoError)
375 int32 stream_id = channel_->GenerateRouteID();
376 bool succeeded = false;
377 Send(new GpuCommandBufferMsg_CreateStreamTexture(
378 route_id_, texture_id, stream_id, &succeeded));
380 DLOG(ERROR) << "GpuCommandBufferMsg_CreateStreamTexture returned failure";
386 uint32 CommandBufferProxyImpl::InsertSyncPoint() {
387 if (last_state_.error != gpu::error::kNoError)
390 uint32 sync_point = 0;
391 Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_, true, &sync_point));
395 uint32_t CommandBufferProxyImpl::InsertFutureSyncPoint() {
396 if (last_state_.error != gpu::error::kNoError)
399 uint32 sync_point = 0;
400 Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_, false, &sync_point));
404 void CommandBufferProxyImpl::RetireSyncPoint(uint32_t sync_point) {
405 if (last_state_.error != gpu::error::kNoError)
408 Send(new GpuCommandBufferMsg_RetireSyncPoint(route_id_, sync_point));
411 void CommandBufferProxyImpl::SignalSyncPoint(uint32 sync_point,
412 const base::Closure& callback) {
413 if (last_state_.error != gpu::error::kNoError)
416 uint32 signal_id = next_signal_id_++;
417 if (!Send(new GpuCommandBufferMsg_SignalSyncPoint(route_id_,
423 signal_tasks_.insert(std::make_pair(signal_id, callback));
426 void CommandBufferProxyImpl::SignalQuery(uint32 query,
427 const base::Closure& callback) {
428 if (last_state_.error != gpu::error::kNoError)
431 // Signal identifiers are hidden, so nobody outside of this class will see
432 // them. (And thus, they cannot save them.) The IDs themselves only last
433 // until the callback is invoked, which will happen as soon as the GPU
434 // catches upwith the command buffer.
435 // A malicious caller trying to create a collision by making next_signal_id
436 // would have to make calls at an astounding rate (300B/s) and even if they
437 // could do that, all they would do is to prevent some callbacks from getting
438 // called, leading to stalled threads and/or memory leaks.
439 uint32 signal_id = next_signal_id_++;
440 if (!Send(new GpuCommandBufferMsg_SignalQuery(route_id_,
446 signal_tasks_.insert(std::make_pair(signal_id, callback));
449 void CommandBufferProxyImpl::SetSurfaceVisible(bool visible) {
450 if (last_state_.error != gpu::error::kNoError)
453 Send(new GpuCommandBufferMsg_SetSurfaceVisible(route_id_, visible));
456 bool CommandBufferProxyImpl::ProduceFrontBuffer(const gpu::Mailbox& mailbox) {
457 if (last_state_.error != gpu::error::kNoError)
460 return Send(new GpuCommandBufferMsg_ProduceFrontBuffer(route_id_, mailbox));
463 scoped_ptr<media::VideoDecodeAccelerator>
464 CommandBufferProxyImpl::CreateVideoDecoder() {
466 return scoped_ptr<media::VideoDecodeAccelerator>();
467 return scoped_ptr<media::VideoDecodeAccelerator>(
468 new GpuVideoDecodeAcceleratorHost(channel_, this));
471 scoped_ptr<media::VideoEncodeAccelerator>
472 CommandBufferProxyImpl::CreateVideoEncoder() {
474 return scoped_ptr<media::VideoEncodeAccelerator>();
475 return scoped_ptr<media::VideoEncodeAccelerator>(
476 new GpuVideoEncodeAcceleratorHost(channel_, this));
479 gpu::error::Error CommandBufferProxyImpl::GetLastError() {
480 return last_state_.error;
483 bool CommandBufferProxyImpl::Send(IPC::Message* msg) {
484 // Caller should not intentionally send a message if the context is lost.
485 DCHECK(last_state_.error == gpu::error::kNoError);
488 if (channel_->Send(msg)) {
491 // Flag the command buffer as lost. Defer deleting the channel until
492 // OnChannelError is called after returning to the message loop in case
493 // it is referenced elsewhere.
494 DVLOG(1) << "CommandBufferProxyImpl::Send failed. Losing context.";
495 last_state_.error = gpu::error::kLostContext;
500 // Callee takes ownership of message, regardless of whether Send is
501 // successful. See IPC::Sender.
506 void CommandBufferProxyImpl::OnUpdateState(
507 const gpu::CommandBuffer::State& state) {
508 // Handle wraparound. It works as long as we don't have more than 2B state
509 // updates in flight across which reordering occurs.
510 if (state.generation - last_state_.generation < 0x80000000U)
514 void CommandBufferProxyImpl::SetOnConsoleMessageCallback(
515 const GpuConsoleMessageCallback& callback) {
516 console_message_callback_ = callback;
519 void CommandBufferProxyImpl::TryUpdateState() {
520 if (last_state_.error == gpu::error::kNoError)
521 shared_state()->Read(&last_state_);
524 gpu::CommandBufferSharedState* CommandBufferProxyImpl::shared_state() const {
525 return reinterpret_cast<gpu::CommandBufferSharedState*>(
526 shared_state_shm_->memory());
529 void CommandBufferProxyImpl::OnSwapBuffersCompleted(
530 const std::vector<ui::LatencyInfo>& latency_info) {
531 if (!swap_buffers_completion_callback_.is_null()) {
532 if (!ui::LatencyInfo::Verify(
533 latency_info, "CommandBufferProxyImpl::OnSwapBuffersCompleted")) {
534 swap_buffers_completion_callback_.Run(std::vector<ui::LatencyInfo>());
537 swap_buffers_completion_callback_.Run(latency_info);
541 } // namespace content