diff options
author | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-10 09:30:28 +0000 |
---|---|---|
committer | gman@chromium.org <gman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-10 09:30:28 +0000 |
commit | c0ce9d8162a70abb545e3ca7376003a492b08490 (patch) | |
tree | e15daf5c75aefb839ea98d2fd8f9cefe69ec44ad /gpu | |
parent | 06489a012706979c4a82bc2f3ac502b82fca2346 (diff) | |
download | chromium_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.h | 88 | ||||
-rw-r--r-- | gpu/command_buffer/service/common_decoder.cc | 150 | ||||
-rw-r--r-- | gpu/command_buffer/service/common_decoder.h | 77 | ||||
-rw-r--r-- | gpu/command_buffer/service/common_decoder_unittest.cc | 418 | ||||
-rw-r--r-- | gpu/command_buffer/service/gles2_cmd_decoder.cc | 16 | ||||
-rw-r--r-- | gpu/gpu.gyp | 1 |
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', ], |