text: /*
  * Assorted commonly used Vulkan helper functions
  *
  * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
  *
  * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
  */
  
  #include "VulkanTools.h"
  
  const std::string getAssetPath()
  {
  #if defined(VK_USE_PLATFORM_ANDROID_KHR)
  	return "";
  #elif defined(VK_EXAMPLE_DATA_DIR)
  	return VK_EXAMPLE_DATA_DIR;
  #else
  	return "./../data/";
  #endif
  }
  
  namespace vks
  {
  	namespace tools
  	{
  		bool errorModeSilent = false;
  
  		std::string errorString(VkResult errorCode)
  		{
  			switch (errorCode)
  			{
  #define STR(r) case VK_ ##r: return #r
  				STR(NOT_READY);
  				STR(TIMEOUT);
  				STR(EVENT_SET);
  				STR(EVENT_RESET);
  				STR(INCOMPLETE);
  				STR(ERROR_OUT_OF_HOST_MEMORY);
  				STR(ERROR_OUT_OF_DEVICE_MEMORY);
  				STR(ERROR_INITIALIZATION_FAILED);
  				STR(ERROR_DEVICE_LOST);
  				STR(ERROR_MEMORY_MAP_FAILED);
  				STR(ERROR_LAYER_NOT_PRESENT);
  				STR(ERROR_EXTENSION_NOT_PRESENT);
  				STR(ERROR_FEATURE_NOT_PRESENT);
  				STR(ERROR_INCOMPATIBLE_DRIVER);
  				STR(ERROR_TOO_MANY_OBJECTS);
  				STR(ERROR_FORMAT_NOT_SUPPORTED);
  				STR(ERROR_SURFACE_LOST_KHR);
  				STR(ERROR_NATIVE_WINDOW_IN_USE_KHR);
  				STR(SUBOPTIMAL_KHR);
  				STR(ERROR_OUT_OF_DATE_KHR);
  				STR(ERROR_INCOMPATIBLE_DISPLAY_KHR);
  				STR(ERROR_VALIDATION_FAILED_EXT);
  				STR(ERROR_INVALID_SHADER_NV);
  #undef STR
  			default:
  				return "UNKNOWN_ERROR";
  			}
  		}
  
  		std::string physicalDeviceTypeString(VkPhysicalDeviceType type)
  		{
  			switch (type)
  			{
  #define STR(r) case VK_PHYSICAL_DEVICE_TYPE_ ##r: return #r
  				STR(OTHER);
  				STR(INTEGRATED_GPU);
  				STR(DISCRETE_GPU);
  				STR(VIRTUAL_GPU);
  				STR(CPU);
  #undef STR
  			default: return "UNKNOWN_DEVICE_TYPE";
  			}
  		}
  
  		VkBool32 getSupportedDepthFormat(VkPhysicalDevice physicalDevice, VkFormat *depthFormat)
  		{
  			// Since all depth formats may be optional, we need to find a suitable depth format to use
  			// Start with the highest precision packed format
  			std::vector<VkFormat> depthFormats = {
  				VK_FORMAT_D32_SFLOAT_S8_UINT,
  				VK_FORMAT_D32_SFLOAT,
  				VK_FORMAT_D24_UNORM_S8_UINT,
  				VK_FORMAT_D16_UNORM_S8_UINT,
  				VK_FORMAT_D16_UNORM
  			};
  
  			for (auto& format : depthFormats)
  			{
  				VkFormatProperties formatProps;
  				vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps);
  				// Format must support depth stencil attachment for optimal tiling
  				if (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
  				{
  					*depthFormat = format;
  					return true;
  				}
  			}
  
  			return false;
  		}
  
  		// Returns if a given format support LINEAR filtering
  		VkBool32 formatIsFilterable(VkPhysicalDevice physicalDevice, VkFormat format, VkImageTiling tiling)
  		{
  			VkFormatProperties formatProps;
  			vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProps);
  
  			if (tiling == VK_IMAGE_TILING_OPTIMAL)
  				return formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
  
  			if (tiling == VK_IMAGE_TILING_LINEAR)
  				return formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
  
  			return false;
  		}
  
  		// Create an image memory barrier for changing the layout of
  		// an image and put it into an active command buffer
  		// See chapter 11.4 "Image Layout" for details
  
  		void setImageLayout(
  			VkCommandBuffer cmdbuffer,
  			VkImage image,
  			VkImageLayout oldImageLayout,
  			VkImageLayout newImageLayout,
  			VkImageSubresourceRange subresourceRange,
  			VkPipelineStageFlags srcStageMask,
  			VkPipelineStageFlags dstStageMask)
  		{
  			// Create an image barrier object
  			VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier();
  			imageMemoryBarrier.oldLayout = oldImageLayout;
  			imageMemoryBarrier.newLayout = newImageLayout;
  			imageMemoryBarrier.image = image;
  			imageMemoryBarrier.subresourceRange = subresourceRange;
  
  			// Source layouts (old)
  			// Source access mask controls actions that have to be finished on the old layout
  			// before it will be transitioned to the new layout
  			switch (oldImageLayout)
  			{
  			case VK_IMAGE_LAYOUT_UNDEFINED:
  				// Image layout is undefined (or does not matter)
  				// Only valid as initial layout
  				// No flags required, listed only for completeness
  				imageMemoryBarrier.srcAccessMask = 0;
  				break;
  
  			case VK_IMAGE_LAYOUT_PREINITIALIZED:
  				// Image is preinitialized
  				// Only valid as initial layout for linear images, preserves memory contents
  				// Make sure host writes have been finished
  				imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
  				// Image is a color attachment
  				// Make sure any writes to the color buffer have been finished
  				imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
  				// Image is a depth/stencil attachment
  				// Make sure any writes to the depth/stencil buffer have been finished
  				imageMemoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
  				// Image is a transfer source
  				// Make sure any reads from the image have been finished
  				imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
  				// Image is a transfer destination
  				// Make sure any writes to the image have been finished
  				imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
  				// Image is read by a shader
  				// Make sure any shader reads from the image have been finished
  				imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
  				break;
  			default:
  				// Other source layouts aren't handled (yet)
  				break;
  			}
  
  			// Target layouts (new)
  			// Destination access mask controls the dependency for the new image layout
  			switch (newImageLayout)
  			{
  			case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
  				// Image will be used as a transfer destination
  				// Make sure any writes to the image have been finished
  				imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
  				// Image will be used as a transfer source
  				// Make sure any reads from the image have been finished
  				imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
  				// Image will be used as a color attachment
  				// Make sure any writes to the color buffer have been finished
  				imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
  				// Image layout will be used as a depth/stencil attachment
  				// Make sure any writes to depth/stencil buffer have been finished
  				imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
  				break;
  
  			case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
  				// Image will be read in a shader (sampler, input attachment)
  				// Make sure any writes to the image have been finished
  				if (imageMemoryBarrier.srcAccessMask == 0)
  				{
  					imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
  				}
  				imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
  				break;
  			default:
  				// Other source layouts aren't handled (yet)
  				break;
  			}
  
  			// Put barrier inside setup command buffer
  			vkCmdPipelineBarrier(
  				cmdbuffer,
  				srcStageMask,
  				dstStageMask,
  				0,
  				0, nullptr,
  				0, nullptr,
  				1, &imageMemoryBarrier);
  		}
  
  		// Fixed sub resource on first mip level and layer
  		void setImageLayout(
  			VkCommandBuffer cmdbuffer,
  			VkImage image,
  			VkImageAspectFlags aspectMask,
  			VkImageLayout oldImageLayout,
  			VkImageLayout newImageLayout,
  			VkPipelineStageFlags srcStageMask,
  			VkPipelineStageFlags dstStageMask)
  		{
  			VkImageSubresourceRange subresourceRange = {};
  			subresourceRange.aspectMask = aspectMask;
  			subresourceRange.baseMipLevel = 0;
  			subresourceRange.levelCount = 1;
  			subresourceRange.layerCount = 1;
  			setImageLayout(cmdbuffer, image, oldImageLayout, newImageLayout, subresourceRange, srcStageMask, dstStageMask);
  		}
  
  		void insertImageMemoryBarrier(
  			VkCommandBuffer cmdbuffer,
  			VkImage image,
  			VkAccessFlags srcAccessMask,
  			VkAccessFlags dstAccessMask,
  			VkImageLayout oldImageLayout,
  			VkImageLayout newImageLayout,
  			VkPipelineStageFlags srcStageMask,
  			VkPipelineStageFlags dstStageMask,
  			VkImageSubresourceRange subresourceRange)
  		{
  			VkImageMemoryBarrier imageMemoryBarrier = vks::initializers::imageMemoryBarrier();
  			imageMemoryBarrier.srcAccessMask = srcAccessMask;
  			imageMemoryBarrier.dstAccessMask = dstAccessMask;
  			imageMemoryBarrier.oldLayout = oldImageLayout;
  			imageMemoryBarrier.newLayout = newImageLayout;
  			imageMemoryBarrier.image = image;
  			imageMemoryBarrier.subresourceRange = subresourceRange;
  
  			vkCmdPipelineBarrier(
  				cmdbuffer,
  				srcStageMask,
  				dstStageMask,
  				0,
  				0, nullptr,
  				0, nullptr,
  				1, &imageMemoryBarrier);
  		}
  
  		void exitFatal(const std::string& message, int32_t exitCode)
  		{
  #if defined(_WIN32)
  			if (!errorModeSilent) {
  				MessageBox(NULL, message.c_str(), NULL, MB_OK | MB_ICONERROR);
  			}
  #elif defined(__ANDROID__)
              LOGE("Fatal error: %s", message.c_str());
  			vks::android::showAlert(message.c_str());
  #endif
  			std::cerr << message << "\n";
  #if !defined(__ANDROID__)
  			exit(exitCode);
  #endif
  		}
  
  		void exitFatal(const std::string& message, VkResult resultCode)
  		{
  			exitFatal(message, (int32_t)resultCode);
  		}
  
  #if defined(__ANDROID__)
  		// Android shaders are stored as assets in the apk
  		// So they need to be loaded via the asset manager
  		VkShaderModule loadShader(AAssetManager* assetManager, const char *fileName, VkDevice device)
  		{
  			// Load shader from compressed asset
  			AAsset* asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_STREAMING);
  			assert(asset);
  			size_t size = AAsset_getLength(asset);
  			assert(size > 0);
  
  			char *shaderCode = new char[size];
  			AAsset_read(asset, shaderCode, size);
  			AAsset_close(asset);
  
  			VkShaderModule shaderModule;
  			VkShaderModuleCreateInfo moduleCreateInfo;
  			moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
  			moduleCreateInfo.pNext = NULL;
  			moduleCreateInfo.codeSize = size;
  			moduleCreateInfo.pCode = (uint32_t*)shaderCode;
  			moduleCreateInfo.flags = 0;
  
  			VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule));
  
  			delete[] shaderCode;
  
  			return shaderModule;
  		}
  #else
  		VkShaderModule loadShader(const char *fileName, VkDevice device)
  		{
  			std::ifstream is(fileName, std::ios::binary | std::ios::in | std::ios::ate);
  
  			if (is.is_open())
  			{
  				size_t size = is.tellg();
  				is.seekg(0, std::ios::beg);
  				char* shaderCode = new char[size];
  				is.read(shaderCode, size);
  				is.close();
  
  				assert(size > 0);
  
  				VkShaderModule shaderModule;
  				VkShaderModuleCreateInfo moduleCreateInfo{};
  				moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
  				moduleCreateInfo.codeSize = size;
  				moduleCreateInfo.pCode = (uint32_t*)shaderCode;
  
  				VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderModule));
  
  				delete[] shaderCode;
  
  				return shaderModule;
  			}
  			else
  			{
  				std::cerr << "Error: Could not open shader file \"" << fileName << "\"" << "\n";
  				return VK_NULL_HANDLE;
  			}
  		}
  #endif
  
  		bool fileExists(const std::string &filename)
  		{
  			std::ifstream f(filename.c_str());
  			return !f.fail();
  		}
  
  		uint32_t alignedSize(uint32_t value, uint32_t alignment)
          {
  	        return (value + alignment - 1) & ~(alignment - 1);
          }
  
  	}
  }
  