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 | 333 |
1 files changed, 333 insertions, 0 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 new file mode 100644 index 00000000000..bd0e86e3f45 --- /dev/null +++ b/chromium/gpu/command_buffer/client/cmd_buffer_helper_test.cc @@ -0,0 +1,333 @@ +// 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. + +// Tests for the Command Buffer Helper. + +#include "base/bind.h" +#include "base/bind_helpers.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" +#include "gpu/command_buffer/service/gpu_scheduler.h" +#include "gpu/command_buffer/service/mocks.h" +#include "gpu/command_buffer/service/transfer_buffer_manager.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_MACOSX) +#include "base/mac/scoped_nsautorelease_pool.h" +#endif + +namespace gpu { + +using testing::Return; +using testing::Mock; +using testing::Truly; +using testing::Sequence; +using testing::DoAll; +using testing::Invoke; +using testing::_; + +const int32 kTotalNumCommandEntries = 10; +const int32 kCommandBufferSizeBytes = + kTotalNumCommandEntries * sizeof(CommandBufferEntry); +const int32 kUnusedCommandId = 5; // we use 0 and 2 currently. + +// 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). +class CommandBufferHelperTest : public testing::Test { + protected: + virtual void SetUp() { + api_mock_.reset(new AsyncAPIMock); + // ignore noops in the mock - we don't want to inspect the internals of the + // helper. + EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, _, _)) + .WillRepeatedly(Return(error::kNoError)); + + { + TransferBufferManager* manager = new TransferBufferManager(); + transfer_buffer_manager_.reset(manager); + EXPECT_TRUE(manager->Initialize()); + } + command_buffer_.reset( + new CommandBufferService(transfer_buffer_manager_.get())); + EXPECT_TRUE(command_buffer_->Initialize()); + + gpu_scheduler_.reset(new GpuScheduler( + command_buffer_.get(), api_mock_.get(), NULL)); + command_buffer_->SetPutOffsetChangeCallback(base::Bind( + &GpuScheduler::PutChanged, base::Unretained(gpu_scheduler_.get()))); + command_buffer_->SetGetBufferChangeCallback(base::Bind( + &GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get()))); + + api_mock_->set_engine(gpu_scheduler_.get()); + + helper_.reset(new CommandBufferHelper(command_buffer_.get())); + helper_->Initialize(kCommandBufferSizeBytes); + } + + virtual void TearDown() { + // If the GpuScheduler posts any tasks, this forces them to run. + base::MessageLoop::current()->RunUntilIdle(); + } + + const CommandParser* GetParser() const { + return gpu_scheduler_->parser(); + } + + // 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, + unsigned int command, + int arg_count, + CommandBufferEntry *args) { + CommandHeader header; + header.size = arg_count + 1; + header.command = command; + CommandBufferEntry* cmds = helper_->GetSpace(arg_count + 1); + CommandBufferOffset put = 0; + cmds[put++].value_header = header; + for (int ii = 0; ii < arg_count; ++ii) { + cmds[put++] = args[ii]; + } + + EXPECT_CALL(*api_mock_, DoCommand(command, arg_count, + Truly(AsyncAPIMock::IsArgs(arg_count, args)))) + .InSequence(sequence_) + .WillOnce(Return(_return)); + } + + // 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(); + CommandBufferOffset parser_get = GetParser()->get(); + CommandBufferOffset limit = put + size; + if (parser_get > parser_put) { + // "busy" buffer wraps, so "free" buffer is between put (inclusive) and + // get (exclusive). + EXPECT_LE(parser_put, put); + EXPECT_GT(parser_get, limit); + } else { + // "busy" buffer does not wrap, so the "free" buffer is the top side (from + // put to the limit) and the bottom side (from 0 to get). + if (put >= parser_put) { + // we're on the top side, check we are below the limit. + EXPECT_GE(kTotalNumCommandEntries, limit); + } else { + // we're on the bottom side, check we are below get. + EXPECT_GT(parser_get, limit); + } + } + } + + int32 GetGetOffset() { + return command_buffer_->GetState().get_offset; + } + + int32 GetPutOffset() { + return command_buffer_->GetState().put_offset; + } + + error::Error GetError() { + return command_buffer_->GetState().error; + } + + CommandBufferOffset get_helper_put() { return helper_->put_; } + +#if defined(OS_MACOSX) + base::mac::ScopedNSAutoreleasePool autorelease_pool_; +#endif + base::MessageLoop message_loop_; + scoped_ptr<AsyncAPIMock> api_mock_; + scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_; + scoped_ptr<CommandBufferService> command_buffer_; + scoped_ptr<GpuScheduler> gpu_scheduler_; + scoped_ptr<CommandBufferHelper> helper_; + Sequence sequence_; +}; + +// Checks that commands in the buffer are properly executed, and that the +// status/error stay valid. +TEST_F(CommandBufferHelperTest, TestCommandProcessing) { + // Check initial state of the engine - it should have been configured by the + // helper. + EXPECT_TRUE(GetParser() != NULL); + EXPECT_EQ(error::kNoError, GetError()); + EXPECT_EQ(0, GetGetOffset()); + + // Add 3 commands through the helper + AddCommandWithExpect(error::kNoError, kUnusedCommandId, 0, NULL); + + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 3; + args1[1].value_float = 4.f; + AddCommandWithExpect(error::kNoError, kUnusedCommandId, 2, args1); + + CommandBufferEntry args2[2]; + args2[0].value_uint32 = 5; + args2[1].value_float = 6.f; + AddCommandWithExpect(error::kNoError, kUnusedCommandId, 2, args2); + + // Wait until it's done. + helper_->Finish(); + // Check that the engine has no more work to do. + EXPECT_TRUE(GetParser()->IsEmpty()); + + // 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 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. + CommandBufferEntry args1[2]; + args1[0].value_uint32 = 5; + args1[1].value_float = 4.f; + + for (unsigned int i = 0; i < 5; ++i) { + AddCommandWithExpect(error::kNoError, kUnusedCommandId + i, 2, args1); + } + + helper_->Finish(); + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + +// 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 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; + } + + for (unsigned int i = 0; i < 5; ++i) { + AddCommandWithExpect( + error::kNoError, i + kUnusedCommandId, kNumArgs, args1); + } + + helper_->Finish(); + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); + + // Check the error status. + EXPECT_EQ(error::kNoError, GetError()); +} + +// Checks that asking for available entries work, and that the parser +// effectively won't use that space. +TEST_F(CommandBufferHelperTest, TestAvailableEntries) { + CommandBufferEntry args[2]; + args[0].value_uint32 = 3; + args[1].value_float = 4.f; + + // Add 2 commands through the helper - 8 entries + AddCommandWithExpect(error::kNoError, kUnusedCommandId + 1, 0, NULL); + AddCommandWithExpect(error::kNoError, kUnusedCommandId + 2, 0, NULL); + AddCommandWithExpect(error::kNoError, kUnusedCommandId + 3, 2, args); + AddCommandWithExpect(error::kNoError, kUnusedCommandId + 4, 2, args); + + // Ask for 5 entries. + helper_->WaitForAvailableEntries(5); + + CommandBufferOffset put = get_helper_put(); + CheckFreeSpace(put, 5); + + // Add more commands. + AddCommandWithExpect(error::kNoError, kUnusedCommandId + 5, 2, args); + + // Wait until everything is done done. + 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 InsertToken/WaitForToken work. +TEST_F(CommandBufferHelperTest, TestToken) { + 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); + // keep track of the buffer position. + CommandBufferOffset command1_put = get_helper_put(); + int32 token = helper_->InsertToken(); + + EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) + .WillOnce(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken), + Return(error::kNoError))); + // Add another command. + AddCommandWithExpect(error::kNoError, kUnusedCommandId + 4, 2, args); + helper_->WaitForToken(token); + // check that the get pointer is beyond the first command. + EXPECT_LE(command1_put, GetGetOffset()); + 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()); + + // Test freeing ring buffer. + helper_->FreeRingBuffer(); + EXPECT_FALSE(helper_->HaveRingBuffer()); + + // Test that InsertToken allocates a new one + int32 token = helper_->InsertToken(); + EXPECT_TRUE(helper_->HaveRingBuffer()); + EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) + .WillOnce(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken), + Return(error::kNoError))); + helper_->WaitForToken(token); + helper_->FreeRingBuffer(); + EXPECT_FALSE(helper_->HaveRingBuffer()); + + // Test that WaitForAvailableEntries allocates a new one + AddCommandWithExpect(error::kNoError, kUnusedCommandId, 0, NULL); + EXPECT_TRUE(helper_->HaveRingBuffer()); + helper_->Finish(); + helper_->FreeRingBuffer(); + EXPECT_FALSE(helper_->HaveRingBuffer()); + + // Check that the commands did happen. + Mock::VerifyAndClearExpectations(api_mock_.get()); +} + +TEST_F(CommandBufferHelperTest, Noop) { + for (int ii = 1; ii < 4; ++ii) { + CommandBufferOffset put_before = get_helper_put(); + helper_->Noop(ii); + CommandBufferOffset put_after = get_helper_put(); + EXPECT_EQ(ii, put_after - put_before); + } +} + +TEST_F(CommandBufferHelperTest, IsContextLost) { + EXPECT_FALSE(helper_->IsContextLost()); + command_buffer_->SetParseError(error::kGenericError); + EXPECT_TRUE(helper_->IsContextLost()); +} + +} // namespace gpu |