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/synchronization/waitable_event.h"
9 #include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
10 
11 namespace impeller {
12 namespace testing {
13 
14 TEST(DescriptorPoolRecyclerVKTest, GetDescriptorPoolRecyclerCreatesNewPools) {
15  auto const context = MockVulkanContextBuilder().Build();
16 
17  auto const [pool1, _] = context->GetDescriptorPoolRecycler()->Get(1024);
18  auto const [pool2, __] = context->GetDescriptorPoolRecycler()->Get(1024);
19 
20  // The two descriptor pools should be different.
21  EXPECT_NE(pool1.get(), pool2.get());
22 
23  context->Shutdown();
24 }
25 
26 TEST(DescriptorPoolRecyclerVKTest, DescriptorPoolCapacityIsRoundedUp) {
27  auto const context = MockVulkanContextBuilder().Build();
28  auto const [pool1, capacity] = context->GetDescriptorPoolRecycler()->Get(1);
29 
30  // Rounds up to a minimum of 64.
31  EXPECT_EQ(capacity, 64u);
32 
33  auto const [pool2, capacity_2] =
34  context->GetDescriptorPoolRecycler()->Get(1023);
35 
36  // Rounds up to the next power of two.
37  EXPECT_EQ(capacity_2, 1024u);
38 
39  context->Shutdown();
40 }
41 
42 namespace {
43 
44 // Invokes the provided callback when the destructor is called.
45 //
46 // Can be moved, but not copied.
47 class DeathRattle final {
48  public:
49  explicit DeathRattle(std::function<void()> callback)
50  : callback_(std::move(callback)) {}
51 
52  DeathRattle(DeathRattle&&) = default;
53  DeathRattle& operator=(DeathRattle&&) = default;
54 
55  ~DeathRattle() { callback_(); }
56 
57  private:
58  std::function<void()> callback_;
59 };
60 
61 } // namespace
62 
63 TEST(DescriptorPoolRecyclerVKTest, ReclaimMakesDescriptorPoolAvailable) {
64  auto const context = MockVulkanContextBuilder().Build();
65 
66  {
67  // Fetch a pool (which will be created).
68  auto pool = DescriptorPoolVK(context);
69  pool.AllocateDescriptorSets(1024, 1024, 1024, {});
70  }
71 
72  // There is a chance that the first death rattle item below is destroyed in
73  // the same reclaim cycle as the pool allocation above. These items are placed
74  // into a std::vector and free'd, which may free in reverse order. That would
75  // imply that the death rattle and subsequent waitable event fires before the
76  // pool is reset. To work around this, we can either manually remove items
77  // from the vector or use two death rattles.
78  for (auto i = 0u; i < 2; i++) {
79  // Add something to the resource manager and have it notify us when it's
80  // destroyed. That should give us a non-flaky signal that the pool has been
81  // reclaimed as well.
82  auto waiter = fml::AutoResetWaitableEvent();
83  auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
84  {
85  UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
86  std::move(rattle));
87  }
88  waiter.Wait();
89  }
90 
91  auto const [pool, _] = context->GetDescriptorPoolRecycler()->Get(1024);
92 
93  // Now check that we only ever created one pool.
94  auto const called = GetMockVulkanFunctions(context->GetDevice());
95  EXPECT_EQ(
96  std::count(called->begin(), called->end(), "vkCreateDescriptorPool"), 1u);
97 
98  context->Shutdown();
99 }
100 
101 TEST(DescriptorPoolRecyclerVKTest, ReclaimDropsDescriptorPoolIfSizeIsExceeded) {
102  auto const context = MockVulkanContextBuilder().Build();
103 
104  // Create 33 pools
105  {
106  std::vector<std::unique_ptr<DescriptorPoolVK>> pools;
107  for (auto i = 0u; i < 33; i++) {
108  auto pool = std::make_unique<DescriptorPoolVK>(context);
109  pool->AllocateDescriptorSets(1024, 1024, 1024, {});
110  pools.push_back(std::move(pool));
111  }
112  }
113 
114  // See note above.
115  for (auto i = 0u; i < 2; i++) {
116  auto waiter = fml::AutoResetWaitableEvent();
117  auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
118  {
119  UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
120  std::move(rattle));
121  }
122  waiter.Wait();
123  }
124 
125  auto const called = GetMockVulkanFunctions(context->GetDevice());
126  EXPECT_EQ(
127  std::count(called->begin(), called->end(), "vkCreateDescriptorPool"),
128  33u);
129  EXPECT_EQ(std::count(called->begin(), called->end(), "vkResetDescriptorPool"),
130  33u);
131 
132  // Now create 33 more descriptor pools and observe that only one more is
133  // allocated.
134  {
135  std::vector<std::unique_ptr<DescriptorPoolVK>> pools;
136  for (auto i = 0u; i < 33; i++) {
137  auto pool = std::make_unique<DescriptorPoolVK>(context);
138  pool->AllocateDescriptorSets(1024, 1024, 1024, {});
139  pools.push_back(std::move(pool));
140  }
141  }
142 
143  for (auto i = 0u; i < 2; i++) {
144  auto waiter = fml::AutoResetWaitableEvent();
145  auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
146  {
147  UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
148  std::move(rattle));
149  }
150  waiter.Wait();
151  }
152 
153  auto const called_twice = GetMockVulkanFunctions(context->GetDevice());
154  // 32 of the descriptor pools were recycled, so only one more is created.
155  EXPECT_EQ(
156  std::count(called->begin(), called->end(), "vkCreateDescriptorPool"),
157  34u);
158 
159  context->Shutdown();
160 }
161 
162 } // namespace testing
163 } // namespace impeller
impeller::UniqueResourceVKT
A unique handle to a resource which will be reclaimed by the specified resource manager.
Definition: resource_manager_vk.h:145
impeller::DescriptorPoolVK
A short-lived fixed-sized descriptor pool. Descriptors from this pool don't need to be freed individu...
Definition: descriptor_pool_vk.h:27
impeller::testing::TEST
TEST(CanvasRecorder, Save)
Definition: canvas_recorder_unittests.cc:61
resource_manager_vk.h
std
Definition: comparable.h:95
descriptor_pool_vk.h
impeller
Definition: aiks_context.cc:10