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 // This file contains the tests for the RingBuffer class. 6 7 #include "gpu/command_buffer/client/ring_buffer.h" 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/message_loop/message_loop.h" 12 #include "gpu/command_buffer/client/cmd_buffer_helper.h" 13 #include "gpu/command_buffer/service/cmd_buffer_engine.h" 14 #include "gpu/command_buffer/service/command_buffer_service.h" 15 #include "gpu/command_buffer/service/gpu_scheduler.h" 16 #include "gpu/command_buffer/service/mocks.h" 17 #include "gpu/command_buffer/service/transfer_buffer_manager.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 #if defined(OS_MACOSX) 21 #include "base/mac/scoped_nsautorelease_pool.h" 22 #endif 23 24 namespace gpu { 25 26 using testing::Return; 27 using testing::Mock; 28 using testing::Truly; 29 using testing::Sequence; 30 using testing::DoAll; 31 using testing::Invoke; 32 using testing::_; 33 34 class BaseRingBufferTest : public testing::Test { 35 protected: 36 static const unsigned int kBaseOffset = 128; 37 static const unsigned int kBufferSize = 1024; 38 39 void RunPendingSetToken() { 40 for (std::vector<const void*>::iterator it = set_token_arguments_.begin(); 41 it != set_token_arguments_.end(); 42 ++it) { 43 api_mock_->SetToken(cmd::kSetToken, 1, *it); 44 } 45 set_token_arguments_.clear(); 46 delay_set_token_ = false; 47 } 48 49 void SetToken(unsigned int command, 50 unsigned int arg_count, 51 const void* _args) { 52 EXPECT_EQ(static_cast<unsigned int>(cmd::kSetToken), command); 53 EXPECT_EQ(1u, arg_count); 54 if (delay_set_token_) 55 set_token_arguments_.push_back(_args); 56 else 57 api_mock_->SetToken(cmd::kSetToken, 1, _args); 58 } 59 60 virtual void SetUp() { 61 delay_set_token_ = false; 62 api_mock_.reset(new AsyncAPIMock); 63 // ignore noops in the mock - we don't want to inspect the internals of the 64 // helper. 65 EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, 0, _)) 66 .WillRepeatedly(Return(error::kNoError)); 67 // Forward the SetToken calls to the engine 68 EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) 69 .WillRepeatedly(DoAll(Invoke(this, &BaseRingBufferTest::SetToken), 70 Return(error::kNoError))); 71 72 { 73 TransferBufferManager* manager = new TransferBufferManager(); 74 transfer_buffer_manager_.reset(manager); 75 EXPECT_TRUE(manager->Initialize()); 76 } 77 command_buffer_.reset( 78 new CommandBufferService(transfer_buffer_manager_.get())); 79 EXPECT_TRUE(command_buffer_->Initialize()); 80 81 gpu_scheduler_.reset(new GpuScheduler( 82 command_buffer_.get(), api_mock_.get(), NULL)); 83 command_buffer_->SetPutOffsetChangeCallback(base::Bind( 84 &GpuScheduler::PutChanged, base::Unretained(gpu_scheduler_.get()))); 85 command_buffer_->SetGetBufferChangeCallback(base::Bind( 86 &GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get()))); 87 88 api_mock_->set_engine(gpu_scheduler_.get()); 89 90 helper_.reset(new CommandBufferHelper(command_buffer_.get())); 91 helper_->Initialize(kBufferSize); 92 } 93 94 int32 GetToken() { 95 return command_buffer_->GetState().token; 96 } 97 98 #if defined(OS_MACOSX) 99 base::mac::ScopedNSAutoreleasePool autorelease_pool_; 100 #endif 101 base::MessageLoop message_loop_; 102 scoped_ptr<AsyncAPIMock> api_mock_; 103 scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_; 104 scoped_ptr<CommandBufferService> command_buffer_; 105 scoped_ptr<GpuScheduler> gpu_scheduler_; 106 scoped_ptr<CommandBufferHelper> helper_; 107 std::vector<const void*> set_token_arguments_; 108 bool delay_set_token_; 109 110 }; 111 112 #ifndef _MSC_VER 113 const unsigned int BaseRingBufferTest::kBaseOffset; 114 const unsigned int BaseRingBufferTest::kBufferSize; 115 #endif 116 117 // Test fixture for RingBuffer test - Creates a RingBuffer, using a 118 // CommandBufferHelper with a mock AsyncAPIInterface for its interface (calling 119 // it directly, not through the RPC mechanism), making sure Noops are ignored 120 // and SetToken are properly forwarded to the engine. 121 class RingBufferTest : public BaseRingBufferTest { 122 protected: 123 virtual void SetUp() { 124 BaseRingBufferTest::SetUp(); 125 allocator_.reset(new RingBuffer(kBaseOffset, kBufferSize, helper_.get())); 126 } 127 128 virtual void TearDown() { 129 // If the GpuScheduler posts any tasks, this forces them to run. 130 base::MessageLoop::current()->RunUntilIdle(); 131 132 BaseRingBufferTest::TearDown(); 133 } 134 135 scoped_ptr<RingBuffer> allocator_; 136 }; 137 138 // Checks basic alloc and free. 139 TEST_F(RingBufferTest, TestBasic) { 140 const unsigned int kSize = 16; 141 EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); 142 EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); 143 RingBuffer::Offset offset = allocator_->Alloc(kSize); 144 EXPECT_GE(kBufferSize, offset - kBaseOffset + kSize); 145 EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); 146 EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeSizeNoWaiting()); 147 int32 token = helper_->InsertToken(); 148 allocator_->FreePendingToken(offset, token); 149 } 150 151 // Checks the free-pending-token mechanism. 152 TEST_F(RingBufferTest, TestFreePendingToken) { 153 const unsigned int kSize = 16; 154 const unsigned int kAllocCount = kBufferSize / kSize; 155 CHECK(kAllocCount * kSize == kBufferSize); 156 157 delay_set_token_ = true; 158 // Allocate several buffers to fill in the memory. 159 int32 tokens[kAllocCount]; 160 for (unsigned int ii = 0; ii < kAllocCount; ++ii) { 161 RingBuffer::Offset offset = allocator_->Alloc(kSize); 162 EXPECT_GE(kBufferSize, offset - kBaseOffset + kSize); 163 tokens[ii] = helper_->InsertToken(); 164 allocator_->FreePendingToken(offset, tokens[ii]); 165 } 166 167 EXPECT_EQ(kBufferSize - (kSize * kAllocCount), 168 allocator_->GetLargestFreeSizeNoWaiting()); 169 170 RunPendingSetToken(); 171 172 // This allocation will need to reclaim the space freed above, so that should 173 // process the commands until a token is passed. 174 RingBuffer::Offset offset1 = allocator_->Alloc(kSize); 175 EXPECT_EQ(kBaseOffset, offset1); 176 177 // Check that the token has indeed passed. 178 EXPECT_LE(tokens[0], GetToken()); 179 180 allocator_->FreePendingToken(offset1, helper_->InsertToken()); 181 } 182 183 // Tests GetLargestFreeSizeNoWaiting 184 TEST_F(RingBufferTest, TestGetLargestFreeSizeNoWaiting) { 185 EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); 186 187 RingBuffer::Offset offset = allocator_->Alloc(kBufferSize); 188 EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); 189 allocator_->FreePendingToken(offset, helper_->InsertToken()); 190 } 191 192 TEST_F(RingBufferTest, TestFreeBug) { 193 // The first and second allocations must not match. 194 const unsigned int kAlloc1 = 10; 195 const unsigned int kAlloc2 = 20; 196 RingBuffer::Offset offset = allocator_->Alloc(kAlloc1); 197 EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); 198 allocator_->FreePendingToken(offset, helper_.get()->InsertToken()); 199 offset = allocator_->Alloc(kAlloc2); 200 EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, 201 allocator_->GetLargestFreeSizeNoWaiting()); 202 allocator_->FreePendingToken(offset, helper_.get()->InsertToken()); 203 offset = allocator_->Alloc(kBufferSize); 204 EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); 205 allocator_->FreePendingToken(offset, helper_.get()->InsertToken()); 206 } 207 208 // Test fixture for RingBufferWrapper test - Creates a 209 // RingBufferWrapper, using a CommandBufferHelper with a mock 210 // AsyncAPIInterface for its interface (calling it directly, not through the 211 // RPC mechanism), making sure Noops are ignored and SetToken are properly 212 // forwarded to the engine. 213 class RingBufferWrapperTest : public BaseRingBufferTest { 214 protected: 215 virtual void SetUp() { 216 BaseRingBufferTest::SetUp(); 217 218 // Though allocating this buffer isn't strictly necessary, it makes 219 // allocations point to valid addresses, so they could be used for 220 // something. 221 buffer_.reset(new int8[kBufferSize + kBaseOffset]); 222 buffer_start_ = buffer_.get() + kBaseOffset; 223 allocator_.reset(new RingBufferWrapper( 224 kBaseOffset, kBufferSize, helper_.get(), buffer_start_)); 225 } 226 227 virtual void TearDown() { 228 // If the GpuScheduler posts any tasks, this forces them to run. 229 base::MessageLoop::current()->RunUntilIdle(); 230 231 BaseRingBufferTest::TearDown(); 232 } 233 234 scoped_ptr<RingBufferWrapper> allocator_; 235 scoped_ptr<int8[]> buffer_; 236 int8* buffer_start_; 237 }; 238 239 // Checks basic alloc and free. 240 TEST_F(RingBufferWrapperTest, TestBasic) { 241 const unsigned int kSize = 16; 242 void* pointer = allocator_->Alloc(kSize); 243 ASSERT_TRUE(pointer); 244 EXPECT_LE(buffer_start_, static_cast<int8*>(pointer)); 245 EXPECT_GE(kBufferSize, static_cast<int8*>(pointer) - buffer_start_ + kSize); 246 247 allocator_->FreePendingToken(pointer, helper_->InsertToken()); 248 249 int8* pointer_int8 = allocator_->AllocTyped<int8>(kSize); 250 ASSERT_TRUE(pointer_int8); 251 EXPECT_LE(buffer_start_, pointer_int8); 252 EXPECT_GE(buffer_start_ + kBufferSize, pointer_int8 + kSize); 253 allocator_->FreePendingToken(pointer_int8, helper_->InsertToken()); 254 255 unsigned int* pointer_uint = allocator_->AllocTyped<unsigned int>(kSize); 256 ASSERT_TRUE(pointer_uint); 257 EXPECT_LE(buffer_start_, reinterpret_cast<int8*>(pointer_uint)); 258 EXPECT_GE(buffer_start_ + kBufferSize, 259 reinterpret_cast<int8* >(pointer_uint + kSize)); 260 261 // Check that it did allocate kSize * sizeof(unsigned int). We can't tell 262 // directly, except from the remaining size. 263 EXPECT_EQ(kBufferSize - kSize - kSize - kSize * sizeof(*pointer_uint), 264 allocator_->GetLargestFreeSizeNoWaiting()); 265 allocator_->FreePendingToken(pointer_uint, helper_->InsertToken()); 266 } 267 268 // Checks the free-pending-token mechanism. 269 TEST_F(RingBufferWrapperTest, TestFreePendingToken) { 270 const unsigned int kSize = 16; 271 const unsigned int kAllocCount = kBufferSize / kSize; 272 CHECK(kAllocCount * kSize == kBufferSize); 273 274 delay_set_token_ = true; 275 // Allocate several buffers to fill in the memory. 276 int32 tokens[kAllocCount]; 277 for (unsigned int ii = 0; ii < kAllocCount; ++ii) { 278 void* pointer = allocator_->Alloc(kSize); 279 EXPECT_TRUE(pointer != NULL); 280 tokens[ii] = helper_->InsertToken(); 281 allocator_->FreePendingToken(pointer, helper_->InsertToken()); 282 } 283 284 EXPECT_EQ(kBufferSize - (kSize * kAllocCount), 285 allocator_->GetLargestFreeSizeNoWaiting()); 286 287 RunPendingSetToken(); 288 289 // This allocation will need to reclaim the space freed above, so that should 290 // process the commands until the token is passed. 291 void* pointer1 = allocator_->Alloc(kSize); 292 EXPECT_EQ(buffer_start_, static_cast<int8*>(pointer1)); 293 294 // Check that the token has indeed passed. 295 EXPECT_LE(tokens[0], GetToken()); 296 297 allocator_->FreePendingToken(pointer1, helper_->InsertToken()); 298 EXPECT_LE(command_buffer_->GetState().token, helper_->InsertToken()); 299 } 300 301 } // namespace gpu 302