blob: 8749f52af4daad48703506dd5ad31be954c7ce34 [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 "flutter/testing/testing.h" // IWYU pragma: keep.
#include "fml/synchronization/waitable_event.h"
#include "impeller/renderer/backend/vulkan/descriptor_pool_vk.h"
#include "impeller/renderer/backend/vulkan/resource_manager_vk.h"
#include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
namespace impeller {
namespace testing {
TEST(DescriptorPoolRecyclerVKTest, GetDescriptorPoolRecyclerCreatesNewPools) {
auto const context = MockVulkanContextBuilder().Build();
auto const [pool1, _] = context->GetDescriptorPoolRecycler()->Get(1024);
auto const [pool2, __] = context->GetDescriptorPoolRecycler()->Get(1024);
// The two descriptor pools should be different.
EXPECT_NE(pool1.get(), pool2.get());
context->Shutdown();
}
TEST(DescriptorPoolRecyclerVKTest, DescriptorPoolCapacityIsRoundedUp) {
auto const context = MockVulkanContextBuilder().Build();
auto const [pool1, capacity] = context->GetDescriptorPoolRecycler()->Get(1);
// Rounds up to a minimum of 64.
EXPECT_EQ(capacity, 64u);
auto const [pool2, capacity_2] =
context->GetDescriptorPoolRecycler()->Get(1023);
// Rounds up to the next power of two.
EXPECT_EQ(capacity_2, 1024u);
context->Shutdown();
}
namespace {
// Invokes the provided callback when the destructor is called.
//
// Can be moved, but not copied.
class DeathRattle final {
public:
explicit DeathRattle(std::function<void()> callback)
: callback_(std::move(callback)) {}
DeathRattle(DeathRattle&&) = default;
DeathRattle& operator=(DeathRattle&&) = default;
~DeathRattle() { callback_(); }
private:
std::function<void()> callback_;
};
} // namespace
TEST(DescriptorPoolRecyclerVKTest, ReclaimMakesDescriptorPoolAvailable) {
auto const context = MockVulkanContextBuilder().Build();
{
// Fetch a pool (which will be created).
auto pool = DescriptorPoolVK(context);
pool.AllocateDescriptorSets(1024, 1024, 1024, {});
}
// There is a chance that the first death rattle item below is destroyed in
// the same reclaim cycle as the pool allocation above. These items are placed
// into a std::vector and free'd, which may free in reverse order. That would
// imply that the death rattle and subsequent waitable event fires before the
// pool is reset. To work around this, we can either manually remove items
// from the vector or use two death rattles.
for (auto i = 0u; i < 2; i++) {
// Add something to the resource manager and have it notify us when it's
// destroyed. That should give us a non-flaky signal that the pool has been
// reclaimed as well.
auto waiter = fml::AutoResetWaitableEvent();
auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
{
UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
std::move(rattle));
}
waiter.Wait();
}
auto const [pool, _] = context->GetDescriptorPoolRecycler()->Get(1024);
// Now check that we only ever created one pool.
auto const called = GetMockVulkanFunctions(context->GetDevice());
EXPECT_EQ(
std::count(called->begin(), called->end(), "vkCreateDescriptorPool"), 1u);
context->Shutdown();
}
TEST(DescriptorPoolRecyclerVKTest, ReclaimDropsDescriptorPoolIfSizeIsExceeded) {
auto const context = MockVulkanContextBuilder().Build();
// Create 33 pools
{
std::vector<std::unique_ptr<DescriptorPoolVK>> pools;
for (auto i = 0u; i < 33; i++) {
auto pool = std::make_unique<DescriptorPoolVK>(context);
pool->AllocateDescriptorSets(1024, 1024, 1024, {});
pools.push_back(std::move(pool));
}
}
// See note above.
for (auto i = 0u; i < 2; i++) {
auto waiter = fml::AutoResetWaitableEvent();
auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
{
UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
std::move(rattle));
}
waiter.Wait();
}
auto const called = GetMockVulkanFunctions(context->GetDevice());
EXPECT_EQ(
std::count(called->begin(), called->end(), "vkCreateDescriptorPool"),
33u);
EXPECT_EQ(std::count(called->begin(), called->end(), "vkResetDescriptorPool"),
33u);
// Now create 33 more descriptor pools and observe that only one more is
// allocated.
{
std::vector<std::unique_ptr<DescriptorPoolVK>> pools;
for (auto i = 0u; i < 33; i++) {
auto pool = std::make_unique<DescriptorPoolVK>(context);
pool->AllocateDescriptorSets(1024, 1024, 1024, {});
pools.push_back(std::move(pool));
}
}
for (auto i = 0u; i < 2; i++) {
auto waiter = fml::AutoResetWaitableEvent();
auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
{
UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
std::move(rattle));
}
waiter.Wait();
}
auto const called_twice = GetMockVulkanFunctions(context->GetDevice());
// 32 of the descriptor pools were recycled, so only one more is created.
EXPECT_EQ(
std::count(called->begin(), called->end(), "vkCreateDescriptorPool"),
34u);
context->Shutdown();
}
} // namespace testing
} // namespace impeller