[Impeller] fix descriptor pool recycler test flake. (#48479)
This test worked by 1) releasing an item to be destructed on a background thead 2) adding a second item to this queue with a waitable event.
The idea being you could wait for the event and that would tell you when 1) was complete. Unfortunately these items are released by being placed into a std::vector, which destroyed in _reverse_ order. Somtimes when the test state was queried the work was done and sometimes it wasn't
To fix this, add a second waitable event that guarantees that both the first event and the original event have finished - because we do not add these event until the first is destructed and we do not add items to the destroy list while it is in progress.
diff --git a/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc b/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc
index 99756e1..c7f3cc3 100644
--- a/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc
+++ b/impeller/renderer/backend/vulkan/descriptor_pool_vk_unittests.cc
@@ -69,16 +69,24 @@
pool.AllocateDescriptorSets(1024, 1024, {});
}
- // Add something to the resource manager and have it notify us when it's
- // destroyed. That should give us a non-flaky signal that the pool has been
- // reclaimed as well.
- auto waiter = fml::AutoResetWaitableEvent();
- auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
- {
- UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
- std::move(rattle));
+ // There is a chance that the first death rattle item below is destroyed in
+ // the same reclaim cycle as the pool allocation above. These items are placed
+ // into a std::vector and free'd, which may free in reverse order. That would
+ // imply that the death rattle and subsequent waitable event fires before the
+ // pool is reset. To work around this, we can either manually remove items
+ // from the vector or use two death rattles.
+ for (auto i = 0u; i < 2; i++) {
+ // Add something to the resource manager and have it notify us when it's
+ // destroyed. That should give us a non-flaky signal that the pool has been
+ // reclaimed as well.
+ auto waiter = fml::AutoResetWaitableEvent();
+ auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
+ {
+ UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
+ std::move(rattle));
+ }
+ waiter.Wait();
}
- waiter.Wait();
auto const [pool, _] = context->GetDescriptorPoolRecycler()->Get(1024);
@@ -90,5 +98,66 @@
context->Shutdown();
}
+TEST(DescriptorPoolRecyclerVKTest, ReclaimDropsDescriptorPoolIfSizeIsExceeded) {
+ auto const context = MockVulkanContextBuilder().Build();
+
+ // Create 33 pools
+ {
+ std::vector<std::unique_ptr<DescriptorPoolVK>> pools;
+ for (auto i = 0u; i < 33; i++) {
+ auto pool = std::make_unique<DescriptorPoolVK>(context);
+ pool->AllocateDescriptorSets(1024, 1024, {});
+ pools.push_back(std::move(pool));
+ }
+ }
+
+ // See note above.
+ for (auto i = 0u; i < 2; i++) {
+ auto waiter = fml::AutoResetWaitableEvent();
+ auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
+ {
+ UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
+ std::move(rattle));
+ }
+ waiter.Wait();
+ }
+
+ auto const called = GetMockVulkanFunctions(context->GetDevice());
+ EXPECT_EQ(
+ std::count(called->begin(), called->end(), "vkCreateDescriptorPool"),
+ 33u);
+ EXPECT_EQ(std::count(called->begin(), called->end(), "vkResetDescriptorPool"),
+ 33u);
+
+ // Now create 33 more descriptor pools and observe that only one more is
+ // allocated.
+ {
+ std::vector<std::unique_ptr<DescriptorPoolVK>> pools;
+ for (auto i = 0u; i < 33; i++) {
+ auto pool = std::make_unique<DescriptorPoolVK>(context);
+ pool->AllocateDescriptorSets(1024, 1024, {});
+ pools.push_back(std::move(pool));
+ }
+ }
+
+ for (auto i = 0u; i < 2; i++) {
+ auto waiter = fml::AutoResetWaitableEvent();
+ auto rattle = DeathRattle([&waiter]() { waiter.Signal(); });
+ {
+ UniqueResourceVKT<DeathRattle> resource(context->GetResourceManager(),
+ std::move(rattle));
+ }
+ waiter.Wait();
+ }
+
+ auto const called_twice = GetMockVulkanFunctions(context->GetDevice());
+ // 32 of the descriptor pools were recycled, so only one more is created.
+ EXPECT_EQ(
+ std::count(called->begin(), called->end(), "vkCreateDescriptorPool"),
+ 34u);
+
+ context->Shutdown();
+}
+
} // namespace testing
} // namespace impeller