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::__anon74bd6c510111::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::__anon74bd6c510111::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, context.getUsedApiVersion())
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::__anon74bd6c510111::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));
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 // Basic present fence test
1119 addFunctionCase(&*presentModeGroup, "basic", presentFenceTest, config);
1120
1121 config.verifyFenceOrdering = true;
1122 // Test ordering guarantee of present fence signals
1123 addFunctionCase(&*presentModeGroup, "ordering", presentFenceTest, config);
1124
1125 if (canDoMultiSwapchainPresent(wsiType))
1126 {
1127 config.verifyFenceOrdering = false;
1128 config.modes = std::vector<VkPresentModeKHR>(3, presentModes[presentModeNdx].mode);
1129 // Present fence test with multiple swapchains
1130 addFunctionCase(&*presentModeGroup, "multi_swapchain", presentFenceTest, config);
1131
1132 config.verifyFenceOrdering = true;
1133 // Test ordering guarantee of present fence signals with multiple swapchains
1134 addFunctionCase(&*presentModeGroup, "mult_swapchain_ordering", presentFenceTest, config);
1135 }
1136
1137 testGroup->addChild(presentModeGroup.release());
1138 }
1139 }
1140
1141 struct PresentModesTestConfig
1142 {
1143 vk::wsi::Type wsiType;
1144 VkPresentModeKHR mode;
1145 };
1146
verifyCompatiblePresentModes(const std::vector<VkPresentModeKHR> & supportedModes,const VkPresentModeKHR queryMode,const std::vector<VkPresentModeKHR> & compatibleModes,const std::vector<VkPresentModeKHR> * previouslyQueriedCompatibleModes)1147 tcu::TestStatus verifyCompatiblePresentModes(const std::vector<VkPresentModeKHR>& supportedModes,
1148 const VkPresentModeKHR queryMode,
1149 const std::vector<VkPresentModeKHR>& compatibleModes,
1150 const std::vector<VkPresentModeKHR>* previouslyQueriedCompatibleModes)
1151 {
1152 // Every returned compatible mode must be supported by the surface
1153 for (size_t i = 0; i < compatibleModes.size(); ++i)
1154 if (std::find(supportedModes.begin(), supportedModes.end(), compatibleModes[i]) == supportedModes.end())
1155 return tcu::TestStatus::fail("Returned compatible present mode " + de::toString(compatibleModes[i]) + " is not a supported present mode");
1156
1157 // The original mode being queried must always be in the compatible list
1158 if (!compatibleModes.empty() && std::find(compatibleModes.begin(), compatibleModes.end(), queryMode) == compatibleModes.end())
1159 return tcu::TestStatus::fail("Returned compatible present modes does not include the mode used in the query");
1160
1161 // There should be no duplicates in the returned modes
1162 std::set<VkPresentModeKHR> visitedModes;
1163 for (VkPresentModeKHR compatibleMode : compatibleModes)
1164 {
1165 if (visitedModes.find(compatibleMode) != visitedModes.end())
1166 return tcu::TestStatus::fail("Duplicate mode " + de::toString(compatibleMode) + " returned in list of compatible present modes");
1167 visitedModes.insert(compatibleMode);
1168 }
1169
1170 // If provided, the returned list of modes should match the last previous query
1171 if (previouslyQueriedCompatibleModes)
1172 {
1173 for (VkPresentModeKHR previousCompatibleMode : *previouslyQueriedCompatibleModes)
1174 if (visitedModes.find(previousCompatibleMode) == visitedModes.end())
1175 return tcu::TestStatus::fail("Different sets of compatible modes returned on re-query (present mode " + de::toString(previousCompatibleMode) + " missing on requery)");
1176 }
1177
1178 return tcu::TestStatus::pass("");
1179 }
1180
presentModesQueryTest(Context & context,const PresentModesTestConfig testParams)1181 tcu::TestStatus presentModesQueryTest(Context& context, const PresentModesTestConfig testParams)
1182 {
1183 const InstanceHelper instHelper (context, testParams.wsiType, false);
1184 const TestNativeObjects native (context, instHelper.supportedExtensions, testParams.wsiType, 1);
1185 Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display, *native.windows[0], context.getTestContext().getCommandLine()));
1186 const DeviceHelper devHelper (context, instHelper.vki, instHelper.instance, *surface, false, false);
1187
1188 const std::vector<VkPresentModeKHR> presentModes = getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surface);
1189 if (std::find(presentModes.begin(), presentModes.end(), testParams.mode) == presentModes.end())
1190 TCU_THROW(NotSupportedError, "Present mode not supported");
1191
1192 // Get the compatible present modes with the given one.
1193 VkSurfacePresentModeEXT presentModeInfo =
1194 {
1195 VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
1196 DE_NULL,
1197 testParams.mode,
1198 };
1199 const VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo =
1200 {
1201 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
1202 &presentModeInfo,
1203 *surface,
1204 };
1205 VkSurfacePresentModeCompatibilityEXT compatibility =
1206 {
1207 VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT,
1208 DE_NULL,
1209 0,
1210 DE_NULL,
1211 };
1212 VkSurfaceCapabilities2KHR capabilities =
1213 {
1214 VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
1215 &compatibility,
1216 {},
1217 };
1218
1219 // Test that querying only the count works.
1220 VK_CHECK(instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1221
1222 // The return value must be at least one, as every mode is compatible with itself.
1223 if (compatibility.presentModeCount < 1)
1224 return tcu::TestStatus::fail("Empty compatible present mode list");
1225
1226 // Test again providing a buffer that's too small
1227 constexpr VkPresentModeKHR invalidValue = (VkPresentModeKHR)0x1234;
1228 std::vector<VkPresentModeKHR> compatibleModes (compatibility.presentModeCount, invalidValue);
1229 compatibility.pPresentModes = compatibleModes.data();
1230
1231 uint32_t originalCompatibleModesCount = compatibility.presentModeCount;
1232
1233 // Check result when count is 0
1234 compatibility.presentModeCount = 0;
1235 VkResult result = instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities);
1236 if (result != VK_SUCCESS)
1237 return tcu::TestStatus::fail("Wrong result when the size is 0");
1238
1239 // Check result when count is too small
1240 compatibility.presentModeCount = originalCompatibleModesCount - 1;
1241 result = instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities);
1242 if (result != VK_SUCCESS)
1243 return tcu::TestStatus::fail("Wrong result when the size is too small");
1244
1245 // Make sure whatever _is_ returned is valid.
1246 if (compatibility.presentModeCount > originalCompatibleModesCount - 1)
1247 return tcu::TestStatus::fail("Re-query returned more results than provided");
1248
1249 // Ensure the rest of the array is not overwritten
1250 for (size_t i = compatibility.presentModeCount; i < compatibleModes.size(); ++i)
1251 {
1252 if (compatibleModes[i] != invalidValue)
1253 return tcu::TestStatus::fail("Query overwrote beyond returned count");
1254 }
1255 compatibleModes.resize(compatibility.presentModeCount);
1256 tcu::TestStatus status = verifyCompatiblePresentModes(presentModes, testParams.mode, compatibleModes, nullptr);
1257 if (status.isFail())
1258 return status;
1259
1260 // Check result when count is correct
1261 compatibility.presentModeCount = originalCompatibleModesCount;
1262 std::vector<VkPresentModeKHR> compatibleModes2(compatibility.presentModeCount, invalidValue);
1263 compatibility.pPresentModes = compatibleModes2.data();
1264
1265 VK_CHECK(instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1266
1267 // Make sure returned modes are valid.
1268 if (compatibility.presentModeCount != originalCompatibleModesCount)
1269 return tcu::TestStatus::fail("Re-query returned different results count than provided");
1270
1271 status = verifyCompatiblePresentModes(presentModes, testParams.mode, compatibleModes2, &compatibleModes);
1272 if (status.isFail())
1273 return status;
1274
1275 // Check that querying with a count higher than supported still returns as many results as before.
1276 compatibility.presentModeCount = originalCompatibleModesCount * 2;
1277 std::vector<VkPresentModeKHR> compatibleModes3(compatibility.presentModeCount, invalidValue);
1278 compatibility.pPresentModes = compatibleModes3.data();
1279
1280 VK_CHECK(instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1281
1282 // Make sure returned modes are the same as before.
1283 if (compatibility.presentModeCount != originalCompatibleModesCount)
1284 return tcu::TestStatus::fail("Re-query returned different results count than provided");
1285
1286 // Ensure the rest of the array is not overwritten
1287 for (size_t i = compatibility.presentModeCount; i < compatibleModes3.size(); ++i)
1288 {
1289 if (compatibleModes3[i] != invalidValue)
1290 return tcu::TestStatus::fail("Query overwrote beyond returned count");
1291 }
1292
1293 compatibleModes3.resize(compatibility.presentModeCount);
1294 status = verifyCompatiblePresentModes(presentModes, testParams.mode, compatibleModes3, &compatibleModes2);
1295 if (status.isFail())
1296 return status;
1297
1298 return tcu::TestStatus::pass("Tests ran successfully");
1299 }
1300
populatePresentModesGroup(tcu::TestCaseGroup * testGroup,Type wsiType)1301 void populatePresentModesGroup (tcu::TestCaseGroup* testGroup, Type wsiType)
1302 {
1303 const struct
1304 {
1305 VkPresentModeKHR mode;
1306 const char* name;
1307 } presentModes[] =
1308 {
1309 { VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate" },
1310 { VK_PRESENT_MODE_MAILBOX_KHR, "mailbox" },
1311 { VK_PRESENT_MODE_FIFO_KHR, "fifo" },
1312 { VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed" },
1313 { VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand" },
1314 { VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous" },
1315 };
1316
1317 for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
1318 {
1319 de::MovePtr<tcu::TestCaseGroup> presentModeGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
1320
1321 {
1322 PresentModesTestConfig config;
1323 config.wsiType = wsiType;
1324 config.mode = presentModes[presentModeNdx].mode;
1325
1326 // Query compatible present modes
1327 addFunctionCase(&*presentModeGroup, "query", presentModesQueryTest, config);
1328 }
1329
1330 {
1331 PresentFenceTestConfig config;
1332 config.wsiType = wsiType;
1333 config.modes = std::vector<VkPresentModeKHR>(1, presentModes[presentModeNdx].mode);
1334 config.deferMemoryAllocation = false;
1335 config.bindImageMemory = false;
1336 config.changePresentModes = true;
1337 config.verifyFenceOrdering = false;
1338
1339 // Switch between compatible modes
1340 addFunctionCase(&*presentModeGroup, "change_modes", presentFenceTest, config);
1341
1342 if (canDoMultiSwapchainPresent(wsiType))
1343 {
1344 config.modes = std::vector<VkPresentModeKHR>(4, presentModes[presentModeNdx].mode);
1345
1346 // Switch between compatible modes with multiple swapchains
1347 addFunctionCase(&*presentModeGroup, "change_modes_multi_swapchain", presentFenceTest, config);
1348
1349 config.modes = std::vector<VkPresentModeKHR>(2, presentModes[presentModeNdx].mode);
1350 config.deferMemoryAllocation = true;
1351
1352 // Switch between compatible modes while swapchain uses deferred allocation
1353 addFunctionCase(&*presentModeGroup, "change_modes_with_deferred_alloc", presentFenceTest, config);
1354 }
1355 }
1356
1357 testGroup->addChild(presentModeGroup.release());
1358 }
1359
1360 if (canDoMultiSwapchainPresent(wsiType))
1361 {
1362 // Switch between compatible modes with multiple swapchains in different modes
1363 de::MovePtr<tcu::TestCaseGroup> heterogenousGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), "heterogenous"));
1364
1365 std::vector<VkPresentModeKHR> modes(3);
1366 for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(presentModes); i++)
1367 {
1368 for (size_t j = 0; j < DE_LENGTH_OF_ARRAY(presentModes); j++)
1369 {
1370 for (size_t k = 0; k < DE_LENGTH_OF_ARRAY(presentModes); k++)
1371 {
1372 // Skip if not actually heterogenous
1373 if (i == j && i == k)
1374 continue;
1375
1376 std::string testName = presentModes[i].name;
1377 testName += "_";
1378 testName += presentModes[j].name;
1379 testName += "_";
1380 testName += presentModes[k].name;
1381
1382 modes[0] = presentModes[i].mode;
1383 modes[1] = presentModes[j].mode;
1384 modes[2] = presentModes[k].mode;
1385
1386 PresentFenceTestConfig config;
1387 config.wsiType = wsiType;
1388 config.modes = modes;
1389 config.deferMemoryAllocation = false;
1390 config.bindImageMemory = false;
1391 config.changePresentModes = true;
1392 config.verifyFenceOrdering = false;
1393
1394 addFunctionCase(&*heterogenousGroup, testName, presentFenceTest, config);
1395 }
1396 }
1397 }
1398
1399 testGroup->addChild(heterogenousGroup.release());
1400 }
1401 }
1402
1403 enum class SwapchainWindowSize
1404 {
1405 Identical,
1406 SwapchainBigger,
1407 SwapchainSmaller,
1408 };
1409
1410 enum class SwapchainWindowAspect
1411 {
1412 Identical,
1413 SwapchainTaller,
1414 SwapchainWider,
1415 };
1416
1417 struct ScalingQueryTestConfig
1418 {
1419 vk::wsi::Type wsiType;
1420 VkPresentModeKHR mode;
1421 };
1422
1423 struct ScalingTestConfig
1424 {
1425 vk::wsi::Type wsiType;
1426 VkPresentModeKHR mode;
1427 VkPresentScalingFlagsEXT scaling;
1428 VkPresentGravityFlagsEXT gravityX;
1429 VkPresentGravityFlagsEXT gravityY;
1430 SwapchainWindowSize size;
1431 SwapchainWindowAspect aspect;
1432 // Either have the swapchain be created with a different size, or resize the window after swapchain creation
1433 bool resizeWindow;
1434 };
1435
scalingQueryTest(Context & context,const ScalingQueryTestConfig testParams)1436 tcu::TestStatus scalingQueryTest(Context& context, const ScalingQueryTestConfig testParams)
1437 {
1438 const InstanceHelper instHelper (context, testParams.wsiType, false);
1439 const TestNativeObjects native (context, instHelper.supportedExtensions, testParams.wsiType, 1);
1440 Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display, *native.windows[0], context.getTestContext().getCommandLine()));
1441 const DeviceHelper devHelper (context, instHelper.vki, instHelper.instance, *surface, false, false);
1442
1443 // Query the scaling capabilities and make sure they only report acceptable values.
1444 VkSurfacePresentScalingCapabilitiesEXT scaling = getSurfaceScalingCapabilities(instHelper.vki, devHelper.physicalDevice, testParams.mode, *surface);
1445
1446 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;
1447 constexpr VkPresentGravityFlagsEXT gravityFlags = VK_PRESENT_GRAVITY_MIN_BIT_EXT | VK_PRESENT_GRAVITY_MAX_BIT_EXT | VK_PRESENT_GRAVITY_CENTERED_BIT_EXT;
1448
1449 if ((scaling.supportedPresentScaling & ~scalingFlags) != 0)
1450 return tcu::TestStatus::fail("Invalid bits in scaling flags");
1451
1452 if ((scaling.supportedPresentGravityX & ~gravityFlags) != 0)
1453 return tcu::TestStatus::fail("Invalid bits in gravity flags (x axis)");
1454
1455 if ((scaling.supportedPresentGravityY & ~gravityFlags) != 0)
1456 return tcu::TestStatus::fail("Invalid bits in gravity flags (y axis)");
1457
1458 return tcu::TestStatus::pass("Tests ran successfully");
1459 }
1460
scalingQueryCompatibleModesTest(Context & context,const ScalingQueryTestConfig testParams)1461 tcu::TestStatus scalingQueryCompatibleModesTest(Context& context, const ScalingQueryTestConfig testParams)
1462 {
1463 const InstanceHelper instHelper (context, testParams.wsiType, false);
1464 const TestNativeObjects native (context, instHelper.supportedExtensions, testParams.wsiType, 1);
1465 Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display, *native.windows[0], context.getTestContext().getCommandLine()));
1466 const DeviceHelper devHelper (context, instHelper.vki, instHelper.instance, *surface, false, false);
1467
1468 // Query compatible present modes, and scaling capabilities for each mode. They must all be identical.
1469 VkSurfacePresentModeEXT presentModeInfo =
1470 {
1471 VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
1472 DE_NULL,
1473 testParams.mode,
1474 };
1475 const VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo =
1476 {
1477 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
1478 &presentModeInfo,
1479 *surface,
1480 };
1481 VkSurfacePresentModeCompatibilityEXT compatibility =
1482 {
1483 VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT,
1484 DE_NULL,
1485 0,
1486 DE_NULL,
1487 };
1488 VkSurfaceCapabilities2KHR capabilities =
1489 {
1490 VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
1491 &compatibility,
1492 {},
1493 };
1494
1495 VK_CHECK(instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1496 std::vector<VkPresentModeKHR> compatibleModes (compatibility.presentModeCount, (VkPresentModeKHR)0x5678);
1497 compatibility.pPresentModes = compatibleModes.data();
1498
1499 VK_CHECK(instHelper.vki.getPhysicalDeviceSurfaceCapabilities2KHR(devHelper.physicalDevice, &surfaceInfo, &capabilities));
1500
1501 std::vector<VkSurfacePresentScalingCapabilitiesEXT> scaling(compatibility.presentModeCount);
1502
1503 for (uint32_t i = 0; i < compatibility.presentModeCount; ++i)
1504 scaling[i] = getSurfaceScalingCapabilities(instHelper.vki, devHelper.physicalDevice, compatibleModes[i], *surface);
1505
1506 for (uint32_t i = 1; i < compatibility.presentModeCount; ++i)
1507 {
1508 if (scaling[i].supportedPresentScaling != scaling[0].supportedPresentScaling)
1509 return tcu::TestStatus::fail("Different scaling flags for compatible present modes is not allowed");
1510
1511 if (scaling[i].supportedPresentGravityX != scaling[0].supportedPresentGravityX)
1512 return tcu::TestStatus::fail("Different gravity flags (x axis) for compatible present modes is not allowed");
1513
1514 if (scaling[i].supportedPresentGravityY != scaling[0].supportedPresentGravityY)
1515 return tcu::TestStatus::fail("Different gravity flags (y axis) for compatible present modes is not allowed");
1516 }
1517
1518 return tcu::TestStatus::pass("Tests ran successfully");
1519 }
1520
scalingTest(Context & context,const ScalingTestConfig testParams)1521 tcu::TestStatus scalingTest(Context& context, const ScalingTestConfig testParams)
1522 {
1523 const InstanceHelper instHelper (context, testParams.wsiType, false);
1524 const TestNativeObjects native (context, instHelper.supportedExtensions, testParams.wsiType, 1);
1525 Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display, *native.windows[0], context.getTestContext().getCommandLine()));
1526
1527 const DeviceHelper devHelper (context, instHelper.vki, instHelper.instance, *surface, true, false);
1528 const DeviceInterface& vkd = devHelper.vkd;
1529 const VkDevice device = *devHelper.device;
1530 SimpleAllocator allocator (vkd, device, getPhysicalDeviceMemoryProperties(instHelper.vki, devHelper.physicalDevice));
1531
1532 std::vector<VkSurfaceFormatKHR> surfaceFormats = getPhysicalDeviceSurfaceFormats(instHelper.vki, devHelper.physicalDevice, *surface);
1533 if(surfaceFormats.empty())
1534 return tcu::TestStatus::fail("No VkSurfaceFormatKHR defined");
1535
1536 const VkSurfaceCapabilitiesKHR capabilities = getPhysicalDeviceSurfaceCapabilities(instHelper.vki, devHelper.physicalDevice, *surface, DE_NULL);
1537 const VkSurfaceTransformFlagBitsKHR transform = (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform;
1538
1539 const std::vector<VkPresentModeKHR> presentModes = getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surface);
1540 if (std::find(presentModes.begin(), presentModes.end(), testParams.mode) == presentModes.end())
1541 TCU_THROW(NotSupportedError, "Present mode not supported");
1542
1543 // Skip if configuration is not supported
1544 VkSurfacePresentScalingCapabilitiesEXT scaling = getSurfaceScalingCapabilities(instHelper.vki, devHelper.physicalDevice, testParams.mode, *surface);
1545
1546 if ((scaling.supportedPresentScaling & testParams.scaling) == 0)
1547 TCU_THROW(NotSupportedError, "Scaling mode is not supported");
1548 if (testParams.scaling != VK_PRESENT_SCALING_STRETCH_BIT_EXT)
1549 {
1550 if ((scaling.supportedPresentGravityX & testParams.gravityX) == 0)
1551 TCU_THROW(NotSupportedError, "Gravity mode is not supported (x axis)");
1552 if ((scaling.supportedPresentGravityY & testParams.gravityY) == 0)
1553 TCU_THROW(NotSupportedError, "Gravity mode is not supported (y axis)");
1554 }
1555
1556 tcu::UVec2 swapchainSize = native.windowSize;
1557 if (!testParams.resizeWindow)
1558 {
1559 switch (testParams.size)
1560 {
1561 case SwapchainWindowSize::SwapchainBigger:
1562 swapchainSize.x() *= 2;
1563 swapchainSize.y() *= 2;
1564 break;
1565 case SwapchainWindowSize::SwapchainSmaller:
1566 swapchainSize.x() /= 2;
1567 swapchainSize.y() /= 2;
1568 break;
1569 default:
1570 break;
1571 }
1572 switch (testParams.aspect)
1573 {
1574 case SwapchainWindowAspect::SwapchainTaller:
1575 swapchainSize.y() += swapchainSize.y() / 2;
1576 break;
1577 case SwapchainWindowAspect::SwapchainWider:
1578 swapchainSize.x() += swapchainSize.x() / 2;
1579 break;
1580 default:
1581 break;
1582 }
1583 }
1584
1585 VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(*surface, surfaceFormats[0], swapchainSize, testParams.mode, transform, capabilities.minImageCount, false);
1586
1587 VkSwapchainPresentScalingCreateInfoEXT scalingInfo =
1588 {
1589 VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT,
1590 DE_NULL,
1591 testParams.scaling,
1592 testParams.gravityX,
1593 testParams.gravityY,
1594 };
1595 swapchainInfo.pNext = &scalingInfo;
1596
1597 const Unique<VkSwapchainKHR> swapchain (createSwapchainKHR(vkd, device, &swapchainInfo));
1598 std::vector<VkImage> swapchainImages = getSwapchainImages(vkd, device, *swapchain);
1599
1600 const Unique<VkCommandPool> commandPool (createCommandPool(vkd, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, devHelper.queueFamilyIndex));
1601
1602 constexpr deUint32 iterations = 100;
1603
1604 // Do testParams.iterations presents, with a fence associated with the last one.
1605 FenceSp presentFence = FenceSp(new Unique<VkFence>(createFence(vkd, device)));
1606 const std::vector<SemaphoreSp> acquireSems (createSemaphores(vkd, device, iterations));
1607 const std::vector<SemaphoreSp> presentSems (createSemaphores(vkd, device, iterations));
1608
1609 const std::vector<CommandBufferSp> commandBuffers (allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, iterations));
1610
1611 const deUint64 foreverNs = 0xFFFFFFFFFFFFFFFFul;
1612
1613 VkImageSubresourceRange range =
1614 {
1615 VK_IMAGE_ASPECT_COLOR_BIT,
1616 0,
1617 1,
1618 0,
1619 1,
1620 };
1621
1622 tcu::UVec2 windowSize = native.windowSize;
1623 if (testParams.resizeWindow)
1624 {
1625 switch (testParams.size)
1626 {
1627 case SwapchainWindowSize::SwapchainBigger:
1628 windowSize.x() /= 2;
1629 windowSize.y() /= 2;
1630 break;
1631 case SwapchainWindowSize::SwapchainSmaller:
1632 windowSize.x() *= 2;
1633 windowSize.y() *= 2;
1634 break;
1635 default:
1636 break;
1637 }
1638 switch (testParams.aspect)
1639 {
1640 case SwapchainWindowAspect::SwapchainTaller:
1641 windowSize.x() += windowSize.x() / 2;
1642 break;
1643 case SwapchainWindowAspect::SwapchainWider:
1644 windowSize.y() += windowSize.y() / 2;
1645 break;
1646 default:
1647 break;
1648 }
1649
1650 native.windows[0]->resize(windowSize);
1651 }
1652
1653 const deUint32 quarterPixels = swapchainSize.x() * swapchainSize.y() / 4;
1654 const tcu::UVec4 red (255, 30, 20, 255);
1655 const tcu::UVec4 green (0, 255, 50, 255);
1656 const tcu::UVec4 blue (40, 60, 255, 255);
1657 const tcu::UVec4 yellow (200, 220, 20, 255);
1658 de::MovePtr<Allocation> redMemory;
1659 de::MovePtr<Allocation> greenMemory;
1660 de::MovePtr<Allocation> blueMemory;
1661 de::MovePtr<Allocation> yellowMemory;
1662 const vk::Move<vk::VkBuffer> redBuffer = createBufferAndBindMemory(devHelper, allocator, red, quarterPixels, &redMemory);
1663 const vk::Move<vk::VkBuffer> greenBuffer = createBufferAndBindMemory(devHelper, allocator, green, quarterPixels, &greenMemory);
1664 const vk::Move<vk::VkBuffer> blueBuffer = createBufferAndBindMemory(devHelper, allocator, blue, quarterPixels, &blueMemory);
1665 const vk::Move<vk::VkBuffer> yellowBuffer = createBufferAndBindMemory(devHelper, allocator, yellow, quarterPixels, &yellowMemory);
1666
1667 try
1668 {
1669 for (deUint32 i = 0; i < iterations; ++i)
1670 {
1671 const VkSemaphore presentSem = **presentSems[i];
1672 const VkSemaphore acquireSem = **acquireSems[i];
1673 deUint32 imageIndex = 0x12345; // initialize to junk value
1674
1675 VK_CHECK(vkd.acquireNextImageKHR(device, *swapchain, foreverNs, acquireSem, DE_NULL, &imageIndex));
1676
1677 beginCommandBuffer(vkd, **commandBuffers[i], 0u);
1678
1679 VkImageMemoryBarrier barrier = {
1680 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1681 DE_NULL,
1682 0,
1683 0,
1684 VK_IMAGE_LAYOUT_UNDEFINED,
1685 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1686 VK_QUEUE_FAMILY_IGNORED,
1687 VK_QUEUE_FAMILY_IGNORED,
1688 swapchainImages[imageIndex],
1689 range,
1690 };
1691
1692 vkd.cmdPipelineBarrier(**commandBuffers[i],
1693 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1694 VK_PIPELINE_STAGE_TRANSFER_BIT,
1695 0u,
1696 0, DE_NULL,
1697 0, DE_NULL,
1698 1, &barrier);
1699
1700 const tcu::UVec2 halfSwapchainSize = swapchainSize / 2u;
1701 copyBufferToImage(vkd, **commandBuffers[i], *redBuffer, swapchainImages[imageIndex], tcu::UVec2(0, 0), halfSwapchainSize);
1702 copyBufferToImage(vkd, **commandBuffers[i], *greenBuffer, swapchainImages[imageIndex], tcu::UVec2(halfSwapchainSize.x(), 0), tcu::UVec2(swapchainSize.x() - halfSwapchainSize.x(), halfSwapchainSize.y()));
1703 copyBufferToImage(vkd, **commandBuffers[i], *blueBuffer, swapchainImages[imageIndex], tcu::UVec2(0, halfSwapchainSize.y()), tcu::UVec2(halfSwapchainSize.x(), swapchainSize.y() - halfSwapchainSize.y()));
1704 copyBufferToImage(vkd, **commandBuffers[i], *yellowBuffer, swapchainImages[imageIndex], halfSwapchainSize, tcu::UVec2(swapchainSize.x() - halfSwapchainSize.x(), swapchainSize.y() - halfSwapchainSize.y()));
1705
1706 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1707 barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1708 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1709
1710 vkd.cmdPipelineBarrier(**commandBuffers[i],
1711 VK_PIPELINE_STAGE_TRANSFER_BIT,
1712 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
1713 0u,
1714 0, DE_NULL,
1715 0, DE_NULL,
1716 1, &barrier);
1717
1718 endCommandBuffer(vkd, **commandBuffers[i]);
1719
1720 // Submit the command buffer
1721 VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
1722 const VkSubmitInfo submitInfo =
1723 {
1724 VK_STRUCTURE_TYPE_SUBMIT_INFO,
1725 DE_NULL,
1726 1,
1727 &acquireSem,
1728 &waitStage,
1729 1u,
1730 &**commandBuffers[i],
1731 1u,
1732 &presentSem,
1733 };
1734 VK_CHECK(vkd.queueSubmit(devHelper.queue, 1u, &submitInfo, DE_NULL));
1735
1736 // Present the frame
1737 const VkSwapchainPresentFenceInfoEXT presentFenceInfo =
1738 {
1739 VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT,
1740 DE_NULL,
1741 1,
1742 &**presentFence,
1743 };
1744 VkResult result;
1745
1746 const VkPresentInfoKHR presentInfo =
1747 {
1748 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
1749 // Signal the present fence on the last present.
1750 i + 1 == iterations ? &presentFenceInfo : nullptr,
1751 1u,
1752 &presentSem,
1753 1,
1754 &*swapchain,
1755 &imageIndex,
1756 &result,
1757 };
1758 VK_CHECK_WSI(vkd.queuePresentKHR(devHelper.queue, &presentInfo));
1759 VK_CHECK_WSI(result);
1760
1761 // TODO: wait for present, capture the screen and verify that scaling is done correctly.
1762 }
1763
1764 // Wait for all presents before terminating the test (when semaphores are destroyed)
1765 VK_CHECK(vkd.waitForFences(device, 1u, &**presentFence, VK_TRUE, kMaxFenceWaitTimeout));
1766 }
1767 catch (...)
1768 {
1769 // Make sure device is idle before destroying resources
1770 vkd.deviceWaitIdle(device);
1771 throw;
1772 }
1773
1774 native.windows[0]->setVisible(false);
1775
1776 return tcu::TestStatus::pass("Tests ran successfully");
1777 }
1778
populateScalingTests(tcu::TestCaseGroup * testGroup,Type wsiType,bool resizeWindow)1779 void populateScalingTests (tcu::TestCaseGroup *testGroup, Type wsiType, bool resizeWindow)
1780 {
1781 const struct
1782 {
1783 VkPresentModeKHR mode;
1784 const char* name;
1785 } presentModes[] =
1786 {
1787 { VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate" },
1788 { VK_PRESENT_MODE_MAILBOX_KHR, "mailbox" },
1789 { VK_PRESENT_MODE_FIFO_KHR, "fifo" },
1790 { VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed" },
1791 { VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand" },
1792 { VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous" },
1793 };
1794
1795 const struct
1796 {
1797 VkPresentScalingFlagBitsEXT scaling;
1798 const char* name;
1799 } scalingFlags[] =
1800 {
1801 { VK_PRESENT_SCALING_ONE_TO_ONE_BIT_EXT, "one_to_one" },
1802 { VK_PRESENT_SCALING_ASPECT_RATIO_STRETCH_BIT_EXT, "aspect_stretch" },
1803 { VK_PRESENT_SCALING_STRETCH_BIT_EXT, "stretch" },
1804 };
1805
1806 const struct
1807 {
1808 VkPresentGravityFlagBitsEXT gravity;
1809 const char* name;
1810 } gravityFlags[] =
1811 {
1812 { VK_PRESENT_GRAVITY_MIN_BIT_EXT, "min" },
1813 { VK_PRESENT_GRAVITY_MAX_BIT_EXT, "max" },
1814 { VK_PRESENT_GRAVITY_CENTERED_BIT_EXT, "center" },
1815 };
1816
1817 for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
1818 {
1819 de::MovePtr<tcu::TestCaseGroup> presentModeGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
1820
1821 {
1822 ScalingQueryTestConfig config;
1823 config.wsiType = wsiType;
1824 config.mode = presentModes[presentModeNdx].mode;
1825
1826 de::MovePtr<tcu::TestCaseGroup> queryGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), "query", "Query supported scaling modes"));
1827 // Basic test
1828 addFunctionCase(&*queryGroup, "basic", scalingQueryTest, config);
1829 // Verify compatible present modes have the same scaling capabilities
1830 addFunctionCase(&*queryGroup, "verify_compatible_present_modes", scalingQueryCompatibleModesTest, config);
1831 presentModeGroup->addChild(queryGroup.release());
1832 }
1833
1834 for (size_t scalingFlagNdx = 0; scalingFlagNdx < DE_LENGTH_OF_ARRAY(scalingFlags); scalingFlagNdx++)
1835 {
1836 de::MovePtr<tcu::TestCaseGroup> scalingFlagGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), scalingFlags[scalingFlagNdx].name));
1837
1838 const bool isStretch = scalingFlags[scalingFlagNdx].scaling == VK_PRESENT_SCALING_STRETCH_BIT_EXT;
1839
1840 for (size_t gravityFlagXNdx = 0; gravityFlagXNdx < DE_LENGTH_OF_ARRAY(gravityFlags); gravityFlagXNdx++)
1841 {
1842 for (size_t gravityFlagYNdx = 0; gravityFlagYNdx < DE_LENGTH_OF_ARRAY(gravityFlags); gravityFlagYNdx++)
1843 {
1844 std::string testName = gravityFlags[gravityFlagXNdx].name;
1845 testName += "_";
1846 testName += gravityFlags[gravityFlagYNdx].name;
1847
1848 de::MovePtr<tcu::TestCaseGroup> gravityFlagsGroup (new tcu::TestCaseGroup(scalingFlagGroup->getTestContext(), testName.c_str()));
1849
1850 ScalingTestConfig config;
1851 config.wsiType = wsiType;
1852 config.mode = presentModes[presentModeNdx].mode;
1853 config.scaling = scalingFlags[scalingFlagNdx].scaling;
1854 config.gravityX = gravityFlags[gravityFlagXNdx].gravity;
1855 config.gravityY = gravityFlags[gravityFlagYNdx].gravity;
1856 config.size = SwapchainWindowSize::Identical;
1857 config.aspect = SwapchainWindowAspect::Identical;
1858 config.resizeWindow = resizeWindow;
1859
1860 // Gravity does not apply to stretch
1861 de::MovePtr<tcu::TestCaseGroup> *group = isStretch ? &scalingFlagGroup : &gravityFlagsGroup;
1862
1863 // Basic test without actual scaling
1864 addFunctionCase(&**group, "same_size_and_aspect", scalingTest, config);
1865
1866 config.size = SwapchainWindowSize::SwapchainBigger;
1867 // Swapchain is bigger than window, but has same aspect
1868 addFunctionCase(&**group, "swapchain_bigger_same_aspect", scalingTest, config);
1869
1870 config.size = SwapchainWindowSize::SwapchainSmaller;
1871 // Swapchain is smaller than window, but has same aspect
1872 addFunctionCase(&**group, "swapchain_smaller_same_aspect", scalingTest, config);
1873
1874 config.size = SwapchainWindowSize::Identical;
1875 config.aspect = SwapchainWindowAspect::SwapchainTaller;
1876 // Swapchain has same width, but is taller than window
1877 addFunctionCase(&**group, "swapchain_taller", scalingTest, config);
1878
1879 config.size = SwapchainWindowSize::SwapchainBigger;
1880 // Swapchain is bigger than window, and is taller in aspect ratio
1881 addFunctionCase(&**group, "swapchain_bigger_taller_aspect", scalingTest, config);
1882
1883 config.size = SwapchainWindowSize::SwapchainSmaller;
1884 // Swapchain is smaller than window, but is taller in aspect ratio
1885 addFunctionCase(&**group, "swapchain_smaller_taller_aspect", scalingTest, config);
1886
1887 config.size = SwapchainWindowSize::Identical;
1888 config.aspect = SwapchainWindowAspect::SwapchainWider;
1889 // Swapchain has same height, but is wider than window
1890 addFunctionCase(&**group, "swapchain_wider", scalingTest, config);
1891
1892 config.size = SwapchainWindowSize::SwapchainBigger;
1893 // Swapchain is bigger than window, and is wider in aspect ratio
1894 addFunctionCase(&**group, "swapchain_bigger_wider_aspect", scalingTest, config);
1895
1896 config.size = SwapchainWindowSize::SwapchainSmaller;
1897 // Swapchain is smaller than window, but is wider in aspect ratio
1898 addFunctionCase(&**group, "swapchain_smaller_wider_aspect", scalingTest, config);
1899
1900 if (isStretch)
1901 {
1902 break;
1903 }
1904
1905 scalingFlagGroup->addChild(gravityFlagsGroup.release());
1906 }
1907
1908 if (isStretch)
1909 {
1910 break;
1911 }
1912 }
1913
1914 presentModeGroup->addChild(scalingFlagGroup.release());
1915 }
1916
1917 testGroup->addChild(presentModeGroup.release());
1918 }
1919 }
1920
populateScalingGroup(tcu::TestCaseGroup * testGroup,Type wsiType)1921 void populateScalingGroup (tcu::TestCaseGroup* testGroup, Type wsiType)
1922 {
1923 populateScalingTests(testGroup, wsiType, false);
1924
1925 de::MovePtr<tcu::TestCaseGroup> resizeWindowGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), "resize_window", "Resize the window instead of creating the swapchain with a different size"));
1926 populateScalingTests(&*resizeWindowGroup, wsiType, true);
1927 testGroup->addChild(resizeWindowGroup.release());
1928 }
1929
populateDeferredAllocGroup(tcu::TestCaseGroup * testGroup,Type wsiType)1930 void populateDeferredAllocGroup (tcu::TestCaseGroup* testGroup, Type wsiType)
1931 {
1932 const struct
1933 {
1934 VkPresentModeKHR mode;
1935 const char* name;
1936 } presentModes[] =
1937 {
1938 { VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate" },
1939 { VK_PRESENT_MODE_MAILBOX_KHR, "mailbox" },
1940 { VK_PRESENT_MODE_FIFO_KHR, "fifo" },
1941 { VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed" },
1942 { VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand" },
1943 { VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous" },
1944 };
1945
1946 for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
1947 {
1948 de::MovePtr<tcu::TestCaseGroup> presentModeGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
1949
1950 PresentFenceTestConfig config;
1951 config.wsiType = wsiType;
1952 config.modes = std::vector<VkPresentModeKHR>(1, presentModes[presentModeNdx].mode);
1953 config.deferMemoryAllocation = true;
1954 config.bindImageMemory = false;
1955 config.changePresentModes = false;
1956 config.verifyFenceOrdering = false;
1957
1958 // Basic deferred allocation test
1959 addFunctionCase(&*presentModeGroup, "basic", presentFenceTest, config);
1960
1961 config.bindImageMemory = true;
1962
1963 // Bind image memory + shared present mode crashing on some drivers for unrelated reasons to VK_EXT_swapchain_maintenance1. Will enable this test separately.
1964 if (presentModes[presentModeNdx].mode != VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR && presentModes[presentModeNdx].mode != VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR)
1965 {
1966 // Bind image with VkBindImageMemorySwapchainInfoKHR
1967 addFunctionCase(&*presentModeGroup, "bind_image", presentFenceTest, config);
1968 }
1969
1970 if (canDoMultiSwapchainPresent(wsiType))
1971 {
1972 config.modes = std::vector<VkPresentModeKHR>(2, presentModes[presentModeNdx].mode);
1973
1974 // Bind image with VkBindImageMemorySwapchainInfoKHR with multiple swapchains
1975 addFunctionCase(&*presentModeGroup, "bind_image_multi_swapchain", presentFenceTest, config);
1976 }
1977
1978 testGroup->addChild(presentModeGroup.release());
1979 }
1980 }
1981
1982 enum class ResizeWindow
1983 {
1984 No,
1985 BeforeAcquire,
1986 BeforePresent,
1987 };
1988
1989 struct ReleaseImagesTestConfig
1990 {
1991 vk::wsi::Type wsiType;
1992 VkPresentModeKHR mode;
1993 VkPresentScalingFlagsEXT scaling;
1994 ResizeWindow resizeWindow;
1995 bool releaseBeforePresent;
1996 bool releaseBeforeRetire;
1997 };
1998
releaseImagesTest(Context & context,const ReleaseImagesTestConfig testParams)1999 tcu::TestStatus releaseImagesTest(Context& context, const ReleaseImagesTestConfig testParams)
2000 {
2001 const InstanceHelper instHelper (context, testParams.wsiType, false);
2002 const TestNativeObjects native (context, instHelper.supportedExtensions, testParams.wsiType, 1);
2003 Unique<VkSurfaceKHR> surface (createSurface(instHelper.vki, instHelper.instance, testParams.wsiType, *native.display, *native.windows[0], context.getTestContext().getCommandLine()));
2004
2005 const DeviceHelper devHelper (context, instHelper.vki, instHelper.instance, *surface, true, false);
2006 const DeviceInterface& vkd = devHelper.vkd;
2007 const VkDevice device = *devHelper.device;
2008
2009 std::vector<VkSurfaceFormatKHR> surfaceFormats = getPhysicalDeviceSurfaceFormats(instHelper.vki, devHelper.physicalDevice, *surface);
2010 if(surfaceFormats.empty())
2011 return tcu::TestStatus::fail("No VkSurfaceFormatKHR defined");
2012
2013 const VkSurfaceCapabilitiesKHR capabilities = getPerPresentSurfaceCapabilities(instHelper.vki, devHelper.physicalDevice, *surface, testParams.mode);
2014 const VkSurfaceTransformFlagBitsKHR transform = (capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) != 0 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : capabilities.currentTransform;
2015
2016 const std::vector<VkPresentModeKHR> presentModes = getPhysicalDeviceSurfacePresentModes(instHelper.vki, devHelper.physicalDevice, *surface);
2017 if (std::find(presentModes.begin(), presentModes.end(), testParams.mode) == presentModes.end())
2018 TCU_THROW(NotSupportedError, "Present mode not supported");
2019
2020 if (testParams.scaling != 0)
2021 {
2022 // Skip if configuration is not supported
2023 VkSurfacePresentScalingCapabilitiesEXT scaling = getSurfaceScalingCapabilities(instHelper.vki, devHelper.physicalDevice, testParams.mode, *surface);
2024
2025 if ((scaling.supportedPresentScaling & testParams.scaling) == 0)
2026 TCU_THROW(NotSupportedError, "Scaling mode is not supported");
2027 }
2028
2029 const bool isSharedPresentMode = testParams.mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR || testParams.mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR;
2030 if (isSharedPresentMode && (capabilities.minImageCount != 1 || capabilities.maxImageCount != 1))
2031 {
2032 return tcu::TestStatus::fail("min and max image count for shared present modes must be 1");
2033 }
2034
2035 deUint32 imageCount = capabilities.minImageCount + 10;
2036 if (capabilities.maxImageCount > 0)
2037 imageCount = de::min(imageCount, capabilities.maxImageCount);
2038
2039 VkSwapchainCreateInfoKHR swapchainInfo = getBasicSwapchainParameters(*surface, surfaceFormats[0], native.windowSize, testParams.mode, transform, imageCount, false);
2040
2041 VkSwapchainPresentScalingCreateInfoEXT scalingInfo =
2042 {
2043 VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT,
2044 DE_NULL,
2045 testParams.scaling,
2046 0,
2047 0,
2048 };
2049 swapchainInfo.pNext = &scalingInfo;
2050
2051 Move<VkSwapchainKHR> swapchain (createSwapchainKHR(vkd, device, &swapchainInfo));
2052 std::vector<VkImage> swapchainImages = getSwapchainImages(vkd, device, *swapchain);
2053
2054 const Unique<VkCommandPool> commandPool (createCommandPool(vkd, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, devHelper.queueFamilyIndex));
2055
2056 const deUint32 iterations = getIterations({testParams.mode}, {}, testParams.resizeWindow != ResizeWindow::No);
2057
2058 // Do testParams.iterations presents, with a fence associated with the last one.
2059 FenceSp presentFence = FenceSp(new Unique<VkFence>(createFence(vkd, device)));
2060 const std::vector<SemaphoreSp> acquireSems (createSemaphores(vkd, device, iterations));
2061 const std::vector<SemaphoreSp> presentSems (createSemaphores(vkd, device, iterations));
2062
2063 const std::vector<CommandBufferSp> commandBuffers (allocateCommandBuffers(vkd, device, *commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, iterations));
2064
2065 const deUint64 foreverNs = 0xFFFFFFFFFFFFFFFFul;
2066
2067 VkImageSubresourceRange range =
2068 {
2069 VK_IMAGE_ASPECT_COLOR_BIT,
2070 0,
2071 1,
2072 0,
2073 1,
2074 };
2075
2076 const deUint32 configHash =
2077 (deUint32)testParams.wsiType |
2078 (deUint32)testParams.mode << 4 |
2079 (deUint32)testParams.scaling << 24 |
2080 (deUint32)testParams.resizeWindow << 28 |
2081 (deUint32)testParams.releaseBeforePresent << 30 |
2082 (deUint32)testParams.releaseBeforeRetire << 31;
2083 de::Random rng (0x53A4C8A1u ^ configHash);
2084
2085 try
2086 {
2087 for (deUint32 i = 0; i < iterations; ++i)
2088 {
2089 // 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).
2090 const deUint32 maxAllowedAcquires = (deUint32)swapchainImages.size() - capabilities.minImageCount + 1;
2091 const deUint32 acquireCount = rng.getUint32() % maxAllowedAcquires + 1;
2092 const bool doPresent = i + 1 == iterations || rng.getUint32() % 10 != 0;
2093 const bool doResize = testParams.resizeWindow != ResizeWindow::No && rng.getUint32() % 10 != 0;
2094 const deUint32 presentIndex = doPresent ? rng.getUint32() % acquireCount : acquireCount;
2095
2096 // Resize the window if requested.
2097 if (doResize && testParams.resizeWindow == ResizeWindow::BeforeAcquire)
2098 {
2099 tcu::UVec2 windowSize = native.windowSize;
2100 windowSize.x() = windowSize.x() - 20 + rng.getUint32() % 41;
2101 windowSize.y() = windowSize.y() - 20 + rng.getUint32() % 41;
2102
2103 native.windows[0]->resize(windowSize);
2104 }
2105
2106 // Acquire N times
2107 const VkSemaphore presentSem = **presentSems[i];
2108 const VkSemaphore acquireSem = **acquireSems[i];
2109 std::vector<deUint32> acquiredIndices (acquireCount, 0x12345);
2110
2111 VkResult result = vkd.acquireNextImageKHR(device, *swapchain, foreverNs, presentIndex == 0 ? acquireSem : DE_NULL, DE_NULL, &acquiredIndices[0]);
2112
2113 // If out of date, recreate the swapchain and reacquire.
2114 if (result == VK_ERROR_OUT_OF_DATE_KHR)
2115 {
2116 swapchainInfo.oldSwapchain = *swapchain;
2117 Move<VkSwapchainKHR> newSwapchain (createSwapchainKHR(vkd, device, &swapchainInfo));
2118 swapchain = std::move(newSwapchain);
2119
2120 const size_t previousImageCount = swapchainImages.size();
2121 swapchainImages = getSwapchainImages(vkd, device, *swapchain);
2122 if (previousImageCount != swapchainImages.size())
2123 TCU_THROW(InternalError, "Unexpected change in number of swapchain images when recreated during window resize");
2124
2125 result = vkd.acquireNextImageKHR(device, *swapchain, foreverNs, presentIndex == 0 ? acquireSem : DE_NULL, DE_NULL, &acquiredIndices[0]);
2126 }
2127
2128 VK_CHECK_WSI(result);
2129
2130 for (deUint32 j = 1; j < acquireCount; ++j)
2131 {
2132 VK_CHECK_WSI(vkd.acquireNextImageKHR(device, *swapchain, foreverNs, presentIndex == j ? acquireSem : DE_NULL, DE_NULL, &acquiredIndices[j]));
2133 }
2134
2135 // Construct a list of image indices to be released. That is every index except the one being presented, if any.
2136 std::vector<deUint32> releaseIndices = acquiredIndices;
2137 if (doPresent)
2138 {
2139 releaseIndices.erase(releaseIndices.begin() + presentIndex);
2140 }
2141
2142 // Randomize the indices to be released.
2143 rng.shuffle(releaseIndices.begin(), releaseIndices.end());
2144
2145 if (doResize && testParams.resizeWindow == ResizeWindow::BeforePresent)
2146 {
2147 tcu::UVec2 windowSize = native.windowSize;
2148 windowSize.x() = windowSize.x() - 20 + rng.getUint32() % 41;
2149 windowSize.y() = windowSize.y() - 20 + rng.getUint32() % 41;
2150
2151 native.windows[0]->resize(windowSize);
2152 }
2153
2154 if (doPresent)
2155 {
2156 beginCommandBuffer(vkd, **commandBuffers[i], 0u);
2157
2158 VkImageMemoryBarrier barrier = {
2159 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
2160 DE_NULL,
2161 0,
2162 0,
2163 VK_IMAGE_LAYOUT_UNDEFINED,
2164 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
2165 VK_QUEUE_FAMILY_IGNORED,
2166 VK_QUEUE_FAMILY_IGNORED,
2167 swapchainImages[acquiredIndices[presentIndex]],
2168 range,
2169 };
2170
2171 VkClearColorValue clearValue;
2172 clearValue.float32[0] = static_cast<float>(i % 33) / 32.0f;
2173 clearValue.float32[1] = static_cast<float>((i + 7) % 33) / 32.0f;
2174 clearValue.float32[2] = static_cast<float>((i + 17) % 33) / 32.0f;
2175 clearValue.float32[3] = 1.0f;
2176
2177 vkd.cmdClearColorImage(**commandBuffers[i], swapchainImages[acquiredIndices[presentIndex]], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearValue, 1, &range);
2178
2179 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2180 barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2181 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2182
2183 vkd.cmdPipelineBarrier(**commandBuffers[i],
2184 VK_PIPELINE_STAGE_TRANSFER_BIT,
2185 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
2186 0u,
2187 0, DE_NULL,
2188 0, DE_NULL,
2189 1, &barrier);
2190
2191 endCommandBuffer(vkd, **commandBuffers[i]);
2192
2193 // Submit the command buffer
2194 VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2195 const VkSubmitInfo submitInfo =
2196 {
2197 VK_STRUCTURE_TYPE_SUBMIT_INFO,
2198 DE_NULL,
2199 1,
2200 &acquireSem,
2201 &waitStage,
2202 1u,
2203 &**commandBuffers[i],
2204 1u,
2205 &presentSem,
2206 };
2207 VK_CHECK(vkd.queueSubmit(devHelper.queue, 1u, &submitInfo, DE_NULL));
2208 }
2209
2210 // If asked to release before present, do so now.
2211 const VkReleaseSwapchainImagesInfoEXT releaseInfo =
2212 {
2213 VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_EXT,
2214 DE_NULL,
2215 *swapchain,
2216 (deUint32)releaseIndices.size(),
2217 releaseIndices.data(),
2218 };
2219
2220 bool imagesReleased = false;
2221 if (testParams.releaseBeforePresent)
2222 {
2223 VK_CHECK(vkd.releaseSwapchainImagesEXT(device, &releaseInfo));
2224 imagesReleased = true;
2225 }
2226
2227 // Present the frame
2228 if (doPresent)
2229 {
2230 const VkSwapchainPresentFenceInfoEXT presentFenceInfo =
2231 {
2232 VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_FENCE_INFO_EXT,
2233 DE_NULL,
2234 1,
2235 &**presentFence,
2236 };
2237
2238 const VkPresentInfoKHR presentInfo =
2239 {
2240 VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
2241 // Signal the present fence on the last present.
2242 i + 1 == iterations ? &presentFenceInfo : nullptr,
2243 1u,
2244 &presentSem,
2245 1,
2246 &*swapchain,
2247 &acquiredIndices[presentIndex],
2248 &result,
2249 };
2250 VkResult aggregateResult = vkd.queuePresentKHR(devHelper.queue, &presentInfo);
2251 if (aggregateResult == VK_ERROR_OUT_OF_DATE_KHR || result == VK_ERROR_OUT_OF_DATE_KHR)
2252 {
2253 // If OUT_OF_DATE is returned from present, recreate the swapchain and release images to the retired swapchain.
2254 if (!imagesReleased && testParams.releaseBeforeRetire)
2255 {
2256 VK_CHECK(vkd.releaseSwapchainImagesEXT(device, &releaseInfo));
2257 imagesReleased = true;
2258 }
2259
2260 swapchainInfo.oldSwapchain = *swapchain;
2261 Move<VkSwapchainKHR> newSwapchain (createSwapchainKHR(vkd, device, &swapchainInfo));
2262
2263 if (!imagesReleased && !testParams.releaseBeforeRetire)
2264 {
2265 // Release the images to the retired swapchain before deleting it (as part of move assignment below)
2266 VK_CHECK(vkd.releaseSwapchainImagesEXT(device, &releaseInfo));
2267 imagesReleased = true;
2268 }
2269
2270 // Must have released old swapchain's images before destruction
2271 DE_ASSERT(imagesReleased);
2272 swapchain = std::move(newSwapchain);
2273
2274 const size_t previousImageCount = swapchainImages.size();
2275 swapchainImages = getSwapchainImages(vkd, device, *swapchain);
2276 if (previousImageCount != swapchainImages.size())
2277 TCU_THROW(InternalError, "Unexpected change in number of swapchain images when recreated during window resize");
2278 }
2279 else
2280 {
2281 VK_CHECK_WSI(result);
2282 VK_CHECK_WSI(result);
2283 }
2284 }
2285
2286 // If asked to release after present, do it now.
2287 if (!imagesReleased)
2288 {
2289 VK_CHECK_WSI(vkd.releaseSwapchainImagesEXT(device, &releaseInfo));
2290 }
2291 }
2292
2293 // Wait for all presents before terminating the test (when semaphores are destroyed)
2294 VK_CHECK(vkd.waitForFences(device, 1u, &**presentFence, VK_TRUE, kMaxFenceWaitTimeout));
2295 }
2296 catch (...)
2297 {
2298 // Make sure device is idle before destroying resources
2299 vkd.deviceWaitIdle(device);
2300 throw;
2301 }
2302
2303 native.windows[0]->setVisible(false);
2304
2305 return tcu::TestStatus::pass("Tests ran successfully");
2306 }
2307
populateReleaseImagesGroup(tcu::TestCaseGroup * testGroup,Type wsiType)2308 void populateReleaseImagesGroup (tcu::TestCaseGroup* testGroup, Type wsiType)
2309 {
2310 const struct
2311 {
2312 VkPresentModeKHR mode;
2313 const char* name;
2314 } presentModes[] =
2315 {
2316 { VK_PRESENT_MODE_IMMEDIATE_KHR, "immediate" },
2317 { VK_PRESENT_MODE_MAILBOX_KHR, "mailbox" },
2318 { VK_PRESENT_MODE_FIFO_KHR, "fifo" },
2319 { VK_PRESENT_MODE_FIFO_RELAXED_KHR, "fifo_relaxed" },
2320 { VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "demand" },
2321 { VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "continuous" },
2322 };
2323
2324 const struct
2325 {
2326 VkPresentScalingFlagsEXT scaling;
2327 const char* name;
2328 } scalingFlags[] =
2329 {
2330 { 0, "no_scaling" },
2331 { VK_PRESENT_SCALING_STRETCH_BIT_EXT, "stretch" },
2332 };
2333
2334 for (size_t presentModeNdx = 0; presentModeNdx < DE_LENGTH_OF_ARRAY(presentModes); presentModeNdx++)
2335 {
2336 de::MovePtr<tcu::TestCaseGroup> presentModeGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), presentModes[presentModeNdx].name));
2337
2338 for (size_t scalingFlagNdx = 0; scalingFlagNdx < DE_LENGTH_OF_ARRAY(scalingFlags); scalingFlagNdx++)
2339 {
2340 de::MovePtr<tcu::TestCaseGroup> scalingFlagGroup (new tcu::TestCaseGroup(testGroup->getTestContext(), scalingFlags[scalingFlagNdx].name));
2341
2342 ReleaseImagesTestConfig config;
2343 config.wsiType = wsiType;
2344 config.mode = presentModes[presentModeNdx].mode;
2345 config.scaling = scalingFlags[scalingFlagNdx].scaling;
2346 config.resizeWindow = ResizeWindow::No;
2347 config.releaseBeforePresent = false;
2348 config.releaseBeforeRetire = false;
2349
2350 // Basic release acquired images test
2351 addFunctionCase(&*scalingFlagGroup, "basic", releaseImagesTest, config);
2352
2353 config.releaseBeforePresent = true;
2354 // Basic release acquired images test where release happens before presenting an image
2355 addFunctionCase(&*scalingFlagGroup, "release_before_present", releaseImagesTest, config);
2356
2357 config.releaseBeforePresent = false;
2358 config.resizeWindow = ResizeWindow::BeforeAcquire;
2359 // Release acquired images after a window resize before acquire
2360 addFunctionCase(&*scalingFlagGroup, "resize_window", releaseImagesTest, config);
2361
2362 config.resizeWindow = ResizeWindow::BeforePresent;
2363 // Release acquired images after a window resize after acquire
2364 addFunctionCase(&*scalingFlagGroup, "resize_window_after_acquire", releaseImagesTest, config);
2365
2366 config.releaseBeforeRetire = true;
2367 // Release acquired images after a window resize after acquire, but release the images before retiring the swapchain
2368 addFunctionCase(&*scalingFlagGroup, "resize_window_after_acquire_release_before_retire", releaseImagesTest, config);
2369
2370 presentModeGroup->addChild(scalingFlagGroup.release());
2371 }
2372
2373 testGroup->addChild(presentModeGroup.release());
2374 }
2375 }
2376
2377 } // anonymous
2378
createMaintenance1Tests(tcu::TestCaseGroup * testGroup,vk::wsi::Type wsiType)2379 void createMaintenance1Tests (tcu::TestCaseGroup* testGroup, vk::wsi::Type wsiType)
2380 {
2381 // Present fence
2382 addTestGroup(testGroup, "present_fence", populatePresentFenceGroup, wsiType);
2383 // Change present modes
2384 addTestGroup(testGroup, "present_modes", populatePresentModesGroup, wsiType);
2385 // Scaling and gravity
2386 addTestGroup(testGroup, "scaling", populateScalingGroup, wsiType);
2387 // Deferred allocation
2388 addTestGroup(testGroup, "deferred_alloc", populateDeferredAllocGroup, wsiType);
2389 // Release acquired images
2390 addTestGroup(testGroup, "release_images", populateReleaseImagesGroup, wsiType);
2391 }
2392
2393 } // wsi
2394
2395 } // vkt
2396