Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Unified Diff: content/browser/gpu/gpu_message_hub.cc

Issue 7054005: Fix gpu acceleration with --in-process-gpu (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: cleanup Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
+}

Powered by Google App Engine
This is Rietveld 408576698