xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/wsi/vktWsiMaintenance1Tests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2022 Google Inc.
6  * Copyright (c) 2022 The Khronos Group Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief VK_EXT_surface_maintenance1 and VK_EXT_swapchain_maintenance1 extension tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktWsiMaintenance1Tests.hpp"
26 
27 #include "vktTestCaseUtil.hpp"
28 #include "vktTestGroupUtil.hpp"
29 #include "vktCustomInstancesDevices.hpp"
30 
31 #include "vkMemUtil.hpp"
32 #include "vkRefUtil.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkDeviceUtil.hpp"
35 #include "vkTypeUtil.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "vkWsiPlatform.hpp"
38 #include "vkWsiUtil.hpp"
39 
40 #include "deRandom.hpp"
41 
42 #include "tcuTestLog.hpp"
43 #include "tcuPlatform.hpp"
44 #include "tcuResultCollector.hpp"
45 #include "tcuCommandLine.hpp"
46 
47 #include <limits>
48 #include <random>
49 #include <set>
50 
51 #if (DE_OS == DE_OS_WIN32)
52 #define NOMINMAX
53 #define WIN32_LEAN_AND_MEAN
54 #include <windows.h>
55 #endif
56 
57 namespace vkt
58 {
59 namespace wsi
60 {
61 
62 namespace
63 {
64 
65 using namespace vk;
66 using namespace vk::wsi;
67 
68 typedef std::vector<VkExtensionProperties> Extensions;
69 
70 constexpr uint64_t kMaxFenceWaitTimeout = 2000000000ul;
71 
72 template <typename T>
checkAllSupported(const Extensions & supportedExtensions,const std::vector<T> & requiredExtensions)73 void checkAllSupported(const Extensions &supportedExtensions, const std::vector<T> &requiredExtensions)
74 {
75     for (auto &requiredExtension : requiredExtensions)
76     {
77         if (!isExtensionStructSupported(supportedExtensions, RequiredExtension(requiredExtension)))
78             TCU_THROW(NotSupportedError, (std::string(requiredExtension) + " is not supported").c_str());
79     }
80 }
81 
createInstanceWithWsi(Context & context,const Extensions & supportedExtensions,Type wsiType,bool requireDeviceGroup,const VkAllocationCallbacks * pAllocator=DE_NULL)82 CustomInstance createInstanceWithWsi(Context &context, const Extensions &supportedExtensions, Type wsiType,
83                                      bool requireDeviceGroup, const VkAllocationCallbacks *pAllocator = DE_NULL)
84 {
85     const uint32_t version = context.getUsedApiVersion();
86     std::vector<std::string> extensions;
87 
88     extensions.push_back("VK_KHR_surface");
89     extensions.push_back(getExtensionName(wsiType));
90     if (isDisplaySurface(wsiType))
91         extensions.push_back("VK_KHR_display");
92 
93     if (!vk::isCoreInstanceExtension(version, "VK_KHR_get_physical_device_properties2"))
94         extensions.push_back("VK_KHR_get_physical_device_properties2");
95 
96     if (isExtensionStructSupported(supportedExtensions, RequiredExtension("VK_KHR_get_surface_capabilities2")))
97         extensions.push_back("VK_KHR_get_surface_capabilities2");
98 
99     extensions.push_back("VK_EXT_surface_maintenance1");
100 
101     if (requireDeviceGroup)
102         extensions.push_back("VK_KHR_device_group_creation");
103 
104     checkAllSupported(supportedExtensions, extensions);
105 
106     return createCustomInstanceWithExtensions(context, extensions, pAllocator);
107 }
108 
getDeviceFeaturesForWsi(void)109 VkPhysicalDeviceFeatures getDeviceFeaturesForWsi(void)
110 {
111     VkPhysicalDeviceFeatures features;
112     deMemset(&features, 0, sizeof(features));
113     return features;
114 }
115 
createDeviceWithWsi(const vk::PlatformInterface & vkp,VkInstance instance,const InstanceInterface & vki,VkPhysicalDevice physicalDevice,const Extensions & supportedExtensions,const uint32_t queueFamilyIndex,const VkAllocationCallbacks * pAllocator,bool requireSwapchainMaintenance1,bool requireDeviceGroup,bool validationEnabled)116 Move<VkDevice> createDeviceWithWsi(const vk::PlatformInterface &vkp, VkInstance instance, const InstanceInterface &vki,
117                                    VkPhysicalDevice physicalDevice, const Extensions &supportedExtensions,
118                                    const uint32_t queueFamilyIndex, const VkAllocationCallbacks *pAllocator,
119                                    bool requireSwapchainMaintenance1, bool requireDeviceGroup, bool validationEnabled)
120 {
121     const float queuePriorities[]              = {1.0f};
122     const VkDeviceQueueCreateInfo queueInfos[] = {{
123         VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
124         DE_NULL,
125         (VkDeviceQueueCreateFlags)0,
126         queueFamilyIndex,
127         DE_LENGTH_OF_ARRAY(queuePriorities),
128         &queuePriorities[0],
129     }};
130     const VkPhysicalDeviceFeatures features    = getDeviceFeaturesForWsi();
131     std::vector<const char *> extensions;
132 
133     extensions.push_back("VK_KHR_swapchain");
134     if (requireSwapchainMaintenance1)
135     {
136         extensions.push_back("VK_EXT_swapchain_maintenance1");
137     }
138     if (requireDeviceGroup)
139     {
140         extensions.push_back("VK_KHR_device_group");
141     }
142     if (isExtensionStructSupported(supportedExtensions, RequiredExtension("VK_KHR_shared_presentable_image")))
143     {
144         extensions.push_back("VK_KHR_shared_presentable_image");
145     }
146 
147     checkAllSupported(supportedExtensions, extensions);
148 
149     VkDeviceCreateInfo deviceParams = {
150         VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
151         DE_NULL,
152         (VkDeviceCreateFlags)0,
153         DE_LENGTH_OF_ARRAY(queueInfos),
154         &queueInfos[0],
155         0u,      // enabledLayerCount
156         DE_NULL, // ppEnabledLayerNames
157         (uint32_t)extensions.size(),
158         extensions.empty() ? DE_NULL : &extensions[0],
159         &features,
160     };
161 
162     return createCustomDevice(validationEnabled, vkp, instance, vki, physicalDevice, &deviceParams, pAllocator);
163 }
164 
165 struct InstanceHelper
166 {
167     const std::vector<VkExtensionProperties> supportedExtensions;
168     const CustomInstance instance;
169     const InstanceDriver &vki;
170 
InstanceHelpervkt::wsi::__anonfb4237260111::InstanceHelper171     InstanceHelper(Context &context, Type wsiType, bool requireDeviceGroup,
172                    const VkAllocationCallbacks *pAllocator = DE_NULL)
173         : supportedExtensions(enumerateInstanceExtensionProperties(context.getPlatformInterface(), DE_NULL))
174         , instance(createInstanceWithWsi(context, supportedExtensions, wsiType, requireDeviceGroup, pAllocator))
175         , vki(instance.getDriver())
176     {
177     }
178 };
179 
180 struct DeviceHelper
181 {
182     const VkPhysicalDevice physicalDevice;
183     const uint32_t queueFamilyIndex;
184     const Unique<VkDevice> device;
185     const DeviceDriver vkd;
186     const VkQueue queue;
187 
DeviceHelpervkt::wsi::__anonfb4237260111::DeviceHelper188     DeviceHelper(Context &context, const InstanceInterface &vki, VkInstance instance, VkSurfaceKHR surface,
189                  bool requireSwapchainMaintenance1, bool requireDeviceGroup,
190                  const VkAllocationCallbacks *pAllocator = DE_NULL)
191         : physicalDevice(chooseDevice(vki, instance, context.getTestContext().getCommandLine()))
192         , queueFamilyIndex(chooseQueueFamilyIndex(vki, physicalDevice, surface))
193         , device(createDeviceWithWsi(context.getPlatformInterface(), instance, vki, physicalDevice,
194                                      enumerateDeviceExtensionProperties(vki, physicalDevice, DE_NULL), queueFamilyIndex,
195                                      pAllocator, requireSwapchainMaintenance1, requireDeviceGroup,
196                                      context.getTestContext().getCommandLine().isValidationEnabled()))
197         , vkd(context.getPlatformInterface(), instance, *device, context.getUsedApiVersion(),
198               context.getTestContext().getCommandLine())
199         , queue(getDeviceQueue(vkd, *device, queueFamilyIndex, 0))
200     {
201     }
202 };
203 
createDisplay(const vk::Platform & platform,const Extensions & supportedExtensions,Type wsiType)204 de::MovePtr<Display> createDisplay(const vk::Platform &platform, const Extensions &supportedExtensions, Type wsiType)
205 {
206     try
207     {
208         return de::MovePtr<Display>(platform.createWsiDisplay(wsiType));
209     }
210     catch (const tcu::NotSupportedError &e)
211     {
212         if (isExtensionStructSupported(supportedExtensions, RequiredExtension(getExtensionName(wsiType))) &&
213             platform.hasDisplay(wsiType))
214         {
215             // If VK_KHR_{platform}_surface was supported, vk::Platform implementation
216             // must support creating native display & window for that WSI type.
217             throw tcu::TestError(e.getMessage());
218         }
219         else
220             throw;
221     }
222 }
223 
createWindow(const Display & display,const tcu::Maybe<tcu::UVec2> & initialSize)224 de::MovePtr<Window> createWindow(const Display &display, const tcu::Maybe<tcu::UVec2> &initialSize)
225 {
226     try
227     {
228         return de::MovePtr<Window>(display.createWindow(initialSize));
229     }
230     catch (const tcu::NotSupportedError &e)
231     {
232         // See createDisplay - assuming that wsi::Display was supported platform port
233         // should also support creating a window.
234         throw tcu::TestError(e.getMessage());
235     }
236 }
237 
238 constexpr uint32_t kDefaultWindowWidth  = 128;
239 constexpr uint32_t kDefaultWindowHeight = 256;
240 
241 struct TestNativeObjects
242 {
243     const de::UniquePtr<Display> display;
244     tcu::UVec2 windowSize;
245     std::vector<de::MovePtr<Window>> windows;
246 
TestNativeObjectsvkt::wsi::__anonfb4237260111::TestNativeObjects247     TestNativeObjects(Context &context, const Extensions &supportedExtensions, Type wsiType, uint32_t windowCount)
248         : display(
249               createDisplay(context.getTestContext().getPlatform().getVulkanPlatform(), supportedExtensions, wsiType))
250         , windowSize(tcu::UVec2(kDefaultWindowWidth, kDefaultWindowHeight))
251     {
252         for (uint32_t i = 0; i < windowCount; ++i)
253         {
254             windows.push_back(createWindow(*display, windowSize));
255             windows.back()->setVisible(true);
256             if (wsiType == TYPE_WIN32)
257             {
258                 windows.back()->setForeground();
259             }
260         }
261     }
262 };
263 
getBasicSwapchainParameters(VkSurfaceKHR surface,VkSurfaceFormatKHR surfaceFormat,const tcu::UVec2 & desiredSize,VkPresentModeKHR presentMode,VkSurfaceTransformFlagBitsKHR transform,uint32_t desiredImageCount,bool deferMemoryAllocation)264 VkSwapchainCreateInfoKHR getBasicSwapchainParameters(VkSurfaceKHR surface, VkSurfaceFormatKHR surfaceFormat,
265                                                      const tcu::UVec2 &desiredSize, VkPresentModeKHR presentMode,
266                                                      VkSurfaceTransformFlagBitsKHR transform,
267                                                      uint32_t desiredImageCount, bool deferMemoryAllocation)
268 {
269     const VkSwapchainCreateInfoKHR parameters = {
270         VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
271         DE_NULL,
272         (VkSwapchainCreateFlagsKHR)(deferMemoryAllocation ? VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT : 0),
273         surface,
274         desiredImageCount,
275         surfaceFormat.format,
276         surfaceFormat.colorSpace,
277         vk::makeExtent2D(desiredSize.x(), desiredSize.y()),
278         1u, // imageArrayLayers
279         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
280         VK_SHARING_MODE_EXCLUSIVE,
281         0u,
282         (const uint32_t *)DE_NULL,
283         transform,
284         VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
285         presentMode,
286         VK_FALSE, // clipped
287         DE_NULL,  // oldSwapchain
288     };
289 
290     return parameters;
291 }
292 
getPhysicalDeviceSurfaceCapabilities(const vk::InstanceInterface & vki,VkPhysicalDevice physicalDevice,VkSurfaceKHR surface,VkImageUsageFlags * sharedImageUsage)293 VkSurfaceCapabilitiesKHR getPhysicalDeviceSurfaceCapabilities(const vk::InstanceInterface &vki,
294                                                               VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
295                                                               VkImageUsageFlags *sharedImageUsage)
296 {
297     const VkPhysicalDeviceSurfaceInfo2KHR info = {
298         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
299         DE_NULL,
300         surface,
301     };
302     VkSharedPresentSurfaceCapabilitiesKHR sharedCapabilities;
303     VkSurfaceCapabilities2KHR capabilities;
304 
305     sharedCapabilities.sType = VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR;
306     sharedCapabilities.pNext = DE_NULL;
307 
308     capabilities.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR;
309     capabilities.pNext = sharedImageUsage ? &sharedCapabilities : DE_NULL;
310 
311     VK_CHECK(vki.getPhysicalDeviceSurfaceCapabilities2KHR(physicalDevice, &info, &capabilities));
312 
313     if (sharedImageUsage)
314     {
315         *sharedImageUsage = sharedCapabilities.sharedPresentSupportedUsageFlags;
316     }
317 
318     return capabilities.surfaceCapabilities;
319 }
320 
getSurfaceCompatiblePresentModes(const vk::InstanceInterface & vki,VkPhysicalDevice physicalDevice,VkSurfaceKHR surface,VkPresentModeKHR presentMode)321 std::vector<VkPresentModeKHR> getSurfaceCompatiblePresentModes(const vk::InstanceInterface &vki,
322                                                                VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
323                                                                VkPresentModeKHR presentMode)
324 {
325     VkSurfacePresentModeEXT presentModeInfo = {
326         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
327         DE_NULL,
328         presentMode,
329     };
330     const VkPhysicalDeviceSurfaceInfo2KHR info = {
331         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
332         &presentModeInfo,
333         surface,
334     };
335 
336     // Currently there are 6 present modes, 100 should cover all future ones!
337     std::vector<VkPresentModeKHR> compatibleModes(100);
338 
339     VkSurfacePresentModeCompatibilityEXT compatibility = {
340         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT,
341         DE_NULL,
342         (uint32_t)compatibleModes.size(),
343         compatibleModes.data(),
344     };
345     VkSurfaceCapabilities2KHR capabilities = {
346         VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
347         &compatibility,
348         {},
349     };
350 
351     VK_CHECK(vki.getPhysicalDeviceSurfaceCapabilities2KHR(physicalDevice, &info, &capabilities));
352 
353     compatibleModes.resize(compatibility.presentModeCount);
354     return compatibleModes;
355 }
356 
getSurfaceScalingCapabilities(const vk::InstanceInterface & vki,VkPhysicalDevice physicalDevice,VkPresentModeKHR presentMode,VkSurfaceKHR surface)357 VkSurfacePresentScalingCapabilitiesEXT getSurfaceScalingCapabilities(const vk::InstanceInterface &vki,
358                                                                      VkPhysicalDevice physicalDevice,
359                                                                      VkPresentModeKHR presentMode, VkSurfaceKHR surface)
360 {
361     VkSurfacePresentModeEXT presentModeInfo = {
362         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
363         DE_NULL,
364         presentMode,
365     };
366     const VkPhysicalDeviceSurfaceInfo2KHR info = {
367         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
368         &presentModeInfo,
369         surface,
370     };
371 
372     VkSurfacePresentScalingCapabilitiesEXT scaling = {
373         VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT, DE_NULL, 0, 0, 0, {}, {},
374     };
375     VkSurfaceCapabilities2KHR capabilities = {
376         VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
377         &scaling,
378         {},
379     };
380 
381     VK_CHECK(vki.getPhysicalDeviceSurfaceCapabilities2KHR(physicalDevice, &info, &capabilities));
382 
383     return scaling;
384 }
385 
getPerPresentSurfaceCapabilities(const vk::InstanceInterface & vki,VkPhysicalDevice physicalDevice,VkSurfaceKHR surface,VkPresentModeKHR presentMode)386 VkSurfaceCapabilitiesKHR getPerPresentSurfaceCapabilities(const vk::InstanceInterface &vki,
387                                                           VkPhysicalDevice physicalDevice, VkSurfaceKHR surface,
388                                                           VkPresentModeKHR presentMode)
389 {
390     VkSurfacePresentModeEXT presentModeInfo = {
391         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
392         DE_NULL,
393         presentMode,
394     };
395     const VkPhysicalDeviceSurfaceInfo2KHR info = {
396         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
397         &presentModeInfo,
398         surface,
399     };
400 
401     VkSurfaceCapabilities2KHR capabilities = {
402         VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
403         DE_NULL,
404         {},
405     };
406 
407     VK_CHECK(vki.getPhysicalDeviceSurfaceCapabilities2KHR(physicalDevice, &info, &capabilities));
408 
409     return capabilities.surfaceCapabilities;
410 }
411 
412 typedef de::SharedPtr<Unique<VkCommandBuffer>> CommandBufferSp;
413 typedef de::SharedPtr<Unique<VkFence>> FenceSp;
414 typedef de::SharedPtr<Unique<VkSemaphore>> SemaphoreSp;
415 typedef de::SharedPtr<Unique<VkImage>> ImageSp;
416 
createFences(const DeviceInterface & vkd,const VkDevice device,size_t numFences)417 std::vector<FenceSp> createFences(const DeviceInterface &vkd, const VkDevice device, size_t numFences)
418 {
419     std::vector<FenceSp> fences(numFences);
420 
421     for (size_t ndx = 0; ndx < numFences; ++ndx)
422         fences[ndx] = FenceSp(new Unique<VkFence>(createFence(vkd, device)));
423 
424     return fences;
425 }
426 
createSemaphores(const DeviceInterface & vkd,const VkDevice device,size_t numSemaphores)427 std::vector<SemaphoreSp> createSemaphores(const DeviceInterface &vkd, const VkDevice device, size_t numSemaphores)
428 {
429     std::vector<SemaphoreSp> semaphores(numSemaphores);
430 
431     for (size_t ndx = 0; ndx < numSemaphores; ++ndx)
432         semaphores[ndx] = SemaphoreSp(new Unique<VkSemaphore>(createSemaphore(vkd, device)));
433 
434     return semaphores;
435 }
436 
allocateCommandBuffers(const DeviceInterface & vkd,const VkDevice device,const VkCommandPool commandPool,const VkCommandBufferLevel level,const size_t numCommandBuffers)437 std::vector<CommandBufferSp> allocateCommandBuffers(const DeviceInterface &vkd, const VkDevice device,
438                                                     const VkCommandPool commandPool, const VkCommandBufferLevel level,
439                                                     const size_t numCommandBuffers)
440 {
441     std::vector<CommandBufferSp> buffers(numCommandBuffers);
442 
443     for (size_t ndx = 0; ndx < numCommandBuffers; ++ndx)
444         buffers[ndx] =
445             CommandBufferSp(new Unique<VkCommandBuffer>(allocateCommandBuffer(vkd, device, commandPool, level)));
446 
447     return buffers;
448 }
449 
createBufferAndBindMemory(const DeviceHelper & devHelper,SimpleAllocator & allocator,const tcu::UVec4 color,uint32_t count,de::MovePtr<Allocation> * pAlloc)450 Move<VkBuffer> createBufferAndBindMemory(const DeviceHelper &devHelper, SimpleAllocator &allocator,
451                                          const tcu::UVec4 color, uint32_t count, de::MovePtr<Allocation> *pAlloc)
452 {
453     const DeviceInterface &vkd = devHelper.vkd;
454     const VkDevice device      = *devHelper.device;
455     const uint32_t queueIndex  = devHelper.queueFamilyIndex;
456 
457     const VkBufferCreateInfo bufferParams = {
458         VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType      sType;
459         DE_NULL,                              // const void*          pNext;
460         0u,                                   // VkBufferCreateFlags  flags;
461         count * 4,                            // VkDeviceSize         size;
462         vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT, // VkBufferUsageFlags   usage;
463         VK_SHARING_MODE_EXCLUSIVE,            // VkSharingMode        sharingMode;
464         1u,                                   // uint32_t             queueFamilyCount;
465         &queueIndex                           // const uint32_t*      pQueueFamilyIndices;
466     };
467 
468     Move<VkBuffer> buffer = createBuffer(vkd, device, &bufferParams);
469 
470     *pAlloc = allocator.allocate(getBufferMemoryRequirements(vkd, device, *buffer), MemoryRequirement::HostVisible);
471     VK_CHECK(vkd.bindBufferMemory(device, *buffer, (*pAlloc)->getMemory(), (*pAlloc)->getOffset()));
472 
473     // Upload color to buffer.  Assuming RGBA, but surface format could be different, such as BGRA.  For the purposes of the test, that doesn't matter.
474     const uint32_t color32 = color.x() | color.y() << 8 | color.z() << 16 | color.w() << 24;
475     std::vector<uint32_t> colors(count, color32);
476     deMemcpy((*pAlloc)->getHostPtr(), colors.data(), colors.size() * sizeof(colors[0]));
477     flushAlloc(vkd, device, **pAlloc);
478 
479     return buffer;
480 }
481 
copyBufferToImage(const DeviceInterface & vkd,VkCommandBuffer commandBuffer,VkBuffer buffer,VkImage image,const tcu::UVec2 offset,const tcu::UVec2 extent)482 void copyBufferToImage(const DeviceInterface &vkd, VkCommandBuffer commandBuffer, VkBuffer buffer, VkImage image,
483                        const tcu::UVec2 offset, const tcu::UVec2 extent)
484 {
485     const VkBufferImageCopy region = {
486         0,
487         0,
488         0,
489         {
490             vk::VK_IMAGE_ASPECT_COLOR_BIT,
491             0,
492             0,
493             1,
494         },
495         {(int32_t)offset.x(), (int32_t)offset.y(), 0},
496         {
497             extent.x(),
498             extent.y(),
499             1u,
500         },
501     };
502 
503     vkd.cmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
504 }
505 
506 struct PresentFenceTestConfig
507 {
508     vk::wsi::Type wsiType;
509     std::vector<VkPresentModeKHR> modes;
510     bool deferMemoryAllocation;
511     bool bindImageMemory;
512     bool changePresentModes;
513     bool verifyFenceOrdering;
514 };
515 
canDoMultiSwapchainPresent(vk::wsi::Type wsiType)516 bool canDoMultiSwapchainPresent(vk::wsi::Type wsiType)
517 {
518     // Android has a bug with the implementation of multi-swapchain present.
519     // This bug has existed since Vulkan 1.0 and is unrelated to
520     // VK_EXT_swapchain_maintenance1.  Once that bug is fixed, multi-swapchain
521     // present tests can be enabled for this platform.
522     return wsiType != TYPE_ANDROID;
523 }
524 
getIterations(std::vector<VkPresentModeKHR> presentModes,std::vector<std::vector<VkPresentModeKHR>> compatiblePresentModes,bool testResizesWindowsFrequently)525 uint32_t getIterations(std::vector<VkPresentModeKHR> presentModes,
526                        std::vector<std::vector<VkPresentModeKHR>> compatiblePresentModes,
527                        bool testResizesWindowsFrequently)
528 {
529     // Look at all the modes that will be used by the test.
530     bool hasFifo    = false;
531     bool hasShared  = false;
532     bool hasNoVsync = false;
533 
534     std::set<VkPresentModeKHR> allModes;
535 
536     for (VkPresentModeKHR mode : presentModes)
537         allModes.insert(mode);
538 
539     for (const auto &compatibleModes : compatiblePresentModes)
540         for (VkPresentModeKHR mode : compatibleModes)
541             allModes.insert(mode);
542 
543     for (VkPresentModeKHR mode : allModes)
544     {
545         switch (mode)
546         {
547         case VK_PRESENT_MODE_FIFO_KHR:
548         case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
549             hasFifo = true;
550             break;
551         case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR:
552         case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR:
553             hasShared = true;
554             break;
555         case VK_PRESENT_MODE_IMMEDIATE_KHR:
556         case VK_PRESENT_MODE_MAILBOX_KHR:
557         default:
558             hasNoVsync = true;
559             break;
560         }
561     }
562 
563     // Return an iteration count that is as high as possible while keeping the test time and memory usage reasonable.
564     //
565     // - If FIFO is used, limit to 120 (~2s on 60Hz)
566     // - Else, limit to 1000
567 
568     if (hasFifo)
569         return testResizesWindowsFrequently ? 60 : 120;
570 
571     (void)hasShared;
572     (void)hasNoVsync;
573     uint32_t iterations = 1000;
574 
575     // If the test resizes windows frequently, reduce the testing time as that's a very slow operation.
576     if (testResizesWindowsFrequently)
577         iterations /= 50;
578 
579     return iterations;
580 }
581 
bindSingleImageMemory(const DeviceInterface & vkd,const VkDevice device,const VkSwapchainKHR swapchain,const VkSwapchainCreateInfoKHR swapchainCreateInfo,uint32_t imageIndex)582 ImageSp bindSingleImageMemory(const DeviceInterface &vkd, const VkDevice device, const VkSwapchainKHR swapchain,
583                               const VkSwapchainCreateInfoKHR swapchainCreateInfo, uint32_t imageIndex)
584 {
585     VkImageSwapchainCreateInfoKHR imageSwapchainCreateInfo = {
586         VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR,
587         DE_NULL,
588         swapchain,
589     };
590 
591     VkImageCreateInfo imageCreateInfo = {
592         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
593         &imageSwapchainCreateInfo,
594         (VkImageCreateFlags)0u,          // flags
595         VK_IMAGE_TYPE_2D,                // imageType
596         swapchainCreateInfo.imageFormat, // format
597         {
598             // extent
599             swapchainCreateInfo.imageExtent.width,  //   width
600             swapchainCreateInfo.imageExtent.height, //   height
601             1u,                                     //   depth
602         },
603         1u,                             // mipLevels
604         1u,                             // arrayLayers
605         VK_SAMPLE_COUNT_1_BIT,          // samples
606         VK_IMAGE_TILING_OPTIMAL,        // tiling
607         swapchainCreateInfo.imageUsage, // usage
608         VK_SHARING_MODE_EXCLUSIVE,      // sharingMode
609         0u,                             // queueFamilyIndexCount
610         DE_NULL,                        // pQueueFamilyIndices
611         VK_IMAGE_LAYOUT_UNDEFINED,      // initialLayout
612     };
613 
614     ImageSp image = ImageSp(new Unique<VkImage>(createImage(vkd, device, &imageCreateInfo)));
615 
616     VkBindImageMemorySwapchainInfoKHR bimSwapchainInfo = {
617         VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR,
618         DE_NULL,
619         swapchain,
620         imageIndex,
621     };
622 
623     VkBindImageMemoryInfo bimInfo = {
624         VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, &bimSwapchainInfo, **image, DE_NULL, 0u,
625     };
626 
627     VK_CHECK(vkd.bindImageMemory2(device, 1, &bimInfo));
628 
629     return image;
630 }
631 
bindImageMemory(const DeviceInterface & vkd,const VkDevice device,const VkSwapchainKHR swapchain,const VkSwapchainCreateInfoKHR swapchainCreateInfo)632 std::vector<ImageSp> bindImageMemory(const DeviceInterface &vkd, const VkDevice device, const VkSwapchainKHR swapchain,
633                                      const VkSwapchainCreateInfoKHR swapchainCreateInfo)
634 {
635     uint32_t numImages = 0;
636     VK_CHECK(vkd.getSwapchainImagesKHR(device, swapchain, &numImages, DE_NULL));
637 
638     std::vector<ImageSp> images(numImages);
639 
640     for (uint32_t i = 0; i < numImages; ++i)
641     {
642         images[i] = bindSingleImageMemory(vkd, device, swapchain, swapchainCreateInfo, i);
643     }
644 
645     return images;
646 }
647 
verifyFenceSignalOrdering(const DeviceInterface & vkd,const VkDevice device,const std::vector<FenceSp> & fences,const uint32_t stride,const uint32_t offset,const uint32_t lastKnownSignaled,const uint32_t maxIndex,tcu::ResultCollector * results)648 void verifyFenceSignalOrdering(const DeviceInterface &vkd, const VkDevice device, const std::vector<FenceSp> &fences,
649                                const uint32_t stride, const uint32_t offset, const uint32_t lastKnownSignaled,
650                                const uint32_t maxIndex, tcu::ResultCollector *results)
651 {
652     // Go over fences from end to last-known-signaled.  Verify that fences are
653     // signaled in order by making sure that a consecutive set of fences are
654     // encountered that are not signaled, followed by potentially a number of
655     // fences that are.
656     bool visitedSignaledFence = false;
657     for (uint32_t i = maxIndex; i > lastKnownSignaled; --i)
658     {
659         const VkFence fence = **fences[(i - 1) * stride + offset];
660         bool isSignaled     = vkd.getFenceStatus(device, fence) != VK_NOT_READY;
661 
662         // Ordering guarantee is broken if an unsignaled fence is encountered when a later fence is signaled.
663         results->check(isSignaled || !visitedSignaledFence,
664                        "Encountered unsignaled fence while a later fence is signaled");
665 
666         if (isSignaled)
667         {
668             visitedSignaledFence = true;
669         }
670     }
671 }
672 
presentFenceTest(Context & context,const PresentFenceTestConfig testParams)673 tcu::TestStatus presentFenceTest(Context &context, const PresentFenceTestConfig testParams)
674 {
675     tcu::TestLog &log = context.getTestContext().getLog();
676     tcu::ResultCollector results(log);
677 
678     const uint32_t surfaceCount = (uint32_t)testParams.modes.size();
679     const InstanceHelper instHelper(context, testParams.wsiType, testParams.bindImageMemory);
680     const TestNativeObjects native(context, instHelper.supportedExtensions, testParams.wsiType, surfaceCount);
681     std::vector<Move<VkSurfaceKHR>> surfaces;
682     for (uint32_t i = 0; i < surfaceCount; ++i)
683     {
684         surfaces.push_back(createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display,
685                                          *native.windows[i], context.getTestContext().getCommandLine()));
686     }
687 
688     const DeviceHelper devHelper(context, instHelper.vki, instHelper.instance, *surfaces[0], true,
689                                  testParams.bindImageMemory);
690     const DeviceInterface &vkd = devHelper.vkd;
691     const VkDevice device      = *devHelper.device;
692 
693     for (uint32_t i = 0; i < surfaceCount; ++i)
694     {
695         const std::vector<VkPresentModeKHR> presentModes =
696             getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surfaces[i]);
697         if (std::find(presentModes.begin(), presentModes.end(), testParams.modes[i]) == presentModes.end())
698             TCU_THROW(NotSupportedError, "Present mode not supported");
699     }
700 
701     std::vector<VkSurfaceFormatKHR> surfaceFormats =
702         getPhysicalDeviceSurfaceFormats(instHelper.vki, devHelper.physicalDevice, *surfaces[0]);
703     if (surfaceFormats.empty())
704         return tcu::TestStatus::fail("No VkSurfaceFormatKHR defined");
705 
706     std::vector<bool> isSharedPresentMode(surfaceCount);
707 
708     for (uint32_t i = 0; i < surfaceCount; ++i)
709     {
710         isSharedPresentMode[i] = testParams.modes[i] == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
711                                  testParams.modes[i] == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
712     }
713 
714     std::vector<VkSwapchainCreateInfoKHR> swapchainInfo;
715     std::vector<Move<VkSwapchainKHR>> swapchains;
716     std::vector<VkSwapchainKHR> swapchainHandles;
717     std::vector<std::vector<VkImage>> swapchainImages;
718     std::vector<std::vector<ImageSp>> bimImages;
719     std::vector<std::vector<VkPresentModeKHR>> compatiblePresentModes;
720     for (uint32_t i = 0; i < surfaceCount; ++i)
721     {
722         VkImageUsageFlags sharedImageUsage = 0;
723         const VkSurfaceCapabilitiesKHR capabilities =
724             getPhysicalDeviceSurfaceCapabilities(instHelper.vki, devHelper.physicalDevice, *surfaces[i],
725                                                  isSharedPresentMode[i] ? &sharedImageUsage : DE_NULL);
726         const VkSurfaceTransformFlagBitsKHR transform =
727             (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0 ?
728                 VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR :
729                 capabilities.currentTransform;
730 
731         if (isSharedPresentMode[i] && (sharedImageUsage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0)
732             TCU_THROW(NotSupportedError, "Transfer dst with shared present mode not supported");
733 
734         swapchainInfo.push_back(getBasicSwapchainParameters(
735             *surfaces[i], surfaceFormats[0], native.windowSize, testParams.modes[i], transform,
736             isSharedPresentMode[i] ? 1 : capabilities.minImageCount, testParams.deferMemoryAllocation));
737 
738         VkSwapchainPresentModesCreateInfoEXT compatibleModesCreateInfo = {
739             VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT,
740             DE_NULL,
741             0,
742             DE_NULL,
743         };
744         if (testParams.changePresentModes)
745         {
746             compatiblePresentModes.push_back(getSurfaceCompatiblePresentModes(instHelper.vki, devHelper.physicalDevice,
747                                                                               *surfaces[i], testParams.modes[i]));
748 
749             compatibleModesCreateInfo.presentModeCount = (uint32_t)compatiblePresentModes.back().size();
750             compatibleModesCreateInfo.pPresentModes    = compatiblePresentModes.back().data();
751             swapchainInfo.back().pNext                 = &compatibleModesCreateInfo;
752         }
753 
754         swapchains.push_back(createSwapchainKHR(vkd, device, &swapchainInfo.back()));
755         swapchainHandles.push_back(*swapchains.back());
756 
757         if (testParams.bindImageMemory)
758         {
759             uint32_t numImages = 0;
760             VK_CHECK(vkd.getSwapchainImagesKHR(device, *swapchains.back(), &numImages, DE_NULL));
761             swapchainImages.push_back(std::vector<VkImage>(numImages, DE_NULL));
762 
763             // If memory allocation is deferred, bind image memory lazily at acquire time.
764             if (testParams.deferMemoryAllocation)
765             {
766                 bimImages.push_back(std::vector<ImageSp>(numImages));
767             }
768             else
769             {
770                 bimImages.push_back(bindImageMemory(vkd, device, *swapchains.back(), swapchainInfo.back()));
771                 for (size_t j = 0; j < bimImages.back().size(); ++j)
772                 {
773                     swapchainImages.back()[j] = **bimImages.back()[j];
774                 }
775             }
776         }
777         else
778         {
779             swapchainImages.push_back(getSwapchainImages(vkd, device, *swapchains.back()));
780         }
781     }
782 
783     const Unique<VkCommandPool> commandPool(
784         createCommandPool(vkd, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, devHelper.queueFamilyIndex));
785 
786     const uint32_t iterations = getIterations(testParams.modes, compatiblePresentModes, false);
787 
788     // Do iterations presents, each with an associated fence.  Destroy the wait semaphores as soon as the corresponding fence signals.
789     const std::vector<FenceSp> presentFences(createFences(vkd, device, iterations * surfaceCount));
790     const std::vector<SemaphoreSp> acquireSems(createSemaphores(vkd, device, iterations * surfaceCount));
791     std::vector<SemaphoreSp> presentSems(createSemaphores(vkd, device, iterations));
792 
793     const std::vector<CommandBufferSp> commandBuffers(
794         allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, iterations));
795 
796     const uint64_t foreverNs = 0xFFFFFFFFFFFFFFFFul;
797 
798     VkImageSubresourceRange range = {
799         VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1,
800     };
801 
802     const uint32_t configHash =
803         (uint32_t)testParams.wsiType | (uint32_t)testParams.modes[0] << 4 |
804         (uint32_t)testParams.deferMemoryAllocation << 28 | (uint32_t)testParams.bindImageMemory << 29 |
805         (uint32_t)testParams.changePresentModes << 30 | (uint32_t)testParams.verifyFenceOrdering << 31;
806     de::Random rng(0x53A4C8A1u ^ configHash);
807 
808     try
809     {
810         std::vector<uint32_t> nextUnfinishedPresent(surfaceCount, 0);
811 
812         for (uint32_t i = 0; i < iterations; ++i)
813         {
814             const VkSemaphore *presentSem = &**presentSems[i];
815             std::vector<VkSemaphore> acquireSem;
816             std::vector<VkFence> presentFence;
817             std::vector<uint32_t> imageIndex(surfaceCount, 0x12345); // initialize to junk value
818             // Acquire an image and clear it
819             beginCommandBuffer(vkd, **commandBuffers[i], 0u);
820 
821             VkImageMemoryBarrier barrier = {
822                 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
823                 DE_NULL,
824                 0,
825                 0,
826                 VK_IMAGE_LAYOUT_UNDEFINED,
827                 VK_IMAGE_LAYOUT_UNDEFINED,
828                 VK_QUEUE_FAMILY_IGNORED,
829                 VK_QUEUE_FAMILY_IGNORED,
830                 DE_NULL,
831                 range,
832             };
833 
834             for (uint32_t j = 0; j < surfaceCount; ++j)
835             {
836                 acquireSem.push_back(**acquireSems[i * surfaceCount + j]);
837                 presentFence.push_back(**presentFences[i * surfaceCount + j]);
838 
839                 VK_CHECK(
840                     vkd.acquireNextImageKHR(device, *swapchains[j], foreverNs, acquireSem[j], DE_NULL, &imageIndex[j]));
841 
842                 // If memory allocation is deferred and bind image memory is used, lazily bind image memory now if this is the first time the image is acquired.
843                 VkImage &acquiredImage = swapchainImages[j][imageIndex[j]];
844                 if (acquiredImage == DE_NULL)
845                 {
846                     DE_ASSERT(testParams.bindImageMemory && testParams.deferMemoryAllocation);
847                     DE_ASSERT(!bimImages[j][imageIndex[j]]);
848 
849                     bimImages[j][imageIndex[j]] =
850                         bindSingleImageMemory(vkd, device, *swapchains[j], swapchainInfo[j], imageIndex[j]);
851                     acquiredImage = **bimImages[j][imageIndex[j]];
852                 }
853 
854                 barrier.newLayout =
855                     isSharedPresentMode[j] ? VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
856                 barrier.image = acquiredImage;
857 
858                 vkd.cmdPipelineBarrier(**commandBuffers[i], VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
859                                        VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0, DE_NULL, 0, DE_NULL, 1, &barrier);
860             }
861 
862             for (uint32_t j = 0; j < surfaceCount; ++j)
863             {
864                 VkClearColorValue clearValue;
865                 clearValue.float32[0] = static_cast<float>((i + j * 5) % 33) / 32.0f;
866                 clearValue.float32[1] = static_cast<float>(((i + j * 5) + 7) % 33) / 32.0f;
867                 clearValue.float32[2] = static_cast<float>(((i + j * 5) + 17) % 33) / 32.0f;
868                 clearValue.float32[3] = 1.0f;
869 
870                 vkd.cmdClearColorImage(**commandBuffers[i], swapchainImages[j][imageIndex[j]],
871                                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearValue, 1, &range);
872             }
873 
874             barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
875 
876             for (uint32_t j = 0; j < surfaceCount; ++j)
877             {
878                 if (!isSharedPresentMode[j])
879                 {
880                     barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
881                     barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
882                 }
883                 else
884                 {
885                     barrier.oldLayout = VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR;
886                 }
887                 barrier.image = swapchainImages[j][imageIndex[j]];
888 
889                 vkd.cmdPipelineBarrier(**commandBuffers[i], VK_PIPELINE_STAGE_TRANSFER_BIT,
890                                        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0u, 0, DE_NULL, 0, DE_NULL, 1, &barrier);
891             }
892 
893             endCommandBuffer(vkd, **commandBuffers[i]);
894 
895             // Submit the command buffer
896             std::vector<VkPipelineStageFlags> waitStages(surfaceCount, VK_PIPELINE_STAGE_TRANSFER_BIT);
897             const VkSubmitInfo submitInfo = {
898                 VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, surfaceCount, acquireSem.data(), waitStages.data(), 1u,
899                 &**commandBuffers[i],          1u,      presentSem,
900             };
901             VK_CHECK(vkd.queueSubmit(devHelper.queue, 1u, &submitInfo, DE_NULL));
902 
903             // Present the frame
904             VkSwapchainPresentFenceInfoEXT presentFenceInfo = {
905                 VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT,
906                 DE_NULL,
907                 surfaceCount,
908                 presentFence.data(),
909             };
910             std::vector<VkResult> result(surfaceCount);
911 
912             VkSwapchainPresentModeInfoEXT presentModeInfo = {
913                 VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT,
914                 DE_NULL,
915                 surfaceCount,
916                 DE_NULL,
917             };
918             std::vector<VkPresentModeKHR> presentModes;
919             if (testParams.changePresentModes && rng.getUint32() % 10 != 0)
920             {
921                 presentModes.resize(surfaceCount);
922                 presentModeInfo.pPresentModes = presentModes.data();
923                 presentFenceInfo.pNext        = &presentModeInfo;
924 
925                 // Randomly switch modes.  This is randomly not done to test that the driver doens't expect it to be specified every time.
926                 for (uint32_t j = 0; j < surfaceCount; ++j)
927                 {
928                     uint32_t randomIndex = rng.getUint32() % (uint32_t)compatiblePresentModes[j].size();
929                     presentModes[j]      = compatiblePresentModes[j][randomIndex];
930                 }
931             }
932 
933             const VkPresentInfoKHR presentInfo = {
934                 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
935                 &presentFenceInfo,
936                 1u,
937                 presentSem,
938                 surfaceCount,
939                 swapchainHandles.data(),
940                 imageIndex.data(),
941                 result.data(),
942             };
943             VK_CHECK_WSI(vkd.queuePresentKHR(devHelper.queue, &presentInfo));
944             for (uint32_t j = 0; j < surfaceCount; ++j)
945             {
946                 VK_CHECK_WSI(result[j]);
947             }
948 
949             for (uint32_t j = 0; j < surfaceCount; ++j)
950             {
951                 // Check previous presents; if any is signaled, immediatey destroy its wait semaphore
952                 while (nextUnfinishedPresent[j] < i)
953                 {
954                     if (vkd.getFenceStatus(device, **presentFences[nextUnfinishedPresent[j] * surfaceCount + j]) ==
955                         VK_NOT_READY)
956                         break;
957 
958                     presentSems[nextUnfinishedPresent[j]].clear();
959                     ++nextUnfinishedPresent[j];
960                 }
961 
962                 if (testParams.verifyFenceOrdering)
963                     verifyFenceSignalOrdering(vkd, device, presentFences, surfaceCount, j, nextUnfinishedPresent[j],
964                                               iterations, &results);
965             }
966         }
967 
968         // Wait for outstanding presents and destroy their wait semaphores
969         for (uint32_t j = 0; j < surfaceCount; ++j)
970         {
971             if (testParams.verifyFenceOrdering)
972                 verifyFenceSignalOrdering(vkd, device, presentFences, surfaceCount, j, nextUnfinishedPresent[j],
973                                           iterations, &results);
974 
975             while (nextUnfinishedPresent[j] < iterations)
976             {
977                 VK_CHECK(vkd.waitForFences(device, 1u, &**presentFences[nextUnfinishedPresent[j] * surfaceCount + j],
978                                            VK_TRUE, kMaxFenceWaitTimeout));
979                 presentSems[nextUnfinishedPresent[j]].clear();
980                 ++nextUnfinishedPresent[j];
981             }
982         }
983     }
984     catch (...)
985     {
986         // Make sure device is idle before destroying resources
987         vkd.deviceWaitIdle(device);
988         throw;
989     }
990 
991     for (uint32_t i = 0; i < surfaceCount; ++i)
992     {
993         native.windows[i]->setVisible(false);
994     }
995 
996     return tcu::TestStatus(results.getResult(), results.getMessage());
997 }
998 
populatePresentFenceGroup(tcu::TestCaseGroup * testGroup,Type wsiType)999 void populatePresentFenceGroup(tcu::TestCaseGroup *testGroup, Type wsiType)
1000 {
1001     const struct
1002     {
1003         VkPresentModeKHR mode;
1004         const char *name;
1005     } presentModes[] = {
1006         {VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate"},
1007         {VK_PRESENT_MODE_MAILBOX_KHR, "mailbox"},
1008         {VK_PRESENT_MODE_FIFO_KHR, "fifo"},
1009         {VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed"},
1010         {VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand"},
1011         {VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous"},
1012     };
1013 
1014     for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
1015     {
1016         de::MovePtr<tcu::TestCaseGroup> presentModeGroup(
1017             new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
1018 
1019         PresentFenceTestConfig config;
1020         config.wsiType               = wsiType;
1021         config.modes                 = std::vector<VkPresentModeKHR>(1, presentModes[presentModeNdx].mode);
1022         config.deferMemoryAllocation = false;
1023         config.bindImageMemory       = false;
1024         config.changePresentModes    = false;
1025         config.verifyFenceOrdering   = false;
1026 
1027         // Basic present fence test
1028         addFunctionCase(&*presentModeGroup, "basic", presentFenceTest, config);
1029 
1030         config.verifyFenceOrdering = true;
1031         // Test ordering guarantee of present fence signals
1032         addFunctionCase(&*presentModeGroup, "ordering", presentFenceTest, config);
1033 
1034         if (canDoMultiSwapchainPresent(wsiType))
1035         {
1036             config.verifyFenceOrdering = false;
1037             config.modes               = std::vector<VkPresentModeKHR>(3, presentModes[presentModeNdx].mode);
1038             // Present fence test with multiple swapchains
1039             addFunctionCase(&*presentModeGroup, "multi_swapchain", presentFenceTest, config);
1040 
1041             config.verifyFenceOrdering = true;
1042             // Test ordering guarantee of present fence signals with multiple swapchains
1043             addFunctionCase(&*presentModeGroup, "mult_swapchain_ordering", presentFenceTest, config);
1044         }
1045 
1046         testGroup->addChild(presentModeGroup.release());
1047     }
1048 }
1049 
1050 struct PresentModesTestConfig
1051 {
1052     vk::wsi::Type wsiType;
1053     VkPresentModeKHR mode;
1054 };
1055 
verifyCompatiblePresentModes(const std::vector<VkPresentModeKHR> & supportedModes,const VkPresentModeKHR queryMode,const std::vector<VkPresentModeKHR> & compatibleModes,const std::vector<VkPresentModeKHR> * previouslyQueriedCompatibleModes)1056 tcu::TestStatus verifyCompatiblePresentModes(const std::vector<VkPresentModeKHR> &supportedModes,
1057                                              const VkPresentModeKHR queryMode,
1058                                              const std::vector<VkPresentModeKHR> &compatibleModes,
1059                                              const std::vector<VkPresentModeKHR> *previouslyQueriedCompatibleModes)
1060 {
1061     // Every returned compatible mode must be supported by the surface
1062     for (size_t i = 0; i < compatibleModes.size(); ++i)
1063         if (std::find(supportedModes.begin(), supportedModes.end(), compatibleModes[i]) == supportedModes.end())
1064             return tcu::TestStatus::fail("Returned compatible present mode " + de::toString(compatibleModes[i]) +
1065                                          " is not a supported present mode");
1066 
1067     // The original mode being queried must always be in the compatible list
1068     if (!compatibleModes.empty() &&
1069         std::find(compatibleModes.begin(), compatibleModes.end(), queryMode) == compatibleModes.end())
1070         return tcu::TestStatus::fail("Returned compatible present modes does not include the mode used in the query");
1071 
1072     // There should be no duplicates in the returned modes
1073     std::set<VkPresentModeKHR> visitedModes;
1074     for (VkPresentModeKHR compatibleMode : compatibleModes)
1075     {
1076         if (visitedModes.find(compatibleMode) != visitedModes.end())
1077             return tcu::TestStatus::fail("Duplicate mode " + de::toString(compatibleMode) +
1078                                          " returned in list of compatible present modes");
1079         visitedModes.insert(compatibleMode);
1080     }
1081 
1082     // If provided, the returned list of modes should match the last previous query
1083     if (previouslyQueriedCompatibleModes)
1084     {
1085         for (VkPresentModeKHR previousCompatibleMode : *previouslyQueriedCompatibleModes)
1086             if (visitedModes.find(previousCompatibleMode) == visitedModes.end())
1087                 return tcu::TestStatus::fail("Different sets of compatible modes returned on re-query (present mode " +
1088                                              de::toString(previousCompatibleMode) + " missing on requery)");
1089     }
1090 
1091     return tcu::TestStatus::pass("");
1092 }
1093 
presentModesQueryTest(Context & context,const PresentModesTestConfig testParams)1094 tcu::TestStatus presentModesQueryTest(Context &context, const PresentModesTestConfig testParams)
1095 {
1096     const InstanceHelper instHelper(context, testParams.wsiType, false);
1097     const TestNativeObjects native(context, instHelper.supportedExtensions, testParams.wsiType, 1);
1098     Unique<VkSurfaceKHR> surface(createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display,
1099                                                *native.windows[0], context.getTestContext().getCommandLine()));
1100     const DeviceHelper devHelper(context, instHelper.vki, instHelper.instance, *surface, false, false);
1101 
1102     const std::vector<VkPresentModeKHR> presentModes =
1103         getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surface);
1104     if (std::find(presentModes.begin(), presentModes.end(), testParams.mode) == presentModes.end())
1105         TCU_THROW(NotSupportedError, "Present mode not supported");
1106 
1107     // Get the compatible present modes with the given one.
1108     VkSurfacePresentModeEXT presentModeInfo = {
1109         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
1110         DE_NULL,
1111         testParams.mode,
1112     };
1113     const VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo = {
1114         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
1115         &presentModeInfo,
1116         *surface,
1117     };
1118     VkSurfacePresentModeCompatibilityEXT compatibility = {
1119         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT,
1120         DE_NULL,
1121         0,
1122         DE_NULL,
1123     };
1124     VkSurfaceCapabilities2KHR capabilities = {
1125         VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
1126         &compatibility,
1127         {},
1128     };
1129 
1130     // Test that querying only the count works.
1131     VK_CHECK(
1132         instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1133 
1134     // The return value must be at least one, as every mode is compatible with itself.
1135     if (compatibility.presentModeCount < 1)
1136         return tcu::TestStatus::fail("Empty compatible present mode list");
1137 
1138     // Test again providing a buffer that's too small
1139     constexpr VkPresentModeKHR invalidValue = (VkPresentModeKHR)0x1234;
1140     std::vector<VkPresentModeKHR> compatibleModes(compatibility.presentModeCount, invalidValue);
1141     compatibility.pPresentModes = compatibleModes.data();
1142 
1143     uint32_t originalCompatibleModesCount = compatibility.presentModeCount;
1144 
1145     // Check result when count is 0
1146     compatibility.presentModeCount = 0;
1147     VkResult result =
1148         instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities);
1149     if (result != VK_SUCCESS)
1150         return tcu::TestStatus::fail("Wrong result when the size is 0");
1151 
1152     // Check result when count is too small
1153     compatibility.presentModeCount = originalCompatibleModesCount - 1;
1154     result =
1155         instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities);
1156     if (result != VK_SUCCESS)
1157         return tcu::TestStatus::fail("Wrong result when the size is too small");
1158 
1159     // Make sure whatever _is_ returned is valid.
1160     if (compatibility.presentModeCount > originalCompatibleModesCount - 1)
1161         return tcu::TestStatus::fail("Re-query returned more results than provided");
1162 
1163     // Ensure the rest of the array is not overwritten
1164     for (size_t i = compatibility.presentModeCount; i < compatibleModes.size(); ++i)
1165     {
1166         if (compatibleModes[i] != invalidValue)
1167             return tcu::TestStatus::fail("Query overwrote beyond returned count");
1168     }
1169     compatibleModes.resize(compatibility.presentModeCount);
1170     tcu::TestStatus status = verifyCompatiblePresentModes(presentModes, testParams.mode, compatibleModes, nullptr);
1171     if (status.isFail())
1172         return status;
1173 
1174     // Check result when count is correct
1175     compatibility.presentModeCount = originalCompatibleModesCount;
1176     std::vector<VkPresentModeKHR> compatibleModes2(compatibility.presentModeCount, invalidValue);
1177     compatibility.pPresentModes = compatibleModes2.data();
1178 
1179     VK_CHECK(
1180         instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1181 
1182     // Make sure returned modes are valid.
1183     if (compatibility.presentModeCount != originalCompatibleModesCount)
1184         return tcu::TestStatus::fail("Re-query returned different results count than provided");
1185 
1186     status = verifyCompatiblePresentModes(presentModes, testParams.mode, compatibleModes2, &compatibleModes);
1187     if (status.isFail())
1188         return status;
1189 
1190     // Check that querying with a count higher than supported still returns as many results as before.
1191     compatibility.presentModeCount = originalCompatibleModesCount * 2;
1192     std::vector<VkPresentModeKHR> compatibleModes3(compatibility.presentModeCount, invalidValue);
1193     compatibility.pPresentModes = compatibleModes3.data();
1194 
1195     VK_CHECK(
1196         instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1197 
1198     // Make sure returned modes are the same as before.
1199     if (compatibility.presentModeCount != originalCompatibleModesCount)
1200         return tcu::TestStatus::fail("Re-query returned different results count than provided");
1201 
1202     // Ensure the rest of the array is not overwritten
1203     for (size_t i = compatibility.presentModeCount; i < compatibleModes3.size(); ++i)
1204     {
1205         if (compatibleModes3[i] != invalidValue)
1206             return tcu::TestStatus::fail("Query overwrote beyond returned count");
1207     }
1208 
1209     compatibleModes3.resize(compatibility.presentModeCount);
1210     status = verifyCompatiblePresentModes(presentModes, testParams.mode, compatibleModes3, &compatibleModes2);
1211     if (status.isFail())
1212         return status;
1213 
1214     return tcu::TestStatus::pass("Tests ran successfully");
1215 }
1216 
populatePresentModesGroup(tcu::TestCaseGroup * testGroup,Type wsiType)1217 void populatePresentModesGroup(tcu::TestCaseGroup *testGroup, Type wsiType)
1218 {
1219     const struct
1220     {
1221         VkPresentModeKHR mode;
1222         const char *name;
1223     } presentModes[] = {
1224         {VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate"},
1225         {VK_PRESENT_MODE_MAILBOX_KHR, "mailbox"},
1226         {VK_PRESENT_MODE_FIFO_KHR, "fifo"},
1227         {VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed"},
1228         {VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand"},
1229         {VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous"},
1230     };
1231 
1232     for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
1233     {
1234         de::MovePtr<tcu::TestCaseGroup> presentModeGroup(
1235             new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
1236 
1237         {
1238             PresentModesTestConfig config;
1239             config.wsiType = wsiType;
1240             config.mode    = presentModes[presentModeNdx].mode;
1241 
1242             // Query compatible present modes
1243             addFunctionCase(&*presentModeGroup, "query", presentModesQueryTest, config);
1244         }
1245 
1246         {
1247             PresentFenceTestConfig config;
1248             config.wsiType               = wsiType;
1249             config.modes                 = std::vector<VkPresentModeKHR>(1, presentModes[presentModeNdx].mode);
1250             config.deferMemoryAllocation = false;
1251             config.bindImageMemory       = false;
1252             config.changePresentModes    = true;
1253             config.verifyFenceOrdering   = false;
1254 
1255             // Switch between compatible modes
1256             addFunctionCase(&*presentModeGroup, "change_modes", presentFenceTest, config);
1257 
1258             if (canDoMultiSwapchainPresent(wsiType))
1259             {
1260                 config.modes = std::vector<VkPresentModeKHR>(4, presentModes[presentModeNdx].mode);
1261 
1262                 // Switch between compatible modes with multiple swapchains
1263                 addFunctionCase(&*presentModeGroup, "change_modes_multi_swapchain", presentFenceTest, config);
1264 
1265                 config.modes                 = std::vector<VkPresentModeKHR>(2, presentModes[presentModeNdx].mode);
1266                 config.deferMemoryAllocation = true;
1267 
1268                 // Switch between compatible modes while swapchain uses deferred allocation
1269                 addFunctionCase(&*presentModeGroup, "change_modes_with_deferred_alloc", presentFenceTest, config);
1270             }
1271         }
1272 
1273         testGroup->addChild(presentModeGroup.release());
1274     }
1275 
1276     if (canDoMultiSwapchainPresent(wsiType))
1277     {
1278         // Switch between compatible modes with multiple swapchains in different modes
1279         de::MovePtr<tcu::TestCaseGroup> heterogenousGroup(
1280             new tcu::TestCaseGroup(testGroup->getTestContext(), "heterogenous"));
1281 
1282         std::vector<VkPresentModeKHR> modes(3);
1283         for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(presentModes); i++)
1284         {
1285             for (size_t j = 0; j < DE_LENGTH_OF_ARRAY(presentModes); j++)
1286             {
1287                 for (size_t k = 0; k < DE_LENGTH_OF_ARRAY(presentModes); k++)
1288                 {
1289                     // Skip if not actually heterogenous
1290                     if (i == j && i == k)
1291                         continue;
1292 
1293                     std::string testName = presentModes[i].name;
1294                     testName += "_";
1295                     testName += presentModes[j].name;
1296                     testName += "_";
1297                     testName += presentModes[k].name;
1298 
1299                     modes[0] = presentModes[i].mode;
1300                     modes[1] = presentModes[j].mode;
1301                     modes[2] = presentModes[k].mode;
1302 
1303                     PresentFenceTestConfig config;
1304                     config.wsiType               = wsiType;
1305                     config.modes                 = modes;
1306                     config.deferMemoryAllocation = false;
1307                     config.bindImageMemory       = false;
1308                     config.changePresentModes    = true;
1309                     config.verifyFenceOrdering   = false;
1310 
1311                     addFunctionCase(&*heterogenousGroup, testName, presentFenceTest, config);
1312                 }
1313             }
1314         }
1315 
1316         testGroup->addChild(heterogenousGroup.release());
1317     }
1318 }
1319 
1320 enum class SwapchainWindowSize
1321 {
1322     Identical,
1323     SwapchainBigger,
1324     SwapchainSmaller,
1325 };
1326 
1327 enum class SwapchainWindowAspect
1328 {
1329     Identical,
1330     SwapchainTaller,
1331     SwapchainWider,
1332 };
1333 
1334 struct ScalingQueryTestConfig
1335 {
1336     vk::wsi::Type wsiType;
1337     VkPresentModeKHR mode;
1338 };
1339 
1340 struct ScalingTestConfig
1341 {
1342     vk::wsi::Type wsiType;
1343     VkPresentModeKHR mode;
1344     VkPresentScalingFlagsEXT scaling;
1345     VkPresentGravityFlagsEXT gravityX;
1346     VkPresentGravityFlagsEXT gravityY;
1347     SwapchainWindowSize size;
1348     SwapchainWindowAspect aspect;
1349     // Either have the swapchain be created with a different size, or resize the window after swapchain creation
1350     bool resizeWindow;
1351 };
1352 
scalingQueryTest(Context & context,const ScalingQueryTestConfig testParams)1353 tcu::TestStatus scalingQueryTest(Context &context, const ScalingQueryTestConfig testParams)
1354 {
1355     const InstanceHelper instHelper(context, testParams.wsiType, false);
1356     const TestNativeObjects native(context, instHelper.supportedExtensions, testParams.wsiType, 1);
1357     Unique<VkSurfaceKHR> surface(createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display,
1358                                                *native.windows[0], context.getTestContext().getCommandLine()));
1359     const DeviceHelper devHelper(context, instHelper.vki, instHelper.instance, *surface, false, false);
1360 
1361     const std::vector<VkPresentModeKHR> presentModes =
1362         getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surface);
1363     if (std::find(presentModes.begin(), presentModes.end(), testParams.mode) == presentModes.end())
1364         TCU_THROW(NotSupportedError, "Present mode not supported");
1365 
1366     // Query the scaling capabilities and make sure they only report acceptable values.
1367     VkSurfacePresentScalingCapabilitiesEXT scaling =
1368         getSurfaceScalingCapabilities(instHelper.vki, devHelper.physicalDevice, testParams.mode, *surface);
1369 
1370     constexpr VkPresentScalingFlagsEXT scalingFlags = VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT |
1371                                                       VK_PRESENT_SCALING_ASPECT_RATIO_STRETCH_BIT_EXT |
1372                                                       VK_PRESENT_SCALING_STRETCH_BIT_EXT;
1373     constexpr VkPresentGravityFlagsEXT gravityFlags =
1374         VK_PRESENT_GRAVITY_MIN_BIT_EXT | VK_PRESENT_GRAVITY_MAX_BIT_EXT | VK_PRESENT_GRAVITY_CENTERED_BIT_EXT;
1375 
1376     if ((scaling.supportedPresentScaling & ~scalingFlags) != 0)
1377         return tcu::TestStatus::fail("Invalid bits in scaling flags");
1378 
1379     if ((scaling.supportedPresentGravityX & ~gravityFlags) != 0)
1380         return tcu::TestStatus::fail("Invalid bits in gravity flags (x axis)");
1381 
1382     if ((scaling.supportedPresentGravityY & ~gravityFlags) != 0)
1383         return tcu::TestStatus::fail("Invalid bits in gravity flags (y axis)");
1384 
1385     return tcu::TestStatus::pass("Tests ran successfully");
1386 }
1387 
scalingQueryCompatibleModesTest(Context & context,const ScalingQueryTestConfig testParams)1388 tcu::TestStatus scalingQueryCompatibleModesTest(Context &context, const ScalingQueryTestConfig testParams)
1389 {
1390     const InstanceHelper instHelper(context, testParams.wsiType, false);
1391     const TestNativeObjects native(context, instHelper.supportedExtensions, testParams.wsiType, 1);
1392     Unique<VkSurfaceKHR> surface(createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display,
1393                                                *native.windows[0], context.getTestContext().getCommandLine()));
1394     const DeviceHelper devHelper(context, instHelper.vki, instHelper.instance, *surface, false, false);
1395 
1396     const std::vector<VkPresentModeKHR> presentModes =
1397         getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surface);
1398     if (std::find(presentModes.begin(), presentModes.end(), testParams.mode) == presentModes.end())
1399         TCU_THROW(NotSupportedError, "Present mode not supported");
1400 
1401     // Query compatible present modes, and scaling capabilities for each mode.  They must all be identical.
1402     VkSurfacePresentModeEXT presentModeInfo = {
1403         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
1404         DE_NULL,
1405         testParams.mode,
1406     };
1407     const VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo = {
1408         VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
1409         &presentModeInfo,
1410         *surface,
1411     };
1412     VkSurfacePresentModeCompatibilityEXT compatibility = {
1413         VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT,
1414         DE_NULL,
1415         0,
1416         DE_NULL,
1417     };
1418     VkSurfaceCapabilities2KHR capabilities = {
1419         VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
1420         &compatibility,
1421         {},
1422     };
1423 
1424     VK_CHECK(
1425         instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1426     std::vector<VkPresentModeKHR> compatibleModes(compatibility.presentModeCount, (VkPresentModeKHR)0x5678);
1427     compatibility.pPresentModes = compatibleModes.data();
1428 
1429     VK_CHECK(
1430         instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1431 
1432     std::vector<VkSurfacePresentScalingCapabilitiesEXT> scaling(compatibility.presentModeCount);
1433 
1434     for (uint32_t i = 0; i < compatibility.presentModeCount; ++i)
1435         scaling[i] =
1436             getSurfaceScalingCapabilities(instHelper.vki, devHelper.physicalDevice, compatibleModes[i], *surface);
1437 
1438     for (uint32_t i = 1; i < compatibility.presentModeCount; ++i)
1439     {
1440         if (scaling[i].supportedPresentScaling != scaling[0].supportedPresentScaling)
1441             return tcu::TestStatus::fail("Different scaling flags for compatible present modes is not allowed");
1442 
1443         if (scaling[i].supportedPresentGravityX != scaling[0].supportedPresentGravityX)
1444             return tcu::TestStatus::fail(
1445                 "Different gravity flags (x axis) for compatible present modes is not allowed");
1446 
1447         if (scaling[i].supportedPresentGravityY != scaling[0].supportedPresentGravityY)
1448             return tcu::TestStatus::fail(
1449                 "Different gravity flags (y axis) for compatible present modes is not allowed");
1450     }
1451 
1452     return tcu::TestStatus::pass("Tests ran successfully");
1453 }
1454 
scalingTest(Context & context,const ScalingTestConfig testParams)1455 tcu::TestStatus scalingTest(Context &context, const ScalingTestConfig testParams)
1456 {
1457     const InstanceHelper instHelper(context, testParams.wsiType, false);
1458     const TestNativeObjects native(context, instHelper.supportedExtensions, testParams.wsiType, 1);
1459     Unique<VkSurfaceKHR> surface(createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display,
1460                                                *native.windows[0], context.getTestContext().getCommandLine()));
1461 
1462     const DeviceHelper devHelper(context, instHelper.vki, instHelper.instance, *surface, true, false);
1463     const DeviceInterface &vkd = devHelper.vkd;
1464     const VkDevice device      = *devHelper.device;
1465     SimpleAllocator allocator(vkd, device, getPhysicalDeviceMemoryProperties(instHelper.vki, devHelper.physicalDevice));
1466 
1467     std::vector<VkSurfaceFormatKHR> surfaceFormats =
1468         getPhysicalDeviceSurfaceFormats(instHelper.vki, devHelper.physicalDevice, *surface);
1469     if (surfaceFormats.empty())
1470         return tcu::TestStatus::fail("No VkSurfaceFormatKHR defined");
1471 
1472     const VkSurfaceCapabilitiesKHR capabilities =
1473         getPhysicalDeviceSurfaceCapabilities(instHelper.vki, devHelper.physicalDevice, *surface, DE_NULL);
1474     const VkSurfaceTransformFlagBitsKHR transform =
1475         (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0 ?
1476             VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR :
1477             capabilities.currentTransform;
1478 
1479     const std::vector<VkPresentModeKHR> presentModes =
1480         getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surface);
1481     if (std::find(presentModes.begin(), presentModes.end(), testParams.mode) == presentModes.end())
1482         TCU_THROW(NotSupportedError, "Present mode not supported");
1483 
1484     // Skip if configuration is not supported
1485     VkSurfacePresentScalingCapabilitiesEXT scaling =
1486         getSurfaceScalingCapabilities(instHelper.vki, devHelper.physicalDevice, testParams.mode, *surface);
1487 
1488     if ((scaling.supportedPresentScaling & testParams.scaling) == 0)
1489         TCU_THROW(NotSupportedError, "Scaling mode is not supported");
1490     if (testParams.scaling != VK_PRESENT_SCALING_STRETCH_BIT_EXT)
1491     {
1492         if ((scaling.supportedPresentGravityX & testParams.gravityX) == 0)
1493             TCU_THROW(NotSupportedError, "Gravity mode is not supported (x axis)");
1494         if ((scaling.supportedPresentGravityY & testParams.gravityY) == 0)
1495             TCU_THROW(NotSupportedError, "Gravity mode is not supported (y axis)");
1496     }
1497 
1498     tcu::UVec2 swapchainSize = native.windowSize;
1499     if (!testParams.resizeWindow)
1500     {
1501         switch (testParams.size)
1502         {
1503         case SwapchainWindowSize::SwapchainBigger:
1504             swapchainSize.x() *= 2;
1505             swapchainSize.y() *= 2;
1506             break;
1507         case SwapchainWindowSize::SwapchainSmaller:
1508             swapchainSize.x() /= 2;
1509             swapchainSize.y() /= 2;
1510             break;
1511         default:
1512             break;
1513         }
1514         switch (testParams.aspect)
1515         {
1516         case SwapchainWindowAspect::SwapchainTaller:
1517             swapchainSize.y() += swapchainSize.y() / 2;
1518             break;
1519         case SwapchainWindowAspect::SwapchainWider:
1520             swapchainSize.x() += swapchainSize.x() / 2;
1521             break;
1522         default:
1523             break;
1524         }
1525     }
1526 
1527     VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(
1528         *surface, surfaceFormats[0], swapchainSize, testParams.mode, transform, capabilities.minImageCount, false);
1529 
1530     VkSwapchainPresentScalingCreateInfoEXT scalingInfo = {
1531         VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT,
1532         DE_NULL,
1533         testParams.scaling,
1534         testParams.gravityX,
1535         testParams.gravityY,
1536     };
1537     swapchainInfo.pNext = &scalingInfo;
1538 
1539     const Unique<VkSwapchainKHR> swapchain(createSwapchainKHR(vkd, device, &swapchainInfo));
1540     std::vector<VkImage> swapchainImages = getSwapchainImages(vkd, device, *swapchain);
1541 
1542     const Unique<VkCommandPool> commandPool(
1543         createCommandPool(vkd, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, devHelper.queueFamilyIndex));
1544 
1545     constexpr uint32_t iterations = 100;
1546 
1547     // Do testParams.iterations presents, with a fence associated with the last one.
1548     FenceSp presentFence = FenceSp(new Unique<VkFence>(createFence(vkd, device)));
1549     const std::vector<SemaphoreSp> acquireSems(createSemaphores(vkd, device, iterations));
1550     const std::vector<SemaphoreSp> presentSems(createSemaphores(vkd, device, iterations));
1551 
1552     const std::vector<CommandBufferSp> commandBuffers(
1553         allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, iterations));
1554 
1555     const uint64_t foreverNs = 0xFFFFFFFFFFFFFFFFul;
1556 
1557     VkImageSubresourceRange range = {
1558         VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1,
1559     };
1560 
1561     tcu::UVec2 windowSize = native.windowSize;
1562     if (testParams.resizeWindow)
1563     {
1564         switch (testParams.size)
1565         {
1566         case SwapchainWindowSize::SwapchainBigger:
1567             windowSize.x() /= 2;
1568             windowSize.y() /= 2;
1569             break;
1570         case SwapchainWindowSize::SwapchainSmaller:
1571             windowSize.x() *= 2;
1572             windowSize.y() *= 2;
1573             break;
1574         default:
1575             break;
1576         }
1577         switch (testParams.aspect)
1578         {
1579         case SwapchainWindowAspect::SwapchainTaller:
1580             windowSize.x() += windowSize.x() / 2;
1581             break;
1582         case SwapchainWindowAspect::SwapchainWider:
1583             windowSize.y() += windowSize.y() / 2;
1584             break;
1585         default:
1586             break;
1587         }
1588 
1589         native.windows[0]->resize(windowSize);
1590     }
1591 
1592     const uint32_t quarterPixels = swapchainSize.x() * swapchainSize.y() / 4;
1593     const tcu::UVec4 red(255, 30, 20, 255);
1594     const tcu::UVec4 green(0, 255, 50, 255);
1595     const tcu::UVec4 blue(40, 60, 255, 255);
1596     const tcu::UVec4 yellow(200, 220, 20, 255);
1597     de::MovePtr<Allocation> redMemory;
1598     de::MovePtr<Allocation> greenMemory;
1599     de::MovePtr<Allocation> blueMemory;
1600     de::MovePtr<Allocation> yellowMemory;
1601     const vk::Move<vk::VkBuffer> redBuffer =
1602         createBufferAndBindMemory(devHelper, allocator, red, quarterPixels, &redMemory);
1603     const vk::Move<vk::VkBuffer> greenBuffer =
1604         createBufferAndBindMemory(devHelper, allocator, green, quarterPixels, &greenMemory);
1605     const vk::Move<vk::VkBuffer> blueBuffer =
1606         createBufferAndBindMemory(devHelper, allocator, blue, quarterPixels, &blueMemory);
1607     const vk::Move<vk::VkBuffer> yellowBuffer =
1608         createBufferAndBindMemory(devHelper, allocator, yellow, quarterPixels, &yellowMemory);
1609 
1610     try
1611     {
1612         for (uint32_t i = 0; i < iterations; ++i)
1613         {
1614             const VkSemaphore presentSem = **presentSems[i];
1615             const VkSemaphore acquireSem = **acquireSems[i];
1616             uint32_t imageIndex          = 0x12345; // initialize to junk value
1617 
1618             VK_CHECK(vkd.acquireNextImageKHR(device, *swapchain, foreverNs, acquireSem, DE_NULL, &imageIndex));
1619 
1620             beginCommandBuffer(vkd, **commandBuffers[i], 0u);
1621 
1622             VkImageMemoryBarrier barrier = {
1623                 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1624                 DE_NULL,
1625                 0,
1626                 0,
1627                 VK_IMAGE_LAYOUT_UNDEFINED,
1628                 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1629                 VK_QUEUE_FAMILY_IGNORED,
1630                 VK_QUEUE_FAMILY_IGNORED,
1631                 swapchainImages[imageIndex],
1632                 range,
1633             };
1634 
1635             vkd.cmdPipelineBarrier(**commandBuffers[i], VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1636                                    VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0, DE_NULL, 0, DE_NULL, 1, &barrier);
1637 
1638             const tcu::UVec2 halfSwapchainSize = swapchainSize / 2u;
1639             copyBufferToImage(vkd, **commandBuffers[i], *redBuffer, swapchainImages[imageIndex], tcu::UVec2(0, 0),
1640                               halfSwapchainSize);
1641             copyBufferToImage(vkd, **commandBuffers[i], *greenBuffer, swapchainImages[imageIndex],
1642                               tcu::UVec2(halfSwapchainSize.x(), 0),
1643                               tcu::UVec2(swapchainSize.x() - halfSwapchainSize.x(), halfSwapchainSize.y()));
1644             copyBufferToImage(vkd, **commandBuffers[i], *blueBuffer, swapchainImages[imageIndex],
1645                               tcu::UVec2(0, halfSwapchainSize.y()),
1646                               tcu::UVec2(halfSwapchainSize.x(), swapchainSize.y() - halfSwapchainSize.y()));
1647             copyBufferToImage(
1648                 vkd, **commandBuffers[i], *yellowBuffer, swapchainImages[imageIndex], halfSwapchainSize,
1649                 tcu::UVec2(swapchainSize.x() - halfSwapchainSize.x(), swapchainSize.y() - halfSwapchainSize.y()));
1650 
1651             barrier.oldLayout     = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1652             barrier.newLayout     = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1653             barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1654 
1655             vkd.cmdPipelineBarrier(**commandBuffers[i], VK_PIPELINE_STAGE_TRANSFER_BIT,
1656                                    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0u, 0, DE_NULL, 0, DE_NULL, 1, &barrier);
1657 
1658             endCommandBuffer(vkd, **commandBuffers[i]);
1659 
1660             // Submit the command buffer
1661             VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
1662             const VkSubmitInfo submitInfo  = {
1663                 VK_STRUCTURE_TYPE_SUBMIT_INFO, DE_NULL, 1,           &acquireSem, &waitStage, 1u,
1664                 &**commandBuffers[i],          1u,      &presentSem,
1665             };
1666             VK_CHECK(vkd.queueSubmit(devHelper.queue, 1u, &submitInfo, DE_NULL));
1667 
1668             // Present the frame
1669             const VkSwapchainPresentFenceInfoEXT presentFenceInfo = {
1670                 VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT,
1671                 DE_NULL,
1672                 1,
1673                 &**presentFence,
1674             };
1675             VkResult result;
1676 
1677             const VkPresentInfoKHR presentInfo = {
1678                 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
1679                 // Signal the present fence on the last present.
1680                 i + 1 == iterations ? &presentFenceInfo : nullptr,
1681                 1u,
1682                 &presentSem,
1683                 1,
1684                 &*swapchain,
1685                 &imageIndex,
1686                 &result,
1687             };
1688             VK_CHECK_WSI(vkd.queuePresentKHR(devHelper.queue, &presentInfo));
1689             VK_CHECK_WSI(result);
1690 
1691             // TODO: wait for present, capture the screen and verify that scaling is done correctly.
1692         }
1693 
1694         // Wait for all presents before terminating the test (when semaphores are destroyed)
1695         VK_CHECK(vkd.waitForFences(device, 1u, &**presentFence, VK_TRUE, kMaxFenceWaitTimeout));
1696     }
1697     catch (...)
1698     {
1699         // Make sure device is idle before destroying resources
1700         vkd.deviceWaitIdle(device);
1701         throw;
1702     }
1703 
1704     native.windows[0]->setVisible(false);
1705 
1706     return tcu::TestStatus::pass("Tests ran successfully");
1707 }
1708 
populateScalingTests(tcu::TestCaseGroup * testGroup,Type wsiType,bool resizeWindow)1709 void populateScalingTests(tcu::TestCaseGroup *testGroup, Type wsiType, bool resizeWindow)
1710 {
1711     const struct
1712     {
1713         VkPresentModeKHR mode;
1714         const char *name;
1715     } presentModes[] = {
1716         {VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate"},
1717         {VK_PRESENT_MODE_MAILBOX_KHR, "mailbox"},
1718         {VK_PRESENT_MODE_FIFO_KHR, "fifo"},
1719         {VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed"},
1720         {VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand"},
1721         {VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous"},
1722     };
1723 
1724     const struct
1725     {
1726         VkPresentScalingFlagBitsEXT scaling;
1727         const char *name;
1728     } scalingFlags[] = {
1729         {VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT, "one_to_one"},
1730         {VK_PRESENT_SCALING_ASPECT_RATIO_STRETCH_BIT_EXT, "aspect_stretch"},
1731         {VK_PRESENT_SCALING_STRETCH_BIT_EXT, "stretch"},
1732     };
1733 
1734     const struct
1735     {
1736         VkPresentGravityFlagBitsEXT gravity;
1737         const char *name;
1738     } gravityFlags[] = {
1739         {VK_PRESENT_GRAVITY_MIN_BIT_EXT, "min"},
1740         {VK_PRESENT_GRAVITY_MAX_BIT_EXT, "max"},
1741         {VK_PRESENT_GRAVITY_CENTERED_BIT_EXT, "center"},
1742     };
1743 
1744     for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
1745     {
1746         de::MovePtr<tcu::TestCaseGroup> presentModeGroup(
1747             new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
1748 
1749         {
1750             ScalingQueryTestConfig config;
1751             config.wsiType = wsiType;
1752             config.mode    = presentModes[presentModeNdx].mode;
1753 
1754             // Query supported scaling modes
1755             de::MovePtr<tcu::TestCaseGroup> queryGroup(new tcu::TestCaseGroup(testGroup->getTestContext(), "query"));
1756             // Basic test
1757             addFunctionCase(&*queryGroup, "basic", scalingQueryTest, config);
1758             // Verify compatible present modes have the same scaling capabilities
1759             addFunctionCase(&*queryGroup, "verify_compatible_present_modes", scalingQueryCompatibleModesTest, config);
1760             presentModeGroup->addChild(queryGroup.release());
1761         }
1762 
1763         for (size_t scalingFlagNdx = 0; scalingFlagNdx < DE_LENGTH_OF_ARRAY(scalingFlags); scalingFlagNdx++)
1764         {
1765             de::MovePtr<tcu::TestCaseGroup> scalingFlagGroup(
1766                 new tcu::TestCaseGroup(testGroup->getTestContext(), scalingFlags[scalingFlagNdx].name));
1767 
1768             const bool isStretch = scalingFlags[scalingFlagNdx].scaling == VK_PRESENT_SCALING_STRETCH_BIT_EXT;
1769 
1770             for (size_t gravityFlagXNdx = 0; gravityFlagXNdx < DE_LENGTH_OF_ARRAY(gravityFlags); gravityFlagXNdx++)
1771             {
1772                 for (size_t gravityFlagYNdx = 0; gravityFlagYNdx < DE_LENGTH_OF_ARRAY(gravityFlags); gravityFlagYNdx++)
1773                 {
1774                     std::string testName = gravityFlags[gravityFlagXNdx].name;
1775                     testName += "_";
1776                     testName += gravityFlags[gravityFlagYNdx].name;
1777 
1778                     de::MovePtr<tcu::TestCaseGroup> gravityFlagsGroup(
1779                         new tcu::TestCaseGroup(scalingFlagGroup->getTestContext(), testName.c_str()));
1780 
1781                     ScalingTestConfig config;
1782                     config.wsiType      = wsiType;
1783                     config.mode         = presentModes[presentModeNdx].mode;
1784                     config.scaling      = scalingFlags[scalingFlagNdx].scaling;
1785                     config.gravityX     = gravityFlags[gravityFlagXNdx].gravity;
1786                     config.gravityY     = gravityFlags[gravityFlagYNdx].gravity;
1787                     config.size         = SwapchainWindowSize::Identical;
1788                     config.aspect       = SwapchainWindowAspect::Identical;
1789                     config.resizeWindow = resizeWindow;
1790 
1791                     // Gravity does not apply to stretch
1792                     de::MovePtr<tcu::TestCaseGroup> *group = isStretch ? &scalingFlagGroup : &gravityFlagsGroup;
1793 
1794                     // Basic test without actual scaling
1795                     addFunctionCase(&**group, "same_size_and_aspect", scalingTest, config);
1796 
1797                     config.size = SwapchainWindowSize::SwapchainBigger;
1798                     // Swapchain is bigger than window, but has same aspect
1799                     addFunctionCase(&**group, "swapchain_bigger_same_aspect", scalingTest, config);
1800 
1801                     config.size = SwapchainWindowSize::SwapchainSmaller;
1802                     // Swapchain is smaller than window, but has same aspect
1803                     addFunctionCase(&**group, "swapchain_smaller_same_aspect", scalingTest, config);
1804 
1805                     config.size   = SwapchainWindowSize::Identical;
1806                     config.aspect = SwapchainWindowAspect::SwapchainTaller;
1807                     // Swapchain has same width, but is taller than window
1808                     addFunctionCase(&**group, "swapchain_taller", scalingTest, config);
1809 
1810                     config.size = SwapchainWindowSize::SwapchainBigger;
1811                     // Swapchain is bigger than window, and is taller in aspect ratio
1812                     addFunctionCase(&**group, "swapchain_bigger_taller_aspect", scalingTest, config);
1813 
1814                     config.size = SwapchainWindowSize::SwapchainSmaller;
1815                     // Swapchain is smaller than window, but is taller in aspect ratio
1816                     addFunctionCase(&**group, "swapchain_smaller_taller_aspect", scalingTest, config);
1817 
1818                     config.size   = SwapchainWindowSize::Identical;
1819                     config.aspect = SwapchainWindowAspect::SwapchainWider;
1820                     // Swapchain has same height, but is wider than window
1821                     addFunctionCase(&**group, "swapchain_wider", scalingTest, config);
1822 
1823                     config.size = SwapchainWindowSize::SwapchainBigger;
1824                     // Swapchain is bigger than window, and is wider in aspect ratio
1825                     addFunctionCase(&**group, "swapchain_bigger_wider_aspect", scalingTest, config);
1826 
1827                     config.size = SwapchainWindowSize::SwapchainSmaller;
1828                     // Swapchain is smaller than window, but is wider in aspect ratio
1829                     addFunctionCase(&**group, "swapchain_smaller_wider_aspect", scalingTest, config);
1830 
1831                     if (isStretch)
1832                     {
1833                         break;
1834                     }
1835 
1836                     scalingFlagGroup->addChild(gravityFlagsGroup.release());
1837                 }
1838 
1839                 if (isStretch)
1840                 {
1841                     break;
1842                 }
1843             }
1844 
1845             presentModeGroup->addChild(scalingFlagGroup.release());
1846         }
1847 
1848         testGroup->addChild(presentModeGroup.release());
1849     }
1850 }
1851 
populateScalingGroup(tcu::TestCaseGroup * testGroup,Type wsiType)1852 void populateScalingGroup(tcu::TestCaseGroup *testGroup, Type wsiType)
1853 {
1854     populateScalingTests(testGroup, wsiType, false);
1855 
1856     de::MovePtr<tcu::TestCaseGroup> resizeWindowGroup(
1857         new tcu::TestCaseGroup(testGroup->getTestContext(), "resize_window"));
1858     populateScalingTests(&*resizeWindowGroup, wsiType, true);
1859     testGroup->addChild(resizeWindowGroup.release());
1860 }
1861 
populateDeferredAllocGroup(tcu::TestCaseGroup * testGroup,Type wsiType)1862 void populateDeferredAllocGroup(tcu::TestCaseGroup *testGroup, Type wsiType)
1863 {
1864     const struct
1865     {
1866         VkPresentModeKHR mode;
1867         const char *name;
1868     } presentModes[] = {
1869         {VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate"},
1870         {VK_PRESENT_MODE_MAILBOX_KHR, "mailbox"},
1871         {VK_PRESENT_MODE_FIFO_KHR, "fifo"},
1872         {VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed"},
1873         {VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand"},
1874         {VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous"},
1875     };
1876 
1877     for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
1878     {
1879         de::MovePtr<tcu::TestCaseGroup> presentModeGroup(
1880             new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
1881 
1882         PresentFenceTestConfig config;
1883         config.wsiType               = wsiType;
1884         config.modes                 = std::vector<VkPresentModeKHR>(1, presentModes[presentModeNdx].mode);
1885         config.deferMemoryAllocation = true;
1886         config.bindImageMemory       = false;
1887         config.changePresentModes    = false;
1888         config.verifyFenceOrdering   = false;
1889 
1890         // Basic deferred allocation test
1891         addFunctionCase(&*presentModeGroup, "basic", presentFenceTest, config);
1892 
1893         config.bindImageMemory = true;
1894 
1895         // Bind image memory + shared present mode crashing on some drivers for unrelated reasons to VK_EXT_swapchain_maintenance1.  Will enable this test separately.
1896         if (presentModes[presentModeNdx].mode != VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR &&
1897             presentModes[presentModeNdx].mode != VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR)
1898         {
1899             // Bind image with VkBindImageMemorySwapchainInfoKHR
1900             addFunctionCase(&*presentModeGroup, "bind_image", presentFenceTest, config);
1901         }
1902 
1903         if (canDoMultiSwapchainPresent(wsiType))
1904         {
1905             config.modes = std::vector<VkPresentModeKHR>(2, presentModes[presentModeNdx].mode);
1906 
1907             // Bind image with VkBindImageMemorySwapchainInfoKHR with multiple swapchains
1908             addFunctionCase(&*presentModeGroup, "bind_image_multi_swapchain", presentFenceTest, config);
1909         }
1910 
1911         testGroup->addChild(presentModeGroup.release());
1912     }
1913 }
1914 
1915 enum class ResizeWindow
1916 {
1917     No,
1918     BeforeAcquire,
1919     BeforePresent,
1920 };
1921 
1922 struct ReleaseImagesTestConfig
1923 {
1924     vk::wsi::Type wsiType;
1925     VkPresentModeKHR mode;
1926     VkPresentScalingFlagsEXT scaling;
1927     ResizeWindow resizeWindow;
1928     bool releaseBeforePresent;
1929     bool releaseBeforeRetire;
1930 };
1931 
releaseImagesTest(Context & context,const ReleaseImagesTestConfig testParams)1932 tcu::TestStatus releaseImagesTest(Context &context, const ReleaseImagesTestConfig testParams)
1933 {
1934     const InstanceHelper instHelper(context, testParams.wsiType, false);
1935     const TestNativeObjects native(context, instHelper.supportedExtensions, testParams.wsiType, 1);
1936     Unique<VkSurfaceKHR> surface(createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display,
1937                                                *native.windows[0], context.getTestContext().getCommandLine()));
1938 
1939     const DeviceHelper devHelper(context, instHelper.vki, instHelper.instance, *surface, true, false);
1940     const DeviceInterface &vkd = devHelper.vkd;
1941     const VkDevice device      = *devHelper.device;
1942 
1943     std::vector<VkSurfaceFormatKHR> surfaceFormats =
1944         getPhysicalDeviceSurfaceFormats(instHelper.vki, devHelper.physicalDevice, *surface);
1945     if (surfaceFormats.empty())
1946         return tcu::TestStatus::fail("No VkSurfaceFormatKHR defined");
1947 
1948     const std::vector<VkPresentModeKHR> presentModes =
1949         getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surface);
1950     if (std::find(presentModes.begin(), presentModes.end(), testParams.mode) == presentModes.end())
1951         TCU_THROW(NotSupportedError, "Present mode not supported");
1952 
1953     const VkSurfaceCapabilitiesKHR capabilities =
1954         getPerPresentSurfaceCapabilities(instHelper.vki, devHelper.physicalDevice, *surface, testParams.mode);
1955     const VkSurfaceTransformFlagBitsKHR transform =
1956         (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0 ?
1957             VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR :
1958             capabilities.currentTransform;
1959 
1960     if (testParams.scaling != 0)
1961     {
1962         // Skip if configuration is not supported
1963         VkSurfacePresentScalingCapabilitiesEXT scaling =
1964             getSurfaceScalingCapabilities(instHelper.vki, devHelper.physicalDevice, testParams.mode, *surface);
1965 
1966         if ((scaling.supportedPresentScaling & testParams.scaling) == 0)
1967             TCU_THROW(NotSupportedError, "Scaling mode is not supported");
1968     }
1969 
1970     const bool isSharedPresentMode = testParams.mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
1971                                      testParams.mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
1972     if (isSharedPresentMode && (capabilities.minImageCount != 1 || capabilities.maxImageCount != 1))
1973     {
1974         return tcu::TestStatus::fail("min and max image count for shared present modes must be 1");
1975     }
1976 
1977     uint32_t imageCount = capabilities.minImageCount + 10;
1978     if (capabilities.maxImageCount > 0)
1979         imageCount = de::min(imageCount, capabilities.maxImageCount);
1980 
1981     VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(*surface, surfaceFormats[0], native.windowSize,
1982                                                                          testParams.mode, transform, imageCount, false);
1983 
1984     VkSwapchainPresentScalingCreateInfoEXT scalingInfo = {
1985         VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT, DE_NULL, testParams.scaling, 0, 0,
1986     };
1987     swapchainInfo.pNext = &scalingInfo;
1988 
1989     Move<VkSwapchainKHR> swapchain(createSwapchainKHR(vkd, device, &swapchainInfo));
1990     std::vector<VkImage> swapchainImages = getSwapchainImages(vkd, device, *swapchain);
1991 
1992     const Unique<VkCommandPool> commandPool(
1993         createCommandPool(vkd, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, devHelper.queueFamilyIndex));
1994 
1995     const uint32_t iterations = getIterations({testParams.mode}, {}, testParams.resizeWindow != ResizeWindow::No);
1996 
1997     // Do testParams.iterations presents, with a fence associated with the last one.
1998     FenceSp presentFence = FenceSp(new Unique<VkFence>(createFence(vkd, device)));
1999     const std::vector<SemaphoreSp> acquireSems(createSemaphores(vkd, device, iterations));
2000     const std::vector<SemaphoreSp> presentSems(createSemaphores(vkd, device, iterations));
2001 
2002     const std::vector<CommandBufferSp> commandBuffers(
2003         allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, iterations));
2004 
2005     const uint64_t foreverNs = 0xFFFFFFFFFFFFFFFFul;
2006 
2007     VkImageSubresourceRange range = {
2008         VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1,
2009     };
2010 
2011     const uint32_t configHash = (uint32_t)testParams.wsiType | (uint32_t)testParams.mode << 4 |
2012                                 (uint32_t)testParams.scaling << 24 | (uint32_t)testParams.resizeWindow << 28 |
2013                                 (uint32_t)testParams.releaseBeforePresent << 30 |
2014                                 (uint32_t)testParams.releaseBeforeRetire << 31;
2015     de::Random rng(0x53A4C8A1u ^ configHash);
2016 
2017     try
2018     {
2019         for (uint32_t i = 0; i < iterations; ++i)
2020         {
2021             // Decide on how many acquires to do, and whether a presentation is to be done.  Presentation is always done for the last iteration, to facilitate clean up (by adding a present fence).
2022             const uint32_t maxAllowedAcquires = (uint32_t)swapchainImages.size() - capabilities.minImageCount + 1;
2023             const uint32_t acquireCount       = rng.getUint32() % maxAllowedAcquires + 1;
2024             const bool doPresent              = i + 1 == iterations || rng.getUint32() % 10 != 0;
2025             const bool doResize         = testParams.resizeWindow != ResizeWindow::No && rng.getUint32() % 10 != 0;
2026             const uint32_t presentIndex = doPresent ? rng.getUint32() % acquireCount : acquireCount;
2027 
2028             // Resize the window if requested.
2029             if (doResize && testParams.resizeWindow == ResizeWindow::BeforeAcquire)
2030             {
2031                 tcu::UVec2 windowSize = native.windowSize;
2032                 windowSize.x()        = windowSize.x() - 20 + rng.getUint32() % 41;
2033                 windowSize.y()        = windowSize.y() - 20 + rng.getUint32() % 41;
2034 
2035                 native.windows[0]->resize(windowSize);
2036             }
2037 
2038             // Acquire N times
2039             const VkSemaphore presentSem = **presentSems[i];
2040             const VkSemaphore acquireSem = **acquireSems[i];
2041             std::vector<uint32_t> acquiredIndices(acquireCount, 0x12345);
2042             FenceSp acquireFenceSp      = FenceSp(new Unique<VkFence>(createFence(vkd, device)));
2043             const VkFence &acquireFence = **acquireFenceSp;
2044 
2045             VkResult result =
2046                 vkd.acquireNextImageKHR(device, *swapchain, foreverNs, presentIndex == 0 ? acquireSem : DE_NULL,
2047                                         acquireFence, &acquiredIndices[0]);
2048             if (result == VK_SUCCESS)
2049             {
2050                 VK_CHECK(vkd.waitForFences(device, 1u, &acquireFence, VK_TRUE, kMaxFenceWaitTimeout));
2051                 VK_CHECK(vkd.resetFences(device, 1u, &acquireFence));
2052             }
2053 
2054             // If out of date, recreate the swapchain and reacquire.
2055             if (result == VK_ERROR_OUT_OF_DATE_KHR)
2056             {
2057                 swapchainInfo.oldSwapchain = *swapchain;
2058                 Move<VkSwapchainKHR> newSwapchain(createSwapchainKHR(vkd, device, &swapchainInfo));
2059                 swapchain = std::move(newSwapchain);
2060 
2061                 const size_t previousImageCount = swapchainImages.size();
2062                 swapchainImages                 = getSwapchainImages(vkd, device, *swapchain);
2063                 if (previousImageCount != swapchainImages.size())
2064                     TCU_THROW(InternalError,
2065                               "Unexpected change in number of swapchain images when recreated during window resize");
2066 
2067                 result =
2068                     vkd.acquireNextImageKHR(device, *swapchain, foreverNs, presentIndex == 0 ? acquireSem : DE_NULL,
2069                                             acquireFence, &acquiredIndices[0]);
2070                 if (result == VK_SUCCESS)
2071                 {
2072                     VK_CHECK(vkd.waitForFences(device, 1u, &acquireFence, VK_TRUE, kMaxFenceWaitTimeout));
2073                     VK_CHECK(vkd.resetFences(device, 1u, &acquireFence));
2074                 }
2075             }
2076 
2077             VK_CHECK_WSI(result);
2078 
2079             for (uint32_t j = 1; j < acquireCount; ++j)
2080             {
2081                 VK_CHECK_WSI(vkd.acquireNextImageKHR(device, *swapchain, foreverNs,
2082                                                      presentIndex == j ? acquireSem : DE_NULL, acquireFence,
2083                                                      &acquiredIndices[j]));
2084                 VK_CHECK(vkd.waitForFences(device, 1u, &acquireFence, VK_TRUE, kMaxFenceWaitTimeout));
2085                 VK_CHECK(vkd.resetFences(device, 1u, &acquireFence));
2086             }
2087 
2088             // Construct a list of image indices to be released.  That is every index except the one being presented, if any.
2089             std::vector<uint32_t> releaseIndices = acquiredIndices;
2090             if (doPresent)
2091             {
2092                 releaseIndices.erase(releaseIndices.begin() + presentIndex);
2093             }
2094             size_t imageReleaseSize = releaseIndices.size();
2095 
2096             // Randomize the indices to be released.
2097             rng.shuffle(releaseIndices.begin(), releaseIndices.end());
2098 
2099             if (doResize && testParams.resizeWindow == ResizeWindow::BeforePresent)
2100             {
2101                 tcu::UVec2 windowSize = native.windowSize;
2102                 windowSize.x()        = windowSize.x() - 20 + rng.getUint32() % 41;
2103                 windowSize.y()        = windowSize.y() - 20 + rng.getUint32() % 41;
2104 
2105                 native.windows[0]->resize(windowSize);
2106             }
2107 
2108             if (doPresent)
2109             {
2110                 beginCommandBuffer(vkd, **commandBuffers[i], 0u);
2111 
2112                 VkImageMemoryBarrier barrier = {
2113                     VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
2114                     DE_NULL,
2115                     0,
2116                     0,
2117                     VK_IMAGE_LAYOUT_UNDEFINED,
2118                     VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
2119                     VK_QUEUE_FAMILY_IGNORED,
2120                     VK_QUEUE_FAMILY_IGNORED,
2121                     swapchainImages[acquiredIndices[presentIndex]],
2122                     range,
2123                 };
2124                 vkd.cmdPipelineBarrier(**commandBuffers[i], VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
2125                                        VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0, nullptr, 0, nullptr, 1, &barrier);
2126 
2127                 VkClearColorValue clearValue;
2128                 clearValue.float32[0] = static_cast<float>(i % 33) / 32.0f;
2129                 clearValue.float32[1] = static_cast<float>((i + 7) % 33) / 32.0f;
2130                 clearValue.float32[2] = static_cast<float>((i + 17) % 33) / 32.0f;
2131                 clearValue.float32[3] = 1.0f;
2132 
2133                 vkd.cmdClearColorImage(**commandBuffers[i], swapchainImages[acquiredIndices[presentIndex]],
2134                                        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearValue, 1, &range);
2135 
2136                 barrier.oldLayout     = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2137                 barrier.newLayout     = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2138                 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2139 
2140                 vkd.cmdPipelineBarrier(**commandBuffers[i], VK_PIPELINE_STAGE_TRANSFER_BIT,
2141                                        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0u, 0, DE_NULL, 0, DE_NULL, 1, &barrier);
2142 
2143                 endCommandBuffer(vkd, **commandBuffers[i]);
2144 
2145                 // Submit the command buffer
2146                 VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2147                 const VkSubmitInfo submitInfo  = {
2148                     VK_STRUCTURE_TYPE_SUBMIT_INFO, DE_NULL, 1,           &acquireSem, &waitStage, 1u,
2149                     &**commandBuffers[i],          1u,      &presentSem,
2150                 };
2151                 VK_CHECK(vkd.queueSubmit(devHelper.queue, 1u, &submitInfo, DE_NULL));
2152             }
2153 
2154             // If asked to release before present, do so now.
2155             const VkReleaseSwapchainImagesInfoEXT releaseInfo = {
2156                 VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_EXT,
2157                 DE_NULL,
2158                 *swapchain,
2159                 (uint32_t)imageReleaseSize,
2160                 releaseIndices.data(),
2161             };
2162 
2163             bool imagesReleased = false;
2164             if (testParams.releaseBeforePresent && imageReleaseSize > 0)
2165             {
2166                 VK_CHECK(vkd.releaseSwapchainImagesEXT(device, &releaseInfo));
2167                 imagesReleased = true;
2168             }
2169 
2170             // Present the frame
2171             if (doPresent)
2172             {
2173                 const VkSwapchainPresentFenceInfoEXT presentFenceInfo = {
2174                     VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT,
2175                     DE_NULL,
2176                     1,
2177                     &**presentFence,
2178                 };
2179 
2180                 const VkPresentInfoKHR presentInfo = {
2181                     VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
2182                     // Signal the present fence on the last present.
2183                     i + 1 == iterations ? &presentFenceInfo : nullptr,
2184                     1u,
2185                     &presentSem,
2186                     1,
2187                     &*swapchain,
2188                     &acquiredIndices[presentIndex],
2189                     &result,
2190                 };
2191                 VkResult aggregateResult = vkd.queuePresentKHR(devHelper.queue, &presentInfo);
2192                 if (aggregateResult == VK_ERROR_OUT_OF_DATE_KHR || result == VK_ERROR_OUT_OF_DATE_KHR)
2193                 {
2194                     // If OUT_OF_DATE is returned from present, recreate the swapchain and release images to the retired swapchain.
2195                     if (!imagesReleased && testParams.releaseBeforeRetire && imageReleaseSize > 0)
2196                     {
2197                         VK_CHECK(vkd.releaseSwapchainImagesEXT(device, &releaseInfo));
2198                         imagesReleased = true;
2199                     }
2200 
2201                     swapchainInfo.oldSwapchain = *swapchain;
2202                     Move<VkSwapchainKHR> newSwapchain(createSwapchainKHR(vkd, device, &swapchainInfo));
2203 
2204                     if (!imagesReleased && !testParams.releaseBeforeRetire && imageReleaseSize > 0)
2205                     {
2206                         // Release the images to the retired swapchain before deleting it (as part of move assignment below)
2207                         VK_CHECK(vkd.releaseSwapchainImagesEXT(device, &releaseInfo));
2208                         imagesReleased = true;
2209                     }
2210 
2211                     // Must have released old swapchain's images before destruction
2212                     DE_ASSERT(imagesReleased || imageReleaseSize == 0);
2213                     swapchain = std::move(newSwapchain);
2214 
2215                     const size_t previousImageCount = swapchainImages.size();
2216                     swapchainImages                 = getSwapchainImages(vkd, device, *swapchain);
2217                     if (previousImageCount != swapchainImages.size())
2218                         TCU_THROW(
2219                             InternalError,
2220                             "Unexpected change in number of swapchain images when recreated during window resize");
2221                 }
2222                 else
2223                 {
2224                     VK_CHECK_WSI(result);
2225                     VK_CHECK_WSI(result);
2226                 }
2227             }
2228 
2229             // If asked to release after present, do it now.
2230             if (!imagesReleased && imageReleaseSize > 0)
2231             {
2232                 VK_CHECK_WSI(vkd.releaseSwapchainImagesEXT(device, &releaseInfo));
2233             }
2234         }
2235 
2236         // Wait for all presents before terminating the test (when semaphores are destroyed)
2237         VK_CHECK(vkd.waitForFences(device, 1u, &**presentFence, VK_TRUE, kMaxFenceWaitTimeout));
2238     }
2239     catch (...)
2240     {
2241         // Make sure device is idle before destroying resources
2242         vkd.deviceWaitIdle(device);
2243         throw;
2244     }
2245 
2246     native.windows[0]->setVisible(false);
2247 
2248     return tcu::TestStatus::pass("Tests ran successfully");
2249 }
2250 
populateReleaseImagesGroup(tcu::TestCaseGroup * testGroup,Type wsiType)2251 void populateReleaseImagesGroup(tcu::TestCaseGroup *testGroup, Type wsiType)
2252 {
2253     const struct
2254     {
2255         VkPresentModeKHR mode;
2256         const char *name;
2257     } presentModes[] = {
2258         {VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate"},
2259         {VK_PRESENT_MODE_MAILBOX_KHR, "mailbox"},
2260         {VK_PRESENT_MODE_FIFO_KHR, "fifo"},
2261         {VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed"},
2262         {VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand"},
2263         {VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous"},
2264     };
2265 
2266     const struct
2267     {
2268         VkPresentScalingFlagsEXT scaling;
2269         const char *name;
2270     } scalingFlags[] = {
2271         {0, "no_scaling"},
2272         {VK_PRESENT_SCALING_STRETCH_BIT_EXT, "stretch"},
2273     };
2274 
2275     for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
2276     {
2277         de::MovePtr<tcu::TestCaseGroup> presentModeGroup(
2278             new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
2279 
2280         for (size_t scalingFlagNdx = 0; scalingFlagNdx < DE_LENGTH_OF_ARRAY(scalingFlags); scalingFlagNdx++)
2281         {
2282             de::MovePtr<tcu::TestCaseGroup> scalingFlagGroup(
2283                 new tcu::TestCaseGroup(testGroup->getTestContext(), scalingFlags[scalingFlagNdx].name));
2284 
2285             ReleaseImagesTestConfig config;
2286             config.wsiType              = wsiType;
2287             config.mode                 = presentModes[presentModeNdx].mode;
2288             config.scaling              = scalingFlags[scalingFlagNdx].scaling;
2289             config.resizeWindow         = ResizeWindow::No;
2290             config.releaseBeforePresent = false;
2291             config.releaseBeforeRetire  = false;
2292 
2293             // Basic release acquired images test
2294             addFunctionCase(&*scalingFlagGroup, "basic", releaseImagesTest, config);
2295 
2296             config.releaseBeforePresent = true;
2297             // Basic release acquired images test where release happens before presenting an image
2298             addFunctionCase(&*scalingFlagGroup, "release_before_present", releaseImagesTest, config);
2299 
2300             config.releaseBeforePresent = false;
2301             config.resizeWindow         = ResizeWindow::BeforeAcquire;
2302             // Release acquired images after a window resize before acquire
2303             addFunctionCase(&*scalingFlagGroup, "resize_window", releaseImagesTest, config);
2304 
2305             config.resizeWindow = ResizeWindow::BeforePresent;
2306             // Release acquired images after a window resize after acquire
2307             addFunctionCase(&*scalingFlagGroup, "resize_window_after_acquire", releaseImagesTest, config);
2308 
2309             config.releaseBeforeRetire = true;
2310             // Release acquired images after a window resize after acquire, but release the images before retiring the swapchain
2311             addFunctionCase(&*scalingFlagGroup, "resize_window_after_acquire_release_before_retire", releaseImagesTest,
2312                             config);
2313 
2314             presentModeGroup->addChild(scalingFlagGroup.release());
2315         }
2316 
2317         testGroup->addChild(presentModeGroup.release());
2318     }
2319 }
2320 
2321 } // namespace
2322 
createMaintenance1Tests(tcu::TestCaseGroup * testGroup,vk::wsi::Type wsiType)2323 void createMaintenance1Tests(tcu::TestCaseGroup *testGroup, vk::wsi::Type wsiType)
2324 {
2325     // Present fence
2326     addTestGroup(testGroup, "present_fence", populatePresentFenceGroup, wsiType);
2327     // Change present modes
2328     addTestGroup(testGroup, "present_modes", populatePresentModesGroup, wsiType);
2329     // Scaling and gravity
2330     addTestGroup(testGroup, "scaling", populateScalingGroup, wsiType);
2331     // Deferred allocation
2332     addTestGroup(testGroup, "deferred_alloc", populateDeferredAllocGroup, wsiType);
2333     // Release acquired images
2334     addTestGroup(testGroup, "release_images", populateReleaseImagesGroup, wsiType);
2335 }
2336 
2337 } // namespace wsi
2338 
2339 } // namespace vkt
2340