summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/service
diff options
context:
space:
mode:
authorgman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-26 08:05:03 +0000
committergman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-26 08:05:03 +0000
commitc87015f53fc61bea2b94f2dc80d0c699d9aebcd5 (patch)
treee34dd0f6151caef21cf51ea39c67af080127fccc /gpu/command_buffer/service
parent12e54045b113f6aca10188b012b1ea529cec6bf2 (diff)
downloadchromium_src-c87015f53fc61bea2b94f2dc80d0c699d9aebcd5.zip
chromium_src-c87015f53fc61bea2b94f2dc80d0c699d9aebcd5.tar.gz
chromium_src-c87015f53fc61bea2b94f2dc80d0c699d9aebcd5.tar.bz2
Separate management of shared memory from CmdBufService to separate class
This is a step on the way to making this per ContextGroup instead of per context TEST=unit tests BUG=129803 R=apatrick@chromium.org Review URL: https://chromiumcodereview.appspot.com/10448030 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@139196 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu/command_buffer/service')
-rw-r--r--gpu/command_buffer/service/command_buffer_service.cc122
-rw-r--r--gpu/command_buffer/service/command_buffer_service.h9
-rw-r--r--gpu/command_buffer/service/command_buffer_service_unittest.cc170
-rw-r--r--gpu/command_buffer/service/transfer_buffer_manager.cc153
-rw-r--r--gpu/command_buffer/service/transfer_buffer_manager.h58
-rw-r--r--gpu/command_buffer/service/transfer_buffer_manager_unittest.cc93
6 files changed, 374 insertions, 231 deletions
diff --git a/gpu/command_buffer/service/command_buffer_service.cc b/gpu/command_buffer/service/command_buffer_service.cc
index 13d3098..402cb7c 100644
--- a/gpu/command_buffer/service/command_buffer_service.cc
+++ b/gpu/command_buffer/service/command_buffer_service.cc
@@ -10,6 +10,7 @@
#include "base/debug/trace_event.h"
#include "gpu/command_buffer/common/cmd_buffer_common.h"
#include "gpu/command_buffer/common/command_buffer_shared.h"
+#include "gpu/command_buffer/service/transfer_buffer_manager.h"
using ::base::SharedMemory;
@@ -24,24 +25,16 @@ CommandBufferService::CommandBufferService()
token_(0),
generation_(0),
error_(error::kNoError),
- context_lost_reason_(error::kUnknown),
- shared_memory_bytes_allocated_(0) {
- // Element zero is always NULL.
- registered_objects_.push_back(Buffer());
+ context_lost_reason_(error::kUnknown) {
}
CommandBufferService::~CommandBufferService() {
- for (size_t i = 0; i < registered_objects_.size(); ++i) {
- if (registered_objects_[i].shared_memory) {
- shared_memory_bytes_allocated_ -= registered_objects_[i].size;
- delete registered_objects_[i].shared_memory;
- }
- }
- // TODO(gman): Should we report 0 bytes to TRACE here?
}
bool CommandBufferService::Initialize() {
- return true;
+ TransferBufferManager* manager = new TransferBufferManager();
+ transfer_buffer_manager_.reset(manager);
+ return manager->Initialize();
}
CommandBufferService::State CommandBufferService::GetState() {
@@ -125,96 +118,17 @@ void CommandBufferService::SetGetOffset(int32 get_offset) {
int32 CommandBufferService::CreateTransferBuffer(size_t size,
int32 id_request) {
- SharedMemory buffer;
- if (!buffer.CreateAnonymous(size))
- return -1;
-
- shared_memory_bytes_allocated_ += size;
- TRACE_COUNTER_ID1(
- "CommandBuffer", "SharedMemory", this, shared_memory_bytes_allocated_);
-
- return RegisterTransferBuffer(&buffer, size, id_request);
+ return transfer_buffer_manager_->CreateTransferBuffer(size, id_request);
}
int32 CommandBufferService::RegisterTransferBuffer(
base::SharedMemory* shared_memory, size_t size, int32 id_request) {
- // Check we haven't exceeded the range that fits in a 32-bit integer.
- if (unused_registered_object_elements_.empty()) {
- if (registered_objects_.size() > std::numeric_limits<uint32>::max())
- return -1;
- }
-
- // Check that the requested ID is sane (not too large, or less than -1)
- if (id_request != -1 && (id_request > 100 || id_request < -1))
- return -1;
-
- // Duplicate the handle.
- base::SharedMemoryHandle duped_shared_memory_handle;
- if (!shared_memory->ShareToProcess(base::GetCurrentProcessHandle(),
- &duped_shared_memory_handle)) {
- return -1;
- }
- scoped_ptr<SharedMemory> duped_shared_memory(
- new SharedMemory(duped_shared_memory_handle, false));
-
- // Map the shared memory into this process. This validates the size.
- if (!duped_shared_memory->Map(size))
- return -1;
-
- // If it could be mapped, allocate an ID and register the shared memory with
- // that ID.
- Buffer buffer;
- buffer.ptr = duped_shared_memory->memory();
- buffer.size = size;
- buffer.shared_memory = duped_shared_memory.release();
-
- // If caller requested specific id, first try to use id_request.
- if (id_request != -1) {
- int32 cur_size = static_cast<int32>(registered_objects_.size());
- if (cur_size <= id_request) {
- // Pad registered_objects_ to reach id_request.
- registered_objects_.resize(static_cast<size_t>(id_request + 1));
- for (int32 id = cur_size; id < id_request; ++id)
- unused_registered_object_elements_.insert(id);
- registered_objects_[id_request] = buffer;
- return id_request;
- } else if (!registered_objects_[id_request].shared_memory) {
- // id_request is already in free list.
- registered_objects_[id_request] = buffer;
- unused_registered_object_elements_.erase(id_request);
- return id_request;
- }
- }
-
- if (unused_registered_object_elements_.empty()) {
- int32 handle = static_cast<int32>(registered_objects_.size());
- registered_objects_.push_back(buffer);
- return handle;
- } else {
- int32 handle = *unused_registered_object_elements_.begin();
- unused_registered_object_elements_.erase(
- unused_registered_object_elements_.begin());
- DCHECK(!registered_objects_[handle].shared_memory);
- registered_objects_[handle] = buffer;
- return handle;
- }
+ return transfer_buffer_manager_->RegisterTransferBuffer(
+ shared_memory, size, id_request);
}
void CommandBufferService::DestroyTransferBuffer(int32 handle) {
- if (handle <= 0)
- return;
-
- if (static_cast<size_t>(handle) >= registered_objects_.size())
- return;
-
- shared_memory_bytes_allocated_ -= registered_objects_[handle].size;
- TRACE_COUNTER_ID1(
- "CommandBuffer", "SharedMemory", this, shared_memory_bytes_allocated_);
-
- delete registered_objects_[handle].shared_memory;
- registered_objects_[handle] = Buffer();
- unused_registered_object_elements_.insert(handle);
-
+ transfer_buffer_manager_->DestroyTransferBuffer(handle);
if (handle == ring_buffer_id_) {
ring_buffer_id_ = -1;
ring_buffer_ = Buffer();
@@ -222,26 +136,10 @@ void CommandBufferService::DestroyTransferBuffer(int32 handle) {
get_offset_ = 0;
put_offset_ = 0;
}
-
- // Remove all null objects from the end of the vector. This allows the vector
- // to shrink when, for example, all objects are unregistered. Note that this
- // loop never removes element zero, which is always NULL.
- while (registered_objects_.size() > 1 &&
- !registered_objects_.back().shared_memory) {
- registered_objects_.pop_back();
- unused_registered_object_elements_.erase(
- static_cast<int32>(registered_objects_.size()));
- }
}
Buffer CommandBufferService::GetTransferBuffer(int32 handle) {
- if (handle < 0)
- return Buffer();
-
- if (static_cast<size_t>(handle) >= registered_objects_.size())
- return Buffer();
-
- return registered_objects_[handle];
+ return transfer_buffer_manager_->GetTransferBuffer(handle);
}
void CommandBufferService::SetToken(int32 token) {
diff --git a/gpu/command_buffer/service/command_buffer_service.h b/gpu/command_buffer/service/command_buffer_service.h
index 8b3e038..77fe951 100644
--- a/gpu/command_buffer/service/command_buffer_service.h
+++ b/gpu/command_buffer/service/command_buffer_service.h
@@ -5,9 +5,6 @@
#ifndef GPU_COMMAND_BUFFER_SERVICE_COMMAND_BUFFER_SERVICE_H_
#define GPU_COMMAND_BUFFER_SERVICE_COMMAND_BUFFER_SERVICE_H_
-#include <set>
-#include <vector>
-
#include "base/callback.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
@@ -17,6 +14,8 @@
namespace gpu {
+class TransferBufferManagerInterface;
+
// An object that implements a shared memory command buffer and a synchronous
// API to manage the put and get pointers.
class GPU_EXPORT CommandBufferService : public CommandBuffer {
@@ -73,13 +72,11 @@ class GPU_EXPORT CommandBufferService : public CommandBuffer {
base::Closure put_offset_change_callback_;
GetBufferChangedCallback get_buffer_change_callback_;
base::Closure parse_error_callback_;
- std::vector<Buffer> registered_objects_;
- std::set<int32> unused_registered_object_elements_;
+ scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
int32 token_;
uint32 generation_;
error::Error error_;
error::ContextLostReason context_lost_reason_;
- size_t shared_memory_bytes_allocated_;
};
} // namespace gpu
diff --git a/gpu/command_buffer/service/command_buffer_service_unittest.cc b/gpu/command_buffer/service/command_buffer_service_unittest.cc
index 7f06639..a771018 100644
--- a/gpu/command_buffer/service/command_buffer_service_unittest.cc
+++ b/gpu/command_buffer/service/command_buffer_service_unittest.cc
@@ -1,9 +1,9 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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 "base/callback.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/threading/thread.h"
#include "gpu/command_buffer/common/cmd_buffer_common.h"
#include "gpu/command_buffer/service/command_buffer_service.h"
@@ -23,6 +23,7 @@ class CommandBufferServiceTest : public testing::Test {
protected:
virtual void SetUp() {
command_buffer_.reset(new CommandBufferService);
+ EXPECT_TRUE(command_buffer_->Initialize());
}
int32 GetGetOffset() {
@@ -41,145 +42,88 @@ class CommandBufferServiceTest : public testing::Test {
return command_buffer_->GetState().error;
}
- base::mac::ScopedNSAutoreleasePool autorelease_pool_;
+ bool Initialize(size_t size) {
+ int32 id = command_buffer_->CreateTransferBuffer(size, -1);
+ EXPECT_GT(id, 0);
+ command_buffer_->SetGetBuffer(id);
+ return true;
+ }
+
scoped_ptr<CommandBufferService> command_buffer_;
};
-TEST_F(CommandBufferServiceTest, NullRingBufferByDefault) {
- EXPECT_TRUE(NULL == command_buffer_->GetRingBuffer().ptr);
-}
-
TEST_F(CommandBufferServiceTest, InitializesCommandBuffer) {
- EXPECT_TRUE(command_buffer_->Initialize(1024));
- EXPECT_TRUE(NULL != command_buffer_->GetRingBuffer().ptr);
+ EXPECT_TRUE(Initialize(1024));
CommandBuffer::State state = command_buffer_->GetState();
- EXPECT_EQ(1024, state.size);
EXPECT_EQ(0, state.get_offset);
EXPECT_EQ(0, state.put_offset);
EXPECT_EQ(0, state.token);
EXPECT_EQ(error::kNoError, state.error);
}
-TEST_F(CommandBufferServiceTest, InitializationSizeIsInEntriesNotBytes) {
- EXPECT_TRUE(command_buffer_->Initialize(1024));
- EXPECT_TRUE(NULL != command_buffer_->GetRingBuffer().ptr);
- EXPECT_GE(1024 * sizeof(CommandBufferEntry),
- command_buffer_->GetRingBuffer().size);
-}
-
-TEST_F(CommandBufferServiceTest, InitializationFailsIfSizeIsZero) {
- EXPECT_FALSE(command_buffer_->Initialize(0));
-}
-
-TEST_F(CommandBufferServiceTest, InitializationFailsIfSizeOutOfRange) {
- EXPECT_FALSE(command_buffer_->Initialize(
- CommandBuffer::kMaxCommandBufferSize + 1));
-}
+namespace {
-TEST_F(CommandBufferServiceTest, InitializationFailsIfSizeIsNegative) {
- EXPECT_FALSE(command_buffer_->Initialize(-1));
-}
-
-TEST_F(CommandBufferServiceTest, InitializeFailsSecondTime) {
- EXPECT_TRUE(command_buffer_->Initialize(1024));
- EXPECT_FALSE(command_buffer_->Initialize(1024));
-}
-
-class MockCallback : public CallbackRunner<Tuple0> {
+class CallbackTest {
public:
- MOCK_METHOD1(RunWithParams, void(const Tuple0&));
+ virtual void PutOffsetChanged() = 0;
+ virtual bool GetBufferChanged(int32 id) = 0;
};
-TEST_F(CommandBufferServiceTest, CanSyncGetAndPutOffset) {
- command_buffer_->Initialize(1024);
+class MockCallbackTest : public CallbackTest {
+ public:
+ MOCK_METHOD0(PutOffsetChanged, void());
+ MOCK_METHOD1(GetBufferChanged, bool(int32));
+};
- StrictMock<MockCallback>* put_offset_change_callback =
- new StrictMock<MockCallback>;
- command_buffer_->SetPutOffsetChangeCallback(put_offset_change_callback);
+} // anonymous namespace
- EXPECT_CALL(*put_offset_change_callback, RunWithParams(_));
- EXPECT_EQ(0, command_buffer_->Flush(2).get_offset);
+TEST_F(CommandBufferServiceTest, CanSyncGetAndPutOffset) {
+ Initialize(1024);
+
+ scoped_ptr<StrictMock<MockCallbackTest> > change_callback(
+ new StrictMock<MockCallbackTest>);
+ command_buffer_->SetPutOffsetChangeCallback(
+ base::Bind(
+ &CallbackTest::PutOffsetChanged,
+ base::Unretained(change_callback.get())));
+
+ EXPECT_CALL(*change_callback, PutOffsetChanged());
+ command_buffer_->Flush(2);
+ EXPECT_EQ(0, GetGetOffset());
EXPECT_EQ(2, GetPutOffset());
- EXPECT_CALL(*put_offset_change_callback, RunWithParams(_));
- EXPECT_EQ(0, command_buffer_->Flush(4).get_offset);
+ EXPECT_CALL(*change_callback, PutOffsetChanged());
+ command_buffer_->Flush(4);
+ EXPECT_EQ(0, GetGetOffset());
EXPECT_EQ(4, GetPutOffset());
command_buffer_->SetGetOffset(2);
EXPECT_EQ(2, GetGetOffset());
- EXPECT_CALL(*put_offset_change_callback, RunWithParams(_));
- EXPECT_EQ(2, command_buffer_->Flush(6).get_offset);
+ EXPECT_CALL(*change_callback, PutOffsetChanged());
+ command_buffer_->Flush(6);
- EXPECT_NE(error::kNoError, command_buffer_->Flush(-1).error);
- EXPECT_NE(error::kNoError,
- command_buffer_->Flush(1024).error);
+ command_buffer_->Flush(-1);
+ EXPECT_NE(error::kNoError, GetError());
+ command_buffer_->Flush(1024);
+ EXPECT_NE(error::kNoError, GetError());
}
-TEST_F(CommandBufferServiceTest, ZeroHandleMapsToNull) {
- EXPECT_TRUE(NULL == command_buffer_->GetTransferBuffer(0).ptr);
-}
+TEST_F(CommandBufferServiceTest, SetGetBuffer) {
+ int32 ring_buffer_id = command_buffer_->CreateTransferBuffer(1024, -1);
+ EXPECT_GT(ring_buffer_id, 0);
-TEST_F(CommandBufferServiceTest, NegativeHandleMapsToNull) {
- EXPECT_TRUE(NULL == command_buffer_->GetTransferBuffer(-1).ptr);
-}
+ scoped_ptr<StrictMock<MockCallbackTest> > change_callback(
+ new StrictMock<MockCallbackTest>);
+ command_buffer_->SetGetBufferChangeCallback(
+ base::Bind(
+ &CallbackTest::GetBufferChanged,
+ base::Unretained(change_callback.get())));
-TEST_F(CommandBufferServiceTest, OutOfRangeHandleMapsToNull) {
- EXPECT_TRUE(NULL == command_buffer_->GetTransferBuffer(1).ptr);
-}
-
-TEST_F(CommandBufferServiceTest, CanCreateTransferBuffers) {
- int32 handle = command_buffer_->CreateTransferBuffer(1024);
- EXPECT_EQ(1, handle);
- Buffer buffer = command_buffer_->GetTransferBuffer(handle);
- ASSERT_TRUE(NULL != buffer.ptr);
- EXPECT_EQ(1024u, buffer.size);
-}
-
-TEST_F(CommandBufferServiceTest, CreateTransferBufferReturnsDistinctHandles) {
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
-}
-
-TEST_F(CommandBufferServiceTest,
- CreateTransferBufferReusesUnregisteredHandles) {
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
- EXPECT_EQ(2, command_buffer_->CreateTransferBuffer(1024));
- command_buffer_->DestroyTransferBuffer(1);
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
- EXPECT_EQ(3, command_buffer_->CreateTransferBuffer(1024));
-}
-
-TEST_F(CommandBufferServiceTest, CannotUnregisterHandleZero) {
- command_buffer_->DestroyTransferBuffer(0);
- EXPECT_TRUE(NULL == command_buffer_->GetTransferBuffer(0).ptr);
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
-}
-
-TEST_F(CommandBufferServiceTest, CannotUnregisterNegativeHandles) {
- command_buffer_->DestroyTransferBuffer(-1);
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
-}
-
-TEST_F(CommandBufferServiceTest, CannotUnregisterUnregisteredHandles) {
- command_buffer_->DestroyTransferBuffer(1);
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
-}
-
-// Testing this case specifically because there is an optimization that takes
-// a different code path in this case.
-TEST_F(CommandBufferServiceTest, UnregistersLastRegisteredHandle) {
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
- command_buffer_->DestroyTransferBuffer(1);
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
-}
+ EXPECT_CALL(*change_callback, GetBufferChanged(ring_buffer_id))
+ .WillOnce(Return(true));
-// Testing this case specifically because there is an optimization that takes
-// a different code path in this case.
-TEST_F(CommandBufferServiceTest, UnregistersTwoLastRegisteredHandles) {
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
- EXPECT_EQ(2, command_buffer_->CreateTransferBuffer(1024));
- command_buffer_->DestroyTransferBuffer(2);
- command_buffer_->DestroyTransferBuffer(1);
- EXPECT_EQ(1, command_buffer_->CreateTransferBuffer(1024));
+ command_buffer_->SetGetBuffer(ring_buffer_id);
+ EXPECT_EQ(0, GetGetOffset());
}
TEST_F(CommandBufferServiceTest, DefaultTokenIsZero) {
diff --git a/gpu/command_buffer/service/transfer_buffer_manager.cc b/gpu/command_buffer/service/transfer_buffer_manager.cc
new file mode 100644
index 0000000..f55dfbd
--- /dev/null
+++ b/gpu/command_buffer/service/transfer_buffer_manager.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 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 "gpu/command_buffer/service/transfer_buffer_manager.h"
+
+#include <limits>
+
+#include "base/process_util.h"
+#include "base/debug/trace_event.h"
+
+using ::base::SharedMemory;
+
+namespace gpu {
+
+TransferBufferManagerInterface::~TransferBufferManagerInterface() {
+}
+
+TransferBufferManager::TransferBufferManager()
+ : shared_memory_bytes_allocated_(0) {
+ // Element zero is always NULL.
+ registered_objects_.push_back(Buffer());
+}
+
+TransferBufferManager::~TransferBufferManager() {
+ for (size_t i = 0; i < registered_objects_.size(); ++i) {
+ if (registered_objects_[i].shared_memory) {
+ shared_memory_bytes_allocated_ -= registered_objects_[i].size;
+ delete registered_objects_[i].shared_memory;
+ }
+ }
+ // TODO(gman): Should we report 0 bytes to TRACE here?
+}
+
+bool TransferBufferManager::Initialize() {
+ return true;
+}
+
+int32 TransferBufferManager::CreateTransferBuffer(
+ size_t size, int32 id_request) {
+ SharedMemory buffer;
+ if (!buffer.CreateAnonymous(size))
+ return -1;
+
+ shared_memory_bytes_allocated_ += size;
+ TRACE_COUNTER_ID1(
+ "CommandBuffer", "SharedMemory", this, shared_memory_bytes_allocated_);
+
+ return RegisterTransferBuffer(&buffer, size, id_request);
+}
+
+int32 TransferBufferManager::RegisterTransferBuffer(
+ base::SharedMemory* shared_memory, size_t size, int32 id_request) {
+ // Check we haven't exceeded the range that fits in a 32-bit integer.
+ if (unused_registered_object_elements_.empty()) {
+ if (registered_objects_.size() > std::numeric_limits<uint32>::max())
+ return -1;
+ }
+
+ // Check that the requested ID is sane (not too large, or less than -1)
+ if (id_request != -1 && (id_request > 100 || id_request < -1))
+ return -1;
+
+ // Duplicate the handle.
+ base::SharedMemoryHandle duped_shared_memory_handle;
+ if (!shared_memory->ShareToProcess(base::GetCurrentProcessHandle(),
+ &duped_shared_memory_handle)) {
+ return -1;
+ }
+ scoped_ptr<SharedMemory> duped_shared_memory(
+ new SharedMemory(duped_shared_memory_handle, false));
+
+ // Map the shared memory into this process. This validates the size.
+ if (!duped_shared_memory->Map(size))
+ return -1;
+
+ // If it could be mapped, allocate an ID and register the shared memory with
+ // that ID.
+ Buffer buffer;
+ buffer.ptr = duped_shared_memory->memory();
+ buffer.size = size;
+ buffer.shared_memory = duped_shared_memory.release();
+
+ // If caller requested specific id, first try to use id_request.
+ if (id_request != -1) {
+ int32 cur_size = static_cast<int32>(registered_objects_.size());
+ if (cur_size <= id_request) {
+ // Pad registered_objects_ to reach id_request.
+ registered_objects_.resize(static_cast<size_t>(id_request + 1));
+ for (int32 id = cur_size; id < id_request; ++id)
+ unused_registered_object_elements_.insert(id);
+ registered_objects_[id_request] = buffer;
+ return id_request;
+ } else if (!registered_objects_[id_request].shared_memory) {
+ // id_request is already in free list.
+ registered_objects_[id_request] = buffer;
+ unused_registered_object_elements_.erase(id_request);
+ return id_request;
+ }
+ }
+
+ if (unused_registered_object_elements_.empty()) {
+ int32 handle = static_cast<int32>(registered_objects_.size());
+ registered_objects_.push_back(buffer);
+ return handle;
+ } else {
+ int32 handle = *unused_registered_object_elements_.begin();
+ unused_registered_object_elements_.erase(
+ unused_registered_object_elements_.begin());
+ DCHECK(!registered_objects_[handle].shared_memory);
+ registered_objects_[handle] = buffer;
+ return handle;
+ }
+}
+
+void TransferBufferManager::DestroyTransferBuffer(int32 handle) {
+ if (handle <= 0)
+ return;
+
+ if (static_cast<size_t>(handle) >= registered_objects_.size())
+ return;
+
+ shared_memory_bytes_allocated_ -= registered_objects_[handle].size;
+ TRACE_COUNTER_ID1(
+ "CommandBuffer", "SharedMemory", this, shared_memory_bytes_allocated_);
+
+ delete registered_objects_[handle].shared_memory;
+ registered_objects_[handle] = Buffer();
+ unused_registered_object_elements_.insert(handle);
+
+ // Remove all null objects from the end of the vector. This allows the vector
+ // to shrink when, for example, all objects are unregistered. Note that this
+ // loop never removes element zero, which is always NULL.
+ while (registered_objects_.size() > 1 &&
+ !registered_objects_.back().shared_memory) {
+ registered_objects_.pop_back();
+ unused_registered_object_elements_.erase(
+ static_cast<int32>(registered_objects_.size()));
+ }
+}
+
+Buffer TransferBufferManager::GetTransferBuffer(int32 handle) {
+ if (handle < 0)
+ return Buffer();
+
+ if (static_cast<size_t>(handle) >= registered_objects_.size())
+ return Buffer();
+
+ return registered_objects_[handle];
+}
+
+} // namespace gpu
+
diff --git a/gpu/command_buffer/service/transfer_buffer_manager.h b/gpu/command_buffer/service/transfer_buffer_manager.h
new file mode 100644
index 0000000..9f24e2c
--- /dev/null
+++ b/gpu/command_buffer/service/transfer_buffer_manager.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 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.
+
+#ifndef GPU_COMMAND_BUFFER_SERVICE_TRANSFER_BUFFER_MANAGER_H_
+#define GPU_COMMAND_BUFFER_SERVICE_TRANSFER_BUFFER_MANAGER_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/shared_memory.h"
+#include "gpu/command_buffer/common/command_buffer_shared.h"
+
+namespace gpu {
+
+class GPU_EXPORT TransferBufferManagerInterface {
+ public:
+ virtual ~TransferBufferManagerInterface();
+
+ virtual int32 CreateTransferBuffer(size_t size, int32 id_request) = 0;
+ virtual int32 RegisterTransferBuffer(
+ base::SharedMemory* shared_memory,
+ size_t size,
+ int32 id_request) = 0;
+ virtual void DestroyTransferBuffer(int32 id) = 0;
+ virtual Buffer GetTransferBuffer(int32 handle) = 0;
+
+};
+
+class GPU_EXPORT TransferBufferManager
+ : public TransferBufferManagerInterface {
+ public:
+ TransferBufferManager();
+
+ bool Initialize();
+ virtual int32 CreateTransferBuffer(size_t size, int32 id_request) OVERRIDE;
+ virtual int32 RegisterTransferBuffer(
+ base::SharedMemory* shared_memory,
+ size_t size,
+ int32 id_request) OVERRIDE;
+ virtual void DestroyTransferBuffer(int32 id) OVERRIDE;
+ virtual Buffer GetTransferBuffer(int32 handle) OVERRIDE;
+
+ private:
+ virtual ~TransferBufferManager();
+
+ std::set<int32> unused_registered_object_elements_;
+ std::vector<Buffer> registered_objects_;
+ size_t shared_memory_bytes_allocated_;
+
+ DISALLOW_COPY_AND_ASSIGN(TransferBufferManager);
+};
+
+} // namespace gpu
+
+#endif // GPU_COMMAND_BUFFER_SERVICE_TRANSFER_BUFFER_MANAGER_H_
diff --git a/gpu/command_buffer/service/transfer_buffer_manager_unittest.cc b/gpu/command_buffer/service/transfer_buffer_manager_unittest.cc
new file mode 100644
index 0000000..029cb0e
--- /dev/null
+++ b/gpu/command_buffer/service/transfer_buffer_manager_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 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 "gpu/command_buffer/service/transfer_buffer_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::SharedMemory;
+
+namespace gpu {
+
+class TransferBufferManagerTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ TransferBufferManager* manager = new TransferBufferManager();
+ transfer_buffer_manager_.reset(manager);
+ manager->Initialize();
+ }
+
+ scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
+};
+
+TEST_F(TransferBufferManagerTest, ZeroHandleMapsToNull) {
+ EXPECT_TRUE(NULL == transfer_buffer_manager_->GetTransferBuffer(0).ptr);
+}
+
+TEST_F(TransferBufferManagerTest, NegativeHandleMapsToNull) {
+ EXPECT_TRUE(NULL == transfer_buffer_manager_->GetTransferBuffer(-1).ptr);
+}
+
+TEST_F(TransferBufferManagerTest, OutOfRangeHandleMapsToNull) {
+ EXPECT_TRUE(NULL == transfer_buffer_manager_->GetTransferBuffer(1).ptr);
+}
+
+TEST_F(TransferBufferManagerTest, CanCreateTransferBuffers) {
+ int32 handle = transfer_buffer_manager_->CreateTransferBuffer(1024, -1);
+ EXPECT_EQ(1, handle);
+ Buffer buffer = transfer_buffer_manager_->GetTransferBuffer(handle);
+ ASSERT_TRUE(NULL != buffer.ptr);
+ EXPECT_EQ(1024u, buffer.size);
+}
+
+TEST_F(TransferBufferManagerTest, CreateTransferBufferReturnsDistinctHandles) {
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+}
+
+TEST_F(TransferBufferManagerTest,
+ CreateTransferBufferReusesUnregisteredHandles) {
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+ EXPECT_EQ(2, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+ transfer_buffer_manager_->DestroyTransferBuffer(1);
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+ EXPECT_EQ(3, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+}
+
+TEST_F(TransferBufferManagerTest, CannotUnregisterHandleZero) {
+ transfer_buffer_manager_->DestroyTransferBuffer(0);
+ EXPECT_TRUE(NULL == transfer_buffer_manager_->GetTransferBuffer(0).ptr);
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+}
+
+TEST_F(TransferBufferManagerTest, CannotUnregisterNegativeHandles) {
+ transfer_buffer_manager_->DestroyTransferBuffer(-1);
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+}
+
+TEST_F(TransferBufferManagerTest, CannotUnregisterUnregisteredHandles) {
+ transfer_buffer_manager_->DestroyTransferBuffer(1);
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+}
+
+// Testing this case specifically because there is an optimization that takes
+// a different code path in this case.
+TEST_F(TransferBufferManagerTest, UnregistersLastRegisteredHandle) {
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+ transfer_buffer_manager_->DestroyTransferBuffer(1);
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+}
+
+// Testing this case specifically because there is an optimization that takes
+// a different code path in this case.
+TEST_F(TransferBufferManagerTest, UnregistersTwoLastRegisteredHandles) {
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+ EXPECT_EQ(2, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+ transfer_buffer_manager_->DestroyTransferBuffer(2);
+ transfer_buffer_manager_->DestroyTransferBuffer(1);
+ EXPECT_EQ(1, transfer_buffer_manager_->CreateTransferBuffer(1024, -1));
+}
+
+} // namespace gpu