Flutter Engine
 
Loading...
Searching...
No Matches
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.
10
11namespace impeller {
12namespace testing {
13
14TEST(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
43TEST(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
55namespace {
56
57// Invokes the provided callback when the destructor is called.
58//
59// Can be moved, but not copied.
60class 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.
75void 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.
99std::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
107TEST(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
137TEST(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
178TEST(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
222TEST(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)
std::shared_ptr< ContextVK > Build()
Create a Vulkan context with Vulkan functions mocked. The caller is given a chance to tinker on the s...
FlutterDesktopBinaryReply callback
TEST(FrameTimingsRecorderTest, RecordVsync)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
std::shared_ptr< std::vector< std::string > > GetMockVulkanFunctions(VkDevice device)
Definition ref_ptr.h:261