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.
5 // Tests for the Command Buffer Helper.
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"
17 #if defined(OS_MACOSX)
18 #include "base/mac/scoped_nsautorelease_pool.h"
23 using testing::Return;
26 using testing::Sequence;
28 using testing::Invoke;
31 const int32 kTotalNumCommandEntries = 10;
32 const int32 kCommandBufferSizeBytes =
33 kTotalNumCommandEntries * sizeof(CommandBufferEntry);
34 const int32 kUnusedCommandId = 5; // we use 0 and 2 currently.
36 // Override CommandBufferService::Flush() to lock flushing and simulate
37 // the buffer becoming full in asynchronous mode.
38 class CommandBufferServiceLocked : public CommandBufferService {
40 explicit CommandBufferServiceLocked(
41 TransferBufferManagerInterface* transfer_buffer_manager)
42 : CommandBufferService(transfer_buffer_manager),
43 flush_locked_(false) {}
44 virtual ~CommandBufferServiceLocked() {}
46 virtual void Flush(int32 put_offset) OVERRIDE {
48 CommandBufferService::Flush(put_offset);
51 void LockFlush() { flush_locked_ = true; }
53 void UnlockFlush() { flush_locked_ = false; }
57 DISALLOW_COPY_AND_ASSIGN(CommandBufferServiceLocked);
60 // Test fixture for CommandBufferHelper test - Creates a CommandBufferHelper,
61 // using a CommandBufferEngine with a mock AsyncAPIInterface for its interface
62 // (calling it directly, not through the RPC mechanism).
63 class CommandBufferHelperTest : public testing::Test {
65 virtual void SetUp() {
66 api_mock_.reset(new AsyncAPIMock);
67 // ignore noops in the mock - we don't want to inspect the internals of the
69 EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, _, _))
70 .WillRepeatedly(Return(error::kNoError));
73 TransferBufferManager* manager = new TransferBufferManager();
74 transfer_buffer_manager_.reset(manager);
75 EXPECT_TRUE(manager->Initialize());
77 command_buffer_.reset(
78 new CommandBufferServiceLocked(transfer_buffer_manager_.get()));
79 EXPECT_TRUE(command_buffer_->Initialize());
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())));
88 api_mock_->set_engine(gpu_scheduler_.get());
90 helper_.reset(new CommandBufferHelper(command_buffer_.get()));
91 helper_->Initialize(kCommandBufferSizeBytes);
94 virtual void TearDown() {
95 // If the GpuScheduler posts any tasks, this forces them to run.
96 base::MessageLoop::current()->RunUntilIdle();
99 const CommandParser* GetParser() const {
100 return gpu_scheduler_->parser();
103 // Adds a command to the buffer through the helper, while adding it as an
104 // expected call on the API mock.
105 void AddCommandWithExpect(error::Error _return,
106 unsigned int command,
108 CommandBufferEntry *args) {
109 CommandHeader header;
110 header.size = arg_count + 1;
111 header.command = command;
112 CommandBufferEntry* cmds = helper_->GetSpace(arg_count + 1);
113 CommandBufferOffset put = 0;
114 cmds[put++].value_header = header;
115 for (int ii = 0; ii < arg_count; ++ii) {
116 cmds[put++] = args[ii];
119 EXPECT_CALL(*api_mock_, DoCommand(command, arg_count,
120 Truly(AsyncAPIMock::IsArgs(arg_count, args))))
121 .InSequence(sequence_)
122 .WillOnce(Return(_return));
125 void TestCommandWrappingFull(int32 cmd_size, int32 start_commands) {
126 const int32 num_args = cmd_size - 1;
127 EXPECT_EQ(kTotalNumCommandEntries % cmd_size, 0);
129 std::vector<CommandBufferEntry> args(num_args);
130 for (int32 ii = 0; ii < num_args; ++ii) {
131 args[ii].value_uint32 = ii + 1;
134 // Initially insert commands up to start_commands and Finish()
135 for (int32 ii = 0; ii < start_commands; ++ii) {
136 AddCommandWithExpect(
137 error::kNoError, ii + kUnusedCommandId, num_args, &args[0]);
141 EXPECT_EQ(GetParser()->put(),
142 (start_commands * cmd_size) % kTotalNumCommandEntries);
143 EXPECT_EQ(GetParser()->get(),
144 (start_commands * cmd_size) % kTotalNumCommandEntries);
146 // Lock flushing to force the buffer to get full
147 command_buffer_->LockFlush();
149 // Add enough commands to over fill the buffer
150 for (int32 ii = 0; ii < kTotalNumCommandEntries / cmd_size + 2; ++ii) {
151 AddCommandWithExpect(error::kNoError,
152 start_commands + ii + kUnusedCommandId,
157 // Flush all commands
158 command_buffer_->UnlockFlush();
161 // Check that the commands did happen.
162 Mock::VerifyAndClearExpectations(api_mock_.get());
164 // Check the error status.
165 EXPECT_EQ(error::kNoError, GetError());
168 // Checks that the buffer from put to put+size is free in the parser.
169 void CheckFreeSpace(CommandBufferOffset put, unsigned int size) {
170 CommandBufferOffset parser_put = GetParser()->put();
171 CommandBufferOffset parser_get = GetParser()->get();
172 CommandBufferOffset limit = put + size;
173 if (parser_get > parser_put) {
174 // "busy" buffer wraps, so "free" buffer is between put (inclusive) and
176 EXPECT_LE(parser_put, put);
177 EXPECT_GT(parser_get, limit);
179 // "busy" buffer does not wrap, so the "free" buffer is the top side (from
180 // put to the limit) and the bottom side (from 0 to get).
181 if (put >= parser_put) {
182 // we're on the top side, check we are below the limit.
183 EXPECT_GE(kTotalNumCommandEntries, limit);
185 // we're on the bottom side, check we are below get.
186 EXPECT_GT(parser_get, limit);
191 int32 GetGetOffset() {
192 return command_buffer_->GetState().get_offset;
195 int32 GetPutOffset() {
196 return command_buffer_->GetState().put_offset;
199 error::Error GetError() {
200 return command_buffer_->GetState().error;
203 CommandBufferOffset get_helper_put() { return helper_->put_; }
205 #if defined(OS_MACOSX)
206 base::mac::ScopedNSAutoreleasePool autorelease_pool_;
208 base::MessageLoop message_loop_;
209 scoped_ptr<AsyncAPIMock> api_mock_;
210 scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
211 scoped_ptr<CommandBufferServiceLocked> command_buffer_;
212 scoped_ptr<GpuScheduler> gpu_scheduler_;
213 scoped_ptr<CommandBufferHelper> helper_;
217 // Checks that commands in the buffer are properly executed, and that the
218 // status/error stay valid.
219 TEST_F(CommandBufferHelperTest, TestCommandProcessing) {
220 // Check initial state of the engine - it should have been configured by the
222 EXPECT_TRUE(GetParser() != NULL);
223 EXPECT_EQ(error::kNoError, GetError());
224 EXPECT_EQ(0, GetGetOffset());
226 // Add 3 commands through the helper
227 AddCommandWithExpect(error::kNoError, kUnusedCommandId, 0, NULL);
229 CommandBufferEntry args1[2];
230 args1[0].value_uint32 = 3;
231 args1[1].value_float = 4.f;
232 AddCommandWithExpect(error::kNoError, kUnusedCommandId, 2, args1);
234 CommandBufferEntry args2[2];
235 args2[0].value_uint32 = 5;
236 args2[1].value_float = 6.f;
237 AddCommandWithExpect(error::kNoError, kUnusedCommandId, 2, args2);
239 // Wait until it's done.
241 // Check that the engine has no more work to do.
242 EXPECT_TRUE(GetParser()->IsEmpty());
244 // Check that the commands did happen.
245 Mock::VerifyAndClearExpectations(api_mock_.get());
247 // Check the error status.
248 EXPECT_EQ(error::kNoError, GetError());
251 // Checks that commands in the buffer are properly executed when wrapping the
252 // buffer, and that the status/error stay valid.
253 TEST_F(CommandBufferHelperTest, TestCommandWrapping) {
254 // Add 5 commands of size 3 through the helper to make sure we do wrap.
255 CommandBufferEntry args1[2];
256 args1[0].value_uint32 = 5;
257 args1[1].value_float = 4.f;
259 for (unsigned int i = 0; i < 5; ++i) {
260 AddCommandWithExpect(error::kNoError, kUnusedCommandId + i, 2, args1);
264 // Check that the commands did happen.
265 Mock::VerifyAndClearExpectations(api_mock_.get());
267 // Check the error status.
268 EXPECT_EQ(error::kNoError, GetError());
271 // Checks the case where the command inserted exactly matches the space left in
272 // the command buffer.
273 TEST_F(CommandBufferHelperTest, TestCommandWrappingExactMultiple) {
274 const int32 kCommandSize = kTotalNumCommandEntries / 2;
275 const size_t kNumArgs = kCommandSize - 1;
276 COMPILE_ASSERT(kTotalNumCommandEntries % kCommandSize == 0,
277 Not_multiple_of_num_command_entries);
278 CommandBufferEntry args1[kNumArgs];
279 for (size_t ii = 0; ii < kNumArgs; ++ii) {
280 args1[ii].value_uint32 = ii + 1;
283 for (unsigned int i = 0; i < 5; ++i) {
284 AddCommandWithExpect(
285 error::kNoError, i + kUnusedCommandId, kNumArgs, args1);
289 // Check that the commands did happen.
290 Mock::VerifyAndClearExpectations(api_mock_.get());
292 // Check the error status.
293 EXPECT_EQ(error::kNoError, GetError());
296 TEST_F(CommandBufferHelperTest, TestCommandWrappingFullAtStart) {
297 TestCommandWrappingFull(2, 0);
300 TEST_F(CommandBufferHelperTest, TestCommandWrappingFullInMiddle) {
301 TestCommandWrappingFull(2, 1);
304 TEST_F(CommandBufferHelperTest, TestCommandWrappingFullAtEnd) {
305 TestCommandWrappingFull(2, kTotalNumCommandEntries / 2);
308 // Checks that asking for available entries work, and that the parser
309 // effectively won't use that space.
310 TEST_F(CommandBufferHelperTest, TestAvailableEntries) {
311 CommandBufferEntry args[2];
312 args[0].value_uint32 = 3;
313 args[1].value_float = 4.f;
315 // Add 2 commands through the helper - 8 entries
316 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 1, 0, NULL);
317 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 2, 0, NULL);
318 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 3, 2, args);
319 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 4, 2, args);
321 // Ask for 5 entries.
322 helper_->WaitForAvailableEntries(5);
324 CommandBufferOffset put = get_helper_put();
325 CheckFreeSpace(put, 5);
327 // Add more commands.
328 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 5, 2, args);
330 // Wait until everything is done done.
333 // Check that the commands did happen.
334 Mock::VerifyAndClearExpectations(api_mock_.get());
336 // Check the error status.
337 EXPECT_EQ(error::kNoError, GetError());
340 // Checks that the InsertToken/WaitForToken work.
341 TEST_F(CommandBufferHelperTest, TestToken) {
342 CommandBufferEntry args[2];
343 args[0].value_uint32 = 3;
344 args[1].value_float = 4.f;
346 // Add a first command.
347 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 3, 2, args);
348 // keep track of the buffer position.
349 CommandBufferOffset command1_put = get_helper_put();
350 int32 token = helper_->InsertToken();
352 EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _))
353 .WillOnce(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken),
354 Return(error::kNoError)));
355 // Add another command.
356 AddCommandWithExpect(error::kNoError, kUnusedCommandId + 4, 2, args);
357 helper_->WaitForToken(token);
358 // check that the get pointer is beyond the first command.
359 EXPECT_LE(command1_put, GetGetOffset());
362 // Check that the commands did happen.
363 Mock::VerifyAndClearExpectations(api_mock_.get());
365 // Check the error status.
366 EXPECT_EQ(error::kNoError, GetError());
369 TEST_F(CommandBufferHelperTest, FreeRingBuffer) {
370 EXPECT_TRUE(helper_->HaveRingBuffer());
372 // Test freeing ring buffer.
373 helper_->FreeRingBuffer();
374 EXPECT_FALSE(helper_->HaveRingBuffer());
376 // Test that InsertToken allocates a new one
377 int32 token = helper_->InsertToken();
378 EXPECT_TRUE(helper_->HaveRingBuffer());
379 EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _))
380 .WillOnce(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken),
381 Return(error::kNoError)));
382 helper_->WaitForToken(token);
383 helper_->FreeRingBuffer();
384 EXPECT_FALSE(helper_->HaveRingBuffer());
386 // Test that WaitForAvailableEntries allocates a new one
387 AddCommandWithExpect(error::kNoError, kUnusedCommandId, 0, NULL);
388 EXPECT_TRUE(helper_->HaveRingBuffer());
390 helper_->FreeRingBuffer();
391 EXPECT_FALSE(helper_->HaveRingBuffer());
393 // Check that the commands did happen.
394 Mock::VerifyAndClearExpectations(api_mock_.get());
397 TEST_F(CommandBufferHelperTest, Noop) {
398 for (int ii = 1; ii < 4; ++ii) {
399 CommandBufferOffset put_before = get_helper_put();
401 CommandBufferOffset put_after = get_helper_put();
402 EXPECT_EQ(ii, put_after - put_before);
406 TEST_F(CommandBufferHelperTest, IsContextLost) {
407 EXPECT_FALSE(helper_->IsContextLost());
408 command_buffer_->SetParseError(error::kGenericError);
409 EXPECT_TRUE(helper_->IsContextLost());