Home | History | Annotate | Download | only in client
      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