// 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 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(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::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; }