• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &region);
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