text: /*-------------------------------------------------------------------------
   * Vulkan Conformance Tests
   * ------------------------
   *
   * Copyright (c) 2019 The Khronos Group Inc.
   * Copyright (c) 2019 Valve Corporation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   *//*!
   * \file
   * \brief Auxiliar functions to help create custom devices and instances.
   *//*--------------------------------------------------------------------*/
  
  #include "vkRefUtil.hpp"
  #include "vkQueryUtil.hpp"
  #include "vkDeviceUtil.hpp"
  #include "tcuCommandLine.hpp"
  #include "vktCustomInstancesDevices.hpp"
  
  #include <algorithm>
  
  using std::vector;
  using std::string;
  using vk::Move;
  using vk::VkInstance;
  using vk::InstanceDriver;
  using vk::DebugReportRecorder;
  
  namespace vkt
  {
  
  namespace
  {
  
  vector<const char*> getValidationLayers (const vector<vk::VkLayerProperties>& supportedLayers)
  {
  	static const char*	s_magicLayer		= "VK_LAYER_KHRONOS_validation";
  	static const char*	s_defaultLayers[]	=
  	{
  		"VK_LAYER_LUNARG_standard_validation",		// Deprecated by at least Vulkan SDK 1.1.121.
  		"VK_LAYER_GOOGLE_threading",				// Deprecated by at least Vulkan SDK 1.1.121.
  		"VK_LAYER_LUNARG_parameter_validation",		// Deprecated by at least Vulkan SDK 1.1.121.
  		"VK_LAYER_LUNARG_device_limits",
  		"VK_LAYER_LUNARG_object_tracker",			// Deprecated by at least Vulkan SDK 1.1.121.
  		"VK_LAYER_LUNARG_image",
  		"VK_LAYER_LUNARG_core_validation",			// Deprecated by at least Vulkan SDK 1.1.121.
  		"VK_LAYER_LUNARG_swapchain",
  		"VK_LAYER_GOOGLE_unique_objects"			// Deprecated by at least Vulkan SDK 1.1.121.
  	};
  
  	vector<const char*>	enabledLayers;
  
  	if (vk::isLayerSupported(supportedLayers, vk::RequiredLayer(s_magicLayer)))
  		enabledLayers.push_back(s_magicLayer);
  	else
  	{
  		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_defaultLayers); ++ndx)
  		{
  			if (isLayerSupported(supportedLayers, vk::RequiredLayer(s_defaultLayers[ndx])))
  				enabledLayers.push_back(s_defaultLayers[ndx]);
  		}
  	}
  
  	return enabledLayers;
  }
  
  } // anonymous
  
  
  vector<const char*> getValidationLayers (const vk::PlatformInterface& vkp)
  {
  	return getValidationLayers(enumerateInstanceLayerProperties(vkp));
  }
  
  vector<const char*> getValidationLayers (const vk::InstanceInterface& vki, vk::VkPhysicalDevice physicalDevice)
  {
  	return getValidationLayers(enumerateDeviceLayerProperties(vki, physicalDevice));
  }
  
  CustomInstance::CustomInstance (Context& context, Move<VkInstance> instance, bool enableDebugReportRecorder, bool printValidationErrors)
  	: m_context		(&context)
  	, m_instance	(instance)
  	, m_driver		(new InstanceDriver(context.getPlatformInterface(), *m_instance))
  	, m_recorder	(enableDebugReportRecorder ? (new DebugReportRecorder(*m_driver, *m_instance, printValidationErrors)) : nullptr)
  {
  }
  
  CustomInstance::CustomInstance ()
  	: m_context		(nullptr)
  	, m_instance	()
  	, m_driver		(nullptr)
  	, m_recorder	(nullptr)
  {
  }
  
  CustomInstance::CustomInstance (CustomInstance&& other)
  	: CustomInstance()
  {
  	this->swap(other);
  }
  
  CustomInstance::~CustomInstance ()
  {
  	collectMessages();
  }
  
  CustomInstance&	CustomInstance::operator= (CustomInstance&& other)
  {
  	CustomInstance destroyer;
  	destroyer.swap(other);
  	this->swap(destroyer);
  	return *this;
  }
  
  void CustomInstance::swap (CustomInstance& other)
  {
  	std::swap(m_context, other.m_context);
  	Move<VkInstance> aux = m_instance; m_instance = other.m_instance; other.m_instance = aux;
  	m_driver.swap(other.m_driver);
  	m_recorder.swap(other.m_recorder);
  }
  
  CustomInstance::operator VkInstance () const
  {
  	return *m_instance;
  }
  
  const vk::InstanceDriver& CustomInstance::getDriver() const
  {
  	return *m_driver;
  }
  
  void CustomInstance::collectMessages ()
  {
  	if (m_recorder)
  		collectAndReportDebugMessages(*m_recorder, *m_context);
  }
  
  UncheckedInstance::UncheckedInstance ()
  	: m_context		(nullptr)
  	, m_allocator	(nullptr)
  	, m_instance	(DE_NULL)
  	, m_driver		(nullptr)
  	, m_recorder	(nullptr)
  {
  }
  
  UncheckedInstance::UncheckedInstance (Context& context, vk::VkInstance instance, const vk::VkAllocationCallbacks* pAllocator, bool enableDebugReportRecorder, bool printValidationErrors)
  	: m_context		(&context)
  	, m_allocator	(pAllocator)
  	, m_instance	(instance)
  	, m_driver		((m_instance != DE_NULL) ? new InstanceDriver(context.getPlatformInterface(), m_instance) : nullptr)
  	, m_recorder	((enableDebugReportRecorder && m_instance != DE_NULL) ? (new DebugReportRecorder(*m_driver, m_instance, printValidationErrors)) : nullptr)
  {
  }
  
  UncheckedInstance::~UncheckedInstance ()
  {
  	if (m_recorder)
  		collectAndReportDebugMessages(*m_recorder, *m_context);
  
  	if (m_instance != DE_NULL)
  	{
  		m_recorder.reset(nullptr);
  		m_driver->destroyInstance(m_instance, m_allocator);
  	}
  }
  
  void UncheckedInstance::swap (UncheckedInstance& other)
  {
  	std::swap(m_context, other.m_context);
  	std::swap(m_allocator, other.m_allocator);
  	vk::VkInstance aux = m_instance; m_instance = other.m_instance; other.m_instance = aux;
  	m_driver.swap(other.m_driver);
  	m_recorder.swap(other.m_recorder);
  }
  
  UncheckedInstance::UncheckedInstance (UncheckedInstance&& other)
  	: UncheckedInstance()
  {
  	this->swap(other);
  }
  
  UncheckedInstance& UncheckedInstance::operator= (UncheckedInstance&& other)
  {
  	UncheckedInstance destroyer;
  	destroyer.swap(other);
  	this->swap(destroyer);
  	return *this;
  }
  
  UncheckedInstance::operator vk::VkInstance () const
  {
  	return m_instance;
  }
  UncheckedInstance::operator bool () const
  {
  	return (m_instance != DE_NULL);
  }
  
  CustomInstance createCustomInstanceWithExtensions (Context& context, const std::vector<std::string>& extensions, const vk::VkAllocationCallbacks* pAllocator, bool allowLayers)
  {
  	vector<const char*>	enabledLayers;
  	vector<string>		enabledLayersStr;
  	const auto&			cmdLine					= context.getTestContext().getCommandLine();
  	const bool			validationEnabled		= (cmdLine.isValidationEnabled() && allowLayers);
  	const bool			printValidationErrors	= cmdLine.printValidationErrors();
  
  	if (validationEnabled)
  	{
  		enabledLayers = getValidationLayers(context.getPlatformInterface());
  		enabledLayersStr = vector<string>(begin(enabledLayers), end(enabledLayers));
  	}
  
  	// Filter extension list and throw NotSupported if a required extension is not supported.
  	const deUint32									apiVersion			= context.getUsedApiVersion();
  	const vk::PlatformInterface&					vkp					= context.getPlatformInterface();
  	const std::vector<vk::VkExtensionProperties>	availableExtensions	= vk::enumerateInstanceExtensionProperties(vkp, DE_NULL);
  	vector<string>									extensionPtrs;
  
  	vector<string> availableExtensionNames;
  	for (const auto& ext : availableExtensions)
  		availableExtensionNames.push_back(ext.extensionName);
  
  	for (const auto& ext : extensions)
  	{
  		if (!vk::isInstanceExtensionSupported(apiVersion, availableExtensionNames, ext))
  			TCU_THROW(NotSupportedError, ext + " is not supported");
  
  		if (!vk::isCoreInstanceExtension(apiVersion, ext))
  			extensionPtrs.push_back(ext);
  	}
  
  	Move<VkInstance> instance = vk::createDefaultInstance(vkp, apiVersion, enabledLayersStr, extensionPtrs, pAllocator);
  	return CustomInstance(context, instance, validationEnabled, printValidationErrors);
  }
  
  CustomInstance createCustomInstanceWithExtension (Context& context, const std::string& extension, const vk::VkAllocationCallbacks* pAllocator, bool allowLayers)
  {
  	return createCustomInstanceWithExtensions(context, std::vector<std::string>(1, extension), pAllocator, allowLayers);
  }
  
  CustomInstance createCustomInstanceFromContext (Context& context, const vk::VkAllocationCallbacks* pAllocator, bool allowLayers)
  {
  	return createCustomInstanceWithExtensions(context, std::vector<std::string>(), pAllocator, allowLayers);
  }
  
  const char kDebugReportExt[] = "VK_EXT_debug_report";
  
  vector<const char*> addDebugReportExt(const vk::PlatformInterface& vkp, const vk::VkInstanceCreateInfo& createInfo)
  {
  	if (!isDebugReportSupported(vkp))
  		TCU_THROW(NotSupportedError, "VK_EXT_debug_report is not supported");
  
  	vector<const char*> actualExtensions;
  	if (createInfo.enabledExtensionCount != 0u)
  	{
  		for (deUint32 i = 0u; i < createInfo.enabledExtensionCount; ++i)
  			actualExtensions.push_back(createInfo.ppEnabledExtensionNames[i]);
  	}
  
  	if (std::find_if(begin(actualExtensions), end(actualExtensions), [](const char* name) { return (strcmp(name, kDebugReportExt) == 0); })
  		== end(actualExtensions))
  	{
  		actualExtensions.push_back(kDebugReportExt);
  	}
  
  	return actualExtensions;
  }
  
  CustomInstance createCustomInstanceFromInfo (Context& context, const vk::VkInstanceCreateInfo* instanceCreateInfo, const vk::VkAllocationCallbacks* pAllocator, bool allowLayers)
  {
  	vector<const char*>				enabledLayers;
  	vector<const char*>				enabledExtensions;
  	vk::VkInstanceCreateInfo		createInfo				= *instanceCreateInfo;
  	const auto&						cmdLine					= context.getTestContext().getCommandLine();
  	const bool						validationEnabled		= cmdLine.isValidationEnabled();
  	const bool						printValidationErrors	= cmdLine.printValidationErrors();
  	const vk::PlatformInterface&	vkp						= context.getPlatformInterface();
  
  	if (validationEnabled && allowLayers)
  	{
  		// Activate some layers if requested.
  		if (createInfo.enabledLayerCount == 0u)
  		{
  			enabledLayers = getValidationLayers(vkp);
  			createInfo.enabledLayerCount = static_cast<deUint32>(enabledLayers.size());
  			createInfo.ppEnabledLayerNames = (enabledLayers.empty() ? DE_NULL : enabledLayers.data());
  		}
  
  		// Make sure the debug report extension is enabled when validation is enabled.
  		enabledExtensions = addDebugReportExt(vkp, createInfo);
  		createInfo.enabledExtensionCount = static_cast<deUint32>(enabledExtensions.size());
  		createInfo.ppEnabledExtensionNames = enabledExtensions.data();
  	}
  
  	return CustomInstance(context, vk::createInstance(vkp, &createInfo, pAllocator), validationEnabled, printValidationErrors);
  }
  
  vk::VkResult createUncheckedInstance (Context& context, const vk::VkInstanceCreateInfo* instanceCreateInfo, const vk::VkAllocationCallbacks* pAllocator, UncheckedInstance* instance, bool allowLayers)
  {
  	vector<const char*>				enabledLayers;
  	vector<const char*>				enabledExtensions;
  	vk::VkInstanceCreateInfo		createInfo				= *instanceCreateInfo;
  	const auto&						cmdLine					= context.getTestContext().getCommandLine();
  	const bool						validationEnabled		= cmdLine.isValidationEnabled();
  	const bool						printValidationErrors	= cmdLine.printValidationErrors();
  	const vk::PlatformInterface&	vkp						= context.getPlatformInterface();
  	const bool						addLayers				= (validationEnabled && allowLayers);
  
  	if (addLayers)
  	{
  		// Activate some layers if requested.
  		if (createInfo.enabledLayerCount == 0u)
  		{
  			enabledLayers = getValidationLayers(vkp);
  			createInfo.enabledLayerCount = static_cast<deUint32>(enabledLayers.size());
  			createInfo.ppEnabledLayerNames = (enabledLayers.empty() ? DE_NULL : enabledLayers.data());
  		}
  
  		// Make sure the debug report extension is enabled when validation is enabled.
  		enabledExtensions = addDebugReportExt(vkp, createInfo);
  		createInfo.enabledExtensionCount = static_cast<deUint32>(enabledExtensions.size());
  		createInfo.ppEnabledExtensionNames = enabledExtensions.data();
  	}
  
  	vk::VkInstance	raw_instance = DE_NULL;
  	vk::VkResult	result = vkp.createInstance(&createInfo, pAllocator, &raw_instance);
  
  	*instance = UncheckedInstance(context, raw_instance, pAllocator, addLayers, printValidationErrors);
  
  	return result;
  }
  
  vk::Move<vk::VkDevice> createCustomDevice (bool validationEnabled, const vk::PlatformInterface& vkp, vk::VkInstance instance, const vk::InstanceInterface& vki, vk::VkPhysicalDevice physicalDevice, const vk::VkDeviceCreateInfo* pCreateInfo, const vk::VkAllocationCallbacks* pAllocator)
  {
  	vector<const char*>		enabledLayers;
  	vk::VkDeviceCreateInfo	createInfo		= *pCreateInfo;
  
  	if (createInfo.enabledLayerCount == 0u && validationEnabled)
  	{
  		enabledLayers = getValidationLayers(vki, physicalDevice);
  		createInfo.enabledLayerCount = static_cast<deUint32>(enabledLayers.size());
  		createInfo.ppEnabledLayerNames = (enabledLayers.empty() ? DE_NULL : enabledLayers.data());
  	}
  
  	return createDevice(vkp, instance, vki, physicalDevice, &createInfo, pAllocator);
  }
  
  vk::VkResult createUncheckedDevice (bool validationEnabled, const vk::InstanceInterface& vki, vk::VkPhysicalDevice physicalDevice, const vk::VkDeviceCreateInfo* pCreateInfo, const vk::VkAllocationCallbacks* pAllocator, vk::VkDevice* pDevice)
  {
  	vector<const char*>		enabledLayers;
  	vk::VkDeviceCreateInfo	createInfo		= *pCreateInfo;
  
  	if (createInfo.enabledLayerCount == 0u && validationEnabled)
  	{
  		enabledLayers = getValidationLayers(vki, physicalDevice);
  		createInfo.enabledLayerCount = static_cast<deUint32>(enabledLayers.size());
  		createInfo.ppEnabledLayerNames = (enabledLayers.empty() ? DE_NULL : enabledLayers.data());
  	}
  
  	return vki.createDevice(physicalDevice, &createInfo, pAllocator, pDevice);
  }
  
  
  }
  