// TODO(jam): move this file to src/content once we have an interface that the
// embedder provides. We can then use it to get the resource and resize the
// window.
// Copyright (c) 2011 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_ui_shim.h"
#include "base/command_line.h"
#include "base/id_map.h"
#include "base/process_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/gpu_data_manager.h"
#include "chrome/browser/io_thread.h"
#include "content/browser/browser_thread.h"
#include "content/browser/gpu_process_host.h"
#include "content/browser/renderer_host/render_process_host.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/renderer_host/render_widget_host_view.h"
#include "content/common/content_switches.h"
#include "content/common/gpu_messages.h"
#include "gpu/common/gpu_trace_event.h"
#if defined(OS_LINUX)
// These two #includes need to come after gpu_messages.h.
#include <gdk/gdkwindow.h> // NOLINT
#include <gdk/gdkx.h> // NOLINT
#include "ui/base/x/x11_util.h"
#include "ui/gfx/gtk_native_view_id_manager.h"
#include "ui/gfx/size.h"
#endif // defined(OS_LINUX)
namespace {
// One of the linux specific headers defines this as a macro.
#ifdef DestroyAll
#undef DestroyAll
#endif
IDMap<GpuProcessHostUIShim> g_hosts_by_id;
class SendOnIOThreadTask : public Task {
public:
SendOnIOThreadTask(int host_id, IPC::Message* msg)
: host_id_(host_id),
msg_(msg) {
}
private:
void Run() {
GpuProcessHost* host = GpuProcessHost::FromID(host_id_);
if (host)
host->Send(msg_.release());
}
int host_id_;
scoped_ptr<IPC::Message> msg_;
};
class UIThreadSender : public IPC::Channel::Sender {
public:
virtual bool Send(IPC::Message* msg) {
// The GPU process must never send a synchronous IPC message to the browser
// process. This could result in deadlock. Unfortunately linux does this for
// GpuHostMsg_ResizeXID. TODO(apatrick): fix this before issuing any GL calls
// on the browser process' GPU thread.
#if !defined(OS_LINUX)
DCHECK(!msg->is_sync());
#endif
// When the GpuChannelManager sends an IPC, post it to the UI thread without
// using IPC.
bool success = BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
new RouteToGpuProcessHostUIShimTask(0, *msg));
delete msg;
return success;
}
};
void ForwardMessageToGpuThread(GpuChannelManager* gpu_channel_manager,
IPC::Message* msg) {
bool success = gpu_channel_manager->OnMessageReceived(*msg);
// If the message was not handled, it is likely it was intended for the
// GpuChildThread, which does not exist in single process and in process GPU
// mode.
DCHECK(success);
delete msg;
}
} // namespace
RouteToGpuProcessHostUIShimTask::RouteToGpuProcessHostUIShimTask(
int host_id,
const IPC::Message& msg)
: host_id_(host_id),
msg_(msg) {
}
RouteToGpuProcessHostUIShimTask::~RouteToGpuProcessHostUIShimTask() {
}
void RouteToGpuProcessHostUIShimTask::Run() {
GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(host_id_);
if (ui_shim)
ui_shim->OnMessageReceived(msg_);
}
GpuProcessHostUIShim::GpuProcessHostUIShim(int host_id)
: host_id_(host_id),
gpu_channel_manager_(NULL),
ui_thread_sender_(NULL) {
g_hosts_by_id.AddWithID(this, host_id_);
if (host_id == 0) {
ui_thread_sender_ = new UIThreadSender;
gpu_channel_manager_ = new GpuChannelManager(
ui_thread_sender_,
NULL,
g_browser_process->io_thread()->message_loop(),
g_browser_process->shutdown_event());
}
}
// static
GpuProcessHostUIShim* GpuProcessHostUIShim::Create(int host_id) {
DCHECK(!FromID(host_id));
return new GpuProcessHostUIShim(host_id);
}
// static
void GpuProcessHostUIShim::Destroy(int host_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
delete FromID(host_id);
}
// static
void GpuProcessHostUIShim::DestroyAll() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
while (!g_hosts_by_id.IsEmpty()) {
IDMap<GpuProcessHostUIShim>::iterator it(&g_hosts_by_id);
delete it.GetCurrentValue();
}
}
// static
GpuProcessHostUIShim* GpuProcessHostUIShim::FromID(int host_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return g_hosts_by_id.Lookup(host_id);
}
bool GpuProcessHostUIShim::Send(IPC::Message* msg) {
DCHECK(CalledOnValidThread());
bool success;
if (host_id_ == 0) {
success = BrowserThread::PostTask(
BrowserThread::GPU,
FROM_HERE,
NewRunnableFunction(ForwardMessageToGpuThread,
gpu_channel_manager_,
msg));
} else {
success = BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
new SendOnIOThreadTask(host_id_, msg));
}
return success;
}
bool GpuProcessHostUIShim::OnMessageReceived(const IPC::Message& message) {
DCHECK(CalledOnValidThread());
if (message.routing_id() != MSG_ROUTING_CONTROL)
return false;
return OnControlMessageReceived(message);
}
#if defined(OS_MACOSX)
void GpuProcessHostUIShim::DidDestroyAcceleratedSurface(int renderer_id,
int render_view_id) {
// Destroy the command buffer that owns the accelerated surface.
Send(new GpuMsg_DestroyCommandBuffer(renderer_id, render_view_id));
}
void GpuProcessHostUIShim::SendToGpuHost(int host_id, IPC::Message* msg) {
GpuProcessHostUIShim* ui_shim = FromID(host_id);
if (!ui_shim)
return;
ui_shim->Send(msg);
}
#endif
GpuProcessHostUIShim::~GpuProcessHostUIShim() {
DCHECK(CalledOnValidThread());
g_hosts_by_id.Remove(host_id_);
// Ensure these are destroyed on the GPU thread.
if (gpu_channel_manager_) {
BrowserThread::DeleteSoon(BrowserThread::GPU,
FROM_HERE,
gpu_channel_manager_);
gpu_channel_manager_ = NULL;
}
if (ui_thread_sender_) {
BrowserThread::DeleteSoon(BrowserThread::GPU,
FROM_HERE,
ui_thread_sender_);
ui_thread_sender_ = NULL;
}
}
bool GpuProcessHostUIShim::OnControlMessageReceived(
const IPC::Message& message) {
DCHECK(CalledOnValidThread());
IPC_BEGIN_MESSAGE_MAP(GpuProcessHostUIShim, message)
IPC_MESSAGE_HANDLER(GpuHostMsg_OnLogMessage,
OnLogMessage)
#if defined(OS_LINUX) && !defined(TOUCH_UI) || defined(OS_WIN)
IPC_MESSAGE_HANDLER(GpuHostMsg_ResizeView, OnResizeView)
#elif defined(OS_MACOSX)
IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceSetIOSurface,
OnAcceleratedSurfaceSetIOSurface)
IPC_MESSAGE_HANDLER(GpuHostMsg_AcceleratedSurfaceBuffersSwapped,
OnAcceleratedSurfaceBuffersSwapped)
#elif defined(OS_WIN)
IPC_MESSAGE_HANDLER(GpuHostMsg_ScheduleComposite, OnScheduleComposite);
#endif
IPC_MESSAGE_UNHANDLED_ERROR()
IPC_END_MESSAGE_MAP()
return true;
}
void GpuProcessHostUIShim::OnLogMessage(
int level,
const std::string& header,
const std::string& message) {
DictionaryValue* dict = new DictionaryValue();
dict->SetInteger("level", level);
dict->SetString("header", header);
dict->SetString("message", message);
GpuDataManager::GetInstance()->AddLogMessage(dict);
}
#if defined(OS_LINUX) && !defined(TOUCH_UI) || defined(OS_WIN)
void GpuProcessHostUIShim::OnResizeView(int32 renderer_id,
int32 render_view_id,
int32 command_buffer_route_id,
gfx::Size size) {
RenderViewHost* host = RenderViewHost::FromID(renderer_id, render_view_id);
if (host) {
RenderWidgetHostView* view = host->view();
if (view) {
gfx::PluginWindowHandle handle = view->GetCompositingSurface();
// Resize the window synchronously. The GPU process must not issue GL
// calls on the command buffer until the window is the size it expects it
// to be.
#if defined(OS_LINUX) && !defined(TOUCH_UI)
GdkWindow* window = reinterpret_cast<GdkWindow*>(
gdk_xid_table_lookup(handle));
if (window) {
Display* display = GDK_WINDOW_XDISPLAY(window);
gdk_window_resize(window, size.width(), size.height());
XSync(display, False);
}
#elif defined(OS_WIN)
SetWindowPos(handle,
NULL,
0, 0,
size.width(),
size.height(),
SWP_NOSENDCHANGING | SWP_NOCOPYBITS | SWP_NOZORDER |
SWP_NOACTIVATE | SWP_DEFERERASE);
#endif
}
}
// Always respond even if the window no longer exists. The GPU process cannot
// make progress on the resizing command buffer until it receives the
// response.
Send(new GpuMsg_ResizeViewACK(renderer_id, command_buffer_route_id));
}
#elif defined(OS_MACOSX)
void GpuProcessHostUIShim::OnAcceleratedSurfaceSetIOSurface(
const GpuHostMsg_AcceleratedSurfaceSetIOSurface_Params& params) {
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);
}
void GpuProcessHostUIShim::OnAcceleratedSurfaceBuffersSwapped(
const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params) {
RenderViewHost* host = RenderViewHost::FromID(params.renderer_id,
params.render_view_id);
if (!host)
return;
RenderWidgetHostView* view = host->view();
if (!view)
return;
view->AcceleratedSurfaceBuffersSwapped(
// Parameters needed to swap the IOSurface.
params.window,
params.surface_id,
// Parameters needed to formulate an acknowledgment.
params.renderer_id,
params.route_id,
host_id_,
params.swap_buffers_count);
}
#endif
#if defined(OS_WIN)
void GpuProcessHostUIShim::OnScheduleComposite(int renderer_id,
int render_view_id) {
RenderViewHost* host = RenderViewHost::FromID(renderer_id,
render_view_id);
if (!host) {
return;
}
host->ScheduleComposite();
}
#endif