blob: 77f425bf7ef814d2a8ef11eaedf9ab8b8606b170 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits>
#include <utility>
#include "flutter/testing/testing.h"
#include "impeller/base/validation.h"
#include "impeller/core/allocator.h"
#include "impeller/core/host_buffer.h"
#include "impeller/entity/entity_playground.h"
namespace impeller {
namespace testing {
using HostBufferTest = EntityPlayground;
INSTANTIATE_PLAYGROUND_SUITE(HostBufferTest);
TEST_P(HostBufferTest, CanEmplace) {
struct Length2 {
uint8_t pad[2];
};
static_assert(sizeof(Length2) == 2u);
auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
for (size_t i = 0; i < 12500; i++) {
auto view = buffer->Emplace(Length2{});
ASSERT_TRUE(view);
ASSERT_EQ(view.range, Range(i * sizeof(Length2), 2u));
}
}
TEST_P(HostBufferTest, CanEmplaceWithAlignment) {
struct Length2 {
uint8_t pad[2];
};
static_assert(sizeof(Length2) == 2);
struct alignas(16) Align16 {
uint8_t pad[2];
};
static_assert(alignof(Align16) == 16);
static_assert(sizeof(Align16) == 16);
auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
ASSERT_TRUE(buffer);
{
auto view = buffer->Emplace(Length2{});
ASSERT_TRUE(view);
ASSERT_EQ(view.range, Range(0u, 2u));
}
{
auto view = buffer->Emplace(Align16{});
ASSERT_TRUE(view);
ASSERT_EQ(view.range.offset, 16u);
ASSERT_EQ(view.range.length, 16u);
}
{
auto view = buffer->Emplace(Length2{});
ASSERT_TRUE(view);
ASSERT_EQ(view.range, Range(32u, 2u));
}
{
auto view = buffer->Emplace(Align16{});
ASSERT_TRUE(view);
ASSERT_EQ(view.range.offset, 48u);
ASSERT_EQ(view.range.length, 16u);
}
}
TEST_P(HostBufferTest, HostBufferInitialState) {
auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 1u);
}
TEST_P(HostBufferTest, ResetIncrementsFrameCounter) {
auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
buffer->Reset();
EXPECT_EQ(buffer->GetStateForTest().current_frame, 1u);
buffer->Reset();
EXPECT_EQ(buffer->GetStateForTest().current_frame, 2u);
buffer->Reset();
EXPECT_EQ(buffer->GetStateForTest().current_frame, 3u);
buffer->Reset();
EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
}
TEST_P(HostBufferTest,
EmplacingLargerThanBlockSizeCreatesOneOffBufferCallback) {
auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
// Emplace an amount larger than the block size, to verify that the host
// buffer does not create a buffer.
auto buffer_view = buffer->Emplace(1024000 + 10, 0, [](uint8_t* data) {});
EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 1u);
}
TEST_P(HostBufferTest, EmplacingLargerThanBlockSizeCreatesOneOffBuffer) {
auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
// Emplace an amount larger than the block size, to verify that the host
// buffer does not create a buffer.
auto buffer_view = buffer->Emplace(nullptr, 1024000 + 10, 0);
EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 1u);
}
TEST_P(HostBufferTest, UnusedBuffersAreDiscardedWhenResetting) {
auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
// Emplace two large allocations to force the allocation of a second buffer.
auto buffer_view_a = buffer->Emplace(1020000, 0, [](uint8_t* data) {});
auto buffer_view_b = buffer->Emplace(1020000, 0, [](uint8_t* data) {});
EXPECT_EQ(buffer->GetStateForTest().current_buffer, 1u);
EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 2u);
EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
// Reset until we get back to this frame.
for (auto i = 0; i < 4; i++) {
buffer->Reset();
}
EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 2u);
EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
// Now when we reset, the buffer should get dropped.
// Reset until we get back to this frame.
for (auto i = 0; i < 4; i++) {
buffer->Reset();
}
EXPECT_EQ(buffer->GetStateForTest().current_buffer, 0u);
EXPECT_EQ(buffer->GetStateForTest().total_buffer_count, 1u);
EXPECT_EQ(buffer->GetStateForTest().current_frame, 0u);
}
TEST_P(HostBufferTest, EmplaceWithProcIsAligned) {
auto buffer = HostBuffer::Create(GetContext()->GetResourceAllocator());
BufferView view = buffer->Emplace(std::array<char, 21>());
EXPECT_EQ(view.range, Range(0, 21));
view = buffer->Emplace(64, 16, [](uint8_t*) {});
EXPECT_EQ(view.range, Range(32, 64));
}
static constexpr const size_t kMagicFailingAllocation = 1024000 * 2;
class FailingAllocator : public Allocator {
public:
explicit FailingAllocator(std::shared_ptr<Allocator> delegate)
: Allocator(), delegate_(std::move(delegate)) {}
~FailingAllocator() = default;
std::shared_ptr<DeviceBuffer> OnCreateBuffer(
const DeviceBufferDescriptor& desc) {
// Magic number used in test below to trigger failure.
if (desc.size == kMagicFailingAllocation) {
return nullptr;
}
return delegate_->CreateBuffer(desc);
}
std::shared_ptr<Texture> OnCreateTexture(const TextureDescriptor& desc) {
return delegate_->CreateTexture(desc);
}
ISize GetMaxTextureSizeSupported() const override {
return delegate_->GetMaxTextureSizeSupported();
}
private:
std::shared_ptr<Allocator> delegate_;
};
TEST_P(HostBufferTest, EmplaceWithFailingAllocationDoesntCrash) {
ScopedValidationDisable disable;
std::shared_ptr<FailingAllocator> allocator =
std::make_shared<FailingAllocator>(GetContext()->GetResourceAllocator());
auto buffer = HostBuffer::Create(allocator);
auto view = buffer->Emplace(nullptr, kMagicFailingAllocation, 0);
EXPECT_EQ(view.buffer, nullptr);
EXPECT_EQ(view.range.offset, 0u);
EXPECT_EQ(view.range.length, 0u);
}
} // namespace testing
} // namespace impeller