1 // Copyright 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #include <gmock/gmock.h>
15 #include <gtest/gtest.h>
16 
17 #include "VirtioGpuTimelines.h"
18 
19 #include <memory>
20 
21 namespace gfxstream {
22 namespace {
23 
24 using testing::ElementsAreArray;
25 using testing::Eq;
26 using testing::ElementsAreArray;
27 using testing::IsEmpty;
28 using testing::Pair;
29 
30 using Ring = VirtioGpuTimelines::Ring;
31 using FenceId = VirtioGpuTimelines::FenceId;
32 using RingGlobal = VirtioGpuRingGlobal;
33 using RingContextSpecific = VirtioGpuRingContextSpecific;
34 
35 const auto kGlobalRing = Ring{VirtioGpuRingGlobal{}};
36 const auto kContext2Ring = Ring{RingContextSpecific{
37     .mCtxId = 2,
38     .mRingIdx = 0,
39 }};
40 const auto kContext3Ring = Ring{RingContextSpecific{
41     .mCtxId = 3,
42     .mRingIdx = 0,
43 }};
44 
TEST(VirtioGpuTimelinesTest,Init)45 TEST(VirtioGpuTimelinesTest, Init) {
46     auto noopCallback = [](const Ring&, FenceId) {};
47     std::unique_ptr<VirtioGpuTimelines> virtioGpuTimelines;
48     virtioGpuTimelines = VirtioGpuTimelines::create(noopCallback);
49     virtioGpuTimelines = VirtioGpuTimelines::create(noopCallback);
50 }
51 
TEST(VirtioGpuTimelinesTest,TasksShouldHaveDifferentIds)52 TEST(VirtioGpuTimelinesTest, TasksShouldHaveDifferentIds) {
53     auto noopCallback = [](const Ring&, FenceId) {};
54     std::unique_ptr<VirtioGpuTimelines> virtioGpuTimelines =
55         VirtioGpuTimelines::create(noopCallback);
56     auto taskId1 = virtioGpuTimelines->enqueueTask(kGlobalRing);
57     auto taskId2 = virtioGpuTimelines->enqueueTask(kGlobalRing);
58     ASSERT_NE(taskId1, taskId2);
59 }
60 
TEST(VirtioGpuTimelinesTest,MultipleTasksAndFencesWithAsyncCallback)61 TEST(VirtioGpuTimelinesTest, MultipleTasksAndFencesWithAsyncCallback) {
62     std::vector<std::pair<Ring, FenceId>> signaledFences;
63 
64     auto fenceCallback =
65         [&](const Ring& ring, FenceId fenceId) {
66             signaledFences.push_back(std::make_pair(ring, fenceId));
67         };
68     std::unique_ptr<VirtioGpuTimelines> virtioGpuTimelines =
69         VirtioGpuTimelines::create(fenceCallback);
70 
71     FenceId fenceId = 0;
72 
73     auto task1Id = virtioGpuTimelines->enqueueTask(kGlobalRing);
74     EXPECT_THAT(signaledFences, IsEmpty());
75 
76     auto fence1Id = fenceId++;
77 
78     virtioGpuTimelines->enqueueFence(kGlobalRing, fence1Id);
79     EXPECT_THAT(signaledFences, IsEmpty());
80 
81     auto task2Id = virtioGpuTimelines->enqueueTask(kGlobalRing);
82     EXPECT_THAT(signaledFences, IsEmpty());
83 
84     auto fence2Id = fenceId++;
85 
86     virtioGpuTimelines->enqueueFence(kGlobalRing, fence2Id);
87     EXPECT_THAT(signaledFences, IsEmpty());
88 
89     virtioGpuTimelines->notifyTaskCompletion(task1Id);
90     EXPECT_THAT(signaledFences,
91                 ElementsAreArray({
92                     Pair(Eq(kGlobalRing), Eq(fence1Id)),
93                 }));
94 
95     auto task3Id = virtioGpuTimelines->enqueueTask(kGlobalRing);
96     EXPECT_THAT(signaledFences,
97                 ElementsAreArray({
98                     Pair(Eq(kGlobalRing), Eq(fence1Id)),
99                 }));
100 
101     auto fence3Id = fenceId++;
102     virtioGpuTimelines->enqueueFence(kGlobalRing, fence3Id);
103     EXPECT_THAT(signaledFences,
104                 ElementsAreArray({
105                     Pair(Eq(kGlobalRing), Eq(fence1Id)),
106                 }));
107 
108     virtioGpuTimelines->notifyTaskCompletion(task2Id);
109     EXPECT_THAT(signaledFences,
110                 ElementsAreArray({
111                     Pair(Eq(kGlobalRing), Eq(fence1Id)),
112                     Pair(Eq(kGlobalRing), Eq(fence2Id)),
113                 }));
114 
115     virtioGpuTimelines->notifyTaskCompletion(task3Id);
116     EXPECT_THAT(signaledFences,
117                 ElementsAreArray({
118                     Pair(Eq(kGlobalRing), Eq(fence1Id)),
119                     Pair(Eq(kGlobalRing), Eq(fence2Id)),
120                     Pair(Eq(kGlobalRing), Eq(fence3Id)),
121                 }));
122 }
123 
TEST(VirtioGpuTimelinesTest,FencesWithoutPendingTasksWithAsyncCallback)124 TEST(VirtioGpuTimelinesTest, FencesWithoutPendingTasksWithAsyncCallback) {
125     std::vector<std::pair<Ring, FenceId>> signaledFences;
126 
127     auto fenceCallback =
128         [&](const Ring& ring, FenceId fenceId) {
129             signaledFences.push_back(std::make_pair(ring, fenceId));
130         };
131     std::unique_ptr<VirtioGpuTimelines> virtioGpuTimelines =
132         VirtioGpuTimelines::create(fenceCallback);
133 
134     FenceId fenceId = 0;
135 
136     auto fence1Id = fenceId++;
137     virtioGpuTimelines->enqueueFence(kGlobalRing, fence1Id);
138     EXPECT_THAT(signaledFences,
139                 ElementsAreArray({
140                     Pair(Eq(kGlobalRing), Eq(fence1Id)),
141                 }));
142 
143     auto fence2Id = fenceId++;
144     virtioGpuTimelines->enqueueFence(kGlobalRing, fence2Id);
145     EXPECT_THAT(signaledFences,
146                 ElementsAreArray({
147                     Pair(Eq(kGlobalRing), Eq(fence1Id)),
148                     Pair(Eq(kGlobalRing), Eq(fence2Id)),
149                 }));
150 }
151 
TEST(VirtioGpuTimelinesTest,FencesSharingSamePendingTasksWithAsyncCallback)152 TEST(VirtioGpuTimelinesTest, FencesSharingSamePendingTasksWithAsyncCallback) {
153    std::vector<std::pair<Ring, FenceId>> signaledFences;
154 
155     auto fenceCallback =
156         [&](const Ring& ring, FenceId fenceId) {
157             signaledFences.push_back(std::make_pair(ring, fenceId));
158         };
159     std::unique_ptr<VirtioGpuTimelines> virtioGpuTimelines =
160         VirtioGpuTimelines::create(fenceCallback);
161 
162     FenceId fenceId = 0;
163 
164     auto taskId = virtioGpuTimelines->enqueueTask(kGlobalRing);
165     EXPECT_THAT(signaledFences, IsEmpty());
166 
167     auto fence1Id = fenceId++;
168     virtioGpuTimelines->enqueueFence(kGlobalRing, fence1Id);
169     EXPECT_THAT(signaledFences, IsEmpty());
170 
171     auto fence2Id = fenceId++;
172     virtioGpuTimelines->enqueueFence(kGlobalRing, fence2Id);
173     EXPECT_THAT(signaledFences, IsEmpty());
174 
175     virtioGpuTimelines->notifyTaskCompletion(taskId);
176     EXPECT_THAT(signaledFences,
177                 ElementsAreArray({
178                     Pair(Eq(kGlobalRing), Eq(fence1Id)),
179                     Pair(Eq(kGlobalRing), Eq(fence2Id)),
180                 }));
181 }
182 
TEST(VirtioGpuTimelinesTest,TasksAndFencesOnMultipleContextsWithAsyncCallback)183 TEST(VirtioGpuTimelinesTest, TasksAndFencesOnMultipleContextsWithAsyncCallback) {
184     std::vector<std::pair<Ring, FenceId>> signaledFences;
185 
186     auto fenceCallback =
187         [&](const Ring& ring, FenceId fenceId) {
188             signaledFences.push_back(std::make_pair(ring, fenceId));
189         };
190     std::unique_ptr<VirtioGpuTimelines> virtioGpuTimelines =
191         VirtioGpuTimelines::create(fenceCallback);
192 
193     auto taskId2 = virtioGpuTimelines->enqueueTask(kContext2Ring);
194     EXPECT_THAT(signaledFences, IsEmpty());
195 
196     auto taskId3 = virtioGpuTimelines->enqueueTask(kContext3Ring);
197     EXPECT_THAT(signaledFences, IsEmpty());
198 
199     virtioGpuTimelines->enqueueFence(kGlobalRing, 1);
200     EXPECT_THAT(signaledFences,
201                 ElementsAreArray({
202                     Pair(Eq(kGlobalRing), Eq(1)),
203                 }));
204 
205     virtioGpuTimelines->enqueueFence(kContext2Ring, 2);
206     EXPECT_THAT(signaledFences,
207                 ElementsAreArray({
208                     Pair(Eq(kGlobalRing), Eq(1)),
209                 }));
210 
211     virtioGpuTimelines->enqueueFence(kContext3Ring, 3);
212     EXPECT_THAT(signaledFences,
213                 ElementsAreArray({
214                     Pair(Eq(kGlobalRing), Eq(1)),
215                 }));
216 
217     virtioGpuTimelines->notifyTaskCompletion(taskId2);
218     EXPECT_THAT(signaledFences,
219                 ElementsAreArray({
220                     Pair(Eq(kGlobalRing), Eq(1)),
221                     Pair(Eq(kContext2Ring), Eq(2)),
222                 }));
223 
224     virtioGpuTimelines->notifyTaskCompletion(taskId3);
225     EXPECT_THAT(signaledFences,
226                 ElementsAreArray({
227                     Pair(Eq(kGlobalRing), Eq(1)),
228                     Pair(Eq(kContext2Ring), Eq(2)),
229                     Pair(Eq(kContext3Ring), Eq(3)),
230                 }));
231 }
232 
TEST(VirtioGpuTimelinesTest,TasksAndFencesOnMultipleRingsWithAsyncCallback)233 TEST(VirtioGpuTimelinesTest, TasksAndFencesOnMultipleRingsWithAsyncCallback) {
234     std::vector<std::pair<Ring, FenceId>> signaledFences;
235 
236     auto fenceCallback =
237         [&](const Ring& ring, FenceId fenceId) {
238             signaledFences.push_back(std::make_pair(ring, fenceId));
239         };
240     std::unique_ptr<VirtioGpuTimelines> virtioGpuTimelines =
241         VirtioGpuTimelines::create(fenceCallback);
242 
243     const auto kContext1Ring1 = Ring{RingContextSpecific{
244         .mCtxId = 1,
245         .mRingIdx = 1,
246     }};
247     const auto kContext1Ring2 = Ring{RingContextSpecific{
248         .mCtxId = 1,
249         .mRingIdx = 2,
250     }};
251     const auto kContext1Ring3 = Ring{RingContextSpecific{
252         .mCtxId = 1,
253         .mRingIdx = 3,
254     }};
255 
256     auto taskId2 = virtioGpuTimelines->enqueueTask(kContext1Ring2);
257     auto taskId3 = virtioGpuTimelines->enqueueTask(kContext1Ring3);
258     EXPECT_THAT(signaledFences, IsEmpty());
259 
260     virtioGpuTimelines->enqueueFence(kContext1Ring1, 1);
261     EXPECT_THAT(signaledFences,
262                 ElementsAreArray({
263                     Pair(Eq(kContext1Ring1), Eq(1)),
264                 }));
265 
266     virtioGpuTimelines->enqueueFence(kContext1Ring2, 2);
267     EXPECT_THAT(signaledFences,
268                 ElementsAreArray({
269                     Pair(Eq(kContext1Ring1), Eq(1)),
270                 }));
271 
272     virtioGpuTimelines->enqueueFence(kContext1Ring3, 3);
273     EXPECT_THAT(signaledFences,
274                 ElementsAreArray({
275                     Pair(Eq(kContext1Ring1), Eq(1)),
276                 }));
277 
278     virtioGpuTimelines->notifyTaskCompletion(taskId2);
279     EXPECT_THAT(signaledFences,
280                 ElementsAreArray({
281                     Pair(Eq(kContext1Ring1), Eq(1)),
282                     Pair(Eq(kContext1Ring2), Eq(2)),
283                 }));
284 
285     virtioGpuTimelines->notifyTaskCompletion(taskId3);
286         EXPECT_THAT(signaledFences,
287                 ElementsAreArray({
288                     Pair(Eq(kContext1Ring1), Eq(1)),
289                     Pair(Eq(kContext1Ring2), Eq(2)),
290                     Pair(Eq(kContext1Ring3), Eq(3)),
291                 }));
292 }
293 
294 }  // namespace
295 }  // namespace gfxstream
296