Index: content/common/gpu/client/ipc/chrome/chrome_gpu_channel_host_ipc_transport.cc |
diff --git a/content/common/gpu/client/ipc/chrome/chrome_gpu_channel_host_ipc_transport.cc b/content/common/gpu/client/ipc/chrome/chrome_gpu_channel_host_ipc_transport.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c3e57bc36d7cd9531b4d40a1d8f7a3fb8e81ea7a |
--- /dev/null |
+++ b/content/common/gpu/client/ipc/chrome/chrome_gpu_channel_host_ipc_transport.cc |
@@ -0,0 +1,317 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/common/gpu/client/ipc/chrome/chrome_gpu_channel_host_ipc_transport.h" |
+ |
+#include "base/threading/thread_restrictions.h" |
+#include "content/common/gpu/client/gpu_channel_host_factory.h" |
+#include "content/common/gpu/client/ipc/chrome/chrome_command_buffer_ipc_transport.h" |
+#include "content/common/gpu/client/ipc/chrome/chrome_gpu_jpeg_decode_accelerator_host_ipc_transport.h" |
+#include "content/common/gpu/gpu_messages.h" |
+#include "ipc/ipc_sync_channel.h" |
+ |
+namespace content { |
+ |
+// static |
+scoped_ptr<GpuChannelHostIPCTransport> |
+ChromeGpuChannelHostIPCTransport::Create() { |
+ return make_scoped_ptr<GpuChannelHostIPCTransport>( |
+ new ChromeGpuChannelHostIPCTransport()); |
+} |
+ |
+ChromeGpuChannelHostIPCTransport::~ChromeGpuChannelHostIPCTransport() { |
+ DCHECK(factory_->IsMainThread()); |
+} |
+ |
+void ChromeGpuChannelHostIPCTransport::BindToService( |
+ GpuChannelHostFactory* factory, |
+ int channel_id, |
+ const IPC::ChannelHandle& channel_handle, |
+ base::WaitableEvent* shutdown_event) { |
+ factory_ = factory; |
+ channel_id_ = channel_id; |
+ DCHECK(factory_->IsMainThread()); |
+ // Open a channel to the GPU process. We pass NULL as the main listener here |
+ // since we need to filter everything to route it to the right thread. |
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = |
+ factory_->GetIOThreadTaskRunner(); |
+ channel_ = |
+ IPC::SyncChannel::Create(channel_handle, IPC::Channel::MODE_CLIENT, NULL, |
+ io_task_runner.get(), true, shutdown_event); |
+ |
+ sync_filter_ = channel_->CreateSyncMessageFilter(); |
+ |
+ channel_filter_ = new MessageFilter(); |
+ |
+ // Install the filter last, because we intercept all leftover |
+ // messages. |
+ channel_->AddFilter(channel_filter_.get()); |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::Send(IPC::Message* msg) { |
+ // Callee takes ownership of message, regardless of whether Send is |
+ // successful. See IPC::Sender. |
+ scoped_ptr<IPC::Message> message(msg); |
+ // The GPU process never sends synchronous IPCs so clear the unblock flag to |
+ // preserve order. |
+ message->set_unblock(false); |
+ |
+ // Currently we need to choose between two different mechanisms for sending. |
+ // On the main thread we use the regular channel Send() method, on another |
+ // thread we use SyncMessageFilter. We also have to be careful interpreting |
+ // IsMainThread() since it might return false during shutdown, |
+ // impl we are actually calling from the main thread (discard message then). |
+ // |
+ // TODO: Can we just always use sync_filter_ since we setup the channel |
+ // without a main listener? |
+ if (factory_->IsMainThread()) { |
+ // channel_ is only modified on the main thread, so we don't need to take a |
+ // lock here. |
+ if (!channel_) { |
+ DVLOG(1) << "GpuChannelHost::Send failed: Channel already destroyed"; |
+ return false; |
+ } |
+ // http://crbug.com/125264 |
+ base::ThreadRestrictions::ScopedAllowWait allow_wait; |
+ bool result = channel_->Send(message.release()); |
+ if (!result) |
+ DVLOG(1) << "GpuChannelHost::Send failed: Channel::Send failed"; |
+ return result; |
+ } |
+ |
+ bool result = sync_filter_->Send(message.release()); |
+ return result; |
+} |
+ |
+int32_t ChromeGpuChannelHostIPCTransport::GenerateRouteID() { |
+ return next_route_id_.GetNext(); |
+} |
+ |
+void ChromeGpuChannelHostIPCTransport::AddRoute( |
+ int route_id, |
+ base::WeakPtr<IPC::Listener> listener) { |
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = |
+ factory_->GetIOThreadTaskRunner(); |
+ io_task_runner->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ChromeGpuChannelHostIPCTransport::MessageFilter::AddRoute, |
+ channel_filter_.get(), route_id, listener, |
+ base::ThreadTaskRunnerHandle::Get())); |
+} |
+ |
+void ChromeGpuChannelHostIPCTransport::AddRouteOnIO( |
+ int route_id, |
+ base::WeakPtr<IPC::Listener> listener) { |
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = |
+ factory_->GetIOThreadTaskRunner(); |
+ io_task_runner->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ChromeGpuChannelHostIPCTransport::MessageFilter::AddRoute, |
+ channel_filter_.get(), route_id, listener, io_task_runner)); |
+} |
+ |
+void ChromeGpuChannelHostIPCTransport::RemoveRoute(int route_id) { |
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = |
+ factory_->GetIOThreadTaskRunner(); |
+ io_task_runner->PostTask( |
+ FROM_HERE, |
+ base::Bind(&ChromeGpuChannelHostIPCTransport::MessageFilter::RemoveRoute, |
+ channel_filter_.get(), route_id)); |
+} |
+ |
+ChromeGpuChannelHostIPCTransport::ChromeGpuChannelHostIPCTransport() |
+ : factory_(nullptr), channel_id_(0) { |
+ next_route_id_.GetNext(); |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::AsyncFlush( |
+ CommandBufferIPCTransport* transport, |
+ int32_t put_offset, |
+ uint32_t flush_count, |
+ const std::vector<ui::LatencyInfo>& latency_info) { |
+ return Send(new GpuCommandBufferMsg_AsyncFlush( |
+ static_cast<ChromeCommandBufferIPCTransport*>(transport)->route_id(), |
+ put_offset, flush_count, latency_info)); |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::CreateJpegDecoder( |
+ GpuJpegDecodeAcceleratorHostIPCTransport* jpeg_decode_transport, |
+ bool* succeeded) { |
+ ChromeGpuJpegDecodeAcceleratorHostIPCTransport* chrome_ipc_transport = |
+ static_cast<ChromeGpuJpegDecodeAcceleratorHostIPCTransport*>( |
+ jpeg_decode_transport); |
+ chrome_ipc_transport->BindToService(this); |
+ int route_id = chrome_ipc_transport->route_id(); |
+ bool sent = Send(new GpuMsg_CreateJpegDecoder(route_id, succeeded)); |
+ |
+ if (sent && *succeeded) |
+ AddRouteOnIO(route_id, chrome_ipc_transport->GetWeakPtrForIO()); |
+ |
+ return sent; |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::CreateViewCommandBuffer( |
+ uint32_t surface_id, |
+ const GpuCreateCommandBufferConfig& init_params, |
+ CommandBufferIPCTransport* command_buffer_transport, |
+ CreateCommandBufferResult* result) { |
+ ChromeCommandBufferIPCTransport* chrome_ipc_transport = |
+ static_cast<ChromeCommandBufferIPCTransport*>(command_buffer_transport); |
+ chrome_ipc_transport->BindToService(this); |
+ int route_id = chrome_ipc_transport->route_id(); |
+ *result = |
+ factory_->CreateViewCommandBuffer(surface_id, init_params, route_id); |
+ if (*result == CREATE_COMMAND_BUFFER_SUCCEEDED) |
+ AddRoute(route_id, chrome_ipc_transport->AsWeakPtr()); |
+ else if (*result == CREATE_COMMAND_BUFFER_FAILED_AND_CHANNEL_LOST) { |
+ // The GPU channel needs to be considered lost. The caller will |
+ // then set up a new connection, and the GPU channel and any |
+ // view command buffers will all be associated with the same GPU |
+ // process. |
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = |
+ factory_->GetIOThreadTaskRunner(); |
+ io_task_runner->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &ChromeGpuChannelHostIPCTransport::MessageFilter::OnChannelError, |
+ channel_filter_.get())); |
+ } |
+ return *result != CREATE_COMMAND_BUFFER_FAILED_AND_CHANNEL_LOST; |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::CreateOffscreenCommandBuffer( |
+ const gfx::Size& size, |
+ const GpuCreateCommandBufferConfig& init_params, |
+ CommandBufferIPCTransport* command_buffer_transport, |
+ bool* succeeded) { |
+ ChromeCommandBufferIPCTransport* chrome_ipc_transport = |
+ static_cast<ChromeCommandBufferIPCTransport*>(command_buffer_transport); |
+ chrome_ipc_transport->BindToService(this); |
+ int route_id = chrome_ipc_transport->route_id(); |
+ bool sent = Send(new GpuChannelMsg_CreateOffscreenCommandBuffer( |
+ size, init_params, route_id, succeeded)); |
+ |
+ if (sent && *succeeded) |
+ AddRoute(route_id, chrome_ipc_transport->AsWeakPtr()); |
+ |
+ return sent; |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::DestroyCommandBuffer( |
+ CommandBufferIPCTransport* command_buffer_transport) { |
+ int route_id = |
+ static_cast<ChromeCommandBufferIPCTransport*>(command_buffer_transport) |
+ ->route_id(); |
+ bool success = Send(new GpuChannelMsg_DestroyCommandBuffer(route_id)); |
+ RemoveRoute(route_id); |
+ return success; |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::IsLost() const { |
+ DCHECK(channel_filter_.get()); |
+ return channel_filter_->IsLost(); |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::Nop() { |
+ return Send(new GpuChannelMsg_Nop()); |
+} |
+ |
+base::SharedMemoryHandle ChromeGpuChannelHostIPCTransport::ShareToGpuProcess( |
+ const base::SharedMemoryHandle& source_handle) { |
+ if (IsLost()) |
+ return base::SharedMemory::NULLHandle(); |
+ |
+#if defined(OS_WIN) || defined(OS_MACOSX) |
+ // Windows and Mac need to explicitly duplicate the handle out to another |
+ // process. |
+ base::SharedMemoryHandle target_handle; |
+ base::ProcessId peer_pid; |
+ { |
+ // TODO(fsamuel): should we do locking inside the transport object? |
+ if (!channel_) |
+ return base::SharedMemory::NULLHandle(); |
+ peer_pid = channel_->GetPeerPID(); |
+ } |
+ bool success = BrokerDuplicateSharedMemoryHandle(source_handle, peer_pid, |
+ &target_handle); |
+ if (!success) |
+ return base::SharedMemory::NULLHandle(); |
+ |
+ return target_handle; |
+#else |
+ return base::SharedMemory::DuplicateHandle(source_handle); |
+#endif // defined(OS_WIN) || defined(OS_MACOSX) |
+} |
+ |
+ChromeGpuChannelHostIPCTransport::MessageFilter::ListenerInfo::ListenerInfo() {} |
+ |
+ChromeGpuChannelHostIPCTransport::MessageFilter::ListenerInfo::~ListenerInfo() { |
+} |
+ |
+ChromeGpuChannelHostIPCTransport::MessageFilter::MessageFilter() |
+ : lost_(false) {} |
+ |
+ChromeGpuChannelHostIPCTransport::MessageFilter::~MessageFilter() {} |
+ |
+void ChromeGpuChannelHostIPCTransport::MessageFilter::AddRoute( |
+ int32_t route_id, |
+ base::WeakPtr<IPC::Listener> listener, |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
+ DCHECK(listeners_.find(route_id) == listeners_.end()); |
+ DCHECK(task_runner); |
+ ListenerInfo info; |
+ info.listener = listener; |
+ info.task_runner = task_runner; |
+ listeners_[route_id] = info; |
+} |
+ |
+void ChromeGpuChannelHostIPCTransport::MessageFilter::RemoveRoute( |
+ int32_t route_id) { |
+ listeners_.erase(route_id); |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::MessageFilter::OnMessageReceived( |
+ const IPC::Message& message) { |
+ // Never handle sync message replies or we will deadlock here. |
+ if (message.is_reply()) |
+ return false; |
+ |
+ auto it = listeners_.find(message.routing_id()); |
+ if (it == listeners_.end()) |
+ return false; |
+ |
+ const ListenerInfo& info = it->second; |
+ info.task_runner->PostTask( |
+ FROM_HERE, |
+ base::Bind(base::IgnoreResult(&IPC::Listener::OnMessageReceived), |
+ info.listener, message)); |
+ return true; |
+} |
+ |
+void ChromeGpuChannelHostIPCTransport::MessageFilter::OnChannelError() { |
+ // Set the lost state before signalling the proxies. That way, if they |
+ // themselves post a task to recreate the context, they will not try to re-use |
+ // this channel host. |
+ { |
+ base::AutoLock lock(lock_); |
+ lost_ = true; |
+ } |
+ |
+ // Inform all the proxies that an error has occurred. This will be reported |
+ // via OpenGL as a lost context. |
+ for (const auto& kv : listeners_) { |
+ const ListenerInfo& info = kv.second; |
+ info.task_runner->PostTask( |
+ FROM_HERE, base::Bind(&IPC::Listener::OnChannelError, info.listener)); |
+ } |
+ |
+ listeners_.clear(); |
+} |
+ |
+bool ChromeGpuChannelHostIPCTransport::MessageFilter::IsLost() const { |
+ base::AutoLock lock(lock_); |
+ return lost_; |
+} |
+ |
+} // namespace content |