| // Copyright (c) 2010 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 "chrome/browser/gpu_process_host.h" |
| |
| #include "app/app_switches.h" |
| #include "base/command_line.h" |
| #include "base/thread.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/browser_thread.h" |
| #include "chrome/browser/gpu_process_host_ui_shim.h" |
| #include "chrome/browser/renderer_host/render_view_host.h" |
| #include "chrome/browser/renderer_host/render_widget_host_view.h" |
| #include "chrome/browser/renderer_host/resource_message_filter.h" |
| #include "chrome/common/child_process_logging.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/gpu_info.h" |
| #include "chrome/common/gpu_messages.h" |
| #include "chrome/common/render_messages.h" |
| #include "ipc/ipc_channel_handle.h" |
| #include "ipc/ipc_switches.h" |
| #include "media/base/media_switches.h" |
| |
| #if defined(OS_LINUX) |
| #include "gfx/gtk_native_view_id_manager.h" |
| #endif |
| |
| namespace { |
| |
| // Tasks used by this file |
| class RouteOnUIThreadTask : public Task { |
| public: |
| explicit RouteOnUIThreadTask(const IPC::Message& msg) : msg_(msg) { |
| } |
| |
| private: |
| void Run() { |
| GpuProcessHostUIShim::Get()->OnMessageReceived(msg_); |
| } |
| IPC::Message msg_; |
| }; |
| |
| // Global GpuProcessHost instance. |
| // We can not use Singleton<GpuProcessHost> because that gets |
| // terminated on the wrong thread (main thread). We need the |
| // GpuProcessHost to be terminated on the same thread on which it is |
| // initialized, the IO thread. |
| static GpuProcessHost* sole_instance_ = NULL; |
| |
| } // anonymous namespace |
| |
| GpuProcessHost::GpuProcessHost() |
| : BrowserChildProcessHost(GPU_PROCESS, NULL), |
| initialized_(false), |
| initialized_successfully_(false) { |
| DCHECK_EQ(sole_instance_, static_cast<GpuProcessHost*>(NULL)); |
| } |
| |
| GpuProcessHost::~GpuProcessHost() { |
| while (!queued_synchronization_replies_.empty()) { |
| delete queued_synchronization_replies_.front().reply; |
| queued_synchronization_replies_.pop(); |
| } |
| DCHECK_EQ(sole_instance_, this); |
| sole_instance_ = NULL; |
| } |
| |
| bool GpuProcessHost::EnsureInitialized() { |
| if (!initialized_) { |
| initialized_ = true; |
| initialized_successfully_ = Init(); |
| } |
| return initialized_successfully_; |
| } |
| |
| bool GpuProcessHost::Init() { |
| if (!CreateChannel()) |
| return false; |
| |
| const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); |
| CommandLine::StringType gpu_launcher = |
| browser_command_line.GetSwitchValueNative(switches::kGpuLauncher); |
| |
| FilePath exe_path = ChildProcessHost::GetChildPath(gpu_launcher.empty()); |
| if (exe_path.empty()) |
| return false; |
| |
| CommandLine* cmd_line = new CommandLine(exe_path); |
| cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess); |
| cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id()); |
| |
| // Propagate relevant command line switches. |
| static const char* const kSwitchNames[] = { |
| switches::kUseGL, |
| switches::kDisableGpuVsync, |
| switches::kDisableLogging, |
| switches::kEnableAcceleratedDecoding, |
| switches::kEnableLogging, |
| switches::kGpuStartupDialog, |
| switches::kLoggingLevel, |
| }; |
| cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, |
| arraysize(kSwitchNames)); |
| |
| // If specified, prepend a launcher program to the command line. |
| if (!gpu_launcher.empty()) |
| cmd_line->PrependWrapper(gpu_launcher); |
| |
| Launch( |
| #if defined(OS_WIN) |
| FilePath(), |
| #elif defined(OS_POSIX) |
| false, // Never use the zygote (GPU plugin can't be sandboxed). |
| base::environment_vector(), |
| #endif |
| cmd_line); |
| |
| return true; |
| } |
| |
| // static |
| GpuProcessHost* GpuProcessHost::Get() { |
| if (sole_instance_ == NULL) |
| sole_instance_ = new GpuProcessHost(); |
| return sole_instance_; |
| } |
| |
| // static |
| void GpuProcessHost::SendAboutGpuCrash() { |
| Get()->Send(new GpuMsg_Crash()); |
| } |
| |
| // static |
| void GpuProcessHost::SendAboutGpuHang() { |
| Get()->Send(new GpuMsg_Hang()); |
| } |
| |
| bool GpuProcessHost::Send(IPC::Message* msg) { |
| if (!EnsureInitialized()) |
| return false; |
| |
| return BrowserChildProcessHost::Send(msg); |
| } |
| |
| void GpuProcessHost::OnMessageReceived(const IPC::Message& message) { |
| if (message.routing_id() == MSG_ROUTING_CONTROL) { |
| OnControlMessageReceived(message); |
| } else { |
| // Need to transfer this message to the UI thread and the |
| // GpuProcessHostUIShim for dispatching via its message router. |
| BrowserThread::PostTask(BrowserThread::UI, |
| FROM_HERE, |
| new RouteOnUIThreadTask(message)); |
| } |
| } |
| |
| void GpuProcessHost::EstablishGpuChannel(int renderer_id, |
| ResourceMessageFilter* filter) { |
| if (Send(new GpuMsg_EstablishChannel(renderer_id))) { |
| sent_requests_.push(ChannelRequest(filter)); |
| } else { |
| ReplyToRenderer(IPC::ChannelHandle(), GPUInfo(), filter); |
| } |
| } |
| |
| void GpuProcessHost::Synchronize(IPC::Message* reply, |
| ResourceMessageFilter* filter) { |
| queued_synchronization_replies_.push(SynchronizationRequest(reply, filter)); |
| Send(new GpuMsg_Synchronize()); |
| } |
| |
| GPUInfo GpuProcessHost::gpu_info() const { |
| return gpu_info_; |
| } |
| |
| GpuProcessHost::ChannelRequest::ChannelRequest(ResourceMessageFilter* filter) |
| : filter(filter) { |
| } |
| |
| GpuProcessHost::ChannelRequest::~ChannelRequest() {} |
| |
| GpuProcessHost::SynchronizationRequest::SynchronizationRequest( |
| IPC::Message* reply, |
| ResourceMessageFilter* filter) |
| : reply(reply), |
| filter(filter) { |
| } |
| |
| GpuProcessHost::SynchronizationRequest::~SynchronizationRequest() {} |
| |
| void GpuProcessHost::OnControlMessageReceived(const IPC::Message& message) { |
| IPC_BEGIN_MESSAGE_MAP(GpuProcessHost, message) |
| IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished) |
| IPC_MESSAGE_HANDLER(GpuHostMsg_SynchronizeReply, OnSynchronizeReply) |
| IPC_MESSAGE_HANDLER(GpuHostMsg_GraphicsInfoCollected, |
| OnGraphicsInfoCollected) |
| #if defined(OS_LINUX) |
| IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_GetViewXID, OnGetViewXID) |
| #elif defined(OS_MACOSX) |
| IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceSetIOSurface, |
| OnAcceleratedSurfaceSetIOSurface) |
| IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped, |
| OnAcceleratedSurfaceBuffersSwapped) |
| #endif |
| IPC_MESSAGE_UNHANDLED_ERROR() |
| IPC_END_MESSAGE_MAP() |
| } |
| |
| void GpuProcessHost::OnChannelEstablished( |
| const IPC::ChannelHandle& channel_handle, |
| const GPUInfo& gpu_info) { |
| const ChannelRequest& request = sent_requests_.front(); |
| ReplyToRenderer(channel_handle, gpu_info, request.filter); |
| sent_requests_.pop(); |
| gpu_info_ = gpu_info; |
| child_process_logging::SetGpuInfo(gpu_info); |
| } |
| |
| void GpuProcessHost::OnSynchronizeReply() { |
| const SynchronizationRequest& request = |
| queued_synchronization_replies_.front(); |
| request.filter->Send(request.reply); |
| queued_synchronization_replies_.pop(); |
| } |
| |
| void GpuProcessHost::OnGraphicsInfoCollected(const GPUInfo& gpu_info) { |
| gpu_info_ = gpu_info; |
| } |
| |
| #if defined(OS_LINUX) |
| |
| namespace { |
| |
| void SendDelayedReply(IPC::Message* reply_msg) { |
| GpuProcessHost::Get()->Send(reply_msg); |
| } |
| |
| void GetViewXIDDispatcher(gfx::NativeViewId id, IPC::Message* reply_msg) { |
| unsigned long xid; |
| |
| GtkNativeViewManager* manager = Singleton<GtkNativeViewManager>::get(); |
| if (!manager->GetPermanentXIDForId(&xid, id)) { |
| DLOG(ERROR) << "Can't find XID for view id " << id; |
| xid = 0; |
| } |
| |
| GpuHostMsg_GetViewXID::WriteReplyParams(reply_msg, xid); |
| |
| // Have to reply from IO thread. |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableFunction(&SendDelayedReply, reply_msg)); |
| } |
| |
| } |
| |
| void GpuProcessHost::OnGetViewXID(gfx::NativeViewId id, |
| IPC::Message *reply_msg) { |
| // Have to request a permanent overlay from UI thread. |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| NewRunnableFunction(&GetViewXIDDispatcher, id, reply_msg)); |
| } |
| |
| #elif defined(OS_MACOSX) |
| |
| namespace { |
| |
| class SetIOSurfaceDispatcher : public Task { |
| public: |
| SetIOSurfaceDispatcher( |
| const GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params& params) |
| : params_(params) { |
| } |
| |
| void Run() { |
| RenderViewHost* host = RenderViewHost::FromID(params_.renderer_id, |
| params_.render_view_id); |
| if (!host) |
| return; |
| RenderWidgetHostView* view = host->view(); |
| if (!view) |
| return; |
| view->AcceleratedSurfaceSetIOSurface(params_.window, |
| params_.width, |
| params_.height, |
| params_.identifier); |
| } |
| |
| private: |
| GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params params_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SetIOSurfaceDispatcher); |
| }; |
| |
| } // namespace |
| |
| void GpuProcessHost::OnAcceleratedSurfaceSetIOSurface( |
| const GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params& params) { |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| new SetIOSurfaceDispatcher(params)); |
| } |
| |
| namespace { |
| |
| class BuffersSwappedDispatcher : public Task { |
| public: |
| BuffersSwappedDispatcher( |
| int32 renderer_id, int32 render_view_id, gfx::PluginWindowHandle window) |
| : renderer_id_(renderer_id), |
| render_view_id_(render_view_id), |
| window_(window) { |
| } |
| |
| void Run() { |
| RenderViewHost* host = RenderViewHost::FromID(renderer_id_, |
| render_view_id_); |
| if (!host) |
| return; |
| RenderWidgetHostView* view = host->view(); |
| if (!view) |
| return; |
| view->AcceleratedSurfaceBuffersSwapped(window_); |
| } |
| |
| private: |
| int32 renderer_id_; |
| int32 render_view_id_; |
| gfx::PluginWindowHandle window_; |
| |
| DISALLOW_COPY_AND_ASSIGN(BuffersSwappedDispatcher); |
| }; |
| |
| } // namespace |
| |
| void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( |
| int32 renderer_id, |
| int32 render_view_id, |
| gfx::PluginWindowHandle window) { |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| new BuffersSwappedDispatcher(renderer_id, render_view_id, window)); |
| } |
| #endif |
| |
| void GpuProcessHost::ReplyToRenderer( |
| const IPC::ChannelHandle& channel, |
| const GPUInfo& gpu_info, |
| ResourceMessageFilter* filter) { |
| ViewMsg_GpuChannelEstablished* message = |
| new ViewMsg_GpuChannelEstablished(channel, gpu_info); |
| // If the renderer process is performing synchronous initialization, |
| // it needs to handle this message before receiving the reply for |
| // the synchronous ViewHostMsg_SynchronizeGpu message. |
| message->set_unblock(true); |
| filter->Send(message); |
| } |
| |
| URLRequestContext* GpuProcessHost::GetRequestContext( |
| uint32 request_id, |
| const ViewHostMsg_Resource_Request& request_data) { |
| return NULL; |
| } |
| |
| bool GpuProcessHost::CanShutdown() { |
| return true; |
| } |