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 static const unsigned int kAlignment = 4; 39 40 void RunPendingSetToken() { 41 for (std::vector<const void*>::iterator it = set_token_arguments_.begin(); 42 it != set_token_arguments_.end(); 43 ++it) { 44 api_mock_->SetToken(cmd::kSetToken, 1, *it); 45 } 46 set_token_arguments_.clear(); 47 delay_set_token_ = false; 48 } 49 50 void SetToken(unsigned int command, 51 unsigned int arg_count, 52 const void* _args) { 53 EXPECT_EQ(static_cast<unsigned int>(cmd::kSetToken), command); 54 EXPECT_EQ(1u, arg_count); 55 if (delay_set_token_) 56 set_token_arguments_.push_back(_args); 57 else 58 api_mock_->SetToken(cmd::kSetToken, 1, _args); 59 } 60 61 virtual void SetUp() { 62 delay_set_token_ = false; 63 api_mock_.reset(new AsyncAPIMock); 64 // ignore noops in the mock - we don't want to inspect the internals of the 65 // helper. 66 EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, 0, _)) 67 .WillRepeatedly(Return(error::kNoError)); 68 // Forward the SetToken calls to the engine 69 EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) 70 .WillRepeatedly(DoAll(Invoke(this, &BaseRingBufferTest::SetToken), 71 Return(error::kNoError))); 72 73 { 74 TransferBufferManager* manager = new TransferBufferManager(); 75 transfer_buffer_manager_.reset(manager); 76 EXPECT_TRUE(manager->Initialize()); 77 } 78 command_buffer_.reset( 79 new CommandBufferService(transfer_buffer_manager_.get())); 80 EXPECT_TRUE(command_buffer_->Initialize()); 81 82 gpu_scheduler_.reset(new GpuScheduler( 83 command_buffer_.get(), api_mock_.get(), NULL)); 84 command_buffer_->SetPutOffsetChangeCallback(base::Bind( 85 &GpuScheduler::PutChanged, base::Unretained(gpu_scheduler_.get()))); 86 command_buffer_->SetGetBufferChangeCallback(base::Bind( 87 &GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get()))); 88 89 api_mock_->set_engine(gpu_scheduler_.get()); 90 91 helper_.reset(new CommandBufferHelper(command_buffer_.get())); 92 helper_->Initialize(kBufferSize); 93 } 94 95 int32 GetToken() { 96 return command_buffer_->GetLastState().token; 97 } 98 99 #if defined(OS_MACOSX) 100 base::mac::ScopedNSAutoreleasePool autorelease_pool_; 101 #endif 102 base::MessageLoop message_loop_; 103 scoped_ptr<AsyncAPIMock> api_mock_; 104 scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_; 105 scoped_ptr<CommandBufferService> command_buffer_; 106 scoped_ptr<GpuScheduler> gpu_scheduler_; 107 scoped_ptr<CommandBufferHelper> helper_; 108 std::vector<const void*> set_token_arguments_; 109 bool delay_set_token_; 110 111 scoped_ptr<int8[]> buffer_; 112 int8* buffer_start_; 113 }; 114 115 #ifndef _MSC_VER 116 const unsigned int BaseRingBufferTest::kBaseOffset; 117 const unsigned int BaseRingBufferTest::kBufferSize; 118 #endif 119 120 // Test fixture for RingBuffer test - Creates a RingBuffer, using a 121 // CommandBufferHelper with a mock AsyncAPIInterface for its interface (calling 122 // it directly, not through the RPC mechanism), making sure Noops are ignored 123 // and SetToken are properly forwarded to the engine. 124 class RingBufferTest : public BaseRingBufferTest { 125 protected: 126 virtual void SetUp() { 127 BaseRingBufferTest::SetUp(); 128 129 buffer_.reset(new int8[kBufferSize + kBaseOffset]); 130 buffer_start_ = buffer_.get() + kBaseOffset; 131 allocator_.reset(new RingBuffer(kAlignment, kBaseOffset, kBufferSize, 132 helper_.get(), buffer_start_)); 133 } 134 135 virtual void TearDown() { 136 // If the GpuScheduler posts any tasks, this forces them to run. 137 base::MessageLoop::current()->RunUntilIdle(); 138 139 BaseRingBufferTest::TearDown(); 140 } 141 142 scoped_ptr<RingBuffer> allocator_; 143 }; 144 145 // Checks basic alloc and free. 146 TEST_F(RingBufferTest, TestBasic) { 147 const unsigned int kSize = 16; 148 EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); 149 EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); 150 void* pointer = allocator_->Alloc(kSize); 151 EXPECT_GE(kBufferSize, allocator_->GetOffset(pointer) - kBaseOffset + kSize); 152 EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); 153 EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeSizeNoWaiting()); 154 int32 token = helper_->InsertToken(); 155 allocator_->FreePendingToken(pointer, token); 156 } 157 158 // Checks the free-pending-token mechanism. 159 TEST_F(RingBufferTest, TestFreePendingToken) { 160 const unsigned int kSize = 16; 161 const unsigned int kAllocCount = kBufferSize / kSize; 162 CHECK(kAllocCount * kSize == kBufferSize); 163 164 delay_set_token_ = true; 165 // Allocate several buffers to fill in the memory. 166 int32 tokens[kAllocCount]; 167 for (unsigned int ii = 0; ii < kAllocCount; ++ii) { 168 void* pointer = allocator_->Alloc(kSize); 169 EXPECT_GE(kBufferSize, 170 allocator_->GetOffset(pointer) - kBaseOffset + kSize); 171 tokens[ii] = helper_->InsertToken(); 172 allocator_->FreePendingToken(pointer, tokens[ii]); 173 } 174 175 EXPECT_EQ(kBufferSize - (kSize * kAllocCount), 176 allocator_->GetLargestFreeSizeNoWaiting()); 177 178 RunPendingSetToken(); 179 180 // This allocation will need to reclaim the space freed above, so that should 181 // process the commands until a token is passed. 182 void* pointer1 = allocator_->Alloc(kSize); 183 EXPECT_EQ(kBaseOffset, allocator_->GetOffset(pointer1)); 184 185 // Check that the token has indeed passed. 186 EXPECT_LE(tokens[0], GetToken()); 187 188 allocator_->FreePendingToken(pointer1, helper_->InsertToken()); 189 } 190 191 // Tests GetLargestFreeSizeNoWaiting 192 TEST_F(RingBufferTest, TestGetLargestFreeSizeNoWaiting) { 193 EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); 194 195 void* pointer = allocator_->Alloc(kBufferSize); 196 EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); 197 allocator_->FreePendingToken(pointer, helper_->InsertToken()); 198 } 199 200 TEST_F(RingBufferTest, TestFreeBug) { 201 // The first and second allocations must not match. 202 const unsigned int kAlloc1 = 3*kAlignment; 203 const unsigned int kAlloc2 = 20; 204 void* pointer = allocator_->Alloc(kAlloc1); 205 EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); 206 allocator_->FreePendingToken(pointer, helper_.get()->InsertToken()); 207 pointer = allocator_->Alloc(kAlloc2); 208 EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, 209 allocator_->GetLargestFreeSizeNoWaiting()); 210 allocator_->FreePendingToken(pointer, helper_.get()->InsertToken()); 211 pointer = allocator_->Alloc(kBufferSize); 212 EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); 213 allocator_->FreePendingToken(pointer, helper_.get()->InsertToken()); 214 } 215 216 } // namespace gpu 217