diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-27 21:29:42 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-27 21:29:42 +0000 |
commit | 59a74142e90e463622aca40f68b210ff360a6bf8 (patch) | |
tree | defb7f383289948bc89decce1b9dd7b7e8eb20b4 /content | |
parent | 4d5223d547585b7baca9f93b4c5d96cbf91eaf10 (diff) | |
download | chromium_src-59a74142e90e463622aca40f68b210ff360a6bf8.zip chromium_src-59a74142e90e463622aca40f68b210ff360a6bf8.tar.gz chromium_src-59a74142e90e463622aca40f68b210ff360a6bf8.tar.bz2 |
Merge 82990 - Move the synchronous GPU messages to the IO thread to avoid deadlock.
I patched in Jonathan's change from http://codereview.chromium.org/6881105/ for the CreateViewCommandBuffer message, and also added the Synchronize and EstablishChannel. The latter required making GpuDataManager callable from the IO thread. I moved the code that loads the blacklist from the prefs and web to Chrome code, since I wanted to do that anyways so that GpuProcessHostUIShim and GpuDataManager can move to content (I'll do that later).
Since the messages are filtered on the IO thread, it's now GpuProcessHost that creates GpuProcessHostUIShim. Accordingly, all the code that used to call GpuProcessHostUIShim to send a message now has to call GpuProcessHost, since the logic of when to create a process is there. Also, since there's no IO thread object for the in-process case, I've had to break that in the meantime. Al will take a look at that later.
BUG=77536
Review URL: http://codereview.chromium.org/6902021
TBR=jam@chromium.org
Review URL: http://codereview.chromium.org/6902087
git-svn-id: svn://svn.chromium.org/chrome/branches/742/src@83219 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/browser/gpu_process_host.cc | 407 | ||||
-rw-r--r-- | content/browser/gpu_process_host.h | 119 | ||||
-rw-r--r-- | content/browser/renderer_host/gpu_message_filter.cc | 43 | ||||
-rw-r--r-- | content/browser/renderer_host/gpu_message_filter.h | 2 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host.cc | 24 |
5 files changed, 486 insertions, 109 deletions
diff --git a/content/browser/gpu_process_host.cc b/content/browser/gpu_process_host.cc index 3cdb894..96e43c3 100644 --- a/content/browser/gpu_process_host.cc +++ b/content/browser/gpu_process_host.cc @@ -9,6 +9,7 @@ #include "base/metrics/histogram.h" #include "base/process_util.h" #include "base/string_piece.h" +#include "chrome/browser/gpu_data_manager.h" #include "chrome/browser/gpu_process_host_ui_shim.h" #include "chrome/browser/tab_contents/render_view_host_delegate_helper.h" #include "chrome/common/chrome_switches.h" @@ -17,9 +18,11 @@ #include "content/browser/renderer_host/render_widget_host.h" #include "content/browser/renderer_host/render_widget_host_view.h" #include "content/common/gpu_messages.h" +#include "gpu/common/gpu_trace_event.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_switches.h" #include "media/base/media_switches.h" +#include "ui/gfx/gl/gl_context.h" #include "ui/gfx/gl/gl_switches.h" #if defined(OS_LINUX) @@ -47,24 +50,137 @@ static int g_gpu_crash_count = 0; // Once this limit is reached, any request to launch the gpu process will fail. static const int kGpuMaxCrashCount = 3; +int g_last_host_id = 0; + +#if defined(OS_LINUX) + +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_); +} + +#endif + +void SendGpuProcessMessage(int renderer_id, + content::CauseForGpuLaunch cause, + IPC::Message* message) { + GpuProcessHost* host = GpuProcessHost::GetForRenderer( + renderer_id, cause); + if (host) { + host->Send(message); + } else { + delete message; + } +} + } // anonymous namespace +#if defined(OS_LINUX) +// 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 GpuProcessHost::SurfaceRef { + public: + explicit SurfaceRef(gfx::PluginWindowHandle surface); + ~SurfaceRef(); + private: + gfx::PluginWindowHandle surface_; +}; + +GpuProcessHost::SurfaceRef::SurfaceRef(gfx::PluginWindowHandle surface) + : surface_(surface) { + GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); + if (!manager->AddRefPermanentXID(surface_)) { + LOG(ERROR) << "Surface " << surface << " cannot be referenced."; + } +} + +GpuProcessHost::SurfaceRef::~SurfaceRef() { + BrowserThread::PostTask(BrowserThread::UI, + FROM_HERE, + new ReleasePermanentXIDDispatcher(surface_)); +} +#endif // defined(OS_LINUX) + // static -GpuProcessHost* GpuProcessHost::Create( - int host_id, - const GpuFeatureFlags& gpu_feature_flags, - content::CauseForGpuLaunch cause_for_gpu_launch) { +GpuProcessHost* GpuProcessHost::GetForRenderer( + int renderer_id, content::CauseForGpuLaunch cause) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - GpuProcessHost* host = new GpuProcessHost(host_id, - gpu_feature_flags, - cause_for_gpu_launch); - if (!host->Init()) { - delete host; + // Don't grant further access to GPU if it is not allowed. + GpuDataManager* gpu_data_manager = GpuDataManager::GetInstance(); + if (gpu_data_manager != NULL && !gpu_data_manager->GpuAccessAllowed()) + return NULL; + + // The current policy is to ignore the renderer ID and use a single GPU + // process for all renderers. Later this will be extended to allow the + // use of multiple GPU processes. + if (!g_hosts_by_id.IsEmpty()) { + IDMap<GpuProcessHost>::iterator it(&g_hosts_by_id); + return it.GetCurrentValue(); + } + + if (cause == content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH) return NULL; + + int host_id; + /* TODO(apatrick): this is currently broken because this function is called on + the IO thread from GpuMessageFilter, and we don't have an IO thread object + when running the GPU code in the browser at the moment. + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) || + CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU)) { + if (!g_browser_process->gpu_thread()) + return NULL; + + // Initialize GL on the GPU thread. + // TODO(apatrick): Handle failure to initialize (asynchronously). + if (!BrowserThread::PostTask( + BrowserThread::GPU, + FROM_HERE, + NewRunnableFunction(&gfx::GLContext::InitializeOneOff))) { + return NULL; + } + + host_id = 0; + } else { + host_id = ++g_last_host_id; } + */ + host_id = ++g_last_host_id; + + UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLaunchCause", + cause, + content::CAUSE_FOR_GPU_LAUNCH_MAX_ENUM); - return host; + GpuProcessHost* host = new GpuProcessHost(host_id); + if (host->Init()) + return host; + + delete host; + return NULL; +} + +// static +void GpuProcessHost::SendOnIO(int renderer_id, + content::CauseForGpuLaunch cause, + IPC::Message* message) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction( + &SendGpuProcessMessage, renderer_id, cause, message)); } // static @@ -77,22 +193,37 @@ GpuProcessHost* GpuProcessHost::FromID(int host_id) { return g_hosts_by_id.Lookup(host_id); } -GpuProcessHost::GpuProcessHost( - int host_id, - const GpuFeatureFlags& gpu_feature_flags, - content::CauseForGpuLaunch cause_for_gpu_launch) +GpuProcessHost::GpuProcessHost(int host_id) : BrowserChildProcessHost(GPU_PROCESS), host_id_(host_id), - gpu_feature_flags_(gpu_feature_flags) { + gpu_process_(base::kNullProcessHandle) { g_hosts_by_id.AddWithID(this, host_id_); - UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLaunchCause", - cause_for_gpu_launch, - content::CAUSE_FOR_GPU_LAUNCH_MAX_ENUM); + if (host_id == 0) + gpu_process_ = base::GetCurrentProcessHandle(); + + // 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)); } GpuProcessHost::~GpuProcessHost() { - DCHECK(CalledOnValidThread()); +#if defined(OS_WIN) + if (gpu_process_) + CloseHandle(gpu_process_); +#endif + + // In case we never started, clean up. + while (!queued_messages_.empty()) { + delete queued_messages_.front(); + queued_messages_.pop(); + } g_hosts_by_id.Remove(host_id_); @@ -121,15 +252,173 @@ void GpuProcessHost::RouteOnUIThread(const IPC::Message& message) { bool GpuProcessHost::Send(IPC::Message* msg) { DCHECK(CalledOnValidThread()); + if (opening_channel()) { + queued_messages_.push(msg); + return true; + } + return BrowserChildProcessHost::Send(msg); } bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) { DCHECK(CalledOnValidThread()); - RouteOnUIThread(message); + IPC_BEGIN_MESSAGE_MAP(GpuProcessHost, 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(RouteOnUIThread(message)) + IPC_END_MESSAGE_MAP() + return true; } +void GpuProcessHost::OnChannelConnected(int32 peer_pid) { + while (!queued_messages_.empty()) { + Send(queued_messages_.front()); + queued_messages_.pop(); + } +} + +void GpuProcessHost::EstablishGpuChannel( + int renderer_id, + EstablishChannelCallback *callback) { + DCHECK(CalledOnValidThread()); + GPU_TRACE_EVENT0("gpu", "GpuProcessHostUIShim::EstablishGpuChannel"); + linked_ptr<EstablishChannelCallback> wrapped_callback(callback); + + // If GPU features are already blacklisted, no need to establish the channel. + if (!GpuDataManager::GetInstance()->GpuAccessAllowed()) { + EstablishChannelError( + wrapped_callback.release(), IPC::ChannelHandle(), + base::kNullProcessHandle, GPUInfo()); + return; + } + + if (Send(new GpuMsg_EstablishChannel(renderer_id))) { + channel_requests_.push(wrapped_callback); + } else { + EstablishChannelError( + wrapped_callback.release(), IPC::ChannelHandle(), + base::kNullProcessHandle, GPUInfo()); + } +} + +void GpuProcessHost::Synchronize(SynchronizeCallback* callback) { + DCHECK(CalledOnValidThread()); + linked_ptr<SynchronizeCallback> wrapped_callback(callback); + + if (Send(new GpuMsg_Synchronize())) { + synchronize_requests_.push(wrapped_callback); + } else { + SynchronizeError(wrapped_callback.release()); + } +} + +void GpuProcessHost::CreateViewCommandBuffer( + gfx::PluginWindowHandle compositing_surface, + int32 render_view_id, + int32 renderer_id, + const GPUCreateCommandBufferConfig& init_params, + CreateCommandBufferCallback* callback) { + DCHECK(CalledOnValidThread()); + linked_ptr<CreateCommandBufferCallback> wrapped_callback(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)); +#endif // defined(OS_LINUX) + + if (compositing_surface != gfx::kNullPluginWindow && + Send(new GpuMsg_CreateViewCommandBuffer( + compositing_surface, render_view_id, renderer_id, init_params))) { + create_command_buffer_requests_.push(wrapped_callback); +#if defined(OS_LINUX) + surface_refs_.insert(std::pair<ViewID, linked_ptr<SurfaceRef> >( + view_id, surface_ref)); +#endif // defined(OS_LINUX) + } else { + CreateCommandBufferError(wrapped_callback.release(), MSG_ROUTING_NONE); + } +} + +void GpuProcessHost::OnChannelEstablished( + const IPC::ChannelHandle& channel_handle) { + // The GPU process should have launched at this point and this object should + // have been notified of its process handle. + DCHECK(gpu_process_); + + 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()) { + Send(new GpuMsg_CloseChannel(channel_handle)); + EstablishChannelError(callback.release(), + IPC::ChannelHandle(), + base::kNullProcessHandle, + GPUInfo()); + RouteOnUIThread(GpuHostMsg_OnLogMessage( + logging::LOG_WARNING, + "WARNING", + "Hardware acceleration is unavailable.")); + return; + } + + callback->Run( + channel_handle, gpu_process_, GpuDataManager::GetInstance()->gpu_info()); +} + +void GpuProcessHost::OnSynchronizeReply() { + // 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 GpuProcessHost::OnCommandBufferCreated(const int32 route_id) { + 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(), route_id); + else + callback->Run(route_id); + } +} + +void GpuProcessHost::OnDestroyCommandBuffer( + gfx::PluginWindowHandle window, int32 renderer_id, + int32 render_view_id) { +#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 GpuProcessHost::OnGraphicsInfoCollected(const GPUInfo& gpu_info) { + GpuDataManager::GetInstance()->UpdateGpuInfo(gpu_info); +} + bool GpuProcessHost::CanShutdown() { return true; } @@ -138,45 +427,21 @@ void GpuProcessHost::OnProcessLaunched() { // Send the GPU process handle to the UI thread before it has to // respond to any requests to establish a GPU channel. The response // to such requests require that the GPU process handle be known. - base::ProcessHandle child_handle; #if defined(OS_WIN) DuplicateHandle(base::GetCurrentProcessHandle(), handle(), base::GetCurrentProcessHandle(), - &child_handle, + &gpu_process_, PROCESS_DUP_HANDLE, FALSE, 0); #else - child_handle = handle(); + gpu_process_ = handle(); #endif - - BrowserThread::PostTask( - BrowserThread::UI, - FROM_HERE, - NewRunnableFunction(&GpuProcessHostUIShim::NotifyGpuProcessLaunched, - host_id_, - child_handle)); } -namespace { - -void SendOutstandingRepliesDispatcher(int host_id) { - GpuProcessHostUIShim *ui_shim = GpuProcessHostUIShim::FromID(host_id); - DCHECK(ui_shim); - ui_shim->SendOutstandingReplies(); -} - -void SendOutstandingReplies(int host_id) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - NewRunnableFunction(&SendOutstandingRepliesDispatcher, host_id)); -} - -} // namespace - void GpuProcessHost::OnChildDied() { - SendOutstandingReplies(host_id_); + SendOutstandingReplies(); // Located in OnChildDied because OnProcessCrashed suffers from a race // condition on Linux. UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents", @@ -190,7 +455,7 @@ void GpuProcessHost::OnChildDied() { } void GpuProcessHost::OnProcessCrashed(int exit_code) { - SendOutstandingReplies(host_id_); + SendOutstandingReplies(); if (++g_gpu_crash_count >= kGpuMaxCrashCount) { // The gpu process is too unstable to use. Disable it for current session. RenderViewHostDelegateHelper::set_gpu_enabled(false); @@ -202,7 +467,7 @@ bool GpuProcessHost::LaunchGpuProcess() { if (!RenderViewHostDelegateHelper::gpu_enabled() || g_gpu_crash_count >= kGpuMaxCrashCount) { - SendOutstandingReplies(host_id_); + SendOutstandingReplies(); RenderViewHostDelegateHelper::set_gpu_enabled(false); return false; } @@ -242,9 +507,9 @@ bool GpuProcessHost::LaunchGpuProcess() { cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, arraysize(kSwitchNames)); - if (gpu_feature_flags_.flags() & GpuFeatureFlags::kGpuFeatureMultisampling) { + GpuFeatureFlags flags = GpuDataManager::GetInstance()->GetGpuFeatureFlags(); + if (flags.flags() & GpuFeatureFlags::kGpuFeatureMultisampling) cmd_line->AppendSwitch(switches::kDisableGLMultisampling); - } // If specified, prepend a launcher program to the command line. if (!gpu_launcher.empty()) @@ -263,3 +528,43 @@ bool GpuProcessHost::LaunchGpuProcess() { LAUNCHED, GPU_PROCESS_LIFETIME_EVENT_MAX); return true; } + +void GpuProcessHost::SendOutstandingReplies() { + // 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::ChannelHandle(), + base::kNullProcessHandle, + GPUInfo()); + } + + // Now unblock all renderers waiting for synchronization replies. + while (!synchronize_requests_.empty()) { + linked_ptr<SynchronizeCallback> callback = synchronize_requests_.front(); + synchronize_requests_.pop(); + SynchronizeError(callback.release()); + } +} + +void GpuProcessHost::EstablishChannelError( + EstablishChannelCallback* callback, + const IPC::ChannelHandle& channel_handle, + base::ProcessHandle renderer_process_for_gpu, + const GPUInfo& gpu_info) { + scoped_ptr<EstablishChannelCallback> wrapped_callback(callback); + wrapped_callback->Run(channel_handle, renderer_process_for_gpu, gpu_info); +} + +void GpuProcessHost::CreateCommandBufferError( + CreateCommandBufferCallback* callback, int32 route_id) { + scoped_ptr<GpuProcessHost::CreateCommandBufferCallback> + wrapped_callback(callback); + callback->Run(route_id); +} + +void GpuProcessHost::SynchronizeError(SynchronizeCallback* callback) { + scoped_ptr<SynchronizeCallback> wrapped_callback(callback); + wrapped_callback->Run(); +} diff --git a/content/browser/gpu_process_host.h b/content/browser/gpu_process_host.h index 7513beb..afcd99b 100644 --- a/content/browser/gpu_process_host.h +++ b/content/browser/gpu_process_host.h @@ -6,10 +6,18 @@ #define CONTENT_BROWSER_GPU_PROCESS_HOST_H_ #pragma once +#include <map> +#include <queue> + +#include "base/callback.h" +#include "base/memory/linked_ptr.h" #include "base/threading/non_thread_safe.h" #include "content/browser/browser_child_process_host.h" -#include "content/common/gpu_feature_flags.h" +#include "content/common/gpu/gpu_info.h" #include "content/common/gpu_process_launch_causes.h" +#include "ui/gfx/native_widget_types.h" + +struct GPUCreateCommandBufferConfig; namespace IPC { class Message; @@ -18,27 +26,63 @@ class Message; class GpuProcessHost : public BrowserChildProcessHost, public base::NonThreadSafe { public: - - // Create a GpuProcessHost with the given ID. The object can be found using - // FromID with the same id. - static GpuProcessHost* Create( - int host_id, - const GpuFeatureFlags& gpu_feature_flags, - content::CauseForGpuLaunch); + // Creates a new GpuProcessHost or gets one for a particular + // renderer process, resulting in the launching of a GPU process if required. + // Returns null on failure. It is not safe to store the pointer once control + // has returned to the message loop as it can be destroyed. Instead store the + // associated GPU host ID. A renderer ID of zero means the browser process. + // This could return NULL if GPU access is not allowed (blacklisted). + static GpuProcessHost* GetForRenderer(int renderer_id, + content::CauseForGpuLaunch cause); + + // Helper function to send the given message to the GPU process on the IO + // thread. Calls GetForRenderer and if a host is returned, sends it. + // Can be called from any thread. + static void SendOnIO(int renderer_id, + content::CauseForGpuLaunch cause, + IPC::Message* message); // Get the GPU process host for the GPU process with the given ID. Returns // null if the process no longer exists. static GpuProcessHost* FromID(int host_id); + int host_id() const { return host_id_; } virtual bool Send(IPC::Message* msg); - // IPC::Channel::Listener implementation. + // ChildProcessHost implementation. virtual bool OnMessageReceived(const IPC::Message& message); + virtual void OnChannelConnected(int32 peer_pid); + + typedef Callback3<const IPC::ChannelHandle&, + base::ProcessHandle, + const GPUInfo&>::Type + EstablishChannelCallback; + + // Tells the GPU process to create a new channel for communication with a + // renderer. Once the GPU process responds asynchronously with the IPC handle + // and GPUInfo, we call the callback. + void EstablishGpuChannel( + int renderer_id, EstablishChannelCallback* callback); + + typedef Callback0::Type SynchronizeCallback; + + // Sends a reply message later when the next GpuHostMsg_SynchronizeReply comes + // in. + void Synchronize(SynchronizeCallback* callback); + + typedef Callback1<int32>::Type CreateCommandBufferCallback; + + // Tells the GPU process to create a new command buffer that draws into the + // window associated with the given renderer. + void CreateViewCommandBuffer( + gfx::PluginWindowHandle compositing_surface, + int32 render_view_id, + int32 renderer_id, + const GPUCreateCommandBufferConfig& init_params, + CreateCommandBufferCallback* callback); private: - GpuProcessHost(int host_id, - const GpuFeatureFlags& gpu_feature_flags, - content::CauseForGpuLaunch); + GpuProcessHost(int host_id); virtual ~GpuProcessHost(); bool Init(); @@ -51,12 +95,61 @@ class GpuProcessHost : public BrowserChildProcessHost, virtual void OnChildDied(); virtual void OnProcessCrashed(int exit_code); + // Message handlers. + void OnChannelEstablished(const IPC::ChannelHandle& channel_handle); + void OnSynchronizeReply(); + void OnCommandBufferCreated(const int32 route_id); + void OnDestroyCommandBuffer( + gfx::PluginWindowHandle window, int32 renderer_id, int32 render_view_id); + void OnGraphicsInfoCollected(const GPUInfo& gpu_info); + bool LaunchGpuProcess(); + void SendOutstandingReplies(); + void EstablishChannelError( + EstablishChannelCallback* callback, + const IPC::ChannelHandle& channel_handle, + base::ProcessHandle renderer_process_for_gpu, + const GPUInfo& gpu_info); + void CreateCommandBufferError(CreateCommandBufferCallback* callback, + int32 route_id); + void SynchronizeError(SynchronizeCallback* callback); + // The serial number of the GpuProcessHost / GpuProcessHostUIShim pair. int host_id_; - GpuFeatureFlags gpu_feature_flags_; + // These are the channel requests that we have already sent to + // the GPU process, but haven't heard back about yet. + std::queue<linked_ptr<EstablishChannelCallback> > channel_requests_; + + // The pending synchronization requests we need to reply to. + std::queue<linked_ptr<SynchronizeCallback> > synchronize_requests_; + + // The pending create command buffer requests we need to reply to. + std::queue<linked_ptr<CreateCommandBufferCallback> > + create_command_buffer_requests_; + +#if defined(OS_LINUX) + typedef std::pair<int32 /* renderer_id */, + int32 /* render_view_id */> ViewID; + + // Encapsulates surfaces that we lock when creating view command buffers. + // We release this lock once the command buffer (or associated GPU process) + // is destroyed. This prevents the browser from destroying the surface + // while the GPU process is drawing to it. + + // Multimap is used to simulate reference counting, see comment in + // GpuProcessHostUIShim::CreateViewCommandBuffer. + class SurfaceRef; + typedef std::multimap<ViewID, linked_ptr<SurfaceRef> > SurfaceRefMap; + SurfaceRefMap surface_refs_; +#endif + + // Qeueud messages to send when the process launches. + std::queue<IPC::Message*> queued_messages_; + + // The handle for the GPU process or null if it is not known to be launched. + base::ProcessHandle gpu_process_; DISALLOW_COPY_AND_ASSIGN(GpuProcessHost); }; diff --git a/content/browser/renderer_host/gpu_message_filter.cc b/content/browser/renderer_host/gpu_message_filter.cc index 5fa10ad..0f8c65d 100644 --- a/content/browser/renderer_host/gpu_message_filter.cc +++ b/content/browser/renderer_host/gpu_message_filter.cc @@ -9,7 +9,6 @@ #include "content/browser/renderer_host/gpu_message_filter.h" #include "base/callback.h" -#include "chrome/browser/gpu_process_host_ui_shim.h" #include "chrome/common/render_messages.h" #include "content/browser/gpu_process_host.h" #include "content/common/gpu_messages.h" @@ -25,13 +24,6 @@ GpuMessageFilter::~GpuMessageFilter() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } -void GpuMessageFilter::OverrideThreadForMessage( - const IPC::Message& message, - BrowserThread::ID* thread) { - if (IPC_MESSAGE_CLASS(message) == GpuMsgStart) - *thread = BrowserThread::UI; -} - bool GpuMessageFilter::OnMessageReceived( const IPC::Message& message, bool* message_was_ok) { @@ -62,7 +54,6 @@ class EstablishChannelCallback public: explicit EstablishChannelCallback(GpuMessageFilter* filter): filter_(filter->AsWeakPtr()) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } virtual void RunWithParams(const TupleType& params) { @@ -94,7 +85,6 @@ class EstablishChannelCallback renderer_process_for_gpu = 0; } - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); IPC::Message* reply = new GpuMsg_GpuChannelEstablished( channel, renderer_process_for_gpu, gpu_info); @@ -115,7 +105,6 @@ class SynchronizeCallback : public CallbackRunner<Tuple0> { SynchronizeCallback(GpuMessageFilter* filter, IPC::Message* reply): filter_(filter->AsWeakPtr()), reply_(reply) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } virtual void RunWithParams(const TupleType& params) { @@ -123,7 +112,6 @@ class SynchronizeCallback : public CallbackRunner<Tuple0> { } void Send() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (filter_) filter_->Send(reply_); } @@ -139,7 +127,7 @@ class CreateCommandBufferCallback : public CallbackRunner<Tuple1<int32> > { IPC::Message* reply) : filter_(filter->AsWeakPtr()), reply_(reply) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); } virtual void RunWithParams(const TupleType& params) { @@ -147,7 +135,7 @@ class CreateCommandBufferCallback : public CallbackRunner<Tuple1<int32> > { } void Send(int32 route_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); GpuHostMsg_CreateViewCommandBuffer::WriteReplyParams(reply_, route_id); if (filter_) filter_->Send(reply_); @@ -172,27 +160,26 @@ void GpuMessageFilter::OnEstablishGpuChannel( // terminates, the renderer process will not find itself unknowingly sending // IPCs to a newly launched GPU process. Also, I will rename this function // to something like OnCreateGpuProcess. - GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(gpu_host_id_); - if (!ui_shim) { - ui_shim = GpuProcessHostUIShim::GetForRenderer(render_process_id_, - cause_for_gpu_launch); - if (!ui_shim) { + GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); + if (!host) { + host = GpuProcessHost::GetForRenderer( + render_process_id_, cause_for_gpu_launch); + if (!host) { callback->Run(IPC::ChannelHandle(), static_cast<base::ProcessHandle>(NULL), GPUInfo()); return; } - gpu_host_id_ = ui_shim->host_id(); + gpu_host_id_ = host->host_id(); } - ui_shim->EstablishGpuChannel(render_process_id_, - callback.release()); + host->EstablishGpuChannel(render_process_id_, callback.release()); } void GpuMessageFilter::OnSynchronizeGpu(IPC::Message* reply) { - GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(gpu_host_id_); - if (!ui_shim) { + GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); + if (!host) { // TODO(apatrick): Eventually, this IPC message will be routed to a // GpuProcessStub with a particular routing ID. The error will be set if // the GpuProcessStub with that routing ID is not in the MessageRouter. @@ -201,7 +188,7 @@ void GpuMessageFilter::OnSynchronizeGpu(IPC::Message* reply) { return; } - ui_shim->Synchronize(new SynchronizeCallback(this, reply)); + host->Synchronize(new SynchronizeCallback(this, reply)); } void GpuMessageFilter::OnCreateViewCommandBuffer( @@ -209,8 +196,8 @@ void GpuMessageFilter::OnCreateViewCommandBuffer( int32 render_view_id, const GPUCreateCommandBufferConfig& init_params, IPC::Message* reply) { - GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(gpu_host_id_); - if (!ui_shim) { + GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_); + if (!host) { // TODO(apatrick): Eventually, this IPC message will be routed to a // GpuProcessStub with a particular routing ID. The error will be set if // the GpuProcessStub with that routing ID is not in the MessageRouter. @@ -219,7 +206,7 @@ void GpuMessageFilter::OnCreateViewCommandBuffer( return; } - ui_shim->CreateViewCommandBuffer( + host->CreateViewCommandBuffer( compositing_surface, render_view_id, render_process_id_, diff --git a/content/browser/renderer_host/gpu_message_filter.h b/content/browser/renderer_host/gpu_message_filter.h index c8e8ada..1653390 100644 --- a/content/browser/renderer_host/gpu_message_filter.h +++ b/content/browser/renderer_host/gpu_message_filter.h @@ -28,8 +28,6 @@ class GpuMessageFilter : public BrowserMessageFilter, explicit GpuMessageFilter(int render_process_id); // BrowserMessageFilter methods: - virtual void OverrideThreadForMessage(const IPC::Message& message, - BrowserThread::ID* thread); virtual bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok); virtual void OnDestruct() const; diff --git a/content/browser/renderer_host/render_widget_host.cc b/content/browser/renderer_host/render_widget_host.cc index d7ad0b5..61691b60 100644 --- a/content/browser/renderer_host/render_widget_host.cc +++ b/content/browser/renderer_host/render_widget_host.cc @@ -9,11 +9,11 @@ #include "base/message_loop.h" #include "base/metrics/histogram.h" #include "chrome/browser/accessibility/browser_accessibility_state.h" -#include "chrome/browser/gpu_process_host_ui_shim.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/render_messages.h" #include "chrome/common/spellcheck_messages.h" +#include "content/browser/gpu_process_host.h" #include "content/browser/renderer_host/backing_store.h" #include "content/browser/renderer_host/backing_store_manager.h" #include "content/browser/renderer_host/render_process_host.h" @@ -229,13 +229,10 @@ void RenderWidgetHost::WasHidden() { // reduce its resource utilization. Send(new ViewMsg_WasHidden(routing_id_)); - GpuProcessHostUIShim* host_ui_shim = - GpuProcessHostUIShim::GetForRenderer( - process()->id(), content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH); - if (host_ui_shim) { - host_ui_shim->Send(new GpuMsg_VisibilityChanged( - routing_id_, process()->id(), false)); - } + GpuProcessHost::SendOnIO( + 0, + content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH, + new GpuMsg_VisibilityChanged(routing_id_, process()->id(), false)); // TODO(darin): what about constrained windows? it doesn't look like they // see a message when their parent is hidden. maybe there is something more @@ -273,13 +270,10 @@ void RenderWidgetHost::WasRestored() { } Send(new ViewMsg_WasRestored(routing_id_, needs_repainting)); - GpuProcessHostUIShim* host_ui_shim = - GpuProcessHostUIShim::GetForRenderer( - process()->id(), content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH); - if (host_ui_shim) { - host_ui_shim->Send(new GpuMsg_VisibilityChanged( - routing_id_, process()->id(), true)); - } + GpuProcessHost::SendOnIO( + 0, + content::CAUSE_FOR_GPU_LAUNCH_NO_LAUNCH, + new GpuMsg_VisibilityChanged(routing_id_, process()->id(), true)); process_->WidgetRestored(); |