0

Moved creation of GPU command buffer shared memory into the browser process.

This is to allow the GPU process to be sandboxed on all platforms.

TEST=try, run WebGL app on win and mac.
BUG=none

Review URL: http://codereview.chromium.org/6588029

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76307 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
apatrick@chromium.org
2011-02-28 23:36:54 +00:00
parent 406e6e22b8
commit 36dbfe198c
15 changed files with 192 additions and 140 deletions

@ -225,9 +225,10 @@ IPC_SYNC_MESSAGE_CONTROL1_0(GpuChannelMsg_DestroyVideoDecoder,
// Initialize a command buffer with the given number of command entries.
// Returns the shared memory handle for the command buffer mapped to the
// calling process.
IPC_SYNC_MESSAGE_ROUTED1_1(GpuCommandBufferMsg_Initialize,
IPC_SYNC_MESSAGE_ROUTED2_1(GpuCommandBufferMsg_Initialize,
base::SharedMemoryHandle /* ring_buffer */,
int32 /* size */,
base::SharedMemoryHandle /* ring_buffer */)
bool /* result */)
// Get the current state of the command buffer.
IPC_SYNC_MESSAGE_ROUTED0_1(GpuCommandBufferMsg_GetState,

@ -192,11 +192,12 @@ bool GpuCommandBufferStub::Send(IPC::Message* message) {
}
void GpuCommandBufferStub::OnInitialize(
base::SharedMemoryHandle ring_buffer,
int32 size,
base::SharedMemoryHandle* ring_buffer) {
bool* result) {
DCHECK(!command_buffer_.get());
*ring_buffer = base::SharedMemory::NULLHandle();
*result = false;
command_buffer_.reset(new gpu::CommandBufferService);
@ -215,50 +216,57 @@ void GpuCommandBufferStub::OnInitialize(
gfx::PluginWindowHandle output_window_handle = handle_;
#endif // defined(OS_WIN)
// Initialize the CommandBufferService and GPUProcessor.
if (command_buffer_->Initialize(size)) {
Buffer buffer = command_buffer_->GetRingBuffer();
if (buffer.shared_memory) {
gpu::GPUProcessor* parent_processor =
parent_ ? parent_->processor_.get() : NULL;
processor_.reset(new gpu::GPUProcessor(command_buffer_.get(), NULL));
if (processor_->Initialize(
output_window_handle,
initial_size_,
allowed_extensions_.c_str(),
requested_attribs_,
parent_processor,
parent_texture_id_)) {
command_buffer_->SetPutOffsetChangeCallback(
NewCallback(processor_.get(),
&gpu::GPUProcessor::ProcessCommands));
processor_->SetSwapBuffersCallback(
NewCallback(this, &GpuCommandBufferStub::OnSwapBuffers));
#if defined(OS_WIN)
// Windows dups the shared memory handle it receives into the current process
// and closes it when this variable goes out of scope.
base::SharedMemory shared_memory(ring_buffer,
false,
channel_->renderer_process());
#else
// POSIX receives a dup of the shared memory handle and closes the dup when
// this variable goes out of scope.
base::SharedMemory shared_memory(ring_buffer, false);
#endif
// Initialize the CommandBufferService and GPUProcessor.
if (command_buffer_->Initialize(&shared_memory, size)) {
gpu::GPUProcessor* parent_processor =
parent_ ? parent_->processor_.get() : NULL;
processor_.reset(new gpu::GPUProcessor(command_buffer_.get(), NULL));
if (processor_->Initialize(
output_window_handle,
initial_size_,
allowed_extensions_.c_str(),
requested_attribs_,
parent_processor,
parent_texture_id_)) {
command_buffer_->SetPutOffsetChangeCallback(
NewCallback(processor_.get(),
&gpu::GPUProcessor::ProcessCommands));
processor_->SetSwapBuffersCallback(
NewCallback(this, &GpuCommandBufferStub::OnSwapBuffers));
// Assume service is responsible for duplicating the handle from the
// calling process.
buffer.shared_memory->ShareToProcess(channel_->renderer_process(),
ring_buffer);
#if defined(OS_MACOSX)
if (handle_) {
// This context conceptually puts its output directly on the
// screen, rendered by the accelerated plugin layer in
// RenderWidgetHostViewMac. Set up a pathway to notify the
// browser process when its contents change.
processor_->SetSwapBuffersCallback(
NewCallback(this,
&GpuCommandBufferStub::SwapBuffersCallback));
}
if (handle_) {
// This context conceptually puts its output directly on the
// screen, rendered by the accelerated plugin layer in
// RenderWidgetHostViewMac. Set up a pathway to notify the
// browser process when its contents change.
processor_->SetSwapBuffersCallback(
NewCallback(this,
&GpuCommandBufferStub::SwapBuffersCallback));
}
#endif // defined(OS_MACOSX)
// Set up a pathway for resizing the output window or framebuffer at the
// right time relative to other GL commands.
processor_->SetResizeCallback(
NewCallback(this, &GpuCommandBufferStub::ResizeCallback));
} else {
processor_.reset();
command_buffer_.reset();
}
// Set up a pathway for resizing the output window or framebuffer at the
// right time relative to other GL commands.
processor_->SetResizeCallback(
NewCallback(this, &GpuCommandBufferStub::ResizeCallback));
*result = true;
} else {
processor_.reset();
command_buffer_.reset();
}
}
}

@ -68,7 +68,9 @@ class GpuCommandBufferStub
private:
// Message handlers:
void OnInitialize(int32 size, base::SharedMemoryHandle* ring_buffer);
void OnInitialize(base::SharedMemoryHandle ring_buffer,
int32 size,
bool* result);
void OnGetState(gpu::CommandBuffer::State* state);
void OnAsyncGetState();
void OnFlush(int32 put_offset, gpu::CommandBuffer::State* state);

@ -2,12 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/plugin/command_buffer_stub.h"
#include "base/callback.h"
#include "base/scoped_open_process.h"
#include "base/shared_memory.h"
#include "chrome/common/gpu_messages.h"
#include "chrome/common/plugin_messages.h"
#include "chrome/plugin/command_buffer_stub.h"
#include "chrome/plugin/plugin_channel.h"
using gpu::Buffer;
@ -67,66 +68,13 @@ void CommandBufferStub::NotifyRepaint() {
Send(new GpuCommandBufferMsg_NotifyRepaint(route_id_));
}
void CommandBufferStub::OnInitialize(int32 size,
base::SharedMemoryHandle* ring_buffer) {
DCHECK(!command_buffer_.get());
*ring_buffer = base::SharedMemory::NULLHandle();
// Assume service is responsible for duplicating the handle from the calling
// process.
base::ScopedOpenProcess peer_process;
if (!peer_process.Open(channel_->peer_pid()))
return;
command_buffer_.reset(new gpu::CommandBufferService);
// Initialize the CommandBufferService.
if (!command_buffer_->Initialize(size)) {
Destroy();
return;
}
// Get the ring buffer.
Buffer buffer = command_buffer_->GetRingBuffer();
if (!buffer.shared_memory) {
Destroy();
return;
}
// Initialize the GPUProcessor.
processor_.reset(new gpu::GPUProcessor(command_buffer_.get(), NULL));
if (!processor_->Initialize(window_, gfx::Size(), NULL, std::vector<int32>(),
NULL, 0)) {
Destroy();
return;
}
// Perform platform specific initialization.
if (!InitializePlatformSpecific()) {
Destroy();
return;
}
// Share the ring buffer to the client process.
if (!buffer.shared_memory->ShareToProcess(peer_process.handle(),
ring_buffer)) {
Destroy();
return;
}
// Setup callbacks for events.
command_buffer_->SetPutOffsetChangeCallback(
NewCallback(processor_.get(),
&gpu::GPUProcessor::ProcessCommands));
#if defined(OS_MACOSX)
processor_->SetSwapBuffersCallback(
NewCallback(this,
&CommandBufferStub::SwapBuffersCallback));
processor_->SetTransportDIBAllocAndFree(
NewCallback(this, &CommandBufferStub::AllocTransportDIB),
NewCallback(this, &CommandBufferStub::FreeTransportDIB));
#endif
void CommandBufferStub::OnInitialize(base::SharedMemoryHandle ring_buffer,
int32 size,
bool* result) {
// TODO(apatrick): Pepper3D v1 is not used anymore. This function is never
// called. Delete the GPU plugin.
NOTREACHED();
*result = false;
}
void CommandBufferStub::OnGetState(gpu::CommandBuffer::State* state) {

@ -45,7 +45,9 @@ class CommandBufferStub : public IPC::Channel::Listener,
private:
// Message handlers:
void OnInitialize(int32 size, base::SharedMemoryHandle* ring_buffer);
void OnInitialize(base::SharedMemoryHandle ring_buffer,
int32 size,
bool* result);
void OnGetState(gpu::CommandBuffer::State* state);
void OnAsyncGetState();
void OnFlush(int32 put_offset, gpu::CommandBuffer::State* state);

@ -68,22 +68,62 @@ void CommandBufferProxy::SetChannelErrorCallback(Callback0::Type* callback) {
bool CommandBufferProxy::Initialize(int32 size) {
DCHECK(!ring_buffer_.get());
// Initialize the service. Assuming we are sandboxed, the GPU
// process is responsible for duplicating the handle. This might not be true
// for NaCl.
base::SharedMemoryHandle handle;
if (Send(new GpuCommandBufferMsg_Initialize(route_id_, size, &handle)) &&
base::SharedMemory::IsHandleValid(handle)) {
ring_buffer_.reset(new base::SharedMemory(handle, false));
if (ring_buffer_->Map(size)) {
num_entries_ = size / sizeof(gpu::CommandBufferEntry);
return true;
}
RenderThread* render_thread = RenderThread::current();
if (!render_thread)
return false;
ring_buffer_.reset();
base::SharedMemoryHandle handle;
if (!render_thread->Send(new ViewHostMsg_AllocateSharedMemoryBuffer(
size,
&handle))) {
return false;
}
return false;
if (!base::SharedMemory::IsHandleValid(handle))
return false;
#if defined(OS_POSIX)
handle.auto_close = false;
#endif
// Take ownership of shared memory. This will close the handle if Send below
// fails. Otherwise, callee takes ownership before this variable
// goes out of scope.
base::SharedMemory shared_memory(handle, false);
return Initialize(&shared_memory, size);
}
bool CommandBufferProxy::Initialize(base::SharedMemory* buffer, int32 size) {
bool result;
if (!Send(new GpuCommandBufferMsg_Initialize(route_id_,
buffer->handle(),
size,
&result))) {
LOG(ERROR) << "Could not send GpuCommandBufferMsg_Initialize.";
return false;
}
if (!result) {
LOG(ERROR) << "Failed to initialize command buffer service.";
return false;
}
base::SharedMemoryHandle handle;
if (!buffer->GiveToProcess(base::GetCurrentProcessHandle(), &handle)) {
LOG(ERROR) << "Failed to duplicate command buffer handle.";
return false;
}
ring_buffer_.reset(new base::SharedMemory(handle, false));
if (!ring_buffer_->Map(size)) {
LOG(ERROR) << "Failed to map shared memory for command buffer.";
ring_buffer_.reset();
return false;
}
num_entries_ = size / sizeof(gpu::CommandBufferEntry);
return true;
}
Buffer CommandBufferProxy::GetRingBuffer() {

@ -45,6 +45,7 @@ class CommandBufferProxy : public gpu::CommandBuffer,
// CommandBuffer implementation:
virtual bool Initialize(int32 size);
virtual bool Initialize(base::SharedMemory* buffer, int32 size);
virtual gpu::Buffer GetRingBuffer();
virtual State GetState();
virtual void Flush(int32 put_offset);

@ -33,6 +33,11 @@ class GLES2MockCommandBufferHelper : public CommandBuffer {
return true;
}
virtual bool Initialize(base::SharedMemory* buffer, int32 size) {
GPU_NOTREACHED();
return false;
}
virtual Buffer GetRingBuffer() {
return ring_buffer_buffer_;
}

@ -8,6 +8,10 @@
#include "../common/buffer.h"
#include "../common/constants.h"
namespace base {
class SharedMemory;
}
namespace gpu {
// Common interface for CommandBuffer implementations.
@ -55,6 +59,9 @@ class CommandBuffer {
// Initialize the command buffer with the given size.
virtual bool Initialize(int32 size) = 0;
// Initialize the command buffer using the given preallocated buffer.
virtual bool Initialize(base::SharedMemory* buffer, int32 size) = 0;
// Gets the ring buffer for the command buffer.
virtual Buffer GetRingBuffer() = 0;

@ -22,6 +22,7 @@ class MockCommandBuffer : public CommandBuffer {
virtual ~MockCommandBuffer();
MOCK_METHOD1(Initialize, bool(int32 size));
MOCK_METHOD2(Initialize, bool(base::SharedMemory* buffer, int32 size));
MOCK_METHOD0(GetRingBuffer, Buffer());
MOCK_METHOD0(GetState, State());
MOCK_METHOD1(Flush, void(int32 put_offset));

@ -25,6 +25,8 @@ CommandBufferService::CommandBufferService()
}
CommandBufferService::~CommandBufferService() {
delete ring_buffer_.shared_memory;
for (size_t i = 0; i < registered_objects_.size(); ++i) {
if (registered_objects_[i].shared_memory)
delete registered_objects_[i].shared_memory;
@ -33,42 +35,59 @@ CommandBufferService::~CommandBufferService() {
bool CommandBufferService::Initialize(int32 size) {
// Fail if already initialized.
if (ring_buffer_.get()) {
LOG(ERROR) << "CommandBufferService::Initialize "
<< "failed because already initialized.";
if (ring_buffer_.shared_memory) {
LOG(ERROR) << "Failed because already initialized.";
return false;
}
if (size <= 0 || size > kMaxCommandBufferSize) {
LOG(ERROR) << "CommandBufferService::Initialize "
<< "because command buffer size was invalid.";
LOG(ERROR) << "Failed because command buffer size was invalid.";
return false;
}
num_entries_ = size / sizeof(CommandBufferEntry);
ring_buffer_.reset(new SharedMemory);
if (ring_buffer_->CreateAndMapAnonymous(size)) {
SharedMemory shared_memory;
if (!shared_memory.CreateAnonymous(size)) {
LOG(ERROR) << "Failed to create shared memory for command buffer.";
return true;
}
num_entries_ = 0;
ring_buffer_.reset();
return Initialize(&shared_memory, size);
}
LOG(ERROR) << "CommandBufferService::Initialize failed because ring buffer "
<< "could not be created or mapped ";
bool CommandBufferService::Initialize(base::SharedMemory* buffer, int32 size) {
// Fail if already initialized.
if (ring_buffer_.shared_memory) {
LOG(ERROR) << "Failed because already initialized.";
return false;
}
return false;
base::SharedMemoryHandle shared_memory_handle;
if (!buffer->ShareToProcess(base::GetCurrentProcessHandle(),
&shared_memory_handle)) {
LOG(ERROR) << "Failed to duplicate command buffer shared memory handle.";
return false;
}
ring_buffer_.shared_memory = new base::SharedMemory(shared_memory_handle,
false);
if (!ring_buffer_.shared_memory->Map(size)) {
LOG(ERROR) << "Failed because ring buffer could not be created or mapped ";
delete ring_buffer_.shared_memory;
ring_buffer_.shared_memory = NULL;
return false;
}
ring_buffer_.ptr = ring_buffer_.shared_memory->memory();
ring_buffer_.size = size;
num_entries_ = size / sizeof(CommandBufferEntry);
return true;
}
Buffer CommandBufferService::GetRingBuffer() {
Buffer buffer;
if (ring_buffer_.get()) {
buffer.ptr = ring_buffer_->memory();
buffer.size = ring_buffer_->created_size();
buffer.shared_memory = ring_buffer_.get();
}
return buffer;
return ring_buffer_;
}
CommandBufferService::State CommandBufferService::GetState() {

@ -26,6 +26,7 @@ class CommandBufferService : public CommandBuffer {
// CommandBuffer implementation:
virtual bool Initialize(int32 size);
virtual bool Initialize(base::SharedMemory* buffer, int32 size);
virtual Buffer GetRingBuffer();
virtual State GetState();
virtual void Flush(int32 put_offset);
@ -52,7 +53,7 @@ class CommandBufferService : public CommandBuffer {
virtual void SetPutOffsetChangeCallback(Callback0::Type* callback);
private:
scoped_ptr< base::SharedMemory> ring_buffer_;
Buffer ring_buffer_;
int32 num_entries_;
int32 get_offset_;
int32 put_offset_;

@ -27,6 +27,11 @@ bool CommandBufferPepper::Initialize(int32 size) {
return false;
}
bool CommandBufferPepper::Initialize(base::SharedMemory* buffer, int32 size) {
GPU_NOTREACHED();
return false;
}
Buffer CommandBufferPepper::GetRingBuffer() {
Buffer buffer;
#if defined(ENABLE_NEW_NPDEVICE_API)

@ -15,6 +15,10 @@
#include "third_party/npapi/bindings/nphostapi.h"
#endif // __native_client__
namespace {
class SharedMemory;
}
// A CommandBuffer proxy implementation that uses the Pepper API to access
// the command buffer.
@ -27,6 +31,7 @@ class CommandBufferPepper : public gpu::CommandBuffer {
// CommandBuffer implementation.
virtual bool Initialize(int32 size);
virtual bool Initialize(base::SharedMemory* buffer, int32 size);
virtual gpu::Buffer GetRingBuffer();
virtual State GetState();
virtual void Flush(int32 put_offset);

@ -177,6 +177,7 @@ class PepperCommandBuffer : public gpu::CommandBuffer {
// CommandBuffer implementation:
virtual bool Initialize(int32 size);
virtual bool Initialize(base::SharedMemory* buffer, int32 size);
virtual gpu::Buffer GetRingBuffer();
virtual State GetState();
virtual void Flush(int32 put_offset);
@ -248,6 +249,12 @@ bool PepperCommandBuffer::Initialize(int32 size) {
return false;
}
bool PepperCommandBuffer::Initialize(base::SharedMemory* buffer, int32 size) {
// Not implemented in proxy.
NOTREACHED();
return false;
}
gpu::Buffer PepperCommandBuffer::GetRingBuffer() {
// Return locally cached ring buffer.
gpu::Buffer buffer;