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"
9 #include "base/callback.h"
10 #include "base/logging.h"
11 #include "base/memory/shared_memory.h"
12 #include "base/stl_util.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/common/child_process_messages.h"
15 #include "content/common/gpu/client/gpu_channel_host.h"
16 #include "content/common/gpu/client/gpu_video_decode_accelerator_host.h"
17 #include "content/common/gpu/client/gpu_video_encode_accelerator_host.h"
18 #include "content/common/gpu/gpu_messages.h"
19 #include "content/common/view_messages.h"
20 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
21 #include "gpu/command_buffer/common/cmd_buffer_common.h"
22 #include "gpu/command_buffer/common/command_buffer_shared.h"
23 #include "gpu/command_buffer/common/gpu_memory_allocation.h"
24 #include "gpu/command_buffer/service/image_factory.h"
25 #include "ui/gfx/geometry/size.h"
26 #include "ui/gl/gl_bindings.h"
30 CommandBufferProxyImpl::CommandBufferProxyImpl(GpuChannelHost
* channel
,
37 last_barrier_put_offset_(-1),
42 CommandBufferProxyImpl::~CommandBufferProxyImpl() {
43 FOR_EACH_OBSERVER(DeletionObserver
,
47 channel_
->DestroyCommandBuffer(this);
52 bool CommandBufferProxyImpl::OnMessageReceived(const IPC::Message
& message
) {
53 scoped_ptr
<base::AutoLock
> lock
;
55 lock
.reset(new base::AutoLock(*lock_
));
57 IPC_BEGIN_MESSAGE_MAP(CommandBufferProxyImpl
, message
)
58 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_Destroyed
, OnDestroyed
);
59 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_ConsoleMsg
, OnConsoleMessage
);
60 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SetMemoryAllocation
,
61 OnSetMemoryAllocation
);
62 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SignalSyncPointAck
,
63 OnSignalSyncPointAck
);
64 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_SwapBuffersCompleted
,
65 OnSwapBuffersCompleted
);
66 IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_UpdateVSyncParameters
,
67 OnUpdateVSyncParameters
);
68 IPC_MESSAGE_UNHANDLED(handled
= false)
75 void CommandBufferProxyImpl::OnChannelError() {
76 scoped_ptr
<base::AutoLock
> lock
;
78 lock
.reset(new base::AutoLock(*lock_
));
80 gpu::error::ContextLostReason context_lost_reason
=
81 gpu::error::kGpuChannelLost
;
82 if (shared_state_shm_
&& shared_state_shm_
->memory()) {
84 // The GPU process might have intentionally been crashed
85 // (exit_on_context_lost), so try to find out the original reason.
86 if (last_state_
.error
== gpu::error::kLostContext
)
87 context_lost_reason
= last_state_
.context_lost_reason
;
89 OnDestroyed(context_lost_reason
, gpu::error::kLostContext
);
92 void CommandBufferProxyImpl::OnDestroyed(gpu::error::ContextLostReason reason
,
93 gpu::error::Error error
) {
95 // Prevent any further messages from being sent.
97 channel_
->DestroyCommandBuffer(this);
101 // When the client sees that the context is lost, they should delete this
102 // CommandBufferProxyImpl and create a new one.
103 last_state_
.error
= error
;
104 last_state_
.context_lost_reason
= reason
;
106 if (!context_lost_callback_
.is_null()) {
107 context_lost_callback_
.Run();
108 // Avoid calling the error callback more than once.
109 context_lost_callback_
.Reset();
113 void CommandBufferProxyImpl::OnConsoleMessage(
114 const GPUCommandBufferConsoleMessage
& message
) {
115 if (!console_message_callback_
.is_null()) {
116 console_message_callback_
.Run(message
.message
, message
.id
);
120 void CommandBufferProxyImpl::SetMemoryAllocationChangedCallback(
121 const MemoryAllocationChangedCallback
& callback
) {
123 if (last_state_
.error
!= gpu::error::kNoError
)
126 memory_allocation_changed_callback_
= callback
;
127 Send(new GpuCommandBufferMsg_SetClientHasMemoryAllocationChangedCallback(
128 route_id_
, !memory_allocation_changed_callback_
.is_null()));
131 void CommandBufferProxyImpl::AddDeletionObserver(DeletionObserver
* observer
) {
133 deletion_observers_
.AddObserver(observer
);
136 void CommandBufferProxyImpl::RemoveDeletionObserver(
137 DeletionObserver
* observer
) {
139 deletion_observers_
.RemoveObserver(observer
);
142 void CommandBufferProxyImpl::OnSetMemoryAllocation(
143 const gpu::MemoryAllocation
& allocation
) {
144 if (!memory_allocation_changed_callback_
.is_null())
145 memory_allocation_changed_callback_
.Run(allocation
);
148 void CommandBufferProxyImpl::OnSignalSyncPointAck(uint32 id
) {
149 SignalTaskMap::iterator it
= signal_tasks_
.find(id
);
150 DCHECK(it
!= signal_tasks_
.end());
151 base::Closure callback
= it
->second
;
152 signal_tasks_
.erase(it
);
156 void CommandBufferProxyImpl::SetContextLostCallback(
157 const base::Closure
& callback
) {
159 context_lost_callback_
= callback
;
162 bool CommandBufferProxyImpl::Initialize() {
163 TRACE_EVENT0("gpu", "CommandBufferProxyImpl::Initialize");
164 shared_state_shm_
.reset(channel_
->factory()->AllocateSharedMemory(
165 sizeof(*shared_state())).release());
166 if (!shared_state_shm_
)
169 if (!shared_state_shm_
->Map(sizeof(*shared_state())))
172 shared_state()->Initialize();
174 // This handle is owned by the GPU process and must be passed to it or it
175 // will leak. In otherwords, do not early out on error between here and the
176 // sending of the Initialize IPC below.
177 base::SharedMemoryHandle handle
=
178 channel_
->ShareToGpuProcess(shared_state_shm_
->handle());
179 if (!base::SharedMemory::IsHandleValid(handle
))
183 if (!Send(new GpuCommandBufferMsg_Initialize(
184 route_id_
, handle
, &result
, &capabilities_
))) {
185 LOG(ERROR
) << "Could not send GpuCommandBufferMsg_Initialize.";
190 LOG(ERROR
) << "Failed to initialize command buffer service.";
194 capabilities_
.image
= true;
199 gpu::CommandBuffer::State
CommandBufferProxyImpl::GetLastState() {
203 int32
CommandBufferProxyImpl::GetLastToken() {
205 return last_state_
.token
;
208 void CommandBufferProxyImpl::Flush(int32 put_offset
) {
210 if (last_state_
.error
!= gpu::error::kNoError
)
214 "CommandBufferProxyImpl::Flush",
218 bool put_offset_changed
= last_put_offset_
!= put_offset
;
219 last_put_offset_
= put_offset
;
220 last_barrier_put_offset_
= put_offset
;
223 channel_
->OrderingBarrier(route_id_
, put_offset
, ++flush_count_
,
224 latency_info_
, put_offset_changed
, true);
227 if (put_offset_changed
)
228 latency_info_
.clear();
231 void CommandBufferProxyImpl::OrderingBarrier(int32 put_offset
) {
232 if (last_state_
.error
!= gpu::error::kNoError
)
235 TRACE_EVENT1("gpu", "CommandBufferProxyImpl::OrderingBarrier", "put_offset",
238 bool put_offset_changed
= last_barrier_put_offset_
!= put_offset
;
239 last_barrier_put_offset_
= put_offset
;
242 channel_
->OrderingBarrier(route_id_
, put_offset
, ++flush_count_
,
243 latency_info_
, put_offset_changed
, false);
246 if (put_offset_changed
)
247 latency_info_
.clear();
250 void CommandBufferProxyImpl::SetLatencyInfo(
251 const std::vector
<ui::LatencyInfo
>& latency_info
) {
253 for (size_t i
= 0; i
< latency_info
.size(); i
++)
254 latency_info_
.push_back(latency_info
[i
]);
257 void CommandBufferProxyImpl::SetSwapBuffersCompletionCallback(
258 const SwapBuffersCompletionCallback
& callback
) {
260 swap_buffers_completion_callback_
= callback
;
263 void CommandBufferProxyImpl::SetUpdateVSyncParametersCallback(
264 const UpdateVSyncParametersCallback
& callback
) {
266 update_vsync_parameters_completion_callback_
= callback
;
269 void CommandBufferProxyImpl::WaitForTokenInRange(int32 start
, int32 end
) {
272 "CommandBufferProxyImpl::WaitForToken",
278 if (!InRange(start
, end
, last_state_
.token
) &&
279 last_state_
.error
== gpu::error::kNoError
) {
280 gpu::CommandBuffer::State state
;
281 if (Send(new GpuCommandBufferMsg_WaitForTokenInRange(
282 route_id_
, start
, end
, &state
)))
283 OnUpdateState(state
);
285 DCHECK(InRange(start
, end
, last_state_
.token
) ||
286 last_state_
.error
!= gpu::error::kNoError
);
289 void CommandBufferProxyImpl::WaitForGetOffsetInRange(int32 start
, int32 end
) {
292 "CommandBufferProxyImpl::WaitForGetOffset",
298 if (!InRange(start
, end
, last_state_
.get_offset
) &&
299 last_state_
.error
== gpu::error::kNoError
) {
300 gpu::CommandBuffer::State state
;
301 if (Send(new GpuCommandBufferMsg_WaitForGetOffsetInRange(
302 route_id_
, start
, end
, &state
)))
303 OnUpdateState(state
);
305 DCHECK(InRange(start
, end
, last_state_
.get_offset
) ||
306 last_state_
.error
!= gpu::error::kNoError
);
309 void CommandBufferProxyImpl::SetGetBuffer(int32 shm_id
) {
311 if (last_state_
.error
!= gpu::error::kNoError
)
314 Send(new GpuCommandBufferMsg_SetGetBuffer(route_id_
, shm_id
));
315 last_put_offset_
= -1;
318 scoped_refptr
<gpu::Buffer
> CommandBufferProxyImpl::CreateTransferBuffer(
324 if (last_state_
.error
!= gpu::error::kNoError
)
327 int32 new_id
= channel_
->ReserveTransferBufferId();
329 scoped_ptr
<base::SharedMemory
> shared_memory(
330 channel_
->factory()->AllocateSharedMemory(size
));
331 if (!shared_memory
) {
332 if (last_state_
.error
== gpu::error::kNoError
)
333 last_state_
.error
= gpu::error::kOutOfBounds
;
337 DCHECK(!shared_memory
->memory());
338 if (!shared_memory
->Map(size
)) {
339 if (last_state_
.error
== gpu::error::kNoError
)
340 last_state_
.error
= gpu::error::kOutOfBounds
;
344 // This handle is owned by the GPU process and must be passed to it or it
345 // will leak. In otherwords, do not early out on error between here and the
346 // sending of the RegisterTransferBuffer IPC below.
347 base::SharedMemoryHandle handle
=
348 channel_
->ShareToGpuProcess(shared_memory
->handle());
349 if (!base::SharedMemory::IsHandleValid(handle
)) {
350 if (last_state_
.error
== gpu::error::kNoError
)
351 last_state_
.error
= gpu::error::kLostContext
;
355 if (!Send(new GpuCommandBufferMsg_RegisterTransferBuffer(route_id_
,
363 scoped_refptr
<gpu::Buffer
> buffer(
364 gpu::MakeBufferFromSharedMemory(shared_memory
.Pass(), size
));
368 void CommandBufferProxyImpl::DestroyTransferBuffer(int32 id
) {
370 if (last_state_
.error
!= gpu::error::kNoError
)
373 Send(new GpuCommandBufferMsg_DestroyTransferBuffer(route_id_
, id
));
376 gpu::Capabilities
CommandBufferProxyImpl::GetCapabilities() {
377 return capabilities_
;
380 int32_t CommandBufferProxyImpl::CreateImage(ClientBuffer buffer
,
383 unsigned internalformat
) {
385 if (last_state_
.error
!= gpu::error::kNoError
)
388 int32 new_id
= channel_
->ReserveImageId();
390 gpu::GpuMemoryBufferManager
* gpu_memory_buffer_manager
=
391 channel_
->gpu_memory_buffer_manager();
392 gfx::GpuMemoryBuffer
* gpu_memory_buffer
=
393 gpu_memory_buffer_manager
->GpuMemoryBufferFromClientBuffer(buffer
);
394 DCHECK(gpu_memory_buffer
);
396 // This handle is owned by the GPU process and must be passed to it or it
397 // will leak. In otherwords, do not early out on error between here and the
398 // sending of the CreateImage IPC below.
399 bool requires_sync_point
= false;
400 gfx::GpuMemoryBufferHandle handle
=
401 channel_
->ShareGpuMemoryBufferToGpuProcess(gpu_memory_buffer
->GetHandle(),
402 &requires_sync_point
);
404 DCHECK(gpu::ImageFactory::IsGpuMemoryBufferFormatSupported(
405 gpu_memory_buffer
->GetFormat(), capabilities_
));
406 DCHECK(gpu::ImageFactory::IsImageSizeValidForGpuMemoryBufferFormat(
407 gfx::Size(width
, height
), gpu_memory_buffer
->GetFormat()));
408 DCHECK(gpu::ImageFactory::IsImageFormatCompatibleWithGpuMemoryBufferFormat(
409 internalformat
, gpu_memory_buffer
->GetFormat()));
410 if (!Send(new GpuCommandBufferMsg_CreateImage(route_id_
,
413 gfx::Size(width
, height
),
414 gpu_memory_buffer
->GetFormat(),
419 if (requires_sync_point
) {
420 gpu_memory_buffer_manager
->SetDestructionSyncPoint(gpu_memory_buffer
,
427 void CommandBufferProxyImpl::DestroyImage(int32 id
) {
429 if (last_state_
.error
!= gpu::error::kNoError
)
432 Send(new GpuCommandBufferMsg_DestroyImage(route_id_
, id
));
435 int32_t CommandBufferProxyImpl::CreateGpuMemoryBufferImage(
438 unsigned internalformat
,
441 scoped_ptr
<gfx::GpuMemoryBuffer
> buffer(
442 channel_
->gpu_memory_buffer_manager()->AllocateGpuMemoryBuffer(
443 gfx::Size(width
, height
),
444 gpu::ImageFactory::ImageFormatToGpuMemoryBufferFormat(internalformat
),
445 gpu::ImageFactory::ImageUsageToGpuMemoryBufferUsage(usage
)));
449 return CreateImage(buffer
->AsClientBuffer(), width
, height
, internalformat
);
452 uint32
CommandBufferProxyImpl::CreateStreamTexture(uint32 texture_id
) {
454 if (last_state_
.error
!= gpu::error::kNoError
)
457 int32 stream_id
= channel_
->GenerateRouteID();
458 bool succeeded
= false;
459 Send(new GpuCommandBufferMsg_CreateStreamTexture(
460 route_id_
, texture_id
, stream_id
, &succeeded
));
462 DLOG(ERROR
) << "GpuCommandBufferMsg_CreateStreamTexture returned failure";
468 void CommandBufferProxyImpl::SetLock(base::Lock
* lock
) {
472 bool CommandBufferProxyImpl::IsGpuChannelLost() {
473 return !channel_
|| channel_
->IsLost();
476 uint32
CommandBufferProxyImpl::InsertSyncPoint() {
478 if (last_state_
.error
!= gpu::error::kNoError
)
481 uint32 sync_point
= 0;
482 Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_
, true, &sync_point
));
486 uint32_t CommandBufferProxyImpl::InsertFutureSyncPoint() {
488 if (last_state_
.error
!= gpu::error::kNoError
)
491 uint32 sync_point
= 0;
492 Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_
, false, &sync_point
));
496 void CommandBufferProxyImpl::RetireSyncPoint(uint32_t sync_point
) {
498 if (last_state_
.error
!= gpu::error::kNoError
)
501 Send(new GpuCommandBufferMsg_RetireSyncPoint(route_id_
, sync_point
));
504 void CommandBufferProxyImpl::SignalSyncPoint(uint32 sync_point
,
505 const base::Closure
& callback
) {
507 if (last_state_
.error
!= gpu::error::kNoError
)
510 uint32 signal_id
= next_signal_id_
++;
511 if (!Send(new GpuCommandBufferMsg_SignalSyncPoint(route_id_
,
517 signal_tasks_
.insert(std::make_pair(signal_id
, callback
));
520 void CommandBufferProxyImpl::SignalQuery(uint32 query
,
521 const base::Closure
& callback
) {
523 if (last_state_
.error
!= gpu::error::kNoError
)
526 // Signal identifiers are hidden, so nobody outside of this class will see
527 // them. (And thus, they cannot save them.) The IDs themselves only last
528 // until the callback is invoked, which will happen as soon as the GPU
529 // catches upwith the command buffer.
530 // A malicious caller trying to create a collision by making next_signal_id
531 // would have to make calls at an astounding rate (300B/s) and even if they
532 // could do that, all they would do is to prevent some callbacks from getting
533 // called, leading to stalled threads and/or memory leaks.
534 uint32 signal_id
= next_signal_id_
++;
535 if (!Send(new GpuCommandBufferMsg_SignalQuery(route_id_
,
541 signal_tasks_
.insert(std::make_pair(signal_id
, callback
));
544 void CommandBufferProxyImpl::SetSurfaceVisible(bool visible
) {
546 if (last_state_
.error
!= gpu::error::kNoError
)
549 Send(new GpuCommandBufferMsg_SetSurfaceVisible(route_id_
, visible
));
552 bool CommandBufferProxyImpl::ProduceFrontBuffer(const gpu::Mailbox
& mailbox
) {
554 if (last_state_
.error
!= gpu::error::kNoError
)
557 return Send(new GpuCommandBufferMsg_ProduceFrontBuffer(route_id_
, mailbox
));
560 scoped_ptr
<media::VideoDecodeAccelerator
>
561 CommandBufferProxyImpl::CreateVideoDecoder() {
563 return scoped_ptr
<media::VideoDecodeAccelerator
>();
564 return scoped_ptr
<media::VideoDecodeAccelerator
>(
565 new GpuVideoDecodeAcceleratorHost(channel_
, this));
568 scoped_ptr
<media::VideoEncodeAccelerator
>
569 CommandBufferProxyImpl::CreateVideoEncoder() {
571 return scoped_ptr
<media::VideoEncodeAccelerator
>();
572 return scoped_ptr
<media::VideoEncodeAccelerator
>(
573 new GpuVideoEncodeAcceleratorHost(channel_
, this));
576 gpu::error::Error
CommandBufferProxyImpl::GetLastError() {
577 return last_state_
.error
;
580 bool CommandBufferProxyImpl::Send(IPC::Message
* msg
) {
581 // Caller should not intentionally send a message if the context is lost.
582 DCHECK(last_state_
.error
== gpu::error::kNoError
);
585 if (channel_
->Send(msg
)) {
588 // Flag the command buffer as lost. Defer deleting the channel until
589 // OnChannelError is called after returning to the message loop in case
590 // it is referenced elsewhere.
591 DVLOG(1) << "CommandBufferProxyImpl::Send failed. Losing context.";
592 last_state_
.error
= gpu::error::kLostContext
;
597 // Callee takes ownership of message, regardless of whether Send is
598 // successful. See IPC::Sender.
603 void CommandBufferProxyImpl::OnUpdateState(
604 const gpu::CommandBuffer::State
& state
) {
605 // Handle wraparound. It works as long as we don't have more than 2B state
606 // updates in flight across which reordering occurs.
607 if (state
.generation
- last_state_
.generation
< 0x80000000U
)
611 void CommandBufferProxyImpl::SetOnConsoleMessageCallback(
612 const GpuConsoleMessageCallback
& callback
) {
614 console_message_callback_
= callback
;
617 void CommandBufferProxyImpl::TryUpdateState() {
618 if (last_state_
.error
== gpu::error::kNoError
)
619 shared_state()->Read(&last_state_
);
622 gpu::CommandBufferSharedState
* CommandBufferProxyImpl::shared_state() const {
623 return reinterpret_cast<gpu::CommandBufferSharedState
*>(
624 shared_state_shm_
->memory());
627 void CommandBufferProxyImpl::OnSwapBuffersCompleted(
628 const std::vector
<ui::LatencyInfo
>& latency_info
,
629 gfx::SwapResult result
) {
630 if (!swap_buffers_completion_callback_
.is_null()) {
631 if (!ui::LatencyInfo::Verify(
632 latency_info
, "CommandBufferProxyImpl::OnSwapBuffersCompleted")) {
633 swap_buffers_completion_callback_
.Run(std::vector
<ui::LatencyInfo
>(),
637 swap_buffers_completion_callback_
.Run(latency_info
, result
);
641 void CommandBufferProxyImpl::OnUpdateVSyncParameters(base::TimeTicks timebase
,
642 base::TimeDelta interval
) {
643 if (!update_vsync_parameters_completion_callback_
.is_null())
644 update_vsync_parameters_completion_callback_
.Run(timebase
, interval
);
647 } // namespace content