1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Tests for the Command Buffer Helper. 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "base/message_loop/message_loop.h" 10 #include "gpu/command_buffer/client/cmd_buffer_helper.h" 11 #include "gpu/command_buffer/service/command_buffer_service.h" 12 #include "gpu/command_buffer/service/gpu_scheduler.h" 13 #include "gpu/command_buffer/service/mocks.h" 14 #include "gpu/command_buffer/service/transfer_buffer_manager.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 17 #if defined(OS_MACOSX) 18 #include "base/mac/scoped_nsautorelease_pool.h" 19 #endif 20 21 namespace gpu { 22 23 using testing::Return; 24 using testing::Mock; 25 using testing::Truly; 26 using testing::Sequence; 27 using testing::DoAll; 28 using testing::Invoke; 29 using testing::_; 30 31 const int32 kTotalNumCommandEntries = 10; 32 const int32 kCommandBufferSizeBytes = 33 kTotalNumCommandEntries * sizeof(CommandBufferEntry); 34 const int32 kUnusedCommandId = 5; // we use 0 and 2 currently. 35 36 // Test fixture for CommandBufferHelper test - Creates a CommandBufferHelper, 37 // using a CommandBufferEngine with a mock AsyncAPIInterface for its interface 38 // (calling it directly, not through the RPC mechanism). 39 class CommandBufferHelperTest : public testing::Test { 40 protected: 41 virtual void SetUp() { 42 api_mock_.reset(new AsyncAPIMock); 43 // ignore noops in the mock - we don't want to inspect the internals of the 44 // helper. 45 EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, _, _)) 46 .WillRepeatedly(Return(error::kNoError)); 47 48 { 49 TransferBufferManager* manager = new TransferBufferManager(); 50 transfer_buffer_manager_.reset(manager); 51 EXPECT_TRUE(manager->Initialize()); 52 } 53 command_buffer_.reset( 54 new CommandBufferService(transfer_buffer_manager_.get())); 55 EXPECT_TRUE(command_buffer_->Initialize()); 56 57 gpu_scheduler_.reset(new GpuScheduler( 58 command_buffer_.get(), api_mock_.get(), NULL)); 59 command_buffer_->SetPutOffsetChangeCallback(base::Bind( 60 &GpuScheduler::PutChanged, base::Unretained(gpu_scheduler_.get()))); 61 command_buffer_->SetGetBufferChangeCallback(base::Bind( 62 &GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get()))); 63 64 api_mock_->set_engine(gpu_scheduler_.get()); 65 66 helper_.reset(new CommandBufferHelper(command_buffer_.get())); 67 helper_->Initialize(kCommandBufferSizeBytes); 68 } 69 70 virtual void TearDown() { 71 // If the GpuScheduler posts any tasks, this forces them to run. 72 base::MessageLoop::current()->RunUntilIdle(); 73 } 74 75 const CommandParser* GetParser() const { 76 return gpu_scheduler_->parser(); 77 } 78 79 // Adds a command to the buffer through the helper, while adding it as an 80 // expected call on the API mock. 81 void AddCommandWithExpect(error::Error _return, 82 unsigned int command, 83 int arg_count, 84 CommandBufferEntry *args) { 85 CommandHeader header; 86 header.size = arg_count + 1; 87 header.command = command; 88 CommandBufferEntry* cmds = helper_->GetSpace(arg_count + 1); 89 CommandBufferOffset put = 0; 90 cmds[put++].value_header = header; 91 for (int ii = 0; ii < arg_count; ++ii) { 92 cmds[put++] = args[ii]; 93 } 94 95 EXPECT_CALL(*api_mock_, DoCommand(command, arg_count, 96 Truly(AsyncAPIMock::IsArgs(arg_count, args)))) 97 .InSequence(sequence_) 98 .WillOnce(Return(_return)); 99 } 100 101 // Checks that the buffer from put to put+size is free in the parser. 102 void CheckFreeSpace(CommandBufferOffset put, unsigned int size) { 103 CommandBufferOffset parser_put = GetParser()->put(); 104 CommandBufferOffset parser_get = GetParser()->get(); 105 CommandBufferOffset limit = put + size; 106 if (parser_get > parser_put) { 107 // "busy" buffer wraps, so "free" buffer is between put (inclusive) and 108 // get (exclusive). 109 EXPECT_LE(parser_put, put); 110 EXPECT_GT(parser_get, limit); 111 } else { 112 // "busy" buffer does not wrap, so the "free" buffer is the top side (from 113 // put to the limit) and the bottom side (from 0 to get). 114 if (put >= parser_put) { 115 // we're on the top side, check we are below the limit. 116 EXPECT_GE(kTotalNumCommandEntries, limit); 117 } else { 118 // we're on the bottom side, check we are below get. 119 EXPECT_GT(parser_get, limit); 120 } 121 } 122 } 123 124 int32 GetGetOffset() { 125 return command_buffer_->GetState().get_offset; 126 } 127 128 int32 GetPutOffset() { 129 return command_buffer_->GetState().put_offset; 130 } 131 132 error::Error GetError() { 133 return command_buffer_->GetState().error; 134 } 135 136 CommandBufferOffset get_helper_put() { return helper_->put_; } 137 138 #if defined(OS_MACOSX) 139 base::mac::ScopedNSAutoreleasePool autorelease_pool_; 140 #endif 141 base::MessageLoop message_loop_; 142 scoped_ptr<AsyncAPIMock> api_mock_; 143 scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_; 144 scoped_ptr<CommandBufferService> command_buffer_; 145 scoped_ptr<GpuScheduler> gpu_scheduler_; 146 scoped_ptr<CommandBufferHelper> helper_; 147 Sequence sequence_; 148 }; 149 150 // Checks that commands in the buffer are properly executed, and that the 151 // status/error stay valid. 152 TEST_F(CommandBufferHelperTest, TestCommandProcessing) { 153 // Check initial state of the engine - it should have been configured by the 154 // helper. 155 EXPECT_TRUE(GetParser() != NULL); 156 EXPECT_EQ(error::kNoError, GetError()); 157 EXPECT_EQ(0, GetGetOffset()); 158 159 // Add 3 commands through the helper 160 AddCommandWithExpect(error::kNoError, kUnusedCommandId, 0, NULL); 161 162 CommandBufferEntry args1[2]; 163 args1[0].value_uint32 = 3; 164 args1[1].value_float = 4.f; 165 AddCommandWithExpect(error::kNoError, kUnusedCommandId, 2, args1); 166 167 CommandBufferEntry args2[2]; 168 args2[0].value_uint32 = 5; 169 args2[1].value_float = 6.f; 170 AddCommandWithExpect(error::kNoError, kUnusedCommandId, 2, args2); 171 172 // Wait until it's done. 173 helper_->Finish(); 174 // Check that the engine has no more work to do. 175 EXPECT_TRUE(GetParser()->IsEmpty()); 176 177 // Check that the commands did happen. 178 Mock::VerifyAndClearExpectations(api_mock_.get()); 179 180 // Check the error status. 181 EXPECT_EQ(error::kNoError, GetError()); 182 } 183 184 // Checks that commands in the buffer are properly executed when wrapping the 185 // buffer, and that the status/error stay valid. 186 TEST_F(CommandBufferHelperTest, TestCommandWrapping) { 187 // Add 5 commands of size 3 through the helper to make sure we do wrap. 188 CommandBufferEntry args1[2]; 189 args1[0].value_uint32 = 5; 190 args1[1].value_float = 4.f; 191 192 for (unsigned int i = 0; i < 5; ++i) { 193 AddCommandWithExpect(error::kNoError, kUnusedCommandId + i, 2, args1); 194 } 195 196 helper_->Finish(); 197 // Check that the commands did happen. 198 Mock::VerifyAndClearExpectations(api_mock_.get()); 199 200 // Check the error status. 201 EXPECT_EQ(error::kNoError, GetError()); 202 } 203 204 // Checks the case where the command inserted exactly matches the space left in 205 // the command buffer. 206 TEST_F(CommandBufferHelperTest, TestCommandWrappingExactMultiple) { 207 const int32 kCommandSize = 5; 208 const size_t kNumArgs = kCommandSize - 1; 209 COMPILE_ASSERT(kTotalNumCommandEntries % kCommandSize == 0, 210 Not_multiple_of_num_command_entries); 211 CommandBufferEntry args1[kNumArgs]; 212 for (size_t ii = 0; ii < kNumArgs; ++ii) { 213 args1[0].value_uint32 = ii + 1; 214 } 215 216 for (unsigned int i = 0; i < 5; ++i) { 217 AddCommandWithExpect( 218 error::kNoError, i + kUnusedCommandId, kNumArgs, args1); 219 } 220 221 helper_->Finish(); 222 // Check that the commands did happen. 223 Mock::VerifyAndClearExpectations(api_mock_.get()); 224 225 // Check the error status. 226 EXPECT_EQ(error::kNoError, GetError()); 227 } 228 229 // Checks that asking for available entries work, and that the parser 230 // effectively won't use that space. 231 TEST_F(CommandBufferHelperTest, TestAvailableEntries) { 232 CommandBufferEntry args[2]; 233 args[0].value_uint32 = 3; 234 args[1].value_float = 4.f; 235 236 // Add 2 commands through the helper - 8 entries 237 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 1, 0, NULL); 238 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 2, 0, NULL); 239 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 3, 2, args); 240 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 4, 2, args); 241 242 // Ask for 5 entries. 243 helper_->WaitForAvailableEntries(5); 244 245 CommandBufferOffset put = get_helper_put(); 246 CheckFreeSpace(put, 5); 247 248 // Add more commands. 249 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 5, 2, args); 250 251 // Wait until everything is done done. 252 helper_->Finish(); 253 254 // Check that the commands did happen. 255 Mock::VerifyAndClearExpectations(api_mock_.get()); 256 257 // Check the error status. 258 EXPECT_EQ(error::kNoError, GetError()); 259 } 260 261 // Checks that the InsertToken/WaitForToken work. 262 TEST_F(CommandBufferHelperTest, TestToken) { 263 CommandBufferEntry args[2]; 264 args[0].value_uint32 = 3; 265 args[1].value_float = 4.f; 266 267 // Add a first command. 268 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 3, 2, args); 269 // keep track of the buffer position. 270 CommandBufferOffset command1_put = get_helper_put(); 271 int32 token = helper_->InsertToken(); 272 273 EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) 274 .WillOnce(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken), 275 Return(error::kNoError))); 276 // Add another command. 277 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 4, 2, args); 278 helper_->WaitForToken(token); 279 // check that the get pointer is beyond the first command. 280 EXPECT_LE(command1_put, GetGetOffset()); 281 helper_->Finish(); 282 283 // Check that the commands did happen. 284 Mock::VerifyAndClearExpectations(api_mock_.get()); 285 286 // Check the error status. 287 EXPECT_EQ(error::kNoError, GetError()); 288 } 289 290 TEST_F(CommandBufferHelperTest, FreeRingBuffer) { 291 EXPECT_TRUE(helper_->HaveRingBuffer()); 292 293 // Test freeing ring buffer. 294 helper_->FreeRingBuffer(); 295 EXPECT_FALSE(helper_->HaveRingBuffer()); 296 297 // Test that InsertToken allocates a new one 298 int32 token = helper_->InsertToken(); 299 EXPECT_TRUE(helper_->HaveRingBuffer()); 300 EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) 301 .WillOnce(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken), 302 Return(error::kNoError))); 303 helper_->WaitForToken(token); 304 helper_->FreeRingBuffer(); 305 EXPECT_FALSE(helper_->HaveRingBuffer()); 306 307 // Test that WaitForAvailableEntries allocates a new one 308 AddCommandWithExpect(error::kNoError, kUnusedCommandId, 0, NULL); 309 EXPECT_TRUE(helper_->HaveRingBuffer()); 310 helper_->Finish(); 311 helper_->FreeRingBuffer(); 312 EXPECT_FALSE(helper_->HaveRingBuffer()); 313 314 // Check that the commands did happen. 315 Mock::VerifyAndClearExpectations(api_mock_.get()); 316 } 317 318 TEST_F(CommandBufferHelperTest, Noop) { 319 for (int ii = 1; ii < 4; ++ii) { 320 CommandBufferOffset put_before = get_helper_put(); 321 helper_->Noop(ii); 322 CommandBufferOffset put_after = get_helper_put(); 323 EXPECT_EQ(ii, put_after - put_before); 324 } 325 } 326 327 TEST_F(CommandBufferHelperTest, IsContextLost) { 328 EXPECT_FALSE(helper_->IsContextLost()); 329 command_buffer_->SetParseError(error::kGenericError); 330 EXPECT_TRUE(helper_->IsContextLost()); 331 } 332 333 } // namespace gpu 334