// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file contains the tests for the RingBuffer class. #include "gpu/command_buffer/client/ring_buffer.h" #include "base/callback.h" #include "base/message_loop.h" #include "base/scoped_nsautorelease_pool.h" #include "gpu/command_buffer/client/cmd_buffer_helper.h" #include "gpu/command_buffer/service/cmd_buffer_engine.h" #include "gpu/command_buffer/service/mocks.h" #include "gpu/command_buffer/service/command_buffer_service.h" #include "gpu/command_buffer/service/gpu_processor.h" #include "testing/gtest/include/gtest/gtest.h" namespace gpu { using testing::Return; using testing::Mock; using testing::Truly; using testing::Sequence; using testing::DoAll; using testing::Invoke; using testing::_; class BaseRingBufferTest : public testing::Test { protected: static const unsigned int kBaseOffset = 128; static const unsigned int kBufferSize = 1024; virtual void SetUp() { api_mock_.reset(new AsyncAPIMock); // ignore noops in the mock - we don't want to inspect the internals of the // helper. EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, 0, _)) .WillRepeatedly(Return(error::kNoError)); // Forward the SetToken calls to the engine EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _)) .WillRepeatedly(DoAll(Invoke(api_mock_.get(), &AsyncAPIMock::SetToken), Return(error::kNoError))); command_buffer_.reset(new CommandBufferService); command_buffer_->Initialize(kBufferSize); Buffer ring_buffer = command_buffer_->GetRingBuffer(); parser_ = new CommandParser(ring_buffer.ptr, ring_buffer.size, 0, ring_buffer.size, 0, api_mock_.get()); gpu_processor_.reset(new GPUProcessor( command_buffer_.get(), NULL, parser_, INT_MAX)); command_buffer_->SetPutOffsetChangeCallback(NewCallback( gpu_processor_.get(), &GPUProcessor::ProcessCommands)); api_mock_->set_engine(gpu_processor_.get()); helper_.reset(new CommandBufferHelper(command_buffer_.get())); helper_->Initialize(kBufferSize); } int32 GetToken() { return command_buffer_->GetState().token; } base::ScopedNSAutoreleasePool autorelease_pool_; MessageLoop message_loop_; scoped_ptr api_mock_; scoped_ptr command_buffer_; scoped_ptr gpu_processor_; CommandParser* parser_; scoped_ptr helper_; }; #ifndef COMPILER_MSVC const unsigned int BaseRingBufferTest::kBaseOffset; const unsigned int BaseRingBufferTest::kBufferSize; #endif // Test fixture for RingBuffer test - Creates a RingBuffer, using a // CommandBufferHelper with a mock AsyncAPIInterface for its interface (calling // it directly, not through the RPC mechanism), making sure Noops are ignored // and SetToken are properly forwarded to the engine. class RingBufferTest : public BaseRingBufferTest { protected: virtual void SetUp() { BaseRingBufferTest::SetUp(); allocator_.reset(new RingBuffer(kBaseOffset, kBufferSize, helper_.get())); } virtual void TearDown() { // If the GPUProcessor posts any tasks, this forces them to run. MessageLoop::current()->RunAllPending(); BaseRingBufferTest::TearDown(); } scoped_ptr allocator_; }; // Checks basic alloc and free. TEST_F(RingBufferTest, TestBasic) { const unsigned int kSize = 16; EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); RingBuffer::Offset offset = allocator_->Alloc(kSize); EXPECT_GE(kBufferSize, offset - kBaseOffset + kSize); EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize()); EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeSizeNoWaiting()); int32 token = helper_->InsertToken(); allocator_->FreePendingToken(offset, token); } // Checks the free-pending-token mechanism. TEST_F(RingBufferTest, TestFreePendingToken) { const unsigned int kSize = 16; const unsigned int kAllocCount = kBufferSize / kSize; CHECK(kAllocCount * kSize == kBufferSize); // Allocate several buffers to fill in the memory. int32 tokens[kAllocCount]; for (unsigned int ii = 0; ii < kAllocCount; ++ii) { RingBuffer::Offset offset = allocator_->Alloc(kSize); EXPECT_GE(kBufferSize, offset - kBaseOffset + kSize); tokens[ii] = helper_->InsertToken(); allocator_->FreePendingToken(offset, tokens[ii]); } EXPECT_EQ(kBufferSize - (kSize * kAllocCount), allocator_->GetLargestFreeSizeNoWaiting()); // This allocation will need to reclaim the space freed above, so that should // process the commands until a token is passed. RingBuffer::Offset offset1 = allocator_->Alloc(kSize); EXPECT_EQ(kBaseOffset, offset1); // Check that the token has indeed passed. EXPECT_LE(tokens[0], GetToken()); allocator_->FreePendingToken(offset1, helper_->InsertToken()); } // Tests GetLargestFreeSizeNoWaiting TEST_F(RingBufferTest, TestGetLargestFreeSizeNoWaiting) { EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting()); RingBuffer::Offset offset = allocator_->Alloc(kBufferSize); EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); allocator_->FreePendingToken(offset, helper_->InsertToken()); } TEST_F(RingBufferTest, TestFreeBug) { // The first and second allocations must not match. const unsigned int kAlloc1 = 10; const unsigned int kAlloc2 = 20; RingBuffer::Offset offset = allocator_->Alloc(kAlloc1); EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting()); allocator_->FreePendingToken(offset, helper_.get()->InsertToken()); offset = allocator_->Alloc(kAlloc2); EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2, allocator_->GetLargestFreeSizeNoWaiting()); allocator_->FreePendingToken(offset, helper_.get()->InsertToken()); offset = allocator_->Alloc(kBufferSize); EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting()); allocator_->FreePendingToken(offset, helper_.get()->InsertToken()); } // Test fixture for RingBufferWrapper test - Creates a // RingBufferWrapper, using a CommandBufferHelper with a mock // AsyncAPIInterface for its interface (calling it directly, not through the // RPC mechanism), making sure Noops are ignored and SetToken are properly // forwarded to the engine. class RingBufferWrapperTest : public BaseRingBufferTest { protected: virtual void SetUp() { BaseRingBufferTest::SetUp(); // Though allocating this buffer isn't strictly necessary, it makes // allocations point to valid addresses, so they could be used for // something. buffer_.reset(new int8[kBufferSize + kBaseOffset]); buffer_start_ = buffer_.get() + kBaseOffset; allocator_.reset(new RingBufferWrapper( kBaseOffset, kBufferSize, helper_.get(), buffer_start_)); } virtual void TearDown() { // If the GPUProcessor posts any tasks, this forces them to run. MessageLoop::current()->RunAllPending(); BaseRingBufferTest::TearDown(); } scoped_ptr allocator_; scoped_array buffer_; int8* buffer_start_; }; // Checks basic alloc and free. TEST_F(RingBufferWrapperTest, TestBasic) { const unsigned int kSize = 16; void* pointer = allocator_->Alloc(kSize); ASSERT_TRUE(pointer); EXPECT_LE(buffer_start_, static_cast(pointer)); EXPECT_GE(kBufferSize, static_cast(pointer) - buffer_start_ + kSize); allocator_->FreePendingToken(pointer, helper_->InsertToken()); int8* pointer_int8 = allocator_->AllocTyped(kSize); ASSERT_TRUE(pointer_int8); EXPECT_LE(buffer_start_, pointer_int8); EXPECT_GE(buffer_start_ + kBufferSize, pointer_int8 + kSize); allocator_->FreePendingToken(pointer_int8, helper_->InsertToken()); unsigned int* pointer_uint = allocator_->AllocTyped(kSize); ASSERT_TRUE(pointer_uint); EXPECT_LE(buffer_start_, reinterpret_cast(pointer_uint)); EXPECT_GE(buffer_start_ + kBufferSize, reinterpret_cast(pointer_uint + kSize)); // Check that it did allocate kSize * sizeof(unsigned int). We can't tell // directly, except from the remaining size. EXPECT_EQ(kBufferSize - kSize - kSize - kSize * sizeof(*pointer_uint), allocator_->GetLargestFreeSizeNoWaiting()); allocator_->FreePendingToken(pointer_uint, helper_->InsertToken()); } // Checks the free-pending-token mechanism. TEST_F(RingBufferWrapperTest, TestFreePendingToken) { const unsigned int kSize = 16; const unsigned int kAllocCount = kBufferSize / kSize; CHECK(kAllocCount * kSize == kBufferSize); // Allocate several buffers to fill in the memory. int32 tokens[kAllocCount]; for (unsigned int ii = 0; ii < kAllocCount; ++ii) { void* pointer = allocator_->Alloc(kSize); EXPECT_TRUE(pointer != NULL); tokens[ii] = helper_->InsertToken(); allocator_->FreePendingToken(pointer, helper_->InsertToken()); } EXPECT_EQ(kBufferSize - (kSize * kAllocCount), allocator_->GetLargestFreeSizeNoWaiting()); // This allocation will need to reclaim the space freed above, so that should // process the commands until the token is passed. void* pointer1 = allocator_->Alloc(kSize); EXPECT_EQ(buffer_start_, static_cast(pointer1)); // Check that the token has indeed passed. EXPECT_LE(tokens[0], GetToken()); allocator_->FreePendingToken(pointer1, helper_->InsertToken()); } } // namespace gpu