#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/message_loop.h"
#include "gpu/command_buffer/client/cmd_buffer_helper.h"
#include "gpu/command_buffer/service/mocks.h"
#include "gpu/command_buffer/service/command_buffer_service.h"
#include "gpu/command_buffer/service/transfer_buffer_manager.h"
#include "gpu/command_buffer/service/gpu_scheduler.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 = 12;
const int32 kUsableNumCommandEntries = 10;
const int32 kCommandBufferSizeBytes =
kTotalNumCommandEntries * sizeof(CommandBufferEntry);
const int32 kUnusedCommandId = 5;
class CommandBufferHelperTest : public testing::Test {
protected:
class DoJumpCommand {
public:
explicit DoJumpCommand(GpuScheduler* gpu_scheduler)
: gpu_scheduler_(gpu_scheduler) {
}
error::Error DoCommand(
unsigned int command,
unsigned int arg_count,
const void* cmd_data) {
const cmd::Jump* jump_cmd = static_cast<const cmd::Jump*>(cmd_data);
gpu_scheduler_->parser()->set_get(jump_cmd->offset);
return error::kNoError;
};
private:
GpuScheduler* gpu_scheduler_;
};
virtual void SetUp() {
api_mock_.reset(new AsyncAPIMock);
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())));
do_jump_command_.reset(new DoJumpCommand(gpu_scheduler_.get()));
EXPECT_CALL(*api_mock_, DoCommand(cmd::kJump, _, _))
.WillRepeatedly(
Invoke(do_jump_command_.get(), &DoJumpCommand::DoCommand));
api_mock_->set_engine(gpu_scheduler_.get());
helper_.reset(new CommandBufferHelper(command_buffer_.get()));
helper_->Initialize(kCommandBufferSizeBytes);
}
virtual void TearDown() {
MessageLoop::current()->RunAllPending();
}
const CommandParser* GetParser() const {
return gpu_scheduler_->parser();
}
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));
}
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) {
EXPECT_LE(parser_put, put);
EXPECT_GT(parser_get, limit);
} else {
if (put >= parser_put) {
EXPECT_GE(kTotalNumCommandEntries, limit);
} else {
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
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_;
scoped_ptr<DoJumpCommand> do_jump_command_;
};
TEST_F(CommandBufferHelperTest, TestCommandProcessing) {
EXPECT_TRUE(GetParser() != NULL);
EXPECT_EQ(error::kNoError, GetError());
EXPECT_EQ(0, GetGetOffset());
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);
helper_->Finish();
EXPECT_TRUE(GetParser()->IsEmpty());
Mock::VerifyAndClearExpectations(api_mock_.get());
EXPECT_EQ(error::kNoError, GetError());
}
TEST_F(CommandBufferHelperTest, TestCommandWrapping) {
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();
Mock::VerifyAndClearExpectations(api_mock_.get());
EXPECT_EQ(error::kNoError, GetError());
}
TEST_F(CommandBufferHelperTest, TestCommandWrappingExactMultiple) {
const int32 kCommandSize = 5;
const size_t kNumArgs = kCommandSize - 1;
COMPILE_ASSERT(kUsableNumCommandEntries % 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();
Mock::VerifyAndClearExpectations(api_mock_.get());
EXPECT_EQ(error::kNoError, GetError());
}
TEST_F(CommandBufferHelperTest, TestAvailableEntries) {
CommandBufferEntry args[2];
args[0].value_uint32 = 3;
args[1].value_float = 4.f;
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);
helper_->WaitForAvailableEntries(5);
CommandBufferOffset put = get_helper_put();
CheckFreeSpace(put, 5);
AddCommandWithExpect(error::kNoError, kUnusedCommandId + 5, 2, args);
helper_->Finish();
Mock::VerifyAndClearExpectations(api_mock_.get());
EXPECT_EQ(error::kNoError, GetError());
}
TEST_F(CommandBufferHelperTest, TestToken) {
CommandBufferEntry args[2];
args[0].value_uint32 = 3;
args[1].value_float = 4.f;
AddCommandWithExpect(error::kNoError, kUnusedCommandId + 3, 2, args);
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)));
AddCommandWithExpect(error::kNoError, kUnusedCommandId + 4, 2, args);
helper_->WaitForToken(token);
EXPECT_LE(command1_put, GetGetOffset());
helper_->Finish();
Mock::VerifyAndClearExpectations(api_mock_.get());
EXPECT_EQ(error::kNoError, GetError());
}
TEST_F(CommandBufferHelperTest, FreeRingBuffer) {
EXPECT_TRUE(helper_->HaveRingBuffer());
helper_->FreeRingBuffer();
EXPECT_FALSE(helper_->HaveRingBuffer());
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());
AddCommandWithExpect(error::kNoError, kUnusedCommandId, 0, NULL);
EXPECT_TRUE(helper_->HaveRingBuffer());
helper_->Finish();
helper_->FreeRingBuffer();
EXPECT_FALSE(helper_->HaveRingBuffer());
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());
}
}