mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
646 lines
23 KiB
C++
646 lines
23 KiB
C++
// 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.
|
|
|
|
// This file contains the tests for the FencedAllocator class.
|
|
|
|
#include "base/bind.h"
|
|
#include "base/bind_helpers.h"
|
|
#include "base/memory/aligned_memory.h"
|
|
#include "base/message_loop/message_loop.h"
|
|
#include "gpu/command_buffer/client/cmd_buffer_helper.h"
|
|
#include "gpu/command_buffer/client/fenced_allocator.h"
|
|
#include "gpu/command_buffer/service/cmd_buffer_engine.h"
|
|
#include "gpu/command_buffer/service/command_buffer_service.h"
|
|
#include "gpu/command_buffer/service/gpu_scheduler.h"
|
|
#include "gpu/command_buffer/service/mocks.h"
|
|
#include "gpu/command_buffer/service/transfer_buffer_manager.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
#if defined(OS_MACOSX)
|
|
#include "base/mac/scoped_nsautorelease_pool.h"
|
|
#endif
|
|
|
|
namespace gpu {
|
|
|
|
using testing::Return;
|
|
using testing::Mock;
|
|
using testing::Truly;
|
|
using testing::Sequence;
|
|
using testing::DoAll;
|
|
using testing::Invoke;
|
|
using testing::InvokeWithoutArgs;
|
|
using testing::_;
|
|
|
|
class BaseFencedAllocatorTest : public testing::Test {
|
|
protected:
|
|
static const unsigned int kBufferSize = 1024;
|
|
static const int kAllocAlignment = 16;
|
|
|
|
void SetUp() override {
|
|
api_mock_.reset(new AsyncAPIMock(true));
|
|
// 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)));
|
|
|
|
{
|
|
TransferBufferManager* manager = new TransferBufferManager();
|
|
transfer_buffer_manager_.reset(manager);
|
|
EXPECT_TRUE(manager->Initialize());
|
|
}
|
|
command_buffer_.reset(
|
|
new CommandBufferService(transfer_buffer_manager_.get()));
|
|
EXPECT_TRUE(command_buffer_->Initialize());
|
|
|
|
gpu_scheduler_.reset(new GpuScheduler(
|
|
command_buffer_.get(), api_mock_.get(), NULL));
|
|
command_buffer_->SetPutOffsetChangeCallback(base::Bind(
|
|
&GpuScheduler::PutChanged, base::Unretained(gpu_scheduler_.get())));
|
|
command_buffer_->SetGetBufferChangeCallback(base::Bind(
|
|
&GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get())));
|
|
|
|
api_mock_->set_engine(gpu_scheduler_.get());
|
|
|
|
helper_.reset(new CommandBufferHelper(command_buffer_.get()));
|
|
helper_->Initialize(kBufferSize);
|
|
}
|
|
|
|
int32 GetToken() {
|
|
return command_buffer_->GetLastState().token;
|
|
}
|
|
|
|
#if defined(OS_MACOSX)
|
|
base::mac::ScopedNSAutoreleasePool autorelease_pool_;
|
|
#endif
|
|
base::MessageLoop message_loop_;
|
|
scoped_ptr<AsyncAPIMock> api_mock_;
|
|
scoped_ptr<TransferBufferManagerInterface> transfer_buffer_manager_;
|
|
scoped_ptr<CommandBufferService> command_buffer_;
|
|
scoped_ptr<GpuScheduler> gpu_scheduler_;
|
|
scoped_ptr<CommandBufferHelper> helper_;
|
|
};
|
|
|
|
#ifndef _MSC_VER
|
|
const unsigned int BaseFencedAllocatorTest::kBufferSize;
|
|
#endif
|
|
|
|
namespace {
|
|
void EmptyPoll() {
|
|
}
|
|
}
|
|
|
|
// Test fixture for FencedAllocator test - Creates a FencedAllocator, 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 FencedAllocatorTest : public BaseFencedAllocatorTest {
|
|
protected:
|
|
void SetUp() override {
|
|
BaseFencedAllocatorTest::SetUp();
|
|
allocator_.reset(new FencedAllocator(kBufferSize,
|
|
helper_.get(),
|
|
base::Bind(&EmptyPoll)));
|
|
}
|
|
|
|
void TearDown() override {
|
|
// If the GpuScheduler posts any tasks, this forces them to run.
|
|
base::MessageLoop::current()->RunUntilIdle();
|
|
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
BaseFencedAllocatorTest::TearDown();
|
|
}
|
|
|
|
scoped_ptr<FencedAllocator> allocator_;
|
|
};
|
|
|
|
// Checks basic alloc and free.
|
|
TEST_F(FencedAllocatorTest, TestBasic) {
|
|
allocator_->CheckConsistency();
|
|
EXPECT_FALSE(allocator_->InUse());
|
|
|
|
const unsigned int kSize = 16;
|
|
FencedAllocator::Offset offset = allocator_->Alloc(kSize);
|
|
EXPECT_TRUE(allocator_->InUse());
|
|
EXPECT_NE(FencedAllocator::kInvalidOffset, offset);
|
|
EXPECT_GE(kBufferSize, offset+kSize);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
allocator_->Free(offset);
|
|
EXPECT_FALSE(allocator_->InUse());
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
|
|
// Test alloc 0 fails.
|
|
TEST_F(FencedAllocatorTest, TestAllocZero) {
|
|
FencedAllocator::Offset offset = allocator_->Alloc(0);
|
|
EXPECT_EQ(FencedAllocator::kInvalidOffset, offset);
|
|
EXPECT_FALSE(allocator_->InUse());
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
|
|
// Checks out-of-memory condition.
|
|
TEST_F(FencedAllocatorTest, TestOutOfMemory) {
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
const unsigned int kSize = 16;
|
|
const unsigned int kAllocCount = kBufferSize / kSize;
|
|
CHECK(kAllocCount * kSize == kBufferSize);
|
|
|
|
// Allocate several buffers to fill in the memory.
|
|
FencedAllocator::Offset offsets[kAllocCount];
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
offsets[i] = allocator_->Alloc(kSize);
|
|
EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[i]);
|
|
EXPECT_GE(kBufferSize, offsets[i]+kSize);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
|
|
// This allocation should fail.
|
|
FencedAllocator::Offset offset_failed = allocator_->Alloc(kSize);
|
|
EXPECT_EQ(FencedAllocator::kInvalidOffset, offset_failed);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// Free one successful allocation, reallocate with half the size
|
|
allocator_->Free(offsets[0]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
offsets[0] = allocator_->Alloc(kSize/2);
|
|
EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[0]);
|
|
EXPECT_GE(kBufferSize, offsets[0]+kSize);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// This allocation should fail as well.
|
|
offset_failed = allocator_->Alloc(kSize);
|
|
EXPECT_EQ(FencedAllocator::kInvalidOffset, offset_failed);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// Free up everything.
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
allocator_->Free(offsets[i]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
}
|
|
|
|
// Checks the free-pending-token mechanism.
|
|
TEST_F(FencedAllocatorTest, TestFreePendingToken) {
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
const unsigned int kSize = 16;
|
|
const unsigned int kAllocCount = kBufferSize / kSize;
|
|
CHECK(kAllocCount * kSize == kBufferSize);
|
|
|
|
// Allocate several buffers to fill in the memory.
|
|
FencedAllocator::Offset offsets[kAllocCount];
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
offsets[i] = allocator_->Alloc(kSize);
|
|
EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[i]);
|
|
EXPECT_GE(kBufferSize, offsets[i]+kSize);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
|
|
// This allocation should fail.
|
|
FencedAllocator::Offset offset_failed = allocator_->Alloc(kSize);
|
|
EXPECT_EQ(FencedAllocator::kInvalidOffset, offset_failed);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// Free one successful allocation, pending fence.
|
|
int32 token = helper_.get()->InsertToken();
|
|
allocator_->FreePendingToken(offsets[0], token);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// The way we hooked up the helper and engine, it won't process commands
|
|
// until it has to wait for something. Which means the token shouldn't have
|
|
// passed yet at this point.
|
|
EXPECT_GT(token, GetToken());
|
|
|
|
// This allocation will need to reclaim the space freed above, so that should
|
|
// process the commands until the token is passed.
|
|
offsets[0] = allocator_->Alloc(kSize);
|
|
EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[0]);
|
|
EXPECT_GE(kBufferSize, offsets[0]+kSize);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
// Check that the token has indeed passed.
|
|
EXPECT_LE(token, GetToken());
|
|
|
|
// Free up everything.
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
allocator_->Free(offsets[i]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
}
|
|
|
|
// Checks the free-pending-token mechanism using FreeUnused
|
|
TEST_F(FencedAllocatorTest, FreeUnused) {
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
const unsigned int kSize = 16;
|
|
const unsigned int kAllocCount = kBufferSize / kSize;
|
|
CHECK(kAllocCount * kSize == kBufferSize);
|
|
|
|
// Allocate several buffers to fill in the memory.
|
|
FencedAllocator::Offset offsets[kAllocCount];
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
offsets[i] = allocator_->Alloc(kSize);
|
|
EXPECT_NE(FencedAllocator::kInvalidOffset, offsets[i]);
|
|
EXPECT_GE(kBufferSize, offsets[i]+kSize);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
EXPECT_TRUE(allocator_->InUse());
|
|
|
|
// No memory should be available.
|
|
EXPECT_EQ(0u, allocator_->GetLargestFreeSize());
|
|
|
|
// Free one successful allocation, pending fence.
|
|
int32 token = helper_.get()->InsertToken();
|
|
allocator_->FreePendingToken(offsets[0], token);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// Force the command buffer to process the token.
|
|
helper_->Finish();
|
|
|
|
// Tell the allocator to update what's available based on the current token.
|
|
allocator_->FreeUnused();
|
|
|
|
// Check that the new largest free size takes into account the unused block.
|
|
EXPECT_EQ(kSize, allocator_->GetLargestFreeSize());
|
|
|
|
// Free two more.
|
|
token = helper_.get()->InsertToken();
|
|
allocator_->FreePendingToken(offsets[1], token);
|
|
token = helper_.get()->InsertToken();
|
|
allocator_->FreePendingToken(offsets[2], token);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// Check that nothing has changed.
|
|
EXPECT_EQ(kSize, allocator_->GetLargestFreeSize());
|
|
|
|
// Force the command buffer to process the token.
|
|
helper_->Finish();
|
|
|
|
// Tell the allocator to update what's available based on the current token.
|
|
allocator_->FreeUnused();
|
|
|
|
// Check that the new largest free size takes into account the unused blocks.
|
|
EXPECT_EQ(kSize * 3, allocator_->GetLargestFreeSize());
|
|
EXPECT_TRUE(allocator_->InUse());
|
|
|
|
// Free up everything.
|
|
for (unsigned int i = 3; i < kAllocCount; ++i) {
|
|
allocator_->Free(offsets[i]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
EXPECT_FALSE(allocator_->InUse());
|
|
}
|
|
|
|
// Tests GetLargestFreeSize
|
|
TEST_F(FencedAllocatorTest, TestGetLargestFreeSize) {
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSize());
|
|
|
|
FencedAllocator::Offset offset = allocator_->Alloc(kBufferSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset);
|
|
EXPECT_EQ(0u, allocator_->GetLargestFreeSize());
|
|
allocator_->Free(offset);
|
|
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSize());
|
|
|
|
const unsigned int kSize = 16;
|
|
offset = allocator_->Alloc(kSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset);
|
|
// The following checks that the buffer is allocated "smartly" - which is
|
|
// dependent on the implementation. But both first-fit or best-fit would
|
|
// ensure that.
|
|
EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeSize());
|
|
|
|
// Allocate 2 more buffers (now 3), and then free the first two. This is to
|
|
// ensure a hole. Note that this is dependent on the first-fit current
|
|
// implementation.
|
|
FencedAllocator::Offset offset1 = allocator_->Alloc(kSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset1);
|
|
FencedAllocator::Offset offset2 = allocator_->Alloc(kSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset2);
|
|
allocator_->Free(offset);
|
|
allocator_->Free(offset1);
|
|
EXPECT_EQ(kBufferSize - 3 * kSize, allocator_->GetLargestFreeSize());
|
|
|
|
offset = allocator_->Alloc(kBufferSize - 3 * kSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset);
|
|
EXPECT_EQ(2 * kSize, allocator_->GetLargestFreeSize());
|
|
|
|
offset1 = allocator_->Alloc(2 * kSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset1);
|
|
EXPECT_EQ(0u, allocator_->GetLargestFreeSize());
|
|
|
|
allocator_->Free(offset);
|
|
allocator_->Free(offset1);
|
|
allocator_->Free(offset2);
|
|
}
|
|
|
|
// Tests GetLargestFreeOrPendingSize
|
|
TEST_F(FencedAllocatorTest, TestGetLargestFreeOrPendingSize) {
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize());
|
|
|
|
FencedAllocator::Offset offset = allocator_->Alloc(kBufferSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset);
|
|
EXPECT_EQ(0u, allocator_->GetLargestFreeOrPendingSize());
|
|
allocator_->Free(offset);
|
|
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize());
|
|
|
|
const unsigned int kSize = 16;
|
|
offset = allocator_->Alloc(kSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset);
|
|
// The following checks that the buffer is allocates "smartly" - which is
|
|
// dependent on the implementation. But both first-fit or best-fit would
|
|
// ensure that.
|
|
EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeOrPendingSize());
|
|
|
|
// Allocate 2 more buffers (now 3), and then free the first two. This is to
|
|
// ensure a hole. Note that this is dependent on the first-fit current
|
|
// implementation.
|
|
FencedAllocator::Offset offset1 = allocator_->Alloc(kSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset1);
|
|
FencedAllocator::Offset offset2 = allocator_->Alloc(kSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset2);
|
|
allocator_->Free(offset);
|
|
allocator_->Free(offset1);
|
|
EXPECT_EQ(kBufferSize - 3 * kSize,
|
|
allocator_->GetLargestFreeOrPendingSize());
|
|
|
|
// Free the last one, pending a token.
|
|
int32 token = helper_.get()->InsertToken();
|
|
allocator_->FreePendingToken(offset2, token);
|
|
|
|
// Now all the buffers have been freed...
|
|
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize());
|
|
// .. but one is still waiting for the token.
|
|
EXPECT_EQ(kBufferSize - 3 * kSize,
|
|
allocator_->GetLargestFreeSize());
|
|
|
|
// The way we hooked up the helper and engine, it won't process commands
|
|
// until it has to wait for something. Which means the token shouldn't have
|
|
// passed yet at this point.
|
|
EXPECT_GT(token, GetToken());
|
|
// This allocation will need to reclaim the space freed above, so that should
|
|
// process the commands until the token is passed, but it will succeed.
|
|
offset = allocator_->Alloc(kBufferSize);
|
|
ASSERT_NE(FencedAllocator::kInvalidOffset, offset);
|
|
// Check that the token has indeed passed.
|
|
EXPECT_LE(token, GetToken());
|
|
allocator_->Free(offset);
|
|
|
|
// Everything now has been freed...
|
|
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize());
|
|
// ... for real.
|
|
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSize());
|
|
}
|
|
|
|
class FencedAllocatorPollTest : public BaseFencedAllocatorTest {
|
|
public:
|
|
static const unsigned int kAllocSize = 128;
|
|
|
|
MOCK_METHOD0(MockedPoll, void());
|
|
|
|
protected:
|
|
virtual void TearDown() {
|
|
// If the GpuScheduler posts any tasks, this forces them to run.
|
|
base::MessageLoop::current()->RunUntilIdle();
|
|
|
|
BaseFencedAllocatorTest::TearDown();
|
|
}
|
|
};
|
|
|
|
TEST_F(FencedAllocatorPollTest, TestPoll) {
|
|
scoped_ptr<FencedAllocator> allocator(
|
|
new FencedAllocator(kBufferSize,
|
|
helper_.get(),
|
|
base::Bind(&FencedAllocatorPollTest::MockedPoll,
|
|
base::Unretained(this))));
|
|
|
|
FencedAllocator::Offset mem1 = allocator->Alloc(kAllocSize);
|
|
FencedAllocator::Offset mem2 = allocator->Alloc(kAllocSize);
|
|
EXPECT_NE(mem1, FencedAllocator::kInvalidOffset);
|
|
EXPECT_NE(mem2, FencedAllocator::kInvalidOffset);
|
|
EXPECT_TRUE(allocator->CheckConsistency());
|
|
EXPECT_EQ(allocator->bytes_in_use(), kAllocSize * 2);
|
|
|
|
// Check that no-op Poll doesn't affect the state.
|
|
EXPECT_CALL(*this, MockedPoll()).RetiresOnSaturation();
|
|
allocator->FreeUnused();
|
|
EXPECT_TRUE(allocator->CheckConsistency());
|
|
EXPECT_EQ(allocator->bytes_in_use(), kAllocSize * 2);
|
|
|
|
// Check that freeing in Poll works.
|
|
base::Closure free_mem1_closure =
|
|
base::Bind(&FencedAllocator::Free,
|
|
base::Unretained(allocator.get()),
|
|
mem1);
|
|
EXPECT_CALL(*this, MockedPoll())
|
|
.WillOnce(InvokeWithoutArgs(&free_mem1_closure, &base::Closure::Run))
|
|
.RetiresOnSaturation();
|
|
allocator->FreeUnused();
|
|
EXPECT_TRUE(allocator->CheckConsistency());
|
|
EXPECT_EQ(allocator->bytes_in_use(), kAllocSize * 1);
|
|
|
|
// Check that freeing still works.
|
|
EXPECT_CALL(*this, MockedPoll()).RetiresOnSaturation();
|
|
allocator->Free(mem2);
|
|
allocator->FreeUnused();
|
|
EXPECT_TRUE(allocator->CheckConsistency());
|
|
EXPECT_EQ(allocator->bytes_in_use(), 0u);
|
|
|
|
allocator.reset();
|
|
}
|
|
|
|
// Test fixture for FencedAllocatorWrapper test - Creates a
|
|
// FencedAllocatorWrapper, 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 FencedAllocatorWrapperTest : public BaseFencedAllocatorTest {
|
|
protected:
|
|
void SetUp() override {
|
|
BaseFencedAllocatorTest::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(static_cast<char*>(base::AlignedAlloc(
|
|
kBufferSize, kAllocAlignment)));
|
|
allocator_.reset(new FencedAllocatorWrapper(kBufferSize,
|
|
helper_.get(),
|
|
base::Bind(&EmptyPoll),
|
|
buffer_.get()));
|
|
}
|
|
|
|
void TearDown() override {
|
|
// If the GpuScheduler posts any tasks, this forces them to run.
|
|
base::MessageLoop::current()->RunUntilIdle();
|
|
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
BaseFencedAllocatorTest::TearDown();
|
|
}
|
|
|
|
scoped_ptr<FencedAllocatorWrapper> allocator_;
|
|
scoped_ptr<char, base::AlignedFreeDeleter> buffer_;
|
|
};
|
|
|
|
// Checks basic alloc and free.
|
|
TEST_F(FencedAllocatorWrapperTest, TestBasic) {
|
|
allocator_->CheckConsistency();
|
|
|
|
const unsigned int kSize = 16;
|
|
void *pointer = allocator_->Alloc(kSize);
|
|
ASSERT_TRUE(pointer);
|
|
EXPECT_LE(buffer_.get(), static_cast<char *>(pointer));
|
|
EXPECT_GE(kBufferSize, static_cast<char *>(pointer) - buffer_.get() + kSize);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
allocator_->Free(pointer);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
char *pointer_char = allocator_->AllocTyped<char>(kSize);
|
|
ASSERT_TRUE(pointer_char);
|
|
EXPECT_LE(buffer_.get(), pointer_char);
|
|
EXPECT_GE(buffer_.get() + kBufferSize, pointer_char + kSize);
|
|
allocator_->Free(pointer_char);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
unsigned int *pointer_uint = allocator_->AllocTyped<unsigned int>(kSize);
|
|
ASSERT_TRUE(pointer_uint);
|
|
EXPECT_LE(buffer_.get(), reinterpret_cast<char *>(pointer_uint));
|
|
EXPECT_GE(buffer_.get() + kBufferSize,
|
|
reinterpret_cast<char *>(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 * sizeof(*pointer_uint),
|
|
allocator_->GetLargestFreeSize());
|
|
allocator_->Free(pointer_uint);
|
|
}
|
|
|
|
// Test alloc 0 fails.
|
|
TEST_F(FencedAllocatorWrapperTest, TestAllocZero) {
|
|
allocator_->CheckConsistency();
|
|
|
|
void *pointer = allocator_->Alloc(0);
|
|
ASSERT_FALSE(pointer);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
|
|
// Checks that allocation offsets are aligned to multiples of 16 bytes.
|
|
TEST_F(FencedAllocatorWrapperTest, TestAlignment) {
|
|
allocator_->CheckConsistency();
|
|
|
|
const unsigned int kSize1 = 75;
|
|
void *pointer1 = allocator_->Alloc(kSize1);
|
|
ASSERT_TRUE(pointer1);
|
|
EXPECT_EQ(reinterpret_cast<intptr_t>(pointer1) & (kAllocAlignment - 1), 0);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
const unsigned int kSize2 = 43;
|
|
void *pointer2 = allocator_->Alloc(kSize2);
|
|
ASSERT_TRUE(pointer2);
|
|
EXPECT_EQ(reinterpret_cast<intptr_t>(pointer2) & (kAllocAlignment - 1), 0);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
allocator_->Free(pointer2);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
allocator_->Free(pointer1);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
|
|
// Checks out-of-memory condition.
|
|
TEST_F(FencedAllocatorWrapperTest, TestOutOfMemory) {
|
|
allocator_->CheckConsistency();
|
|
|
|
const unsigned int kSize = 16;
|
|
const unsigned int kAllocCount = kBufferSize / kSize;
|
|
CHECK(kAllocCount * kSize == kBufferSize);
|
|
|
|
// Allocate several buffers to fill in the memory.
|
|
void *pointers[kAllocCount];
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
pointers[i] = allocator_->Alloc(kSize);
|
|
EXPECT_TRUE(pointers[i]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
|
|
// This allocation should fail.
|
|
void *pointer_failed = allocator_->Alloc(kSize);
|
|
EXPECT_FALSE(pointer_failed);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// Free one successful allocation, reallocate with half the size
|
|
allocator_->Free(pointers[0]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
pointers[0] = allocator_->Alloc(kSize/2);
|
|
EXPECT_TRUE(pointers[0]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// This allocation should fail as well.
|
|
pointer_failed = allocator_->Alloc(kSize);
|
|
EXPECT_FALSE(pointer_failed);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// Free up everything.
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
allocator_->Free(pointers[i]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
}
|
|
|
|
// Checks the free-pending-token mechanism.
|
|
TEST_F(FencedAllocatorWrapperTest, TestFreePendingToken) {
|
|
allocator_->CheckConsistency();
|
|
|
|
const unsigned int kSize = 16;
|
|
const unsigned int kAllocCount = kBufferSize / kSize;
|
|
CHECK(kAllocCount * kSize == kBufferSize);
|
|
|
|
// Allocate several buffers to fill in the memory.
|
|
void *pointers[kAllocCount];
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
pointers[i] = allocator_->Alloc(kSize);
|
|
EXPECT_TRUE(pointers[i]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
|
|
// This allocation should fail.
|
|
void *pointer_failed = allocator_->Alloc(kSize);
|
|
EXPECT_FALSE(pointer_failed);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// Free one successful allocation, pending fence.
|
|
int32 token = helper_.get()->InsertToken();
|
|
allocator_->FreePendingToken(pointers[0], token);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
|
|
// The way we hooked up the helper and engine, it won't process commands
|
|
// until it has to wait for something. Which means the token shouldn't have
|
|
// passed yet at this point.
|
|
EXPECT_GT(token, GetToken());
|
|
|
|
// This allocation will need to reclaim the space freed above, so that should
|
|
// process the commands until the token is passed.
|
|
pointers[0] = allocator_->Alloc(kSize);
|
|
EXPECT_TRUE(pointers[0]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
// Check that the token has indeed passed.
|
|
EXPECT_LE(token, GetToken());
|
|
|
|
// Free up everything.
|
|
for (unsigned int i = 0; i < kAllocCount; ++i) {
|
|
allocator_->Free(pointers[i]);
|
|
EXPECT_TRUE(allocator_->CheckConsistency());
|
|
}
|
|
}
|
|
|
|
} // namespace gpu
|