#include "gpu/command_buffer/client/ring_buffer.h"
#include <stdint.h>
#include <memory>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "gpu/command_buffer/client/cmd_buffer_helper.h"
#include "gpu/command_buffer/service/command_buffer_direct.h"
#include "gpu/command_buffer/service/mocks.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;
static const unsigned int kAlignment = 4;
void RunPendingSetToken() {
for (std::vector<const volatile void*>::iterator it =
set_token_arguments_.begin();
it != set_token_arguments_.end(); ++it) {
api_mock_->SetToken(cmd::kSetToken, 1, *it);
}
set_token_arguments_.clear();
delay_set_token_ = false;
}
void SetToken(unsigned int command,
unsigned int arg_count,
const volatile void* _args) {
EXPECT_EQ(static_cast<unsigned int>(cmd::kSetToken), command);
EXPECT_EQ(1u, arg_count);
if (delay_set_token_)
set_token_arguments_.push_back(_args);
else
api_mock_->SetToken(cmd::kSetToken, 1, _args);
}
void SetUp() override {
delay_set_token_ = false;
command_buffer_ = std::make_unique<CommandBufferDirect>();
api_mock_ = std::make_unique<AsyncAPIMock>(true, command_buffer_.get(),
command_buffer_->service());
EXPECT_CALL(*api_mock_, DoCommand(cmd::kNoop, 0, _))
.WillRepeatedly(Return(error::kNoError));
EXPECT_CALL(*api_mock_.get(), DoCommand(cmd::kSetToken, 1, _))
.WillRepeatedly(DoAll(Invoke(this, &BaseRingBufferTest::SetToken),
Return(error::kNoError)));
helper_.reset(new CommandBufferHelper(command_buffer_.get()));
helper_->Initialize(kBufferSize);
}
int32_t GetToken() { return command_buffer_->GetLastState().token; }
std::unique_ptr<CommandBufferDirect> command_buffer_;
std::unique_ptr<AsyncAPIMock> api_mock_;
std::unique_ptr<CommandBufferHelper> helper_;
std::vector<const volatile void*> set_token_arguments_;
bool delay_set_token_;
std::unique_ptr<int8_t[]> buffer_;
raw_ptr<int8_t> buffer_start_;
base::test::SingleThreadTaskEnvironment task_environment_;
};
const unsigned int BaseRingBufferTest::kBaseOffset;
const unsigned int BaseRingBufferTest::kBufferSize;
class RingBufferTest : public BaseRingBufferTest {
protected:
void SetUp() override {
BaseRingBufferTest::SetUp();
buffer_.reset(new int8_t[kBufferSize + kBaseOffset]);
buffer_start_ = buffer_.get() + kBaseOffset;
allocator_.reset(new RingBuffer(kAlignment, kBaseOffset, kBufferSize,
helper_.get(), buffer_start_));
}
void TearDown() override {
base::RunLoop().RunUntilIdle();
BaseRingBufferTest::TearDown();
}
std::unique_ptr<RingBuffer> allocator_;
};
TEST_F(RingBufferTest, TestBasic) {
const unsigned int kSize = 16;
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize());
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
void* pointer = allocator_->Alloc(kSize);
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
EXPECT_GE(kBufferSize, allocator_->GetOffset(pointer) - kBaseOffset + kSize);
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeOrPendingSize());
EXPECT_EQ(kBufferSize - kSize, allocator_->GetLargestFreeSizeNoWaiting());
int32_t token = helper_->InsertToken();
allocator_->FreePendingToken(pointer, token);
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
}
TEST_F(RingBufferTest, TestCanAllocGetLargestFreeOrPendingSize) {
buffer_.reset(new int8_t[kBufferSize + 2 + kBaseOffset]);
buffer_start_ = buffer_.get() + kBaseOffset;
allocator_.reset(new RingBuffer(kAlignment, kBaseOffset, kBufferSize + 2,
helper_.get(), buffer_start_));
void* pointer = allocator_->Alloc(allocator_->GetLargestFreeOrPendingSize());
allocator_->FreePendingToken(pointer, helper_->InsertToken());
}
TEST_F(RingBufferTest, TestFreePendingToken) {
const unsigned int kSize = 16;
const unsigned int kAllocCount = kBufferSize / kSize;
CHECK(kAllocCount * kSize == kBufferSize);
delay_set_token_ = true;
int32_t tokens[kAllocCount];
for (unsigned int ii = 0; ii < kAllocCount; ++ii) {
void* pointer = allocator_->Alloc(kSize);
EXPECT_GE(kBufferSize,
allocator_->GetOffset(pointer) - kBaseOffset + kSize);
tokens[ii] = helper_->InsertToken();
allocator_->FreePendingToken(pointer, tokens[ii]);
}
EXPECT_EQ(kBufferSize - (kSize * kAllocCount),
allocator_->GetLargestFreeSizeNoWaiting());
RunPendingSetToken();
void* pointer1 = allocator_->Alloc(kSize);
EXPECT_EQ(kBaseOffset, allocator_->GetOffset(pointer1));
EXPECT_LE(tokens[0], GetToken());
allocator_->FreePendingToken(pointer1, helper_->InsertToken());
}
TEST_F(RingBufferTest, TestGetLargestFreeSizeNoWaiting) {
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting());
void* pointer = allocator_->Alloc(kBufferSize);
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(pointer, helper_->InsertToken());
}
TEST_F(RingBufferTest, TestFreeBug) {
const unsigned int kAlloc1 = 3*kAlignment;
const unsigned int kAlloc2 = 20;
void* pointer = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(pointer, helper_.get()->InsertToken());
pointer = allocator_->Alloc(kAlloc2);
EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2,
allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(pointer, helper_.get()->InsertToken());
pointer = allocator_->Alloc(kBufferSize);
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(pointer, helper_.get()->InsertToken());
}
TEST_F(RingBufferTest, DiscardTest) {
const unsigned int kAlloc1 = 3*kAlignment;
void* ptr = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
allocator_->DiscardBlock(ptr);
EXPECT_EQ(kBufferSize, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
}
TEST_F(RingBufferTest, DiscardFrontTest) {
const unsigned int kAlloc1 = 3*kAlignment;
const unsigned int kAlloc2 = 2*kAlignment;
const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2;
void* ptr1 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
void* ptr2 = allocator_->Alloc(kAlloc2);
EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1,
static_cast<uint8_t*>(ptr2));
EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2,
allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
void* ptr3 = allocator_->Alloc(kAlloc3);
EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2,
static_cast<uint8_t*>(ptr3));
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
allocator_->DiscardBlock(ptr1);
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
EXPECT_EQ(kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
}
TEST_F(RingBufferTest, DiscardMiddleTest) {
const unsigned int kAlloc1 = 3*kAlignment;
const unsigned int kAlloc2 = 2*kAlignment;
const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2;
void* ptr1 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken());
void* ptr2 = allocator_->Alloc(kAlloc2);
EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1,
static_cast<uint8_t*>(ptr2));
EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2,
allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken());
void* ptr3 = allocator_->Alloc(kAlloc3);
EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2,
static_cast<uint8_t*>(ptr3));
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
allocator_->DiscardBlock(ptr2);
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
}
TEST_F(RingBufferTest, DiscardEndTest) {
const unsigned int kAlloc1 = 3*kAlignment;
const unsigned int kAlloc2 = 2*kAlignment;
const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2;
void* ptr1 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken());
void* ptr2 = allocator_->Alloc(kAlloc2);
EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1,
static_cast<uint8_t*>(ptr2));
EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2,
allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken());
void* ptr3 = allocator_->Alloc(kAlloc3);
EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2,
static_cast<uint8_t*>(ptr3));
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
allocator_->DiscardBlock(ptr3);
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
EXPECT_EQ(kAlloc3, allocator_->GetLargestFreeSizeNoWaiting());
}
TEST_F(RingBufferTest, DiscardLoopedEndTest) {
const unsigned int kAlloc1 = 3*kAlignment;
const unsigned int kAlloc2 = 2*kAlignment;
const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2;
void* ptr1 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken());
void* ptr2 = allocator_->Alloc(kAlloc2);
EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1,
static_cast<uint8_t*>(ptr2));
EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2,
allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken());
void* ptr3 = allocator_->Alloc(kAlloc3);
EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2,
static_cast<uint8_t*>(ptr3));
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken());
allocator_->DiscardBlock(ptr1);
void* ptr4 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(ptr1, ptr4);
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
allocator_->DiscardBlock(ptr4);
EXPECT_EQ(kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
}
TEST_F(RingBufferTest, DiscardEndWithPaddingTest) {
const unsigned int kAlloc1 = 3*kAlignment;
const unsigned int kAlloc2 = 2*kAlignment;
const unsigned int kPadding = kAlignment;
const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2 - kPadding;
void* ptr1 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken());
void* ptr2 = allocator_->Alloc(kAlloc2);
EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1,
static_cast<uint8_t*>(ptr2));
EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2,
allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken());
void* ptr3 = allocator_->Alloc(kAlloc3);
EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2,
static_cast<uint8_t*>(ptr3));
EXPECT_EQ(kPadding, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken());
allocator_->DiscardBlock(ptr1);
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
void* ptr4 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(ptr1, ptr4);
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
allocator_->DiscardBlock(ptr4);
EXPECT_EQ(kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
void* padding = allocator_->Alloc(kPadding);
EXPECT_EQ(kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(padding, helper_.get()->InsertToken());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
}
TEST_F(RingBufferTest, DiscardAllPaddingFromEndTest) {
const unsigned int kAlloc1 = 3*kAlignment;
const unsigned int kAlloc2 = 2*kAlignment;
const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2;
void* ptr1 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken());
void* ptr2 = allocator_->Alloc(kAlloc2);
EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1,
static_cast<uint8_t*>(ptr2));
EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2,
allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken());
void* ptr3 = allocator_->Alloc(kAlloc3);
EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2,
static_cast<uint8_t*>(ptr3));
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->DiscardBlock(ptr2);
EXPECT_EQ(allocator_->NumUsedBlocks(), 1u);
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->DiscardBlock(ptr3);
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
EXPECT_EQ(kAlloc2 + kAlloc3, allocator_->GetLargestFreeSizeNoWaiting());
}
TEST_F(RingBufferTest, DiscardAllPaddingFromBeginningTest) {
const unsigned int kAlloc1 = 3*kAlignment;
const unsigned int kAlloc2 = 2*kAlignment;
const unsigned int kAlloc3 = kBufferSize - kAlloc1 - kAlloc2;
void* ptr1 = allocator_->Alloc(kAlloc1);
EXPECT_EQ(kBufferSize - kAlloc1, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr1, helper_.get()->InsertToken());
void* ptr2 = allocator_->Alloc(kAlloc2);
EXPECT_EQ(static_cast<uint8_t*>(ptr1) + kAlloc1,
static_cast<uint8_t*>(ptr2));
EXPECT_EQ(kBufferSize - kAlloc1 - kAlloc2,
allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr2, helper_.get()->InsertToken());
void* ptr3 = allocator_->Alloc(kAlloc3);
EXPECT_EQ(static_cast<uint8_t*>(ptr2) + kAlloc2,
static_cast<uint8_t*>(ptr3));
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr3, helper_.get()->InsertToken());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
allocator_->DiscardBlock(ptr2);
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->DiscardBlock(ptr1);
EXPECT_EQ(kAlloc1 + kAlloc2, allocator_->GetLargestFreeSizeNoWaiting());
EXPECT_EQ(allocator_->NumUsedBlocks(), 0u);
}
TEST_F(RingBufferTest, LargestFreeSizeNoWaiting) {
void* ptr = allocator_->Alloc(kBufferSize);
EXPECT_EQ(0u, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->ShrinkLastBlock(kBufferSize - 2 * kAlignment - 1);
EXPECT_EQ(2 * kAlignment, allocator_->GetLargestFreeSizeNoWaiting());
allocator_->FreePendingToken(ptr, helper_.get()->InsertToken());
}
}