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