blob: 369adfd83aa254d97092600ae16ca9a021d0b4a4 [file] [log] [blame]
// Copyright (c) 2012 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.
// Tests for the Command Buffer Helper.
#include "gpu/command_buffer/client/transfer_buffer.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "base/compiler_specific.h"
#include "gpu/command_buffer/client/client_test_helper.h"
#include "gpu/command_buffer/client/cmd_buffer_helper.h"
#include "gpu/command_buffer/common/command_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AtMost;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrictMock;
namespace gpu {
class TransferBufferTest : public testing::Test {
protected:
static const int32_t kNumCommandEntries = 400;
static const int32_t kCommandBufferSizeBytes =
kNumCommandEntries * sizeof(CommandBufferEntry);
static const unsigned int kStartingOffset = 64;
static const unsigned int kAlignment = 4;
static const size_t kTransferBufferSize = 256;
TransferBufferTest()
: transfer_buffer_id_(0) {
}
void SetUp() override;
void TearDown() override;
virtual void Initialize(unsigned int size_to_flush) {
ASSERT_TRUE(transfer_buffer_->Initialize(
kTransferBufferSize,
kStartingOffset,
kTransferBufferSize,
kTransferBufferSize,
kAlignment,
size_to_flush));
}
MockClientCommandBufferMockFlush* command_buffer() const {
return command_buffer_.get();
}
std::unique_ptr<MockClientCommandBufferMockFlush> command_buffer_;
std::unique_ptr<CommandBufferHelper> helper_;
std::unique_ptr<TransferBuffer> transfer_buffer_;
int32_t transfer_buffer_id_;
};
void TransferBufferTest::SetUp() {
command_buffer_.reset(new StrictMock<MockClientCommandBufferMockFlush>());
helper_.reset(new CommandBufferHelper(command_buffer()));
ASSERT_TRUE(helper_->Initialize(kCommandBufferSizeBytes));
transfer_buffer_id_ = command_buffer()->GetNextFreeTransferBufferId();
transfer_buffer_.reset(new TransferBuffer(helper_.get()));
}
void TransferBufferTest::TearDown() {
if (transfer_buffer_->HaveBuffer()) {
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
}
// For command buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
EXPECT_CALL(*command_buffer(), OnFlush()).Times(AtMost(1));
EXPECT_CALL(*command_buffer(), Flush(_)).Times(AtMost(1));
transfer_buffer_.reset();
}
// GCC requires these declarations, but MSVC requires they not be present
#ifndef _MSC_VER
const int32_t TransferBufferTest::kNumCommandEntries;
const int32_t TransferBufferTest::kCommandBufferSizeBytes;
const unsigned int TransferBufferTest::kStartingOffset;
const unsigned int TransferBufferTest::kAlignment;
const size_t TransferBufferTest::kTransferBufferSize;
#endif
TEST_F(TransferBufferTest, Basic) {
Initialize(0);
EXPECT_TRUE(transfer_buffer_->HaveBuffer());
EXPECT_EQ(transfer_buffer_id_, transfer_buffer_->GetShmId());
EXPECT_EQ(
kTransferBufferSize - kStartingOffset,
transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
}
TEST_F(TransferBufferTest, Free) {
Initialize(0);
EXPECT_TRUE(transfer_buffer_->HaveBuffer());
EXPECT_EQ(transfer_buffer_id_, transfer_buffer_->GetShmId());
// Free buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
// See it's freed.
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
// See that it gets reallocated.
EXPECT_EQ(transfer_buffer_id_, transfer_buffer_->GetShmId());
EXPECT_TRUE(transfer_buffer_->HaveBuffer());
// Free buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
// See it's freed.
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
// See that it gets reallocated.
EXPECT_TRUE(transfer_buffer_->GetResultBuffer() != NULL);
EXPECT_TRUE(transfer_buffer_->HaveBuffer());
// Free buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
// See it's freed.
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
// See that it gets reallocated.
unsigned int size = 0;
void* data = transfer_buffer_->AllocUpTo(1, &size);
EXPECT_TRUE(data != NULL);
EXPECT_TRUE(transfer_buffer_->HaveBuffer());
transfer_buffer_->FreePendingToken(data, 1);
// Free buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
// See it's freed.
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
// See that it gets reallocated.
transfer_buffer_->GetResultOffset();
EXPECT_TRUE(transfer_buffer_->HaveBuffer());
EXPECT_EQ(
kTransferBufferSize - kStartingOffset,
transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
// Test freeing twice.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
transfer_buffer_->Free();
}
TEST_F(TransferBufferTest, TooLargeAllocation) {
Initialize(0);
// Check that we can't allocate large than max size.
void* ptr = transfer_buffer_->Alloc(kTransferBufferSize + 1);
EXPECT_TRUE(ptr == NULL);
// Check we if we try to allocate larger than max we get max.
unsigned int size_allocated = 0;
ptr = transfer_buffer_->AllocUpTo(
kTransferBufferSize + 1, &size_allocated);
ASSERT_TRUE(ptr != NULL);
EXPECT_EQ(kTransferBufferSize - kStartingOffset, size_allocated);
transfer_buffer_->FreePendingToken(ptr, 1);
}
TEST_F(TransferBufferTest, MemoryAlignmentAfterZeroAllocation) {
Initialize(32u);
void* ptr = transfer_buffer_->Alloc(0);
EXPECT_EQ((reinterpret_cast<uintptr_t>(ptr) & (kAlignment - 1)), 0u);
transfer_buffer_->FreePendingToken(ptr, static_cast<unsigned int>(-1));
// Check that the pointer is aligned on the following allocation.
ptr = transfer_buffer_->Alloc(4);
EXPECT_EQ((reinterpret_cast<uintptr_t>(ptr) & (kAlignment - 1)), 0u);
transfer_buffer_->FreePendingToken(ptr, 1);
}
TEST_F(TransferBufferTest, Flush) {
Initialize(16u);
unsigned int size_allocated = 0;
for (int i = 0; i < 8; ++i) {
void* ptr = transfer_buffer_->AllocUpTo(8u, &size_allocated);
ASSERT_TRUE(ptr != NULL);
EXPECT_EQ(8u, size_allocated);
if (i % 2) {
EXPECT_CALL(*command_buffer(), Flush(_))
.Times(1)
.RetiresOnSaturation();
}
transfer_buffer_->FreePendingToken(ptr, helper_->InsertToken());
}
for (int i = 0; i < 8; ++i) {
void* ptr = transfer_buffer_->Alloc(8u);
ASSERT_TRUE(ptr != NULL);
if (i % 2) {
EXPECT_CALL(*command_buffer(), Flush(_))
.Times(1)
.RetiresOnSaturation();
}
transfer_buffer_->FreePendingToken(ptr, helper_->InsertToken());
}
}
class MockClientCommandBufferCanFail : public MockClientCommandBufferMockFlush {
public:
MockClientCommandBufferCanFail() {
}
virtual ~MockClientCommandBufferCanFail() {
}
MOCK_METHOD2(CreateTransferBuffer,
scoped_refptr<Buffer>(size_t size, int32_t* id));
scoped_refptr<gpu::Buffer> RealCreateTransferBuffer(size_t size,
int32_t* id) {
return MockCommandBufferBase::CreateTransferBuffer(size, id);
}
};
class TransferBufferExpandContractTest : public testing::Test {
protected:
static const int32_t kNumCommandEntries = 400;
static const int32_t kCommandBufferSizeBytes =
kNumCommandEntries * sizeof(CommandBufferEntry);
static const unsigned int kStartingOffset = 64;
static const unsigned int kAlignment = 4;
static const size_t kStartTransferBufferSize = 256;
static const size_t kMaxTransferBufferSize = 1024;
static const size_t kMinTransferBufferSize = 128;
TransferBufferExpandContractTest()
: transfer_buffer_id_(0) {
}
void SetUp() override;
void TearDown() override;
MockClientCommandBufferCanFail* command_buffer() const {
return command_buffer_.get();
}
std::unique_ptr<MockClientCommandBufferCanFail> command_buffer_;
std::unique_ptr<CommandBufferHelper> helper_;
std::unique_ptr<TransferBuffer> transfer_buffer_;
int32_t transfer_buffer_id_;
};
void TransferBufferExpandContractTest::SetUp() {
command_buffer_.reset(new StrictMock<MockClientCommandBufferCanFail>());
EXPECT_CALL(*command_buffer(),
CreateTransferBuffer(kCommandBufferSizeBytes, _))
.WillOnce(Invoke(
command_buffer(),
&MockClientCommandBufferCanFail::RealCreateTransferBuffer))
.RetiresOnSaturation();
helper_.reset(new CommandBufferHelper(command_buffer()));
ASSERT_TRUE(helper_->Initialize(kCommandBufferSizeBytes));
transfer_buffer_id_ = command_buffer()->GetNextFreeTransferBufferId();
EXPECT_CALL(*command_buffer(),
CreateTransferBuffer(kStartTransferBufferSize, _))
.WillOnce(Invoke(
command_buffer(),
&MockClientCommandBufferCanFail::RealCreateTransferBuffer))
.RetiresOnSaturation();
transfer_buffer_.reset(new TransferBuffer(helper_.get()));
ASSERT_TRUE(transfer_buffer_->Initialize(
kStartTransferBufferSize,
kStartingOffset,
kMinTransferBufferSize,
kMaxTransferBufferSize,
kAlignment,
0));
}
void TransferBufferExpandContractTest::TearDown() {
if (transfer_buffer_->HaveBuffer()) {
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
}
// For command buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_.reset();
}
// GCC requires these declarations, but MSVC requires they not be present
#ifndef _MSC_VER
const int32_t TransferBufferExpandContractTest::kNumCommandEntries;
const int32_t TransferBufferExpandContractTest::kCommandBufferSizeBytes;
const unsigned int TransferBufferExpandContractTest::kStartingOffset;
const unsigned int TransferBufferExpandContractTest::kAlignment;
const size_t TransferBufferExpandContractTest::kStartTransferBufferSize;
const size_t TransferBufferExpandContractTest::kMaxTransferBufferSize;
const size_t TransferBufferExpandContractTest::kMinTransferBufferSize;
#endif
TEST_F(TransferBufferExpandContractTest, Expand) {
// Check it starts at starting size.
EXPECT_EQ(
kStartTransferBufferSize - kStartingOffset,
transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
EXPECT_CALL(*command_buffer(),
CreateTransferBuffer(kStartTransferBufferSize * 2, _))
.WillOnce(Invoke(
command_buffer(),
&MockClientCommandBufferCanFail::RealCreateTransferBuffer))
.RetiresOnSaturation();
// Try next power of 2.
const size_t kSize1 = 512 - kStartingOffset;
unsigned int size_allocated = 0;
void* ptr = transfer_buffer_->AllocUpTo(kSize1, &size_allocated);
ASSERT_TRUE(ptr != NULL);
EXPECT_EQ(kSize1, size_allocated);
EXPECT_EQ(kSize1, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
transfer_buffer_->FreePendingToken(ptr, 1);
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
EXPECT_CALL(*command_buffer(),
CreateTransferBuffer(kMaxTransferBufferSize, _))
.WillOnce(Invoke(
command_buffer(),
&MockClientCommandBufferCanFail::RealCreateTransferBuffer))
.RetiresOnSaturation();
// Try next power of 2.
const size_t kSize2 = 1024 - kStartingOffset;
ptr = transfer_buffer_->AllocUpTo(kSize2, &size_allocated);
ASSERT_TRUE(ptr != NULL);
EXPECT_EQ(kSize2, size_allocated);
EXPECT_EQ(kSize2, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
transfer_buffer_->FreePendingToken(ptr, 1);
// Try next one more. Should not go past max.
size_allocated = 0;
const size_t kSize3 = kSize2 + 1;
ptr = transfer_buffer_->AllocUpTo(kSize3, &size_allocated);
EXPECT_EQ(kSize2, size_allocated);
EXPECT_EQ(kSize2, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
transfer_buffer_->FreePendingToken(ptr, 1);
}
TEST_F(TransferBufferExpandContractTest, Contract) {
// Check it starts at starting size.
EXPECT_EQ(
kStartTransferBufferSize - kStartingOffset,
transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
// Free buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
// See it's freed.
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
// Try to allocate again, fail first request
EXPECT_CALL(*command_buffer(),
CreateTransferBuffer(kStartTransferBufferSize, _))
.WillOnce(
DoAll(SetArgPointee<1>(-1), Return(scoped_refptr<gpu::Buffer>())))
.RetiresOnSaturation();
EXPECT_CALL(*command_buffer(),
CreateTransferBuffer(kMinTransferBufferSize, _))
.WillOnce(Invoke(
command_buffer(),
&MockClientCommandBufferCanFail::RealCreateTransferBuffer))
.RetiresOnSaturation();
const size_t kSize1 = 256 - kStartingOffset;
const size_t kSize2 = 128 - kStartingOffset;
unsigned int size_allocated = 0;
void* ptr = transfer_buffer_->AllocUpTo(kSize1, &size_allocated);
ASSERT_TRUE(ptr != NULL);
EXPECT_EQ(kSize2, size_allocated);
EXPECT_EQ(kSize2, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
transfer_buffer_->FreePendingToken(ptr, 1);
// Free buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
// See it's freed.
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
// Try to allocate again,
EXPECT_CALL(*command_buffer(),
CreateTransferBuffer(kMinTransferBufferSize, _))
.WillOnce(Invoke(
command_buffer(),
&MockClientCommandBufferCanFail::RealCreateTransferBuffer))
.RetiresOnSaturation();
ptr = transfer_buffer_->AllocUpTo(kSize1, &size_allocated);
ASSERT_TRUE(ptr != NULL);
EXPECT_EQ(kSize2, size_allocated);
EXPECT_EQ(kSize2, transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
transfer_buffer_->FreePendingToken(ptr, 1);
}
TEST_F(TransferBufferExpandContractTest, OutOfMemory) {
// Free buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
// See it's freed.
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
// Try to allocate again, fail both requests.
EXPECT_CALL(*command_buffer(), CreateTransferBuffer(_, _))
.WillOnce(
DoAll(SetArgPointee<1>(-1), Return(scoped_refptr<gpu::Buffer>())))
.WillOnce(
DoAll(SetArgPointee<1>(-1), Return(scoped_refptr<gpu::Buffer>())))
.WillOnce(
DoAll(SetArgPointee<1>(-1), Return(scoped_refptr<gpu::Buffer>())))
.RetiresOnSaturation();
const size_t kSize1 = 512 - kStartingOffset;
unsigned int size_allocated = 0;
void* ptr = transfer_buffer_->AllocUpTo(kSize1, &size_allocated);
ASSERT_TRUE(ptr == NULL);
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
}
TEST_F(TransferBufferExpandContractTest, ReallocsToDefault) {
// Free buffer.
EXPECT_CALL(*command_buffer(), DestroyTransferBuffer(_))
.Times(1)
.RetiresOnSaturation();
transfer_buffer_->Free();
// See it's freed.
EXPECT_FALSE(transfer_buffer_->HaveBuffer());
// See that it gets reallocated.
EXPECT_CALL(*command_buffer(),
CreateTransferBuffer(kStartTransferBufferSize, _))
.WillOnce(Invoke(
command_buffer(),
&MockClientCommandBufferCanFail::RealCreateTransferBuffer))
.RetiresOnSaturation();
EXPECT_EQ(transfer_buffer_id_, transfer_buffer_->GetShmId());
EXPECT_TRUE(transfer_buffer_->HaveBuffer());
// Check it's the default size.
EXPECT_EQ(
kStartTransferBufferSize - kStartingOffset,
transfer_buffer_->GetCurrentMaxAllocationWithoutRealloc());
}
} // namespace gpu