Flutter Impeller
fence_waiter_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 "fml/synchronization/waitable_event.h"
6 #include "gtest/gtest.h" // IWYU pragma: keep
7 #include "impeller/renderer/backend/vulkan/fence_waiter_vk.h" // IWYU pragma: keep
8 #include "impeller/renderer/backend/vulkan/test/mock_vulkan.h"
9 
10 namespace impeller {
11 namespace testing {
12 
13 namespace {
14 auto default_submit_callback = [](vk::Fence) { return fml::Status(); };
15 } // namespace
16 
17 TEST(FenceWaiterVKTest, IgnoresNullFence) {
18  auto const context = MockVulkanContextBuilder().Build();
19  auto const waiter = context->GetFenceWaiter();
20  const auto add_status =
21  waiter->AddFence(vk::UniqueFence(), default_submit_callback, []() {});
22  EXPECT_EQ(add_status.code(), fml::StatusCode::kInvalidArgument);
23 }
24 
25 TEST(FenceWaiterVKTest, IgnoresNullCallback) {
26  auto const context = MockVulkanContextBuilder().Build();
27  auto const device = context->GetDevice();
28  auto const waiter = context->GetFenceWaiter();
29 
30  auto fence = device.createFenceUnique({}).value;
31  const auto add_status =
32  waiter->AddFence(std::move(fence), default_submit_callback, nullptr);
33  EXPECT_EQ(add_status.code(), fml::StatusCode::kInvalidArgument);
34 }
35 
36 TEST(FenceWaiterVKTest, ReturnsSubmitError) {
37  auto const context = MockVulkanContextBuilder().Build();
38  auto const device = context->GetDevice();
39  auto const waiter = context->GetFenceWaiter();
40 
41  auto submit_callback = [&](vk::Fence) {
42  return fml::Status(fml::StatusCode::kCancelled, "Submit failed");
43  };
44 
45  auto fence = device.createFenceUnique({}).value;
46  const auto add_status =
47  waiter->AddFence(std::move(fence), submit_callback, []() {});
48  EXPECT_EQ(add_status.code(), fml::StatusCode::kCancelled);
49 }
50 
51 TEST(FenceWaiterVKTest, ExecutesFenceCallback) {
52  auto const context = MockVulkanContextBuilder().Build();
53  auto const device = context->GetDevice();
54  auto const waiter = context->GetFenceWaiter();
55 
56  bool submit_called = false;
57  auto submit_callback = [&](vk::Fence) {
58  submit_called = true;
59  return fml::Status();
60  };
61 
62  auto signal = fml::ManualResetWaitableEvent();
63  auto fence = device.createFenceUnique({}).value;
64  waiter->AddFence(std::move(fence), submit_callback,
65  [&signal]() { signal.Signal(); });
66  EXPECT_TRUE(submit_called);
67 
68  signal.Wait();
69 }
70 
71 TEST(FenceWaiterVKTest, ExecutesFenceCallbackX2) {
72  auto const context = MockVulkanContextBuilder().Build();
73  auto const device = context->GetDevice();
74  auto const waiter = context->GetFenceWaiter();
75 
76  auto signal = fml::ManualResetWaitableEvent();
77  auto fence = device.createFenceUnique({}).value;
78  waiter->AddFence(std::move(fence), default_submit_callback,
79  [&signal]() { signal.Signal(); });
80 
81  auto signal2 = fml::ManualResetWaitableEvent();
82  auto fence2 = device.createFenceUnique({}).value;
83  waiter->AddFence(std::move(fence2), default_submit_callback,
84  [&signal2]() { signal2.Signal(); });
85 
86  signal.Wait();
87  signal2.Wait();
88 }
89 
90 TEST(FenceWaiterVKTest, ExecutesNewFenceThenOldFence) {
91  auto const context = MockVulkanContextBuilder().Build();
92  auto const device = context->GetDevice();
93  auto const waiter = context->GetFenceWaiter();
94 
95  auto signal = fml::ManualResetWaitableEvent();
96  auto fence = device.createFenceUnique({}).value;
97  MockFence::SetStatus(fence, vk::Result::eNotReady);
98  auto raw_fence = MockFence::GetRawPointer(fence);
99  waiter->AddFence(std::move(fence), default_submit_callback,
100  [&signal]() { signal.Signal(); });
101 
102  // The easiest way to verify that the callback was _not_ called is to wait
103  // for a timeout, but that could introduce flakiness. Instead, we'll add a
104  // second fence that will signal immediately, and wait for that one instead.
105  {
106  auto signal2 = fml::ManualResetWaitableEvent();
107  auto fence2 = device.createFenceUnique({}).value;
108  MockFence::SetStatus(fence2, vk::Result::eSuccess);
109  waiter->AddFence(std::move(fence2), default_submit_callback,
110  [&signal2]() { signal2.Signal(); });
111  signal2.Wait();
112  }
113 
114  // Now, we'll signal the first fence, and wait for the callback to be called.
115  raw_fence->SetStatus(vk::Result::eSuccess);
116 
117  // Now, we'll signal the first fence, and wait for the callback to be called.
118  signal.Wait();
119 }
120 
121 TEST(FenceWaiterVKTest, AddFenceDoesNothingIfTerminating) {
122  auto signal = fml::ManualResetWaitableEvent();
123 
124  {
125  auto const context = MockVulkanContextBuilder().Build();
126  auto const device = context->GetDevice();
127  auto const waiter = context->GetFenceWaiter();
128  waiter->Terminate();
129 
130  auto fence = device.createFenceUnique({}).value;
131  waiter->AddFence(std::move(fence), default_submit_callback,
132  [&signal]() { signal.Signal(); });
133  }
134 
135  // Ensure the fence did _not_ signal.
136  EXPECT_TRUE(signal.WaitWithTimeout(fml::TimeDelta::FromMilliseconds(100)));
137 }
138 
139 TEST(FenceWaiterVKTest, InProgressFencesStillWaitIfTerminated) {
140  MockFence* raw_fence = nullptr;
141  auto signal = fml::ManualResetWaitableEvent();
142 
143  auto const context = MockVulkanContextBuilder().Build();
144  auto const device = context->GetDevice();
145  auto const waiter = context->GetFenceWaiter();
146 
147  // Add a fence that isn't signalled yet.
148  auto fence = device.createFenceUnique({}).value;
149 
150  // Even if the fence is eSuccess, it's not guaranteed to be called in time.
151  MockFence::SetStatus(fence, vk::Result::eNotReady);
152  raw_fence = MockFence::GetRawPointer(fence);
153  waiter->AddFence(std::move(fence), default_submit_callback,
154  [&signal]() { signal.Signal(); });
155 
156  // Signal the fence after a delay.
157  std::thread thread([&]() {
158  std::this_thread::sleep_for(std::chrono::milliseconds{100u});
159  raw_fence->SetStatus(vk::Result::eSuccess);
160  });
161 
162  // Terminate the waiter. This will block until all pending fences are
163  // signalled.
164  waiter->Terminate();
165 
166  // This will hang if the fence was not signalled.
167  signal.Wait();
168 
169  thread.join();
170 }
171 
172 } // namespace testing
173 } // namespace impeller
int32_t value
TEST(AllocationSizeTest, CanCreateTypedAllocations)
std::shared_ptr< ContextGLES > context