1 // Note: needs to be included before DisplayVk to avoid conflicts
2 // between gtest and x11 headers.
3 #include <gtest/gtest.h>
4 
5 #include "DisplayVk.h"
6 
7 #include "BorrowedImageVk.h"
8 #include "Standalone.h"
9 #include "aemu/base/synchronization/Lock.h"
10 #include "tests/VkTestUtils.h"
11 #include "vulkan/VulkanDispatch.h"
12 
13 using gfxstream::DisplaySurface;
14 
15 namespace gfxstream {
16 namespace vk {
17 namespace {
18 
19 class DisplayVkTest : public ::testing::Test {
20    protected:
21     using RenderTexture = RenderTextureVk;
22 
SetUpTestCase()23     static void SetUpTestCase() { k_vk = vkDispatch(false); }
24 
SetUp()25     void SetUp() override {
26         // skip the test when testing without a window
27         if (!shouldUseWindow()) {
28             GTEST_SKIP();
29         }
30         ASSERT_NE(k_vk, nullptr);
31 
32         createInstance();
33         createWindowAndSurface();
34         m_window = createOrGetTestWindow(0, 0, k_width, k_height);
35         pickPhysicalDevice();
36         createLogicalDevice();
37         k_vk->vkGetDeviceQueue(m_vkDevice, m_compositorQueueFamilyIndex, 0, &m_compositorVkQueue);
38         m_compositorVkQueueLock = std::make_shared<android::base::Lock>();
39         k_vk->vkGetDeviceQueue(m_vkDevice, m_swapChainQueueFamilyIndex, 0, &m_swapChainVkQueue);
40         m_swapChainVkQueueLock = std::make_shared<android::base::Lock>();
41         VkCommandPoolCreateInfo commandPoolCi = {
42             .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
43             .queueFamilyIndex = m_compositorQueueFamilyIndex};
44         ASSERT_EQ(k_vk->vkCreateCommandPool(m_vkDevice, &commandPoolCi, nullptr, &m_vkCommandPool),
45                   VK_SUCCESS);
46         m_displayVk = std::make_unique<DisplayVk>(
47             *k_vk, m_vkPhysicalDevice, m_swapChainQueueFamilyIndex, m_compositorQueueFamilyIndex,
48             m_vkDevice, m_compositorVkQueue, m_compositorVkQueueLock, m_swapChainVkQueue,
49             m_swapChainVkQueueLock);
50         m_displaySurface = std::make_unique<gfxstream::DisplaySurface>(
51             k_width, k_height,
52             DisplaySurfaceVk::create(*k_vk, m_vkInstance, m_window->getNativeWindow()));
53         ASSERT_NE(m_displaySurface, nullptr);
54         m_displayVk->bindToSurface(m_displaySurface.get());
55     }
56 
TearDown()57     void TearDown() override {
58         if (shouldUseWindow()) {
59             ASSERT_EQ(k_vk->vkQueueWaitIdle(m_compositorVkQueue), VK_SUCCESS);
60             ASSERT_EQ(k_vk->vkQueueWaitIdle(m_swapChainVkQueue), VK_SUCCESS);
61 
62             m_displayVk.reset();
63             k_vk->vkDestroyCommandPool(m_vkDevice, m_vkCommandPool, nullptr);
64             k_vk->vkDestroyDevice(m_vkDevice, nullptr);
65             k_vk->vkDestroySurfaceKHR(m_vkInstance, m_vkSurface, nullptr);
66             k_vk->vkDestroyInstance(m_vkInstance, nullptr);
67         }
68     }
69 
createBorrowedImageInfo(const std::unique_ptr<const RenderTexture> & texture)70     std::unique_ptr<BorrowedImageInfoVk> createBorrowedImageInfo(
71         const std::unique_ptr<const RenderTexture>& texture) {
72         static uint32_t sTextureId = 0;
73 
74         auto info = std::make_unique<BorrowedImageInfoVk>();
75         info->id = sTextureId++;
76         info->width = texture->m_vkImageCreateInfo.extent.width;
77         info->height = texture->m_vkImageCreateInfo.extent.height;
78         info->image = texture->m_vkImage;
79         info->imageCreateInfo = texture->m_vkImageCreateInfo;
80         info->preBorrowLayout = RenderTexture::k_vkImageLayout;
81         info->preBorrowQueueFamilyIndex = m_compositorQueueFamilyIndex;
82         info->postBorrowLayout = RenderTexture::k_vkImageLayout;
83         info->postBorrowQueueFamilyIndex = m_compositorQueueFamilyIndex;
84         return info;
85     }
86 
87     static const VulkanDispatch* k_vk;
88     static constexpr uint32_t k_width = 0x100;
89     static constexpr uint32_t k_height = 0x100;
90 
91     OSWindow *m_window;
92     VkInstance m_vkInstance = VK_NULL_HANDLE;
93     VkSurfaceKHR m_vkSurface = VK_NULL_HANDLE;
94     VkPhysicalDevice m_vkPhysicalDevice = VK_NULL_HANDLE;
95     uint32_t m_swapChainQueueFamilyIndex = 0;
96     uint32_t m_compositorQueueFamilyIndex = 0;
97     VkDevice m_vkDevice = VK_NULL_HANDLE;
98     VkQueue m_compositorVkQueue = VK_NULL_HANDLE;
99     std::shared_ptr<android::base::Lock> m_compositorVkQueueLock;
100     VkQueue m_swapChainVkQueue = VK_NULL_HANDLE;
101     std::shared_ptr<android::base::Lock> m_swapChainVkQueueLock;
102     VkCommandPool m_vkCommandPool = VK_NULL_HANDLE;
103     std::unique_ptr<DisplayVk> m_displayVk = nullptr;
104     std::unique_ptr<DisplaySurface> m_displaySurface = nullptr;
105 
106    private:
createInstance()107     void createInstance() {
108         VkApplicationInfo appInfo = {.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
109                                      .pNext = nullptr,
110                                      .pApplicationName = "emulator SwapChainStateVk unittest",
111                                      .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
112                                      .pEngineName = "No Engine",
113                                      .engineVersion = VK_MAKE_VERSION(1, 0, 0),
114                                      .apiVersion = VK_API_VERSION_1_1};
115         auto extensions = SwapChainStateVk::getRequiredInstanceExtensions();
116         VkInstanceCreateInfo instanceCi = {
117             .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
118             .pApplicationInfo = &appInfo,
119             .enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
120             .ppEnabledExtensionNames = extensions.data()};
121         ASSERT_EQ(k_vk->vkCreateInstance(&instanceCi, nullptr, &m_vkInstance), VK_SUCCESS);
122         ASSERT_TRUE(m_vkInstance != VK_NULL_HANDLE);
123     }
124 
createWindowAndSurface()125     void createWindowAndSurface() {
126         m_window = createOrGetTestWindow(0, 0, k_width, k_height);
127         ASSERT_NE(m_window, nullptr);
128         // TODO(kaiyili, b/179477624): add support for other platforms
129 #ifdef _WIN32
130         VkWin32SurfaceCreateInfoKHR surfaceCi = {
131             .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
132             .hinstance = GetModuleHandle(nullptr),
133             .hwnd = m_window->getNativeWindow()};
134         ASSERT_EQ(k_vk->vkCreateWin32SurfaceKHR(m_vkInstance, &surfaceCi, nullptr, &m_vkSurface),
135                   VK_SUCCESS);
136 #endif
137     }
138 
pickPhysicalDevice()139     void pickPhysicalDevice() {
140         uint32_t physicalDeviceCount = 0;
141         ASSERT_EQ(k_vk->vkEnumeratePhysicalDevices(m_vkInstance, &physicalDeviceCount, nullptr),
142                   VK_SUCCESS);
143         ASSERT_GT(physicalDeviceCount, 0);
144         std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
145         ASSERT_EQ(k_vk->vkEnumeratePhysicalDevices(m_vkInstance, &physicalDeviceCount,
146                                                    physicalDevices.data()),
147                   VK_SUCCESS);
148         for (const auto &device : physicalDevices) {
149             uint32_t queueFamilyCount = 0;
150             k_vk->vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
151             ASSERT_GT(queueFamilyCount, 0);
152             std::vector<VkQueueFamilyProperties> queueProps(queueFamilyCount);
153             k_vk->vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount,
154                                                            queueProps.data());
155             std::optional<uint32_t> maybeSwapChainQueueFamilyIndex = std::nullopt;
156             std::optional<uint32_t> maybeCompositorQueueFamilyIndex = std::nullopt;
157             for (uint32_t queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount;
158                  queueFamilyIndex++) {
159                 if (!maybeSwapChainQueueFamilyIndex.has_value() &&
160                     SwapChainStateVk::validateQueueFamilyProperties(*k_vk, device, m_vkSurface,
161                                                                     queueFamilyIndex) &&
162                     SwapChainStateVk::createSwapChainCi(*k_vk, m_vkSurface, device, k_width,
163                                                         k_height, {queueFamilyIndex})) {
164                     maybeSwapChainQueueFamilyIndex = queueFamilyIndex;
165                 }
166                 if (!maybeCompositorQueueFamilyIndex.has_value() &&
167                     CompositorVk::queueSupportsComposition(queueProps[queueFamilyIndex])) {
168                     maybeCompositorQueueFamilyIndex = queueFamilyIndex;
169                 }
170             }
171             if (!maybeSwapChainQueueFamilyIndex.has_value() ||
172                 !maybeCompositorQueueFamilyIndex.has_value()) {
173                 continue;
174             }
175             m_swapChainQueueFamilyIndex = maybeSwapChainQueueFamilyIndex.value();
176             m_compositorQueueFamilyIndex = maybeCompositorQueueFamilyIndex.value();
177             m_vkPhysicalDevice = device;
178             return;
179         }
180         FAIL() << "Can't find a suitable VkPhysicalDevice.";
181     }
182 
createLogicalDevice()183     void createLogicalDevice() {
184         const float queuePriority = 1.0f;
185         std::vector<VkDeviceQueueCreateInfo> queueCis(0);
186         for (auto queueFamilyIndex : std::unordered_set<uint32_t>(
187                  {m_swapChainQueueFamilyIndex, m_compositorQueueFamilyIndex})) {
188             VkDeviceQueueCreateInfo queueCi = {.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
189                                                .queueFamilyIndex = queueFamilyIndex,
190                                                .queueCount = 1,
191                                                .pQueuePriorities = &queuePriority};
192             queueCis.push_back(queueCi);
193         }
194         VkPhysicalDeviceFeatures2 features = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
195                                               .pNext = nullptr};
196         auto extensions = SwapChainStateVk::getRequiredDeviceExtensions();
197         VkDeviceCreateInfo deviceCi = {
198             .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
199             .pNext = &features,
200             .queueCreateInfoCount = static_cast<uint32_t>(queueCis.size()),
201             .pQueueCreateInfos = queueCis.data(),
202             .enabledLayerCount = 0,
203             .enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
204             .ppEnabledExtensionNames = extensions.data(),
205             .pEnabledFeatures = nullptr};
206         ASSERT_EQ(k_vk->vkCreateDevice(m_vkPhysicalDevice, &deviceCi, nullptr, &m_vkDevice),
207                   VK_SUCCESS);
208         ASSERT_TRUE(m_vkDevice != VK_NULL_HANDLE);
209     }
210 };
211 
212 const VulkanDispatch* DisplayVkTest::k_vk = nullptr;
213 
TEST_F(DisplayVkTest,Init)214 TEST_F(DisplayVkTest, Init) {}
215 
TEST_F(DisplayVkTest,PostWithoutSurfaceShouldntCrash)216 TEST_F(DisplayVkTest, PostWithoutSurfaceShouldntCrash) {
217     uint32_t textureWidth = 20;
218     uint32_t textureHeight = 40;
219     DisplayVk displayVk(*k_vk, m_vkPhysicalDevice, m_swapChainQueueFamilyIndex,
220                         m_compositorQueueFamilyIndex, m_vkDevice, m_compositorVkQueue,
221                         m_compositorVkQueueLock, m_swapChainVkQueue, m_swapChainVkQueueLock);
222     auto texture = RenderTexture::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
223                                          m_vkCommandPool, textureWidth, textureHeight);
224     std::vector<uint32_t> pixels(textureWidth * textureHeight, 0);
225     ASSERT_TRUE(texture->write(pixels));
226     const auto imageInfo = createBorrowedImageInfo(texture);
227     displayVk.post(imageInfo.get());
228 }
229 
TEST_F(DisplayVkTest,SimplePost)230 TEST_F(DisplayVkTest, SimplePost) {
231     uint32_t textureWidth = 20;
232     uint32_t textureHeight = 40;
233     auto texture = RenderTexture::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
234                                          m_vkCommandPool, textureWidth, textureHeight);
235     std::vector<uint32_t> pixels(textureWidth * textureHeight);
236     for (int i = 0; i < textureHeight; i++) {
237         for (int j = 0; j < textureWidth; j++) {
238             uint8_t *pixel = reinterpret_cast<uint8_t *>(&pixels[i * textureWidth + j]);
239             pixel[0] = static_cast<uint8_t>((i * 0xff / textureHeight) & 0xff);
240             pixel[1] = static_cast<uint8_t>((j * 0xff / textureWidth) & 0xff);
241             pixel[2] = 0;
242             pixel[3] = 0xff;
243         }
244     }
245     ASSERT_TRUE(texture->write(pixels));
246     std::vector<std::shared_future<void>> waitForGpuFutures;
247     for (uint32_t i = 0; i < 10; i++) {
248         const auto imageInfo = createBorrowedImageInfo(texture);
249         auto postResult = m_displayVk->post(imageInfo.get());
250         ASSERT_TRUE(postResult.success);
251         waitForGpuFutures.emplace_back(std::move(postResult.postCompletedWaitable));
252     }
253     for (auto &waitForGpuFuture : waitForGpuFutures) {
254         waitForGpuFuture.wait();
255     }
256 }
257 
TEST_F(DisplayVkTest,PostTwoColorBuffers)258 TEST_F(DisplayVkTest, PostTwoColorBuffers) {
259     uint32_t textureWidth = 20;
260     uint32_t textureHeight = 40;
261     auto redTexture =
262         RenderTexture::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
263                               m_vkCommandPool, textureWidth, textureHeight);
264     auto greenTexture =
265         RenderTexture::create(*k_vk, m_vkDevice, m_vkPhysicalDevice, m_compositorVkQueue,
266                               m_vkCommandPool, textureWidth, textureHeight);
267     uint32_t red = 0xff0000ff;
268     uint32_t green = 0xff00ff00;
269     std::vector<uint32_t> redPixels(textureWidth * textureHeight, red);
270     std::vector<uint32_t> greenPixels(textureWidth * textureHeight, green);
271     ASSERT_TRUE(redTexture->write(redPixels));
272     ASSERT_TRUE(greenTexture->write(greenPixels));
273     std::vector<std::shared_future<void>> waitForGpuFutures;
274     for (uint32_t i = 0; i < 10; i++) {
275         const auto redImageInfo = createBorrowedImageInfo(redTexture);
276         const auto greenImageInfo = createBorrowedImageInfo(greenTexture);
277         auto redPostResult = m_displayVk->post(redImageInfo.get());
278         ASSERT_TRUE(redPostResult.success);
279         waitForGpuFutures.emplace_back(std::move(redPostResult.postCompletedWaitable));
280 
281         auto greenPostResult = m_displayVk->post(greenImageInfo.get());
282         ASSERT_TRUE(greenPostResult.success);
283         waitForGpuFutures.emplace_back(std::move(greenPostResult.postCompletedWaitable));
284     }
285     for (auto &waitForGpuFuture : waitForGpuFutures) {
286         waitForGpuFuture.wait();
287     }
288 }
289 
290 }  // namespace
291 }  // namespace vk
292 }  // namespace gfxstream
293