Add ICU message format support
[chromium-blink-merge.git] / content / common / gpu / client / command_buffer_proxy_impl.cc
blobf2511a778ea2a19b1696657db0e246b3ac70be04
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 <vector>
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"
28 namespace content {
30 CommandBufferProxyImpl::CommandBufferProxyImpl(GpuChannelHost* channel,
31 int route_id)
32 : lock_(nullptr),
33 channel_(channel),
34 route_id_(route_id),
35 flush_count_(0),
36 last_put_offset_(-1),
37 last_barrier_put_offset_(-1),
38 next_signal_id_(0) {
39 DCHECK(channel);
42 CommandBufferProxyImpl::~CommandBufferProxyImpl() {
43 FOR_EACH_OBSERVER(DeletionObserver,
44 deletion_observers_,
45 OnWillDeleteImpl());
46 if (channel_) {
47 channel_->DestroyCommandBuffer(this);
48 channel_ = nullptr;
52 bool CommandBufferProxyImpl::OnMessageReceived(const IPC::Message& message) {
53 scoped_ptr<base::AutoLock> lock;
54 if (lock_)
55 lock.reset(new base::AutoLock(*lock_));
56 bool handled = true;
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)
69 IPC_END_MESSAGE_MAP()
71 DCHECK(handled);
72 return handled;
75 void CommandBufferProxyImpl::OnChannelError() {
76 scoped_ptr<base::AutoLock> lock;
77 if (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()) {
83 TryUpdateState();
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) {
94 CheckLock();
95 // Prevent any further messages from being sent.
96 if (channel_) {
97 channel_->DestroyCommandBuffer(this);
98 channel_ = nullptr;
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) {
122 CheckLock();
123 if (last_state_.error != gpu::error::kNoError)
124 return;
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) {
132 CheckLock();
133 deletion_observers_.AddObserver(observer);
136 void CommandBufferProxyImpl::RemoveDeletionObserver(
137 DeletionObserver* observer) {
138 CheckLock();
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);
153 callback.Run();
156 void CommandBufferProxyImpl::SetContextLostCallback(
157 const base::Closure& callback) {
158 CheckLock();
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_)
167 return false;
169 if (!shared_state_shm_->Map(sizeof(*shared_state())))
170 return false;
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))
180 return false;
182 bool result = false;
183 if (!Send(new GpuCommandBufferMsg_Initialize(
184 route_id_, handle, &result, &capabilities_))) {
185 LOG(ERROR) << "Could not send GpuCommandBufferMsg_Initialize.";
186 return false;
189 if (!result) {
190 LOG(ERROR) << "Failed to initialize command buffer service.";
191 return false;
194 capabilities_.image = true;
196 return true;
199 gpu::CommandBuffer::State CommandBufferProxyImpl::GetLastState() {
200 return last_state_;
203 int32 CommandBufferProxyImpl::GetLastToken() {
204 TryUpdateState();
205 return last_state_.token;
208 void CommandBufferProxyImpl::Flush(int32 put_offset) {
209 CheckLock();
210 if (last_state_.error != gpu::error::kNoError)
211 return;
213 TRACE_EVENT1("gpu",
214 "CommandBufferProxyImpl::Flush",
215 "put_offset",
216 put_offset);
218 bool put_offset_changed = last_put_offset_ != put_offset;
219 last_put_offset_ = put_offset;
220 last_barrier_put_offset_ = put_offset;
222 if (channel_) {
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)
233 return;
235 TRACE_EVENT1("gpu", "CommandBufferProxyImpl::OrderingBarrier", "put_offset",
236 put_offset);
238 bool put_offset_changed = last_barrier_put_offset_ != put_offset;
239 last_barrier_put_offset_ = put_offset;
241 if (channel_) {
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) {
252 CheckLock();
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) {
259 CheckLock();
260 swap_buffers_completion_callback_ = callback;
263 void CommandBufferProxyImpl::SetUpdateVSyncParametersCallback(
264 const UpdateVSyncParametersCallback& callback) {
265 CheckLock();
266 update_vsync_parameters_completion_callback_ = callback;
269 void CommandBufferProxyImpl::WaitForTokenInRange(int32 start, int32 end) {
270 CheckLock();
271 TRACE_EVENT2("gpu",
272 "CommandBufferProxyImpl::WaitForToken",
273 "start",
274 start,
275 "end",
276 end);
277 TryUpdateState();
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) {
290 CheckLock();
291 TRACE_EVENT2("gpu",
292 "CommandBufferProxyImpl::WaitForGetOffset",
293 "start",
294 start,
295 "end",
296 end);
297 TryUpdateState();
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) {
310 CheckLock();
311 if (last_state_.error != gpu::error::kNoError)
312 return;
314 Send(new GpuCommandBufferMsg_SetGetBuffer(route_id_, shm_id));
315 last_put_offset_ = -1;
318 scoped_refptr<gpu::Buffer> CommandBufferProxyImpl::CreateTransferBuffer(
319 size_t size,
320 int32* id) {
321 CheckLock();
322 *id = -1;
324 if (last_state_.error != gpu::error::kNoError)
325 return NULL;
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;
334 return NULL;
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;
341 return NULL;
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;
352 return NULL;
355 if (!Send(new GpuCommandBufferMsg_RegisterTransferBuffer(route_id_,
356 new_id,
357 handle,
358 size))) {
359 return NULL;
362 *id = new_id;
363 scoped_refptr<gpu::Buffer> buffer(
364 gpu::MakeBufferFromSharedMemory(shared_memory.Pass(), size));
365 return buffer;
368 void CommandBufferProxyImpl::DestroyTransferBuffer(int32 id) {
369 CheckLock();
370 if (last_state_.error != gpu::error::kNoError)
371 return;
373 Send(new GpuCommandBufferMsg_DestroyTransferBuffer(route_id_, id));
376 gpu::Capabilities CommandBufferProxyImpl::GetCapabilities() {
377 return capabilities_;
380 int32_t CommandBufferProxyImpl::CreateImage(ClientBuffer buffer,
381 size_t width,
382 size_t height,
383 unsigned internalformat) {
384 CheckLock();
385 if (last_state_.error != gpu::error::kNoError)
386 return -1;
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_,
411 new_id,
412 handle,
413 gfx::Size(width, height),
414 gpu_memory_buffer->GetFormat(),
415 internalformat))) {
416 return -1;
419 if (requires_sync_point) {
420 gpu_memory_buffer_manager->SetDestructionSyncPoint(gpu_memory_buffer,
421 InsertSyncPoint());
424 return new_id;
427 void CommandBufferProxyImpl::DestroyImage(int32 id) {
428 CheckLock();
429 if (last_state_.error != gpu::error::kNoError)
430 return;
432 Send(new GpuCommandBufferMsg_DestroyImage(route_id_, id));
435 int32_t CommandBufferProxyImpl::CreateGpuMemoryBufferImage(
436 size_t width,
437 size_t height,
438 unsigned internalformat,
439 unsigned usage) {
440 CheckLock();
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)));
446 if (!buffer)
447 return -1;
449 return CreateImage(buffer->AsClientBuffer(), width, height, internalformat);
452 uint32 CommandBufferProxyImpl::CreateStreamTexture(uint32 texture_id) {
453 CheckLock();
454 if (last_state_.error != gpu::error::kNoError)
455 return 0;
457 int32 stream_id = channel_->GenerateRouteID();
458 bool succeeded = false;
459 Send(new GpuCommandBufferMsg_CreateStreamTexture(
460 route_id_, texture_id, stream_id, &succeeded));
461 if (!succeeded) {
462 DLOG(ERROR) << "GpuCommandBufferMsg_CreateStreamTexture returned failure";
463 return 0;
465 return stream_id;
468 void CommandBufferProxyImpl::SetLock(base::Lock* lock) {
469 lock_ = lock;
472 bool CommandBufferProxyImpl::IsGpuChannelLost() {
473 return !channel_ || channel_->IsLost();
476 uint32 CommandBufferProxyImpl::InsertSyncPoint() {
477 CheckLock();
478 if (last_state_.error != gpu::error::kNoError)
479 return 0;
481 uint32 sync_point = 0;
482 Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_, true, &sync_point));
483 return sync_point;
486 uint32_t CommandBufferProxyImpl::InsertFutureSyncPoint() {
487 CheckLock();
488 if (last_state_.error != gpu::error::kNoError)
489 return 0;
491 uint32 sync_point = 0;
492 Send(new GpuCommandBufferMsg_InsertSyncPoint(route_id_, false, &sync_point));
493 return sync_point;
496 void CommandBufferProxyImpl::RetireSyncPoint(uint32_t sync_point) {
497 CheckLock();
498 if (last_state_.error != gpu::error::kNoError)
499 return;
501 Send(new GpuCommandBufferMsg_RetireSyncPoint(route_id_, sync_point));
504 void CommandBufferProxyImpl::SignalSyncPoint(uint32 sync_point,
505 const base::Closure& callback) {
506 CheckLock();
507 if (last_state_.error != gpu::error::kNoError)
508 return;
510 uint32 signal_id = next_signal_id_++;
511 if (!Send(new GpuCommandBufferMsg_SignalSyncPoint(route_id_,
512 sync_point,
513 signal_id))) {
514 return;
517 signal_tasks_.insert(std::make_pair(signal_id, callback));
520 void CommandBufferProxyImpl::SignalQuery(uint32 query,
521 const base::Closure& callback) {
522 CheckLock();
523 if (last_state_.error != gpu::error::kNoError)
524 return;
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_,
536 query,
537 signal_id))) {
538 return;
541 signal_tasks_.insert(std::make_pair(signal_id, callback));
544 void CommandBufferProxyImpl::SetSurfaceVisible(bool visible) {
545 CheckLock();
546 if (last_state_.error != gpu::error::kNoError)
547 return;
549 Send(new GpuCommandBufferMsg_SetSurfaceVisible(route_id_, visible));
552 bool CommandBufferProxyImpl::ProduceFrontBuffer(const gpu::Mailbox& mailbox) {
553 CheckLock();
554 if (last_state_.error != gpu::error::kNoError)
555 return false;
557 return Send(new GpuCommandBufferMsg_ProduceFrontBuffer(route_id_, mailbox));
560 scoped_ptr<media::VideoDecodeAccelerator>
561 CommandBufferProxyImpl::CreateVideoDecoder() {
562 if (!channel_)
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() {
570 if (!channel_)
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);
584 if (channel_) {
585 if (channel_->Send(msg)) {
586 return true;
587 } else {
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;
593 return false;
597 // Callee takes ownership of message, regardless of whether Send is
598 // successful. See IPC::Sender.
599 delete msg;
600 return false;
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)
608 last_state_ = state;
611 void CommandBufferProxyImpl::SetOnConsoleMessageCallback(
612 const GpuConsoleMessageCallback& callback) {
613 CheckLock();
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>(),
634 result);
635 return;
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