summaryrefslogtreecommitdiffstats
path: root/gpu
diff options
context:
space:
mode:
authorgman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-10 09:30:28 +0000
committergman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-10 09:30:28 +0000
commitc0ce9d8162a70abb545e3ca7376003a492b08490 (patch)
treee15daf5c75aefb839ea98d2fd8f9cefe69ec44ad /gpu
parent06489a012706979c4a82bc2f3ac502b82fca2346 (diff)
downloadchromium_src-c0ce9d8162a70abb545e3ca7376003a492b08490.zip
chromium_src-c0ce9d8162a70abb545e3ca7376003a492b08490.tar.gz
chromium_src-c0ce9d8162a70abb545e3ca7376003a492b08490.tar.bz2
Implements bucket commands and adds unit tests to
common_decoder TEST=none BUG=none Review URL: http://codereview.chromium.org/479008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34242 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'gpu')
-rw-r--r--gpu/command_buffer/common/cmd_buffer_common.h88
-rw-r--r--gpu/command_buffer/service/common_decoder.cc150
-rw-r--r--gpu/command_buffer/service/common_decoder.h77
-rw-r--r--gpu/command_buffer/service/common_decoder_unittest.cc418
-rw-r--r--gpu/command_buffer/service/gles2_cmd_decoder.cc16
-rw-r--r--gpu/gpu.gyp1
6 files changed, 674 insertions, 76 deletions
diff --git a/gpu/command_buffer/common/cmd_buffer_common.h b/gpu/command_buffer/common/cmd_buffer_common.h
index f33f4ef..1d0c4cb 100644
--- a/gpu/command_buffer/common/cmd_buffer_common.h
+++ b/gpu/command_buffer/common/cmd_buffer_common.h
@@ -107,9 +107,10 @@ union CommandBufferEntry {
float value_float;
};
-COMPILE_ASSERT(sizeof(CommandBufferEntry) == 4,
- Sizeof_CommandBufferEntry_is_not_4);
+const size_t kCommandBufferEntrySize = 4;
+COMPILE_ASSERT(sizeof(CommandBufferEntry) == kCommandBufferEntrySize,
+ Sizeof_CommandBufferEntry_is_not_4);
// Make sure the compiler does not add extra padding to any of the command
// structures.
@@ -181,8 +182,8 @@ namespace cmd {
OP(SetBucketSize) /* 7 */ \
OP(SetBucketData) /* 8 */ \
OP(SetBucketDataImmediate) /* 9 */ \
- OP(GetResultSize) /* 10 */ \
- OP(GetResultData) /* 11 */ \
+ OP(GetBucketSize) /* 10 */ \
+ OP(GetBucketData) /* 11 */ \
// Common commands.
enum CommandId {
@@ -566,76 +567,86 @@ COMPILE_ASSERT(offsetof(SetBucketDataImmediate, offset) == 8,
COMPILE_ASSERT(offsetof(SetBucketDataImmediate, size) == 12,
Offsetof_SetBucketDataImmediate_size_not_12);
-// Gets the size of a result the service has available.
-// Sending a variable size result back to the client, for example any API that
-// returns a string, is problematic since the largest thing you can send back is
-// the size of your shared memory. This command along with GetResultData
-// implement a way to get a result a piece at a time to help solve that problem
-// in a generic way.
-struct GetResultSize {
- typedef GetResultSize ValueType;
- static const CommandId kCmdId = kGetResultSize;
+// Gets the size of a bucket the service has available. Sending a variable size
+// result back to the client, for example any API that returns a string, is
+// problematic since the largest thing you can send back is the size of your
+// shared memory. This command along with GetBucketData implements a way to get
+// a result a piece at a time to help solve that problem in a generic way.
+struct GetBucketSize {
+ typedef GetBucketSize ValueType;
+ static const CommandId kCmdId = kGetBucketSize;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
- void Init(uint32 _shared_memory_id,
+ void Init(uint32 _bucket_id,
+ uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
SetHeader();
+ bucket_id = _bucket_id;
shared_memory_id = _shared_memory_id;
shared_memory_offset = _shared_memory_offset;
}
static void* Set(void* cmd,
+ uint32 _bucket_id,
uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
static_cast<ValueType*>(cmd)->Init(
+ _bucket_id,
_shared_memory_id,
_shared_memory_offset);
return NextCmdAddress<ValueType>(cmd);
}
CommandHeader header;
+ uint32 bucket_id;
uint32 shared_memory_id;
uint32 shared_memory_offset;
};
-COMPILE_ASSERT(sizeof(GetResultSize) == 12, Sizeof_GetResultSize_is_not_12);
-COMPILE_ASSERT(offsetof(GetResultSize, header) == 0,
- Offsetof_GetResultSize_header_not_0);
-COMPILE_ASSERT(offsetof(GetResultSize, shared_memory_id) == 4,
- Offsetof_GetResultSize_shared_memory_id_not_4);
-COMPILE_ASSERT(offsetof(GetResultSize, shared_memory_offset) == 8,
- Offsetof_GetResultSize_shared_memory_offset_not_8);
+COMPILE_ASSERT(sizeof(GetBucketSize) == 16, Sizeof_GetBucketSize_is_not_16);
+COMPILE_ASSERT(offsetof(GetBucketSize, header) == 0,
+ Offsetof_GetBucketSize_header_not_0);
+COMPILE_ASSERT(offsetof(GetBucketSize, bucket_id) == 4,
+ Offsetof_GetBucketSize_bucket_id_not_4);
+COMPILE_ASSERT(offsetof(GetBucketSize, shared_memory_id) == 8,
+ Offsetof_GetBucketSize_shared_memory_id_not_8);
+COMPILE_ASSERT(offsetof(GetBucketSize, shared_memory_offset) == 12,
+ Offsetof_GetBucketSize_shared_memory_offset_not_12);
// Gets a piece of a result the service as available.
-// See GetResultSize.
-struct GetResultData {
- typedef GetResultData ValueType;
- static const CommandId kCmdId = kGetResultData;
+// See GetBucketSize.
+struct GetBucketData {
+ typedef GetBucketData ValueType;
+ static const CommandId kCmdId = kGetBucketData;
static const cmd::ArgFlags kArgFlags = cmd::kFixed;
void SetHeader() {
header.SetCmd<ValueType>();
}
- void Init(uint32 _offset,
+ void Init(uint32 _bucket_id,
+ uint32 _offset,
uint32 _size,
uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
SetHeader();
+ bucket_id = _bucket_id;
offset = _offset;
size = _size;
shared_memory_id = _shared_memory_id;
shared_memory_offset = _shared_memory_offset;
}
static void* Set(void* cmd,
+ uint32 _bucket_id,
uint32 _offset,
uint32 _size,
uint32 _shared_memory_id,
uint32 _shared_memory_offset) {
static_cast<ValueType*>(cmd)->Init(
+ _bucket_id,
_offset,
_size,
_shared_memory_id,
@@ -644,23 +655,26 @@ struct GetResultData {
}
CommandHeader header;
+ uint32 bucket_id;
uint32 offset;
uint32 size;
uint32 shared_memory_id;
uint32 shared_memory_offset;
};
-COMPILE_ASSERT(sizeof(GetResultData) == 20, Sizeof_GetResultData_is_not_20);
-COMPILE_ASSERT(offsetof(GetResultData, header) == 0,
- Offsetof_GetResultData_header_not_0);
-COMPILE_ASSERT(offsetof(GetResultData, offset) == 4,
- Offsetof_GetResultData_offset_not_4);
-COMPILE_ASSERT(offsetof(GetResultData, size) == 8,
- Offsetof_GetResultData_size_not_8);
-COMPILE_ASSERT(offsetof(GetResultData, shared_memory_id) == 12,
- Offsetof_GetResultData_shared_memory_id_not_12);
-COMPILE_ASSERT(offsetof(GetResultData, shared_memory_offset) == 16,
- Offsetof_GetResultData_shared_memory_offset_not_16);
+COMPILE_ASSERT(sizeof(GetBucketData) == 24, Sizeof_GetBucketData_is_not_20);
+COMPILE_ASSERT(offsetof(GetBucketData, header) == 0,
+ Offsetof_GetBucketData_header_not_0);
+COMPILE_ASSERT(offsetof(GetBucketData, bucket_id) == 4,
+ Offsetof_GetBucketData_bucket_id_not_4);
+COMPILE_ASSERT(offsetof(GetBucketData, offset) == 8,
+ Offsetof_GetBucketData_offset_not_8);
+COMPILE_ASSERT(offsetof(GetBucketData, size) == 12,
+ Offsetof_GetBucketData_size_not_12);
+COMPILE_ASSERT(offsetof(GetBucketData, shared_memory_id) == 16,
+ Offsetof_GetBucketData_shared_memory_id_not_16);
+COMPILE_ASSERT(offsetof(GetBucketData, shared_memory_offset) == 20,
+ Offsetof_GetBucketData_shared_memory_offset_not_20);
} // namespace cmd
diff --git a/gpu/command_buffer/service/common_decoder.cc b/gpu/command_buffer/service/common_decoder.cc
index 5830cd4..c0d545f 100644
--- a/gpu/command_buffer/service/common_decoder.cc
+++ b/gpu/command_buffer/service/common_decoder.cc
@@ -35,6 +35,30 @@
namespace command_buffer {
+const void* CommonDecoder::Bucket::GetData(size_t offset, size_t size) const {
+ if (OffsetSizeValid(offset, size)) {
+ return data_.get() + offset;
+ }
+ return NULL;
+}
+
+void CommonDecoder::Bucket::SetSize(size_t size) {
+ if (size != size_) {
+ data_.reset(size ? new int8[size] : NULL);
+ size_ = size;
+ memset(data_.get(), 0, size);
+ }
+}
+
+bool CommonDecoder::Bucket::SetData(
+ const void* src, size_t offset, size_t size) {
+ if (OffsetSizeValid(offset, size)) {
+ memcpy(data_.get() + offset, src, size);
+ return true;
+ }
+ return false;
+}
+
void* CommonDecoder::GetAddressAndCheckSize(unsigned int shm_id,
unsigned int offset,
unsigned int size) {
@@ -53,8 +77,25 @@ const char* CommonDecoder::GetCommonCommandName(
return cmd::GetCommandName(command_id);
}
+CommonDecoder::Bucket* CommonDecoder::GetBucket(uint32 bucket_id) const {
+ BucketMap::const_iterator iter(buckets_.find(bucket_id));
+ return iter != buckets_.end() ? &(*iter->second) : NULL;
+}
+
namespace {
+// Returns the address of the first byte after a struct.
+template <typename T>
+const void* AddressAfterStruct(const T& pod) {
+ return reinterpret_cast<const uint8*>(&pod) + sizeof(pod);
+}
+
+// Returns the address of the frst byte after the struct.
+template <typename RETURN_TYPE, typename COMMAND_TYPE>
+RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod) {
+ return static_cast<RETURN_TYPE>(const_cast<void*>(AddressAfterStruct(pod)));
+}
+
// A struct to hold info about each command.
struct CommandInfo {
int arg_flags; // How to handle the arguments for this command
@@ -87,11 +128,13 @@ parse_error::ParseError CommonDecoder::DoCommonCommand(
unsigned int info_arg_count = static_cast<unsigned int>(info.arg_count);
if ((info.arg_flags == cmd::kFixed && arg_count == info_arg_count) ||
(info.arg_flags == cmd::kAtLeastN && arg_count >= info_arg_count)) {
+ uint32 immediate_data_size =
+ (arg_count - info_arg_count) * sizeof(CommandBufferEntry); // NOLINT
switch (command) {
#define COMMON_COMMAND_BUFFER_CMD_OP(name) \
case cmd::name::kCmdId: \
return Handle ## name( \
- arg_count, \
+ immediate_data_size, \
*static_cast<const cmd::name*>(cmd_data)); \
COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP)
@@ -107,85 +150,148 @@ parse_error::ParseError CommonDecoder::DoCommonCommand(
}
parse_error::ParseError CommonDecoder::HandleNoop(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::Noop& args) {
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleSetToken(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::SetToken& args) {
engine_->set_token(args.token);
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleJump(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::Jump& args) {
DCHECK(false); // TODO(gman): Implement.
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleJumpRelative(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::JumpRelative& args) {
DCHECK(false); // TODO(gman): Implement.
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleCall(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::Call& args) {
DCHECK(false); // TODO(gman): Implement.
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleCallRelative(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::CallRelative& args) {
DCHECK(false); // TODO(gman): Implement.
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleReturn(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::Return& args) {
DCHECK(false); // TODO(gman): Implement.
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleSetBucketSize(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::SetBucketSize& args) {
- DCHECK(false); // TODO(gman): Implement.
+ uint32 bucket_id = args.bucket_id;
+ uint32 size = args.size;
+
+ Bucket* bucket = GetBucket(bucket_id);
+ if (!bucket) {
+ bucket = new Bucket();
+ buckets_[bucket_id] = linked_ptr<Bucket>(bucket);
+ }
+
+ bucket->SetSize(size);
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleSetBucketData(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::SetBucketData& args) {
- DCHECK(false); // TODO(gman): Implement.
+ uint32 bucket_id = args.bucket_id;
+ uint32 offset = args.offset;
+ uint32 size = args.size;
+ const void* data = GetSharedMemoryAs<const void*>(
+ args.shared_memory_id, args.shared_memory_offset, size);
+ if (!data) {
+ return parse_error::kParseInvalidArguments;
+ }
+ Bucket* bucket = GetBucket(bucket_id);
+ if (!bucket) {
+ return parse_error::kParseInvalidArguments;
+ }
+ if (!bucket->SetData(data, offset, size)) {
+ return parse_error::kParseInvalidArguments;
+ }
+
return parse_error::kParseNoError;
}
parse_error::ParseError CommonDecoder::HandleSetBucketDataImmediate(
- uint32 arg_count,
+ uint32 immediate_data_size,
const cmd::SetBucketDataImmediate& args) {
- DCHECK(false); // TODO(gman): Implement.
+ const void* data = GetImmediateDataAs<const void*>(args);
+ uint32 bucket_id = args.bucket_id;
+ uint32 offset = args.offset;
+ uint32 size = args.size;
+ if (size > immediate_data_size) {
+ return parse_error::kParseInvalidArguments;
+ }
+ Bucket* bucket = GetBucket(bucket_id);
+ if (!bucket) {
+ return parse_error::kParseInvalidArguments;
+ }
+ if (!bucket->SetData(data, offset, size)) {
+ return parse_error::kParseInvalidArguments;
+ }
return parse_error::kParseNoError;
}
-parse_error::ParseError CommonDecoder::HandleGetResultSize(
- uint32 arg_count,
- const cmd::GetResultSize& args) {
- DCHECK(false); // TODO(gman): Implement.
+parse_error::ParseError CommonDecoder::HandleGetBucketSize(
+ uint32 immediate_data_size,
+ const cmd::GetBucketSize& args) {
+ uint32 bucket_id = args.bucket_id;
+ uint32* data = GetSharedMemoryAs<uint32*>(
+ args.shared_memory_id, args.shared_memory_offset, sizeof(*data));
+ if (!data) {
+ return parse_error::kParseInvalidArguments;
+ }
+ Bucket* bucket = GetBucket(bucket_id);
+ if (!bucket) {
+ return parse_error::kParseInvalidArguments;
+ }
+ *data = bucket->size();
return parse_error::kParseNoError;
}
-parse_error::ParseError CommonDecoder::HandleGetResultData(
- uint32 arg_count,
- const cmd::GetResultData& args) {
- DCHECK(false); // TODO(gman): Implement.
+parse_error::ParseError CommonDecoder::HandleGetBucketData(
+ uint32 immediate_data_size,
+ const cmd::GetBucketData& args) {
+ uint32 bucket_id = args.bucket_id;
+ uint32 offset = args.offset;
+ uint32 size = args.size;
+ void* data = GetSharedMemoryAs<void*>(
+ args.shared_memory_id, args.shared_memory_offset, size);
+ if (!data) {
+ return parse_error::kParseInvalidArguments;
+ }
+ Bucket* bucket = GetBucket(bucket_id);
+ if (!bucket) {
+ return parse_error::kParseInvalidArguments;
+ }
+ const void* src = bucket->GetData(offset, size);
+ if (!src) {
+ return parse_error::kParseInvalidArguments;
+ }
+ memcpy(data, src, size);
return parse_error::kParseNoError;
}
diff --git a/gpu/command_buffer/service/common_decoder.h b/gpu/command_buffer/service/common_decoder.h
index 25d1dbe..c96cc9c 100644
--- a/gpu/command_buffer/service/common_decoder.h
+++ b/gpu/command_buffer/service/common_decoder.h
@@ -32,6 +32,9 @@
#ifndef GPU_COMMAND_BUFFER_SERVICE_CROSS_COMMON_DECODER_H_
#define GPU_COMMAND_BUFFER_SERVICE_CROSS_COMMON_DECODER_H_
+#include <map>
+#include "base/linked_ptr.h"
+#include "base/scoped_ptr.h"
#include "gpu/command_buffer/service/cmd_parser.h"
namespace command_buffer {
@@ -44,6 +47,65 @@ class CommonDecoder : public AsyncAPIInterface {
public:
typedef parse_error::ParseError ParseError;
+ // A bucket is a buffer to help collect memory across a command buffer. When
+ // creating a command buffer implementation of an existing API, sometimes that
+ // API has functions that take a pointer to data. A good example is OpenGL's
+ // glBufferData. Because the data is separated between client and service,
+ // there are 2 ways to get this data across. 1 is to put all the data in
+ // shared memory. The problem with this is the data can be arbitarily large
+ // and the host OS may not support that much shared memory. Another solution
+ // is to shuffle memory across a little bit at a time, collecting it on the
+ // service side and when it is all there then call glBufferData. Buckets
+ // implement this second solution. Using the common commands, SetBucketSize,
+ // SetBucketData, SetBucketDataImmediate the client can fill a bucket. It can
+ // then call a command that uses that bucket (like BufferDataBucket in the
+ // GLES2 command buffer implementation).
+ //
+ // If you are designing an API from scratch you can avoid this need for
+ // Buckets by making your API always take an offset and a size
+ // similar to glBufferSubData.
+ //
+ // Buckets also help pass strings to/from the service. To return a string of
+ // arbitary size, the service puts the string in a bucket. The client can
+ // then query the size of a bucket and request sections of the bucket to
+ // be passed across shared memory.
+ class Bucket {
+ public:
+ Bucket() : size_(0) {
+ }
+
+ size_t size() const {
+ return size_;
+ }
+
+ // Gets a pointer to a section the bucket. Returns NULL if offset or size is
+ // out of range.
+ const void* GetData(size_t offset, size_t size) const;
+
+ template <typename T>
+ T GetDataAs(size_t offset, size_t size) const {
+ return reinterpret_cast<T>(GetData(offset, size));
+ }
+
+ // Sets the size of the bucket.
+ void SetSize(size_t size);
+
+ // Sets a part of the bucket.
+ // Returns false if offset or size is out of range.
+ bool SetData(const void* src, size_t offset, size_t size);
+
+ private:
+ bool OffsetSizeValid(size_t offset, size_t size) const {
+ size_t temp = offset + size;
+ return temp <= size_ && temp >= offset;
+ }
+
+ size_t size_;
+ scoped_array<int8> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(Bucket);
+ };
+
CommonDecoder() : engine_(NULL) {
}
virtual ~CommonDecoder() {
@@ -83,15 +145,25 @@ class CommonDecoder : public AsyncAPIInterface {
unsigned int offset,
unsigned int size);
+ // Typed version of GetAddressAndCheckSize.
+ template <typename T>
+ T GetSharedMemoryAs(unsigned int shm_id, unsigned int offset,
+ unsigned int size) {
+ return static_cast<T>(GetAddressAndCheckSize(shm_id, offset, size));
+ }
+
// Gets an name for a common command.
const char* GetCommonCommandName(cmd::CommandId command_id) const;
+ // Gets a bucket. Returns NULL if the bucket does not exist.
+ Bucket* GetBucket(uint32 bucket_id) const;
+
private:
// Generate a member function prototype for each command in an automated and
// typesafe way.
#define COMMON_COMMAND_BUFFER_CMD_OP(name) \
parse_error::ParseError Handle ## name( \
- unsigned int arg_count, \
+ uint32 immediate_data_size, \
const cmd::name& args); \
COMMON_COMMAND_BUFFER_CMDS(COMMON_COMMAND_BUFFER_CMD_OP)
@@ -99,6 +171,9 @@ class CommonDecoder : public AsyncAPIInterface {
#undef COMMON_COMMAND_BUFFER_CMD_OP
CommandBufferEngine* engine_;
+
+ typedef std::map<uint32, linked_ptr<Bucket> > BucketMap;
+ BucketMap buckets_;
};
} // namespace command_buffer
diff --git a/gpu/command_buffer/service/common_decoder_unittest.cc b/gpu/command_buffer/service/common_decoder_unittest.cc
new file mode 100644
index 0000000..ce3a5d5
--- /dev/null
+++ b/gpu/command_buffer/service/common_decoder_unittest.cc
@@ -0,0 +1,418 @@
+// Copyright (c) 2009 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/common_decoder.h"
+#include "gpu/command_buffer/service/cmd_buffer_engine.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace command_buffer {
+
+TEST(CommonDecoderBucket, Basic) {
+ CommonDecoder::Bucket bucket;
+ EXPECT_EQ(0, bucket.size());
+ EXPECT_TRUE(NULL == bucket.GetData(0, 0));
+}
+
+TEST(CommonDecoderBucket, Size) {
+ CommonDecoder::Bucket bucket;
+ bucket.SetSize(24);
+ EXPECT_EQ(24, bucket.size());
+ bucket.SetSize(12);
+ EXPECT_EQ(12, bucket.size());
+}
+
+TEST(CommonDecoderBucket, GetData) {
+ CommonDecoder::Bucket bucket;
+
+ bucket.SetSize(24);
+ EXPECT_TRUE(NULL != bucket.GetData(0, 0));
+ EXPECT_TRUE(NULL != bucket.GetData(24, 0));
+ EXPECT_TRUE(NULL == bucket.GetData(25, 0));
+ EXPECT_TRUE(NULL != bucket.GetData(0, 24));
+ EXPECT_TRUE(NULL == bucket.GetData(0, 25));
+ bucket.SetSize(23);
+ EXPECT_TRUE(NULL == bucket.GetData(0, 24));
+}
+
+TEST(CommonDecoderBucket, SetData) {
+ CommonDecoder::Bucket bucket;
+ static const char data[] = "testing";
+
+ bucket.SetSize(10);
+ EXPECT_TRUE(bucket.SetData(data, 0, sizeof(data)));
+ EXPECT_EQ(0, memcmp(data, bucket.GetData(0, sizeof(data)), sizeof(data)));
+ EXPECT_TRUE(bucket.SetData(data, 2, sizeof(data)));
+ EXPECT_EQ(0, memcmp(data, bucket.GetData(2, sizeof(data)), sizeof(data)));
+ EXPECT_FALSE(bucket.SetData(data, 0, sizeof(data) * 2));
+ EXPECT_FALSE(bucket.SetData(data, 5, sizeof(data)));
+}
+
+class TestCommonDecoder : public CommonDecoder {
+ public:
+ // Overridden from AsyncAPIInterface
+ const char* GetCommandName(unsigned int command_id) const {
+ return GetCommonCommandName(static_cast<cmd::CommandId>(command_id));
+ }
+
+ // Overridden from AsyncAPIInterface
+ parse_error::ParseError DoCommand(
+ unsigned int command,
+ unsigned int arg_count,
+ const void* cmd_data) {
+ return DoCommonCommand(command, arg_count, cmd_data);
+ }
+
+ CommonDecoder::Bucket* GetBucket(uint32 id) const {
+ return CommonDecoder::GetBucket(id);
+ }
+};
+
+class MockCommandBufferEngine : public CommandBufferEngine {
+ public:
+ static const int32 kValidShmId = 1;
+
+ MockCommandBufferEngine()
+ : CommandBufferEngine(),
+ token_() {
+ }
+
+ // Overridden from CommandBufferEngine.
+ virtual void* GetSharedMemoryAddress(int32 shm_id) {
+ return (shm_id == kValidShmId) ? buffer_ : NULL;
+ }
+
+ // Overridden from CommandBufferEngine.
+ virtual size_t GetSharedMemorySize(int32 shm_id) {
+ return (shm_id == kValidShmId) ? kBufferSize : 0;
+ }
+
+ template <typename T>
+ T GetSharedMemoryAs(uint32 offset) {
+ DCHECK(offset < kBufferSize);
+ return reinterpret_cast<T>(&buffer_[offset]);
+ }
+
+ uint32 GetSharedMemoryOffset(const void* memory) {
+ ptrdiff_t offset = reinterpret_cast<const int8*>(memory) - &buffer_[0];
+ DCHECK(offset >= 0);
+ DCHECK(offset < kBufferSize);
+ return static_cast<uint32>(offset);
+ }
+
+ // Overridden from CommandBufferEngine.
+ virtual void set_token(int32 token) {
+ token_ = token;
+ }
+
+ int32 token() const {
+ return token_;
+ }
+
+ private:
+ static const size_t kBufferSize = 1024;
+
+ int8 buffer_[kBufferSize];
+ int32 token_;
+};
+
+class CommonDecoderTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ decoder_.set_engine(&engine_);
+ }
+
+ virtual void TearDown() {
+ }
+
+ template <typename T>
+ parse_error::ParseError ExecuteCmd(const T& cmd) {
+ COMPILE_ASSERT(T::kArgFlags == cmd::kFixed, Cmd_kArgFlags_not_kFixed);
+ return decoder_.DoCommand(cmd.kCmdId,
+ ComputeNumEntries(sizeof(cmd)) - 1,
+ &cmd);
+ }
+
+ template <typename T>
+ parse_error::ParseError ExecuteImmediateCmd(const T& cmd, size_t data_size) {
+ COMPILE_ASSERT(T::kArgFlags == cmd::kAtLeastN, Cmd_kArgFlags_not_kAtLeastN);
+ return decoder_.DoCommand(cmd.kCmdId,
+ ComputeNumEntries(sizeof(cmd) + data_size) - 1,
+ &cmd);
+ }
+
+ MockCommandBufferEngine engine_;
+ TestCommonDecoder decoder_;
+};
+
+TEST_F(CommonDecoderTest, HandleNoop) {
+ cmd::Noop cmd;
+ const uint32 kSkipCount = 5;
+ cmd.Init(kSkipCount);
+ EXPECT_EQ(parse_error::kParseNoError,
+ ExecuteImmediateCmd(
+ cmd, kSkipCount * kCommandBufferEntrySize));
+}
+
+TEST_F(CommonDecoderTest, SetToken) {
+ cmd::SetToken cmd;
+ const int32 kTokenId = 123;
+ EXPECT_EQ(0, engine_.token());
+ cmd.Init(kTokenId);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(kTokenId, engine_.token());
+}
+
+TEST_F(CommonDecoderTest, Jump) {
+ // cmd::Jump cmd;
+ // TODO(gman): implement.
+}
+
+TEST_F(CommonDecoderTest, JumpRelative) {
+ // cmd::JumpRelative cmd;
+ // TODO(gman): implement.
+}
+
+TEST_F(CommonDecoderTest, Call) {
+ // cmd::Call cmd;
+ // TODO(gman): implement.
+}
+
+TEST_F(CommonDecoderTest, CallRelative) {
+ // cmd::CallRelative cmd;
+ // TODO(gman): implement.
+}
+
+TEST_F(CommonDecoderTest, Return) {
+ // cmd::Return cmd;
+ // TODO(gman): implement.
+}
+
+TEST_F(CommonDecoderTest, SetBucketSize) {
+ cmd::SetBucketSize cmd;
+ const uint32 kBucketId = 123;
+ const uint32 kBucketLength1 = 1234;
+ const uint32 kBucketLength2 = 78;
+ // Check the bucket does not exist.
+ EXPECT_TRUE(NULL == decoder_.GetBucket(kBucketId));
+ // Check we can create one.
+ cmd.Init(kBucketId, kBucketLength1);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ CommonDecoder::Bucket* bucket;
+ bucket = decoder_.GetBucket(kBucketId);
+ EXPECT_TRUE(NULL != bucket);
+ EXPECT_EQ(kBucketLength1, bucket->size());
+ // Check we can change it.
+ cmd.Init(kBucketId, kBucketLength2);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ bucket = decoder_.GetBucket(kBucketId);
+ EXPECT_TRUE(NULL != bucket);
+ EXPECT_EQ(kBucketLength2, bucket->size());
+ // Check we can delete it.
+ cmd.Init(kBucketId, 0);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ bucket = decoder_.GetBucket(kBucketId);
+ EXPECT_EQ(0, bucket->size());
+}
+
+TEST_F(CommonDecoderTest, SetBucketData) {
+ cmd::SetBucketSize size_cmd;
+ cmd::SetBucketData cmd;
+
+ static const char kData[] = "1234567890123456789";
+
+ const uint32 kBucketId = 123;
+ const uint32 kInvalidBucketId = 124;
+
+ size_cmd.Init(kBucketId, sizeof(kData));
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(size_cmd));
+ CommonDecoder::Bucket* bucket = decoder_.GetBucket(kBucketId);
+ // Check the data is not there.
+ EXPECT_NE(0, memcmp(bucket->GetData(0, sizeof(kData)), kData, sizeof(kData)));
+
+ // Check we can set it.
+ const uint32 kSomeOffsetInSharedMemory = 50;
+ void* memory = engine_.GetSharedMemoryAs<void*>(kSomeOffsetInSharedMemory);
+ memcpy(memory, kData, sizeof(kData));
+ cmd.Init(kBucketId, 0, sizeof(kData),
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(0, memcmp(bucket->GetData(0, sizeof(kData)), kData, sizeof(kData)));
+
+ // Check we can set it partially.
+ static const char kData2[] = "ABCEDFG";
+ const uint32 kSomeOffsetInBucket = 5;
+ memcpy(memory, kData2, sizeof(kData2));
+ cmd.Init(kBucketId, kSomeOffsetInBucket, sizeof(kData2),
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(0, memcmp(bucket->GetData(kSomeOffsetInBucket, sizeof(kData2)),
+ kData2, sizeof(kData2)));
+ const char* bucket_data = bucket->GetDataAs<const char*>(0, sizeof(kData));
+ // Check that nothing was affected outside of updated area.
+ EXPECT_EQ(kData[kSomeOffsetInBucket - 1],
+ bucket_data[kSomeOffsetInBucket - 1]);
+ EXPECT_EQ(kData[kSomeOffsetInBucket + sizeof(kData2)],
+ bucket_data[kSomeOffsetInBucket + sizeof(kData2)]);
+
+ // Check that it fails if the bucket_id is invalid
+ cmd.Init(kInvalidBucketId, kSomeOffsetInBucket, sizeof(kData2),
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_NE(parse_error::kParseNoError, ExecuteCmd(cmd));
+
+ // Check that it fails if the offset is out of range.
+ cmd.Init(kBucketId, bucket->size(), 1,
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_NE(parse_error::kParseNoError, ExecuteCmd(cmd));
+
+ // Check that it fails if the size is out of range.
+ cmd.Init(kBucketId, 0, bucket->size() + 1,
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_NE(parse_error::kParseNoError, ExecuteCmd(cmd));
+}
+
+TEST_F(CommonDecoderTest, SetBucketDataImmediate) {
+ cmd::SetBucketSize size_cmd;
+ int8 buffer[1024];
+ cmd::SetBucketDataImmediate& cmd =
+ *reinterpret_cast<cmd::SetBucketDataImmediate*>(&buffer);
+
+ static const char kData[] = "1234567890123456789";
+
+ const uint32 kBucketId = 123;
+ const uint32 kInvalidBucketId = 124;
+
+ size_cmd.Init(kBucketId, sizeof(kData));
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(size_cmd));
+ CommonDecoder::Bucket* bucket = decoder_.GetBucket(kBucketId);
+ // Check the data is not there.
+ EXPECT_NE(0, memcmp(bucket->GetData(0, sizeof(kData)), kData, sizeof(kData)));
+
+ // Check we can set it.
+ void* memory = &buffer[0] + sizeof(cmd);
+ memcpy(memory, kData, sizeof(kData));
+ cmd.Init(kBucketId, 0, sizeof(kData));
+ EXPECT_EQ(parse_error::kParseNoError,
+ ExecuteImmediateCmd(cmd, sizeof(kData)));
+ EXPECT_EQ(0, memcmp(bucket->GetData(0, sizeof(kData)), kData, sizeof(kData)));
+
+ // Check we can set it partially.
+ static const char kData2[] = "ABCEDFG";
+ const uint32 kSomeOffsetInBucket = 5;
+ memcpy(memory, kData2, sizeof(kData2));
+ cmd.Init(kBucketId, kSomeOffsetInBucket, sizeof(kData2));
+ EXPECT_EQ(parse_error::kParseNoError,
+ ExecuteImmediateCmd(cmd, sizeof(kData2)));
+ EXPECT_EQ(0, memcmp(bucket->GetData(kSomeOffsetInBucket, sizeof(kData2)),
+ kData2, sizeof(kData2)));
+ const char* bucket_data = bucket->GetDataAs<const char*>(0, sizeof(kData));
+ // Check that nothing was affected outside of updated area.
+ EXPECT_EQ(kData[kSomeOffsetInBucket - 1],
+ bucket_data[kSomeOffsetInBucket - 1]);
+ EXPECT_EQ(kData[kSomeOffsetInBucket + sizeof(kData2)],
+ bucket_data[kSomeOffsetInBucket + sizeof(kData2)]);
+
+ // Check that it fails if the bucket_id is invalid
+ cmd.Init(kInvalidBucketId, kSomeOffsetInBucket, sizeof(kData2));
+ EXPECT_NE(parse_error::kParseNoError,
+ ExecuteImmediateCmd(cmd, sizeof(kData2)));
+
+ // Check that it fails if the offset is out of range.
+ cmd.Init(kBucketId, bucket->size(), 1);
+ EXPECT_NE(parse_error::kParseNoError,
+ ExecuteImmediateCmd(cmd, sizeof(kData2)));
+
+ // Check that it fails if the size is out of range.
+ cmd.Init(kBucketId, 0, bucket->size() + 1);
+ EXPECT_NE(parse_error::kParseNoError,
+ ExecuteImmediateCmd(cmd, sizeof(kData2)));
+}
+
+TEST_F(CommonDecoderTest, GetBucketSize) {
+ cmd::SetBucketSize size_cmd;
+ cmd::GetBucketSize cmd;
+
+ const uint32 kBucketSize = 456;
+ const uint32 kBucketId = 123;
+ const uint32 kInvalidBucketId = 124;
+
+ size_cmd.Init(kBucketId, kBucketSize);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(size_cmd));
+
+ // Check that the size is correct.
+ const uint32 kSomeOffsetInSharedMemory = 50;
+ uint32* memory =
+ engine_.GetSharedMemoryAs<uint32*>(kSomeOffsetInSharedMemory);
+ *memory = 0xFFFFFFFF;
+ cmd.Init(kBucketId,
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(kBucketSize, *memory);
+
+ // Check that it fails if the bucket_id is invalid
+ cmd.Init(kInvalidBucketId,
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_NE(parse_error::kParseNoError, ExecuteCmd(cmd));
+}
+
+TEST_F(CommonDecoderTest, GetBucketData) {
+ cmd::SetBucketSize size_cmd;
+ cmd::SetBucketData set_cmd;
+ cmd::GetBucketData cmd;
+
+ static const char kData[] = "1234567890123456789";
+ static const char zero[sizeof(kData)] = { 0, };
+
+ const uint32 kBucketId = 123;
+ const uint32 kInvalidBucketId = 124;
+
+ size_cmd.Init(kBucketId, sizeof(kData));
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(size_cmd));
+ CommonDecoder::Bucket* bucket = decoder_.GetBucket(kBucketId);
+ const uint32 kSomeOffsetInSharedMemory = 50;
+ uint8* memory = engine_.GetSharedMemoryAs<uint8*>(kSomeOffsetInSharedMemory);
+ memcpy(memory, kData, sizeof(kData));
+ set_cmd.Init(kBucketId, 0, sizeof(kData),
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(set_cmd));
+
+ // Check we can get the whole thing.
+ memset(memory, 0, sizeof(kData));
+ cmd.Init(kBucketId, 0, sizeof(kData),
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(0, memcmp(memory, kData, sizeof(kData)));
+
+ // Check we can get a piece.
+ const uint32 kSomeOffsetInBucket = 5;
+ const uint32 kLengthOfPiece = 6;
+ const uint8 kSentinel = 0xff;
+ memset(memory, 0, sizeof(kData));
+ memory[-1] = kSentinel;
+ cmd.Init(kBucketId, kSomeOffsetInBucket, kLengthOfPiece,
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_EQ(parse_error::kParseNoError, ExecuteCmd(cmd));
+ EXPECT_EQ(0, memcmp(memory, kData + kSomeOffsetInBucket, kLengthOfPiece));
+ EXPECT_EQ(0, memcmp(memory + kLengthOfPiece, zero,
+ sizeof(kData) - kLengthOfPiece));
+ EXPECT_EQ(kSentinel, memory[-1]);
+
+ // Check that it fails if the bucket_id is invalid
+ cmd.Init(kInvalidBucketId, kSomeOffsetInBucket, sizeof(kData),
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_NE(parse_error::kParseNoError, ExecuteCmd(cmd));
+
+ // Check that it fails if the offset is invalid
+ cmd.Init(kBucketId, sizeof(kData) + 1, 1,
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_NE(parse_error::kParseNoError, ExecuteCmd(cmd));
+
+ // Check that it fails if the size is invalid
+ cmd.Init(kBucketId, 0, sizeof(kData) + 1,
+ MockCommandBufferEngine::kValidShmId, kSomeOffsetInSharedMemory);
+ EXPECT_NE(parse_error::kParseNoError, ExecuteCmd(cmd));
+}
+
+
+} // namespace command_buffer
+
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index b6a1414..5d0d2d7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -31,15 +31,6 @@ RETURN_TYPE GetImmediateDataAs(const COMMAND_TYPE& pod) {
return static_cast<RETURN_TYPE>(const_cast<void*>(AddressAfterStruct(pod)));
}
-// Returns the size in bytes of the data of an Immediate command, a command with
-// its data inline in the command buffer.
-template <typename T>
-unsigned int ImmediateDataSize(uint32 arg_count) {
- return static_cast<unsigned int>(
- (arg_count + 1 - ComputeNumEntries(sizeof(T))) *
- sizeof(CommandBufferEntry)); // NOLINT
-}
-
// Checks if there is enough immediate data.
template<typename T>
bool CheckImmediateDataSize(
@@ -249,13 +240,6 @@ class GLES2DecoderImpl : public GLES2Decoder {
bool InitPlatformSpecific();
bool InitGlew();
- // Typed version of GetAddressAndCheckSize.
- template <typename T>
- T GetSharedMemoryAs(unsigned int shm_id, unsigned int offset,
- unsigned int size) {
- return static_cast<T>(GetAddressAndCheckSize(shm_id, offset, size));
- }
-
// Template to help call glGenXXX functions.
template <void gl_gen_function(GLsizei, GLuint*)>
bool GenGLObjects(GLsizei n, const GLuint* client_ids) {
diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp
index 0b06d7d..2fa660e7 100644
--- a/gpu/gpu.gyp
+++ b/gpu/gpu.gyp
@@ -264,6 +264,7 @@
'sources': [
'command_buffer/service/cmd_parser_test.cc',
'command_buffer/service/command_buffer_service_unittest.cc',
+ 'command_buffer/service/common_decoder_unittest.cc',
'command_buffer/service/gpu_processor_unittest.cc',
'command_buffer/service/resource_test.cc',
],