#include "VulkanTools.h" #if !(defined(VK_USE_PLATFORM_IOS_MVK) || defined(VK_USE_PLATFORM_MACOS_MVK)) // iOS & macOS: VulkanExampleBase::getAssetPath() implemented externally to allow access to Objective-C components const std::string getAssetPath() { #if defined(VK_USE_PLATFORM_ANDROID_KHR) return ""; #elif defined(VK_EXAMPLE_DATA_DIR) std::filesystem::path dataFilePath; std::string dataFile; if (std::filesystem::exists("./../data/")) { return "./../data/"; } else if (std::filesystem::exists("./data/")) { return "./data/"; } else if (std::filesystem::exists("./../../data/")) { return "../../data/"; } else { std::filesystem::path currentPath = std::filesystem::current_path(); dataFilePath = VK_EXAMPLE_DATA_DIR; dataFilePath = dataFilePath.lexically_relative(currentPath); dataFile = dataFilePath.generic_string(); return dataFile; } #endif } #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 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; } VkBool32 formatHasStencil(VkFormat format) { std::vector stencilFormats = { VK_FORMAT_S8_UINT, VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, }; return std::find(stencilFormats.begin(), stencilFormats.end(), format) != std::end(stencilFormats); } // 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); } } }