| 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);
|
| +}
|
|
|