Flutter Impeller
command_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(CommandPoolRecyclerVKTest, GetsACommandPoolPerThread) {
15  auto const context = MockVulkanContextBuilder().Build();
16 
17  {
18  // Record the memory location of each pointer to a command pool.
19  //
20  // These pools have to be held at this context, otherwise they will be
21  // dropped and recycled and potentially reused by another thread, causing
22  // flaky tests.
23  std::shared_ptr<CommandPoolVK> pool1;
24  std::shared_ptr<CommandPoolVK> pool2;
25 
26  // Create a command pool in two threads and record the memory location.
27  std::thread thread1(
28  [&]() { pool1 = context->GetCommandPoolRecycler()->Get(); });
29 
30  std::thread thread2(
31  [&]() { pool2 = context->GetCommandPoolRecycler()->Get(); });
32 
33  thread1.join();
34  thread2.join();
35 
36  // The two command pools should be different.
37  EXPECT_NE(pool1, pool2);
38  }
39 
40  context->Shutdown();
41 }
42 
43 TEST(CommandPoolRecyclerVKTest, GetsTheSameCommandPoolOnSameThread) {
44  auto const context = MockVulkanContextBuilder().Build();
45 
46  auto const pool1 = context->GetCommandPoolRecycler()->Get();
47  auto const pool2 = context->GetCommandPoolRecycler()->Get();
48 
49  // The two command pools should be the same.
50  EXPECT_EQ(pool1.get(), pool2.get());
51 
52  context->Shutdown();
53 }
54 
55 namespace {
56 
57 // Invokes the provided callback when the destructor is called.
58 //
59 // Can be moved, but not copied.
60 class DeathRattle final {
61  public:
62  explicit DeathRattle(std::function<void()> callback)
63  : callback_(std::move(callback)) {}
64 
65  DeathRattle(DeathRattle&&) = default;
66  DeathRattle& operator=(DeathRattle&&) = default;
67 
68  ~DeathRattle() { callback_(); }
69 
70  private:
71  std::function<void()> callback_;
72 };
73 
74 // Wait for reclaim of recycled command pools.
75 void WaitForReclaim(const std::shared_ptr<ContextVK>& context) {
76  // Add a resource to the resource manager and wait for its destructor to
77  // signal an event.
78  //
79  // This must be done twice because the resource manager does not guarantee
80  // the order in which resources are handled within the set of reclaimable
81  // resources. When the first DeathRattle is signaled there may be pools
82  // within the pending set that have not yet been reclaimed. After the second
83  // DeathRattle is signaled all resources in the original set will have been
84  // reclaimed.
85  for (int i = 0; i < 2; i++) {
86  auto waiter = fml::AutoResetWaitableEvent();
87  auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
88  {
89  UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
90  std::move(rattle));
91  }
92  waiter.Wait();
93  }
94 }
95 
96 // The list of function calls returned by the mock Vulkan device is not thread
97 // safe. Wait for the background thread to finish any pending reclaim
98 // operations before obtaining the list.
99 std::shared_ptr<std::vector<std::string>> ReclaimAndGetMockVulkanFunctions(
100  const std::shared_ptr<ContextVK>& context) {
101  WaitForReclaim(context);
102  return GetMockVulkanFunctions(context->GetDevice());
103 }
104 
105 } // namespace
106 
107 TEST(CommandPoolRecyclerVKTest, ReclaimMakesCommandPoolAvailable) {
108  auto const context = MockVulkanContextBuilder().Build();
109 
110  {
111  // Fetch a pool (which will be created).
112  auto const recycler = context->GetCommandPoolRecycler();
113  auto const pool = recycler->Get();
114 
115  // This normally is called at the end of a frame.
116  recycler->Dispose();
117  }
118 
119  WaitForReclaim(context);
120 
121  // On another thread explicitly, request a new pool.
122  std::thread thread([&]() {
123  auto const pool = context->GetCommandPoolRecycler()->Get();
124  EXPECT_NE(pool.get(), nullptr);
125  });
126 
127  thread.join();
128 
129  // Now check that we only ever created one pool.
130  auto const called = ReclaimAndGetMockVulkanFunctions(context);
131  EXPECT_EQ(std::count(called->begin(), called->end(), "vkCreateCommandPool"),
132  1u);
133 
134  context->Shutdown();
135 }
136 
137 TEST(CommandPoolRecyclerVKTest, CommandBuffersAreRecycled) {
138  auto const context = MockVulkanContextBuilder().Build();
139 
140  {
141  // Fetch a pool (which will be created).
142  auto const recycler = context->GetCommandPoolRecycler();
143  auto pool = recycler->Get();
144 
145  auto buffer = pool->CreateCommandBuffer();
146  pool->CollectCommandBuffer(std::move(buffer));
147 
148  // This normally is called at the end of a frame.
149  recycler->Dispose();
150  }
151 
152  WaitForReclaim(context);
153 
154  {
155  // Create a second pool and command buffer, which should reused the existing
156  // pool and cmd buffer.
157  auto const recycler = context->GetCommandPoolRecycler();
158  auto pool = recycler->Get();
159 
160  auto buffer = pool->CreateCommandBuffer();
161  pool->CollectCommandBuffer(std::move(buffer));
162 
163  // This normally is called at the end of a frame.
164  recycler->Dispose();
165  }
166 
167  // Now check that we only ever created one pool and one command buffer.
168  auto const called = ReclaimAndGetMockVulkanFunctions(context);
169  EXPECT_EQ(std::count(called->begin(), called->end(), "vkCreateCommandPool"),
170  1u);
171  EXPECT_EQ(
172  std::count(called->begin(), called->end(), "vkAllocateCommandBuffers"),
173  1u);
174 
175  context->Shutdown();
176 }
177 
178 TEST(CommandPoolRecyclerVKTest, ExtraCommandBufferAllocationsTriggerTrim) {
179  auto const context = MockVulkanContextBuilder().Build();
180 
181  {
182  // Fetch a pool (which will be created).
183  auto const recycler = context->GetCommandPoolRecycler();
184  auto pool = recycler->Get();
185 
186  // Allocate a large number of command buffers
187  for (auto i = 0; i < 64; i++) {
188  auto buffer = pool->CreateCommandBuffer();
189  pool->CollectCommandBuffer(std::move(buffer));
190  }
191 
192  // This normally is called at the end of a frame.
193  recycler->Dispose();
194  }
195 
196  // Command pool is reset but does not release resources.
197  auto called = ReclaimAndGetMockVulkanFunctions(context);
198  EXPECT_EQ(std::count(called->begin(), called->end(), "vkResetCommandPool"),
199  1u);
200 
201  // Create the pool a second time, but dont use any command buffers.
202  {
203  // Fetch a pool (which will be created).
204  auto const recycler = context->GetCommandPoolRecycler();
205  auto pool = recycler->Get();
206 
207  // This normally is called at the end of a frame.
208  recycler->Dispose();
209  }
210 
211  // Verify that the cmd pool was trimmed.
212 
213  // Now check that we only ever created one pool and one command buffer.
214  called = ReclaimAndGetMockVulkanFunctions(context);
215  EXPECT_EQ(std::count(called->begin(), called->end(),
216  "vkResetCommandPoolReleaseResources"),
217  1u);
218 
219  context->Shutdown();
220 }
221 
222 TEST(CommandPoolRecyclerVKTest, RecyclerGlobalPoolMapSize) {
223  auto context = MockVulkanContextBuilder().Build();
224  auto const recycler = context->GetCommandPoolRecycler();
225 
226  // The global pool list for this context should initially be empty.
227  EXPECT_EQ(CommandPoolRecyclerVK::GetGlobalPoolCount(*context), 0);
228 
229  // Creating a pool for this thread should insert the pool into the global map.
230  auto pool = recycler->Get();
231  EXPECT_EQ(CommandPoolRecyclerVK::GetGlobalPoolCount(*context), 1);
232 
233  // Disposing this thread's pool should remove it from the global map.
234  pool.reset();
235  recycler->Dispose();
236  EXPECT_EQ(CommandPoolRecyclerVK::GetGlobalPoolCount(*context), 0);
237 
238  context->Shutdown();
239 }
240 
241 } // namespace testing
242 } // namespace impeller
static int GetGlobalPoolCount(const ContextVK &context)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: comparable.h:95