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, ®ion);
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