// Copyright 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "VirtioGpuTimelines.h" #include namespace gfxstream { namespace { using testing::ElementsAreArray; using testing::Eq; using testing::ElementsAreArray; using testing::IsEmpty; using testing::Pair; using Ring = VirtioGpuTimelines::Ring; using FenceId = VirtioGpuTimelines::FenceId; using RingGlobal = VirtioGpuRingGlobal; using RingContextSpecific = VirtioGpuRingContextSpecific; const auto kGlobalRing = Ring{VirtioGpuRingGlobal{}}; const auto kContext2Ring = Ring{RingContextSpecific{ .mCtxId = 2, .mRingIdx = 0, }}; const auto kContext3Ring = Ring{RingContextSpecific{ .mCtxId = 3, .mRingIdx = 0, }}; TEST(VirtioGpuTimelinesTest, Init) { auto noopCallback = [](const Ring&, FenceId) {}; std::unique_ptr virtioGpuTimelines; virtioGpuTimelines = VirtioGpuTimelines::create(noopCallback); virtioGpuTimelines = VirtioGpuTimelines::create(noopCallback); } TEST(VirtioGpuTimelinesTest, TasksShouldHaveDifferentIds) { auto noopCallback = [](const Ring&, FenceId) {}; std::unique_ptr virtioGpuTimelines = VirtioGpuTimelines::create(noopCallback); auto taskId1 = virtioGpuTimelines->enqueueTask(kGlobalRing); auto taskId2 = virtioGpuTimelines->enqueueTask(kGlobalRing); ASSERT_NE(taskId1, taskId2); } TEST(VirtioGpuTimelinesTest, MultipleTasksAndFencesWithAsyncCallback) { std::vector> signaledFences; auto fenceCallback = [&](const Ring& ring, FenceId fenceId) { signaledFences.push_back(std::make_pair(ring, fenceId)); }; std::unique_ptr virtioGpuTimelines = VirtioGpuTimelines::create(fenceCallback); FenceId fenceId = 0; auto task1Id = virtioGpuTimelines->enqueueTask(kGlobalRing); EXPECT_THAT(signaledFences, IsEmpty()); auto fence1Id = fenceId++; virtioGpuTimelines->enqueueFence(kGlobalRing, fence1Id); EXPECT_THAT(signaledFences, IsEmpty()); auto task2Id = virtioGpuTimelines->enqueueTask(kGlobalRing); EXPECT_THAT(signaledFences, IsEmpty()); auto fence2Id = fenceId++; virtioGpuTimelines->enqueueFence(kGlobalRing, fence2Id); EXPECT_THAT(signaledFences, IsEmpty()); virtioGpuTimelines->notifyTaskCompletion(task1Id); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(fence1Id)), })); auto task3Id = virtioGpuTimelines->enqueueTask(kGlobalRing); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(fence1Id)), })); auto fence3Id = fenceId++; virtioGpuTimelines->enqueueFence(kGlobalRing, fence3Id); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(fence1Id)), })); virtioGpuTimelines->notifyTaskCompletion(task2Id); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(fence1Id)), Pair(Eq(kGlobalRing), Eq(fence2Id)), })); virtioGpuTimelines->notifyTaskCompletion(task3Id); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(fence1Id)), Pair(Eq(kGlobalRing), Eq(fence2Id)), Pair(Eq(kGlobalRing), Eq(fence3Id)), })); } TEST(VirtioGpuTimelinesTest, FencesWithoutPendingTasksWithAsyncCallback) { std::vector> signaledFences; auto fenceCallback = [&](const Ring& ring, FenceId fenceId) { signaledFences.push_back(std::make_pair(ring, fenceId)); }; std::unique_ptr virtioGpuTimelines = VirtioGpuTimelines::create(fenceCallback); FenceId fenceId = 0; auto fence1Id = fenceId++; virtioGpuTimelines->enqueueFence(kGlobalRing, fence1Id); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(fence1Id)), })); auto fence2Id = fenceId++; virtioGpuTimelines->enqueueFence(kGlobalRing, fence2Id); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(fence1Id)), Pair(Eq(kGlobalRing), Eq(fence2Id)), })); } TEST(VirtioGpuTimelinesTest, FencesSharingSamePendingTasksWithAsyncCallback) { std::vector> signaledFences; auto fenceCallback = [&](const Ring& ring, FenceId fenceId) { signaledFences.push_back(std::make_pair(ring, fenceId)); }; std::unique_ptr virtioGpuTimelines = VirtioGpuTimelines::create(fenceCallback); FenceId fenceId = 0; auto taskId = virtioGpuTimelines->enqueueTask(kGlobalRing); EXPECT_THAT(signaledFences, IsEmpty()); auto fence1Id = fenceId++; virtioGpuTimelines->enqueueFence(kGlobalRing, fence1Id); EXPECT_THAT(signaledFences, IsEmpty()); auto fence2Id = fenceId++; virtioGpuTimelines->enqueueFence(kGlobalRing, fence2Id); EXPECT_THAT(signaledFences, IsEmpty()); virtioGpuTimelines->notifyTaskCompletion(taskId); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(fence1Id)), Pair(Eq(kGlobalRing), Eq(fence2Id)), })); } TEST(VirtioGpuTimelinesTest, TasksAndFencesOnMultipleContextsWithAsyncCallback) { std::vector> signaledFences; auto fenceCallback = [&](const Ring& ring, FenceId fenceId) { signaledFences.push_back(std::make_pair(ring, fenceId)); }; std::unique_ptr virtioGpuTimelines = VirtioGpuTimelines::create(fenceCallback); auto taskId2 = virtioGpuTimelines->enqueueTask(kContext2Ring); EXPECT_THAT(signaledFences, IsEmpty()); auto taskId3 = virtioGpuTimelines->enqueueTask(kContext3Ring); EXPECT_THAT(signaledFences, IsEmpty()); virtioGpuTimelines->enqueueFence(kGlobalRing, 1); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(1)), })); virtioGpuTimelines->enqueueFence(kContext2Ring, 2); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(1)), })); virtioGpuTimelines->enqueueFence(kContext3Ring, 3); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(1)), })); virtioGpuTimelines->notifyTaskCompletion(taskId2); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(1)), Pair(Eq(kContext2Ring), Eq(2)), })); virtioGpuTimelines->notifyTaskCompletion(taskId3); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kGlobalRing), Eq(1)), Pair(Eq(kContext2Ring), Eq(2)), Pair(Eq(kContext3Ring), Eq(3)), })); } TEST(VirtioGpuTimelinesTest, TasksAndFencesOnMultipleRingsWithAsyncCallback) { std::vector> signaledFences; auto fenceCallback = [&](const Ring& ring, FenceId fenceId) { signaledFences.push_back(std::make_pair(ring, fenceId)); }; std::unique_ptr virtioGpuTimelines = VirtioGpuTimelines::create(fenceCallback); const auto kContext1Ring1 = Ring{RingContextSpecific{ .mCtxId = 1, .mRingIdx = 1, }}; const auto kContext1Ring2 = Ring{RingContextSpecific{ .mCtxId = 1, .mRingIdx = 2, }}; const auto kContext1Ring3 = Ring{RingContextSpecific{ .mCtxId = 1, .mRingIdx = 3, }}; auto taskId2 = virtioGpuTimelines->enqueueTask(kContext1Ring2); auto taskId3 = virtioGpuTimelines->enqueueTask(kContext1Ring3); EXPECT_THAT(signaledFences, IsEmpty()); virtioGpuTimelines->enqueueFence(kContext1Ring1, 1); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kContext1Ring1), Eq(1)), })); virtioGpuTimelines->enqueueFence(kContext1Ring2, 2); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kContext1Ring1), Eq(1)), })); virtioGpuTimelines->enqueueFence(kContext1Ring3, 3); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kContext1Ring1), Eq(1)), })); virtioGpuTimelines->notifyTaskCompletion(taskId2); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kContext1Ring1), Eq(1)), Pair(Eq(kContext1Ring2), Eq(2)), })); virtioGpuTimelines->notifyTaskCompletion(taskId3); EXPECT_THAT(signaledFences, ElementsAreArray({ Pair(Eq(kContext1Ring1), Eq(1)), Pair(Eq(kContext1Ring2), Eq(2)), Pair(Eq(kContext1Ring3), Eq(3)), })); } } // namespace } // namespace gfxstream