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
24namespace gpu {
25
26using testing::Return;
27using testing::Mock;
28using testing::Truly;
29using testing::Sequence;
30using testing::DoAll;
31using testing::Invoke;
32using testing::_;
33
34class 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(true));
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
116const unsigned int BaseRingBufferTest::kBaseOffset;
117const 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.
124class 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.
146TEST_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.
159TEST_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
192TEST_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
200TEST_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