Index: content/browser/gpu/gpu_message_hub.cc |
diff --git a/content/browser/gpu/gpu_message_hub.cc b/content/browser/gpu/gpu_message_hub.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ae411fedbdeac21c3a04a09ca42c6c41aaf79825 |
--- /dev/null |
+++ b/content/browser/gpu/gpu_message_hub.cc |
@@ -0,0 +1,452 @@ |
+// Copyright (c) 2011 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/browser/gpu/gpu_message_hub.h" |
+ |
+#include "base/command_line.h" |
+#include "content/browser/browser_thread.h" |
+#include "content/browser/gpu/gpu_data_manager.h" |
+#include "content/browser/gpu/gpu_process_host.h" |
+#include "content/browser/gpu/gpu_process_host_ui_shim.h" |
+#include "content/browser/gpu/gpu_thread_host.h" |
+#include "content/common/content_switches.h" |
+#include "content/common/gpu/gpu_info.h" |
+#include "content/common/gpu/gpu_messages.h" |
+#include "ipc/ipc_channel_handle.h" |
+#include "ipc/ipc_message.h" |
+#include "ipc/ipc_message_macros.h" |
+ |
+#if defined(OS_LINUX) |
+#include "ui/gfx/gtk_native_view_id_manager.h" |
+ |
+class ReleasePermanentXIDDispatcher: public Task { |
+ public: |
+ explicit ReleasePermanentXIDDispatcher(gfx::PluginWindowHandle surface); |
+ void Run(); |
+ private: |
+ gfx::PluginWindowHandle surface_; |
+}; |
+ |
+ReleasePermanentXIDDispatcher::ReleasePermanentXIDDispatcher( |
+ gfx::PluginWindowHandle surface) |
+ : surface_(surface) { |
+} |
+ |
+void ReleasePermanentXIDDispatcher::Run() { |
+ GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); |
+ manager->ReleasePermanentXID(surface_); |
+} |
+ |
+// Used to put a lock on surfaces so that the window to which the GPU |
+// process is drawing to doesn't disappear while it is drawing when |
+// a tab is closed. |
+class GpuMessageHub::SurfaceRef { |
+ public: |
+ explicit SurfaceRef(gfx::PluginWindowHandle surface); |
+ ~SurfaceRef(); |
+ private: |
+ gfx::PluginWindowHandle surface_; |
+}; |
+ |
+GpuMessageHub::SurfaceRef::SurfaceRef(gfx::PluginWindowHandle surface) |
+ : surface_(surface) { |
+ GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); |
+ if (!manager->AddRefPermanentXID(surface_)) { |
+ LOG(ERROR) << "Surface " << surface << " cannot be referenced."; |
+ } |
+} |
+ |
+GpuMessageHub::SurfaceRef::~SurfaceRef() { |
+ BrowserThread::PostTask(BrowserThread::UI, |
+ FROM_HERE, |
+ new ReleasePermanentXIDDispatcher(surface_)); |
+} |
+#endif // defined(OS_LINUX |
+ |
+ |
+// A task that will forward an IPC message to the UI shim. |
+class RouteToGpuProcessHostUIShimTask : public Task { |
+ public: |
+ RouteToGpuProcessHostUIShimTask(int host_id, IPC::Message* msg) |
+ : host_id_(host_id), |
+ msg_(*msg) { |
+ } |
+ |
+ private: |
+ virtual void Run() { |
+ GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(host_id_); |
+ if (ui_shim) |
+ ui_shim->OnMessageReceived(msg_); |
+ } |
+ |
+ int host_id_; |
+ IPC::Message msg_; |
+}; |
+ |
+ |
+// A task that will forward an IPC message to a GpuProcessHost instance. |
+class SendToGpuProcessHostTask : public Task { |
+ public: |
+ SendToGpuProcessHostTask(int host_id, IPC::Message* msg) |
+ : host_id_(host_id), |
+ msg_(msg) { |
+ } |
+ |
+ private: |
+ void Run() { |
+ GpuProcessHost* host = GpuProcessHost::FromID(host_id_); |
+ if (host) |
+ host->Send(msg_.release()); |
+ } |
+ |
+ int host_id_; |
+ scoped_ptr<IPC::Message> msg_; |
+}; |
+ |
+// static |
+bool GpuMessageHub::SendToGPU(int host_id, IPC::Message* msg) { |
+ bool success; |
+ |
+ if (host_id == 0) { |
+ GpuThreadHost* host = GpuThreadHost::GetInstance(); |
+ success = host->Send(msg); |
+ } else { |
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
+ success = BrowserThread::PostTask( |
+ BrowserThread::IO, |
+ FROM_HERE, |
+ new SendToGpuProcessHostTask(host_id, msg)); |
+ } else { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ GpuProcessHost* host = GpuProcessHost::FromID(host_id); |
+ success = host->Send(msg); |
+ } |
+ } |
+ |
+ return success; |
+} |
+ |
+// static |
+bool GpuMessageHub::EstablishGpuChannel(int renderer_id, |
+ content::CauseForGpuLaunch cause, |
+ EstablishChannelCallback* callback, |
+ int& host_id) { |
+ linked_ptr<EstablishChannelCallback> wrapped_callback(callback); |
+ |
+ // If GPU features are already blacklisted, no need to establish the channel. |
+ if (!GpuDataManager::GetInstance()->GpuAccessAllowed()) { |
+ EstablishChannelError(callback); |
+ return false; |
+ } |
+ |
+ IPC::Message::Sender* sender; |
+ GpuMessageHub* hub; |
+ |
+ if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) || |
+ CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU)) { |
+ GpuThreadHost* host = GpuThreadHost::GetInstance(); |
+ if (host) { |
+ host_id = 0; |
+ hub = host->GetMessageHub(); |
+ sender = host; |
+ } else { |
+ EstablishChannelError(callback); |
+ return false; |
+ } |
+ } else { |
+ GpuProcessHost* host = GpuProcessHost::GetForRenderer(renderer_id, |
+ cause); |
+ if (host) { |
+ host_id = host->host_id(); |
+ hub = host->GetMessageHub(); |
+ sender = host; |
+ } else { |
+ EstablishChannelError(callback); |
+ return false; |
+ } |
+ } |
+ |
+ DCHECK(hub); |
+ |
+ if (!sender->Send(new GpuMsg_EstablishChannel(renderer_id))) { |
+ EstablishChannelError(callback); |
+ return false; |
+ } |
+ |
+ hub->ExpectChannelEstablished(wrapped_callback); |
+ return true; |
+} |
+ |
+// static |
+void GpuMessageHub::Synchronize(int host_id, SynchronizeCallback* callback) { |
+ linked_ptr<SynchronizeCallback> wrapped_callback(callback); |
+ |
+ IPC::Message::Sender* sender; |
+ GpuMessageHub* hub; |
+ |
+ if (!FromID(host_id, sender, hub)) { |
+ SynchronizeError(callback); |
+ return; |
+ } |
+ |
+ if (sender->Send(new GpuMsg_Synchronize())) |
+ hub->ExpectSynchronizeReply(wrapped_callback); |
+ else |
+ SynchronizeError(callback); |
+} |
+ |
+// static |
+void GpuMessageHub::CreateViewCommandBuffer( |
+ int host_id, |
+ gfx::PluginWindowHandle compositing_surface, |
+ int32 render_view_id, |
+ int32 renderer_id, |
+ const GPUCreateCommandBufferConfig& init_params, |
+ CreateCommandBufferCallback* callback) { |
+ linked_ptr<CreateCommandBufferCallback> wrapped_callback(callback); |
+ |
+ IPC::Message::Sender* sender; |
+ GpuMessageHub* hub; |
+ |
+ if (!FromID(host_id, sender, hub)) { |
+ CreateCommandBufferError(callback); |
+ return; |
+ } |
+ |
+ if (sender->Send(new GpuMsg_CreateViewCommandBuffer( |
+ compositing_surface, render_view_id, renderer_id, init_params))) |
+ hub->ExpectCommandBufferCreated(compositing_surface, |
+ render_view_id, |
+ renderer_id, |
+ wrapped_callback); |
+ else |
+ CreateCommandBufferError(callback); |
+} |
+ |
+// static |
+void GpuMessageHub::EstablishChannelError(EstablishChannelCallback* callback) { |
+ callback->Run(IPC::ChannelHandle(), |
+ base::kNullProcessHandle, |
+ GPUInfo()); |
+} |
+ |
+// static |
+void GpuMessageHub::CreateCommandBufferError( |
+ CreateCommandBufferCallback* callback) { |
+ callback->Run(MSG_ROUTING_NONE); |
+} |
+ |
+// static |
+void GpuMessageHub::SynchronizeError(SynchronizeCallback* callback) { |
+ callback->Run(); |
+} |
+ |
+// static |
+bool GpuMessageHub::FromID( |
+ int host_id, |
+ IPC::Message::Sender*& host, |
+ GpuMessageHub*& hub) { |
+ |
+ if (host_id == 0) { |
+ GpuThreadHost* thread_host = GpuThreadHost::GetInstance(); |
+ |
+ if (!thread_host) |
+ return false; |
+ |
+ hub = thread_host->GetMessageHub(); |
+ host = thread_host; |
+ } else { |
+ GpuProcessHost* process_host = GpuProcessHost::FromID(host_id); |
+ |
+ if (!process_host) |
+ return false; |
+ |
+ hub = process_host->GetMessageHub(); |
+ host = process_host; |
+ } |
+ |
+ DCHECK(hub); |
+ return true; |
+} |
+ |
+GpuMessageHub::GpuMessageHub(int host_id) |
+ : host_id_(host_id), |
+ gpu_process_(base::kNullProcessHandle) { |
+ // Post a task to create the corresponding GpuProcessHostUIShim. The |
+ // GpuProcessHostUIShim will be destroyed if either the browser exits, |
+ // in which case it calls GpuProcessHostUIShim::DestroyAll, or the |
+ // GpuProcessHost is destroyed, which happens when the corresponding GPU |
+ // process terminates or fails to launch. |
+ BrowserThread::PostTask( |
+ BrowserThread::UI, |
+ FROM_HERE, |
+ NewRunnableFunction(&GpuProcessHostUIShim::Create, host_id)); |
+} |
+ |
+ |
+GpuMessageHub::~GpuMessageHub() { |
+ BrowserThread::PostTask(BrowserThread::UI, |
+ FROM_HERE, |
+ NewRunnableFunction(GpuProcessHostUIShim::Destroy, |
+ host_id_)); |
+} |
+ |
+void GpuMessageHub::SetGPUProcess(base::ProcessHandle gpu_process) { |
+ gpu_process_ = gpu_process; |
+} |
+ |
+bool GpuMessageHub::RouteToUIThread(IPC::Message* message) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ // The GPU process must never send a synchronous IPC message to the browser |
+ // process. This could result in deadlock. Unfortunately linux does this for |
+ // GpuHostMsg_ResizeXID. TODO(apatrick): fix this before issuing any GL calls |
+ // on the browser process' GPU thread. |
+#if !defined(OS_LINUX) |
+ DCHECK(!message->is_sync()); |
+#endif |
+ |
+ return BrowserThread::PostTask( |
+ BrowserThread::UI, |
+ FROM_HERE, |
+ new RouteToGpuProcessHostUIShimTask(host_id_, message)); |
+} |
+ |
+bool GpuMessageHub::OnMessageReceived(const IPC::Message& message) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ IPC_BEGIN_MESSAGE_MAP(GpuMessageHub, message) |
+ IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished) |
+ IPC_MESSAGE_HANDLER(GpuHostMsg_SynchronizeReply, OnSynchronizeReply) |
+ IPC_MESSAGE_HANDLER(GpuHostMsg_CommandBufferCreated, OnCommandBufferCreated) |
+ IPC_MESSAGE_HANDLER(GpuHostMsg_DestroyCommandBuffer, OnDestroyCommandBuffer) |
+ IPC_MESSAGE_HANDLER(GpuHostMsg_GraphicsInfoCollected, |
+ OnGraphicsInfoCollected) |
+ IPC_MESSAGE_UNHANDLED(RouteToUIThread(new IPC::Message(message))) |
+ IPC_END_MESSAGE_MAP() |
+ |
+ return true; |
+} |
+ |
+void GpuMessageHub::ExpectChannelEstablished( |
+ linked_ptr<EstablishChannelCallback>& callback) { |
+ DCHECK(CalledOnValidThread()); |
+ channel_requests_.push(callback); |
+} |
+ |
+void GpuMessageHub::ExpectSynchronizeReply( |
+ linked_ptr<SynchronizeCallback>& callback) { |
+ DCHECK(CalledOnValidThread()); |
+ synchronize_requests_.push(callback); |
+} |
+ |
+void GpuMessageHub::ExpectCommandBufferCreated( |
+ gfx::PluginWindowHandle compositing_surface, |
+ int32 render_view_id, |
+ int32 renderer_id, |
+ linked_ptr<CreateCommandBufferCallback>& callback) { |
+ DCHECK(CalledOnValidThread()); |
+ create_command_buffer_requests_.push(callback); |
+ |
+#if defined(OS_LINUX) |
+ ViewID view_id(renderer_id, render_view_id); |
+ |
+ // There should only be one such command buffer (for the compositor). In |
+ // practice, if the GPU process lost a context, GraphicsContext3D with |
+ // associated command buffer and view surface will not be gone until new |
+ // one is in place and all layers are reattached. |
+ linked_ptr<SurfaceRef> surface_ref; |
+ SurfaceRefMap::iterator it = surface_refs_.find(view_id); |
+ if (it != surface_refs_.end()) |
+ surface_ref = (*it).second; |
+ else |
+ surface_ref.reset(new SurfaceRef(compositing_surface)); |
+ |
+ surface_refs_.insert(std::pair<ViewID, linked_ptr<SurfaceRef> >( |
+ view_id, surface_ref)); |
+#endif // defined(OS_LINUX) |
+} |
+ |
+void GpuMessageHub::SendOutstandingReplies() { |
+ DCHECK(CalledOnValidThread()); |
+ // First send empty channel handles for all EstablishChannel requests. |
+ while (!channel_requests_.empty()) { |
+ linked_ptr<EstablishChannelCallback> callback = channel_requests_.front(); |
+ channel_requests_.pop(); |
+ EstablishChannelError(callback.release()); |
+ } |
+} |
+ |
+IPC::Message::Sender* GpuMessageHub::FromID(int host_id) { |
+ if (host_id == 0) |
+ return GpuThreadHost::GetInstance(); |
+ else |
+ return GpuProcessHost::FromID(host_id); |
+} |
+ |
+void GpuMessageHub::OnChannelEstablished( |
+ const IPC::ChannelHandle& channel_handle) { |
+ DCHECK(CalledOnValidThread()); |
+ // The GPU process should have launched at this point and this object should |
+ // have been notified of its process handle. |
+ DCHECK(gpu_process_ != base::kNullProcessHandle && channel_requests_.size()); |
+ |
+ linked_ptr<EstablishChannelCallback> callback = channel_requests_.front(); |
+ channel_requests_.pop(); |
+ |
+ // Currently if any of the GPU features are blacklisted, we don't establish a |
+ // GPU channel. |
+ if (!channel_handle.name.empty() && |
+ !GpuDataManager::GetInstance()->GpuAccessAllowed()) { |
+ IPC::Message::Sender* sender = FromID(host_id_); |
+ if (sender) |
+ sender->Send(new GpuMsg_CloseChannel(channel_handle)); |
+ EstablishChannelError(callback.release()); |
+ RouteToUIThread(new GpuHostMsg_OnLogMessage( |
+ logging::LOG_WARNING, |
+ "WARNING", |
+ "Hardware acceleration is unavailable.")); |
+ return; |
+ } |
+ |
+ callback->Run( |
+ channel_handle, gpu_process_, GpuDataManager::GetInstance()->gpu_info()); |
+} |
+ |
+void GpuMessageHub::OnSynchronizeReply() { |
+ DCHECK(CalledOnValidThread()); |
+ // Guard against race conditions in abrupt GPU process termination. |
+ if (!synchronize_requests_.empty()) { |
+ linked_ptr<SynchronizeCallback> callback(synchronize_requests_.front()); |
+ synchronize_requests_.pop(); |
+ callback->Run(); |
+ } |
+} |
+ |
+void GpuMessageHub::OnCommandBufferCreated(const int32 route_id) { |
+ DCHECK(CalledOnValidThread()); |
+ if (!create_command_buffer_requests_.empty()) { |
+ linked_ptr<CreateCommandBufferCallback> callback = |
+ create_command_buffer_requests_.front(); |
+ create_command_buffer_requests_.pop(); |
+ if (route_id == MSG_ROUTING_NONE) |
+ CreateCommandBufferError(callback.release()); |
+ else |
+ callback->Run(route_id); |
+ } |
+} |
+ |
+void GpuMessageHub::OnDestroyCommandBuffer(gfx::PluginWindowHandle window, |
+ int32 renderer_id, |
+ int32 render_view_id) { |
+ DCHECK(CalledOnValidThread()); |
+#if defined(OS_LINUX) |
+ ViewID view_id(renderer_id, render_view_id); |
+ SurfaceRefMap::iterator it = surface_refs_.find(view_id); |
+ if (it != surface_refs_.end()) |
+ surface_refs_.erase(it); |
+#endif // defined(OS_LINUX) |
+} |
+ |
+void GpuMessageHub::OnGraphicsInfoCollected(const GPUInfo& gpu_info) { |
+ GpuDataManager::GetInstance()->UpdateGpuInfo(gpu_info); |
+} |