1 // TODO(jam): move this file to src/content once we have an interface that the 2 // embedder provides. We can then use it to get the resource and resize the 3 // window. 4 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style license that can be 6 // found in the LICENSE file. 7 8 #include "chrome/browser/gpu_process_host_ui_shim.h" 9 10 #include "base/command_line.h" 11 #include "base/id_map.h" 12 #include "base/process_util.h" 13 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/gpu_data_manager.h" 15 #include "chrome/browser/io_thread.h" 16 #include "content/browser/browser_thread.h" 17 #include "content/browser/gpu_process_host.h" 18 #include "content/browser/renderer_host/render_process_host.h" 19 #include "content/browser/renderer_host/render_view_host.h" 20 #include "content/browser/renderer_host/render_widget_host_view.h" 21 #include "content/common/content_switches.h" 22 #include "content/common/gpu_messages.h" 23 #include "gpu/common/gpu_trace_event.h" 24 25 #if defined(OS_LINUX) 26 // These two #includes need to come after gpu_messages.h. 27 #include <gdk/gdkwindow.h> // NOLINT 28 #include <gdk/gdkx.h> // NOLINT 29 #include "ui/base/x/x11_util.h" 30 #include "ui/gfx/gtk_native_view_id_manager.h" 31 #include "ui/gfx/size.h" 32 #endif // defined(OS_LINUX) 33 namespace { 34 35 // One of the linux specific headers defines this as a macro. 36 #ifdef DestroyAll 37 #undef DestroyAll 38 #endif 39 40 IDMap<GpuProcessHostUIShim> g_hosts_by_id; 41 42 class SendOnIOThreadTask : public Task { 43 public: 44 SendOnIOThreadTask(int host_id, IPC::Message* msg) 45 : host_id_(host_id), 46 msg_(msg) { 47 } 48 49 private: 50 void Run() { 51 GpuProcessHost* host = GpuProcessHost::FromID(host_id_); 52 if (host) 53 host->Send(msg_.release()); 54 } 55 56 int host_id_; 57 scoped_ptr<IPC::Message> msg_; 58 }; 59 60 class UIThreadSender : public IPC::Channel::Sender { 61 public: 62 virtual bool Send(IPC::Message* msg) { 63 // The GPU process must never send a synchronous IPC message to the browser 64 // process. This could result in deadlock. Unfortunately linux does this for 65 // GpuHostMsg_ResizeXID. TODO(apatrick): fix this before issuing any GL calls 66 // on the browser process' GPU thread. 67 #if !defined(OS_LINUX) 68 DCHECK(!msg->is_sync()); 69 #endif 70 71 // When the GpuChannelManager sends an IPC, post it to the UI thread without 72 // using IPC. 73 bool success = BrowserThread::PostTask( 74 BrowserThread::UI, 75 FROM_HERE, 76 new RouteToGpuProcessHostUIShimTask(0, *msg)); 77 78 delete msg; 79 return success; 80 } 81 }; 82 83 void ForwardMessageToGpuThread(GpuChannelManager* gpu_channel_manager, 84 IPC::Message* msg) { 85 bool success = gpu_channel_manager->OnMessageReceived(*msg); 86 87 // If the message was not handled, it is likely it was intended for the 88 // GpuChildThread, which does not exist in single process and in process GPU 89 // mode. 90 DCHECK(success); 91 92 delete msg; 93 } 94 95 } // namespace 96 97 RouteToGpuProcessHostUIShimTask::RouteToGpuProcessHostUIShimTask( 98 int host_id, 99 const IPC::Message& msg) 100 : host_id_(host_id), 101 msg_(msg) { 102 } 103 104 RouteToGpuProcessHostUIShimTask::~RouteToGpuProcessHostUIShimTask() { 105 } 106 107 void RouteToGpuProcessHostUIShimTask::Run() { 108 GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(host_id_); 109 if (ui_shim) 110 ui_shim->OnMessageReceived(msg_); 111 } 112 113 GpuProcessHostUIShim::GpuProcessHostUIShim(int host_id) 114 : host_id_(host_id), 115 gpu_channel_manager_(NULL), 116 ui_thread_sender_(NULL) { 117 g_hosts_by_id.AddWithID(this, host_id_); 118 if (host_id == 0) { 119 ui_thread_sender_ = new UIThreadSender; 120 gpu_channel_manager_ = new GpuChannelManager( 121 ui_thread_sender_, 122 NULL, 123 g_browser_process->io_thread()->message_loop(), 124 g_browser_process->shutdown_event()); 125 } 126 } 127 128 // static 129 GpuProcessHostUIShim* GpuProcessHostUIShim::Create(int host_id) { 130 DCHECK(!FromID(host_id)); 131 return new GpuProcessHostUIShim(host_id); 132 } 133 134 // static 135 void GpuProcessHostUIShim::Destroy(int host_id) { 136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 137 delete FromID(host_id); 138 } 139 140 // static 141 void GpuProcessHostUIShim::DestroyAll() { 142 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 143 while (!g_hosts_by_id.IsEmpty()) { 144 IDMap<GpuProcessHostUIShim>::iterator it(&g_hosts_by_id); 145 delete it.GetCurrentValue(); 146 } 147 } 148 149 // static 150 GpuProcessHostUIShim* GpuProcessHostUIShim::FromID(int host_id) { 151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 152 return g_hosts_by_id.Lookup(host_id); 153 } 154 155 bool GpuProcessHostUIShim::Send(IPC::Message* msg) { 156 DCHECK(CalledOnValidThread()); 157 158 bool success; 159 160 if (host_id_ == 0) { 161 success = BrowserThread::PostTask( 162 BrowserThread::GPU, 163 FROM_HERE, 164 NewRunnableFunction(ForwardMessageToGpuThread, 165 gpu_channel_manager_, 166 msg)); 167 } else { 168 success = BrowserThread::PostTask( 169 BrowserThread::IO, 170 FROM_HERE, 171 new SendOnIOThreadTask(host_id_, msg)); 172 } 173 174 return success; 175 } 176 177 bool GpuProcessHostUIShim::OnMessageReceived(const IPC::Message& message) { 178 DCHECK(CalledOnValidThread()); 179 180 if (message.routing_id() != MSG_ROUTING_CONTROL) 181 return false; 182 183 return OnControlMessageReceived(message); 184 } 185 186 #if defined(OS_MACOSX) 187 188 void GpuProcessHostUIShim::DidDestroyAcceleratedSurface(int renderer_id, 189 int render_view_id) { 190 // Destroy the command buffer that owns the accelerated surface. 191 Send(new GpuMsg_DestroyCommandBuffer(renderer_id, render_view_id)); 192 } 193 194 void GpuProcessHostUIShim::SendToGpuHost(int host_id, IPC::Message* msg) { 195 GpuProcessHostUIShim* ui_shim = FromID(host_id); 196 if (!ui_shim) 197 return; 198 199 ui_shim->Send(msg); 200 } 201 202 #endif 203 204 GpuProcessHostUIShim::~GpuProcessHostUIShim() { 205 DCHECK(CalledOnValidThread()); 206 g_hosts_by_id.Remove(host_id_); 207 208 // Ensure these are destroyed on the GPU thread. 209 if (gpu_channel_manager_) { 210 BrowserThread::DeleteSoon(BrowserThread::GPU, 211 FROM_HERE, 212 gpu_channel_manager_); 213 gpu_channel_manager_ = NULL; 214 } 215 if (ui_thread_sender_) { 216 BrowserThread::DeleteSoon(BrowserThread::GPU, 217 FROM_HERE, 218 ui_thread_sender_); 219 ui_thread_sender_ = NULL; 220 } 221 } 222 223 bool GpuProcessHostUIShim::OnControlMessageReceived( 224 const IPC::Message& message) { 225 DCHECK(CalledOnValidThread()); 226 227 IPC_BEGIN_MESSAGE_MAP(GpuProcessHostUIShim, message) 228 IPC_MESSAGE_HANDLER(GpuHostMsg_OnLogMessage, 229 OnLogMessage) 230 #if defined(OS_LINUX) && !defined(TOUCH_UI) || defined(OS_WIN) 231 IPC_MESSAGE_HANDLER(GpuHostMsg_ResizeView, OnResizeView) 232 #elif defined(OS_MACOSX) 233 IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceSetIOSurface, 234 OnAcceleratedSurfaceSetIOSurface) 235 IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped, 236 OnAcceleratedSurfaceBuffersSwapped) 237 #elif defined(OS_WIN) 238 IPC_MESSAGE_HANDLER(GpuHostMsg_ScheduleComposite, OnScheduleComposite); 239 #endif 240 IPC_MESSAGE_UNHANDLED_ERROR() 241 IPC_END_MESSAGE_MAP() 242 243 return true; 244 } 245 246 void GpuProcessHostUIShim::OnLogMessage( 247 int level, 248 const std::string& header, 249 const std::string& message) { 250 DictionaryValue* dict = new DictionaryValue(); 251 dict->SetInteger("level", level); 252 dict->SetString("header", header); 253 dict->SetString("message", message); 254 GpuDataManager::GetInstance()->AddLogMessage(dict); 255 } 256 257 #if defined(OS_LINUX) && !defined(TOUCH_UI) || defined(OS_WIN) 258 259 void GpuProcessHostUIShim::OnResizeView(int32 renderer_id, 260 int32 render_view_id, 261 int32 command_buffer_route_id, 262 gfx::Size size) { 263 RenderViewHost* host = RenderViewHost::FromID(renderer_id, render_view_id); 264 if (host) { 265 RenderWidgetHostView* view = host->view(); 266 if (view) { 267 gfx::PluginWindowHandle handle = view->GetCompositingSurface(); 268 269 // Resize the window synchronously. The GPU process must not issue GL 270 // calls on the command buffer until the window is the size it expects it 271 // to be. 272 #if defined(OS_LINUX) && !defined(TOUCH_UI) 273 GdkWindow* window = reinterpret_cast<GdkWindow*>( 274 gdk_xid_table_lookup(handle)); 275 if (window) { 276 Display* display = GDK_WINDOW_XDISPLAY(window); 277 gdk_window_resize(window, size.width(), size.height()); 278 XSync(display, False); 279 } 280 #elif defined(OS_WIN) 281 SetWindowPos(handle, 282 NULL, 283 0, 0, 284 size.width(), 285 size.height(), 286 SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOZORDER | 287 SWP_NOACTIVATE | SWP_DEFERERASE); 288 #endif 289 } 290 } 291 292 // Always respond even if the window no longer exists. The GPU process cannot 293 // make progress on the resizing command buffer until it receives the 294 // response. 295 Send(new GpuMsg_ResizeViewACK(renderer_id, command_buffer_route_id)); 296 } 297 298 #elif defined(OS_MACOSX) 299 300 void GpuProcessHostUIShim::OnAcceleratedSurfaceSetIOSurface( 301 const GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params& params) { 302 RenderViewHost* host = RenderViewHost::FromID(params.renderer_id, 303 params.render_view_id); 304 if (!host) 305 return; 306 RenderWidgetHostView* view = host->view(); 307 if (!view) 308 return; 309 view->AcceleratedSurfaceSetIOSurface(params.window, 310 params.width, 311 params.height, 312 params.identifier); 313 } 314 315 void GpuProcessHostUIShim::OnAcceleratedSurfaceBuffersSwapped( 316 const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) { 317 RenderViewHost* host = RenderViewHost::FromID(params.renderer_id, 318 params.render_view_id); 319 if (!host) 320 return; 321 RenderWidgetHostView* view = host->view(); 322 if (!view) 323 return; 324 view->AcceleratedSurfaceBuffersSwapped( 325 // Parameters needed to swap the IOSurface. 326 params.window, 327 params.surface_id, 328 // Parameters needed to formulate an acknowledgment. 329 params.renderer_id, 330 params.route_id, 331 host_id_, 332 params.swap_buffers_count); 333 } 334 335 #endif 336 337 #if defined(OS_WIN) 338 339 void GpuProcessHostUIShim::OnScheduleComposite(int renderer_id, 340 int render_view_id) { 341 RenderViewHost* host = RenderViewHost::FromID(renderer_id, 342 render_view_id); 343 if (!host) { 344 return; 345 } 346 host->ScheduleComposite(); 347 } 348 349 #endif 350