Flutter Impeller
descriptor_pool_vk_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/testing/testing.h" // IWYU pragma: keep.
6 #include "fml/closure.h"
7 #include "fml/synchronization/waitable_event.h"
11 #include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
12 
13 namespace impeller {
14 namespace testing {
15 
16 TEST(DescriptorPoolRecyclerVKTest, GetDescriptorPoolRecyclerCreatesNewPools) {
17  auto const context = MockVulkanContextBuilder().Build();
18 
19  auto const pool1 = context->GetDescriptorPoolRecycler()->Get();
20  auto const pool2 = context->GetDescriptorPoolRecycler()->Get();
21 
22  // The two descriptor pools should be different.
23  EXPECT_NE(pool1.get(), pool2.get());
24 
25  context->Shutdown();
26 }
27 
28 TEST(DescriptorPoolRecyclerVKTest, ReclaimMakesDescriptorPoolAvailable) {
29  auto const context = MockVulkanContextBuilder().Build();
30 
31  {
32  // Fetch a pool (which will be created).
33  auto pool = DescriptorPoolVK(context);
34  pool.AllocateDescriptorSets({}, *context);
35  }
36 
37  // There is a chance that the first death rattle item below is destroyed in
38  // the same reclaim cycle as the pool allocation above. These items are placed
39  // into a std::vector and free'd, which may free in reverse order. That would
40  // imply that the death rattle and subsequent waitable event fires before the
41  // pool is reset. To work around this, we can either manually remove items
42  // from the vector or use two death rattles.
43  for (auto i = 0u; i < 2; i++) {
44  // Add something to the resource manager and have it notify us when it's
45  // destroyed. That should give us a non-flaky signal that the pool has been
46  // reclaimed as well.
47  auto waiter = fml::AutoResetWaitableEvent();
48  auto rattle = fml::ScopedCleanupClosure([&waiter]() { waiter.Signal(); });
49  {
51  context->GetResourceManager(), std::move(rattle));
52  }
53  waiter.Wait();
54  }
55 
56  auto const pool = context->GetDescriptorPoolRecycler()->Get();
57 
58  // Now check that we only ever created one pool.
59  auto const called = GetMockVulkanFunctions(context->GetDevice());
60  EXPECT_EQ(
61  std::count(called->begin(), called->end(), "vkCreateDescriptorPool"), 1u);
62 
63  context->Shutdown();
64 }
65 
66 TEST(DescriptorPoolRecyclerVKTest, ReclaimDropsDescriptorPoolIfSizeIsExceeded) {
67  auto const context = MockVulkanContextBuilder().Build();
68 
69  // Create 33 pools
70  {
71  std::vector<std::unique_ptr<DescriptorPoolVK>> pools;
72  for (auto i = 0u; i < 33; i++) {
73  auto pool = std::make_unique<DescriptorPoolVK>(context);
74  pool->AllocateDescriptorSets({}, *context);
75  pools.push_back(std::move(pool));
76  }
77  }
78 
79  // See note above.
80  for (auto i = 0u; i < 2; i++) {
81  auto waiter = fml::AutoResetWaitableEvent();
82  auto rattle = fml::ScopedCleanupClosure([&waiter]() { waiter.Signal(); });
83  {
85  context->GetResourceManager(), std::move(rattle));
86  }
87  waiter.Wait();
88  }
89 
90  auto const called = GetMockVulkanFunctions(context->GetDevice());
91  EXPECT_EQ(
92  std::count(called->begin(), called->end(), "vkCreateDescriptorPool"),
93  33u);
94  EXPECT_EQ(std::count(called->begin(), called->end(), "vkResetDescriptorPool"),
95  33u);
96 
97  // Now create 33 more descriptor pools and observe that only one more is
98  // allocated.
99  {
100  std::vector<std::unique_ptr<DescriptorPoolVK>> pools;
101  for (auto i = 0u; i < 33; i++) {
102  auto pool = std::make_unique<DescriptorPoolVK>(context);
103  pool->AllocateDescriptorSets({}, *context);
104  pools.push_back(std::move(pool));
105  }
106  }
107 
108  for (auto i = 0u; i < 2; i++) {
109  auto waiter = fml::AutoResetWaitableEvent();
110  auto rattle = fml::ScopedCleanupClosure([&waiter]() { waiter.Signal(); });
111  {
113  context->GetResourceManager(), std::move(rattle));
114  }
115  waiter.Wait();
116  }
117 
118  auto const called_twice = GetMockVulkanFunctions(context->GetDevice());
119  // 32 of the descriptor pools were recycled, so only one more is created.
120  EXPECT_EQ(
121  std::count(called->begin(), called->end(), "vkCreateDescriptorPool"),
122  34u);
123 
124  context->Shutdown();
125 }
126 
127 TEST(DescriptorPoolRecyclerVKTest, MultipleCommandBuffersShareDescriptorPool) {
128  auto const context = MockVulkanContextBuilder().Build();
129 
130  auto cmd_buffer_1 = context->CreateCommandBuffer();
131  auto cmd_buffer_2 = context->CreateCommandBuffer();
132 
133  CommandBufferVK& vk_1 = CommandBufferVK::Cast(*cmd_buffer_1);
134  CommandBufferVK& vk_2 = CommandBufferVK::Cast(*cmd_buffer_2);
135 
136  EXPECT_EQ(&vk_1.GetDescriptorPool(), &vk_2.GetDescriptorPool());
137 
138  // Resetting resources creates a new pool.
139  context->DisposeThreadLocalCachedResources();
140 
141  auto cmd_buffer_3 = context->CreateCommandBuffer();
142  CommandBufferVK& vk_3 = CommandBufferVK::Cast(*cmd_buffer_3);
143 
144  EXPECT_NE(&vk_1.GetDescriptorPool(), &vk_3.GetDescriptorPool());
145 
146  context->Shutdown();
147 }
148 
149 } // namespace testing
150 } // namespace impeller
static CommandBufferVK & Cast(CommandBuffer &base)
Definition: backend_cast.h:13
DescriptorPoolVK & GetDescriptorPool() const
A per-frame descriptor pool. Descriptors from this pool don't need to be freed individually....
A unique handle to a resource which will be reclaimed by the specified resource manager.
TEST(AllocationSizeTest, CanCreateTypedAllocations)