diff options
Diffstat (limited to 'chromium/gpu/command_buffer/client/cmd_buffer_helper_test.cc')
-rw-r--r-- | chromium/gpu/command_buffer/client/cmd_buffer_helper_test.cc | 400 |
1 files changed, 389 insertions, 11 deletions
diff --git a/chromium/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/chromium/gpu/command_buffer/client/cmd_buffer_helper_test.cc index bd0e86e3f45..c2d1361555a 100644 --- a/chromium/gpu/command_buffer/client/cmd_buffer_helper_test.cc +++ b/chromium/gpu/command_buffer/client/cmd_buffer_helper_test.cc @@ -4,8 +4,11 @@ // Tests for the Command Buffer Helper. +#include <list> + #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/memory/linked_ptr.h" #include "base/message_loop/message_loop.h" #include "gpu/command_buffer/client/cmd_buffer_helper.h" #include "gpu/command_buffer/service/command_buffer_service.h" @@ -28,11 +31,54 @@ using testing::DoAll; using testing::Invoke; using testing::_; -const int32 kTotalNumCommandEntries = 10; +const int32 kTotalNumCommandEntries = 32; const int32 kCommandBufferSizeBytes = kTotalNumCommandEntries * sizeof(CommandBufferEntry); const int32 kUnusedCommandId = 5; // we use 0 and 2 currently. +// Override CommandBufferService::Flush() to lock flushing and simulate +// the buffer becoming full in asynchronous mode. +class CommandBufferServiceLocked : public CommandBufferService { + public: + explicit CommandBufferServiceLocked( + TransferBufferManagerInterface* transfer_buffer_manager) + : CommandBufferService(transfer_buffer_manager), + flush_locked_(false), + last_flush_(-1), + flush_count_(0) {} + virtual ~CommandBufferServiceLocked() {} + + virtual void Flush(int32 put_offset) OVERRIDE { + flush_count_++; + if (!flush_locked_) { + last_flush_ = -1; + CommandBufferService::Flush(put_offset); + } else { + last_flush_ = put_offset; + } + } + + void LockFlush() { flush_locked_ = true; } + + void UnlockFlush() { flush_locked_ = false; } + + int FlushCount() { return flush_count_; } + + virtual void WaitForGetOffsetInRange(int32 start, int32 end) OVERRIDE { + if (last_flush_ != -1) { + CommandBufferService::Flush(last_flush_); + last_flush_ = -1; + } + CommandBufferService::WaitForGetOffsetInRange(start, end); + } + + private: + bool flush_locked_; + int last_flush_; + int flush_count_; + DISALLOW_COPY_AND_ASSIGN(CommandBufferServiceLocked); +}; + // Test fixture for CommandBufferHelper test - Creates a CommandBufferHelper, // using a CommandBufferEngine with a mock AsyncAPIInterface for its interface // (calling it directly, not through the RPC mechanism). @@ -51,7 +97,7 @@ class CommandBufferHelperTest : public testing::Test { EXPECT_TRUE(manager->Initialize()); } command_buffer_.reset( - new CommandBufferService(transfer_buffer_manager_.get())); + new CommandBufferServiceLocked(transfer_buffer_manager_.get())); EXPECT_TRUE(command_buffer_->Initialize()); gpu_scheduler_.reset(new GpuScheduler( @@ -65,17 +111,22 @@ class CommandBufferHelperTest : public testing::Test { helper_.reset(new CommandBufferHelper(command_buffer_.get())); helper_->Initialize(kCommandBufferSizeBytes); + + test_command_next_id_ = kUnusedCommandId; } virtual void TearDown() { // If the GpuScheduler posts any tasks, this forces them to run. base::MessageLoop::current()->RunUntilIdle(); + test_command_args_.clear(); } const CommandParser* GetParser() const { return gpu_scheduler_->parser(); } + int32 ImmediateEntryCount() const { return helper_->immediate_entry_count_; } + // Adds a command to the buffer through the helper, while adding it as an // expected call on the API mock. void AddCommandWithExpect(error::Error _return, @@ -85,7 +136,8 @@ class CommandBufferHelperTest : public testing::Test { CommandHeader header; header.size = arg_count + 1; header.command = command; - CommandBufferEntry* cmds = helper_->GetSpace(arg_count + 1); + CommandBufferEntry* cmds = + static_cast<CommandBufferEntry*>(helper_->GetSpace(arg_count + 1)); CommandBufferOffset put = 0; cmds[put++].value_header = header; for (int ii = 0; ii < arg_count; ++ii) { @@ -98,6 +150,68 @@ class CommandBufferHelperTest : public testing::Test { .WillOnce(Return(_return)); } + void AddUniqueCommandWithExpect(error::Error _return, int cmd_size) { + EXPECT_GE(cmd_size, 1); + EXPECT_LT(cmd_size, kTotalNumCommandEntries); + int arg_count = cmd_size - 1; + + // Allocate array for args. + linked_ptr<std::vector<CommandBufferEntry> > args_ptr( + new std::vector<CommandBufferEntry>(arg_count ? arg_count : 1)); + + for (int32 ii = 0; ii < arg_count; ++ii) { + (*args_ptr)[ii].value_uint32 = 0xF00DF00D + ii; + } + + // Add command and save args in test_command_args_ until the test completes. + AddCommandWithExpect( + _return, test_command_next_id_++, arg_count, &(*args_ptr)[0]); + test_command_args_.insert(test_command_args_.end(), args_ptr); + } + + void TestCommandWrappingFull(int32 cmd_size, int32 start_commands) { + const int32 num_args = cmd_size - 1; + EXPECT_EQ(kTotalNumCommandEntries % cmd_size, 0); + + std::vector<CommandBufferEntry> args(num_args); + for (int32 ii = 0; ii < num_args; ++ii) { + args[ii].value_uint32 = ii + 1; + } + + // Initially insert commands up to start_commands and Finish(). + for (int32 ii = 0; ii < start_commands; ++ii) { + AddCommandWithExpect( + error::kNoError, ii + kUnusedCommandId, num_args, &args[0]); + } + helper_->Finish(); + + EXPECT_EQ(GetParser()->put(), + (start_commands * cmd_size) % kTotalNumCommandEntries); + EXPECT_EQ(GetParser()->get(), + (start_commands * cmd_size) % kTotalNumCommandEntries); + + // Lock flushing to force the buffer to get full. + command_buffer_->LockFlush(); + + // Add enough commands to over fill the buffer. + for (int32 ii = 0; ii < kTotalNumCommandEntries / cmd_size + 2; ++ii) { + AddCommandWithExpect(error::kNoError, + start_commands + ii + kUnusedCommandId, + num_args, + &args[0]); + } + + // Flush all commands. + command_buffer_->UnlockFlush(); + helper_->Finish(); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); + } + // Checks that the buffer from put to put+size is free in the parser. void CheckFreeSpace(CommandBufferOffset put, unsigned int size) { CommandBufferOffset parser_put = GetParser()->put(); @@ -122,15 +236,21 @@ class CommandBufferHelperTest : public testing::Test { } int32 GetGetOffset() { - return command_buffer_->GetState().get_offset; + return command_buffer_->GetLastState().get_offset; } int32 GetPutOffset() { - return command_buffer_->GetState().put_offset; + return command_buffer_->GetLastState().put_offset; } + int32 GetHelperGetOffset() { return helper_->get_offset(); } + + int32 GetHelperPutOffset() { return helper_->put_; } + + uint32 GetHelperFlushGeneration() { return helper_->flush_generation(); } + error::Error GetError() { - return command_buffer_->GetState().error; + return command_buffer_->GetLastState().error; } CommandBufferOffset get_helper_put() { return helper_->put_; } @@ -141,12 +261,176 @@ class CommandBufferHelperTest : public testing::Test { base::MessageLoop message_loop_; scoped_ptr<AsyncAPIMock> api_mock_; scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_; - scoped_ptr<CommandBufferService> command_buffer_; + scoped_ptr<CommandBufferServiceLocked> command_buffer_; scoped_ptr<GpuScheduler> gpu_scheduler_; scoped_ptr<CommandBufferHelper> helper_; + std::list<linked_ptr<std::vector<CommandBufferEntry> > > test_command_args_; + unsigned int test_command_next_id_; Sequence sequence_; }; +// Checks immediate_entry_count_ changes based on 'usable' state. +TEST_F(CommandBufferHelperTest, TestCalcImmediateEntriesNotUsable) { + // Auto flushing mode is tested separately. + helper_->SetAutomaticFlushes(false); + EXPECT_EQ(helper_->usable(), true); + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries - 1); + helper_->ClearUsable(); + EXPECT_EQ(ImmediateEntryCount(), 0); +} + +// Checks immediate_entry_count_ changes based on RingBuffer state. +TEST_F(CommandBufferHelperTest, TestCalcImmediateEntriesNoRingBuffer) { + helper_->SetAutomaticFlushes(false); + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries - 1); + helper_->FreeRingBuffer(); + EXPECT_EQ(ImmediateEntryCount(), 0); +} + +// Checks immediate_entry_count_ calc when Put >= Get and Get == 0. +TEST_F(CommandBufferHelperTest, TestCalcImmediateEntriesGetAtZero) { + // No internal auto flushing. + helper_->SetAutomaticFlushes(false); + command_buffer_->LockFlush(); + + // Start at Get = Put = 0. + EXPECT_EQ(GetHelperPutOffset(), 0); + EXPECT_EQ(GetHelperGetOffset(), 0); + + // Immediate count should be 1 less than the end of the buffer. + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries - 1); + AddUniqueCommandWithExpect(error::kNoError, 2); + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries - 3); + + helper_->Finish(); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + +// Checks immediate_entry_count_ calc when Put >= Get and Get > 0. +TEST_F(CommandBufferHelperTest, TestCalcImmediateEntriesGetInMiddle) { + // No internal auto flushing. + helper_->SetAutomaticFlushes(false); + command_buffer_->LockFlush(); + + // Move to Get = Put = 2. + AddUniqueCommandWithExpect(error::kNoError, 2); + helper_->Finish(); + EXPECT_EQ(GetHelperPutOffset(), 2); + EXPECT_EQ(GetHelperGetOffset(), 2); + + // Immediate count should be up to the end of the buffer. + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries - 2); + AddUniqueCommandWithExpect(error::kNoError, 2); + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries - 4); + + helper_->Finish(); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + +// Checks immediate_entry_count_ calc when Put < Get. +TEST_F(CommandBufferHelperTest, TestCalcImmediateEntriesGetBeforePut) { + // Move to Get = kTotalNumCommandEntries / 4, Put = 0. + const int kInitGetOffset = kTotalNumCommandEntries / 4; + helper_->SetAutomaticFlushes(false); + command_buffer_->LockFlush(); + AddUniqueCommandWithExpect(error::kNoError, kInitGetOffset); + helper_->Finish(); + AddUniqueCommandWithExpect(error::kNoError, + kTotalNumCommandEntries - kInitGetOffset); + + // Flush instead of Finish will let Put wrap without the command buffer + // immediately processing the data between Get and Put. + helper_->Flush(); + + EXPECT_EQ(GetHelperGetOffset(), kInitGetOffset); + EXPECT_EQ(GetHelperPutOffset(), 0); + + // Immediate count should be up to Get - 1. + EXPECT_EQ(ImmediateEntryCount(), kInitGetOffset - 1); + AddUniqueCommandWithExpect(error::kNoError, 2); + EXPECT_EQ(ImmediateEntryCount(), kInitGetOffset - 3); + + helper_->Finish(); + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + +// Checks immediate_entry_count_ calc when automatic flushing is enabled. +TEST_F(CommandBufferHelperTest, TestCalcImmediateEntriesAutoFlushing) { + command_buffer_->LockFlush(); + + // Start at Get = Put = 0. + EXPECT_EQ(GetHelperPutOffset(), 0); + EXPECT_EQ(GetHelperGetOffset(), 0); + + // Without auto flushes, up to kTotalNumCommandEntries - 1 is available. + helper_->SetAutomaticFlushes(false); + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries - 1); + + // With auto flushes, and Get == Last Put, + // up to kTotalNumCommandEntries / kAutoFlushSmall is available. + helper_->SetAutomaticFlushes(true); + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries / kAutoFlushSmall); + + // With auto flushes, and Get != Last Put, + // up to kTotalNumCommandEntries / kAutoFlushBig is available. + AddUniqueCommandWithExpect(error::kNoError, 2); + helper_->Flush(); + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries / kAutoFlushBig); + + helper_->Finish(); + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + +// Checks immediate_entry_count_ calc when automatic flushing is enabled, and +// we allocate commands over the immediate_entry_count_ size. +TEST_F(CommandBufferHelperTest, TestCalcImmediateEntriesOverFlushLimit) { + // Lock internal flushing. + command_buffer_->LockFlush(); + + // Start at Get = Put = 0. + EXPECT_EQ(GetHelperPutOffset(), 0); + EXPECT_EQ(GetHelperGetOffset(), 0); + + // Pre-check ImmediateEntryCount is limited with automatic flushing enabled. + helper_->SetAutomaticFlushes(true); + EXPECT_EQ(ImmediateEntryCount(), kTotalNumCommandEntries / kAutoFlushSmall); + + // Add a command larger than ImmediateEntryCount(). + AddUniqueCommandWithExpect(error::kNoError, ImmediateEntryCount() + 1); + + // ImmediateEntryCount() should now be 0, to force a flush check on the next + // command. + EXPECT_EQ(ImmediateEntryCount(), 0); + + // Add a command when ImmediateEntryCount() == 0. + AddUniqueCommandWithExpect(error::kNoError, ImmediateEntryCount() + 1); + + helper_->Finish(); + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + // Checks that commands in the buffer are properly executed, and that the // status/error stay valid. TEST_F(CommandBufferHelperTest, TestCommandProcessing) { @@ -184,12 +468,16 @@ TEST_F(CommandBufferHelperTest, TestCommandProcessing) { // Checks that commands in the buffer are properly executed when wrapping the // buffer, and that the status/error stay valid. TEST_F(CommandBufferHelperTest, TestCommandWrapping) { - // Add 5 commands of size 3 through the helper to make sure we do wrap. + // Add num_commands * commands of size 3 through the helper to make sure we + // do wrap. kTotalNumCommandEntries must not be a multiple of 3. + COMPILE_ASSERT(kTotalNumCommandEntries % 3 != 0, + Is_multiple_of_num_command_entries); + const int kNumCommands = (kTotalNumCommandEntries / 3) * 2; CommandBufferEntry args1[2]; args1[0].value_uint32 = 5; args1[1].value_float = 4.f; - for (unsigned int i = 0; i < 5; ++i) { + for (int i = 0; i < kNumCommands; ++i) { AddCommandWithExpect(error::kNoError, kUnusedCommandId + i, 2, args1); } @@ -204,13 +492,13 @@ TEST_F(CommandBufferHelperTest, TestCommandWrapping) { // Checks the case where the command inserted exactly matches the space left in // the command buffer. TEST_F(CommandBufferHelperTest, TestCommandWrappingExactMultiple) { - const int32 kCommandSize = 5; + const int32 kCommandSize = kTotalNumCommandEntries / 2; const size_t kNumArgs = kCommandSize - 1; COMPILE_ASSERT(kTotalNumCommandEntries % kCommandSize == 0, Not_multiple_of_num_command_entries); CommandBufferEntry args1[kNumArgs]; for (size_t ii = 0; ii < kNumArgs; ++ii) { - args1[0].value_uint32 = ii + 1; + args1[ii].value_uint32 = ii + 1; } for (unsigned int i = 0; i < 5; ++i) { @@ -226,6 +514,22 @@ TEST_F(CommandBufferHelperTest, TestCommandWrappingExactMultiple) { EXPECT_EQ(error::kNoError, GetError()); } +// Checks exact wrapping condition with Get = 0. +TEST_F(CommandBufferHelperTest, TestCommandWrappingFullAtStart) { + TestCommandWrappingFull(2, 0); +} + +// Checks exact wrapping condition with 0 < Get < kTotalNumCommandEntries. +TEST_F(CommandBufferHelperTest, TestCommandWrappingFullInMiddle) { + TestCommandWrappingFull(2, 1); +} + +// Checks exact wrapping condition with Get = kTotalNumCommandEntries. +// Get should wrap back to 0, but making sure. +TEST_F(CommandBufferHelperTest, TestCommandWrappingFullAtEnd) { + TestCommandWrappingFull(2, kTotalNumCommandEntries / 2); +} + // Checks that asking for available entries work, and that the parser // effectively won't use that space. TEST_F(CommandBufferHelperTest, TestAvailableEntries) { @@ -287,6 +591,46 @@ TEST_F(CommandBufferHelperTest, TestToken) { EXPECT_EQ(error::kNoError, GetError()); } +// Checks WaitForToken doesn't Flush if token is already read. +TEST_F(CommandBufferHelperTest, TestWaitForTokenFlush) { + CommandBufferEntry args[2]; + args[0].value_uint32 = 3; + args[1].value_float = 4.f; + + // Add a first command. + AddCommandWithExpect(error::kNoError, kUnusedCommandId + 3, 2, args); + int32 token = helper_->InsertToken(); + + EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) + .WillOnce(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken), + Return(error::kNoError))); + + int flush_count = command_buffer_->FlushCount(); + + // Test that waiting for pending token causes a Flush. + helper_->WaitForToken(token); + EXPECT_EQ(command_buffer_->FlushCount(), flush_count + 1); + + // Test that we don't Flush repeatedly. + helper_->WaitForToken(token); + EXPECT_EQ(command_buffer_->FlushCount(), flush_count + 1); + + // Add another command. + AddCommandWithExpect(error::kNoError, kUnusedCommandId + 4, 2, args); + + // Test that we don't Flush repeatedly even if commands are pending. + helper_->WaitForToken(token); + EXPECT_EQ(command_buffer_->FlushCount(), flush_count + 1); + + helper_->Finish(); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + TEST_F(CommandBufferHelperTest, FreeRingBuffer) { EXPECT_TRUE(helper_->HaveRingBuffer()); @@ -330,4 +674,38 @@ TEST_F(CommandBufferHelperTest, IsContextLost) { EXPECT_TRUE(helper_->IsContextLost()); } +// Checks helper's 'flush generation' updates. +TEST_F(CommandBufferHelperTest, TestFlushGeneration) { + // Explicit flushing only. + helper_->SetAutomaticFlushes(false); + + // Generation should change after Flush() but not before. + uint32 gen1, gen2, gen3; + + gen1 = GetHelperFlushGeneration(); + AddUniqueCommandWithExpect(error::kNoError, 2); + gen2 = GetHelperFlushGeneration(); + helper_->Flush(); + gen3 = GetHelperFlushGeneration(); + EXPECT_EQ(gen2, gen1); + EXPECT_NE(gen3, gen2); + + // Generation should change after Finish() but not before. + gen1 = GetHelperFlushGeneration(); + AddUniqueCommandWithExpect(error::kNoError, 2); + gen2 = GetHelperFlushGeneration(); + helper_->Finish(); + gen3 = GetHelperFlushGeneration(); + EXPECT_EQ(gen2, gen1); + EXPECT_NE(gen3, gen2); + + helper_->Finish(); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + } // namespace gpu |