blob: 091410c1c340438d83fe36be38a94401b0f817ff [file] [log] [blame]
// Copyright (c) 2010 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.
#if defined(OS_WIN)
#include <windows.h>
#endif
#include "chrome/gpu/gpu_channel.h"
#include "base/command_line.h"
#include "base/lock.h"
#include "base/process_util.h"
#include "base/string_util.h"
#include "base/waitable_event.h"
#include "build/build_config.h"
#include "chrome/common/child_process.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/gpu_messages.h"
#include "chrome/gpu/gpu_thread.h"
#if defined(OS_POSIX)
#include "ipc/ipc_channel_posix.h"
#endif
GpuChannel::GpuChannel(int renderer_id)
: renderer_id_(renderer_id)
#if defined(OS_POSIX)
, renderer_fd_(-1)
#endif
{
const CommandLine* command_line = CommandLine::ForCurrentProcess();
log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
}
GpuChannel::~GpuChannel() {
#if defined(OS_POSIX)
// If we still have the renderer FD, close it.
if (renderer_fd_ != -1) {
close(renderer_fd_);
}
#endif
}
void GpuChannel::OnChannelConnected(int32 peer_pid) {
if (!renderer_process_.Open(peer_pid)) {
NOTREACHED();
}
}
void GpuChannel::OnMessageReceived(const IPC::Message& message) {
if (log_messages_) {
LOG(INFO) << "received message @" << &message << " on channel @" << this
<< " with type " << message.type();
}
if (message.routing_id() == MSG_ROUTING_CONTROL) {
OnControlMessageReceived(message);
} else {
// The sender should know not to route messages to an object after it
// has been destroyed.
CHECK(router_.RouteMessage(message));
}
}
void GpuChannel::OnChannelError() {
static_cast<GpuThread*>(ChildThread::current())->RemoveChannel(renderer_id_);
}
bool GpuChannel::Send(IPC::Message* message) {
if (log_messages_) {
LOG(INFO) << "sending message @" << message << " on channel @" << this
<< " with type " << message->type();
}
if (!channel_.get()) {
delete message;
return false;
}
return channel_->Send(message);
}
void GpuChannel::OnControlMessageReceived(const IPC::Message& msg) {
IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg)
IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateViewCommandBuffer,
OnCreateViewCommandBuffer)
IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenCommandBuffer,
OnCreateOffscreenCommandBuffer)
IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer,
OnDestroyCommandBuffer)
IPC_MESSAGE_UNHANDLED_ERROR()
IPC_END_MESSAGE_MAP()
}
int GpuChannel::GenerateRouteID() {
static int last_id = 0;
return ++last_id;
}
void GpuChannel::OnCreateViewCommandBuffer(gfx::NativeViewId view_id,
int32* route_id) {
*route_id = 0;
#if defined(ENABLE_GPU)
gfx::PluginWindowHandle handle = NULL;
#if defined(OS_WIN)
gfx::NativeView view = gfx::NativeViewFromId(view_id);
// Check that the calling renderer is allowed to render to this window.
// TODO(apatrick): consider killing the renderer process rather than failing.
int view_renderer_id = reinterpret_cast<int>(
GetProp(view, chrome::kChromiumRendererIdProperty));
if (view_renderer_id != renderer_id_)
return;
handle = view;
#elif defined(OS_LINUX)
ChildThread* gpu_thread = ChildThread::current();
// Ask the browser for the view's XID.
// TODO(piman): This assumes that it doesn't change. It can change however
// when tearing off tabs. This needs a fix in the browser UI code. A possible
// alternative would be to add a socket/plug pair like with plugins but that
// has issues with events and focus.
gpu_thread->Send(new GpuHostMsg_GetViewXID(view_id, &handle));
#else
// TODO(apatrick): This needs to be something valid for mac and linux.
// Offscreen rendering will work on these platforms but not rendering to the
// window.
DCHECK_EQ(view_id, 0);
#endif
*route_id = GenerateRouteID();
scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub(
this, handle, NULL, gfx::Size(), 0, *route_id));
router_.AddRoute(*route_id, stub.get());
stubs_.AddWithID(stub.release(), *route_id);
#endif // ENABLE_GPU
}
void GpuChannel::OnCreateOffscreenCommandBuffer(int32 parent_route_id,
const gfx::Size& size,
uint32 parent_texture_id,
int32* route_id) {
#if defined(ENABLE_GPU)
*route_id = GenerateRouteID();
GpuCommandBufferStub* parent_stub = NULL;
if (parent_route_id != 0)
parent_stub = stubs_.Lookup(parent_route_id);
scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub(
this,
NULL,
parent_stub,
size,
parent_texture_id,
*route_id));
router_.AddRoute(*route_id, stub.get());
stubs_.AddWithID(stub.release(), *route_id);
#else
*route_id = 0;
#endif
}
void GpuChannel::OnDestroyCommandBuffer(int32 route_id) {
#if defined(ENABLE_GPU)
router_.RemoveRoute(route_id);
stubs_.Remove(route_id);
#endif
}
bool GpuChannel::Init() {
// Check whether we're already initialized.
if (channel_.get())
return true;
// Map renderer ID to a (single) channel to that process.
std::string channel_name = GetChannelName();
#if defined(OS_POSIX)
// This gets called when the GpuChannel is initially created. At this
// point, create the socketpair and assign the GPU side FD to the channel
// name. Keep the renderer side FD as a member variable in the PluginChannel
// to be able to transmit it through IPC.
int gpu_fd;
IPC::SocketPair(&gpu_fd, &renderer_fd_);
IPC::AddChannelSocket(channel_name, gpu_fd);
#endif
channel_.reset(new IPC::SyncChannel(
channel_name, IPC::Channel::MODE_SERVER, this, NULL,
ChildProcess::current()->io_message_loop(), false,
ChildProcess::current()->GetShutDownEvent()));
return true;
}
std::string GpuChannel::GetChannelName() {
return StringPrintf("%d.r%d", base::GetCurrentProcId(), renderer_id_);
}