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)
33namespace {
34
35// One of the linux specific headers defines this as a macro.
36#ifdef DestroyAll
37#undef DestroyAll
38#endif
39
40IDMap<GpuProcessHostUIShim> g_hosts_by_id;
41
42class 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
60class 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
83void 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
97RouteToGpuProcessHostUIShimTask::RouteToGpuProcessHostUIShimTask(
98    int host_id,
99    const IPC::Message& msg)
100  : host_id_(host_id),
101    msg_(msg) {
102}
103
104RouteToGpuProcessHostUIShimTask::~RouteToGpuProcessHostUIShimTask() {
105}
106
107void RouteToGpuProcessHostUIShimTask::Run() {
108  GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(host_id_);
109  if (ui_shim)
110    ui_shim->OnMessageReceived(msg_);
111}
112
113GpuProcessHostUIShim::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
129GpuProcessHostUIShim* GpuProcessHostUIShim::Create(int host_id) {
130  DCHECK(!FromID(host_id));
131  return new GpuProcessHostUIShim(host_id);
132}
133
134// static
135void GpuProcessHostUIShim::Destroy(int host_id) {
136  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
137  delete FromID(host_id);
138}
139
140// static
141void 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
150GpuProcessHostUIShim* GpuProcessHostUIShim::FromID(int host_id) {
151  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
152  return g_hosts_by_id.Lookup(host_id);
153}
154
155bool 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
177bool 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
188void 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
194void 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
204GpuProcessHostUIShim::~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
223bool 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
246void 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
259void 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
300void 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
315void 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
339void 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