1/*-------------------------------------------------------------------------
2* Vulkan Conformance Tests
3* ------------------------
4*
5*
6* Copyright (c) 2019 Google Inc.
7* Copyright (c) 2019 Khronos Group
8*
9* Licensed under the Apache License, Version 2.0 (the "License");
10* you may not use this file except in compliance with the License.
11* You may obtain a copy of the License at
12*
13*      http://www.apache.org/licenses/LICENSE-2.0
14*
15* Unless required by applicable law or agreed to in writing, software
16* distributed under the License is distributed on an "AS IS" BASIS,
17* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18* See the License for the specific language governing permissions and
19* limitations under the License.
20*
21*//*!
22* \file
23* \brief API Version Check test - prints out version info
24*//*--------------------------------------------------------------------*/
25
26#include <iostream>
27#include <typeinfo>
28
29#include "tcuDefs.hpp"
30#include "tcuTestCase.hpp"
31#include "tcuTestLog.hpp"
32#include "tcuFunctionLibrary.hpp"
33#include "tcuPlatform.hpp"
34#include "tcuCommandLine.hpp"
35
36#include "vkApiVersion.hpp"
37#include "vkDefs.hpp"
38#include "vkPlatform.hpp"
39#include "vkSafetyCriticalUtil.hpp"
40
41#include "vktApiVersionCheck.hpp"
42#include "vktTestCase.hpp"
43#include "vktCustomInstancesDevices.hpp"
44
45#include "vkDeviceUtil.hpp"
46#include "vkQueryUtil.hpp"
47#include "vkRefUtil.hpp"
48
49#include "deString.h"
50#include "deStringUtil.hpp"
51
52#include <map>
53#include <vector>
54
55using namespace vk;
56using namespace std;
57
58namespace vkt
59{
60
61namespace api
62{
63
64namespace
65{
66
67#include "vkExtensionFunctions.inl"
68#include "vkCoreFunctionalities.inl"
69
70class APIVersionTestInstance : public TestInstance
71{
72public:
73								APIVersionTestInstance	(Context&				ctx)
74									: TestInstance	(ctx)
75	{}
76	virtual tcu::TestStatus		iterate					(void)
77	{
78		tcu::TestLog&			log						= m_context.getTestContext().getLog();
79		const vk::ApiVersion	maxVulkanVersion		= vk::unpackVersion(m_context.getMaximumFrameworkVulkanVersion());
80		const vk::ApiVersion	instanceVersion			= vk::unpackVersion(m_context.getAvailableInstanceVersion());
81		const ::std::string		instanceVersionString	= de::toString(instanceVersion.majorNum) + ::std::string(".") + de::toString(instanceVersion.minorNum) + ::std::string(".") + de::toString(instanceVersion.patchNum);
82		const vk::ApiVersion	deviceVersion			= vk::unpackVersion(m_context.getDeviceVersion());
83		const ::std::string		deviceVersionString		= de::toString(deviceVersion.majorNum) + ::std::string(".") + de::toString(deviceVersion.minorNum) + ::std::string(".") + de::toString(deviceVersion.patchNum);
84		const vk::ApiVersion	usedApiVersion			= vk::unpackVersion(m_context.getUsedApiVersion());
85		const ::std::string		usedApiVersionString	= de::toString(usedApiVersion.majorNum) + ::std::string(".") + de::toString(usedApiVersion.minorNum) + ::std::string(".") + de::toString(usedApiVersion.patchNum);
86
87		log << tcu::TestLog::Message << "availableInstanceVersion: " << instanceVersion << tcu::TestLog::EndMessage;
88		log << tcu::TestLog::Message << "deviceVersion: " << deviceVersion << tcu::TestLog::EndMessage;
89		log << tcu::TestLog::Message << "usedApiVersion: " << usedApiVersion << tcu::TestLog::EndMessage;
90
91		if (deviceVersion.majorNum > maxVulkanVersion.majorNum || deviceVersion.minorNum > maxVulkanVersion.minorNum)
92			return tcu::TestStatus::fail(de::toString("This version of CTS does not support Vulkan device version ") + deviceVersionString);
93		else
94			return tcu::TestStatus::pass(usedApiVersionString);
95	}
96};
97
98class APIVersionTestCase : public TestCase
99{
100public:
101							APIVersionTestCase	(tcu::TestContext&		testCtx)
102								: TestCase	(testCtx, "version")
103	{}
104
105	virtual					~APIVersionTestCase	(void)
106	{}
107	virtual TestInstance*	createInstance		(Context&				ctx) const
108	{
109		return new APIVersionTestInstance(ctx);
110	}
111
112private:
113};
114
115class APIEntryPointsTestInstance : public TestInstance
116{
117public:
118	struct APIContext
119	{
120		VkInstance				instance;
121		VkDevice				device;
122		GetInstanceProcAddrFunc	getInstanceProcAddr;
123		GetDeviceProcAddrFunc	getDeviceProcAddr;
124	};
125
126								APIEntryPointsTestInstance	(Context&				ctx)
127									: TestInstance	(ctx)
128	{
129	}
130
131	virtual tcu::TestStatus		iterate						(void)
132	{
133		tcu::TestLog&						log					= m_context.getTestContext().getLog();
134		const deUint32						instanceApiVersion	= m_context.getAvailableInstanceVersion();
135		const deUint32						deviceApiVersion	= m_context.getUsedApiVersion();
136		const vk::Platform&					platform			= m_context.getTestContext().getPlatform().getVulkanPlatform();
137#ifdef DE_PLATFORM_USE_LIBRARY_TYPE
138		de::MovePtr<vk::Library>			vkLibrary			= de::MovePtr<vk::Library>(platform.createLibrary(vk::Platform::LibraryType::LIBRARY_TYPE_VULKAN, m_context.getTestContext().getCommandLine().getVkLibraryPath()));
139#else
140		de::MovePtr<vk::Library>			vkLibrary			= de::MovePtr<vk::Library>(platform.createLibrary(m_context.getTestContext().getCommandLine().getVkLibraryPath()));
141#endif
142		const tcu::FunctionLibrary&			funcLibrary			= vkLibrary->getFunctionLibrary();
143		deUint32							failsQuantity		= 0u;
144
145		// Tests with default instance and device without extensions
146		{
147			CustomInstance			instance			= createCustomInstanceFromContext(m_context, DE_NULL, false);
148			Move<VkDevice>			device				= createTestDevice(m_context, instance, vector<string>(), false);
149			GetInstanceProcAddrFunc	getInstanceProcAddr	= reinterpret_cast<GetInstanceProcAddrFunc>(funcLibrary.getFunction("vkGetInstanceProcAddr"));
150			GetDeviceProcAddrFunc	getDeviceProcAddr	= reinterpret_cast<GetDeviceProcAddrFunc>(getInstanceProcAddr(instance, "vkGetDeviceProcAddr"));
151			APIContext				ctx					= { instance, *device, getInstanceProcAddr, getDeviceProcAddr };
152
153			// Check entry points of core functions
154			{
155				ApisMap							functions			= ApisMap();
156				initApisMap(functions);
157				ApisMap::const_iterator			lastGoodVersion		= functions.begin();
158				const ApisMap::const_iterator	versionsEnd			= functions.end();
159				for (ApisMap::const_iterator it = lastGoodVersion; it != versionsEnd; ++it)
160				{
161					if (it->first <= m_context.getUsedApiVersion())
162						lastGoodVersion = it;
163				}
164
165				log << tcu::TestLog::Message << "Regular check - tries to get core functions from proper vkGet*ProcAddr." << tcu::TestLog::EndMessage;
166				const char* const				regularResult		= regularCheck(ctx, log, failsQuantity, lastGoodVersion->second) ? "Passed" : "Failed";
167				log << tcu::TestLog::Message << regularResult << tcu::TestLog::EndMessage;
168
169				log << tcu::TestLog::Message << "Cross check - tries to get core functions from improper vkGet*ProcAddr." << tcu::TestLog::EndMessage;
170				const char* const				mixupResult			= mixupAddressProcCheck(ctx, log, failsQuantity, lastGoodVersion->second) ? "Passed" : "Failed";
171				log << tcu::TestLog::Message << mixupResult << tcu::TestLog::EndMessage;
172			}
173
174			// Check function entry points of disabled extensions
175			{
176				FunctionInfosList				extFunctions		= FunctionInfosList();
177				extFunctions.push_back(FunctionInfo("vkTrimCommandPoolKHR", FUNCTIONORIGIN_DEVICE));
178				extFunctions.push_back(FunctionInfo("vkCmdPushDescriptorSetKHR", FUNCTIONORIGIN_DEVICE));
179				extFunctions.push_back(FunctionInfo("vkCreateSamplerYcbcrConversionKHR", FUNCTIONORIGIN_DEVICE));
180				extFunctions.push_back(FunctionInfo("vkGetSwapchainStatusKHR", FUNCTIONORIGIN_DEVICE));
181				extFunctions.push_back(FunctionInfo("vkCreateSwapchainKHR", FUNCTIONORIGIN_DEVICE));
182				extFunctions.push_back(FunctionInfo("vkGetImageSparseMemoryRequirements2KHR", FUNCTIONORIGIN_DEVICE));
183				extFunctions.push_back(FunctionInfo("vkBindBufferMemory2KHR", FUNCTIONORIGIN_DEVICE));
184				extFunctions.push_back(FunctionInfo("vkImportFenceWin32HandleKHR", FUNCTIONORIGIN_DEVICE));
185				extFunctions.push_back(FunctionInfo("vkGetBufferMemoryRequirements2KHR", FUNCTIONORIGIN_DEVICE));
186				extFunctions.push_back(FunctionInfo("vkGetImageMemoryRequirements2KHR", FUNCTIONORIGIN_DEVICE));
187
188				log << tcu::TestLog::Message << "Disabled extensions check - tries to get functions of disabled extensions from proper vkGet*ProcAddr." << tcu::TestLog::EndMessage;
189				const char * const				result					= specialCasesCheck(ctx, log, failsQuantity, extFunctions) ? "Passed" : "Failed";
190				log << tcu::TestLog::Message << result << tcu::TestLog::EndMessage;
191			}
192
193			// Check special cases
194			{
195				FunctionInfosList				nonexistingFunctions	= FunctionInfosList();
196				for (deUint32 i = 0; i <= FUNCTIONORIGIN_DEVICE; ++i)
197				{
198					const FunctionOrigin origin = static_cast<FunctionOrigin>(i);
199					nonexistingFunctions.push_back(FunctionInfo("vkSomeName", origin));
200					nonexistingFunctions.push_back(FunctionInfo("vkNonexistingKHR", origin));
201					nonexistingFunctions.push_back(FunctionInfo("", origin));
202				}
203
204				log << tcu::TestLog::Message << "Special check - tries to get some nonexisting functions from various vkGet*ProcAddr." << tcu::TestLog::EndMessage;
205				const char * const				result				= specialCasesCheck(ctx, log, failsQuantity, nonexistingFunctions) ? "Passed" : "Failed";
206				log << tcu::TestLog::Message << result << tcu::TestLog::EndMessage;
207			}
208		}
209
210		// Tests with instance and device with extensions
211		{
212			CustomInstance			instance			= createCustomInstanceWithExtensions(m_context, getSupportedInstanceExtensions(instanceApiVersion), DE_NULL, false);
213			Move<VkDevice>			device				= createTestDevice(m_context, instance, getSupportedDeviceExtensions(deviceApiVersion), false);
214			GetInstanceProcAddrFunc	getInstanceProcAddr	= reinterpret_cast<GetInstanceProcAddrFunc>(funcLibrary.getFunction("vkGetInstanceProcAddr"));
215			GetDeviceProcAddrFunc	getDeviceProcAddr	= reinterpret_cast<GetDeviceProcAddrFunc>(getInstanceProcAddr(instance, "vkGetDeviceProcAddr"));
216			APIContext				ctx					= { instance, *device, getInstanceProcAddr, getDeviceProcAddr };
217
218			// Check function entry points of enabled extensions
219			{
220				vector<FunctionInfo>	extFunctions;
221
222				// Add supported instance extension functions
223				for (size_t instanceExtNdx = 0; instanceExtNdx < DE_LENGTH_OF_ARRAY(instanceExtensionNames); instanceExtNdx++)
224				{
225					vector<const char*> instanceExtFunctions;
226					vector<const char*> deviceExtFunctions;
227
228					if (isSupportedInstanceExt(instanceExtensionNames[instanceExtNdx], instanceApiVersion))
229					{
230						getInstanceExtensionFunctions(instanceApiVersion, instanceExtensionNames[instanceExtNdx], instanceExtFunctions);
231					}
232					if (isSupportedInstanceExt(instanceExtensionNames[instanceExtNdx], deviceApiVersion))
233					{
234						getDeviceExtensionFunctions(deviceApiVersion, instanceExtensionNames[instanceExtNdx], deviceExtFunctions);
235					}
236
237					for (size_t instanceFuncNdx = 0; instanceFuncNdx < instanceExtFunctions.size(); instanceFuncNdx++)
238						extFunctions.push_back(FunctionInfo(instanceExtFunctions[instanceFuncNdx], FUNCTIONORIGIN_INSTANCE));
239
240					for (size_t deviceFuncNdx = 0; deviceFuncNdx < deviceExtFunctions.size(); deviceFuncNdx++)
241						extFunctions.push_back(FunctionInfo(deviceExtFunctions[deviceFuncNdx], FUNCTIONORIGIN_DEVICE));
242				}
243
244				// Add supported device extension functions
245				for (size_t deviceExtNdx = 0; deviceExtNdx < DE_LENGTH_OF_ARRAY(deviceExtensionNames); deviceExtNdx++)
246				{
247					vector<const char*> deviceExtFunctions;
248
249					if (isSupportedDeviceExt(deviceExtensionNames[deviceExtNdx], deviceApiVersion))
250						getDeviceExtensionFunctions(deviceApiVersion, deviceExtensionNames[deviceExtNdx], deviceExtFunctions);
251
252					for (size_t deviceFuncNdx = 0; deviceFuncNdx < deviceExtFunctions.size(); deviceFuncNdx++)
253						extFunctions.push_back(FunctionInfo(deviceExtFunctions[deviceFuncNdx], FUNCTIONORIGIN_DEVICE));
254				}
255
256				log << tcu::TestLog::Message << "Enabled extensions check - tries to get functions of supported extensions from proper vkGet*ProcAddr." << tcu::TestLog::EndMessage;
257				const char * const		result = regularCheck(ctx, log, failsQuantity, extFunctions) ? "Passed" : "Failed";
258				log << tcu::TestLog::Message << result << tcu::TestLog::EndMessage;
259			}
260		}
261
262		if (failsQuantity > 0u)
263			return tcu::TestStatus::fail("Fail");
264		else
265			return tcu::TestStatus::pass("Pass");
266	}
267
268private:
269
270	deUint32 findQueueFamilyIndex(const InstanceInterface& vkInstance, VkPhysicalDevice physicalDevice, VkQueueFlags requiredCaps)
271	{
272		deUint32								numQueues = 0;
273		vkInstance.getPhysicalDeviceQueueFamilyProperties(physicalDevice, &numQueues, DE_NULL);
274		if (numQueues > 0)
275		{
276			vector<VkQueueFamilyProperties>		properties(numQueues);
277			vkInstance.getPhysicalDeviceQueueFamilyProperties(physicalDevice, &numQueues, &properties[0]);
278			if (numQueues != static_cast<deUint32>(properties.size()))
279				TCU_FAIL("Returned queue family count changes between queries.");
280			for (deUint32 queueNdx = 0u; queueNdx < numQueues; queueNdx++)
281				if ((properties[queueNdx].queueFlags & requiredCaps) == requiredCaps)
282					return queueNdx;
283		}
284		TCU_FAIL("Returned queue family count was 0.");
285		return 0u;
286	}
287
288	vector<string> filterMultiAuthorExtensions (vector<VkExtensionProperties> extProperties)
289	{
290		vector<string>	multiAuthorExtensions;
291		const char*		extensionGroups[] =
292		{
293			"VK_KHR_",
294			"VK_EXT_"
295		};
296
297		for (size_t extNdx = 0; extNdx < extProperties.size(); extNdx++)
298		{
299			for (int extGroupNdx = 0; extGroupNdx < DE_LENGTH_OF_ARRAY(extensionGroups); extGroupNdx++)
300			{
301				if (deStringBeginsWith(extProperties[extNdx].extensionName, extensionGroups[extGroupNdx]))
302					multiAuthorExtensions.push_back(extProperties[extNdx].extensionName);
303			}
304		}
305
306		return multiAuthorExtensions;
307	}
308
309	vector<string> getSupportedInstanceExtensions (const deUint32 apiVersion)
310	{
311		vector<VkExtensionProperties>	enumeratedExtensions (enumerateInstanceExtensionProperties(m_context.getPlatformInterface(), DE_NULL));
312		vector<VkExtensionProperties>	supportedExtensions;
313
314		for (size_t extNdx = 0; extNdx < enumeratedExtensions.size(); extNdx++)
315		{
316			if (!isCoreInstanceExtension(apiVersion, enumeratedExtensions[extNdx].extensionName))
317				supportedExtensions.push_back(enumeratedExtensions[extNdx]);
318		}
319
320		return filterMultiAuthorExtensions(supportedExtensions);
321	}
322
323	vector<string> getSupportedDeviceExtensions (const deUint32 apiVersion)
324	{
325		vector<VkExtensionProperties>	enumeratedExtensions (enumerateDeviceExtensionProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), DE_NULL));
326		vector<VkExtensionProperties>	supportedExtensions;
327
328		for (size_t extNdx = 0; extNdx < enumeratedExtensions.size(); extNdx++)
329		{
330			if (!isCoreDeviceExtension(apiVersion, enumeratedExtensions[extNdx].extensionName))
331				supportedExtensions.push_back(enumeratedExtensions[extNdx]);
332		}
333
334		return filterMultiAuthorExtensions(supportedExtensions);
335	}
336
337	Move<VkDevice> createTestDevice (const Context& context, VkInstance instance, vector<string> extensions = vector<string>(), bool allowLayers = true)
338	{
339		auto&						cmdLine			= context.getTestContext().getCommandLine();
340		const PlatformInterface&	vkp				= context.getPlatformInterface();
341		const InstanceInterface&	vki				= context.getInstanceInterface();
342		VkPhysicalDevice			physicalDevice	= chooseDevice(context.getInstanceInterface(), instance, cmdLine);
343		vector<const char*>			extensionPtrs;
344		const float					queuePriority	= 1.0f;
345		const deUint32				queueIndex		= findQueueFamilyIndex(vki, physicalDevice, VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);
346
347		for (size_t i = 0; i < extensions.size(); i++)
348			extensionPtrs.push_back(extensions[i].c_str());
349
350		VkDeviceQueueCreateInfo		queueInfo		= {
351			VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
352			DE_NULL,
353			static_cast<VkDeviceQueueCreateFlags>(0u),
354			queueIndex,
355			1u,
356			&queuePriority
357		};
358
359		void* pNext									= DE_NULL;
360#ifdef CTS_USES_VULKANSC
361		VkDeviceObjectReservationCreateInfo memReservationInfo	= context.getTestContext().getCommandLine().isSubProcess() ? context.getResourceInterface()->getStatMax() : resetDeviceObjectReservationCreateInfo();
362		memReservationInfo.pNext								= pNext;
363		pNext													= &memReservationInfo;
364
365		VkPhysicalDeviceVulkanSC10Features sc10Features			= createDefaultSC10Features();
366		sc10Features.pNext										= pNext;
367		pNext													= &sc10Features;
368
369		VkPipelineCacheCreateInfo			pcCI;
370		std::vector<VkPipelinePoolSize>		poolSizes;
371		if (context.getTestContext().getCommandLine().isSubProcess())
372		{
373			if (context.getResourceInterface()->getCacheDataSize() > 0)
374			{
375				pcCI =
376				{
377					VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,		// VkStructureType				sType;
378					DE_NULL,											// const void*					pNext;
379					VK_PIPELINE_CACHE_CREATE_READ_ONLY_BIT |
380						VK_PIPELINE_CACHE_CREATE_USE_APPLICATION_STORAGE_BIT,	// VkPipelineCacheCreateFlags	flags;
381					context.getResourceInterface()->getCacheDataSize(),	// deUintptr					initialDataSize;
382					context.getResourceInterface()->getCacheData()		// const void*					pInitialData;
383				};
384				memReservationInfo.pipelineCacheCreateInfoCount		= 1;
385				memReservationInfo.pPipelineCacheCreateInfos		= &pcCI;
386			}
387
388			poolSizes							= context.getResourceInterface()->getPipelinePoolSizes();
389			if (!poolSizes.empty())
390			{
391				memReservationInfo.pipelinePoolSizeCount		= deUint32(poolSizes.size());
392				memReservationInfo.pPipelinePoolSizes			= poolSizes.data();
393			}
394		}
395#endif // CTS_USES_VULKANSC
396
397		const VkDeviceCreateInfo	deviceInfo		= {
398			VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
399			pNext,
400			static_cast<VkDeviceCreateFlags>(0u),
401			1u,
402			&queueInfo,
403			0u,
404			DE_NULL,
405			(deUint32)extensions.size(),
406			extensions.size() ? &extensionPtrs[0] : DE_NULL,
407			DE_NULL,
408		};
409
410		const bool					validationEnabled = (cmdLine.isValidationEnabled() && allowLayers);
411		return createCustomDevice(validationEnabled, vkp, instance, vki, physicalDevice, &deviceInfo);
412	}
413
414	void reportFail (tcu::TestLog& log, const char* const functionName, const char* const firstParamName, const char* const secondParamName, deBool shouldBeNonNull, deUint32& failsQuantity)
415	{
416		log << tcu::TestLog::Message
417			<< "[" << failsQuantity << "] " << functionName << '(' << firstParamName << ", \"" << secondParamName << "\") "
418			<< "returned " << (shouldBeNonNull ? "nullptr" : "non-null") << ". Should return " << (shouldBeNonNull ? "valid function address." : "nullptr.")
419			<< tcu::TestLog::EndMessage;
420		++failsQuantity;
421	}
422
423	void checkPlatformFunction (const APIContext& ctx, tcu::TestLog& log, const char* const name, deBool shouldBeNonNull, deUint32& failsQuantity)
424	{
425		if ((ctx.getInstanceProcAddr(DE_NULL, name) == DE_NULL) == shouldBeNonNull)
426			reportFail(log, "vkGetInstanceProcAddr", "DE_NULL", name, shouldBeNonNull, failsQuantity);
427	}
428
429	void checkInstanceFunction (const APIContext& ctx, tcu::TestLog& log, const char* const name, deBool shouldBeNonNull, deUint32& failsQuantity)
430	{
431		if ((ctx.getInstanceProcAddr(ctx.instance, name) == DE_NULL) == shouldBeNonNull)
432			reportFail(log, "vkGetInstanceProcAddr", "instance", name, shouldBeNonNull, failsQuantity);
433	}
434
435	void checkDeviceFunction (const APIContext& ctx, tcu::TestLog& log, const char* const name, deBool shouldBeNonNull, deUint32& failsQuantity)
436	{
437		if ((ctx.getDeviceProcAddr(ctx.device, name) == DE_NULL) == shouldBeNonNull)
438			reportFail(log, "vkGetDeviceProcAddr", "device", name, shouldBeNonNull, failsQuantity);
439	}
440
441	deBool isSupportedInstanceExt (const string extName, const deUint32 apiVersion)
442	{
443		const vector<string> supportedInstanceExtensions (getSupportedInstanceExtensions(apiVersion));
444
445		return de::contains(supportedInstanceExtensions.begin(), supportedInstanceExtensions.end(), extName);
446	}
447
448	deBool isSupportedDeviceExt (const string extName, const deUint32 apiVersion)
449	{
450		const vector<string> supportedDeviceExtensions (getSupportedDeviceExtensions(apiVersion));
451
452		return de::contains(supportedDeviceExtensions.begin(), supportedDeviceExtensions.end(), extName);
453	}
454
455	deBool mixupAddressProcCheck (const APIContext& ctx, tcu::TestLog& log, deUint32& failsQuantity, const vector<pair<const char*, FunctionOrigin> >& testsArr)
456	{
457		const deUint32 startingQuantity = failsQuantity;
458		for (deUint32 ndx = 0u; ndx < testsArr.size(); ++ndx)
459		{
460			if (deStringEqual(testsArr[ndx].first, "vkGetInstanceProcAddr") || deStringEqual(testsArr[ndx].first, "vkEnumerateInstanceVersion"))
461				continue;
462
463			const char*	   functionName = testsArr[ndx].first;
464			const deUint32 functionType = testsArr[ndx].second;
465			if (functionType == FUNCTIONORIGIN_INSTANCE)
466			{
467				checkPlatformFunction(ctx, log, functionName, DE_FALSE, failsQuantity);
468				checkDeviceFunction(ctx, log, functionName, DE_FALSE, failsQuantity);
469			}
470			else if (functionType == FUNCTIONORIGIN_DEVICE)
471				checkPlatformFunction(ctx, log, functionName, DE_FALSE, failsQuantity);
472		}
473		return startingQuantity == failsQuantity;
474	}
475
476	deBool specialCasesCheck (const APIContext& ctx, tcu::TestLog& log, deUint32& failsQuantity, const vector<pair<const char*, FunctionOrigin> >& testsArr)
477	{
478		const deUint32 startingQuantity = failsQuantity;
479		for (deUint32 ndx = 0u; ndx < testsArr.size(); ++ndx)
480		{
481			const deUint32 functionType = testsArr[ndx].second;
482			if (functionType == FUNCTIONORIGIN_PLATFORM)
483				checkPlatformFunction(ctx, log, testsArr[ndx].first, DE_FALSE, failsQuantity);
484			else if (functionType == FUNCTIONORIGIN_INSTANCE)
485				checkInstanceFunction(ctx, log, testsArr[ndx].first, DE_FALSE, failsQuantity);
486			else if (functionType == FUNCTIONORIGIN_DEVICE)
487				checkDeviceFunction(ctx, log, testsArr[ndx].first, DE_FALSE, failsQuantity);
488		}
489		return startingQuantity == failsQuantity;
490	}
491
492	deBool regularCheck (const APIContext& ctx, tcu::TestLog& log, deUint32& failsQuantity, const vector<pair<const char*, FunctionOrigin> >& testsArr)
493	{
494		const deUint32 startingQuantity = failsQuantity;
495
496		for (deUint32 ndx = 0u; ndx < testsArr.size(); ++ndx)
497		{
498			const auto&	funcName	= testsArr[ndx].first;
499			const auto&	funcType	= testsArr[ndx].second;
500			const auto	apiVersion	= m_context.getUsedApiVersion();
501
502			if (deStringEqual(funcName, "vkGetInstanceProcAddr") && apiVersion < VK_API_VERSION_1_2)
503				continue;
504
505			// VK_KHR_draw_indirect_count was promoted to core in Vulkan 1.2, but these entrypoints are not mandatory unless the
506			// device supports the extension. In that case, the drawIndirectCount feature bit will also be true. Any of the two
507			// checks is valid. We use the extension name for convenience here.
508			if ((deStringEqual(funcName, "vkCmdDrawIndirectCount") || deStringEqual(funcName, "vkCmdDrawIndexedIndirectCount"))
509				&& !isSupportedDeviceExt("VK_KHR_draw_indirect_count", apiVersion))
510				continue;
511
512			// vkCmdPushDescriptorSetWithTemplateKHR is available if:
513			// - VK_KHR_push_descriptor is supported AND
514			//   - API >= VK_VERSION_1_1 OR
515			//   - VK_KHR_descriptor_update_template is supported
516			if (deStringEqual(funcName, "vkCmdPushDescriptorSetWithTemplateKHR") &&
517			    (!isSupportedDeviceExt("VK_KHR_push_descriptor", apiVersion) ||
518			      (apiVersion < VK_API_VERSION_1_1 && !isSupportedDeviceExt("VK_KHR_descriptor_update_template", apiVersion))))
519				continue;
520
521			if (funcType == FUNCTIONORIGIN_PLATFORM)
522			{
523				checkPlatformFunction(ctx, log, funcName, DE_TRUE, failsQuantity);
524			}
525			else if (funcType == FUNCTIONORIGIN_INSTANCE)
526			{
527				checkInstanceFunction(ctx, log, funcName, DE_TRUE, failsQuantity);
528				checkDeviceFunction(ctx, log, funcName, DE_FALSE, failsQuantity);
529			}
530			else if (funcType == FUNCTIONORIGIN_DEVICE)
531			{
532				checkInstanceFunction(ctx, log, funcName, DE_TRUE, failsQuantity);
533				checkDeviceFunction(ctx, log, funcName, DE_TRUE, failsQuantity);
534			}
535		}
536
537		return startingQuantity == failsQuantity;
538	}
539};
540
541class APIEntryPointsTestCase : public TestCase
542{
543public:
544							APIEntryPointsTestCase			(tcu::TestContext&		testCtx)
545								: TestCase	(testCtx, "entry_points")
546	{}
547
548	virtual					~APIEntryPointsTestCase			(void)
549	{}
550	virtual TestInstance*	createInstance					(Context&				ctx) const
551	{
552		return new APIEntryPointsTestInstance(ctx);
553	}
554
555private:
556};
557
558class APIUnavailableEntryPointsTestInstance : public TestInstance
559{
560public:
561	APIUnavailableEntryPointsTestInstance(Context& ctx): TestInstance(ctx)
562	{}
563
564	virtual tcu::TestStatus iterate(void)
565	{
566		const vk::PlatformInterface&	vkp					= m_context.getPlatformInterface();
567		tcu::TestLog&					log					= m_context.getTestContext().getLog();
568		const auto						supportedApiVersion	= m_context.getUsedApiVersion();
569		bool							testPassed			= true;
570
571		ApisMap functionsPerVersion;
572		initApisMap(functionsPerVersion);
573
574		// create custom instance for each api version
575		for (const auto& testedApiVersion : functionsPerVersion)
576		{
577			// we cant test api versions that are higher then api version support by this device
578			if (testedApiVersion.first > supportedApiVersion)
579				break;
580
581			// there is no api version above the last api version
582			if (testedApiVersion.first == functionsPerVersion.rbegin()->first)
583				break;
584
585			VkApplicationInfo appInfo	= initVulkanStructure();
586			appInfo.pApplicationName	= "a";
587			appInfo.pEngineName			= "b";
588			appInfo.apiVersion			= testedApiVersion.first;
589			VkInstanceCreateInfo instanceCreateInfo	= initVulkanStructure();
590			instanceCreateInfo.pApplicationInfo		= &appInfo;
591
592#ifndef CTS_USES_VULKANSC
593			char const * requiredExtensionForVk10 = "VK_KHR_get_physical_device_properties2";
594			if (appInfo.apiVersion == VK_API_VERSION_1_0)
595			{
596				instanceCreateInfo.enabledExtensionCount = 1U;
597				instanceCreateInfo.ppEnabledExtensionNames = &requiredExtensionForVk10;
598			}
599#endif // CTS_USES_VULKANSC
600
601			// create instance for currentluy tested vulkan version
602			Move<VkInstance>					customInstance			(vk::createInstance(vkp, &instanceCreateInfo, DE_NULL));
603			std::unique_ptr<vk::InstanceDriver>	instanceDriver			(new InstanceDriver(vkp, *customInstance));
604			const VkPhysicalDevice				physicalDevice			= chooseDevice(*instanceDriver, *customInstance, m_context.getTestContext().getCommandLine());
605			const auto							queueFamilyProperties	= getPhysicalDeviceQueueFamilyProperties(*instanceDriver, physicalDevice);
606
607			const float queuePriority = 1.0f;
608			VkDeviceQueueCreateInfo deviceQueueCreateInfo	= initVulkanStructure();
609			deviceQueueCreateInfo.queueCount				= 1;
610			deviceQueueCreateInfo.pQueuePriorities			= &queuePriority;
611
612			VkDeviceCreateInfo deviceCreateInfo				= initVulkanStructure();
613			deviceCreateInfo.queueCreateInfoCount			= 1u;
614			deviceCreateInfo.pQueueCreateInfos				= &deviceQueueCreateInfo;
615
616#ifndef CTS_USES_VULKANSC
617			char const * extensionName						= "VK_KHR_maintenance5";
618			deviceCreateInfo.enabledExtensionCount			= 1u;
619			deviceCreateInfo.ppEnabledExtensionNames		= &extensionName;
620
621			vk::VkPhysicalDeviceMaintenance5FeaturesKHR maint5 = initVulkanStructure();
622			vk::VkPhysicalDeviceFeatures2 features2			= initVulkanStructure(&maint5);
623			instanceDriver->getPhysicalDeviceFeatures2(physicalDevice, &features2);
624			deviceCreateInfo.pNext							= &features2;
625#endif // CTS_USES_VULKANSC
626
627			// create custom device
628			const Unique<VkDevice>	device			(createCustomDevice(false, vkp, *customInstance, *instanceDriver, physicalDevice, &deviceCreateInfo));
629			const DeviceDriver		deviceDriver	(vkp, *customInstance, *device, supportedApiVersion);
630
631			log << tcu::TestLog::Message << "Checking apiVersion("
632				<< VK_API_VERSION_MAJOR(testedApiVersion.first) << ", "
633				<< VK_API_VERSION_MINOR(testedApiVersion.first) << ")" << tcu::TestLog::EndMessage;
634
635			// iterate over api versions that are above tested api version
636			auto& previousVersionFunctions = functionsPerVersion[VK_API_VERSION_1_0];
637			for (const auto& versionFunctions : functionsPerVersion)
638			{
639				// skip api versions that are not above tested api version
640				if (versionFunctions.first <= testedApiVersion.first)
641				{
642					previousVersionFunctions = versionFunctions.second;
643					continue;
644				}
645
646				// iterate over all functions
647				for (const auto& function : versionFunctions.second)
648				{
649					// we are interested only in device functions
650					if (function.second != FUNCTIONORIGIN_DEVICE)
651						continue;
652
653					// skip functions that are present in previous version;
654					// functionsPerVersion contains all functions that are
655					// available in vulkan version, not only ones that were added
656					const auto&	funcName	= function.first;
657					const auto	isMatch		= [&funcName](const FunctionInfo& fi) { return !strcmp(funcName, fi.first); };
658					auto matchIt = std::find_if(begin(previousVersionFunctions), end(previousVersionFunctions), isMatch);
659					if (matchIt != previousVersionFunctions.end())
660						continue;
661
662					// check if returned function pointer is NULL
663					if (deviceDriver.getDeviceProcAddr(*device, funcName) != DE_NULL)
664					{
665						log << tcu::TestLog::Message << "getDeviceProcAddr(" << funcName
666							<< ") returned non-null pointer, expected NULL" << tcu::TestLog::EndMessage;
667						testPassed = false;
668					}
669				}
670
671				previousVersionFunctions = versionFunctions.second;
672			}
673		}
674
675		if (testPassed)
676			return tcu::TestStatus::pass("Pass");
677		return tcu::TestStatus::fail("Fail");
678	}
679};
680
681class APIUnavailableEntryPointsTestCase : public TestCase
682{
683public:
684	// Check if vkGetDeviceProcAddr returns NULL for functions beyond app version.
685	APIUnavailableEntryPointsTestCase(tcu::TestContext& testCtx)
686		: TestCase(testCtx, "unavailable_entry_points")
687	{}
688
689	virtual void			checkSupport(Context& context) const
690	{
691		context.requireDeviceFunctionality("VK_KHR_maintenance5");
692	}
693
694	virtual TestInstance*	createInstance(Context& ctx) const
695	{
696		return new APIUnavailableEntryPointsTestInstance(ctx);
697	}
698};
699
700} // anonymous
701
702tcu::TestCaseGroup*			createVersionSanityCheckTests	(tcu::TestContext & testCtx)
703{
704	de::MovePtr<tcu::TestCaseGroup>	versionTests	(new tcu::TestCaseGroup(testCtx, "version_check"));
705	versionTests->addChild(new APIVersionTestCase(testCtx));
706	versionTests->addChild(new APIEntryPointsTestCase(testCtx));
707
708#ifndef CTS_USES_VULKANSC
709	versionTests->addChild(new APIUnavailableEntryPointsTestCase(testCtx));
710#endif
711
712	return versionTests.release();
713}
714
715} // api
716
717} // vkt
718