OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "content/browser/gpu/gpu_message_hub.h" |
| 6 |
| 7 #include "base/command_line.h" |
| 8 #include "content/browser/browser_thread.h" |
| 9 #include "content/browser/gpu/gpu_data_manager.h" |
| 10 #include "content/browser/gpu/gpu_process_host.h" |
| 11 #include "content/browser/gpu/gpu_process_host_ui_shim.h" |
| 12 #include "content/browser/gpu/gpu_thread_host.h" |
| 13 #include "content/common/content_switches.h" |
| 14 #include "content/common/gpu/gpu_info.h" |
| 15 #include "content/common/gpu/gpu_messages.h" |
| 16 #include "ipc/ipc_channel_handle.h" |
| 17 #include "ipc/ipc_message.h" |
| 18 #include "ipc/ipc_message_macros.h" |
| 19 |
| 20 #if defined(OS_LINUX) |
| 21 #include "ui/gfx/gtk_native_view_id_manager.h" |
| 22 |
| 23 class ReleasePermanentXIDDispatcher: public Task { |
| 24 public: |
| 25 explicit ReleasePermanentXIDDispatcher(gfx::PluginWindowHandle surface); |
| 26 void Run(); |
| 27 private: |
| 28 gfx::PluginWindowHandle surface_; |
| 29 }; |
| 30 |
| 31 ReleasePermanentXIDDispatcher::ReleasePermanentXIDDispatcher( |
| 32 gfx::PluginWindowHandle surface) |
| 33 : surface_(surface) { |
| 34 } |
| 35 |
| 36 void ReleasePermanentXIDDispatcher::Run() { |
| 37 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); |
| 38 manager->ReleasePermanentXID(surface_); |
| 39 } |
| 40 |
| 41 // Used to put a lock on surfaces so that the window to which the GPU |
| 42 // process is drawing to doesn't disappear while it is drawing when |
| 43 // a tab is closed. |
| 44 class GpuMessageHub::SurfaceRef { |
| 45 public: |
| 46 explicit SurfaceRef(gfx::PluginWindowHandle surface); |
| 47 ~SurfaceRef(); |
| 48 private: |
| 49 gfx::PluginWindowHandle surface_; |
| 50 }; |
| 51 |
| 52 GpuMessageHub::SurfaceRef::SurfaceRef(gfx::PluginWindowHandle surface) |
| 53 : surface_(surface) { |
| 54 GtkNativeViewManager* manager = GtkNativeViewManager::GetInstance(); |
| 55 if (!manager->AddRefPermanentXID(surface_)) { |
| 56 LOG(ERROR) << "Surface " << surface << " cannot be referenced."; |
| 57 } |
| 58 } |
| 59 |
| 60 GpuMessageHub::SurfaceRef::~SurfaceRef() { |
| 61 BrowserThread::PostTask(BrowserThread::UI, |
| 62 FROM_HERE, |
| 63 new ReleasePermanentXIDDispatcher(surface_)); |
| 64 } |
| 65 #endif // defined(OS_LINUX |
| 66 |
| 67 |
| 68 // A task that will forward an IPC message to the UI shim. |
| 69 class RouteToGpuProcessHostUIShimTask : public Task { |
| 70 public: |
| 71 RouteToGpuProcessHostUIShimTask(int host_id, IPC::Message* msg) |
| 72 : host_id_(host_id), |
| 73 msg_(*msg) { |
| 74 } |
| 75 |
| 76 private: |
| 77 virtual void Run() { |
| 78 GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(host_id_); |
| 79 if (ui_shim) |
| 80 ui_shim->OnMessageReceived(msg_); |
| 81 } |
| 82 |
| 83 int host_id_; |
| 84 IPC::Message msg_; |
| 85 }; |
| 86 |
| 87 |
| 88 // A task that will forward an IPC message to a GpuProcessHost instance. |
| 89 class SendToGpuProcessHostTask : public Task { |
| 90 public: |
| 91 SendToGpuProcessHostTask(int host_id, IPC::Message* msg) |
| 92 : host_id_(host_id), |
| 93 msg_(msg) { |
| 94 } |
| 95 |
| 96 private: |
| 97 void Run() { |
| 98 GpuProcessHost* host = GpuProcessHost::FromID(host_id_); |
| 99 if (host) |
| 100 host->Send(msg_.release()); |
| 101 } |
| 102 |
| 103 int host_id_; |
| 104 scoped_ptr<IPC::Message> msg_; |
| 105 }; |
| 106 |
| 107 // static |
| 108 bool GpuMessageHub::SendToGPU(int host_id, IPC::Message* msg) { |
| 109 bool success; |
| 110 |
| 111 if (host_id == 0) { |
| 112 GpuThreadHost* host = GpuThreadHost::GetInstance(); |
| 113 success = host->Send(msg); |
| 114 } else { |
| 115 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| 116 success = BrowserThread::PostTask( |
| 117 BrowserThread::IO, |
| 118 FROM_HERE, |
| 119 new SendToGpuProcessHostTask(host_id, msg)); |
| 120 } else { |
| 121 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 122 GpuProcessHost* host = GpuProcessHost::FromID(host_id); |
| 123 success = host->Send(msg); |
| 124 } |
| 125 } |
| 126 |
| 127 return success; |
| 128 } |
| 129 |
| 130 // static |
| 131 bool GpuMessageHub::EstablishGpuChannel(int renderer_id, |
| 132 content::CauseForGpuLaunch cause, |
| 133 EstablishChannelCallback* callback, |
| 134 int& host_id) { |
| 135 linked_ptr<EstablishChannelCallback> wrapped_callback(callback); |
| 136 |
| 137 // If GPU features are already blacklisted, no need to establish the channel. |
| 138 if (!GpuDataManager::GetInstance()->GpuAccessAllowed()) { |
| 139 EstablishChannelError(callback); |
| 140 return false; |
| 141 } |
| 142 |
| 143 IPC::Message::Sender* sender; |
| 144 GpuMessageHub* hub; |
| 145 |
| 146 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) || |
| 147 CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU)) { |
| 148 GpuThreadHost* host = GpuThreadHost::GetInstance(); |
| 149 if (host) { |
| 150 host_id = 0; |
| 151 hub = host->GetMessageHub(); |
| 152 sender = host; |
| 153 } else { |
| 154 EstablishChannelError(callback); |
| 155 return false; |
| 156 } |
| 157 } else { |
| 158 GpuProcessHost* host = GpuProcessHost::GetForRenderer(renderer_id, |
| 159 cause); |
| 160 if (host) { |
| 161 host_id = host->host_id(); |
| 162 hub = host->GetMessageHub(); |
| 163 sender = host; |
| 164 } else { |
| 165 EstablishChannelError(callback); |
| 166 return false; |
| 167 } |
| 168 } |
| 169 |
| 170 DCHECK(hub); |
| 171 |
| 172 if (!sender->Send(new GpuMsg_EstablishChannel(renderer_id))) { |
| 173 EstablishChannelError(callback); |
| 174 return false; |
| 175 } |
| 176 |
| 177 hub->ExpectChannelEstablished(wrapped_callback); |
| 178 return true; |
| 179 } |
| 180 |
| 181 // static |
| 182 void GpuMessageHub::Synchronize(int host_id, SynchronizeCallback* callback) { |
| 183 linked_ptr<SynchronizeCallback> wrapped_callback(callback); |
| 184 |
| 185 IPC::Message::Sender* sender; |
| 186 GpuMessageHub* hub; |
| 187 |
| 188 if (!FromID(host_id, sender, hub)) { |
| 189 SynchronizeError(callback); |
| 190 return; |
| 191 } |
| 192 |
| 193 if (sender->Send(new GpuMsg_Synchronize())) |
| 194 hub->ExpectSynchronizeReply(wrapped_callback); |
| 195 else |
| 196 SynchronizeError(callback); |
| 197 } |
| 198 |
| 199 // static |
| 200 void GpuMessageHub::CreateViewCommandBuffer( |
| 201 int host_id, |
| 202 gfx::PluginWindowHandle compositing_surface, |
| 203 int32 render_view_id, |
| 204 int32 renderer_id, |
| 205 const GPUCreateCommandBufferConfig& init_params, |
| 206 CreateCommandBufferCallback* callback) { |
| 207 linked_ptr<CreateCommandBufferCallback> wrapped_callback(callback); |
| 208 |
| 209 IPC::Message::Sender* sender; |
| 210 GpuMessageHub* hub; |
| 211 |
| 212 if (!FromID(host_id, sender, hub)) { |
| 213 CreateCommandBufferError(callback); |
| 214 return; |
| 215 } |
| 216 |
| 217 if (sender->Send(new GpuMsg_CreateViewCommandBuffer( |
| 218 compositing_surface, render_view_id, renderer_id, init_params))) |
| 219 hub->ExpectCommandBufferCreated(compositing_surface, |
| 220 render_view_id, |
| 221 renderer_id, |
| 222 wrapped_callback); |
| 223 else |
| 224 CreateCommandBufferError(callback); |
| 225 } |
| 226 |
| 227 // static |
| 228 void GpuMessageHub::EstablishChannelError(EstablishChannelCallback* callback) { |
| 229 callback->Run(IPC::ChannelHandle(), |
| 230 base::kNullProcessHandle, |
| 231 GPUInfo()); |
| 232 } |
| 233 |
| 234 // static |
| 235 void GpuMessageHub::CreateCommandBufferError( |
| 236 CreateCommandBufferCallback* callback) { |
| 237 callback->Run(MSG_ROUTING_NONE); |
| 238 } |
| 239 |
| 240 // static |
| 241 void GpuMessageHub::SynchronizeError(SynchronizeCallback* callback) { |
| 242 callback->Run(); |
| 243 } |
| 244 |
| 245 // static |
| 246 bool GpuMessageHub::FromID( |
| 247 int host_id, |
| 248 IPC::Message::Sender*& host, |
| 249 GpuMessageHub*& hub) { |
| 250 |
| 251 if (host_id == 0) { |
| 252 GpuThreadHost* thread_host = GpuThreadHost::GetInstance(); |
| 253 |
| 254 if (!thread_host) |
| 255 return false; |
| 256 |
| 257 hub = thread_host->GetMessageHub(); |
| 258 host = thread_host; |
| 259 } else { |
| 260 GpuProcessHost* process_host = GpuProcessHost::FromID(host_id); |
| 261 |
| 262 if (!process_host) |
| 263 return false; |
| 264 |
| 265 hub = process_host->GetMessageHub(); |
| 266 host = process_host; |
| 267 } |
| 268 |
| 269 DCHECK(hub); |
| 270 return true; |
| 271 } |
| 272 |
| 273 GpuMessageHub::GpuMessageHub(int host_id) |
| 274 : host_id_(host_id), |
| 275 gpu_process_(base::kNullProcessHandle) { |
| 276 // Post a task to create the corresponding GpuProcessHostUIShim. The |
| 277 // GpuProcessHostUIShim will be destroyed if either the browser exits, |
| 278 // in which case it calls GpuProcessHostUIShim::DestroyAll, or the |
| 279 // GpuProcessHost is destroyed, which happens when the corresponding GPU |
| 280 // process terminates or fails to launch. |
| 281 BrowserThread::PostTask( |
| 282 BrowserThread::UI, |
| 283 FROM_HERE, |
| 284 NewRunnableFunction(&GpuProcessHostUIShim::Create, host_id)); |
| 285 } |
| 286 |
| 287 |
| 288 GpuMessageHub::~GpuMessageHub() { |
| 289 BrowserThread::PostTask(BrowserThread::UI, |
| 290 FROM_HERE, |
| 291 NewRunnableFunction(GpuProcessHostUIShim::Destroy, |
| 292 host_id_)); |
| 293 } |
| 294 |
| 295 void GpuMessageHub::SetGPUProcess(base::ProcessHandle gpu_process) { |
| 296 gpu_process_ = gpu_process; |
| 297 } |
| 298 |
| 299 bool GpuMessageHub::RouteToUIThread(IPC::Message* message) { |
| 300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 301 // The GPU process must never send a synchronous IPC message to the browser |
| 302 // process. This could result in deadlock. Unfortunately linux does this for |
| 303 // GpuHostMsg_ResizeXID. TODO(apatrick): fix this before issuing any GL calls |
| 304 // on the browser process' GPU thread. |
| 305 #if !defined(OS_LINUX) |
| 306 DCHECK(!message->is_sync()); |
| 307 #endif |
| 308 |
| 309 return BrowserThread::PostTask( |
| 310 BrowserThread::UI, |
| 311 FROM_HERE, |
| 312 new RouteToGpuProcessHostUIShimTask(host_id_, message)); |
| 313 } |
| 314 |
| 315 bool GpuMessageHub::OnMessageReceived(const IPC::Message& message) { |
| 316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 317 IPC_BEGIN_MESSAGE_MAP(GpuMessageHub, message) |
| 318 IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished) |
| 319 IPC_MESSAGE_HANDLER(GpuHostMsg_SynchronizeReply, OnSynchronizeReply) |
| 320 IPC_MESSAGE_HANDLER(GpuHostMsg_CommandBufferCreated, OnCommandBufferCreated) |
| 321 IPC_MESSAGE_HANDLER(GpuHostMsg_DestroyCommandBuffer, OnDestroyCommandBuffer) |
| 322 IPC_MESSAGE_HANDLER(GpuHostMsg_GraphicsInfoCollected, |
| 323 OnGraphicsInfoCollected) |
| 324 IPC_MESSAGE_UNHANDLED(RouteToUIThread(new IPC::Message(message))) |
| 325 IPC_END_MESSAGE_MAP() |
| 326 |
| 327 return true; |
| 328 } |
| 329 |
| 330 void GpuMessageHub::ExpectChannelEstablished( |
| 331 linked_ptr<EstablishChannelCallback>& callback) { |
| 332 DCHECK(CalledOnValidThread()); |
| 333 channel_requests_.push(callback); |
| 334 } |
| 335 |
| 336 void GpuMessageHub::ExpectSynchronizeReply( |
| 337 linked_ptr<SynchronizeCallback>& callback) { |
| 338 DCHECK(CalledOnValidThread()); |
| 339 synchronize_requests_.push(callback); |
| 340 } |
| 341 |
| 342 void GpuMessageHub::ExpectCommandBufferCreated( |
| 343 gfx::PluginWindowHandle compositing_surface, |
| 344 int32 render_view_id, |
| 345 int32 renderer_id, |
| 346 linked_ptr<CreateCommandBufferCallback>& callback) { |
| 347 DCHECK(CalledOnValidThread()); |
| 348 create_command_buffer_requests_.push(callback); |
| 349 |
| 350 #if defined(OS_LINUX) |
| 351 ViewID view_id(renderer_id, render_view_id); |
| 352 |
| 353 // There should only be one such command buffer (for the compositor). In |
| 354 // practice, if the GPU process lost a context, GraphicsContext3D with |
| 355 // associated command buffer and view surface will not be gone until new |
| 356 // one is in place and all layers are reattached. |
| 357 linked_ptr<SurfaceRef> surface_ref; |
| 358 SurfaceRefMap::iterator it = surface_refs_.find(view_id); |
| 359 if (it != surface_refs_.end()) |
| 360 surface_ref = (*it).second; |
| 361 else |
| 362 surface_ref.reset(new SurfaceRef(compositing_surface)); |
| 363 |
| 364 surface_refs_.insert(std::pair<ViewID, linked_ptr<SurfaceRef> >( |
| 365 view_id, surface_ref)); |
| 366 #endif // defined(OS_LINUX) |
| 367 } |
| 368 |
| 369 void GpuMessageHub::SendOutstandingReplies() { |
| 370 DCHECK(CalledOnValidThread()); |
| 371 // First send empty channel handles for all EstablishChannel requests. |
| 372 while (!channel_requests_.empty()) { |
| 373 linked_ptr<EstablishChannelCallback> callback = channel_requests_.front(); |
| 374 channel_requests_.pop(); |
| 375 EstablishChannelError(callback.release()); |
| 376 } |
| 377 } |
| 378 |
| 379 IPC::Message::Sender* GpuMessageHub::FromID(int host_id) { |
| 380 if (host_id == 0) |
| 381 return GpuThreadHost::GetInstance(); |
| 382 else |
| 383 return GpuProcessHost::FromID(host_id); |
| 384 } |
| 385 |
| 386 void GpuMessageHub::OnChannelEstablished( |
| 387 const IPC::ChannelHandle& channel_handle) { |
| 388 DCHECK(CalledOnValidThread()); |
| 389 // The GPU process should have launched at this point and this object should |
| 390 // have been notified of its process handle. |
| 391 DCHECK(gpu_process_ != base::kNullProcessHandle && channel_requests_.size()); |
| 392 |
| 393 linked_ptr<EstablishChannelCallback> callback = channel_requests_.front(); |
| 394 channel_requests_.pop(); |
| 395 |
| 396 // Currently if any of the GPU features are blacklisted, we don't establish a |
| 397 // GPU channel. |
| 398 if (!channel_handle.name.empty() && |
| 399 !GpuDataManager::GetInstance()->GpuAccessAllowed()) { |
| 400 IPC::Message::Sender* sender = FromID(host_id_); |
| 401 if (sender) |
| 402 sender->Send(new GpuMsg_CloseChannel(channel_handle)); |
| 403 EstablishChannelError(callback.release()); |
| 404 RouteToUIThread(new GpuHostMsg_OnLogMessage( |
| 405 logging::LOG_WARNING, |
| 406 "WARNING", |
| 407 "Hardware acceleration is unavailable.")); |
| 408 return; |
| 409 } |
| 410 |
| 411 callback->Run( |
| 412 channel_handle, gpu_process_, GpuDataManager::GetInstance()->gpu_info()); |
| 413 } |
| 414 |
| 415 void GpuMessageHub::OnSynchronizeReply() { |
| 416 DCHECK(CalledOnValidThread()); |
| 417 // Guard against race conditions in abrupt GPU process termination. |
| 418 if (!synchronize_requests_.empty()) { |
| 419 linked_ptr<SynchronizeCallback> callback(synchronize_requests_.front()); |
| 420 synchronize_requests_.pop(); |
| 421 callback->Run(); |
| 422 } |
| 423 } |
| 424 |
| 425 void GpuMessageHub::OnCommandBufferCreated(const int32 route_id) { |
| 426 DCHECK(CalledOnValidThread()); |
| 427 if (!create_command_buffer_requests_.empty()) { |
| 428 linked_ptr<CreateCommandBufferCallback> callback = |
| 429 create_command_buffer_requests_.front(); |
| 430 create_command_buffer_requests_.pop(); |
| 431 if (route_id == MSG_ROUTING_NONE) |
| 432 CreateCommandBufferError(callback.release()); |
| 433 else |
| 434 callback->Run(route_id); |
| 435 } |
| 436 } |
| 437 |
| 438 void GpuMessageHub::OnDestroyCommandBuffer(gfx::PluginWindowHandle window, |
| 439 int32 renderer_id, |
| 440 int32 render_view_id) { |
| 441 DCHECK(CalledOnValidThread()); |
| 442 #if defined(OS_LINUX) |
| 443 ViewID view_id(renderer_id, render_view_id); |
| 444 SurfaceRefMap::iterator it = surface_refs_.find(view_id); |
| 445 if (it != surface_refs_.end()) |
| 446 surface_refs_.erase(it); |
| 447 #endif // defined(OS_LINUX) |
| 448 } |
| 449 |
| 450 void GpuMessageHub::OnGraphicsInfoCollected(const GPUInfo& gpu_info) { |
| 451 GpuDataManager::GetInstance()->UpdateGpuInfo(gpu_info); |
| 452 } |
OLD | NEW |