| 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"
7 #include "impeller/renderer/backend/vulkan/descriptor_pool_vk.h"
8 #include "impeller/renderer/backend/vulkan/resource_manager_vk.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