blob: bb71e624d970dd1ba6b3377d31b23b88c2e5d251 [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/process_util.h"
#include "base/string_util.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"
#include "chrome/gpu/gpu_video_service.h"
#include "content/common/child_process.h"
#if defined(OS_POSIX)
#include "ipc/ipc_channel_posix.h"
#endif
GpuChannel::GpuChannel(GpuThread* gpu_thread,
int renderer_id)
: gpu_thread_(gpu_thread),
renderer_id_(renderer_id),
renderer_process_(NULL),
renderer_pid_(NULL) {
DCHECK(gpu_thread);
DCHECK(renderer_id);
const CommandLine* command_line = CommandLine::ForCurrentProcess();
log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
}
GpuChannel::~GpuChannel() {
#if defined(OS_WIN)
if (renderer_process_)
CloseHandle(renderer_process_);
#endif
}
bool GpuChannel::OnMessageReceived(const IPC::Message& message) {
if (log_messages_) {
VLOG(1) << "received message @" << &message << " on channel @" << this
<< " with type " << message.type();
}
if (message.routing_id() == MSG_ROUTING_CONTROL)
return OnControlMessageReceived(message);
if (!router_.RouteMessage(message)) {
// Respond to sync messages even if router failed to route.
if (message.is_sync()) {
IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
reply->set_reply_error();
Send(reply);
}
return false;
}
return true;
}
void GpuChannel::OnChannelError() {
gpu_thread_->RemoveChannel(renderer_id_);
}
void GpuChannel::OnChannelConnected(int32 peer_pid) {
renderer_pid_ = peer_pid;
}
bool GpuChannel::Send(IPC::Message* message) {
if (log_messages_) {
VLOG(1) << "sending message @" << message << " on channel @" << this
<< " with type " << message->type();
}
if (!channel_.get()) {
delete message;
return false;
}
return channel_->Send(message);
}
void GpuChannel::CreateViewCommandBuffer(
gfx::PluginWindowHandle window,
int32 render_view_id,
const GPUCreateCommandBufferConfig& init_params,
int32* route_id) {
*route_id = MSG_ROUTING_NONE;
#if defined(ENABLE_GPU)
*route_id = GenerateRouteID();
scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub(
this, window, NULL, gfx::Size(), init_params.allowed_extensions,
init_params.attribs, 0, *route_id, renderer_id_, render_view_id));
router_.AddRoute(*route_id, stub.get());
stubs_.AddWithID(stub.release(), *route_id);
#endif // ENABLE_GPU
}
#if defined(OS_MACOSX)
void GpuChannel::AcceleratedSurfaceBuffersSwapped(
int32 route_id, uint64 swap_buffers_count) {
GpuCommandBufferStub* stub = stubs_.Lookup(route_id);
if (stub == NULL)
return;
stub->AcceleratedSurfaceBuffersSwapped(swap_buffers_count);
}
void GpuChannel::DidDestroySurface(int32 renderer_route_id) {
// Since acclerated views are created in the renderer process and then sent
// to the browser process during GPU channel construction, it is possible that
// this is called before a GpuCommandBufferStub for |renderer_route_id| was
// put into |stubs_|. Hence, do not walk |stubs_| here but instead remember
// all |renderer_route_id|s this was called for and use them later.
destroyed_renderer_routes_.insert(renderer_route_id);
}
bool GpuChannel::IsRenderViewGone(int32 renderer_route_id) {
return destroyed_renderer_routes_.count(renderer_route_id) > 0;
}
#endif
bool GpuChannel::OnControlMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg)
IPC_MESSAGE_HANDLER(GpuChannelMsg_Initialize, OnInitialize)
IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenCommandBuffer,
OnCreateOffscreenCommandBuffer)
IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer,
OnDestroyCommandBuffer)
IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateVideoDecoder,
OnCreateVideoDecoder)
IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyVideoDecoder,
OnDestroyVideoDecoder)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
DCHECK(handled);
return handled;
}
int GpuChannel::GenerateRouteID() {
static int last_id = 0;
return ++last_id;
}
void GpuChannel::OnInitialize(base::ProcessHandle renderer_process) {
// Initialize should only happen once.
DCHECK(!renderer_process_);
// Verify that the renderer has passed its own process handle.
if (base::GetProcId(renderer_process) == renderer_pid_)
renderer_process_ = renderer_process;
}
void GpuChannel::OnCreateOffscreenCommandBuffer(
int32 parent_route_id,
const gfx::Size& size,
const GPUCreateCommandBufferConfig& init_params,
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,
gfx::kNullPluginWindow,
parent_stub,
size,
init_params.allowed_extensions,
init_params.attribs,
parent_texture_id,
*route_id,
0, 0));
router_.AddRoute(*route_id, stub.get());
stubs_.AddWithID(stub.release(), *route_id);
#else
*route_id = MSG_ROUTING_NONE;
#endif
}
void GpuChannel::OnDestroyCommandBuffer(int32 route_id) {
#if defined(ENABLE_GPU)
router_.RemoveRoute(route_id);
stubs_.Remove(route_id);
#endif
}
void GpuChannel::OnCreateVideoDecoder(int32 context_route_id,
int32 decoder_host_id) {
// TODO(cevans): do NOT re-enable this until GpuVideoService has been checked
// for integer overflows, including the classic "width * height" overflow.
#if 0
VLOG(1) << "GpuChannel::OnCreateVideoDecoder";
GpuVideoService* service = GpuVideoService::GetInstance();
if (service == NULL) {
// TODO(hclam): Need to send a failure message.
return;
}
// The context ID corresponds to the command buffer used.
GpuCommandBufferStub* stub = stubs_.Lookup(context_route_id);
int32 decoder_id = GenerateRouteID();
// TODO(hclam): Need to be careful about the lifetime of the command buffer
// decoder.
bool ret = service->CreateVideoDecoder(
this, &router_, decoder_host_id, decoder_id,
stub->processor()->decoder());
DCHECK(ret) << "Failed to create a GpuVideoDecoder";
#endif
}
void GpuChannel::OnDestroyVideoDecoder(int32 decoder_id) {
#if defined(ENABLE_GPU)
LOG(ERROR) << "GpuChannel::OnDestroyVideoDecoder";
GpuVideoService* service = GpuVideoService::GetInstance();
if (service == NULL)
return;
service->DestroyVideoDecoder(&router_, decoder_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();
channel_.reset(new IPC::SyncChannel(
channel_name, IPC::Channel::MODE_SERVER, this,
ChildProcess::current()->io_message_loop(), false,
ChildProcess::current()->GetShutDownEvent()));
return true;
}
std::string GpuChannel::GetChannelName() {
return StringPrintf("%d.r%d.gpu", base::GetCurrentProcId(), renderer_id_);
}
#if defined(OS_POSIX)
int GpuChannel::GetRendererFileDescriptor() {
int fd = -1;
if (channel_.get()) {
fd = channel_->GetClientFileDescriptor();
}
return fd;
}
#endif // defined(OS_POSIX)