From 5d254bc513e13428051fcd4ea3d9ce6cd01bc3f0 Mon Sep 17 00:00:00 2001 From: "matthias@arch" Date: Fri, 21 Oct 2022 22:00:06 +0200 Subject: [PATCH] Restructured file, removed friends, added public interface --- src/vulkan_instance.cpp | 3830 ++++++++++++++++++--------------------- src/vulkan_instance.hpp | 782 ++++---- 2 files changed, 2219 insertions(+), 2393 deletions(-) diff --git a/src/vulkan_instance.cpp b/src/vulkan_instance.cpp index c8f746a..d3c55e2 100644 --- a/src/vulkan_instance.cpp +++ b/src/vulkan_instance.cpp @@ -4,8 +4,10 @@ #include "font.hpp" #include "vk_enum_string.h" +#include "vulkan_allocator.hpp" #include #include +#include #include #include #include @@ -30,2142 +32,1812 @@ namespace gz::vk { using std::uint32_t; -const unsigned int WIDTH = 600; -const unsigned int HEIGHT = 600; - -const std::vector extensions = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - /* VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME */ -}; -const std::vector instanceExtensions = { -}; - - - -// -// DEBUG // -const std::vector validationLayers = { - "VK_LAYER_KHRONOS_validation", -}; +// SETTINGS +// + const unsigned int WIDTH = 600; + const unsigned int HEIGHT = 600; + + const std::vector extensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + /* VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME */ + }; + const std::vector instanceExtensions = { + }; + + // + // DEBUG + // + const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation", + }; #ifdef NDEBUG - constexpr bool enableValidationLayers = false; + constexpr bool enableValidationLayers = false; #else - constexpr bool enableValidationLayers = true; + constexpr bool enableValidationLayers = true; #endif -constexpr VkDebugUtilsMessageSeverityFlagsEXT debugMessageSevereties = - VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + constexpr VkDebugUtilsMessageSeverityFlagsEXT debugMessageSevereties = + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; -constexpr VkDebugUtilsMessageTypeFlagsEXT debugMessageTypes = - VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; -const std::vector validationFeaturesEnable { - VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT, - VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT, - VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT -}; -const VkValidationFeaturesEXT validationFeatures { - VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, // sType - nullptr, // pNext - static_cast(validationFeaturesEnable.size()), // enabled feature count - validationFeaturesEnable.data(), // enable features - 0, // disabled feature count - nullptr // disabled features -}; + constexpr VkDebugUtilsMessageTypeFlagsEXT debugMessageTypes = + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + const std::vector validationFeaturesEnable { + VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT, + VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT, + VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT + }; + const VkValidationFeaturesEXT validationFeatures { + VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, // sType + nullptr, // pNext + static_cast(validationFeaturesEnable.size()), // enabled feature count + validationFeaturesEnable.data(), // enable features + 0, // disabled feature count + nullptr // disabled features + }; -/* constexpr VkDebugUtilsMessageTypeFlagsEXT debugFlags = */ -/* VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT | */ -/* VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; */ + /* constexpr VkDebugUtilsMessageTypeFlagsEXT debugFlags = */ + /* VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT | */ + /* VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; */ + + constexpr VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCI { + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // sType + &validationFeatures, // pNext + /* nullptr, // pNext */ + NO_FLAGS, // flags + debugMessageSevereties, // messageSeverety + debugMessageTypes, // messageType + VulkanInstance::debugLog, // pfnUserCallback + nullptr, // pUserData + + }; -constexpr VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCI { - VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, // sType - &validationFeatures, // pNext - /* nullptr, // pNext */ - NO_FLAGS, // flags - debugMessageSevereties, // messageSeverety - debugMessageTypes, // messageType - VulkanInstance::debugLog, // pfnUserCallback - nullptr, // pUserData - -}; // -// EXTENSIONS +// UTILITY FUNCTIONS // -bool checkRequiredExtensionsAvailable(const std::vector& requiredExtensions, const std::vector& availableExtensions) { - bool extensionAvailable; - for (const auto& requiredExtension : requiredExtensions) { - extensionAvailable = false; - for (const auto& extension : availableExtensions) { - if (strcmp(requiredExtension, extension.extensionName) == 0) { - extensionAvailable = true; - break; - } - } - if (!extensionAvailable) { - return false; - } - } - return true; -} - - -std::vector getRequiredExtensions() { - uint32_t glfwExtensionCount = 0; - const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - std::vector requiredExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount); - - if (enableValidationLayers) { - requiredExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - return requiredExtensions; -} - - -// check if all extensions are supported by device -bool deviceExtensionsSupported(VkPhysicalDevice device) { - uint32_t extensionCount; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); - std::vector availableExtensions(extensionCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); - - bool extensionAvailable; - for (const char* requiredExtension : extensions) { - extensionAvailable = false; - for (const auto& extensionProperties : availableExtensions) { - if (strcmp(requiredExtension, extensionProperties.extensionName) == 0) { - extensionAvailable = true; - break; - } - } - if (!extensionAvailable) { return false; } - } - return true; -} - -// check if validation layers are supported/installed -bool validationLayersSupported() { - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - bool layerAvailable; - for (const char* requiredLayer : validationLayers) { - layerAvailable = false; - for (const auto& layerProperties : availableLayers) { - if (strcmp(requiredLayer, layerProperties.layerName) == 0) { - layerAvailable = true; - break; - } - } - if (!layerAvailable) { return false; } - } - return true; -} - - -// -// App -// -void VulkanInstance::createInstance() { - - VkApplicationInfo appInfo{}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pEngineName = "Glowzwiebel Engine"; - appInfo.pApplicationName = "Hallo Dreieck"; - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.apiVersion = VK_API_VERSION_1_3; - - VkInstanceCreateInfo instanceCI{}; - instanceCI.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instanceCI.pApplicationInfo = &appInfo; - instanceCI.pNext = &debugUtilsMessengerCI; - instanceCI.flags = 0; - instanceCI.ppEnabledExtensionNames = instanceExtensions.data(); - instanceCI.enabledExtensionCount = instanceExtensions.size(); - - if (enableValidationLayers) { - if (!validationLayersSupported()) { - throw std::runtime_error("Validation layers enabled but not available."); - } - instanceCI.enabledLayerCount = static_cast(validationLayers.size()); - instanceCI.ppEnabledLayerNames = validationLayers.data(); - } - else { - instanceCI.enabledLayerCount = 0; - instanceCI.ppEnabledLayerNames = nullptr; - } - - std::vector requiredExtensions = getRequiredExtensions(); - instanceCI.enabledExtensionCount = static_cast(requiredExtensions.size()); - instanceCI.ppEnabledExtensionNames = requiredExtensions.data();; - - // log requiredExtensions - std::string message; - message.reserve(80); - message += "Required extensions ("; - message += std::to_string(instanceCI.enabledExtensionCount) + ')'; - for (uint32_t i = 0; i < requiredExtensions.size(); i++) { - message += "\n\t"; - message += std::to_string(i + 1) + ": " + requiredExtensions[i]; - } - vLog(message); - - VkResult result = vkCreateInstance(&instanceCI, nullptr, &instance); - if (result != VK_SUCCESS) { - vLog("Failed to create instance.", "VkResult:", STR_VK_RESULT(result)); - throw std::runtime_error("Failed to create instance."); - } - - uint32_t extensionCount = 0; - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); // get count - std::vector extensions(extensionCount); - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); - - // log available extensions - message = "Available extensions ("; - message += std::to_string(extensionCount) + "):"; - for (uint32_t i = 0; i < extensions.size(); i++) { - message += "\n\t"; - message += std::to_string(i + 1) + ": " + extensions[i].extensionName; - } - vLog(message); - - if (!checkRequiredExtensionsAvailable(requiredExtensions, extensions)) { - throw std::runtime_error("Not all required extensions are available."); - } -} - - - -bool VulkanInstance::lastColorWhite; -std::vector VulkanInstance::objectsUsingVulkan; -void VulkanInstance::registerObjectUsingVulkan(const ObjectUsingVulkan& obj) { - objectsUsingVulkan.push_back(obj); -} -const std::regex reAddress(R"(0x[0-9a-f]+)"); -std::string VulkanInstance::handleOwnerString(100, ' '); // reserve size 100 -void VulkanInstance::getHandleOwnerString(std::string_view message) { - handleOwnerString.clear(); - re::svmatch match; - if (re::regex_search(message, match, reAddress)) { - handleOwnerString = "Handle ownerships: [ "; - bool foundHandle = false; - for (auto it = match.begin(); it != match.end(); it++) { - uint64_t address; - std::stringstream ss; - ss << std::hex << it->str(); - ss >> address; - for (auto& obj : objectsUsingVulkan) { - /* vLog(address, obj.handles); */ - if (obj.handles.contains(address)) { - handleOwnerString += it->str() + " - " + obj.objectName; - address = 0; - foundHandle = true; + // + // EXTENSIONS + // + bool checkRequiredExtensionsAvailable(const std::vector& requiredExtensions, const std::vector& availableExtensions) { + bool extensionAvailable; + for (const auto& requiredExtension : requiredExtensions) { + extensionAvailable = false; + for (const auto& extension : availableExtensions) { + if (strcmp(requiredExtension, extension.extensionName) == 0) { + extensionAvailable = true; + break; } } - if (address != 0) { - handleOwnerString += "Unknown"; + if (!extensionAvailable) { + return false; } - handleOwnerString += ", "; } - if (foundHandle) { - handleOwnerString.erase(handleOwnerString.size()-2); - handleOwnerString += " ]"; - } - else { - handleOwnerString.clear(); + return true; + } + + + std::vector getRequiredExtensions() { + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + std::vector requiredExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount); + + if (enableValidationLayers) { + requiredExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } + return requiredExtensions; } -} -gz::Log VulkanInstance::vLog("vulkan.log", true, true, "Vulkan", VULKAN_MESSAGE_PREFIX_COLOR, true, VULKAN_MESSAGE_TIME_COLOR); -VKAPI_ATTR VkBool32 VKAPI_CALL VulkanInstance::debugLog( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverety, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) { - // Set message color - std::string type; - std::vector colors; - switch(messageType) { - case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: - type = "General:"; - colors.push_back(gz::Color::GREEN); - break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: - type = "Performance:"; - colors.push_back(gz::Color::CYAN); - break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: - type = "Validation:"; - colors.push_back(gz::Color::MAGENTA); - break; - default: - type = "None:"; - colors.push_back(gz::Color::WHITE); - } - - // color for second keyword / whole message if not validation message - switch(messageSeverety) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: - colors.push_back(gz::Color::RESET); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: - colors.push_back(gz::Color::WHITE); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - colors.push_back(gz::Color::BG_YELLOW); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - colors.push_back(gz::Color::BG_RED); - break; - default: - colors.push_back(gz::Color::RESET); - } - - - std::string_view message(pCallbackData->pMessage); - std::string_view messageIdName(pCallbackData->pMessageIdName); - // extra differenciation between different validation messages - // is validation or performance message: get type from brackets, print owner of any pointers - if (messageType & (VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)) { - // if cant be found npos + 1 = 0 - size_t startAt = message.find(']') + 1; - if (messageIdName.starts_with("UNASSIGNED-DEBUG-PRINTF")) { - type = "Shader:"; - colors[0] = gz::Color::LI_CYAN; - startAt = message.find_first_of('|'); - if (startAt == std::string::npos) { startAt = 0; } - else { startAt += 2; } // skip "| " - } - else if (messageIdName.starts_with("UNASSIGNED-BestPractices")) { - type = "Best Practice:"; - colors[0] = gz::Color::LI_GREEN; - } - else if (messageIdName.starts_with("SYNC-HAZARD")) { - type = "Synchronization"; - colors[0] = gz::Color::LI_RED; - } - message = std::string_view(message.begin() + startAt, message.end()); - - colors.push_back(colors[1]); // color for messageIdName, use also for brackets - colors.push_back(colors[1]); - // color for actual message - if (lastColorWhite) { - colors.push_back(gz::Color::RESET); - } - else { - colors.push_back(gz::Color::WHITE); - - } - // color for handleOwnerString - colors.push_back(gz::Color::LI_CYAN); - getHandleOwnerString(message); - vLog.clog(colors, std::move(type), "[", messageIdName, "]", message, handleOwnerString); - } - else { - vLog.clog(colors, std::move(type), message); - } - lastColorWhite = !lastColorWhite; - return VK_FALSE; -} - - - - - -// -// DEVICE -// -PhysicalDeviceFeatures VulkanInstance::getRequiredFeatures() const { - PhysicalDeviceFeatures requiredFeatures; - requiredFeatures.f13.synchronization2 = VK_TRUE; - return requiredFeatures; -} - - -void VulkanInstance::selectPhysicalDevice() { - uint32_t deviceCount = 0; - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); - - if (deviceCount == 0) { - vLog.error("Could not find any GPU."); - throw std::runtime_error("Could not find any GPU."); - } - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); - - // find best gpu - std::vector deviceScores; - for (const auto& device : devices) { - deviceScores.push_back(rateDevice(device)); - } - unsigned int bestGPUIndex = std::max_element(deviceScores.begin(), deviceScores.end()) - deviceScores.begin(); - if (deviceScores[bestGPUIndex] == 0) { - vLog.error("Could not find suitable GPU."); - throw std::runtime_error("Could not find suitable GPU."); - } - physicalDevice = devices[bestGPUIndex]; - - vkGetPhysicalDeviceProperties(physicalDevice, &phDevProperties); - vkGetPhysicalDeviceFeatures2(physicalDevice, &phDevFeatures.f); - qFamilyIndices = findQueueFamilies(physicalDevice); - vLog("Selected GPU:", phDevProperties.deviceName, "with the following queue family indices:", qFamilyIndices); -} - - -const unsigned int SCORE_DISCRETE_GPU = 5000; -const unsigned int SCORE_INTEGRATED_GPU = 2000; -const unsigned int SCORE_VIRTUAL_GPU = 1000; -const unsigned int SCORE_HAS_ALL_QUEUE_FAMILIES = 100; -const unsigned int SCORE_HAS_FEATURE = 100; - -unsigned int VulkanInstance::rateDevice(VkPhysicalDevice device) { - unsigned int score; - - // rate type - VkPhysicalDeviceProperties properties{}; - vkGetPhysicalDeviceProperties(device, &properties); - switch(properties.deviceType) { - case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: - score = SCORE_DISCRETE_GPU; - break; - case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: - score = SCORE_INTEGRATED_GPU; - break; - case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: - score = SCORE_VIRTUAL_GPU; - break; - default: - return 0; - } - - qFamilyIndices = findQueueFamilies(device); - // has needed queue families - if (qFamilyIndices.hasAllValues()) { - score += SCORE_HAS_ALL_QUEUE_FAMILIES; - } - else if (!qFamilyIndices.hasNecessaryValues()) { - return 0; - } - - // supports all extensions - if (!deviceExtensionsSupported(device)) { - return 0; - } - - // supports all features - PhysicalDeviceFeatures requiredFeatures = getRequiredFeatures(); - PhysicalDeviceFeatures features; - vLog("features sType", STR_VK_STRUCTURE_TYPE(features.f.sType)); - vkGetPhysicalDeviceFeatures2(device, &features.f); - if (!features.requiredFeaturesAvailable(requiredFeatures)) { - return 0; - } - // optional features - if (features.f.features.samplerAnisotropy == VK_TRUE) { score += SCORE_HAS_FEATURE; } - - // swap chain support - SwapChainSupport scDetails = querySwapChainSupport(device); - if (scDetails.formats.empty() or scDetails.presentModes.empty()) { - return 0; - } - - // rate memory - VkPhysicalDeviceMemoryProperties memProperties{}; - vkGetPhysicalDeviceMemoryProperties(device, &memProperties); - for (uint32_t i = 0; i < memProperties.memoryHeapCount; i++) { - score += memProperties.memoryHeaps[i].size / 1e6; - } - - - vLog("rateDevice: GPU: ", properties.deviceName, " - Score: ", score); - return score; -} - - -void VulkanInstance::setValidSettings() { - // anisotropic filtering - settings.setAllowedValues("anisotropy_enable", { false, vkBool2Bool(phDevFeatures.f.features.samplerAnisotropy) }, gz::SM_LIST); - settings.setAllowedValues("max_anisotropy", { 1.0f, phDevProperties.limits.maxSamplerAnisotropy }, gz::SM_RANGE); -} - -VkFormat VulkanInstance::findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { - for (const VkFormat& format : candidates) { - VkFormatProperties formatProperties; - vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); - if (tiling == VK_IMAGE_TILING_LINEAR and - (formatProperties.linearTilingFeatures & features) == features) { - return format; - } - else if (tiling == VK_IMAGE_TILING_OPTIMAL and - (formatProperties.optimalTilingFeatures & features) == features) { - return format; - } - } - throw VkException(std::string("Could not find a suitable format. tiling: ") + STR_VK_IMAGE_TILING(tiling) + ".", "findSupportedFormat"); -} - - -// -// QUEUE -// -QueueFamilyIndices VulkanInstance::findQueueFamilies(VkPhysicalDevice device) { - QueueFamilyIndices indices; - - uint32_t qFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &qFamilyCount, nullptr); - std::vector qFamilies(qFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &qFamilyCount, qFamilies.data()); - vLog("findQueueFamilies: found", qFamilyCount, "queue families:"); - - VkBool32 presentSupport = false; - for (unsigned int i = 0; i < qFamilyCount; i++) { - // check for queue with graphic capabilities - if (qFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { - indices.graphicsFamily = i; - } - else if (qFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT) { // only transfer, not graphics - indices.transferFamily = i; - } - - // check for surface support - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (presentSupport == VK_TRUE) { - indices.presentFamily = i; - } - - vLog("findQueueFamilies:", i, "-", qFamilies[i].queueFlags); - if (indices.hasAllValues()) { - vLog("findQueueFamilies: Found all wanted families."); - break; - } - } - return indices; -} - - -// -// LOGICAL DEVICE -// -void VulkanInstance::createLogicalDevice() { - - std::unordered_set qFamiliyIndicesSet = { // must be unique - qFamilyIndices.graphicsFamily.value(), qFamilyIndices.presentFamily.value(), - }; - if (qFamilyIndices.transferFamily.has_value()) { - qFamiliyIndicesSet.insert(qFamilyIndices.transferFamily.value()); - } - - std::vector deviceQueueCI; - for (uint32_t index : qFamiliyIndicesSet) { - deviceQueueCI.push_back(VkDeviceQueueCreateInfo()); - deviceQueueCI.back().sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - deviceQueueCI.back().queueFamilyIndex = index; - deviceQueueCI.back().queueCount = 1; - float qPriority = 1.0f; - deviceQueueCI.back().pQueuePriorities = &qPriority; - } - - PhysicalDeviceFeatures enabledFeatures = getRequiredFeatures(); - // optional features - enabledFeatures.f.features.samplerAnisotropy = bool2VkBool(settings.getOr("anisotropy_enable", false)); - - VkDeviceCreateInfo deviceCI{}; - deviceCI.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCI.pQueueCreateInfos = deviceQueueCI.data(); - deviceCI.queueCreateInfoCount = static_cast(deviceQueueCI.size()); - deviceCI.pNext = &enabledFeatures.f; - /* deviceCI.pEnabledFeatures = &deviceFeatures.f; */ - deviceCI.enabledExtensionCount = static_cast(extensions.size()); - deviceCI.ppEnabledExtensionNames = extensions.data(); - - VkResult result = vkCreateDevice(physicalDevice, &deviceCI, nullptr, &device); - if (result != VK_SUCCESS) { - vLog.error("createLogicalDevice: Failed to create logical device.", "VkResult:", STR_VK_RESULT(result)); - throw std::runtime_error("createLogicalDevice: Failed to create logical device."); - } - - // get handles - uint32_t index = 0; - vkGetDeviceQueue(device, qFamilyIndices.graphicsFamily.value(), index, &graphicsQ); - vkGetDeviceQueue(device, qFamilyIndices.presentFamily.value(), index, &presentQ); - if (qFamilyIndices.transferFamily.has_value()) { - vkGetDeviceQueue(device, qFamilyIndices.transferFamily.value(), index, &transferQ); - } - else { - transferQ = graphicsQ; - } - vLog("createLogicalDevice: Created logical device."); -} - - -// -// SURFACE -// -void VulkanInstance::createSurface() { - VkResult result = glfwCreateWindowSurface(instance, window, nullptr, &surface); - if (result != VK_SUCCESS) { - vLog.error("createSurface: Failed to create window surface.", "VkResult:", STR_VK_RESULT(result)); - throw std::runtime_error("createSurface: Failed to create window surface."); - } -} - - -// -// SWAP CHAIN -// -SwapChainSupport VulkanInstance::querySwapChainSupport(VkPhysicalDevice physicalDevice) { - SwapChainSupport scDetails{}; - - uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr); - scDetails.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, scDetails.formats.data()); - - uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr); - scDetails.presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, scDetails.presentModes.data()); - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &scDetails.capabilities); - - return scDetails; -} - - -VkSurfaceFormatKHR VulkanInstance::selectSwapChainSurfaceFormat(const std::vector& availableFormats) { - vLog("selectSwapChainSurfaceFormat:", availableFormats.size(), "formats available."); - for (const auto& format : availableFormats) { - if (format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - return format; - } - } - return availableFormats.front(); -} - - -VkPresentModeKHR VulkanInstance::selectSwapChainPresentMode(const std::vector& availableModes) { - for (const auto& mode : availableModes) { - if (mode == VK_PRESENT_MODE_MAILBOX_KHR) { - return mode; - } - } - return VK_PRESENT_MODE_FIFO_KHR; -} - - -VkExtent2D VulkanInstance::selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { - // if width = max(uint32_t) then surface size will be determined by a swapchain targeting it - if (capabilities.currentExtent.width == std::numeric_limits::max()) { - int width, height; - glfwGetFramebufferSize(window, &width, &height); - VkExtent2D actualExtent {}; - actualExtent.width = std::clamp(static_cast(width), capabilities.minImageExtent.width, capabilities.maxImageExtent.width); - actualExtent.height = std::clamp(static_cast(height), capabilities.minImageExtent.height, capabilities.maxImageExtent.height); - return actualExtent; - } - else { - return capabilities.currentExtent; - } -} - - -void VulkanInstance::createSwapChain() { - SwapChainSupport scDetails = querySwapChainSupport(physicalDevice); - VkSurfaceFormatKHR surfaceFormat = selectSwapChainSurfaceFormat(scDetails.formats); - - uint32_t imageCount = scDetails.capabilities.minImageCount + 1; - // maxImageCount = 0 -> no max - if (scDetails.capabilities.maxImageCount > 0 and imageCount < scDetails.capabilities.maxImageCount) { - imageCount = scDetails.capabilities.maxImageCount; - } - - scImageFormat = surfaceFormat.format; - scExtent = selectSwapExtent(scDetails.capabilities); - - VkSwapchainCreateInfoKHR swapChainCI{}; - swapChainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - swapChainCI.surface = surface; - swapChainCI.minImageCount = imageCount; - swapChainCI.imageFormat = scImageFormat; - swapChainCI.imageColorSpace = surfaceFormat.colorSpace; - swapChainCI.imageExtent = scExtent; - swapChainCI.imageArrayLayers = 1; - // if postProcessing: VK_IMAGE_USAGE_TRANSFER_DST_BIT - swapChainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - - /* QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); */ - // must live until vkCreateSwapchainKHR - std::vector qIndices; - if (qFamilyIndices.graphicsFamily.value() != qFamilyIndices.presentFamily.value()) { - swapChainCI.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - qIndices = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.presentFamily.value() }; - swapChainCI.queueFamilyIndexCount = static_cast(qIndices.size()); - swapChainCI.pQueueFamilyIndices = qIndices.data(); - } - else { - swapChainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - } - - swapChainCI.preTransform = scDetails.capabilities.currentTransform; - swapChainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swapChainCI.presentMode = selectSwapChainPresentMode(scDetails.presentModes); - swapChainCI.clipped = VK_TRUE; - swapChainCI.oldSwapchain = VK_NULL_HANDLE; - - VkResult result = vkCreateSwapchainKHR(device, &swapChainCI, nullptr, &swapChain); - if (result != VK_SUCCESS) { - vLog.error("createSwapChain: Couldn not create swap chain", "VkResult:", STR_VK_RESULT(result)); - throw std::runtime_error("createSwapChain: Couldn not create swap chain"); - } - - // get image handles - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); - scImages.resize(imageCount); - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, scImages.data()); - vLog("createSwapChain: Created swap chain."); -} - - -void VulkanInstance::recreateSwapChain() { - int width, height = 0; - glfwGetFramebufferSize(window, &width, &height); - while (width == 0 || height == 0) { // minimized - glfwGetFramebufferSize(window, &width, &height); - glfwWaitEvents(); - } - vLog("recreateSwapChain: new framebuffer size:", width, height); - vkDeviceWaitIdle(device); - cleanupSwapChain(); - - createSwapChain(); - createSwapChainImageViews(); - /* createDescriptorSetLayout(); */ - /* createGraphicsPipeline("shaders/vert.spv", "shaders/frag.spv", true, renderPass1, pipelines[PL_3D]); */ - /* createGraphicsPipeline("shaders/vert2D.spv", "shaders/frag2D.spv", false, renderPassBegin, pipelines[PL_2D]); */ - /* createDepthImageAndView(); */ - /* recreateFramebuffers(); */ - /* createUniformBuffers(); */ - createCommandBuffers(commandBuffersBegin); - createCommandBuffers(commandBuffersEnd); - /* createDescriptorPool(); */ - /* createDescriptorSets(); */ - for (auto& f : scRecreateCallbacks) { - f(); - } - vLog("recreateSwapChain: Updating all ObjectsUsingVulkan"); - for (auto& obj : VulkanInstance::objectsUsingVulkan) { - obj.updateHandles(); - } -} - - -void VulkanInstance::cleanupSwapChain() { - /* vkDestroyImageView(device, depthImageView, nullptr); */ - /* vkDestroyImage(device, depthImage, nullptr); */ - /* vkFreeMemory(device, depthImageMemory, nullptr); */ - - for (auto& imageView : scImageViews) { - vkDestroyImageView(device, imageView, nullptr); - } - vkDestroySwapchainKHR(device, swapChain, nullptr); -} - - -void VulkanInstance::registerSwapChainRecreateCallback(std::function callbackF) { - scRecreateCallbacks.push_back(callbackF); -} -void VulkanInstance::registerCleanupCallback(std::function callbackF) { - cleanupCallbacks.push_back(callbackF); -} - - - -// -// IMAGE VIEW -// -void VulkanInstance::createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags) { - VkImageViewCreateInfo imageViewCI{}; - imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCI.format = format; - imageViewCI.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - imageViewCI.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - imageViewCI.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - imageViewCI.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - imageViewCI.subresourceRange.aspectMask = aspectFlags; - imageViewCI.subresourceRange.baseMipLevel = 0; - imageViewCI.subresourceRange.levelCount = 1; - imageViewCI.subresourceRange.baseArrayLayer = 0; - imageViewCI.subresourceRange.layerCount = 1; - imageViewCI.image = image; - VkResult result = vkCreateImageView(device, &imageViewCI, nullptr, &imageView); - if (result != VK_SUCCESS) { - throw getVkException(result, "Could not create image view", "createImageViews"); - } -} - - -void VulkanInstance::createSwapChainImageViews() { - scImageViews.resize(scImages.size()); - for (unsigned int i = 0; i < scImageViews.size(); i++) { - createImageView(scImageFormat, scImages[i], scImageViews[i], VK_IMAGE_ASPECT_COLOR_BIT); - } - vLog("createImageViews: Created", scImageViews.size(), "image views."); -} - - -// -// DESCRIPTORS -// -void VulkanInstance::createDescriptorSetLayout(std::vector bindings, VkDescriptorSetLayout& layout) { - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; - descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - descriptorSetLayoutCI.bindingCount = static_cast(bindings.size()); - descriptorSetLayoutCI.pBindings = bindings.data(); - - - VkResult result = vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &layout); - if (result != VK_SUCCESS) { - throw getVkException(result, "Could not create descriptorSetLayout", "createDescriptorSetLayout"); - } - /* vLog("createDescriptorSetLayout: Created descriptor set layout."); */ -} - - - -/* VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; */ -/* descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; */ -/* descriptorSetLayoutCI.bindingCount = static_cast(bindings.size()); */ -/* descriptorSetLayoutCI.pBindings = bindings.data(); */ - - -/* VkResult result = vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayout); */ -/* if (result != VK_SUCCESS) { */ -/* throw getVkException(result, "Could not create descriptorSetLayout", "createDescriptorSetLayout"); */ -/* } */ -/* vLog("createDescriptorSetLayout: Created descriptor set layout."); */ -/* } */ - - -/* void VulkanInstance::createDescriptorPool() { */ -/* std::array poolSizes; */ -/* poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; */ -/* poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ -/* poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; */ -/* poolSizes[1].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ - -/* VkDescriptorPoolCreateInfo poolCI{}; */ -/* poolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; */ -/* poolCI.poolSizeCount = static_cast(poolSizes.size()); */ -/* poolCI.pPoolSizes = poolSizes.data(); */ -/* poolCI.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); */ -/* VkResult result = vkCreateDescriptorPool(device, &poolCI, nullptr, &descriptorPool); */ -/* if (result != VK_SUCCESS) { */ -/* throw getVkException(result, "Failed to create descriptor pool", "createDescriptorPool"); */ -/* } */ -/* vLog("createDescriptorPool: Created descriptor pool."); */ -/* } */ - - -/* void VulkanInstance::createDescriptorSets() { */ -/* std::vector layouts(MAX_FRAMES_IN_FLIGHT, descriptorSetLayout); */ -/* VkDescriptorSetAllocateInfo setAI{}; */ -/* setAI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; */ -/* setAI.descriptorPool = descriptorPool; */ -/* setAI.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); */ -/* setAI.pSetLayouts = layouts.data(); */ - -/* descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); */ -/* VkResult result = vkAllocateDescriptorSets(device, &setAI, descriptorSets.data()); */ -/* if (result != VK_SUCCESS) { */ -/* throw getVkException(result, "Failed to create descriptor sets", "createDescriptorPool"); */ -/* } */ - -/* // configure sets */ -/* for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { */ -/* VkDescriptorBufferInfo bufferI{}; */ -/* // TODO */ -/* /1* bufferI.buffer = uniformBuffers[i]; *1/ */ -/* bufferI.offset = 0; */ -/* bufferI.range = VK_WHOLE_SIZE; // sizeof(UniformBufferObject); */ - -/* VkDescriptorImageInfo imageI{}; */ -/* imageI.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; */ -/* imageI.imageView = textureImageView; */ -/* imageI.sampler = textureSampler; */ - -/* std::array descriptorW{}; */ -/* descriptorW[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; */ -/* descriptorW[0].dstSet = descriptorSets[i]; */ -/* descriptorW[0].dstBinding = bindingUniformBuffer; */ -/* descriptorW[0].dstArrayElement = 0; */ -/* descriptorW[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; */ -/* descriptorW[0].descriptorCount = 1; */ -/* descriptorW[0].pBufferInfo = &bufferI; */ -/* /1* descriptorW[0].pImageInfo = nullptr; *1/ */ -/* /1* descriptorW[0].pTexelBufferView = nullptr; *1/ */ - -/* descriptorW[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; */ -/* descriptorW[1].dstSet = descriptorSets[i]; */ -/* descriptorW[1].dstBinding = bindingCombinedImageSampler; */ -/* descriptorW[1].dstArrayElement = 0; */ -/* descriptorW[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; */ -/* descriptorW[1].descriptorCount = 1; */ -/* /1* descriptorW[1].pBufferInfo = &bufferI; *1/ */ -/* descriptorW[1].pImageInfo = &imageI; */ -/* /1* descriptorW[1].pTexelBufferView = nullptr; *1/ */ - -/* uint32_t descriptorWriteCount = static_cast(descriptorW.size()); */ -/* uint32_t descriptorCopyCount = 0; */ -/* vkUpdateDescriptorSets(device, descriptorWriteCount, descriptorW.data(), descriptorCopyCount, nullptr); */ -/* } */ -/* vLog("createDescriptorSets: Created descriptor sets."); */ -/* } */ - - -// -// PIPELINE -// -/* void App::createGraphicsPipeline() { */ -/* auto vertShaderCode = readFile("shaders/vert.spv"); */ -/* auto fragShaderCode = readFile("shaders/frag.spv"); */ - -/* VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); */ -/* VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); */ - -/* VkPipelineShaderStageCreateInfo vertexShaderStageCI{}; */ -/* vertexShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; */ -/* vertexShaderStageCI.stage = VK_SHADER_STAGE_VERTEX_BIT; */ -/* vertexShaderStageCI.module = vertShaderModule; */ -/* vertexShaderStageCI.pName = "main"; */ -/* /1* vssCreateInfo.pSpecializationInfo = nullptr; *1/ */ - -/* VkPipelineShaderStageCreateInfo fragmentShaderStageCI{}; */ -/* fragmentShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; */ -/* fragmentShaderStageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT; */ -/* fragmentShaderStageCI.module = fragShaderModule; */ -/* fragmentShaderStageCI.pName = "main"; */ -/* /1* vssCreateInfo.pSpecializationInfo = nullptr; *1/ */ - -/* VkPipelineShaderStageCreateInfo shaderStagesCI[] = { vertexShaderStageCI, fragmentShaderStageCI }; */ - -/* VkVertexInputBindingDescription bindingD = Vertex::getBindingDescription(); */ -/* // array */ -/* auto attributeD = Vertex::getAttributeDescriptions(); */ - -/* VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; */ -/* vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; */ -/* vertexInputStateCI.vertexBindingDescriptionCount = 1; */ -/* vertexInputStateCI.pVertexBindingDescriptions = &bindingD; */ -/* vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(attributeD.size());; */ -/* vertexInputStateCI.pVertexAttributeDescriptions = attributeD.data(); */ - -/* VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; */ -/* inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; */ -/* inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; */ -/* inputAssemblyStateCI.primitiveRestartEnable = VK_FALSE; */ - -/* VkViewport viewport{}; */ -/* viewport.x = 0.0f; */ -/* viewport.y = 0.0f; */ -/* viewport.width = static_cast(scExtent.width); */ -/* viewport.height = static_cast(scExtent.height); */ -/* viewport.minDepth = 0.0f; */ -/* viewport.maxDepth = 1.0f; */ - -/* VkRect2D scissor{}; */ -/* scissor.offset = {0, 0}; */ -/* scissor.extent = scExtent; */ - -/* VkPipelineViewportStateCreateInfo viewportStateCI{}; */ -/* viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; */ -/* viewportStateCI.viewportCount = 1; */ -/* viewportStateCI.pViewports = &viewport; */ -/* viewportStateCI.scissorCount = 1; */ -/* viewportStateCI.pScissors = &scissor; */ - -/* VkPipelineRasterizationStateCreateInfo rasterizerCI{}; */ -/* rasterizerCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; */ -/* rasterizerCI.depthClampEnable = VK_FALSE; */ -/* rasterizerCI.rasterizerDiscardEnable = VK_FALSE; */ -/* rasterizerCI.polygonMode = VK_POLYGON_MODE_FILL; */ -/* rasterizerCI.lineWidth = 1.0f; */ -/* rasterizerCI.cullMode = VK_CULL_MODE_BACK_BIT; */ -/* // not clockwise because of inverted y axis from glm */ -/* rasterizerCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; */ -/* rasterizerCI.depthBiasEnable = VK_FALSE; */ -/* /1* rasterizerCI.depthBiasConstantFactor = 0.0f; *1/ */ -/* /1* rasterizerCI.depthBiasClamp = 0.0f; *1/ */ -/* /1* rasterizerCI.depthBiasSlopeFactor = 0.0f; *1/ */ - -/* VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; */ -/* multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; */ -/* multisampleStateCI.sampleShadingEnable = VK_FALSE; */ -/* multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; */ -/* /1* multisampleStateCI.minSampleShading = 1.0f; *1/ */ -/* /1* multisampleStateCI.pSampleMask = nullptr; *1/ */ -/* /1* multisampleStateCI.alphaToCoverageEnable = VK_FALSE; *1/ */ -/* /1* multisampleStateCI.alphaToOneEnable = VK_FALSE; *1/ */ - -/* VkPipelineColorBlendAttachmentState colorBlendAttachment{}; */ -/* colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; */ -/* colorBlendAttachment.blendEnable = VK_FALSE; */ -/* /1* colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; *1/ */ -/* /1* colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; *1/ */ -/* /1* colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; *1/ */ -/* /1* colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; *1/ */ -/* /1* colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; *1/ */ -/* /1* colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; *1/ */ - -/* VkPipelineColorBlendStateCreateInfo colorBlendCI{}; */ -/* colorBlendCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; */ -/* colorBlendCI.logicOpEnable = VK_FALSE; */ -/* /1* colorBlendCI.logicOp = VK_LOGIC_OP_COPY; *1/ */ -/* colorBlendCI.attachmentCount = 1; */ -/* colorBlendCI.pAttachments = &colorBlendAttachment; */ -/* colorBlendCI.blendConstants[0] = 0.0f; */ -/* colorBlendCI.blendConstants[1] = 0.0f; */ -/* colorBlendCI.blendConstants[2] = 0.0f; */ -/* colorBlendCI.blendConstants[3] = 0.0f; */ - -/* /1* std::vector dynamicStates = { *1/ */ -/* /1* VK_DYNAMIC_STATE_VIEWPORT, *1/ */ -/* /1* VK_DYNAMIC_STATE_LINE_WIDTH *1/ */ -/* /1* }; *1/ */ -/* /1* VkPipelineDynamicStateCreateInfo dynamicState{}; *1/ */ -/* /1* dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; *1/ */ -/* /1* dynamicState.dynamicStateCount = static_cast(dynamicStates.size()); *1/ */ -/* /1* dynamicState.pDynamicStates = dynamicStates.data(); *1/ */ - -/* VkPipelineDepthStencilStateCreateInfo depthStencilCI{}; */ -/* depthStencilCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; */ -/* depthStencilCI.depthTestEnable = VK_TRUE; */ -/* depthStencilCI.depthWriteEnable = VK_TRUE; */ -/* depthStencilCI.depthCompareOp = VK_COMPARE_OP_LESS; */ -/* depthStencilCI.depthBoundsTestEnable = VK_FALSE; */ -/* /1* depthStencilCI.minDepthBounds = 0.0f; *1/ */ -/* /1* depthStencilCI.maxDepthBounds = 1.0f; *1/ */ -/* depthStencilCI.stencilTestEnable = VK_FALSE; */ -/* /1* depthStencilCI.front = {}; *1/ */ -/* /1* depthStencilCI.back = {}; *1/ */ - -/* VkPipelineLayoutCreateInfo pipelineLayoutCI{}; */ -/* pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; */ -/* /1* pipelineLayoutCI.pushConstantRangeCount = 0; *1/ */ -/* /1* pipelineLayoutCI.pPushConstantRanges = nullptr; *1/ */ -/* pipelineLayoutCI.setLayoutCount = 1; */ -/* pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; */ -/* VkResult result = vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout); */ -/* if (result != VK_SUCCESS) { */ -/* throw getVkException(result, "Failed to create pipeline layout", "createGraphicsPipeline"); */ -/* } */ - -/* VkGraphicsPipelineCreateInfo pipelineCI{}; */ -/* pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; */ -/* pipelineCI.stageCount = 2; */ -/* pipelineCI.pStages = shaderStagesCI; */ -/* pipelineCI.pVertexInputState = &vertexInputStateCI; */ -/* pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; */ -/* pipelineCI.pViewportState = &viewportStateCI; */ -/* pipelineCI.pRasterizationState = &rasterizerCI; */ -/* pipelineCI.pMultisampleState = &multisampleStateCI; */ -/* pipelineCI.pDepthStencilState = &depthStencilCI; */ -/* pipelineCI.pColorBlendState = &colorBlendCI; */ -/* /1* pipelineCI.pDynamicState = nullptr; *1/ */ -/* pipelineCI.layout = pipelineLayout; */ -/* pipelineCI.renderPass = renderPass; */ -/* pipelineCI.subpass = 0; */ -/* /1* pipelineCI.basePipelineHandle = VK_NULL_HANDLE; *1/ */ -/* /1* pipelineCI.basePipelineIndex = -1; *1/ */ - -/* result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCI, nullptr, &graphicsPipeline); */ -/* if (result != VK_SUCCESS) { */ -/* throw getVkException(result, "Could not create graphics pipeline", "createGraphicsPipeline"); */ -/* } */ - -/* vkDestroyShaderModule(device, vertShaderModule, nullptr); */ -/* vkDestroyShaderModule(device, fragShaderModule, nullptr); */ - -/* vLog("createGraphicsPipeline: Created graphics pipeline."); */ -/* } */ + // check if all extensions are supported by device + bool deviceExtensionsSupported(VkPhysicalDevice device) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); - -template -void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline) { - auto vertShaderCode = readFile(vertexShader); - auto fragShaderCode = readFile(fragmentShader); - - VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); - VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); - - VkPipelineShaderStageCreateInfo vertexShaderStageCI{}; - vertexShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertexShaderStageCI.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertexShaderStageCI.module = vertShaderModule; - vertexShaderStageCI.pName = "main"; - /* vssCreateInfo.pSpecializationInfo = nullptr; */ - - VkPipelineShaderStageCreateInfo fragmentShaderStageCI{}; - fragmentShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragmentShaderStageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragmentShaderStageCI.module = fragShaderModule; - fragmentShaderStageCI.pName = "main"; - /* vssCreateInfo.pSpecializationInfo = nullptr; */ - - VkPipelineShaderStageCreateInfo shaderStagesCI[] = { vertexShaderStageCI, fragmentShaderStageCI }; - - VkVertexInputBindingDescription bindingD = VertexT::getBindingDescription(); - // array - auto attributeD = VertexT::getAttributeDescriptions(); - - VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; - vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputStateCI.vertexBindingDescriptionCount = 1; - vertexInputStateCI.pVertexBindingDescriptions = &bindingD; - vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(attributeD.size());; - vertexInputStateCI.pVertexAttributeDescriptions = attributeD.data(); - - VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; - inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssemblyStateCI.primitiveRestartEnable = VK_FALSE; - - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = static_cast(scExtent.width); - viewport.height = static_cast(scExtent.height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor{}; - scissor.offset = {0, 0}; - scissor.extent = scExtent; - - VkPipelineViewportStateCreateInfo viewportStateCI{}; - viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportStateCI.viewportCount = 1; - viewportStateCI.pViewports = &viewport; - viewportStateCI.scissorCount = 1; - viewportStateCI.pScissors = &scissor; - - VkPipelineRasterizationStateCreateInfo rasterizerCI{}; - rasterizerCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizerCI.depthClampEnable = VK_FALSE; - rasterizerCI.rasterizerDiscardEnable = VK_FALSE; - rasterizerCI.polygonMode = VK_POLYGON_MODE_FILL; - rasterizerCI.lineWidth = 1.0f; - rasterizerCI.cullMode = VK_CULL_MODE_BACK_BIT; - // not clockwise because of inverted y axis from glm - rasterizerCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; - rasterizerCI.depthBiasEnable = VK_FALSE; - /* rasterizerCI.depthBiasConstantFactor = 0.0f; */ - /* rasterizerCI.depthBiasClamp = 0.0f; */ - /* rasterizerCI.depthBiasSlopeFactor = 0.0f; */ - - VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; - multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampleStateCI.sampleShadingEnable = VK_FALSE; - multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - /* multisampleStateCI.minSampleShading = 1.0f; */ - /* multisampleStateCI.pSampleMask = nullptr; */ - /* multisampleStateCI.alphaToCoverageEnable = VK_FALSE; */ - /* multisampleStateCI.alphaToOneEnable = VK_FALSE; */ - - VkPipelineColorBlendAttachmentState colorBlendAttachment{}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - /* colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; */ - /* colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; */ - /* colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; */ - /* colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; */ - /* colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; */ - /* colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; */ - - VkPipelineColorBlendStateCreateInfo colorBlendCI{}; - colorBlendCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlendCI.logicOpEnable = VK_FALSE; - /* colorBlendCI.logicOp = VK_LOGIC_OP_COPY; */ - colorBlendCI.attachmentCount = 1; - colorBlendCI.pAttachments = &colorBlendAttachment; - colorBlendCI.blendConstants[0] = 0.0f; - colorBlendCI.blendConstants[1] = 0.0f; - colorBlendCI.blendConstants[2] = 0.0f; - colorBlendCI.blendConstants[3] = 0.0f; - - /* std::vector dynamicStates = { */ - /* VK_DYNAMIC_STATE_VIEWPORT, */ - /* VK_DYNAMIC_STATE_LINE_WIDTH */ - /* }; */ - /* VkPipelineDynamicStateCreateInfo dynamicState{}; */ - /* dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; */ - /* dynamicState.dynamicStateCount = static_cast(dynamicStates.size()); */ - /* dynamicState.pDynamicStates = dynamicStates.data(); */ - - VkPipelineDepthStencilStateCreateInfo depthStencilCI{}; - depthStencilCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - depthStencilCI.depthTestEnable = VK_TRUE; - depthStencilCI.depthWriteEnable = VK_TRUE; - depthStencilCI.depthCompareOp = VK_COMPARE_OP_LESS; - depthStencilCI.depthBoundsTestEnable = VK_FALSE; - /* depthStencilCI.minDepthBounds = 0.0f; */ - /* depthStencilCI.maxDepthBounds = 1.0f; */ - depthStencilCI.stencilTestEnable = VK_FALSE; - /* depthStencilCI.front = {}; */ - /* depthStencilCI.back = {}; */ - - VkPipelineLayoutCreateInfo pipelineLayoutCI{}; - pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - /* pipelineLayoutCI.pushConstantRangeCount = 0; */ - /* pipelineLayoutCI.pPushConstantRanges = nullptr; */ - pipelineLayoutCI.setLayoutCount = static_cast(descriptorSetLayouts.size()); - pipelineLayoutCI.pSetLayouts = descriptorSetLayouts.data(); - VkResult result = vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipeline.layout); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create pipeline layout", "createGraphicsPipeline"); - } - - VkGraphicsPipelineCreateInfo pipelineCI{}; - pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - pipelineCI.stageCount = 2; - pipelineCI.pStages = shaderStagesCI; - pipelineCI.pVertexInputState = &vertexInputStateCI; - pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; - pipelineCI.pViewportState = &viewportStateCI; - pipelineCI.pRasterizationState = &rasterizerCI; - pipelineCI.pMultisampleState = &multisampleStateCI; - if (useDepthStencil) { - pipelineCI.pDepthStencilState = &depthStencilCI; - } - else { - pipelineCI.pDepthStencilState = nullptr; - } - pipelineCI.pColorBlendState = &colorBlendCI; - /* pipelineCI.pDynamicState = nullptr; */ - pipelineCI.layout = pipeline.layout; - pipelineCI.renderPass = renderPass; - pipelineCI.subpass = 0; - /* pipelineCI.basePipelineHandle = VK_NULL_HANDLE; */ - /* pipelineCI.basePipelineIndex = -1; */ - - result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCI, nullptr, &pipeline.pipeline); - if (result != VK_SUCCESS) { - throw getVkException(result, "Could not create graphics pipeline", "createGraphicsPipeline"); - } - - vkDestroyShaderModule(device, vertShaderModule, nullptr); - vkDestroyShaderModule(device, fragShaderModule, nullptr); - - vLog("createGraphicsPipeline: Created graphics pipeline."); -} -template void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); -template void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); - -// -// BUFFER -// -void VulkanInstance::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory, VkSharingMode sharingMode, std::vector* qFamiliesWithAccess) { - VkBufferCreateInfo bufferCI{}; - bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferCI.size = size; - bufferCI.usage = usage; - bufferCI.sharingMode = sharingMode; - std::vector queueFamiliesWithAccess; - if (sharingMode == VK_SHARING_MODE_CONCURRENT) { - if (qFamiliesWithAccess == nullptr) { - throw VkUserError("Sharing mode is VK_SHARING_MODE_CONCURRENT but qFamiliesWithAccess is nullptr", "createBuffer"); - } - bufferCI.queueFamilyIndexCount = static_cast(qFamiliesWithAccess->size()); - bufferCI.pQueueFamilyIndices = qFamiliesWithAccess->data(); - } - - VkResult result = vkCreateBuffer(device, &bufferCI, nullptr, &buffer); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create buffer", "createBuffer"); - } - - VkMemoryRequirements memoryRequirements{}; - vkGetBufferMemoryRequirements(device, buffer, &memoryRequirements); - - VkMemoryAllocateInfo allocI{}; - allocI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocI.allocationSize = memoryRequirements.size; - VkMemoryPropertyFlags wantedProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - allocI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, wantedProperties); - - result = vkAllocateMemory(device, &allocI, nullptr, &bufferMemory); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to allocate buffer memory!", "createBuffer"); - } - - vkBindBufferMemory(device, buffer, bufferMemory, NO_OFFSET); -} - - -VkCommandBuffer VulkanInstance::beginSingleTimeCommands(VkCommandPool commandPool) { - VkCommandBufferAllocateInfo allocI{}; - allocI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocI.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocI.commandPool = commandPool; - allocI.commandBufferCount = 1; - - VkCommandBuffer cmdBuffer; - vkAllocateCommandBuffers(device, &allocI, &cmdBuffer); - - VkCommandBufferBeginInfo beginI{}; - beginI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - vkBeginCommandBuffer(cmdBuffer, &beginI); - return cmdBuffer; -} - - -void VulkanInstance::endSingleTimeCommands(VkCommandBuffer cmdBuffer, VkCommandPool commandPool, VkQueue q) { - VkResult result = vkEndCommandBuffer(cmdBuffer); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to end commandBuffer", "endSingleTimeCommands"); - } - - VkSubmitInfo submitI{}; - submitI.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitI.commandBufferCount = 1; - submitI.pCommandBuffers = &cmdBuffer; - uint32_t submitCount = 1; - result = vkQueueSubmit(q, submitCount, &submitI, VK_NULL_HANDLE); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to submit commandBuffer", "endSingleTimeCommands"); - } - result = vkQueueWaitIdle(q); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to wait for queue idle", "endSingleTimeCommands"); - } - const uint32_t commandBufferCount = 1; - vkFreeCommandBuffers(device, commandPool, commandBufferCount, &cmdBuffer); -} - - -void VulkanInstance::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { - VkCommandBuffer cmdBuffer = beginSingleTimeCommands(commandPoolTransfer); - VkBufferCopy copyRegion{}; - copyRegion.srcOffset = 0; - copyRegion.dstOffset = 0; - copyRegion.size = size; - uint32_t copyRegionCount = 1; - vkCmdCopyBuffer(cmdBuffer, srcBuffer, dstBuffer, copyRegionCount, ©Region); - endSingleTimeCommands(cmdBuffer, commandPoolTransfer, transferQ); -} - - -uint32_t VulkanInstance::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { - VkPhysicalDeviceMemoryProperties memoryProperties{}; - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); - for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { - if ((typeFilter & (1 << i)) and (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { // if ith bit of typefilter is set - return i; - } - } - throw VkException("Could not find suitable memory type", "findMemoryType"); -} - - -// -// VERTEX BUFFER -// -template -void VulkanInstance::createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize) { - vertexBufferSize = vertexCount * sizeof(VertexT); - - // create vertex buffer - VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE; - std::vector qFamiliesWithAccess; - if (qFamilyIndices.transferFamily.has_value()) { // prefer dedicated transfer family - qFamiliesWithAccess = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.transferFamily.value() }; - } - createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory , sharingMode, &qFamiliesWithAccess); - - vLog("createVertexBuffer: Created vertex buffer with size", vertexBufferSize); -} -template void VulkanInstance::createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize); -template void VulkanInstance::createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize); - - -// -// INDEX BUFFER -// -template -void VulkanInstance::createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize) { - indexBufferSize = indexCount * sizeof(T); - - // create index buffer - VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE; - std::vector qFamiliesWithAccess; - if (qFamilyIndices.transferFamily.has_value()) { // prefer dedicated transfer family - qFamiliesWithAccess = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.transferFamily.value() }; - } - createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory, sharingMode, &qFamiliesWithAccess); - - vLog("createIndexBuffer: Created index buffer with size:", indexBufferSize); -} -template void VulkanInstance::createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize); -template void VulkanInstance::createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize); - - - -// -// FRAMEBUFFERS -// -void VulkanInstance::createFramebuffers(std::vector& framebuffers, std::vector& imageViews, VkRenderPass& renderPass) { - framebuffers.resize(scImageViews.size()); - - VkFramebufferCreateInfo framebufferCI{}; - framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferCI.renderPass = renderPass; - framebufferCI.width = scExtent.width; - framebufferCI.height = scExtent.height; - framebufferCI.layers = 1; - - for (size_t i = 0; i < framebuffers.size(); i++) { - // TODO include depth image view for 3D - std::vector attachments = { imageViews[i] }; //, depthImageView }; - framebufferCI.pAttachments = attachments.data(); - framebufferCI.attachmentCount = static_cast(attachments.size()); - - VkResult result = vkCreateFramebuffer(device, &framebufferCI, nullptr, &framebuffers[i]); - if (result != VK_SUCCESS) { - throw getVkException(result, "Could not create framebuffer", "createFramebuffers_"); - } - } - vLog("createFramebuffers: Created", framebuffers.size(), "framebuffers."); -} - - -void VulkanInstance::destroyFramebuffers(std::vector& framebuffers) { - for (auto& framebuffer : framebuffers) { - vkDestroyFramebuffer(device, framebuffer, nullptr); - } -} - - -void VulkanInstance::frameBufferResizedCallback(GLFWwindow* window, int width, int height) { - VulkanInstance* app = reinterpret_cast(glfwGetWindowUserPointer(window)); - app->frameBufferResized = true; -} - - -// -// COMMAND POOL -// -void VulkanInstance::createCommandPools() { - /* QueueFamilyIndicjes queueFamilyIndices = findQueueFamilies(physicalDevice); */ - VkCommandPoolCreateInfo commandPoolGraphicsCI{}; - commandPoolGraphicsCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - commandPoolGraphicsCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - commandPoolGraphicsCI.queueFamilyIndex = qFamilyIndices.graphicsFamily.value(); - VkResult result = vkCreateCommandPool(device, &commandPoolGraphicsCI, nullptr, &commandPoolGraphics); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create graphics command pool", "createCommandPool"); - } - - if (qFamilyIndices.transferFamily.has_value()) { - VkCommandPoolCreateInfo commandPoolTransferCI{}; - commandPoolTransferCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - commandPoolTransferCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - commandPoolTransferCI.queueFamilyIndex = qFamilyIndices.transferFamily.value(); - result = vkCreateCommandPool(device, &commandPoolTransferCI, nullptr, &commandPoolTransfer); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create transfer command pool", "createCommandPool"); - } - } - else { - commandPoolTransfer = commandPoolGraphics; - } -} - - -// -// COMMAND BUFFER -// -void VulkanInstance::createCommandBuffers(std::vector& commandBuffers) { - commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); - - VkCommandBufferAllocateInfo commandBufferAI{}; - commandBufferAI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - commandBufferAI.commandPool = commandPoolGraphics; - commandBufferAI.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - commandBufferAI.commandBufferCount = static_cast(commandBuffers.size()); - VkResult result = vkAllocateCommandBuffers(device, &commandBufferAI, commandBuffers.data()); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create command buffer", "createCommandBuffer"); - } -} - - -void VulkanInstance::destroyCommandBuffers(std::vector& commandBuffers) { - vkFreeCommandBuffers(device, commandPoolGraphics, static_cast(commandBuffers.size()), commandBuffers.data()); - commandBuffers.resize(0); -} - - - -void VulkanInstance::createSyncObjects() { - imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); - - VkSemaphoreCreateInfo semaphoreCI{}; - semaphoreCI.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VkFenceCreateInfo fenceCI{}; - fenceCI.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceCI.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - VkResult result = vkCreateSemaphore(device, &semaphoreCI, nullptr, &imageAvailableSemaphores[i]); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create imageAvailableSemaphores", "createSyncObjects"); - } - result = vkCreateSemaphore(device, &semaphoreCI, nullptr, &renderFinishedSemaphores[i]); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create renderFinishedSemaphores", "createSyncObjects"); - } - result = vkCreateFence(device, &fenceCI, nullptr, &inFlightFences[i]); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create inFlightFences", "createSyncObjects"); - } - } -} - -// -// IMAGE UTILITY -// -void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory) { - VkImageCreateInfo imageCI{}; - imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCI.imageType = VK_IMAGE_TYPE_2D; - imageCI.extent.width = static_cast(width); - imageCI.extent.height = static_cast(height); - imageCI.extent.depth = 1; - imageCI.mipLevels = 1; - imageCI.arrayLayers = 1; - imageCI.format = format; - // use linear when direct texel access is needed - imageCI.tiling = tiling; - imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageCI.usage = usage; - imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCI.samples = VK_SAMPLE_COUNT_1_BIT; - /* imageCI.flags = 0; */ - VkResult result = vkCreateImage(device, &imageCI, NO_ALLOC, &image); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create image", "createTextureImage"); - } - - VkMemoryRequirements memoryRequirements; - vkGetImageMemoryRequirements(device, image, &memoryRequirements); - - VkMemoryAllocateInfo memoryAI{}; - memoryAI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - memoryAI.allocationSize = memoryRequirements.size; - memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryProperties); - - result = vkAllocateMemory(device, &memoryAI, NO_ALLOC, &imageMemory); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to allocate memory for image", "createTextureImage"); - } - - result = vkBindImageMemory(device, image, imageMemory, NO_OFFSET); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create image", "createTextureImage"); - } -} - - -void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height) { - VkCommandBuffer cmdBuffer = beginSingleTimeCommands(commandPoolTransfer); - VkBufferImageCopy region{}; - region.bufferOffset = NO_OFFSET; - region.bufferRowLength = 0; - region.bufferImageHeight = 0; - - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.imageSubresource.mipLevel = 0; - region.imageSubresource.baseArrayLayer = 0; - region.imageSubresource.layerCount = 1; - - region.imageOffset = { offsetX, offsetY, 0 }; - region.imageExtent = { width, height, 1 }; - const uint32_t regionCount = 1; - vkCmdCopyBufferToImage(cmdBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount, ®ion); - - endSingleTimeCommands(cmdBuffer, commandPoolTransfer, transferQ); -} - - -void VulkanInstance::copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, VkExtent2D extent) { - VkImageCopy region{}; - region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.srcSubresource.mipLevel = 0; - region.srcSubresource.baseArrayLayer = 0; - region.srcSubresource.layerCount = 1; - - region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - region.dstSubresource.mipLevel = 0; - region.dstSubresource.baseArrayLayer = 0; - region.dstSubresource.layerCount = 1; - - /* std::array offsets; */ - /* offsets[0] = { 0, 0, 0 }; */ - /* offsets[1] = { 0, 0, 0 }; */ - region.srcOffset = { 0, 0, 0 }; - region.dstOffset = { 0, 0, 0 }; - region.extent = { extent.width, extent.height, 1 }; - const uint32_t regionCount = 1; - vkCmdCopyImage(cmdBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount, ®ion); -} - - -bool hasStencilComponent(VkFormat format) { - return format == VK_FORMAT_D32_SFLOAT_S8_UINT or format == VK_FORMAT_D24_UNORM_S8_UINT; -} - - -void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandBuffer* cmdBuffer) { - VkImageMemoryBarrier2 barrier{}; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; - barrier.oldLayout = oldLayout; - barrier.newLayout = newLayout; - // not using barrier for queue famlily ownership transfer - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - barrier.srcAccessMask = 0; - barrier.dstAccessMask = 0; - - VkCommandPool commandPool; - VkQueue q; - if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and - newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; - if (hasStencilComponent(format)) { - barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - - barrier.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - commandPool = commandPoolGraphics; - q = graphicsQ; - } - else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and - newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = 0; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - - barrier.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; - commandPool = commandPoolTransfer; - q = transferQ; - } - else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and - newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - commandPool = commandPoolGraphics; - q = graphicsQ; - } - else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL and - newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - - barrier.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; - commandPool = commandPoolGraphics; - q = graphicsQ; - } - else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and - newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { - barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; - - barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; - barrier.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - commandPool = commandPoolGraphics; - q = graphicsQ; - } - else { - throw VkUserError(std::string("transitionImageLayout: Unsupported layout transition") + STR_VK_IMAGE_LAYOUT(oldLayout) + "->" + STR_VK_IMAGE_LAYOUT(newLayout), "transitionImageLayout");; - } - // if not provided, get a single time command buffer - VkCommandBuffer cmdBuffer_; - if (cmdBuffer == nullptr) { - cmdBuffer_ = beginSingleTimeCommands(commandPool); - } - else { - cmdBuffer_ = *cmdBuffer; - } - VkDependencyInfo depI = getDepInfo(barrier); - vkCmdPipelineBarrier2(cmdBuffer_, &depI); - - if (cmdBuffer == nullptr) { - endSingleTimeCommands(cmdBuffer_, commandPool, q); - } -} - - -// -// DEPTH -// -VkFormat VulkanInstance::findDepthFormat() { - return findSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); -} - - -/* void VulkanInstance::createDepthImageAndView() { */ -/* VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; */ -/* VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; */ -/* VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; */ -/* VkFormat depthFormat = findDepthFormat(); */ - -/* createImage(scExtent.width, scExtent.height, depthFormat, tiling, imageUsage, memoryProperties, depthImage, depthImageMemory); */ -/* createImageView(depthFormat, depthImage, depthImageView, VK_IMAGE_ASPECT_DEPTH_BIT); */ - -/* transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); */ - - -/* } */ - -// -// TEXTURE -// -/* void VulkanInstance::createTextureImageAndView() { */ -/* int textureWidth, textureHeight, textureChannels; */ -/* stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha); */ -/* if (!pixels) { */ -/* throw Exception("Failed to load texture image", "createTextureImage"); */ -/* } */ - -/* const size_t BYTES_PER_PIXEL = 4; */ -/* VkDeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL; */ -/* VkBuffer stagingBuffer; */ -/* VkDeviceMemory stagingBufferMemory; */ -/* createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, */ -/* VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, */ -/* stagingBuffer, stagingBufferMemory); */ -/* void* data; */ -/* vkMapMemory(device, stagingBufferMemory, NO_OFFSET, imageSize, NO_FLAGS, &data); */ -/* memcpy(data, pixels, static_cast(imageSize)); */ -/* vkUnmapMemory(device, stagingBufferMemory); */ - -/* stbi_image_free(pixels); */ -/* createImage(textureWidth, textureHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, */ -/* VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, */ -/* VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, */ -/* textureImage, textureImageMemory); */ - -/* transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); */ -/* copyBufferToImage(stagingBuffer, textureImage, static_cast(textureWidth), static_cast(textureHeight)); */ -/* transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); */ - -/* vkDestroyBuffer(device, stagingBuffer, nullptr); */ -/* vkFreeMemory(device, stagingBufferMemory, nullptr); */ - -/* createImageView(VK_FORMAT_R8G8B8A8_SRGB, textureImage, textureImageView, VK_IMAGE_ASPECT_COLOR_BIT); */ -/* } */ - - -void VulkanInstance::createTextureSampler(VkSampler& sampler) { - VkSamplerCreateInfo samplerCI{}; - samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; - // TODO: LINEAR or NEAREST - samplerCI.magFilter = VK_FILTER_LINEAR; - samplerCI.minFilter = VK_FILTER_LINEAR; - samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; - samplerCI.anisotropyEnable = bool2VkBool(settings.get("anisotropy_enable")); - samplerCI.maxAnisotropy = settings.get("max_anisotropy"); - samplerCI.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; - // TODO - samplerCI.unnormalizedCoordinates = VK_FALSE; - samplerCI.compareEnable = VK_FALSE; - samplerCI.compareOp = VK_COMPARE_OP_ALWAYS; - samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - samplerCI.mipLodBias = 0.0f; - samplerCI.minLod = 0.0f; - samplerCI.maxLod = 0.0f; - - VkResult result = vkCreateSampler(device, &samplerCI, nullptr, &sampler); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create texture sampler.", "createTextureSampler"); - } -} - - -// -// SHADER MODULE -// -VkShaderModule VulkanInstance::createShaderModule(const std::vector& code) { - VkShaderModuleCreateInfo smCreateInfo{}; - smCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - smCreateInfo.codeSize = code.size(); - smCreateInfo.pCode = reinterpret_cast(code.data()); - - VkShaderModule shaderModule{}; - - VkResult result = vkCreateShaderModule(device, &smCreateInfo, nullptr, &shaderModule); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to create shader module. Code size: " + std::to_string(code.size()), "createShaderModule"); - } - return shaderModule; -} - - -// -// SHADERS -// -std::vector VulkanInstance::readFile(const std::string& filename) { - std::ifstream file(filename, std::ios::ate | std::ios::binary); - - std::vector buffer; - if (file.is_open()) { - size_t fileSize = file.tellg(); - buffer.resize(fileSize); - file.seekg(0); - file.read(buffer.data(), fileSize); - - } - else { - vLog.error("readFile: Failed to read file:", filename); - } - file.close(); - - return buffer; -} - - -// -// MODELS -// -void VulkanInstance::loadModel() { - tinyobj::attrib_t attrib; - std::vector shapes; - std::vector materials; - std::string warnings, errors; - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warnings, &errors, MODEL_PATH.c_str())) { - vLog.warning("Warning from tinyobj::LoadObj: ", warnings); - throw gz::Exception("Error loading obj: " + errors, "loadModel"); - } - if (!warnings.empty()) { - vLog.warning("Warning from tinyobj::LoadObj: ", warnings); - } - std::unordered_map uniqueVertices; - for (const auto& shape : shapes) { - for (const auto& index : shape.mesh.indices) { - Vertex3D vertex; - vertex.pos = { - attrib.vertices[3 * index.vertex_index + 0], - attrib.vertices[3 * index.vertex_index + 1], - attrib.vertices[3 * index.vertex_index + 2] - }; - vertex.texCoord = { - attrib.vertices[2 * index.texcoord_index + 0], - // obj: y = 0 means bottom, vulkan: y = 0 means top - 1.0f - attrib.vertices[2 * index.texcoord_index + 1], - }; - vertex.color = { 1.0f, 1.0f, 1.0f }; - if (!uniqueVertices.contains(vertex)) { - uniqueVertices.insert({ vertex, model.vertices.size() }); - model.vertices.push_back(vertex); + bool extensionAvailable; + for (const char* requiredExtension : extensions) { + extensionAvailable = false; + for (const auto& extensionProperties : availableExtensions) { + if (strcmp(requiredExtension, extensionProperties.extensionName) == 0) { + extensionAvailable = true; + break; + } } - /* model.vertices.push_back(vertex); */ - model.indices.push_back(uniqueVertices[vertex]); + if (!extensionAvailable) { return false; } } + return true; } -} + // + // VALIDATION LAYERS + // + // check if validation layers are supported/installed + bool validationLayersSupported() { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); -// -// DEBUG -// -void VulkanInstance::setupDebugMessenger() { - // get the address of the vkCreateDebugUtilsMessengerEXT function - VkResult result = runVkResultFunction("vkCreateDebugUtilsMessengerEXT", instance, &debugUtilsMessengerCI, nullptr, &debugMessenger); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to initialise debug messenger", "setupDebugMessenger"); + bool layerAvailable; + for (const char* requiredLayer : validationLayers) { + layerAvailable = false; + for (const auto& layerProperties : availableLayers) { + if (strcmp(requiredLayer, layerProperties.layerName) == 0) { + layerAvailable = true; + break; + } + } + if (!layerAvailable) { return false; } + } + return true; } - /* auto f = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); */ - /* if (f == nullptr) { throw std::runtime_error("Failed to initialise debug messenger."); } */ - /* else { */ - /* f(instance, &debugUtilsMessengerCreateInfo, nullptr, &debugMessenger); */ - /* } */ -} - - - -void VulkanInstance::cleanupDebugMessenger() { - runVkVoidFunction("vkDestroyDebugUtilsMessengerEXT", instance, debugMessenger, nullptr); - /* throw std::runtime_error("Failed to destroy debug messenger."); */ -} // -// APP // -void VulkanInstance::createWindow() { - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); - glfwSetWindowUserPointer(window, this); - glfwSetFramebufferSizeCallback(window, frameBufferResizedCallback); -} - - -void VulkanInstance::init() { - createWindow(); - if (glfwVulkanSupported() == GLFW_FALSE) { - throw std::runtime_error("Vulkan not supported."); - } - VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("VulkanInstance", - { &instance, &physicalDevice, &device, &graphicsQ, &presentQ, &transferQ, &commandPoolGraphics, &commandPoolTransfer, &surface, &swapChain }, - { &scImages, &scImageViews, &commandBuffersBegin, &commandBuffersEnd })); - createInstance(); - setupDebugMessenger(); - createSurface(); - selectPhysicalDevice(); - setValidSettings(); - createLogicalDevice(); - - createSwapChain(); - createSwapChainImageViews(); - /* createDescriptorSetLayout(); */ - // after descriptorSetLayout - /* createGraphicsPipeline("shaders/vert.spv", "shaders/frag.spv", true, renderPass1, pipelines[PL_3D]); */ - createCommandPools(); - /* createDepthImageAndView(); // after CommandPools */ - /* createTextureImageAndView(); // after CommandPools */ - /* createFramebuffers(); // after depth image */ - /* createTextureSampler(); */ - loadModel(); - /* createVertexBuffer(VERTEX_BUFFER_SIZE); */ - /* createIndexBuffer(INDEX_BUFFER_SIZE); */ - /* createUniformBuffers(); */ - createCommandBuffers(commandBuffersBegin); - createCommandBuffers(commandBuffersEnd); - createSyncObjects(); - /* createDescriptorPool(); */ - /* createDescriptorSets(); // after UniformBuffers */ - - for (auto& obj : VulkanInstance::objectsUsingVulkan) { - obj.updateHandles(); - } -} - - -void VulkanInstance::cleanup() { - vkDeviceWaitIdle(device); - // call callbacks in reverse order - for (auto it = cleanupCallbacks.rbegin(); it != cleanupCallbacks.rend(); it++) { - (*it)(); - } - /* vLog("cleanup:", commandBuffersToSubmitThisFrame.size()); */ - - destroyCommandBuffers(commandBuffersBegin); - destroyCommandBuffers(commandBuffersEnd); - - /* vkDestroyImageView(device, textureImageView, nullptr); */ - /* vkDestroySampler(device, textureSampler, nullptr); */ - /* vkDestroyImage(device, textureImage, nullptr); */ - /* vkFreeMemory(device, textureImageMemory, nullptr); */ - - cleanupSwapChain(); - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroyFence(device, inFlightFences[i], nullptr); - } - - /* vkFreeCommandBuffers(device, commandPoolGraphics, static_cast(commandBuffers2D.size()), commandBuffers2D.data()); */ - /* vkFreeCommandBuffers(device, commandPoolGraphics, static_cast(commandBuffers3D.size()), commandBuffers3D.data()); */ - - vkDestroyCommandPool(device, commandPoolGraphics, nullptr); - vkDestroyCommandPool(device, commandPoolTransfer, nullptr); - - vkDestroyDevice(device, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - cleanupDebugMessenger(); - vkDestroyInstance(instance, nullptr); - glfwDestroyWindow(window); - glfwTerminate(); -} - - +// IMPLEMENTATION OF VULKANINSTANCE // -// APP // +// PUBLIC INTERFACE: FOR MAIN FUNCTION + void VulkanInstance::init() { + createWindow(); + if (glfwVulkanSupported() == GLFW_FALSE) { + throw std::runtime_error("Vulkan not supported."); + } + VulkanInstance::registerObjectUsingVulkan(ObjectUsingVulkan("VulkanInstance", + { &instance, &physicalDevice, &device, &graphicsQ, &presentQ, &transferQ, &commandPoolGraphics, &commandPoolTransfer, &surface, &swapChain }, + { &scImages, &scImageViews, &commandBuffersBegin, &commandBuffersEnd })); + createInstance(); + setupDebugMessenger(); + createSurface(); + selectPhysicalDevice(); + setValidSettings(); + MAX_FRAMES_IN_FLIGHT = settings.getCopyOr("max_frames_in_flight", 1); + createLogicalDevice(); -void VulkanInstance::submitThisFrame(VkCommandBuffer& cmdBuffer) { - commandBuffersToSubmitThisFrame.push_back(cmdBuffer); -} + createSwapChain(); + createSwapChainImageViews(); + createCommandPools(); + + createCommandBuffers(commandBuffersBegin); + createCommandBuffers(commandBuffersEnd); + createSyncObjects(); + /* createDescriptorPool(); */ + /* createDescriptorSets(); // after UniformBuffers */ + + for (auto& obj : VulkanInstance::objectsUsingVulkan) { + obj.updateHandles(); + } + } -uint32_t VulkanInstance::beginFrameDraw() { - vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + void VulkanInstance::cleanup() { + vkDeviceWaitIdle(device); + // call callbacks in reverse order + for (auto it = cleanupCallbacks.rbegin(); it != cleanupCallbacks.rend(); it++) { + (*it)(); + } + /* vLog.log0("cleanup:", commandBuffersToSubmitThisFrame.size()); */ + + destroyCommandBuffers(commandBuffersBegin); + destroyCommandBuffers(commandBuffersEnd); + + /* vkDestroyImageView(device, textureImageView, nullptr); */ + /* vkDestroySampler(device, textureSampler, nullptr); */ + /* vkDestroyImage(device, textureImage, nullptr); */ + /* vkFreeMemory(device, textureImageMemory, nullptr); */ + + cleanupSwapChain(); + + for (size_t i = 0; i < getMaxFramesInFlight(); i++) { + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + + /* vkFreeCommandBuffers(device, commandPoolGraphics, static_cast(commandBuffers2D.size()), commandBuffers2D.data()); */ + /* vkFreeCommandBuffers(device, commandPoolGraphics, static_cast(commandBuffers3D.size()), commandBuffers3D.data()); */ + + vkDestroyCommandPool(device, commandPoolGraphics, nullptr); + vkDestroyCommandPool(device, commandPoolTransfer, nullptr); + + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + cleanupDebugMessenger(); + vkDestroyInstance(instance, nullptr); + glfwDestroyWindow(window); + glfwTerminate(); + } + + + uint32_t VulkanInstance::beginFrameDraw() { + vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + uint32_t blockFor = 0; + /* uint64_t blockFor = UINT64_MAX; */ + VkResult result = vkAcquireNextImageKHR(device, swapChain, blockFor, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR or frameBufferResized) { // result == VK_SUBOPTIMAL_KHR or + vLog.log0("drawFrame: result:", STR_VK_RESULT(result), "frameBufferResized:", frameBufferResized); + frameBufferResized = false; + recreateSwapChain(); + return imageIndex; + } + else if (result != VK_SUBOPTIMAL_KHR and result != VK_SUCCESS) { + /* vLog.error("Failed to acquire swap chain image. Result:", STR_VK_RESULT(result)); */ + throw getVkException(result, "Failed to acquire swap chain image.", "beginFrameDraw"); + } + + vkResetFences(device, 1, &inFlightFences[currentFrame]); + + // clear image + vkResetCommandBuffer(commandBuffersBegin[currentFrame], NO_FLAGS); + VkCommandBufferBeginInfo commandBufferBI{}; + commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + result = vkBeginCommandBuffer(commandBuffersBegin[currentFrame], &commandBufferBI); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to begin clear command buffer", "beginFrameDraw"); + } + // transition to transfer dst layout + transitionImageLayout(scImages[imageIndex], scImageFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &commandBuffersBegin[currentFrame]); + VkImageMemoryBarrier2 imageBarrier{}; + imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + imageBarrier.image = scImages[imageIndex]; + imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + imageBarrier.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT; + /* imageBarrier.srcAccessMask = VK_ACCESS_2_SHADING_RATE_IMAGE_READ_BIT_NV; */ + imageBarrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageBarrier.subresourceRange.baseMipLevel = 0; + imageBarrier.subresourceRange.levelCount = 1; + imageBarrier.subresourceRange.baseArrayLayer = 0; + imageBarrier.subresourceRange.layerCount = 1; + VkDependencyInfo depI = getDepInfo(imageBarrier); + vkCmdPipelineBarrier2(commandBuffersBegin[currentFrame], &depI); + + result = vkEndCommandBuffer(commandBuffersBegin[currentFrame]); + if (result != VK_SUCCESS) { + vLog.error("Failed to record clear command buffer", "VkResult:", STR_VK_RESULT(result)); + throw getVkException(result, "Failed to record 2D - command buffer", "beginFrameDraw"); + } + commandBuffersToSubmitThisFrame.push_back(commandBuffersBegin[currentFrame]); - uint32_t imageIndex; - uint32_t blockFor = 0; - /* uint64_t blockFor = UINT64_MAX; */ - VkResult result = vkAcquireNextImageKHR(device, swapChain, blockFor, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR or frameBufferResized) { // result == VK_SUBOPTIMAL_KHR or - vLog("drawFrame: result:", STR_VK_RESULT(result), "frameBufferResized:", frameBufferResized); - frameBufferResized = false; - recreateSwapChain(); return imageIndex; } - else if (result != VK_SUBOPTIMAL_KHR and result != VK_SUCCESS) { - /* vLog.error("Failed to acquire swap chain image. Result:", STR_VK_RESULT(result)); */ - throw getVkException(result, "Failed to acquire swap chain image.", "beginFrameDraw"); + + + void VulkanInstance::endFrameDraw(uint32_t imageIndex) { + vkResetCommandBuffer(commandBuffersEnd[currentFrame], NO_FLAGS); + VkCommandBufferBeginInfo commandBufferBI{}; + commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VkResult result = vkBeginCommandBuffer(commandBuffersEnd[currentFrame], &commandBufferBI); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to begin command buffer", "beginFrameDraw"); + } + // transition to present layout + transitionImageLayout(scImages[imageIndex], scImageFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, &commandBuffersEnd[currentFrame]); + result = vkEndCommandBuffer(commandBuffersEnd[currentFrame]); + if (result != VK_SUCCESS) { + vLog.error("Failed to record clear command buffer", "VkResult:", STR_VK_RESULT(result)); + throw getVkException(result, "Failed to record command buffer", "beginFrameDraw"); + } + commandBuffersToSubmitThisFrame.push_back(commandBuffersEnd[currentFrame]); + + // submit all command buffers + VkSubmitInfo submitI{}; + submitI.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitI.waitSemaphoreCount = 1; + submitI.pWaitSemaphores = waitSemaphores; + submitI.pWaitDstStageMask = waitStages; + submitI.commandBufferCount = static_cast(commandBuffersToSubmitThisFrame.size()); + submitI.pCommandBuffers = commandBuffersToSubmitThisFrame.data(); + VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; + submitI.signalSemaphoreCount = 1; + submitI.pSignalSemaphores = signalSemaphores; + + uint32_t submitCount = 1; + result = vkQueueSubmit(graphicsQ, submitCount, &submitI, inFlightFences[currentFrame]); + if (result != VK_SUCCESS) { + vLog.error("Failed to submit draw command buffer", "VkResult:", STR_VK_RESULT(result)); + throw std::runtime_error("Failed to submit draw command buffer"); + } + commandBuffersToSubmitThisFrame.clear(); + + // present the image + VkPresentInfoKHR presentI{}; + presentI.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentI.waitSemaphoreCount = 1; + presentI.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = {swapChain}; + presentI.swapchainCount = 1; + presentI.pSwapchains = swapChains; + presentI.pImageIndices = &imageIndex; + presentI.pResults = nullptr; + vkQueuePresentKHR(presentQ, &presentI); + + currentFrame = ++currentFrame % getMaxFramesInFlight(); } - vkResetFences(device, 1, &inFlightFences[currentFrame]); - // clear image - vkResetCommandBuffer(commandBuffersBegin[currentFrame], NO_FLAGS); - VkCommandBufferBeginInfo commandBufferBI{}; - commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - result = vkBeginCommandBuffer(commandBuffersBegin[currentFrame], &commandBufferBI); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to begin clear command buffer", "beginFrameDraw"); +// PUBLIC INTERFACE: INIT AND CLEANUP + void VulkanInstance::registerSwapChainRecreateCallback(std::function callbackF) { + scRecreateCallbacks.push_back(callbackF); } - // transition to transfer dst layout - transitionImageLayout(scImages[imageIndex], scImageFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &commandBuffersBegin[currentFrame]); - VkImageMemoryBarrier2 imageBarrier{}; - imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; - imageBarrier.image = scImages[imageIndex]; - imageBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - imageBarrier.srcStageMask = VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT; - /* imageBarrier.srcAccessMask = VK_ACCESS_2_SHADING_RATE_IMAGE_READ_BIT_NV; */ - imageBarrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; - imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrier.subresourceRange.baseMipLevel = 0; - imageBarrier.subresourceRange.levelCount = 1; - imageBarrier.subresourceRange.baseArrayLayer = 0; - imageBarrier.subresourceRange.layerCount = 1; - VkDependencyInfo depI = getDepInfo(imageBarrier); - vkCmdPipelineBarrier2(commandBuffersBegin[currentFrame], &depI); - /* VkRenderPassBeginInfo renderPassBI{}; */ - /* renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; */ - /* renderPassBI.renderPass = renderPassBegin; */ - /* renderPassBI.framebuffer = scFramebuffers[imageIndex]; */ - /* renderPassBI.renderArea.offset = { 0, 0 }; */ - /* renderPassBI.renderArea.extent = scExtent; */ - /* std::array clearValues{}; */ - /* clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; */ - /* clearValues[1].depthStencil = {1.0f, 0}; */ - /* renderPassBI.clearValueCount = static_cast(clearValues.size()); */ - /* renderPassBI.pClearValues = clearValues.data(); */ - - /* vkCmdBeginRenderPass(commandBuffersBegin[imageIndex], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); */ - /* vkCmdEndRenderPass(commandBuffersBegin[imageIndex]); */ - result = vkEndCommandBuffer(commandBuffersBegin[currentFrame]); - if (result != VK_SUCCESS) { - vLog.error("Failed to record clear command buffer", "VkResult:", STR_VK_RESULT(result)); - throw getVkException(result, "Failed to record 2D - command buffer", "beginFrameDraw"); + void VulkanInstance::registerCleanupCallback(std::function callbackF) { + cleanupCallbacks.push_back(callbackF); } - commandBuffersToSubmitThisFrame.push_back(commandBuffersBegin[currentFrame]); - - return imageIndex; -} -void VulkanInstance::endFrameDraw(uint32_t imageIndex) { - vkResetCommandBuffer(commandBuffersEnd[currentFrame], NO_FLAGS); - VkCommandBufferBeginInfo commandBufferBI{}; - commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VkResult result = vkBeginCommandBuffer(commandBuffersEnd[currentFrame], &commandBufferBI); - if (result != VK_SUCCESS) { - throw getVkException(result, "Failed to begin command buffer", "beginFrameDraw"); + void VulkanInstance::registerObjectUsingVulkan(const ObjectUsingVulkan& obj) { + objectsUsingVulkan.push_back(obj); } - // transition to present layout - transitionImageLayout(scImages[imageIndex], scImageFormat, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, &commandBuffersEnd[currentFrame]); - /* VkRenderPassBeginInfo renderPassBI{}; */ - /* renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; */ - /* renderPassBI.renderPass = renderPassEnd; */ - /* renderPassBI.framebuffer = scFramebuffers[imageIndex]; */ - /* renderPassBI.renderArea.offset = { 0, 0 }; */ - /* renderPassBI.renderArea.extent = scExtent; */ - /* vkCmdBeginRenderPass(commandBuffersEnd[imageIndex], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE); */ - /* vkCmdEndRenderPass(commandBuffersEnd[imageIndex]); */ - result = vkEndCommandBuffer(commandBuffersEnd[currentFrame]); - if (result != VK_SUCCESS) { - vLog.error("Failed to record clear command buffer", "VkResult:", STR_VK_RESULT(result)); - throw getVkException(result, "Failed to record command buffer", "beginFrameDraw"); + + + +// PUBLIC INTERFACE: VARIOUS UTILITY + void VulkanInstance::submitThisFrame(VkCommandBuffer& cmdBuffer) { + commandBuffersToSubmitThisFrame.push_back(cmdBuffer); } - commandBuffersToSubmitThisFrame.push_back(commandBuffersEnd[currentFrame]); - VkSubmitInfo submitI{}; - submitI.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; - VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - submitI.waitSemaphoreCount = 1; - submitI.pWaitSemaphores = waitSemaphores; - submitI.pWaitDstStageMask = waitStages; - submitI.commandBufferCount = static_cast(commandBuffersToSubmitThisFrame.size()); - submitI.pCommandBuffers = commandBuffersToSubmitThisFrame.data(); - VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; - submitI.signalSemaphoreCount = 1; - submitI.pSignalSemaphores = signalSemaphores; - - uint32_t submitCount = 1; - result = vkQueueSubmit(graphicsQ, submitCount, &submitI, inFlightFences[currentFrame]); - if (result != VK_SUCCESS) { - vLog.error("Failed to submit draw command buffer", "VkResult:", STR_VK_RESULT(result)); - throw std::runtime_error("Failed to submit draw command buffer"); + void VulkanInstance::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { + VkCommandBuffer cmdBuffer = beginSingleTimeCommands(POOL_TRANSFER); + VkBufferCopy copyRegion{}; + copyRegion.srcOffset = 0; + copyRegion.dstOffset = 0; + copyRegion.size = size; + uint32_t copyRegionCount = 1; + vkCmdCopyBuffer(cmdBuffer, srcBuffer, dstBuffer, copyRegionCount, ©Region); + endSingleTimeCommands(cmdBuffer, POOL_TRANSFER); } - commandBuffersToSubmitThisFrame.clear(); - VkPresentInfoKHR presentI{}; - presentI.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentI.waitSemaphoreCount = 1; - presentI.pWaitSemaphores = signalSemaphores; + VkCommandBuffer VulkanInstance::beginSingleTimeCommands(InstanceCommandPool commandPool) { + VkCommandBufferAllocateInfo allocI{}; + allocI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocI.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + if (commandPool == POOL_TRANSFER) { + allocI.commandPool = commandPoolTransfer; + } + else { + allocI.commandPool = commandPoolGraphics; + } + allocI.commandBufferCount = 1; + + VkCommandBuffer cmdBuffer; + vkAllocateCommandBuffers(device, &allocI, &cmdBuffer); + + VkCommandBufferBeginInfo beginI{}; + beginI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginI.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(cmdBuffer, &beginI); + return cmdBuffer; + } + + + void VulkanInstance::endSingleTimeCommands(VkCommandBuffer cmdBuffer, InstanceCommandPool commandPool) { + VkResult result = vkEndCommandBuffer(cmdBuffer); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to end commandBuffer", "endSingleTimeCommands"); + } + + VkCommandPool cmdPool; + VkQueue q; + if (commandPool == POOL_TRANSFER) { + cmdPool = commandPoolTransfer; + q = transferQ; + } + else { + cmdPool = commandPoolGraphics; + q = graphicsQ; + } + + VkSubmitInfo submitI{}; + submitI.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitI.commandBufferCount = 1; + submitI.pCommandBuffers = &cmdBuffer; + uint32_t submitCount = 1; + result = vkQueueSubmit(q, submitCount, &submitI, VK_NULL_HANDLE); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to submit commandBuffer", "endSingleTimeCommands"); + } + result = vkQueueWaitIdle(q); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to wait for queue idle", "endSingleTimeCommands"); + } + const uint32_t commandBufferCount = 1; + vkFreeCommandBuffers(device, cmdPool, commandBufferCount, &cmdBuffer); + } + + +// TODO + // DEPTH + /* VkFormat VulkanInstance::findDepthFormat() { */ + /* return findSupportedFormat({ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT }, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); */ + /* } */ + + +// PUBLIC INTERFACE: CREATION AND DESTRUCTION + // COMMAND BUFFER + void VulkanInstance::createCommandBuffers(std::vector& commandBuffers) { + commandBuffers.resize(getMaxFramesInFlight()); + + VkCommandBufferAllocateInfo commandBufferAI{}; + commandBufferAI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandBufferAI.commandPool = commandPoolGraphics; + commandBufferAI.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandBufferAI.commandBufferCount = static_cast(commandBuffers.size()); + VkResult result = vkAllocateCommandBuffers(device, &commandBufferAI, commandBuffers.data()); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create command buffer", "createCommandBuffer"); + } + } + + void VulkanInstance::destroyCommandBuffers(std::vector& commandBuffers) { + vkFreeCommandBuffers(device, commandPoolGraphics, static_cast(commandBuffers.size()), commandBuffers.data()); + commandBuffers.resize(0); + } + + + // DESCRIPTORS SET LAYOUTS + void VulkanInstance::createDescriptorSetLayout(std::vector bindings, VkDescriptorSetLayout& layout) { + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; + descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCI.bindingCount = static_cast(bindings.size()); + descriptorSetLayoutCI.pBindings = bindings.data(); + + + VkResult result = vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &layout); + if (result != VK_SUCCESS) { + throw getVkException(result, "Could not create descriptorSetLayout", "createDescriptorSetLayout"); + } + /* vLog.log0("createDescriptorSetLayout: Created descriptor set layout."); */ + } + + + // GRAPHICS PIPELINE + template + void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline) { + auto vertShaderCode = readFile(vertexShader); + auto fragShaderCode = readFile(fragmentShader); + + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); + + VkPipelineShaderStageCreateInfo vertexShaderStageCI{}; + vertexShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertexShaderStageCI.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertexShaderStageCI.module = vertShaderModule; + vertexShaderStageCI.pName = "main"; + /* vssCreateInfo.pSpecializationInfo = nullptr; */ + + VkPipelineShaderStageCreateInfo fragmentShaderStageCI{}; + fragmentShaderStageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragmentShaderStageCI.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragmentShaderStageCI.module = fragShaderModule; + fragmentShaderStageCI.pName = "main"; + /* vssCreateInfo.pSpecializationInfo = nullptr; */ + + VkPipelineShaderStageCreateInfo shaderStagesCI[] = { vertexShaderStageCI, fragmentShaderStageCI }; + + VkVertexInputBindingDescription bindingD = VertexT::getBindingDescription(); + // array + auto attributeD = VertexT::getAttributeDescriptions(); + + VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; + vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputStateCI.vertexBindingDescriptionCount = 1; + vertexInputStateCI.pVertexBindingDescriptions = &bindingD; + vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(attributeD.size());; + vertexInputStateCI.pVertexAttributeDescriptions = attributeD.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{}; + inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssemblyStateCI.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(scExtent.width); + viewport.height = static_cast(scExtent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = scExtent; + + VkPipelineViewportStateCreateInfo viewportStateCI{}; + viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportStateCI.viewportCount = 1; + viewportStateCI.pViewports = &viewport; + viewportStateCI.scissorCount = 1; + viewportStateCI.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizerCI{}; + rasterizerCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizerCI.depthClampEnable = VK_FALSE; + rasterizerCI.rasterizerDiscardEnable = VK_FALSE; + rasterizerCI.polygonMode = VK_POLYGON_MODE_FILL; + rasterizerCI.lineWidth = 1.0f; + rasterizerCI.cullMode = VK_CULL_MODE_BACK_BIT; + // not clockwise because of inverted y axis from glm + rasterizerCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizerCI.depthBiasEnable = VK_FALSE; + /* rasterizerCI.depthBiasConstantFactor = 0.0f; */ + /* rasterizerCI.depthBiasClamp = 0.0f; */ + /* rasterizerCI.depthBiasSlopeFactor = 0.0f; */ + + VkPipelineMultisampleStateCreateInfo multisampleStateCI{}; + multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampleStateCI.sampleShadingEnable = VK_FALSE; + multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + /* multisampleStateCI.minSampleShading = 1.0f; */ + /* multisampleStateCI.pSampleMask = nullptr; */ + /* multisampleStateCI.alphaToCoverageEnable = VK_FALSE; */ + /* multisampleStateCI.alphaToOneEnable = VK_FALSE; */ + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + /* colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; */ + /* colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; */ + /* colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; */ + /* colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; */ + /* colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; */ + /* colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; */ + + VkPipelineColorBlendStateCreateInfo colorBlendCI{}; + colorBlendCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlendCI.logicOpEnable = VK_FALSE; + /* colorBlendCI.logicOp = VK_LOGIC_OP_COPY; */ + colorBlendCI.attachmentCount = 1; + colorBlendCI.pAttachments = &colorBlendAttachment; + colorBlendCI.blendConstants[0] = 0.0f; + colorBlendCI.blendConstants[1] = 0.0f; + colorBlendCI.blendConstants[2] = 0.0f; + colorBlendCI.blendConstants[3] = 0.0f; + + /* std::vector dynamicStates = { */ + /* VK_DYNAMIC_STATE_VIEWPORT, */ + /* VK_DYNAMIC_STATE_LINE_WIDTH */ + /* }; */ + /* VkPipelineDynamicStateCreateInfo dynamicState{}; */ + /* dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; */ + /* dynamicState.dynamicStateCount = static_cast(dynamicStates.size()); */ + /* dynamicState.pDynamicStates = dynamicStates.data(); */ + + VkPipelineDepthStencilStateCreateInfo depthStencilCI{}; + depthStencilCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencilCI.depthTestEnable = VK_TRUE; + depthStencilCI.depthWriteEnable = VK_TRUE; + depthStencilCI.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencilCI.depthBoundsTestEnable = VK_FALSE; + /* depthStencilCI.minDepthBounds = 0.0f; */ + /* depthStencilCI.maxDepthBounds = 1.0f; */ + depthStencilCI.stencilTestEnable = VK_FALSE; + /* depthStencilCI.front = {}; */ + /* depthStencilCI.back = {}; */ + + VkPipelineLayoutCreateInfo pipelineLayoutCI{}; + pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + /* pipelineLayoutCI.pushConstantRangeCount = 0; */ + /* pipelineLayoutCI.pPushConstantRanges = nullptr; */ + pipelineLayoutCI.setLayoutCount = static_cast(descriptorSetLayouts.size()); + pipelineLayoutCI.pSetLayouts = descriptorSetLayouts.data(); + VkResult result = vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipeline.layout); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create pipeline layout", "createGraphicsPipeline"); + } + + VkGraphicsPipelineCreateInfo pipelineCI{}; + pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineCI.stageCount = 2; + pipelineCI.pStages = shaderStagesCI; + pipelineCI.pVertexInputState = &vertexInputStateCI; + pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; + pipelineCI.pViewportState = &viewportStateCI; + pipelineCI.pRasterizationState = &rasterizerCI; + pipelineCI.pMultisampleState = &multisampleStateCI; + if (useDepthStencil) { + pipelineCI.pDepthStencilState = &depthStencilCI; + } + else { + pipelineCI.pDepthStencilState = nullptr; + } + pipelineCI.pColorBlendState = &colorBlendCI; + /* pipelineCI.pDynamicState = nullptr; */ + pipelineCI.layout = pipeline.layout; + pipelineCI.renderPass = renderPass; + pipelineCI.subpass = 0; + /* pipelineCI.basePipelineHandle = VK_NULL_HANDLE; */ + /* pipelineCI.basePipelineIndex = -1; */ + + result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineCI, nullptr, &pipeline.pipeline); + if (result != VK_SUCCESS) { + throw getVkException(result, "Could not create graphics pipeline", "createGraphicsPipeline"); + } + + vkDestroyShaderModule(device, vertShaderModule, nullptr); + vkDestroyShaderModule(device, fragShaderModule, nullptr); + + vLog.log0("createGraphicsPipeline: Created graphics pipeline."); + } + template void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); + template void VulkanInstance::createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); + + + + // SHADER MODULE + VkShaderModule VulkanInstance::createShaderModule(const std::vector& code) { + VkShaderModuleCreateInfo smCreateInfo{}; + smCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + smCreateInfo.codeSize = code.size(); + smCreateInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule{}; + + VkResult result = vkCreateShaderModule(device, &smCreateInfo, nullptr, &shaderModule); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create shader module. Code size: " + std::to_string(code.size()), "createShaderModule"); + } + return shaderModule; + } + + + // BUFFER + void VulkanInstance::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& memI, VkSharingMode sharingMode, std::vector* qFamiliesWithAccess) { + VkBufferCreateInfo bufferCI{}; + bufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCI.size = size; + bufferCI.usage = usage; + bufferCI.sharingMode = sharingMode; + std::vector queueFamiliesWithAccess; + if (sharingMode == VK_SHARING_MODE_CONCURRENT) { + if (qFamiliesWithAccess == nullptr) { + throw VkUserError("Sharing mode is VK_SHARING_MODE_CONCURRENT but qFamiliesWithAccess is nullptr", "createBuffer"); + } + bufferCI.queueFamilyIndexCount = static_cast(qFamiliesWithAccess->size()); + bufferCI.pQueueFamilyIndices = qFamiliesWithAccess->data(); + } + + VkResult result = vkCreateBuffer(device, &bufferCI, nullptr, &buffer); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create buffer", "createBuffer"); + } + + VkMemoryRequirements memoryRequirements{}; + vkGetBufferMemoryRequirements(device, buffer, &memoryRequirements); + + VkMemoryAllocateInfo memoryAI{}; + memoryAI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memoryAI.allocationSize = memoryRequirements.size; + VkMemoryPropertyFlags wantedProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, wantedProperties); + + allocator.allocate(memoryAI, memI); + + vkBindBufferMemory(device, buffer, memI.memory, memI.offset); + } + + + void VulkanInstance::destroyBuffer(VkBuffer& buffer, MemoryInfo& bufferMemory) { + vkDestroyBuffer(device, buffer, NO_ALLOC); + allocator.free(bufferMemory); + buffer = VK_NULL_HANDLE; + } + + + // VERTEX BUFFER + template + void VulkanInstance::createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, MemoryInfo& vertexBufferMemory, VkDeviceSize& vertexBufferSize) { + vertexBufferSize = vertexCount * sizeof(VertexT); + + // create vertex buffer + VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE; + std::vector qFamiliesWithAccess; + if (qFamilyIndices.transferFamily.has_value()) { // prefer dedicated transfer family + qFamiliesWithAccess = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.transferFamily.value() }; + } + createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory , sharingMode, &qFamiliesWithAccess); + + vLog.log0("createVertexBuffer: Created vertex buffer with size", vertexBufferSize); + } + template void VulkanInstance::createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, MemoryInfo& vertexBufferMemory, VkDeviceSize& vertexBufferSize); + template void VulkanInstance::createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, MemoryInfo& vertexBufferMemory, VkDeviceSize& vertexBufferSize); + + + // INDEX BUFFER + template + void VulkanInstance::createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, MemoryInfo& indexBufferMemory, VkDeviceSize& indexBufferSize) { + indexBufferSize = indexCount * sizeof(T); + + // create index buffer + VkSharingMode sharingMode = VK_SHARING_MODE_EXCLUSIVE; + std::vector qFamiliesWithAccess; + if (qFamilyIndices.transferFamily.has_value()) { // prefer dedicated transfer family + qFamiliesWithAccess = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.transferFamily.value() }; + } + createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory, sharingMode, &qFamiliesWithAccess); + + vLog.log0("createIndexBuffer: Created index buffer with size:", indexBufferSize); + } + template void VulkanInstance::createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, MemoryInfo& indexBufferMemory, VkDeviceSize& indexBufferSize); + template void VulkanInstance::createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, MemoryInfo& indexBufferMemory, VkDeviceSize& indexBufferSize); + + + // FRAMEBUFFERS + void VulkanInstance::createFramebuffers(std::vector& framebuffers, std::vector& imageViews, VkRenderPass& renderPass) { + framebuffers.resize(scImageViews.size()); + + VkFramebufferCreateInfo framebufferCI{}; + framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferCI.renderPass = renderPass; + framebufferCI.width = scExtent.width; + framebufferCI.height = scExtent.height; + framebufferCI.layers = 1; + + for (size_t i = 0; i < framebuffers.size(); i++) { + // TODO include depth image view for 3D + std::vector attachments = { imageViews[i] }; //, depthImageView }; + framebufferCI.pAttachments = attachments.data(); + framebufferCI.attachmentCount = static_cast(attachments.size()); + + VkResult result = vkCreateFramebuffer(device, &framebufferCI, nullptr, &framebuffers[i]); + if (result != VK_SUCCESS) { + throw getVkException(result, "Could not create framebuffer", "createFramebuffers_"); + } + } + vLog.log0("createFramebuffers: Created", framebuffers.size(), "framebuffers."); + } + + + void VulkanInstance::destroyFramebuffers(std::vector& framebuffers) { + for (auto& framebuffer : framebuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + } + + + // TEXTURE SAMPLER + void VulkanInstance::createTextureSampler(VkSampler& sampler) { + VkSamplerCreateInfo samplerCI{}; + samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + // TODO: LINEAR or NEAREST + samplerCI.magFilter = VK_FILTER_LINEAR; + samplerCI.minFilter = VK_FILTER_LINEAR; + samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCI.anisotropyEnable = bool2VkBool(settings.get("anisotropy_enable")); + samplerCI.maxAnisotropy = settings.get("max_anisotropy"); + samplerCI.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + // TODO + samplerCI.unnormalizedCoordinates = VK_FALSE; + samplerCI.compareEnable = VK_FALSE; + samplerCI.compareOp = VK_COMPARE_OP_ALWAYS; + samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCI.mipLodBias = 0.0f; + samplerCI.minLod = 0.0f; + samplerCI.maxLod = 0.0f; + + VkResult result = vkCreateSampler(device, &samplerCI, nullptr, &sampler); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create texture sampler.", "createTextureSampler"); + } + } + +// PUBLIC INTERFACE: IMAGE UTILITY + void VulkanInstance::createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory) { + VkImageCreateInfo imageCI{}; + imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCI.imageType = VK_IMAGE_TYPE_2D; + imageCI.extent.width = static_cast(width); + imageCI.extent.height = static_cast(height); + imageCI.extent.depth = 1; + imageCI.mipLevels = 1; + imageCI.arrayLayers = 1; + imageCI.format = format; + // use linear when direct texel access is needed + imageCI.tiling = tiling; + imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCI.usage = usage; + imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCI.samples = VK_SAMPLE_COUNT_1_BIT; + /* imageCI.flags = 0; */ + VkResult result = vkCreateImage(device, &imageCI, NO_ALLOC, &image); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create image", "createImage"); + } + + VkMemoryRequirements memoryRequirements; + vkGetImageMemoryRequirements(device, image, &memoryRequirements); + + VkMemoryAllocateInfo memoryAI{}; + memoryAI.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memoryAI.allocationSize = memoryRequirements.size; + memoryAI.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, memoryProperties); + + MemoryInfo memI; + allocator.allocate(memoryAI, memI); + /* result = vkAllocateMemory(device, &memoryAI, NO_ALLOC, &imageMemory); */ + + result = vkBindImageMemory(device, image, memI.memory, memI.offset); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create image", "createImage"); + } + } + + + // IMAGE VIEW + void VulkanInstance::createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags) { + VkImageViewCreateInfo imageViewCI{}; + imageViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewCI.format = format; + imageViewCI.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + imageViewCI.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + imageViewCI.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + imageViewCI.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + imageViewCI.subresourceRange.aspectMask = aspectFlags; + imageViewCI.subresourceRange.baseMipLevel = 0; + imageViewCI.subresourceRange.levelCount = 1; + imageViewCI.subresourceRange.baseArrayLayer = 0; + imageViewCI.subresourceRange.layerCount = 1; + imageViewCI.image = image; + VkResult result = vkCreateImageView(device, &imageViewCI, nullptr, &imageView); + if (result != VK_SUCCESS) { + throw getVkException(result, "Could not create image view", "createImageViews"); + } + } + + + void VulkanInstance::copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height) { + VkCommandBuffer cmdBuffer = beginSingleTimeCommands(POOL_TRANSFER); + VkBufferImageCopy region{}; + region.bufferOffset = NO_OFFSET; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + + region.imageOffset = { offsetX, offsetY, 0 }; + region.imageExtent = { width, height, 1 }; + const uint32_t regionCount = 1; + vkCmdCopyBufferToImage(cmdBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount, ®ion); + + endSingleTimeCommands(cmdBuffer, POOL_TRANSFER); + } + + + void VulkanInstance::copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, VkExtent2D extent) { + VkImageCopy region{}; + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.srcSubresource.mipLevel = 0; + region.srcSubresource.baseArrayLayer = 0; + region.srcSubresource.layerCount = 1; + + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.dstSubresource.mipLevel = 0; + region.dstSubresource.baseArrayLayer = 0; + region.dstSubresource.layerCount = 1; + + /* std::array offsets; */ + /* offsets[0] = { 0, 0, 0 }; */ + /* offsets[1] = { 0, 0, 0 }; */ + region.srcOffset = { 0, 0, 0 }; + region.dstOffset = { 0, 0, 0 }; + region.extent = { extent.width, extent.height, 1 }; + const uint32_t regionCount = 1; + vkCmdCopyImage(cmdBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount, ®ion); + } + + + + bool hasStencilComponent(VkFormat format) { + return format == VK_FORMAT_D32_SFLOAT_S8_UINT or format == VK_FORMAT_D24_UNORM_S8_UINT; + } + void VulkanInstance::transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandBuffer* cmdBuffer) { + VkImageMemoryBarrier2 barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier.oldLayout = oldLayout; + barrier.newLayout = newLayout; + // not using barrier for queue famlily ownership transfer + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = 0; + + InstanceCommandPool commandPool; + if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and + newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + if (hasStencilComponent(format)) { + barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; + } + + barrier.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; + commandPool = POOL_GRAPHICS; + } + else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED and + newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + barrier.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + commandPool = POOL_TRANSFER; + } + else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and + newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + commandPool = POOL_GRAPHICS; + } + else if (oldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL and + newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + + barrier.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + commandPool = POOL_GRAPHICS; + } + else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL and + newLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + + barrier.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + commandPool = POOL_GRAPHICS; + } + else { + throw VkUserError(std::string("transitionImageLayout: Unsupported layout transition") + STR_VK_IMAGE_LAYOUT(oldLayout) + "->" + STR_VK_IMAGE_LAYOUT(newLayout), "transitionImageLayout");; + } + // if not provided, get a single time command buffer + VkCommandBuffer cmdBuffer_; + if (cmdBuffer == nullptr) { + cmdBuffer_ = beginSingleTimeCommands(commandPool); + } + else { + cmdBuffer_ = *cmdBuffer; + } + VkDependencyInfo depI = getDepInfo(barrier); + vkCmdPipelineBarrier2(cmdBuffer_, &depI); + + if (cmdBuffer == nullptr) { + endSingleTimeCommands(cmdBuffer_, commandPool); + } + } + +// +// STATIC: DEBUG LOG +// + gz::Log VulkanInstance::vLog("vulkan.log", true, true, "Vulkan", VULKAN_MESSAGE_PREFIX_COLOR, true, VULKAN_MESSAGE_TIME_COLOR); + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanInstance::debugLog( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverety, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) { + + // Set message color + std::string type; + std::vector colors; + switch(messageType) { + case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: + type = "General:"; + colors.push_back(gz::Color::GREEN); + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: + type = "Performance:"; + colors.push_back(gz::Color::CYAN); + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: + type = "Validation:"; + colors.push_back(gz::Color::MAGENTA); + break; + default: + type = "None:"; + colors.push_back(gz::Color::WHITE); + } + + // color for second keyword / whole message if not validation message + switch(messageSeverety) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + colors.push_back(gz::Color::RESET); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + colors.push_back(gz::Color::WHITE); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + colors.push_back(gz::Color::BG_YELLOW); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + colors.push_back(gz::Color::BG_RED); + break; + default: + colors.push_back(gz::Color::RESET); + } + + + std::string_view message(pCallbackData->pMessage); + std::string_view messageIdName(pCallbackData->pMessageIdName); + // extra differenciation between different validation messages + // is validation or performance message: get type from brackets, print owner of any pointers + if (messageType & (VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)) { + // if cant be found npos + 1 = 0 + size_t startAt = message.find(']') + 1; + if (messageIdName.starts_with("UNASSIGNED-DEBUG-PRINTF")) { + type = "Shader:"; + colors[0] = gz::Color::LI_CYAN; + startAt = message.find_first_of('|'); + if (startAt == std::string::npos) { startAt = 0; } + else { startAt += 2; } // skip "| " + } + else if (messageIdName.starts_with("UNASSIGNED-BestPractices")) { + type = "Best Practice:"; + colors[0] = gz::Color::LI_GREEN; + } + else if (messageIdName.starts_with("SYNC-HAZARD")) { + type = "Synchronization"; + colors[0] = gz::Color::LI_RED; + } + message = std::string_view(message.begin() + startAt, message.end()); + + colors.push_back(colors[1]); // color for messageIdName, use also for brackets + colors.push_back(colors[1]); + // color for actual message + if (lastColorWhite) { + colors.push_back(gz::Color::RESET); + } + else { + colors.push_back(gz::Color::WHITE); + + } + // color for handleOwnerString + colors.push_back(gz::Color::LI_CYAN); + getHandleOwnerString(message); + vLog.clog(colors, std::move(type), "[", messageIdName, "]", message, handleOwnerString); + } + else { + vLog.clog(colors, std::move(type), message); + } + lastColorWhite = !lastColorWhite; + if (throwExceptionOnValidationError and messageSeverety == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + throw VkException("Validation error:" + std::string(message), "VulkanInstance::debugLog"); + } + return VK_FALSE; + } + + + +// +// PRIVATE MEMBERS +// +// INSTANCE + void VulkanInstance::createInstance() { + + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pEngineName = "Glowzwiebel Engine"; + appInfo.pApplicationName = "Hallo Dreieck"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.apiVersion = VK_API_VERSION_1_3; + + VkInstanceCreateInfo instanceCI{}; + instanceCI.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCI.pApplicationInfo = &appInfo; + instanceCI.pNext = &debugUtilsMessengerCI; + instanceCI.flags = 0; + instanceCI.ppEnabledExtensionNames = instanceExtensions.data(); + instanceCI.enabledExtensionCount = instanceExtensions.size(); + + if (enableValidationLayers) { + if (!validationLayersSupported()) { + throw std::runtime_error("Validation layers enabled but not available."); + } + instanceCI.enabledLayerCount = static_cast(validationLayers.size()); + instanceCI.ppEnabledLayerNames = validationLayers.data(); + } + else { + instanceCI.enabledLayerCount = 0; + instanceCI.ppEnabledLayerNames = nullptr; + } + + std::vector requiredExtensions = getRequiredExtensions(); + instanceCI.enabledExtensionCount = static_cast(requiredExtensions.size()); + instanceCI.ppEnabledExtensionNames = requiredExtensions.data();; + + // log requiredExtensions + std::string message; + message.reserve(80); + message += "Required extensions ("; + message += std::to_string(instanceCI.enabledExtensionCount) + ')'; + for (uint32_t i = 0; i < requiredExtensions.size(); i++) { + message += "\n\t"; + message += std::to_string(i + 1) + ": " + requiredExtensions[i]; + } + vLog.log3(message); + + VkResult result = vkCreateInstance(&instanceCI, nullptr, &instance); + if (result != VK_SUCCESS) { + vLog.log3("Failed to create instance.", "VkResult:", STR_VK_RESULT(result)); + throw std::runtime_error("Failed to create instance."); + } + + uint32_t extensionCount = 0; + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); // get count + std::vector extensions(extensionCount); + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); + + // log available extensions + message = "Available extensions ("; + message += std::to_string(extensionCount) + "):"; + for (uint32_t i = 0; i < extensions.size(); i++) { + message += "\n\t"; + message += std::to_string(i + 1) + ": " + extensions[i].extensionName; + } + vLog.log3(message); + + if (!checkRequiredExtensionsAvailable(requiredExtensions, extensions)) { + throw std::runtime_error("Not all required extensions are available."); + } + } + + + + +// PHYSICAL DEVICE + PhysicalDeviceFeatures VulkanInstance::getRequiredFeatures() const { + PhysicalDeviceFeatures requiredFeatures; + requiredFeatures.f13.synchronization2 = VK_TRUE; + return requiredFeatures; + } + + + void VulkanInstance::selectPhysicalDevice() { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + if (deviceCount == 0) { + vLog.error("Could not find any GPU."); + throw std::runtime_error("Could not find any GPU."); + } + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + + // find best gpu + std::vector deviceScores; + for (const auto& device : devices) { + deviceScores.push_back(rateDevice(device)); + } + unsigned int bestGPUIndex = std::max_element(deviceScores.begin(), deviceScores.end()) - deviceScores.begin(); + if (deviceScores[bestGPUIndex] == 0) { + vLog.error("Could not find suitable GPU."); + throw std::runtime_error("Could not find suitable GPU."); + } + physicalDevice = devices[bestGPUIndex]; + + vkGetPhysicalDeviceProperties(physicalDevice, &phDevProperties); + vkGetPhysicalDeviceFeatures2(physicalDevice, &phDevFeatures.f); + qFamilyIndices = findQueueFamilies(physicalDevice); + vLog("Selected GPU:", phDevProperties.deviceName, "with the following queue family indices:", qFamilyIndices); + } + + + const unsigned int SCORE_DISCRETE_GPU = 5000; + const unsigned int SCORE_INTEGRATED_GPU = 2000; + const unsigned int SCORE_VIRTUAL_GPU = 1000; + const unsigned int SCORE_HAS_ALL_QUEUE_FAMILIES = 100; + const unsigned int SCORE_HAS_FEATURE = 100; + + unsigned int VulkanInstance::rateDevice(VkPhysicalDevice device) { + unsigned int score; + + // rate type + VkPhysicalDeviceProperties properties{}; + vkGetPhysicalDeviceProperties(device, &properties); + switch(properties.deviceType) { + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + score = SCORE_DISCRETE_GPU; + break; + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: + score = SCORE_INTEGRATED_GPU; + break; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + score = SCORE_VIRTUAL_GPU; + break; + default: + return 0; + } + + qFamilyIndices = findQueueFamilies(device); + // has needed queue families + if (qFamilyIndices.hasAllValues()) { + score += SCORE_HAS_ALL_QUEUE_FAMILIES; + } + else if (!qFamilyIndices.hasNecessaryValues()) { + return 0; + } + + // supports all extensions + if (!deviceExtensionsSupported(device)) { + return 0; + } + + // supports all features + PhysicalDeviceFeatures requiredFeatures = getRequiredFeatures(); + PhysicalDeviceFeatures features; + vLog("features sType", STR_VK_STRUCTURE_TYPE(features.f.sType)); + vkGetPhysicalDeviceFeatures2(device, &features.f); + if (!features.requiredFeaturesAvailable(requiredFeatures)) { + return 0; + } + // optional features + if (features.f.features.samplerAnisotropy == VK_TRUE) { score += SCORE_HAS_FEATURE; } + + // swap chain support + SwapChainSupport scDetails = querySwapChainSupport(device); + if (scDetails.formats.empty() or scDetails.presentModes.empty()) { + return 0; + } + + // rate memory + VkPhysicalDeviceMemoryProperties memProperties{}; + vkGetPhysicalDeviceMemoryProperties(device, &memProperties); + for (uint32_t i = 0; i < memProperties.memoryHeapCount; i++) { + score += memProperties.memoryHeaps[i].size / 1e6; + } + + + vLog("rateDevice: GPU: ", properties.deviceName, " - Score: ", score); + return score; + } + + + VkFormat VulkanInstance::findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { + for (const VkFormat& format : candidates) { + VkFormatProperties formatProperties; + vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); + if (tiling == VK_IMAGE_TILING_LINEAR and + (formatProperties.linearTilingFeatures & features) == features) { + return format; + } + else if (tiling == VK_IMAGE_TILING_OPTIMAL and + (formatProperties.optimalTilingFeatures & features) == features) { + return format; + } + } + throw VkException(std::string("Could not find a suitable format. tiling: ") + STR_VK_IMAGE_TILING(tiling) + ".", "findSupportedFormat"); + } + + +// SETTINGS + void VulkanInstance::setValidSettings() { + // IMPORTANT: EDIT DOCUMENTATION WHEN ADDING/CHANGING VALUES + // anisotropic filtering + settings.setAllowedValues("anisotropy_enable", { false, vkBool2Bool(phDevFeatures.f.features.samplerAnisotropy) }, gz::SM_LIST); + settings.setAllowedValues("max_anisotropy", { 1.0f, phDevProperties.limits.maxSamplerAnisotropy }, gz::SM_RANGE); + settings.setAllowedValues("max_frames_in_flight", { 1, 4 }, SM_RANGE); + } + + +// QUEUE + QueueFamilyIndices VulkanInstance::findQueueFamilies(VkPhysicalDevice device) { + QueueFamilyIndices indices; + + uint32_t qFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &qFamilyCount, nullptr); + std::vector qFamilies(qFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &qFamilyCount, qFamilies.data()); + vLog("findQueueFamilies: found", qFamilyCount, "queue families:"); + + VkBool32 presentSupport = false; + for (unsigned int i = 0; i < qFamilyCount; i++) { + // check for queue with graphic capabilities + if (qFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + else if (qFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT) { // only transfer, not graphics + indices.transferFamily = i; + } + + // check for surface support + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + if (presentSupport == VK_TRUE) { + indices.presentFamily = i; + } + + vLog("findQueueFamilies:", i, "-", qFamilies[i].queueFlags); + if (indices.hasAllValues()) { + vLog("findQueueFamilies: Found all wanted families."); + break; + } + } + return indices; + } + + +// LOGICAL DEVICE + void VulkanInstance::createLogicalDevice() { + + std::unordered_set qFamiliyIndicesSet = { // must be unique + qFamilyIndices.graphicsFamily.value(), qFamilyIndices.presentFamily.value(), + }; + if (qFamilyIndices.transferFamily.has_value()) { + qFamiliyIndicesSet.insert(qFamilyIndices.transferFamily.value()); + } + + std::vector deviceQueueCI; + for (uint32_t index : qFamiliyIndicesSet) { + deviceQueueCI.push_back(VkDeviceQueueCreateInfo()); + deviceQueueCI.back().sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCI.back().queueFamilyIndex = index; + deviceQueueCI.back().queueCount = 1; + float qPriority = 1.0f; + deviceQueueCI.back().pQueuePriorities = &qPriority; + } + + PhysicalDeviceFeatures enabledFeatures = getRequiredFeatures(); + // optional features + enabledFeatures.f.features.samplerAnisotropy = bool2VkBool(settings.getOr("anisotropy_enable", false)); + + VkDeviceCreateInfo deviceCI{}; + deviceCI.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCI.pQueueCreateInfos = deviceQueueCI.data(); + deviceCI.queueCreateInfoCount = static_cast(deviceQueueCI.size()); + deviceCI.pNext = &enabledFeatures.f; + /* deviceCI.pEnabledFeatures = &deviceFeatures.f; */ + deviceCI.enabledExtensionCount = static_cast(extensions.size()); + deviceCI.ppEnabledExtensionNames = extensions.data(); + + VkResult result = vkCreateDevice(physicalDevice, &deviceCI, nullptr, &device); + if (result != VK_SUCCESS) { + vLog.error("createLogicalDevice: Failed to create logical device.", "VkResult:", STR_VK_RESULT(result)); + throw std::runtime_error("createLogicalDevice: Failed to create logical device."); + } + + // get handles + uint32_t index = 0; + vkGetDeviceQueue(device, qFamilyIndices.graphicsFamily.value(), index, &graphicsQ); + vkGetDeviceQueue(device, qFamilyIndices.presentFamily.value(), index, &presentQ); + if (qFamilyIndices.transferFamily.has_value()) { + vkGetDeviceQueue(device, qFamilyIndices.transferFamily.value(), index, &transferQ); + } + else { + transferQ = graphicsQ; + } + vLog.log0("createLogicalDevice: Created logical device."); + } + + +// SURFACE + void VulkanInstance::createSurface() { + VkResult result = glfwCreateWindowSurface(instance, window, nullptr, &surface); + if (result != VK_SUCCESS) { + vLog.error("createSurface: Failed to create window surface.", "VkResult:", STR_VK_RESULT(result)); + throw std::runtime_error("createSurface: Failed to create window surface."); + } + } + + +// SWAP CHAIN + SwapChainSupport VulkanInstance::querySwapChainSupport(VkPhysicalDevice physicalDevice) { + SwapChainSupport scDetails{}; + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr); + scDetails.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, scDetails.formats.data()); + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr); + scDetails.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, scDetails.presentModes.data()); + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &scDetails.capabilities); + + return scDetails; + } + + + VkSurfaceFormatKHR VulkanInstance::selectSwapChainSurfaceFormat(const std::vector& availableFormats) { + vLog.log0("selectSwapChainSurfaceFormat:", availableFormats.size(), "formats available."); + for (const auto& format : availableFormats) { + if (format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + return format; + } + } + return availableFormats.front(); + } + + + VkPresentModeKHR VulkanInstance::selectSwapChainPresentMode(const std::vector& availableModes) { + for (const auto& mode : availableModes) { + if (mode == VK_PRESENT_MODE_MAILBOX_KHR) { + return mode; + } + } + return VK_PRESENT_MODE_FIFO_KHR; + } + + + VkExtent2D VulkanInstance::selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { + // if width = max(uint32_t) then surface size will be determined by a swapchain targeting it + if (capabilities.currentExtent.width == std::numeric_limits::max()) { + int width, height; + glfwGetFramebufferSize(window, &width, &height); + VkExtent2D actualExtent {}; + actualExtent.width = std::clamp(static_cast(width), capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = std::clamp(static_cast(height), capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + return actualExtent; + } + else { + return capabilities.currentExtent; + } + } + + + void VulkanInstance::createSwapChain() { + SwapChainSupport scDetails = querySwapChainSupport(physicalDevice); + VkSurfaceFormatKHR surfaceFormat = selectSwapChainSurfaceFormat(scDetails.formats); + + uint32_t imageCount = scDetails.capabilities.minImageCount + 1; + // maxImageCount = 0 -> no max + if (scDetails.capabilities.maxImageCount > 0 and imageCount < scDetails.capabilities.maxImageCount) { + imageCount = scDetails.capabilities.maxImageCount; + } + + scImageFormat = surfaceFormat.format; + scExtent = selectSwapExtent(scDetails.capabilities); + + VkSwapchainCreateInfoKHR swapChainCI{}; + swapChainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapChainCI.surface = surface; + swapChainCI.minImageCount = imageCount; + swapChainCI.imageFormat = scImageFormat; + swapChainCI.imageColorSpace = surfaceFormat.colorSpace; + swapChainCI.imageExtent = scExtent; + swapChainCI.imageArrayLayers = 1; + // if postProcessing: VK_IMAGE_USAGE_TRANSFER_DST_BIT + swapChainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + /* QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); */ + // must live until vkCreateSwapchainKHR + std::vector qIndices; + if (qFamilyIndices.graphicsFamily.value() != qFamilyIndices.presentFamily.value()) { + swapChainCI.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + qIndices = { qFamilyIndices.graphicsFamily.value(), qFamilyIndices.presentFamily.value() }; + swapChainCI.queueFamilyIndexCount = static_cast(qIndices.size()); + swapChainCI.pQueueFamilyIndices = qIndices.data(); + } + else { + swapChainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + swapChainCI.preTransform = scDetails.capabilities.currentTransform; + swapChainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapChainCI.presentMode = selectSwapChainPresentMode(scDetails.presentModes); + swapChainCI.clipped = VK_TRUE; + swapChainCI.oldSwapchain = VK_NULL_HANDLE; + + VkResult result = vkCreateSwapchainKHR(device, &swapChainCI, nullptr, &swapChain); + if (result != VK_SUCCESS) { + vLog.error("createSwapChain: Couldn not create swap chain", "VkResult:", STR_VK_RESULT(result)); + throw std::runtime_error("createSwapChain: Couldn not create swap chain"); + } + + // get image handles + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + scImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapChain, &imageCount, scImages.data()); + vLog.log0("createSwapChain: Created swap chain."); + } + + + void VulkanInstance::recreateSwapChain() { + int width, height = 0; + glfwGetFramebufferSize(window, &width, &height); + while (width == 0 || height == 0) { // minimized + glfwGetFramebufferSize(window, &width, &height); + glfwWaitEvents(); + } + vLog.log0("recreateSwapChain: new framebuffer size:", width, height); + vkDeviceWaitIdle(device); + cleanupSwapChain(); + + createSwapChain(); + createSwapChainImageViews(); + + createCommandBuffers(commandBuffersBegin); + createCommandBuffers(commandBuffersEnd); + + for (auto& f : scRecreateCallbacks) { + f(); + } + vLog.log0("recreateSwapChain: Updating all ObjectsUsingVulkan"); + for (auto& obj : VulkanInstance::objectsUsingVulkan) { + obj.updateHandles(); + } + } + + + void VulkanInstance::cleanupSwapChain() { + for (auto& imageView : scImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + + + void VulkanInstance::createSwapChainImageViews() { + scImageViews.resize(scImages.size()); + for (unsigned int i = 0; i < scImageViews.size(); i++) { + createImageView(scImageFormat, scImages[i], scImageViews[i], VK_IMAGE_ASPECT_COLOR_BIT); + } + vLog.log0("createImageViews: Created", scImageViews.size(), "image views."); + } + + + uint32_t VulkanInstance::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) { + VkPhysicalDeviceMemoryProperties memoryProperties{}; + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { + // if ith bit of typefilter is set + if ((typeFilter & (1 << i)) and (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + throw VkException("Could not find suitable memory type", "findMemoryType"); + } + + + void VulkanInstance::frameBufferResizedCallback(GLFWwindow* window, int width, int height) { + VulkanInstance* app = reinterpret_cast(glfwGetWindowUserPointer(window)); + app->frameBufferResized = true; + } + + +// COMMAND POOLS + void VulkanInstance::createCommandPools() { + /* QueueFamilyIndicjes queueFamilyIndices = findQueueFamilies(physicalDevice); */ + VkCommandPoolCreateInfo commandPoolGraphicsCI{}; + commandPoolGraphicsCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + commandPoolGraphicsCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + commandPoolGraphicsCI.queueFamilyIndex = qFamilyIndices.graphicsFamily.value(); + VkResult result = vkCreateCommandPool(device, &commandPoolGraphicsCI, nullptr, &commandPoolGraphics); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create graphics command pool", "createCommandPool"); + } + + if (qFamilyIndices.transferFamily.has_value()) { + VkCommandPoolCreateInfo commandPoolTransferCI{}; + commandPoolTransferCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + commandPoolTransferCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + commandPoolTransferCI.queueFamilyIndex = qFamilyIndices.transferFamily.value(); + result = vkCreateCommandPool(device, &commandPoolTransferCI, nullptr, &commandPoolTransfer); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create transfer command pool", "createCommandPool"); + } + } + else { + commandPoolTransfer = commandPoolGraphics; + } + } + + +// SYNCHRONIZATION + void VulkanInstance::createSyncObjects() { + imageAvailableSemaphores.resize(getMaxFramesInFlight()); + renderFinishedSemaphores.resize(getMaxFramesInFlight()); + inFlightFences.resize(getMaxFramesInFlight()); + + VkSemaphoreCreateInfo semaphoreCI{}; + semaphoreCI.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceCI{}; + fenceCI.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCI.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < getMaxFramesInFlight(); i++) { + VkResult result = vkCreateSemaphore(device, &semaphoreCI, nullptr, &imageAvailableSemaphores[i]); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create imageAvailableSemaphores", "createSyncObjects"); + } + result = vkCreateSemaphore(device, &semaphoreCI, nullptr, &renderFinishedSemaphores[i]); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create renderFinishedSemaphores", "createSyncObjects"); + } + result = vkCreateFence(device, &fenceCI, nullptr, &inFlightFences[i]); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to create inFlightFences", "createSyncObjects"); + } + } + } + + + /* void VulkanInstance::createDepthImageAndView() { */ + /* VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; */ + /* VkImageUsageFlags imageUsage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; */ + /* VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL; */ + /* VkFormat depthFormat = findDepthFormat(); */ + + /* createImage(scExtent.width, scExtent.height, depthFormat, tiling, imageUsage, memoryProperties, depthImage, depthImageMemory); */ + /* createImageView(depthFormat, depthImage, depthImageView, VK_IMAGE_ASPECT_DEPTH_BIT); */ + + /* transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); */ + + + /* } */ + + +// SHADERS + std::vector VulkanInstance::readFile(const std::string& filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + + std::vector buffer; + if (file.is_open()) { + size_t fileSize = file.tellg(); + buffer.resize(fileSize); + file.seekg(0); + file.read(buffer.data(), fileSize); + + } + else { + vLog.error("readFile: Failed to read file:", filename); + } + file.close(); + + return buffer; + } + + +// MODEL + void VulkanInstance::loadModel(VerticesAndIndices& model) { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + std::string warnings, errors; + if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warnings, &errors, MODEL_PATH.c_str())) { + vLog.warning("Warning from tinyobj::LoadObj: ", warnings); + throw gz::Exception("Error loading obj: " + errors, "loadModel"); + } + if (!warnings.empty()) { + vLog.warning("Warning from tinyobj::LoadObj: ", warnings); + } + std::unordered_map uniqueVertices; + for (const auto& shape : shapes) { + for (const auto& index : shape.mesh.indices) { + Vertex3D vertex; + vertex.pos = { + attrib.vertices[3 * index.vertex_index + 0], + attrib.vertices[3 * index.vertex_index + 1], + attrib.vertices[3 * index.vertex_index + 2] + }; + vertex.texCoord = { + attrib.vertices[2 * index.texcoord_index + 0], + // obj: y = 0 means bottom, vulkan: y = 0 means top + 1.0f - attrib.vertices[2 * index.texcoord_index + 1], + }; + vertex.color = { 1.0f, 1.0f, 1.0f }; + if (!uniqueVertices.contains(vertex)) { + uniqueVertices.insert({ vertex, model.vertices.size() }); + model.vertices.push_back(vertex); + } + /* model.vertices.push_back(vertex); */ + model.indices.push_back(uniqueVertices[vertex]); + } + } + } + + +// DEBUG + void VulkanInstance::setupDebugMessenger() { + // get the address of the vkCreateDebugUtilsMessengerEXT function + VkResult result = runVkResultFunction("vkCreateDebugUtilsMessengerEXT", instance, &debugUtilsMessengerCI, nullptr, &debugMessenger); + if (result != VK_SUCCESS) { + throw getVkException(result, "Failed to initialise debug messenger", "setupDebugMessenger"); + } + /* auto f = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); */ + /* if (f == nullptr) { throw std::runtime_error("Failed to initialise debug messenger."); } */ + /* else { */ + /* f(instance, &debugUtilsMessengerCreateInfo, nullptr, &debugMessenger); */ + /* } */ + } + + + void VulkanInstance::cleanupDebugMessenger() { + runVkVoidFunction("vkDestroyDebugUtilsMessengerEXT", instance, debugMessenger, nullptr); + /* throw std::runtime_error("Failed to destroy debug messenger."); */ + } + + +// WINDOW + void VulkanInstance::createWindow() { + glfwInit(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); + glfwSetWindowUserPointer(window, this); + glfwSetFramebufferSizeCallback(window, frameBufferResizedCallback); + } + + +// HANDLE OWNERSHIP + bool VulkanInstance::lastColorWhite; + std::vector VulkanInstance::objectsUsingVulkan; + + const std::regex reAddress(R"(0x[0-9a-f]+)"); + std::string VulkanInstance::handleOwnerString(100, ' '); // reserve size 100 + // + void VulkanInstance::getHandleOwnerString(std::string_view message) { + handleOwnerString.clear(); + re::svmatch match; + if (re::regex_search(message, match, reAddress)) { + handleOwnerString = "Handle ownerships: [ "; + bool foundHandle = false; + for (auto it = match.begin(); it != match.end(); it++) { + uint64_t address; + std::stringstream ss; + ss << std::hex << it->str(); + ss >> address; + for (auto& obj : objectsUsingVulkan) { + /* vLog(address, obj.handles); */ + if (obj.handles.contains(address)) { + handleOwnerString += it->str() + " - " + obj.objectName; + address = 0; + foundHandle = true; + } + } + if (address != 0) { + handleOwnerString += "Unknown"; + } + handleOwnerString += ", "; + } + if (foundHandle) { + handleOwnerString.erase(handleOwnerString.size()-2); + handleOwnerString += " ]"; + } + else { + handleOwnerString.clear(); + } + } + } + - VkSwapchainKHR swapChains[] = {swapChain}; - presentI.swapchainCount = 1; - presentI.pSwapchains = swapChains; - presentI.pImageIndices = &imageIndex; - presentI.pResults = nullptr; - vkQueuePresentKHR(presentQ, &presentI); - currentFrame = ++currentFrame % MAX_FRAMES_IN_FLIGHT; -} } // namespace gz::vk diff --git a/src/vulkan_instance.hpp b/src/vulkan_instance.hpp index 9c3ae47..679121b 100644 --- a/src/vulkan_instance.hpp +++ b/src/vulkan_instance.hpp @@ -11,6 +11,7 @@ #include "vertex.hpp" #include "shape.hpp" #include "vulkan_util.hpp" +#include "vulkan_allocator.hpp" #include @@ -23,11 +24,10 @@ #include namespace gz::vk { - + constexpr bool throwExceptionOnValidationError = true; constexpr gz::Color VULKAN_MESSAGE_PREFIX_COLOR = gz::Color::BO_BLUE; constexpr gz::Color VULKAN_MESSAGE_TIME_COLOR = gz::Color::BLUE; const std::string CONFIG_FILE = "vulkan.conf"; - const int MAX_FRAMES_IN_FLIGHT = 3; #define SettingsTypes uint32_t, bool, float /* const std::string MODEL_PATH = "models/gazebo-3d-model/gazebo.obj"; */ @@ -40,6 +40,7 @@ namespace gz::vk { { "framerate", "60" }, { "anisotropy_enable", "false" }, { "max_anisotropy", "1" }, + { "max_frames_in_flight", "3" }, /* { "", "" } */ }; @@ -51,14 +52,22 @@ namespace gz::vk { constexpr VkAllocationCallbacks* NO_ALLOC = nullptr; const size_t VERTEX_BUFFER_SIZE = 512; const size_t INDEX_BUFFER_SIZE = 512; + enum InstanceCommandPool { + POOL_GRAPHICS, POOL_TRANSFER + }; + + /** + * @nosubgrouping + */ class VulkanInstance { - friend class Renderer; - friend class Renderer2D; - friend class Renderer3D; - friend class TextureManager; - friend class TextureAtlas; public: - VulkanInstance(gz::SettingsManagerCreateInfosmCI) : settings(smCI) {}; + /** + * @name Public Interface: For the "creator" of the instance + * @details + * These functions will probably called from your `main` function and loop. + */ + /// @{ + VulkanInstance(gz::SettingsManagerCreateInfosmCI) : settings(smCI), allocator(*this) {}; /** * @brief Initializes the vulkan instance * @details @@ -87,9 +96,19 @@ namespace gz::vk { * -# Call endFrameDraw */ uint32_t beginFrameDraw(); - void submitThisFrame(VkCommandBuffer& cmdBuffer); void endFrameDraw(uint32_t imageIndex); - GLFWwindow* window; + void cleanup(); + const GLFWwindow* getWindow() const { return window; } + /// @} + + /** + * @name Public Interface: init and cleanup + * @details + * These functions can be used by other parts of your program that use this instance, + * like a renderer. + */ + public: + /// @{ /** * @brief Destroys everything that was initalized in init * @details @@ -99,182 +118,91 @@ namespace gz::vk { * -# destroys the window * -# calls glfwTerminate() */ - void cleanup(); void registerCleanupCallback(std::function callbackF); + void registerSwapChainRecreateCallback(std::function callbackF); - /// The frame in the swap chain that is currently drawn to - uint32_t currentFrame = 0; - - // - // SETTINGS - // - gz::SettingsManager settings; - private: - std::vector commandBuffersToSubmitThisFrame; - - void createWindow(); - - std::vector> cleanupCallbacks; - std::vector> scRecreateCallbacks; - - // - // INSTANCE - // - VkInstance instance; /** - * @brief Create the vulkan instance + * @brief Register vulkan handles used by another object * @details - * -# check if validationLayers are available (if enabled) - * -# create instance with info - * -# check if all extensions required by glfw are available + * Use this function to register vulkan handles used by another object. + * When a @ref debugLog "validation message" contains one of these handles, the owner can + * be determined and added to the message. + * This makes the debugging process easier when multiple objects use the same type of vulkan handle. */ - void createInstance(); + static void registerObjectUsingVulkan(const ObjectUsingVulkan& obj); + /// @} - // - // PHYSICAL DEVICE - // - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - /// all the properties of the selected physcial device - VkPhysicalDeviceProperties phDevProperties; - /// all the features that the selected physical device supports - PhysicalDeviceFeatures phDevFeatures; - /** - * @brief Get rhe required physical device features - * @details - * The required features are: - * - synchronization2 - */ - PhysicalDeviceFeatures getRequiredFeatures() const; - /** - * @brief Assign the physicalDevice handle to the @ref rateDevice "best rated" GPU - * @details - * After this method, physicalDevice, phDevProperties and phDevFeatures will be initialized - */ - void selectPhysicalDevice(); - /** - * @brief Assigns a score to a physical device. - * @details - * score = GPU_TYPE_FACTOR + VRAM_SIZE (in MB) + SUPPORTED_QUEUES_FACTOR + FEATURES_FACTORS - * A GPU is unsuitable and gets score 0 if it does not: - * - have the needed queue families - * - support all necessary extensions - * - have swap chain support - */ - unsigned int rateDevice(VkPhysicalDevice device); - /** - * @brief Set valid values for the SettingsManager according to phDevFeatures and phDevProperties - * @details - * Must be called after selectPhysicalDevice and before createLogicalDevice - * Sets valid values for: - * - anisotropy_enable - * - max_anisotropy - * - */ - void setValidSettings(); - /** - * @brief Find the best of the supported formats - * @param candidates Candidate format, from best to worst - * @returns The first format from candidates that is supported - */ - VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); + /** + * @name Public Interface: Various utility + * @details + * These functions can be used by other parts of your program that use this instance, + * like a renderer. + */ + public: + /// @{ + const VkDevice& getDevice() const { return device; } + const SettingsManager& getSettings() const { return settings; } + const VkExtent2D& getScExtent() const { return scExtent; } + const std::vector& getScImages() const { return scImages; } + const VkFormat& getScImageFormat() const { return scImageFormat; } - // - // QUEUE - // - QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); - QueueFamilyIndices qFamilyIndices; - VkQueue graphicsQ; - VkQueue presentQ; - // if possible, use dedicated transferQ. if not available, transferQ = graphicsQ - VkQueue transferQ; - // - // LOGICAL DEVICE - // - VkDevice device; /** - * @details - * request anisotropic sampling feature + * @brief Get the maximum number of frames in flight + * @see MAX_FRAMES_IN_FLIGHT */ - void createLogicalDevice(); + const uint32_t getMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; } + /** + * @brief Get the frame that is currently in flight + * @see currentFrame + */ + const uint32_t getCurrentFrame() const { return currentFrame; } - // - // SURFACE - // - VkSurfaceKHR surface; - void createSurface(); - // - // SWAP CHAIN - // - VkSwapchainKHR swapChain; - std::vector scImages; - std::vector scImageViews; - VkFormat scImageFormat; - VkExtent2D scExtent; + void submitThisFrame(VkCommandBuffer& cmdBuffer); - SwapChainSupport querySwapChainSupport(VkPhysicalDevice device); /** - * @todo Rate formats if preferred is not available - */ - VkSurfaceFormatKHR selectSwapChainSurfaceFormat(const std::vector& availableFormats); - /** - * @todo Check settings for preferred mode - */ - VkPresentModeKHR selectSwapChainPresentMode(const std::vector& availableModes); - VkExtent2D selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); - void createSwapChain(); - void createSwapChainImageViews(); - void cleanupSwapChain(); - /** - * @brief Recreate the swap chain for the current window size + * @brief Copy from srcBuffer to dstBuffer * @details - * Does: - * -# get new window dimensions - * - blocks while dimensions == 0 (minimized) - * -# calls cleanupSwapChain - * -# calls createSwapChain - * -# other stuff - * -# calls all callbacks registered with registerSwapChainRecreateCallback + * Uses @ref beginSingleTimeCommands() "a single time command buffer" from commandPoolTransfer. */ - void recreateSwapChain(); - // - // IMAGE VIEW - // + void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size); + /** - * @brief Create a 2D imageView with format for image. + * @brief Begin a command buffer that is going to be used once + * @param commandPool: The command pool from which the buffer should be allocated */ - void createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags); - // - // RENDER PASS - // - /* VkRenderPass renderPassBegin; */ - /* VkRenderPass renderPassEnd; */ + VkCommandBuffer beginSingleTimeCommands(InstanceCommandPool commandPool); /** - * @brief Create the render pass - * @details - * The subpass will contain the following attachments - * -# color attachment - * -# depth stencil attachment + * @brief Submit cmdBuffer on the queue and free it afterwards + * @param cmdBuffer: Command buffer returned by beginSingleTimeCommands() + * @param commandPool: The same pool as passed to beginSingleTimeCommands() */ - /* void createRenderPassBegin(); */ - /* void createRenderPassEnd(); */ - // - // DESCRIPTORS - // - /* VkDescriptorSetLayout descriptorSetLayout; */ + void endSingleTimeCommands(VkCommandBuffer cmdBuffer, InstanceCommandPool commandPool); + /// @} + + /** + * @name Public Interface: Vulkan object creation/descruction + * @details + * These functions aim at making creation and desctruction of common vulkan objects easier + * and can be used by other parts of your program that use this instance, + * like a renderer. + */ + public: + /// @{ + /** + * @brief Create MAX_FRAMES_IN_FLIGHT command buffers from the commandPoolGraphics + */ + void createCommandBuffers(std::vector& commandBuffers); + /** + * @brief Destroy all command buffers (must be from commandPoolGraphics) + */ + void destroyCommandBuffers(std::vector& commandBuffers); + /** * @brief Create a descriptor layout with bindings */ void createDescriptorSetLayout(std::vector bindings, VkDescriptorSetLayout& layout); - /** - * @} - */ - // - // PIPELINE - // - /* void createGraphicsPipeline(); */ - VkShaderModule createShaderModule(const std::vector& code); /** * @brief Create a new graphics pipeline * @param vertexShader Path to the SPIR-V vertex shader @@ -283,6 +211,8 @@ namespace gz::vk { * @param renderPass The (already created) render pass * @param pipelineLayout Pipeline layout handle to bind * @param pipeline Pipeline handle to bind + * @param T the type of Vertex to use + * * @details * Create a pipeline with: * - 2 shader stages: vertex and fragment shader @@ -292,95 +222,83 @@ namespace gz::vk { * - rasterizer: * - triangles are filled with the colors from the vertex (VK_POLYGON_FILL) * - counter clockwise front face (VK_FRONT_FACE_COUNTER_CLOCKWISE) - * - * */ template void createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, std::vector& descriptorSetLayouts, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline); - // - // FONT - // - // TODO - /* VkPipeline fontPipeline; */ - /* VkPipelineLayout fontPipelineLayout; */ - /* void createFontPipeline(); */ - // - // BUFFERS - // - void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory, VkSharingMode sharingMode=VK_SHARING_MODE_EXCLUSIVE, std::vector* qFamiliesWithAccess=nullptr); - uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + + VkShaderModule createShaderModule(const std::vector& code); + /** - * @brief Begin a command buffer that is going to be used once - * @param commandPool: The command pool from which the buffer should be allocated + * @brief Allocate memory, create a buffer and bind buffer to memory + * @param size The size of the buffer + * @param buffer The (null) handle to the buffer + * @param bufferMemory Reference to an (uninitialized) MemoryInfo struct. It will be valid when the function returns + * @param properties Properties that the buffers memory needs to satisfy */ - VkCommandBuffer beginSingleTimeCommands(VkCommandPool commandPool); + void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, MemoryInfo& bufferMemory, VkSharingMode sharingMode=VK_SHARING_MODE_EXCLUSIVE, std::vector* qFamiliesWithAccess=nullptr); /** - * @brief Submit cmdBuffer on the queue and free it afterwards - * @param cmdBuffer: Command buffer returned by beginSingleTimeCommands() - * @param q: The queue belonging to the commandPool parameter from beginSingleTimeCommands() + * @brief Destroy buffer and free memory */ - void endSingleTimeCommands(VkCommandBuffer cmdBuffer, VkCommandPool commandPool, VkQueue q); - /** - * @brief Copy from srcBuffer to dstBuffer - */ - void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size); - // - // VERTEX BUFFER - // + void destroyBuffer(VkBuffer& buffer, MemoryInfo& bufferMemory); + /** * @brief Create a vertex buffer * @param vertexCount Number of vertices the buffer should contain - * @todo Basically the same as createIndexBuffer + * @param vertexBuffer The (null) handle to the buffer + * @param vertexBufferMemory Reference to an (uninitialized) MemoryInfo struct. It will be valid when the function returns + * @details + * The function is instantiated for Vertex2D and Vertex3D */ template - void createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize); - // - // INDEX BUFFER - // + void createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, MemoryInfo& vertexBufferMemory, VkDeviceSize& vertexBufferSize); + + /** + * @brief Create an index buffer + * @param indexCount Number of indices the buffer should contain + * @param indexBuffer The (null) handle to the buffer + * @param indexBufferMemory Reference to an (uninitialized) MemoryInfo struct. It will be valid when the function returns + * @details + * The function is instantiated for uint16_t and uint32_t + */ template - void createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize); - // - // UNIFORM BUFFER - // + void createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, MemoryInfo& indexBufferMemory, VkDeviceSize& indexBufferSize); + + /** + * @brief Create MAX_FRAMES_IN_FLIGHT uniform buffers + */ template - void createUniformBuffers(std::vector& uniformBuffers, std::vector& uniformBuffersMemory); - /* void createUniformBuffers(); */ - // - // FRAMEBUFFERS - // + void createUniformBuffers(std::vector& uniformBuffers, std::vector& uniformBuffersMemory); + + /** + * @brief Create as many framebuffers as the swap chain has images, with the extent of the swap chain + */ + void createFramebuffers(std::vector& framebuffers, std::vector& imageViews, VkRenderPass& renderPass); /** * @brief Destroy the framebuffers */ void destroyFramebuffers(std::vector& framebuffers); - void createFramebuffers(std::vector& framebuffers, std::vector& imageViews, VkRenderPass& renderPass); - bool frameBufferResized = false; - static void frameBufferResizedCallback(GLFWwindow* window, int width, int height); - // - // COMMAND POOL - // - VkCommandPool commandPoolGraphics; - VkCommandPool commandPoolTransfer; - void createCommandPools(); - // - // COMMAND BUFFER - // + /** - * @brief Create MAX_FRAMES_IN_FLIGHT command buffers from the commandPoolGraphics + * @brief Create a texture sampler + * @todo add options and documentation */ - void createCommandBuffers(std::vector& commandBuffers); - /** - * @brief Destroy all command buffers (must be from commandPoolGraphics) - */ - void destroyCommandBuffers(std::vector& commandBuffers); - std::vector commandBuffersBegin; - std::vector commandBuffersEnd; - // - // IMAGE UTILITY - // + void createTextureSampler(VkSampler& sampler); + + /** + * @name Public Interface: Image utility + * @details + * These functions can be used by other parts of your program that use this instance, + * like a renderer. + */ + /// @{ /** * @brief Create the image, allocate the imageMemory and bind the image to it */ void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory); + /** + * @brief Create a 2D imageView with format for image. + */ + void createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags); void copyBufferToImage(VkBuffer buffer, VkImage image, int32_t offsetX, int32_t offsetY, uint32_t width, uint32_t height); /** * @todo make a version using vkCmdResolveImage for multisampled images @@ -416,69 +334,15 @@ namespace gz::vk { * @throws VkUserError if the transition is not supported. */ void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandBuffer* cmdBuffer=nullptr); - - // - // DEPTH - // - /* VkImage depthImage; */ - /* VkDeviceMemory depthImageMemory; */ - /* VkImageView depthImageView; */ - VkFormat findDepthFormat(); - /* void createDepthImageAndView(); */ - // - // TEXTURES - // - /* VkImage textureImage; */ - /* VkDeviceMemory textureImageMemory; */ - /* VkImageView textureImageView; */ - /* VkSampler textureSampler; */ - /// Load textures/image.png - /* void createTextureImageAndView(); */ - void createTextureSampler(VkSampler& sampler); - - // - // SYNCHRONIZATION - // - std::vector imageAvailableSemaphores; - std::vector renderFinishedSemaphores; - std::vector inFlightFences; - void createSyncObjects(); - // - // SHADERS - // - static std::vector readFile(const std::string& filename); - // - // MODELS - // - void loadModel(); - VerticesAndIndices model; - // - // DEBUG - // - void setupDebugMessenger(); - void cleanupDebugMessenger(); - VkDebugUtilsMessengerEXT debugMessenger; - static gz::Log vLog; - public: - // - // UTILITY - // - /** - * @name Debugging - */ - /// @{ - /** - * @name Handle ownership - * @brief Retrieve the owner of a vulkan handle - * @see ObjectUsingVulkan - */ - /// @{ - static std::vector objectsUsingVulkan; - static void registerObjectUsingVulkan(const ObjectUsingVulkan& obj); - static void getHandleOwnerString(std::string_view message); - static std::string handleOwnerString; - static bool lastColorWhite; /// @} + + + + /** + * @name Debugging (validation messages) + */ + public: + /// @{ /** * @brief Log messages from validation layers with the Apps logger * @details @@ -486,7 +350,7 @@ namespace gz::vk { * - Validation and performace messages usually have a bracket containing a VUID, * these brackets are printed in yellow/red when its a warning/error. * - Handles printed by the validation layer are checked with objectsUsingVulkan, if found - * the owner of the handle is added to the message @see registerObjectUsingVulkan, ObjectUsingVulkan + * the owner of the handle is added to the message @see registerObjectUsingVulkan(), ObjectUsingVulkan * - Different message sources (layer, shader, general...) get a prefix with a different color */ static VKAPI_ATTR VkBool32 VKAPI_CALL debugLog( @@ -496,29 +360,293 @@ namespace gz::vk { void* pUserData); /// @} + private: + /** + * @name Settings + */ + /// @{ + gz::SettingsManager settings; + /** + * @brief Set valid values for the SettingsManager according to phDevFeatures and phDevProperties + * @details + * Must be called after selectPhysicalDevice() and before createLogicalDevice() + * Sets valid values for: + * - anisotropy_enable (bool) [features.samplerAnisotropy] + * - max_anisotropy (float) [1-limits.maxSamplerAnisotropy] + * - max_frames_in_flight (uint32_t) [1-4] + * + */ + void setValidSettings(); + /** + * @brief The maximum number of frames that can be ready for presentation + * @details + * A higher number can lead to more stable frame rates, but also increases latency. + * + * This variable is assigned from setting "max_frames_in_flight". + * @warning + * Do not use the setting from the settings manager: + * A lot of resources depend on this variable and if it gets changed at runtime, the program would crash. + * Always use this variable (or the @ref getMaxFramesInFlight() "getter"). + * Changes to max_frames_in_flight will take effect upon restart. + */ + uint32_t MAX_FRAMES_IN_FLIGHT; + /// @} + /** + * @name Window + */ + /// @{ + GLFWwindow* window; + void createWindow(); + /// @} + + /** + * @name Framebuffers + */ + /// @{ + bool frameBufferResized = false; + static void frameBufferResizedCallback(GLFWwindow* window, int width, int height); + + /** + * @brief The frame that is currently in flight + * @details + * The value is between 0 and MAX_FRAMES_IN_FLIGHT - 1. + * It specifies which command buffer or uniform buffer to use for drawing the current frame, + * it is not the image index for the current @ref scImages "swap chain image". + */ + uint32_t currentFrame = 0; + /// @} + + /** + * @name Cleanup + */ + /// @{ + std::vector> cleanupCallbacks; + std::vector> scRecreateCallbacks; + /// @} + + /** + * @name Instance + */ + /// @{ + VkInstance instance; + /** + * @brief Create the vulkan instance + * @details + * -# check if validationLayers are available (if enabled) + * -# create instance with info + * -# check if all extensions required by glfw are available + */ + void createInstance(); + /// @} + + /** + * @name Physical device + */ + /// @{ + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + /// all the properties of the selected physcial device + VkPhysicalDeviceProperties phDevProperties; + /// all the features that the selected physical device supports + PhysicalDeviceFeatures phDevFeatures; + /** + * @brief Get the required physical device features + * @details + * The required features are: + * - synchronization2 + */ + PhysicalDeviceFeatures getRequiredFeatures() const; + /** + * @brief Assign the physicalDevice handle to the @ref rateDevice "best rated" GPU + * @details + * After this method, physicalDevice, phDevProperties and phDevFeatures will be initialized + */ + void selectPhysicalDevice(); + /** + * @brief Assigns a score to a physical device. + * @details + * score = GPU_TYPE_FACTOR + VRAM_SIZE (in MB) + SUPPORTED_QUEUES_FACTOR + FEATURES_FACTORS + * A GPU is unsuitable and gets score 0 if it does not: + * - have the needed queue families + * - support all necessary extensions + * - have swap chain support + */ + unsigned int rateDevice(VkPhysicalDevice device); + /** + * @brief Find the best of the supported formats + * @param candidates Candidate format, from best to worst + * @returns The first format from candidates that is supported + */ + VkFormat findSupportedFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); + /// @} + + /** + * @name Memory management + */ + /// @{ + uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); + VulkanAllocator allocator; + /// @} + + /** + * @name Queue + */ + /// @{ + QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); + QueueFamilyIndices qFamilyIndices; + VkQueue graphicsQ; + VkQueue presentQ; + // if possible, use dedicated transferQ. if not available, transferQ = graphicsQ + VkQueue transferQ; + /// @} + + /** + * @name Logical device + */ + /// @{ + VkDevice device; + /** + * @details + * request anisotropic sampling feature + */ + void createLogicalDevice(); + /// @} + + /** + * @name Surface + */ + /// @{ + VkSurfaceKHR surface; + void createSurface(); + /// @} + + /** + * @name Swap chain + */ + /// @{ + VkSwapchainKHR swapChain; + std::vector scImages; + std::vector scImageViews; + VkFormat scImageFormat; + VkExtent2D scExtent; + + SwapChainSupport querySwapChainSupport(VkPhysicalDevice device); + /** + * @todo Rate formats if preferred is not available + */ + VkSurfaceFormatKHR selectSwapChainSurfaceFormat(const std::vector& availableFormats); + /** + * @todo Check settings for preferred mode + */ + VkPresentModeKHR selectSwapChainPresentMode(const std::vector& availableModes); + VkExtent2D selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); + void createSwapChain(); + void createSwapChainImageViews(); + void cleanupSwapChain(); + /** + * @brief Recreate the swap chain for the current window size + * @details + * Does: + * -# get new window dimensions + * - blocks while dimensions == 0 (minimized) + * -# calls cleanupSwapChain + * -# calls createSwapChain + * -# other stuff + * -# calls all callbacks registered with registerSwapChainRecreateCallback + */ + void recreateSwapChain(); + /// @} + + /** + * @name Command pools + */ + /// @{ + VkCommandPool commandPoolGraphics; + VkCommandPool commandPoolTransfer; + void createCommandPools(); + /// @} + /** + * @name Command buffers + */ + /// @{ + std::vector commandBuffersBegin; + std::vector commandBuffersEnd; + /** + * @brief All command buffers to submit on graphics queue for the current frame + * @details + * Use submitThisFrame() to submit a command buffer + */ + std::vector commandBuffersToSubmitThisFrame; + /// @} + + /** + * @name Synchronization + */ + /// @{ + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + void createSyncObjects(); + /// @} + + + /** + * @name Shaders + */ + /// @{ + /** + * @brief Load a binary file into vector of bytes + * @todo Move to more generic constant, does not need to be member function + */ + static std::vector readFile(const std::string& filename); + /// @} + + /** + * @name Models + */ + /// @{ + void loadModel(VerticesAndIndices& model); + /// @} + + + /** + * @name Utility + */ + /// @{ template - VkResult runVkResultFunction(const char* name, Args&&... args) { - auto f = reinterpret_cast(vkGetInstanceProcAddr(instance, name)); - if (f == nullptr) { - vLog.error("getVkFunction: Could not get function:", name); - throw std::runtime_error("getVkFunction: Could not get function."); - } - else { - return f(std::forward(args)...); - } - }; + VkResult runVkResultFunction(const char* name, Args&&... args); template - void runVkVoidFunction(const char* name, Args&&... args) { - auto f = reinterpret_cast(vkGetInstanceProcAddr(instance, name)); - if (f == nullptr) { - vLog.error("getVkFunction: Could not get function:", name); - throw std::runtime_error("getVkFunction: Could not get function."); - } - else { - f(std::forward(args)...); - } - } + void runVkVoidFunction(const char* name, Args&&... args); + + /** + * @name Debugging: (log and validation debug messenger) + */ + /// @{ + void setupDebugMessenger(); + void cleanupDebugMessenger(); + VkDebugUtilsMessengerEXT debugMessenger; + static gz::Log vLog; + /// @} /// @} + + /** + * @name Debugging: (handle ownership) + */ + /// @{ + static std::vector objectsUsingVulkan; + /** + * @brief Search for handles in message and get their owners + * @details + * Searches for handles (hex numbers) in message and sets handleOwnerString.\n + * If message contains no handle, handleOwnerString will be empty. + * If message contains at least one handle, handleOwnerString will be: + * `Handle Ownerships: [ - , ...]` + */ + static void getHandleOwnerString(std::string_view message); + static std::string handleOwnerString; + /// Used for alternating the color of validation messages between grey and white + static bool lastColorWhite; + /// @} + }; // VulkanInstance // @@ -528,7 +656,7 @@ namespace gz::vk { // UNIFORM BUFFERS // template - void VulkanInstance::createUniformBuffers(std::vector& uniformBuffers, std::vector& uniformBuffersMemory) { + void VulkanInstance::createUniformBuffers(std::vector& uniformBuffers, std::vector& uniformBuffersMemory) { VkDeviceSize bufferSize = sizeof(T); uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); @@ -537,11 +665,37 @@ namespace gz::vk { for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]); } -} + } + + // + // UTILITY + // + template + VkResult VulkanInstance::runVkResultFunction(const char* name, Args&&... args) { + auto f = reinterpret_cast(vkGetInstanceProcAddr(instance, name)); + if (f == nullptr) { + vLog.error("getVkFunction: Could not get function:", name); + throw std::runtime_error("getVkFunction: Could not get function."); + } + else { + return f(std::forward(args)...); + } + }; + + template + void VulkanInstance::runVkVoidFunction(const char* name, Args&&... args) { + auto f = reinterpret_cast(vkGetInstanceProcAddr(instance, name)); + if (f == nullptr) { + vLog.error("getVkFunction: Could not get function:", name); + throw std::runtime_error("getVkFunction: Could not get function."); + } + else { + f(std::forward(args)...); + } + } } /** - * @file Creating a vulkan instance - * @todo Write/get allocator for buffers + * @file Vulkan instance */