Created
October 3, 2025 15:26
-
-
Save xdegtyarev/4aaf50541d9ab351b18218bb4cdc7008 to your computer and use it in GitHub Desktop.
vkdemo.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <__ranges/subrange.h> | |
| #include <_types/_uint32_t.h> | |
| #include <array> | |
| #include <chrono> | |
| #include <glm/ext/matrix_clip_space.hpp> | |
| #include <glm/ext/matrix_transform.hpp> | |
| #include <glm/trigonometric.hpp> | |
| #include <random> | |
| #include <string> | |
| #include <valarray> | |
| #include <vector> | |
| #include <cstddef> | |
| #include <iostream> | |
| #include <stdexcept> | |
| #include <cstdlib> | |
| #include <set> | |
| #include <stdlib.h> | |
| #include <cstdint> // uint32_t | |
| #include <limits> //std::numberic_limits | |
| #include <algorithm> //std::clamp | |
| #include <fstream>//load shaders | |
| #include <vulkan/vk_platform.h> | |
| #include <vulkan/vulkan_core.h> | |
| #define GLFW_INCLUDE_VULKAN | |
| #include <GLFW/glfw3.h> | |
| #define GLM_ENABLE_EXPERIMENTAL | |
| #define GLM_FORCE_RADIANS | |
| #define GLM_FORCE_DEPTH_ZERO_TO_ONE | |
| #define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES | |
| #include <glm/glm.hpp> | |
| #include <glm/vec4.hpp> | |
| #include <glm/mat4x4.hpp> | |
| #include <glm/gtc/matrix_transform.hpp> | |
| #include <glm/ext/scalar_uint_sized.hpp> | |
| #include <glm/fwd.hpp> | |
| #include <glm/gtx/hash.hpp> | |
| #define STB_IMAGE_IMPLEMENTATION | |
| #include <stb_image.h> | |
| #define TINYOBJLOADER_IMPLEMENTATION | |
| #include <tiny_obj_loader.h> | |
| struct UniformBufferObjectTime{ | |
| glm::float32 deltaTime; | |
| }; | |
| struct UniformBufferObject { | |
| glm::mat4 model; | |
| glm::mat4 view; | |
| glm::mat4 proj; | |
| }; | |
| struct vertex{ | |
| glm::vec3 pos; | |
| glm::vec3 col; | |
| glm::vec2 uv0; | |
| static VkVertexInputBindingDescription getBindingDescription(){ | |
| VkVertexInputBindingDescription bindingDescription{}; | |
| bindingDescription.binding = 0; // index of binding; | |
| bindingDescription.stride = sizeof(vertex); //bytes from entry to next | |
| bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; //move after each vtx or instance (here instance) | |
| return bindingDescription; | |
| } | |
| static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions(){ | |
| std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions; | |
| //pos | |
| attributeDescriptions[0].binding = 0; // from which binding data comes | |
| attributeDescriptions[0].location = 0; // to which location in shader it goes | |
| attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; // vec3; | |
| attributeDescriptions[0].offset = offsetof(vertex, pos); | |
| attributeDescriptions[1].binding = 0; // from which binding data comes | |
| attributeDescriptions[1].location = 1; // to which location in shader it goes | |
| attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; // vec3; | |
| attributeDescriptions[1].offset = offsetof(vertex, col); | |
| attributeDescriptions[2].binding = 0; // from which binding data comes | |
| attributeDescriptions[2].location = 2; // to which location in shader it goes | |
| attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; // vec3; | |
| attributeDescriptions[2].offset = offsetof(vertex, uv0); | |
| return attributeDescriptions; | |
| } | |
| bool operator==(const vertex& other) const { | |
| return pos == other.pos && col == other.col && uv0 == other.uv0; | |
| } | |
| }; | |
| struct particle{ | |
| glm::vec2 pos; | |
| glm::vec2 vel; | |
| glm::vec4 col; | |
| static VkVertexInputBindingDescription getBindingDescription(){ | |
| VkVertexInputBindingDescription bindingDescription{}; | |
| bindingDescription.binding = 0; // index of binding; | |
| bindingDescription.stride = sizeof(particle); //bytes from entry to next | |
| bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; //move after each vtx or instance (here instance) | |
| return bindingDescription; | |
| } | |
| static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions(){ | |
| std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions; | |
| //pos | |
| attributeDescriptions[0].binding = 0; // from which binding data comes | |
| attributeDescriptions[0].location = 0; // to which location in shader it goes | |
| attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; // vec2; | |
| attributeDescriptions[0].offset = offsetof(particle, pos); | |
| attributeDescriptions[1].binding = 0; // from which binding data comes | |
| attributeDescriptions[1].location = 1; // to which location in shader it goes | |
| attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; // vec3; | |
| attributeDescriptions[1].offset = offsetof(particle, col); | |
| return attributeDescriptions; | |
| } | |
| }; | |
| //hash for vertext type in order to use unordered map | |
| namespace std { | |
| template<> struct hash<vertex> { | |
| size_t operator()(vertex const& vertex) const { | |
| return ((hash<glm::vec3>()(vertex.pos) ^ | |
| (hash<glm::vec3>()(vertex.col) << 1)) >> 1) ^ | |
| (hash<glm::vec2>()(vertex.uv0) << 1); | |
| } | |
| }; | |
| } | |
| //CONFIG + Singletons; | |
| namespace karaage{ | |
| const uint32_t WIDTH = 800; | |
| const uint32_t HEIGHT = 600; | |
| const uint32_t MAX_FRAMES_IN_FLIGHT = 2; | |
| const uint32_t MAX_PARTICLES = 8192; | |
| const char* meshPath = "meshes/viking_room.obj"; | |
| const char* texturePath = "textures/viking_room.png"; | |
| GLFWwindow* window; | |
| VkInstance vkInstance; | |
| VkSurfaceKHR surface; | |
| VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; | |
| VkDevice logicalDevice; | |
| VkQueue graphicsQueue; | |
| VkQueue presentQueue; | |
| VkQueue computeQueue; | |
| VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT; | |
| //swapchain | |
| VkSwapchainKHR swapChain; | |
| std::vector<VkImage> swapChainImages; | |
| std::vector<VkImageView> swapChainImageViews; | |
| std::vector<VkFramebuffer> swapChainFramebuffers; | |
| VkFormat swapChainImageFormat; | |
| VkExtent2D swapChainExtent; | |
| //color | |
| VkImage colorImage; | |
| VkDeviceMemory colorImageMemory; | |
| VkImageView colorImageView; | |
| //depth | |
| VkImage depthImage; | |
| VkDeviceMemory depthImageMemory; | |
| VkImageView depthImageView; | |
| VkRenderPass renderPass; | |
| VkDescriptorSetLayout descriptorSetLayout; | |
| VkDescriptorPool descriptorPool; | |
| std::vector<VkDescriptorSet> descriptorSets; | |
| VkDescriptorSetLayout computeDescriptorSetLayout; | |
| VkDescriptorPool computeDescriptorPool; | |
| std::vector<VkDescriptorSet> computeDescriptorSets; | |
| VkPipelineLayout pipelineLayout; | |
| VkPipelineLayout particlePipelineLayout; | |
| std::vector<VkPipeline> graphicsPipeline; | |
| VkPipelineLayout graphicsParticlePipelineLayout; | |
| VkPipelineLayout computePipelineLayout; | |
| VkPipeline computePipeline; | |
| VkCommandPool commandPool; | |
| //Vectors cause we allow multiple frames in flight; | |
| std::vector<VkCommandBuffer> commandBuffers; | |
| std::vector<VkCommandBuffer> computeCommandBuffers; | |
| //Sync | |
| std::vector<VkSemaphore> imageAvailableSemaphores; | |
| std::vector<VkSemaphore> renderFinishedSemaphores; | |
| std::vector<VkSemaphore> computeFinishedSemaphores; | |
| std::vector<VkFence> inFlightFences; | |
| std::vector<VkFence> computeInFlightFences; | |
| //SSBO for compute; | |
| std::vector<VkBuffer> shaderStorageBuffers; | |
| std::vector<VkDeviceMemory> shaderStorageBuffersMemory; | |
| // | |
| bool framebufferResized = false; | |
| //mesh | |
| std::vector<vertex> vertices; | |
| std::vector<uint32_t> indices; | |
| //VertexData | |
| VkBuffer vertexBuffer; | |
| VkDeviceMemory vertexBufferMemory; | |
| VkBuffer indexBuffer; | |
| VkDeviceMemory indexBufferMemory; | |
| //UBOs | |
| std::vector<VkBuffer> uniformBuffers; | |
| std::vector<VkDeviceMemory> uniformBuffersMemory; | |
| std::vector<void*> uniformBuffersMapped; | |
| std::vector<VkBuffer> computeUBOs; | |
| std::vector<VkDeviceMemory> computeUBOsMemory; | |
| std::vector<void*> computeUBOsMapped; | |
| //Textures | |
| uint32_t mipLevels; | |
| VkImage textureImage; | |
| VkDeviceMemory textureImageMemory; | |
| VkImageView textureImageView; | |
| VkSampler textureSampler; | |
| //extensions | |
| const std::vector<const char*> physicalDeviceExtensions = { | |
| VK_KHR_SWAPCHAIN_EXTENSION_NAME, | |
| "VK_KHR_portability_subset" | |
| }; | |
| //validation layers | |
| const std::vector<const char*> validationLayers = { | |
| "VK_LAYER_KHRONOS_validation"//enabling extension | |
| }; | |
| #ifdef NDEBUG// NDEBUG is used to mark Not debug code | |
| const bool enableValidationLayers = false; | |
| #else | |
| const bool enableValidationLayers = true; | |
| VkDebugUtilsMessengerEXT debugMessenger; | |
| #endif | |
| uint32_t currentFrame = 0; | |
| float lastFrameTime = 0.0f; | |
| double lastTime = 0.0f; | |
| } | |
| //Catching all debug messages from VK layers | |
| static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback( | |
| VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, | |
| VkDebugUtilsMessageTypeFlagsEXT messageType, | |
| const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, | |
| void* pUserData) { | |
| switch (messageSeverity) { | |
| case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: | |
| std::cerr << "[Error] Validation Layers: " << pCallbackData->pMessage << std::endl; | |
| break; | |
| case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: | |
| std::cerr << "[Warning] Validation Layers: " << pCallbackData->pMessage << std::endl; | |
| break; | |
| case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: | |
| std::cerr << "[Info] Validation Layers: " << pCallbackData->pMessage << std::endl; | |
| break; | |
| case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: | |
| std::cerr << "[Verbose] Validation Layers: " << pCallbackData->pMessage << std::endl; | |
| break; | |
| default: | |
| std::cerr << "[Default] Validation Layers: " << pCallbackData->pMessage << std::endl; | |
| break; | |
| } | |
| return VK_FALSE; | |
| } | |
| static std::vector<char> readFile(const std::string& filename){ | |
| std::ifstream file(filename, std::ios::ate | std::ios::binary); | |
| if(!file.is_open()){ | |
| throw std::runtime_error("[EXCEPTION]Failed to open file"); | |
| } | |
| size_t fileSize = (size_t) file.tellg(); | |
| std::vector<char> buffer(fileSize); | |
| file.seekg(0); | |
| file.read(buffer.data(), fileSize); | |
| file.close(); | |
| return buffer; | |
| } | |
| VkSampleCountFlagBits getMaxUsableSampleCount(){ | |
| VkPhysicalDeviceProperties properties; | |
| vkGetPhysicalDeviceProperties(karaage::physicalDevice, &properties); | |
| VkSampleCountFlags maxSampleCounts = | |
| properties.limits.framebufferColorSampleCounts & | |
| properties.limits.framebufferDepthSampleCounts; | |
| if (maxSampleCounts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; } | |
| if (maxSampleCounts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; } | |
| if (maxSampleCounts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; } | |
| if (maxSampleCounts & VK_SAMPLE_COUNT_8_BIT) { return VK_SAMPLE_COUNT_8_BIT; } | |
| if (maxSampleCounts & VK_SAMPLE_COUNT_4_BIT) { return VK_SAMPLE_COUNT_4_BIT; } | |
| if (maxSampleCounts & VK_SAMPLE_COUNT_2_BIT) { return VK_SAMPLE_COUNT_2_BIT; } | |
| return VK_SAMPLE_COUNT_1_BIT; | |
| } | |
| void onFramebufferResized(GLFWwindow* window, int width, int height){ | |
| std::cout | |
| << "Resizing window request with width: " | |
| << width | |
| << " and height: " | |
| << height | |
| << std::endl; | |
| karaage::framebufferResized = true; | |
| } | |
| //requesting validation layer support | |
| bool checkValidationLayerSupport(){ | |
| uint32_t layerCount; | |
| vkEnumerateInstanceLayerProperties(&layerCount, nullptr); | |
| std::vector<VkLayerProperties> availableLayers(layerCount); | |
| vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); | |
| std::cout << "layers available: " << layerCount << std::endl; | |
| //parse through all validation layers to find if any of them is available | |
| for (const char* layerName: karaage::validationLayers){ | |
| bool layerFound = false; | |
| for (const auto& layerProperties: availableLayers){ | |
| std::cout << "layer queued " << layerProperties.layerName << std::endl; | |
| if(strcmp(layerName, layerProperties.layerName) == 0){ | |
| layerFound = true; | |
| break; | |
| } | |
| } | |
| //nothing found | |
| if(!layerFound){ | |
| std::cout << "Layer was not found " << layerName << std::endl; | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| // | |
| void initCLI(){ | |
| std::cout | |
| << std::endl | |
| << "------------------------------------------------------------------------------------------------" | |
| << std::endl | |
| << "karaage engine" | |
| << std::endl; | |
| } | |
| void initWindow(){ | |
| glfwInit(); | |
| //calling with no api to avoid OGL context | |
| glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
| //not yet handling resizing | |
| glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); | |
| karaage::window = glfwCreateWindow( | |
| karaage::WIDTH, | |
| karaage::HEIGHT, | |
| "karaage", | |
| nullptr, | |
| nullptr); | |
| glfwSetFramebufferSizeCallback(karaage::window, onFramebufferResized); | |
| } | |
| /** | |
| * @brief Gets the required extensions. | |
| * | |
| * @return The required extension. | |
| */ | |
| std::vector<const char*> getRequiredExtensions(){ | |
| uint32_t glfwExtentionCount = 0; | |
| const char** glfwExtentions; | |
| glfwExtentions = glfwGetRequiredInstanceExtensions(&glfwExtentionCount); | |
| std::vector<const char*> extensions(glfwExtentions,glfwExtentions + glfwExtentionCount); | |
| if(karaage::enableValidationLayers){ | |
| extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); | |
| } | |
| extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); | |
| return extensions; | |
| } | |
| VkResult CreateDebugUtilsMessengerEXT( | |
| VkInstance instance, | |
| const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, | |
| const VkAllocationCallbacks* pAllocator, | |
| VkDebugUtilsMessengerEXT* pDebugMessenger){ | |
| auto fn = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr( | |
| instance, | |
| "vkCreateDebugUtilsMessengerEXT" | |
| ); | |
| if(fn != nullptr){ | |
| return fn(instance,pCreateInfo, pAllocator, pDebugMessenger); | |
| }else{ | |
| return VK_ERROR_EXTENSION_NOT_PRESENT; | |
| } | |
| } | |
| void DestroyDebugUtilsMessengerEXT( | |
| VkInstance instance, | |
| VkDebugUtilsMessengerEXT debugMessenger, | |
| const VkAllocationCallbacks* pAllocator | |
| ){ | |
| auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr( | |
| instance, | |
| "vkDestroyDebugUtilsMessengerEXT" | |
| ); | |
| if(func != nullptr){ | |
| func(instance, debugMessenger, pAllocator); | |
| }else{ | |
| throw std::runtime_error("[EXCEPTION]Failed to call vkDestroyDebugUtilsMessengerEXT"); | |
| } | |
| } | |
| void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo){ | |
| createInfo = {}; | |
| createInfo.sType = | |
| VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; | |
| createInfo.messageSeverity = | |
| 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; | |
| createInfo.messageType = | |
| VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | |
| | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | |
| | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; | |
| createInfo.pfnUserCallback = debugCallback; | |
| createInfo.pUserData = nullptr; //opt | |
| } | |
| void setupDebugMessenger(){ | |
| if(!karaage::enableValidationLayers) return; | |
| VkDebugUtilsMessengerCreateInfoEXT createInfo; | |
| populateDebugMessengerCreateInfo(createInfo); | |
| if(CreateDebugUtilsMessengerEXT( | |
| karaage::vkInstance, | |
| &createInfo, nullptr, | |
| &karaage::debugMessenger | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION]Failed to set up debug messenger"); | |
| } | |
| } | |
| void createVKInstance(){ | |
| if(karaage::enableValidationLayers && !checkValidationLayerSupport()){ | |
| throw std::runtime_error("[EXCEPTION]Falidation layers requested, but not available"); | |
| }else{ | |
| std::cout << "Starting without validation layers" << std::endl; | |
| } | |
| VkApplicationInfo appInfo{ | |
| .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, | |
| .pApplicationName = "Karaage VK", | |
| .applicationVersion = VK_MAKE_VERSION(0, 0, 1), | |
| .pEngineName = "Karaage", | |
| .engineVersion = VK_MAKE_VERSION(0, 0, 1), | |
| .apiVersion = VK_API_VERSION_1_3 | |
| }; | |
| VkInstanceCreateInfo createInfo{ | |
| .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
| .pApplicationInfo = &appInfo | |
| }; | |
| VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; | |
| //adding validation layer names | |
| if(karaage::enableValidationLayers){ | |
| createInfo.enabledLayerCount = static_cast<uint32_t>(karaage::validationLayers.size()); | |
| createInfo.ppEnabledLayerNames = karaage::validationLayers.data(); | |
| populateDebugMessengerCreateInfo(debugCreateInfo); | |
| createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo; | |
| }else{ | |
| createInfo.enabledLayerCount = 0; | |
| createInfo.pNext = nullptr; | |
| } | |
| //forming extensions request | |
| auto enableExtensionsRequest = getRequiredExtensions(); | |
| uint32_t enabledExtensionCount = static_cast<uint32_t>(enableExtensionsRequest.size()); | |
| createInfo.enabledExtensionCount = enabledExtensionCount; | |
| createInfo.ppEnabledExtensionNames = enableExtensionsRequest.data(); | |
| createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; | |
| //Creating instance | |
| VkResult result = vkCreateInstance(&createInfo, nullptr, &karaage::vkInstance); | |
| if(result != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION]Failed to create instance"); | |
| }else{ | |
| std::cout << "VK Instance created successfully" << std::endl; | |
| } | |
| uint32_t extensionCount = 0; | |
| vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); | |
| std::cout << "Requested: " << enabledExtensionCount << std::endl; | |
| std::cout << "Actual Extensions: " << extensionCount << std::endl; | |
| std::vector<VkExtensionProperties> extensions(extensionCount); | |
| vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); | |
| #ifdef KARAAGE_PRINT_LIST_EXTENSIONS | |
| std::cout << "Listing loaded extensions" << std::endl; | |
| for(const auto& extension: extensions){ | |
| std::cout << extension.extensionName << std::endl; | |
| } | |
| #endif | |
| } | |
| struct QueueFamilyIndices{ | |
| std::optional<uint32_t> graphicsAndComputeFamily; | |
| std::optional<uint32_t> graphicsFamily; | |
| std::optional<uint32_t> presentFamily; | |
| bool isComplete(){ | |
| return graphicsAndComputeFamily.has_value() | |
| && presentFamily.has_value(); | |
| } | |
| }; | |
| QueueFamilyIndices findQueueFamilies(VkPhysicalDevice physicalDevice){ | |
| QueueFamilyIndices indices; | |
| uint32_t queueFamilyCount = 0; | |
| vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); | |
| std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); | |
| vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data()); | |
| //checking if any queue has GRAPHICS BIT AND Compute BIT | |
| int i = 0; | |
| for(const auto& queueFamily: queueFamilies){ | |
| VkBool32 presentSupport = false; | |
| vkGetPhysicalDeviceSurfaceSupportKHR( | |
| physicalDevice, | |
| i, | |
| karaage::surface, | |
| &presentSupport | |
| ); | |
| //checking if queue is presenting | |
| if(presentSupport){ | |
| indices.presentFamily = i; | |
| } | |
| //if graphics and compute | |
| if((queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) && | |
| (queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT) | |
| ) { | |
| indices.graphicsAndComputeFamily = i; | |
| } | |
| //checking if queue is graphics NOT checking now as we have graphics and compute | |
| // if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { | |
| // indices.graphicsFamily = i; | |
| // } | |
| //all queues found | |
| if(indices.isComplete()) { | |
| break; | |
| } | |
| ++i; | |
| } | |
| return indices; | |
| } | |
| struct SwapChainSupportInfo { | |
| VkSurfaceCapabilitiesKHR capabilities; | |
| std::vector<VkSurfaceFormatKHR> formats; | |
| std::vector<VkPresentModeKHR> presentModes; | |
| }; | |
| SwapChainSupportInfo querySwapChainSupportInfo(VkPhysicalDevice physicalDevice){ | |
| SwapChainSupportInfo supportInfo; | |
| //getting capabilities | |
| vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, karaage::surface, &supportInfo.capabilities); | |
| //getting formats | |
| uint32_t formatCount; | |
| vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, karaage::surface, &formatCount, nullptr); | |
| if(formatCount != 0){ | |
| supportInfo.formats.resize(formatCount); | |
| vkGetPhysicalDeviceSurfaceFormatsKHR( | |
| physicalDevice, | |
| karaage::surface, | |
| &formatCount, | |
| supportInfo.formats.data() | |
| ); | |
| } | |
| //getting present modes | |
| uint32_t modeCount; | |
| vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, karaage::surface, &modeCount, nullptr); | |
| if(modeCount != 0){ | |
| supportInfo.presentModes.resize(modeCount); | |
| vkGetPhysicalDeviceSurfacePresentModesKHR( | |
| physicalDevice, | |
| karaage::surface, | |
| &modeCount, | |
| supportInfo.presentModes.data() | |
| ); | |
| } | |
| return supportInfo; | |
| } | |
| //ticking off extensions from req list; | |
| bool checkDeviceExtensionsSupport(VkPhysicalDevice physicalDevice){ | |
| uint32_t extensionCount; | |
| vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr); | |
| std::vector<VkExtensionProperties> availableExtensions(extensionCount); | |
| vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data()); | |
| std::set<std::string> requiredExtensions( | |
| karaage::physicalDeviceExtensions.begin(), | |
| karaage::physicalDeviceExtensions.end() | |
| ); | |
| for(const auto& extensions: availableExtensions){ | |
| requiredExtensions.erase(extensions.extensionName); | |
| } | |
| return requiredExtensions.empty(); | |
| } | |
| bool isDeviceSuitable(VkPhysicalDevice physicalDevice){ | |
| QueueFamilyIndices indices = findQueueFamilies(physicalDevice); | |
| //Getting physical physicalDevice props | |
| VkPhysicalDeviceProperties deviceProperties; | |
| vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); | |
| //Getting physical physicalDevice nfeatures | |
| VkPhysicalDeviceFeatures deviceFeatures; | |
| vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); | |
| bool isSwapChainCompatible = false; | |
| bool isExtensionsSupported = checkDeviceExtensionsSupport(physicalDevice); | |
| if(isExtensionsSupported){ | |
| SwapChainSupportInfo swapChainSupportInfo = querySwapChainSupportInfo(physicalDevice); | |
| isSwapChainCompatible | |
| = !swapChainSupportInfo.formats.empty() | |
| && !swapChainSupportInfo.presentModes.empty(); | |
| } | |
| return | |
| ( | |
| deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || | |
| deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU | |
| ) | |
| && indices.isComplete() | |
| && isExtensionsSupported | |
| && isSwapChainCompatible | |
| && deviceFeatures.samplerAnisotropy; | |
| } | |
| VkSurfaceFormatKHR chooseSwapChainFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats){ | |
| for (const auto& availableFormat: availableFormats){ | |
| if(availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB | |
| && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR){ | |
| return availableFormat; | |
| } | |
| } | |
| //settle with the first available if failing to choose preferred | |
| return availableFormats[0]; | |
| } | |
| VkPresentModeKHR choosePresentMode(const std::vector<VkPresentModeKHR>& availableModes){ | |
| for(const auto& availableMode: availableModes){ | |
| //choosing mailbox is energy is not concern; | |
| if(availableMode == VK_PRESENT_MODE_MAILBOX_KHR){ | |
| return availableMode; | |
| } | |
| } | |
| return VK_PRESENT_MODE_FIFO_KHR; | |
| } | |
| VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities){ | |
| if(capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()){ | |
| return capabilities.currentExtent; | |
| }else{ | |
| int width, height; | |
| //query current window res; | |
| glfwGetFramebufferSize(karaage::window, &width, &height); | |
| VkExtent2D actualExtent = { | |
| static_cast<uint32_t>(width), | |
| static_cast<uint32_t>(height) | |
| }; | |
| actualExtent.width = std::clamp( | |
| actualExtent.width, | |
| capabilities.minImageExtent.width, | |
| capabilities.maxImageExtent.width); | |
| actualExtent.height = std::clamp( | |
| actualExtent.height, | |
| capabilities.minImageExtent.height, | |
| capabilities.maxImageExtent.height); | |
| return actualExtent; | |
| } | |
| } | |
| void pickPhysicalDevice(){ | |
| uint32_t deviceCount = 0; | |
| vkEnumeratePhysicalDevices(karaage::vkInstance, &deviceCount, nullptr); | |
| if(deviceCount == 0){ | |
| throw std::runtime_error("[EXCEPTION]Fkaraage::ERROR] Can't find GPU that supports Vulkan"); | |
| }else{ | |
| std::cout << "Devices found: " << deviceCount << std::endl; | |
| } | |
| std::vector<VkPhysicalDevice> devices(deviceCount); | |
| vkEnumeratePhysicalDevices(karaage::vkInstance, &deviceCount, devices.data()); | |
| //evaluate if devices are suitable | |
| for(const auto& device: devices){ | |
| if(isDeviceSuitable(device)){ | |
| karaage::physicalDevice = device; | |
| karaage::msaaSamples = getMaxUsableSampleCount(); | |
| break; | |
| } | |
| } | |
| if(karaage::physicalDevice == VK_NULL_HANDLE){ | |
| throw std::runtime_error("[EXCEPTION]Fkaraage::ERROR] Fetched GPUs don't support Vulkan"); | |
| } | |
| } | |
| void createLogicalDevice(){ | |
| const uint32_t QUEUE_COUNT = 1; | |
| QueueFamilyIndices indices = findQueueFamilies(karaage::physicalDevice); | |
| std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; | |
| std::set<uint32_t> uniqueQueueFamilies = { | |
| indices.graphicsAndComputeFamily.value(), | |
| indices.presentFamily.value() | |
| }; | |
| //queue priority is 0..1 and is required | |
| float queuePriority = 1.0f; | |
| for(uint32_t queueFamily: uniqueQueueFamilies){ | |
| VkDeviceQueueCreateInfo queueCreateInfo{}; | |
| queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; | |
| queueCreateInfo.queueFamilyIndex = queueFamily; | |
| queueCreateInfo.queueCount = QUEUE_COUNT; | |
| queueCreateInfo.pQueuePriorities = &queuePriority; | |
| queueCreateInfos.push_back(queueCreateInfo); | |
| } | |
| //defining requried device features | |
| VkPhysicalDeviceFeatures deviceFeatures{ | |
| .samplerAnisotropy = VK_TRUE, | |
| .sampleRateShading = VK_TRUE | |
| }; | |
| // | |
| VkDeviceCreateInfo createInfo{ | |
| .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | |
| //settings queues | |
| .queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()), | |
| .pQueueCreateInfos = queueCreateInfos.data(), | |
| //settings features | |
| .pEnabledFeatures = &deviceFeatures, | |
| //settings device-specific extensions (portability subset is requred) | |
| .enabledExtensionCount = static_cast<uint32_t>(karaage::physicalDeviceExtensions.size()), | |
| .ppEnabledExtensionNames = karaage::physicalDeviceExtensions.data(), | |
| }; | |
| //settings device-specific validation layers that are ignored | |
| if(karaage::enableValidationLayers){ | |
| createInfo.enabledLayerCount = static_cast<uint32_t>(karaage::validationLayers.size()); | |
| createInfo.ppEnabledLayerNames = karaage::validationLayers.data(); | |
| }else{ | |
| createInfo.enabledLayerCount = 0; | |
| } | |
| if(vkCreateDevice(karaage::physicalDevice, &createInfo, nullptr, &karaage::logicalDevice) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION]Failed to get logical devices"); | |
| } | |
| vkGetDeviceQueue( | |
| karaage::logicalDevice, | |
| indices.graphicsAndComputeFamily.value(), | |
| 0, | |
| &karaage::graphicsQueue | |
| ); | |
| vkGetDeviceQueue( | |
| karaage::logicalDevice, | |
| indices.graphicsAndComputeFamily.value(), | |
| 0, | |
| &karaage::computeQueue | |
| ); | |
| vkGetDeviceQueue( | |
| karaage::logicalDevice, | |
| indices.presentFamily.value(), | |
| 0, | |
| &karaage::presentQueue | |
| ); | |
| } | |
| void createSurface(){ | |
| if(glfwCreateWindowSurface( | |
| karaage::vkInstance, | |
| karaage::window, | |
| nullptr, | |
| &karaage::surface | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION]Failed to create Window Surface"); | |
| } | |
| } | |
| void createSwapChain(){ | |
| //request what's avaialable | |
| SwapChainSupportInfo swapChainSupportInfo = querySwapChainSupportInfo(karaage::physicalDevice); | |
| //get best formats | |
| VkSurfaceFormatKHR surfaceFormat = chooseSwapChainFormat(swapChainSupportInfo.formats); | |
| VkPresentModeKHR presentMode = choosePresentMode(swapChainSupportInfo.presentModes); | |
| VkExtent2D extent = chooseSwapExtent(swapChainSupportInfo.capabilities); | |
| //requesting min+1 to wait for driver finish internal operations but less than max; | |
| uint32_t imageCount = swapChainSupportInfo.capabilities.minImageCount + 1; | |
| if(swapChainSupportInfo.capabilities.maxImageCount > 0 | |
| && imageCount > swapChainSupportInfo.capabilities.maxImageCount | |
| ){ | |
| imageCount = swapChainSupportInfo.capabilities.maxImageCount; | |
| } | |
| VkSwapchainCreateInfoKHR createInfo{}; | |
| createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
| createInfo.surface = karaage::surface; | |
| //details | |
| createInfo.minImageCount = imageCount; | |
| createInfo.imageFormat = surfaceFormat.format; | |
| createInfo.imageColorSpace = surfaceFormat.colorSpace; | |
| createInfo.imageExtent = extent; | |
| //amount of layers each images has (unless stereoscopic) | |
| createInfo.imageArrayLayers = 1; | |
| //usage type | |
| createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
| QueueFamilyIndices indices = findQueueFamilies(karaage::physicalDevice); | |
| uint32_t queueFamilyIndices[] = { | |
| indices.graphicsAndComputeFamily.value(), | |
| indices.presentFamily.value() | |
| }; | |
| if(indices.graphicsAndComputeFamily != indices.presentFamily){ | |
| createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; | |
| //specifying onwership since graphics and present queues are different | |
| createInfo.queueFamilyIndexCount = 2; | |
| createInfo.pQueueFamilyIndices = queueFamilyIndices; | |
| }else{ | |
| createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| //optional params | |
| createInfo.queueFamilyIndexCount = 0; | |
| createInfo.pQueueFamilyIndices = nullptr; | |
| } | |
| //using current transformation | |
| createInfo.preTransform = swapChainSupportInfo.capabilities.currentTransform; | |
| //composite alpha - if alpha should be used for blending with other windows (ignore) | |
| createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
| createInfo.presentMode = presentMode; | |
| //enable unless we really need pixel data for readbacks; | |
| createInfo.clipped = VK_TRUE; | |
| //TODO handle res change and swapchain replacement; | |
| createInfo.oldSwapchain = VK_NULL_HANDLE; | |
| //creating | |
| if(vkCreateSwapchainKHR( | |
| karaage::logicalDevice, | |
| &createInfo, | |
| nullptr, | |
| &karaage::swapChain) | |
| != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION]Failed to create swapchain"); | |
| } | |
| //grab image handles; | |
| vkGetSwapchainImagesKHR(karaage::logicalDevice, karaage::swapChain, &imageCount, nullptr); | |
| karaage::swapChainImages.resize(imageCount); | |
| vkGetSwapchainImagesKHR( | |
| karaage::logicalDevice, | |
| karaage::swapChain, | |
| &imageCount, | |
| karaage::swapChainImages.data() | |
| ); | |
| //store format and extent we used | |
| karaage::swapChainImageFormat = surfaceFormat.format; | |
| karaage::swapChainExtent = extent; | |
| } | |
| VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectMask, uint32_t mipLevels){ | |
| VkImageViewCreateInfo createInfo{ | |
| .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
| .image = image, | |
| .viewType = VK_IMAGE_VIEW_TYPE_2D, | |
| .format = format, | |
| .components = VK_COMPONENT_SWIZZLE_IDENTITY, | |
| .subresourceRange.aspectMask = aspectMask, | |
| .subresourceRange.baseArrayLayer = 0, | |
| .subresourceRange.baseMipLevel = 0, | |
| .subresourceRange.layerCount = 1, | |
| .subresourceRange.levelCount = mipLevels | |
| }; | |
| VkImageView imageView; | |
| if(vkCreateImageView( | |
| karaage::logicalDevice, | |
| &createInfo, | |
| nullptr, | |
| &imageView | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("Failed to create texture image view"); | |
| } | |
| return imageView; | |
| } | |
| void createImageViews(){ | |
| auto imageCount = karaage::swapChainImages.size(); | |
| karaage::swapChainImageViews.resize(imageCount); | |
| for(size_t i = 0; i < imageCount; i++){ | |
| karaage::swapChainImageViews[i] = createImageView( | |
| karaage::swapChainImages[i], | |
| karaage::swapChainImageFormat, | |
| VK_IMAGE_ASPECT_COLOR_BIT, | |
| 1 | |
| ); | |
| } | |
| } | |
| VkShaderModule createShaderModule(const std::vector<char>& code){ | |
| //size of bytecode poiinter is a uint32_t pointer rather than char pointera | |
| //data is aligned as we already use vector; | |
| VkShaderModuleCreateInfo shaderModuleCreateInfo{ | |
| .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, | |
| .codeSize = code.size(), | |
| .pCode = reinterpret_cast<const uint32_t*>(code.data()) | |
| }; | |
| //creating | |
| VkShaderModule shaderModule; | |
| if(vkCreateShaderModule(karaage::logicalDevice, &shaderModuleCreateInfo, nullptr, &shaderModule) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create shader module"); | |
| } | |
| return shaderModule; | |
| } | |
| VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features){ | |
| for(auto format:candidates){ | |
| VkFormatProperties props; | |
| vkGetPhysicalDeviceFormatProperties(karaage::physicalDevice, format,&props); | |
| if(tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features)){ | |
| return format; | |
| } else if(tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features)){ | |
| return format; | |
| } | |
| } | |
| throw std::runtime_error("No suitable format found"); | |
| } | |
| VkFormat 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 createRenderPass(){ | |
| VkAttachmentDescription colorAttachement{ | |
| //color format should match swapchain | |
| .format = karaage::swapChainImageFormat, | |
| .samples = karaage::msaaSamples, | |
| //load/store op; | |
| .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, //clear image on load; | |
| .storeOp = VK_ATTACHMENT_STORE_OP_STORE, | |
| //stencil l/s op | |
| .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
| .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
| //layout init/final | |
| .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | |
| .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL //MSAA can't be presented directly | |
| }; | |
| //definign subpass colorAttachment ref by index | |
| VkAttachmentReference colorAttachementRef{ | |
| //which attachement to reference by index in descroptions array | |
| .attachment = 0, | |
| //which layout to have during subpass (vk will auto transition) | |
| //we intent attachement to behave like color buffer; | |
| .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL | |
| }; | |
| VkAttachmentDescription depthAttachement{ | |
| .format = findDepthFormat(), | |
| .samples = karaage::msaaSamples, | |
| .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | |
| .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
| .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
| .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
| .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | |
| .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL | |
| }; | |
| VkAttachmentReference depthAttachementRef{ | |
| .attachment = 1, | |
| .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL | |
| }; | |
| //resolve target | |
| VkAttachmentDescription colorAttachmentResolve{ | |
| .format = karaage::swapChainImageFormat, | |
| .samples = VK_SAMPLE_COUNT_1_BIT, | |
| .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
| .storeOp = VK_ATTACHMENT_STORE_OP_STORE, | |
| .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
| .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
| .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | |
| .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, //framebuffer layout | |
| }; | |
| //definign subpass colorAttachment ref by index | |
| VkAttachmentReference colorAttachementResolveRef{ | |
| .attachment = 2, | |
| .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL | |
| }; | |
| //subpassDependencies: | |
| VkSubpassDependency dependency{ | |
| .srcSubpass = VK_SUBPASS_EXTERNAL,//implicit subpass before our; | |
| .dstSubpass = 0,//our subpass | |
| //wait on stages marked here: | |
| .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, | |
| .srcAccessMask = 0, | |
| //wait in color attachmenet stage involve writing of the color attachement | |
| //no transition until it's necessary and allowed when we want to start writing colors | |
| .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, | |
| .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, | |
| }; | |
| //subpassDescription | |
| VkSubpassDescription subpass{ | |
| .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,//bind point (compute/graphics) | |
| .colorAttachmentCount = 1, | |
| .pColorAttachments = &colorAttachementRef, | |
| .pResolveAttachments = &colorAttachementResolveRef, | |
| .pDepthStencilAttachment = &depthAttachementRef | |
| }; | |
| std::array<VkAttachmentDescription,3> attachements {colorAttachement,depthAttachement, colorAttachmentResolve}; | |
| //other types of attachments (input (read from shader). resolve - multisampling color) | |
| VkRenderPassCreateInfo renderPassInfo{ | |
| .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | |
| .attachmentCount = static_cast<uint32_t>(attachements.size()), | |
| .pAttachments = attachements.data(), | |
| .subpassCount = 1, | |
| .pSubpasses = &subpass, | |
| .dependencyCount = 1, | |
| .pDependencies = &dependency, | |
| }; | |
| //create pass; | |
| if(vkCreateRenderPass( | |
| karaage::logicalDevice, | |
| &renderPassInfo, | |
| nullptr, | |
| &karaage::renderPass | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create renderpass"); | |
| } | |
| } | |
| void createDescriptorSetLayout(){ | |
| VkDescriptorSetLayoutBinding uboLayoutBinding{ | |
| .binding = 0, //index specified in the shader | |
| .descriptorCount = 1, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, // type specified in shader | |
| .pImmutableSamplers = nullptr, | |
| .stageFlags = VK_SHADER_STAGE_VERTEX_BIT,//using in VTX shader | |
| }; | |
| VkDescriptorSetLayoutBinding samplerLayoutBinding{ | |
| .binding = 1, | |
| .descriptorCount = 1, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | |
| .pImmutableSamplers = nullptr, | |
| .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,//using in FRAG | |
| }; | |
| std::array<VkDescriptorSetLayoutBinding, 2> bindings = {uboLayoutBinding,samplerLayoutBinding}; | |
| VkDescriptorSetLayoutCreateInfo layoutInfo{ | |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | |
| .bindingCount = static_cast<uint32_t>(bindings.size()), | |
| .pBindings = bindings.data() | |
| }; | |
| if(vkCreateDescriptorSetLayout( | |
| karaage::logicalDevice, | |
| &layoutInfo, | |
| nullptr, | |
| &karaage::descriptorSetLayout | |
| ) != VK_SUCCESS | |
| ){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create descriptor set layout"); | |
| } | |
| std::cout << " Descriptor Set created" << std::endl; | |
| } | |
| void createComputePipeline(){ | |
| auto computeShaderCode = readFile("shaders/compute/simple.spv"); | |
| std::cout << " ComputeShader binary size:" << computeShaderCode.size() << std::endl; | |
| VkShaderModule computeShaderModule = createShaderModule(computeShaderCode); | |
| //COMPUTE SHADER STAGE | |
| VkPipelineShaderStageCreateInfo computeShaderStageInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| .stage = VK_SHADER_STAGE_COMPUTE_BIT, | |
| .module = computeShaderModule, | |
| .pName = "main" | |
| }; | |
| VkPipelineLayoutCreateInfo pipelineLayoutInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
| .setLayoutCount = 1, | |
| .pSetLayouts = &karaage::computeDescriptorSetLayout, | |
| }; | |
| if (vkCreatePipelineLayout( | |
| karaage::logicalDevice, | |
| &pipelineLayoutInfo, | |
| nullptr, | |
| &karaage::computePipelineLayout | |
| ) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create compute pipeline layout!"); | |
| } | |
| VkComputePipelineCreateInfo pipelineInfo{ | |
| .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, | |
| .layout = karaage::computePipelineLayout, | |
| .stage = computeShaderStageInfo, | |
| }; | |
| if (vkCreateComputePipelines( | |
| karaage::logicalDevice, | |
| VK_NULL_HANDLE, | |
| 1, | |
| &pipelineInfo, | |
| nullptr, | |
| &karaage::computePipeline | |
| ) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create compute pipeline!"); | |
| } | |
| vkDestroyShaderModule( | |
| karaage::logicalDevice, | |
| computeShaderModule, | |
| nullptr | |
| ); | |
| } | |
| void createGraphicsPipeline(){ | |
| //read shader bytecode | |
| auto vertShaderCode = readFile("shaders/vs/simple.spv"); | |
| std::cout << " VertShader binary size:" << vertShaderCode.size() << std::endl; | |
| auto fragShaderCode = readFile("shaders/frag/simple.spv"); | |
| std::cout << " FragShader binary size:" << fragShaderCode.size() << std::endl; | |
| //create shader bytecode wrappers | |
| VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); | |
| VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); | |
| //VERTEX SHADER STAGE | |
| VkPipelineShaderStageCreateInfo vertShaderStageInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| .stage = VK_SHADER_STAGE_VERTEX_BIT, | |
| .module = vertShaderModule, | |
| .pName = "main" //entrypoint | |
| }; | |
| // | |
| //FRAGMENT SHADER STAGE | |
| VkPipelineShaderStageCreateInfo fragShaderStageInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | |
| .module = fragShaderModule, | |
| .pName = "main" | |
| }; | |
| // | |
| //Capture shader-driven stages | |
| VkPipelineShaderStageCreateInfo shaderStages[] = { | |
| vertShaderStageInfo, | |
| fragShaderStageInfo | |
| }; | |
| //setting up dynamic states; | |
| //config of dynamic states will be ignored and will require to be set during draw; | |
| std::vector<VkDynamicState> dynamicStates = { | |
| VK_DYNAMIC_STATE_VIEWPORT, | |
| VK_DYNAMIC_STATE_SCISSOR | |
| }; | |
| VkPipelineDynamicStateCreateInfo dynamicState{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | |
| .dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()), | |
| .pDynamicStates = dynamicStates.data() | |
| }; | |
| //Vertex input format: | |
| // bindings: spacing between data and whether data is per-vertex or per-instance | |
| // attribute descriptions (type of the attribites passed to the vertex shader) | |
| auto bindingDescription = vertex::getBindingDescription(); | |
| auto attributeDescriptions = vertex::getAttributeDescriptions(); | |
| VkPipelineVertexInputStateCreateInfo vertexInputInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | |
| //bindings | |
| .vertexBindingDescriptionCount = 1, | |
| .pVertexBindingDescriptions = &bindingDescription, | |
| //attribute description; | |
| .vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size()), | |
| .pVertexAttributeDescriptions = attributeDescriptions.data() | |
| }; | |
| //Input Assembly (What kind of geometry will be drawn from VTX (restart primititve?)) | |
| VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
| .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | |
| .primitiveRestartEnable = VK_FALSE | |
| }; | |
| //setup viewport (region of framebuffer to render output to) | |
| VkViewport viewport{ | |
| .x = 0.0f, | |
| .y = 0.0f, | |
| .width = (float) karaage::swapChainExtent.width, | |
| .height = (float) karaage::swapChainExtent.height, | |
| .minDepth = 0.0f, | |
| .maxDepth = 1.0f | |
| }; | |
| //scisor rect (use swapchain extents); | |
| VkRect2D scisor{ | |
| .offset = {0,0}, | |
| .extent = karaage::swapChainExtent, | |
| }; | |
| //Setting dynamic pipestates; | |
| VkPipelineViewportStateCreateInfo viewportState{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | |
| .viewportCount = 1, | |
| .pViewports = &viewport, | |
| .scissorCount = 1, | |
| .pScissors = &scisor, | |
| }; | |
| // | |
| //Rasterizer (Performs Depth Testing, Face CUlling, Scissor Test (allows edge rendering) | |
| // | |
| VkPipelineRasterizationStateCreateInfo rasterizer{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | |
| //no clampdepth; (not clamp if rendering shadowmaps); | |
| .depthClampEnable = VK_FALSE, | |
| //disable any output to framebuffer (just disacar everything) | |
| .rasterizerDiscardEnable = VK_FALSE, | |
| //filling polygons (other modes require enabling GPU feature) | |
| .polygonMode = VK_POLYGON_MODE_FILL, | |
| //line width, anything larger requires wideLines GPU featurep | |
| .lineWidth = 1.0f, | |
| //Cull backfaces | |
| .cullMode = VK_CULL_MODE_BACK_BIT, | |
| .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, | |
| //depth altering | |
| .depthBiasEnable = VK_FALSE, | |
| .depthBiasConstantFactor = 0.0f, | |
| .depthBiasClamp = 0.0f, | |
| .depthBiasSlopeFactor = 0.0f, | |
| }; | |
| // | |
| // Multipsampling | |
| // | |
| VkPipelineMultisampleStateCreateInfo multisampling{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
| .sampleShadingEnable = VK_TRUE, | |
| .rasterizationSamples = karaage::msaaSamples, | |
| .minSampleShading = 0.2f, //closer to 1 is smoother | |
| .pSampleMask = nullptr, | |
| .alphaToCoverageEnable = VK_FALSE, | |
| .alphaToOneEnable = VK_FALSE | |
| }; | |
| // Depth / Stencil | |
| // VkPipelineDepthStencilStateCreateInfo | |
| // | |
| //Color blending; (combine fragment color with what is in the framebuffer) | |
| //Mix old and new | |
| //or combine old and new | |
| //VkPipelineColorBlendAttachmentState - config per attached framebuffer | |
| //VkPipelineColorBlendStateCreateInfo - global color blend setting | |
| VkPipelineColorBlendAttachmentState colorBlendAttachementState{ | |
| .colorWriteMask = | |
| VK_COLOR_COMPONENT_R_BIT | |
| | VK_COLOR_COMPONENT_G_BIT | |
| | VK_COLOR_COMPONENT_B_BIT | |
| | VK_COLOR_COMPONENT_A_BIT, | |
| //no blend | |
| .blendEnable = VK_FALSE, | |
| .srcColorBlendFactor = VK_BLEND_FACTOR_ONE, | |
| .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, | |
| .colorBlendOp = VK_BLEND_OP_ADD, | |
| .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, | |
| .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | |
| .alphaBlendOp = VK_BLEND_OP_ADD | |
| }; | |
| VkPipelineColorBlendStateCreateInfo colorBlendingCreateInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
| .logicOpEnable = VK_FALSE, | |
| .logicOp = VK_LOGIC_OP_COPY, | |
| .attachmentCount = 1, | |
| .pAttachments = &colorBlendAttachementState, | |
| .blendConstants[0] = 0.0f, | |
| .blendConstants[1] = 0.0f, | |
| .blendConstants[2] = 0.0f, | |
| .blendConstants[3] = 0.0f, | |
| }; | |
| //Pipeline layout specifying uniform vars that transfered | |
| VkPipelineLayoutCreateInfo pipelineLayoutInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
| .setLayoutCount = 1, | |
| .pSetLayouts = &karaage::descriptorSetLayout, | |
| //push constraints another way of passing dynamic values to shaders; | |
| .pushConstantRangeCount = 0, | |
| .pPushConstantRanges = nullptr | |
| }; | |
| if(vkCreatePipelineLayout( | |
| karaage::logicalDevice, | |
| &pipelineLayoutInfo, | |
| nullptr, | |
| &karaage::pipelineLayout | |
| ) != VK_SUCCESS | |
| ){ | |
| throw std::runtime_error("[EXCEPTION]Failed to create pipeline layout"); | |
| } | |
| VkPipelineDepthStencilStateCreateInfo depthStencil{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, | |
| .depthTestEnable = VK_TRUE, | |
| .depthWriteEnable = VK_TRUE, | |
| .depthCompareOp = VK_COMPARE_OP_LESS, | |
| .depthBoundsTestEnable = VK_FALSE, //keep only if fit specified depth | |
| .minDepthBounds = 0.0f, //for bounds test | |
| .maxDepthBounds = 1.0f, //for bounds test | |
| .stencilTestEnable = VK_FALSE, //stencil test | |
| }; | |
| //creating mesh graphics pipelines | |
| VkGraphicsPipelineCreateInfo pipelineInfo{ | |
| .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
| .stageCount = 2, // VS + FS | |
| .pStages = shaderStages, | |
| .pVertexInputState = &vertexInputInfo, | |
| .pInputAssemblyState = &inputAssemblyCreateInfo, | |
| .pViewportState = &viewportState, | |
| .pRasterizationState = &rasterizer, | |
| .pMultisampleState = &multisampling, | |
| .pDepthStencilState = &depthStencil, | |
| .pColorBlendState = &colorBlendingCreateInfo, | |
| .pDynamicState = &dynamicState, | |
| .layout = karaage::pipelineLayout, | |
| .renderPass = karaage::renderPass, | |
| .subpass = 0,//index of subpas where this pipe will be used | |
| }; | |
| auto vertParticleShaderCode = readFile("shaders/vs/particle.spv"); | |
| std::cout << " VertShader binary size:" << vertParticleShaderCode.size() << std::endl; | |
| auto fragParticleShaderCode = readFile("shaders/frag/particle.spv"); | |
| std::cout << " FragShader binary size:" << fragParticleShaderCode.size() << std::endl; | |
| //create shaders for particles | |
| VkShaderModule vertParticleShaderModule = createShaderModule(vertParticleShaderCode); | |
| VkShaderModule fragParticleShaderModule = createShaderModule(fragParticleShaderCode); | |
| //VERTEX SHADER STAGE | |
| VkPipelineShaderStageCreateInfo vertParticleShaderStageInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| .stage = VK_SHADER_STAGE_VERTEX_BIT, | |
| .module = vertParticleShaderModule, | |
| .pName = "main" //entrypoint | |
| }; | |
| // | |
| //FRAGMENT SHADER STAGE | |
| VkPipelineShaderStageCreateInfo fragParticleShaderStageInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
| .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | |
| .module = fragParticleShaderModule, | |
| .pName = "main" | |
| }; | |
| // | |
| //Capture shader-driven stages | |
| VkPipelineShaderStageCreateInfo particleShaderStages[] = { | |
| vertParticleShaderStageInfo, | |
| fragParticleShaderStageInfo | |
| }; | |
| auto particleBindingDescription = particle::getBindingDescription(); | |
| auto particleAttributeDescriptions = particle::getAttributeDescriptions(); | |
| VkPipelineVertexInputStateCreateInfo particleVertexInputInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | |
| //bindings | |
| .vertexBindingDescriptionCount = 1, | |
| .pVertexBindingDescriptions = &particleBindingDescription, | |
| //attribute description; | |
| .vertexAttributeDescriptionCount = static_cast<uint32_t>(particleAttributeDescriptions.size()), | |
| .pVertexAttributeDescriptions = particleAttributeDescriptions.data() | |
| }; | |
| //Input Assembly (What kind of geometry will be drawn from VTX (restart primititve?)) | |
| VkPipelineInputAssemblyStateCreateInfo particleInputAssemblyCreateInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
| .topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, | |
| .primitiveRestartEnable = VK_FALSE | |
| }; | |
| VkPipelineRasterizationStateCreateInfo particleRasterizer{}; | |
| rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; | |
| rasterizer.depthClampEnable = VK_FALSE; | |
| rasterizer.rasterizerDiscardEnable = VK_FALSE; | |
| rasterizer.polygonMode = VK_POLYGON_MODE_FILL; | |
| rasterizer.lineWidth = 0.1f; | |
| rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; | |
| rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; | |
| rasterizer.depthBiasEnable = VK_FALSE; | |
| VkPipelineLayoutCreateInfo particlePipelineLayoutInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
| .setLayoutCount = 0, | |
| .pSetLayouts = nullptr | |
| }; | |
| if (vkCreatePipelineLayout( | |
| karaage::logicalDevice, | |
| &particlePipelineLayoutInfo, | |
| nullptr, | |
| &karaage::particlePipelineLayout | |
| ) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create pipeline layout!"); | |
| } | |
| VkPipelineDepthStencilStateCreateInfo particleDepthStencil{ | |
| .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, | |
| .depthTestEnable = VK_FALSE, | |
| .depthWriteEnable = VK_TRUE, | |
| .depthCompareOp = VK_COMPARE_OP_ALWAYS, | |
| .depthBoundsTestEnable = VK_FALSE, //keep only if fit specified depth | |
| .minDepthBounds = 0.0f, //for bounds test | |
| .maxDepthBounds = 1.0f, //for bounds test | |
| .stencilTestEnable = VK_FALSE, //stencil test | |
| }; | |
| VkGraphicsPipelineCreateInfo particlePipelineInfo{ | |
| .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
| .stageCount = 2, | |
| .pStages = particleShaderStages, | |
| .pVertexInputState = &particleVertexInputInfo, | |
| .pInputAssemblyState = &particleInputAssemblyCreateInfo, | |
| .pViewportState = &viewportState, | |
| .pRasterizationState = &particleRasterizer, | |
| .pMultisampleState = &multisampling, | |
| .pDepthStencilState = &particleDepthStencil, | |
| .pColorBlendState = &colorBlendingCreateInfo, | |
| .pDynamicState = &dynamicState, | |
| .layout = karaage::particlePipelineLayout, | |
| .renderPass = karaage::renderPass, | |
| .subpass = 0,//index of subpas where this pipe will be used | |
| }; | |
| VkGraphicsPipelineCreateInfo pipelineInfos[] = { | |
| pipelineInfo, | |
| particlePipelineInfo | |
| }; | |
| karaage::graphicsPipeline.resize(2); | |
| //create pipe no cache assigned; | |
| if(vkCreateGraphicsPipelines( | |
| karaage::logicalDevice, | |
| VK_NULL_HANDLE, | |
| 2, | |
| pipelineInfos, | |
| nullptr, | |
| karaage::graphicsPipeline.data() | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION]Failed To crate graphics pipeline"); | |
| } | |
| //cleanup loaded shaders | |
| vkDestroyShaderModule(karaage::logicalDevice, fragShaderModule, nullptr); | |
| vkDestroyShaderModule(karaage::logicalDevice, vertShaderModule, nullptr); | |
| vkDestroyShaderModule(karaage::logicalDevice, fragParticleShaderModule, nullptr); | |
| vkDestroyShaderModule(karaage::logicalDevice, vertParticleShaderModule, nullptr); | |
| } | |
| void createFramebuffers(){ | |
| //setting same size of framebuffers as SwapChain image view; | |
| karaage::swapChainFramebuffers.resize(karaage::swapChainImageViews.size()); | |
| for(size_t i = 0; i < karaage::swapChainImageViews.size(); i++){ | |
| std::array<VkImageView,3> attachements = { | |
| karaage::colorImageView, | |
| karaage::depthImageView, | |
| karaage::swapChainImageViews[i], | |
| }; | |
| VkFramebufferCreateInfo framebufferInfo{ | |
| .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | |
| .renderPass = karaage::renderPass, //define compatablity | |
| .attachmentCount = static_cast<uint32_t>(attachements.size()), | |
| .pAttachments = attachements.data(), | |
| .width = karaage::swapChainExtent.width, | |
| .height = karaage::swapChainExtent.height, | |
| .layers = 1// num of layers in image array | |
| }; | |
| if(vkCreateFramebuffer( | |
| karaage::logicalDevice, | |
| &framebufferInfo, | |
| nullptr, | |
| &karaage::swapChainFramebuffers[i] | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create framebuffer"); | |
| } | |
| } | |
| } | |
| void cleanupSwapChain(){ | |
| vkDestroyImageView(karaage::logicalDevice,karaage::colorImageView,nullptr); | |
| vkDestroyImage(karaage::logicalDevice, karaage::colorImage,nullptr); | |
| vkFreeMemory(karaage::logicalDevice, karaage::colorImageMemory, nullptr); | |
| vkDestroyImageView(karaage::logicalDevice,karaage::depthImageView,nullptr); | |
| vkDestroyImage(karaage::logicalDevice, karaage::depthImage,nullptr); | |
| vkFreeMemory(karaage::logicalDevice, karaage::depthImageMemory, nullptr); | |
| for(auto framebuffer: karaage::swapChainFramebuffers){ | |
| vkDestroyFramebuffer(karaage::logicalDevice, framebuffer, nullptr); | |
| } | |
| for(auto imageView: karaage::swapChainImageViews){ | |
| vkDestroyImageView(karaage::logicalDevice, imageView, nullptr); | |
| } | |
| vkDestroySwapchainKHR(karaage::logicalDevice, karaage::swapChain, nullptr); | |
| } | |
| //early decl; | |
| void createDepthResource(); | |
| void createColorResource(); | |
| //doesn't handle HDR/SDR pipe conversion, only size; | |
| void recreateSwapChain(){ | |
| int width = 0, height = 0; | |
| glfwGetFramebufferSize(karaage::window, &width, &height); | |
| while (width == 0 || height == 0) { | |
| glfwWaitEvents(); | |
| glfwGetFramebufferSize(karaage::window, &width, &height); | |
| } | |
| //wait for resoruces to be freed | |
| vkDeviceWaitIdle(karaage::logicalDevice); | |
| cleanupSwapChain(); | |
| createSwapChain(); | |
| createImageViews(); | |
| createColorResource(); | |
| createDepthResource(); | |
| createFramebuffers(); | |
| } | |
| void createCommandPool(){ | |
| QueueFamilyIndices queueFamilyIndices = findQueueFamilies(karaage::physicalDevice); | |
| VkCommandPoolCreateInfo poolInfo{ | |
| .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
| .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, | |
| .queueFamilyIndex = queueFamilyIndices.graphicsAndComputeFamily.value(), | |
| }; | |
| if(vkCreateCommandPool(karaage::logicalDevice, &poolInfo, nullptr, &karaage::commandPool)!=VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create command pool"); | |
| } | |
| } | |
| uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties){ | |
| VkPhysicalDeviceMemoryProperties memProperties; | |
| vkGetPhysicalDeviceMemoryProperties(karaage::physicalDevice, &memProperties); | |
| //loop through all memtypes, typeFilter is BITFIELD of suitable types; | |
| //props define special features of the memory (map to write from cpu); | |
| for(uint32_t i = 0; i < memProperties.memoryTypeCount; i++){ | |
| if( | |
| (typeFilter & (1 << i)) //check types | |
| && ((memProperties.memoryTypes[i].propertyFlags & properties) == properties) //check properties | |
| ){ | |
| return i; | |
| } | |
| } | |
| throw std::runtime_error("[EXCEPTION] Couldn't find sutable memory type"); | |
| } | |
| void createBuffer( | |
| VkDeviceSize size, | |
| VkBufferUsageFlags usage, | |
| VkMemoryPropertyFlags properties, | |
| VkBuffer& buffer, | |
| VkDeviceMemory& bufferMemory | |
| ){ | |
| VkBufferCreateInfo bufferInfo{ | |
| .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | |
| .size = size, | |
| .usage = usage, | |
| .sharingMode = VK_SHARING_MODE_EXCLUSIVE | |
| }; | |
| if(vkCreateBuffer( | |
| karaage::logicalDevice, | |
| &bufferInfo, | |
| nullptr, | |
| &buffer | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create vertex buffer"); | |
| } | |
| //No memory allocated yet | |
| //Query memory requirements for the buffer; | |
| VkMemoryRequirements memRequirements; | |
| vkGetBufferMemoryRequirements(karaage::logicalDevice, buffer, &memRequirements); | |
| //prep alloc info | |
| //VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT //coherent bit (may affect perf) | |
| VkMemoryAllocateInfo allocInfo{ | |
| .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
| .allocationSize = memRequirements.size, | |
| .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties) | |
| }; | |
| //alloc | |
| if(vkAllocateMemory( | |
| karaage::logicalDevice, | |
| &allocInfo, | |
| nullptr, | |
| &bufferMemory | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to alloc vertex buffer"); | |
| } | |
| //bind memory to buffer; | |
| if(vkBindBufferMemory(karaage::logicalDevice, buffer, bufferMemory, 0) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to bind mem to vertex buffer"); | |
| } | |
| } | |
| VkCommandBuffer BeginSingleTimeCommands(){ | |
| //memory transfer uses command buffers | |
| VkCommandBufferAllocateInfo allocInfo{ | |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
| .commandPool = karaage::commandPool, | |
| .commandBufferCount = 1 | |
| }; | |
| VkCommandBuffer commandBuffer; | |
| vkAllocateCommandBuffers( | |
| karaage::logicalDevice, | |
| &allocInfo, | |
| &commandBuffer | |
| ); | |
| //Start cmdbuf | |
| VkCommandBufferBeginInfo beginInfo{ | |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT //onetime | |
| }; | |
| vkBeginCommandBuffer(commandBuffer, &beginInfo); | |
| return commandBuffer; | |
| } | |
| void EndSingleTimeCommands(VkCommandBuffer commandBuffer){ | |
| vkEndCommandBuffer(commandBuffer); | |
| //Submit part | |
| VkSubmitInfo submitInfo{ | |
| .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| .commandBufferCount = 1, | |
| .pCommandBuffers = &commandBuffer | |
| }; | |
| vkQueueSubmit(karaage::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); //no fences | |
| vkQueueWaitIdle(karaage::graphicsQueue);//TODO: wait for fence | |
| vkFreeCommandBuffers(karaage::logicalDevice, karaage::commandPool, 1, &commandBuffer); | |
| } | |
| void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size){ | |
| VkCommandBuffer commandBuffer = BeginSingleTimeCommands(); | |
| { | |
| //commands | |
| VkBufferCopy copyRegion{ | |
| .srcOffset = 0, | |
| .dstOffset = 0, | |
| .size = size, | |
| }; | |
| vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); | |
| } | |
| EndSingleTimeCommands(commandBuffer); | |
| } | |
| void createShaderStorageBuffers(){ | |
| // Initialize particles | |
| std::default_random_engine rndEngine((unsigned)time(nullptr)); | |
| std::uniform_real_distribution<float> rndDist(0.0f, 1.0f); | |
| // Initial particle positions on a circle | |
| std::vector<particle> particles(karaage::MAX_PARTICLES); | |
| for (auto& particle : particles) { | |
| float r = 1.0f * sqrt(rndDist(rndEngine)); | |
| float theta = rndDist(rndEngine) * 2 * 3.14159265358979323846; | |
| float x = r * cos(theta) * karaage::HEIGHT / karaage::WIDTH; | |
| float y = r * sin(theta); | |
| particle.pos = glm::vec2(x, y); | |
| particle.vel = glm::normalize(glm::vec2(x,y)) * 0.00025f; | |
| particle.col = glm::vec4(rndDist(rndEngine), rndDist(rndEngine), rndDist(rndEngine), 1.0f); | |
| } | |
| VkDeviceSize bufferSize = sizeof(particle) * karaage::MAX_PARTICLES; | |
| VkBuffer stagingBuffer; | |
| VkDeviceMemory stagingBufferMemory; | |
| createBuffer( | |
| bufferSize, | |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, | |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, | |
| stagingBuffer, | |
| stagingBufferMemory | |
| ); | |
| void* data; | |
| vkMapMemory(karaage::logicalDevice, stagingBufferMemory, 0, bufferSize, 0, &data); | |
| memcpy(data,particles.data(),static_cast<size_t>(bufferSize)); | |
| vkUnmapMemory(karaage::logicalDevice, stagingBufferMemory); | |
| karaage::shaderStorageBuffers.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::shaderStorageBuffersMemory.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| for(size_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){ | |
| createBuffer( | |
| bufferSize, | |
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, | |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
| karaage::shaderStorageBuffers[i], | |
| karaage::shaderStorageBuffersMemory[i] | |
| ); | |
| copyBuffer( | |
| stagingBuffer, | |
| karaage::shaderStorageBuffers[i], | |
| bufferSize | |
| ); | |
| } | |
| vkDestroyBuffer(karaage::logicalDevice, stagingBuffer, nullptr); | |
| vkFreeMemory(karaage::logicalDevice, stagingBufferMemory, nullptr); | |
| } | |
| void createComputeDescriptorSetLayout() { | |
| std::array<VkDescriptorSetLayoutBinding, 3> layoutBindings{{ | |
| { | |
| .binding = 0, | |
| .descriptorCount = 1, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | |
| .pImmutableSamplers = nullptr, | |
| .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | |
| }, | |
| {//double buffered particles[0] | |
| .binding = 1, | |
| .descriptorCount = 1, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, | |
| .pImmutableSamplers = nullptr, | |
| .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | |
| }, | |
| {//double buffered particles[1] | |
| .binding = 2, | |
| .descriptorCount = 1, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, | |
| .pImmutableSamplers = nullptr, | |
| .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | |
| } | |
| }}; | |
| VkDescriptorSetLayoutCreateInfo layoutInfo{ | |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | |
| .bindingCount = 3, | |
| .pBindings = layoutBindings.data(), | |
| }; | |
| if (vkCreateDescriptorSetLayout( | |
| karaage::logicalDevice, | |
| &layoutInfo, | |
| nullptr, | |
| &karaage::computeDescriptorSetLayout | |
| ) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to create compute descriptor set layout!"); | |
| } | |
| } | |
| void createComputeDescriptorSets() { | |
| std::vector<VkDescriptorSetLayout> layouts( | |
| karaage::MAX_FRAMES_IN_FLIGHT, | |
| karaage::computeDescriptorSetLayout | |
| ); | |
| VkDescriptorSetAllocateInfo allocInfo{ | |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, | |
| .descriptorPool = karaage::computeDescriptorPool, | |
| .descriptorSetCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT), | |
| .pSetLayouts = layouts.data(), | |
| }; | |
| karaage::computeDescriptorSets.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| if (vkAllocateDescriptorSets( | |
| karaage::logicalDevice, | |
| &allocInfo, | |
| karaage::computeDescriptorSets.data() | |
| ) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to allocate descriptor sets!"); | |
| } | |
| for (size_t i = 0; i < karaage::MAX_FRAMES_IN_FLIGHT; i++) { | |
| VkDescriptorBufferInfo uniformBufferInfo{}; | |
| uniformBufferInfo.buffer = karaage::uniformBuffers[i]; | |
| uniformBufferInfo.offset = 0; | |
| uniformBufferInfo.range = sizeof(UniformBufferObject); | |
| VkDescriptorBufferInfo storageBufferInfoLastFrame{ | |
| .buffer = karaage::shaderStorageBuffers[(i - 1) % karaage::MAX_FRAMES_IN_FLIGHT], | |
| .offset = 0, | |
| .range = sizeof(particle) * karaage::MAX_PARTICLES, | |
| }; | |
| VkDescriptorBufferInfo storageBufferInfoCurrentFrame{ | |
| .buffer = karaage::shaderStorageBuffers[i], | |
| .offset = 0, | |
| .range = sizeof(particle) * karaage::MAX_PARTICLES, | |
| }; | |
| std::array<VkWriteDescriptorSet, 3> descriptorWrites{{ | |
| { | |
| .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | |
| .dstSet = karaage::computeDescriptorSets[i], | |
| .dstBinding = 0, | |
| .dstArrayElement = 0, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | |
| .descriptorCount = 1, | |
| .pBufferInfo = &uniformBufferInfo | |
| }, | |
| { | |
| .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | |
| .dstSet = karaage::computeDescriptorSets[i], | |
| .dstBinding = 1, | |
| .dstArrayElement = 0, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, | |
| .descriptorCount = 1, | |
| .pBufferInfo = &storageBufferInfoLastFrame, | |
| }, | |
| { | |
| .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | |
| .dstSet = karaage::computeDescriptorSets[i], | |
| .dstBinding = 2, | |
| .dstArrayElement = 0, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, | |
| .descriptorCount = 1, | |
| .pBufferInfo = &storageBufferInfoCurrentFrame, | |
| } | |
| }}; | |
| vkUpdateDescriptorSets( | |
| karaage::logicalDevice, | |
| 3, | |
| descriptorWrites.data(), | |
| 0, | |
| nullptr | |
| ); | |
| } | |
| } | |
| bool hasStencilComponent(VkFormat format){ | |
| return | |
| format == VK_FORMAT_D32_SFLOAT_S8_UINT || | |
| format == VK_FORMAT_D24_UNORM_S8_UINT || | |
| format == VK_FORMAT_D16_UNORM_S8_UINT || | |
| format == VK_FORMAT_S8_UINT; | |
| } | |
| void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels){ | |
| VkCommandBuffer commandBuffer = BeginSingleTimeCommands(); | |
| { | |
| VkImageAspectFlags aspectMask = 0; | |
| VkPipelineStageFlags srcStage = 0; | |
| VkPipelineStageFlags dstStage = 0; | |
| VkAccessFlags srcMask = 0; | |
| VkAccessFlags dstMask = 0; | |
| if( | |
| oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && | |
| newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL | |
| ){ | |
| srcMask = 0; | |
| dstMask = VK_ACCESS_TRANSFER_WRITE_BIT; | |
| srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | |
| dstStage = VK_PIPELINE_STAGE_TRANSFER_BIT; | |
| aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| } | |
| else if( | |
| oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && | |
| newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL | |
| ){ | |
| srcMask = VK_ACCESS_TRANSFER_WRITE_BIT; | |
| dstMask = VK_ACCESS_SHADER_READ_BIT; | |
| srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT; | |
| dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; | |
| aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
| }else if( | |
| oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && | |
| newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL | |
| ){ | |
| srcMask = 0; | |
| dstMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; | |
| srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | |
| dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; | |
| aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; | |
| if (hasStencilComponent(format)) { | |
| aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; | |
| } | |
| }else{ | |
| throw std::invalid_argument("unsupported layout transition"); | |
| } | |
| VkImageMemoryBarrier barrier{ | |
| .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | |
| .oldLayout = oldLayout, | |
| .newLayout = newLayout, | |
| //no queue transfers ignoring; | |
| .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| //what image and part of it is affected | |
| .image = image, | |
| .subresourceRange.aspectMask = aspectMask, | |
| .subresourceRange.baseArrayLayer = 0, | |
| .subresourceRange.baseMipLevel = 0, | |
| .subresourceRange.layerCount = 1, | |
| .subresourceRange.levelCount = mipLevels, | |
| //TODO | |
| .srcAccessMask = srcMask, | |
| .dstAccessMask = dstMask | |
| }; | |
| vkCmdPipelineBarrier( | |
| commandBuffer, | |
| srcStage, //what stages are before barrier | |
| dstStage, //what stages wait for barrier | |
| 0,//allows reading by regions | |
| 0,nullptr, | |
| 0,nullptr, | |
| 1,&barrier | |
| ); | |
| } | |
| EndSingleTimeCommands(commandBuffer); | |
| } | |
| void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height){ | |
| VkCommandBuffer commandBuffer = BeginSingleTimeCommands(); | |
| { | |
| VkBufferImageCopy region{ | |
| .bufferOffset = 0, | |
| .bufferRowLength = 0, | |
| .bufferImageHeight = 0, | |
| //subres | |
| .imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | |
| .imageSubresource.baseArrayLayer = 0, | |
| .imageSubresource.mipLevel = 0, | |
| .imageSubresource.layerCount = 1, | |
| .imageOffset = {0,0,0}, | |
| .imageExtent = {width,height,1} | |
| }; | |
| vkCmdCopyBufferToImage( | |
| commandBuffer, | |
| buffer, | |
| image, | |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, //assume already transitioned to optimal for copy layout | |
| 1, | |
| ®ion | |
| ); | |
| } | |
| EndSingleTimeCommands(commandBuffer); | |
| } | |
| void createImage2D( | |
| uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, | |
| VkFormat format, VkImageTiling tiling, | |
| VkImageUsageFlags usageFlags, VkMemoryPropertyFlags properties, | |
| VkImage& image, VkDeviceMemory& imageMemory | |
| ){ | |
| VkImageCreateInfo imageInfo{ | |
| .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | |
| .imageType = VK_IMAGE_TYPE_2D, | |
| .extent.width = static_cast<uint32_t>(width), | |
| .extent.height = static_cast<uint32_t>(height), | |
| .extent.depth = 1, | |
| .mipLevels = mipLevels, | |
| .arrayLayers = 1, | |
| .format = format, | |
| .tiling = tiling, //row-major(linear) or optimal | |
| .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | |
| .usage = usageFlags, //destination for the buffer copy and sampled in shader | |
| .samples = numSamples, | |
| .sharingMode = VK_SHARING_MODE_EXCLUSIVE // one queue family (graphics pipe) | |
| }; | |
| if(vkCreateImage( | |
| karaage::logicalDevice, | |
| &imageInfo, | |
| nullptr, | |
| &image | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("Failed to create texture image"); | |
| } | |
| VkMemoryRequirements memRequirements; | |
| vkGetImageMemoryRequirements(karaage::logicalDevice, image, &memRequirements); | |
| VkMemoryAllocateInfo allocInfo{ | |
| .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
| .allocationSize = memRequirements.size, | |
| .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties) | |
| }; | |
| if(vkAllocateMemory( | |
| karaage::logicalDevice, | |
| &allocInfo, | |
| nullptr, | |
| &imageMemory | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("Failed to allocate texture memory"); | |
| } | |
| vkBindImageMemory(karaage::logicalDevice, image, imageMemory, 0); | |
| } | |
| void createColorResource(){ | |
| VkFormat colorFormat = karaage::swapChainImageFormat; | |
| createImage2D( | |
| karaage::swapChainExtent.width, | |
| karaage::swapChainExtent.height, | |
| 1, | |
| karaage::msaaSamples, | |
| colorFormat, | |
| VK_IMAGE_TILING_OPTIMAL, | |
| VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, | |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
| karaage::colorImage, | |
| karaage::colorImageMemory | |
| ); | |
| karaage::colorImageView = createImageView( | |
| karaage::colorImage, | |
| colorFormat, | |
| VK_IMAGE_ASPECT_COLOR_BIT, | |
| 1 | |
| ); | |
| // transitionImageLayout( | |
| // karaage::colorImage, | |
| // colorFormat, | |
| // VK_IMAGE_LAYOUT_UNDEFINED, | |
| // VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, | |
| // 1 | |
| // ); | |
| } | |
| void createDepthResource(){ | |
| VkFormat depthFormat = findDepthFormat(); | |
| createImage2D( | |
| karaage::swapChainExtent.width, | |
| karaage::swapChainExtent.height, | |
| 1, | |
| karaage::msaaSamples, | |
| depthFormat, | |
| VK_IMAGE_TILING_OPTIMAL, | |
| VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, | |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
| karaage::depthImage, | |
| karaage::depthImageMemory | |
| ); | |
| karaage::depthImageView = createImageView( | |
| karaage::depthImage, | |
| depthFormat, | |
| VK_IMAGE_ASPECT_DEPTH_BIT, | |
| 1 | |
| ); | |
| // transitionImageLayout( | |
| // karaage::depthImage, | |
| // depthFormat, | |
| // VK_IMAGE_LAYOUT_UNDEFINED, | |
| // VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, | |
| // 1 | |
| // ); | |
| } | |
| void generateMipmaps(VkImage image, VkFormat imageFormat, uint32_t texWidth, uint32_t texHeight, uint32_t mipLevels){ | |
| //check if format supports blitting; | |
| VkFormatProperties formatProperties; | |
| vkGetPhysicalDeviceFormatProperties( | |
| karaage::physicalDevice, | |
| imageFormat, | |
| &formatProperties | |
| ); | |
| if(!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)){ | |
| throw std::runtime_error("linear blitting is not supported with the requested format"); | |
| } | |
| VkCommandBuffer commandBuffer = BeginSingleTimeCommands(); | |
| VkImageMemoryBarrier barrier{ | |
| .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | |
| .image = image, | |
| .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | |
| .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | |
| .subresourceRange.baseArrayLayer = 0, | |
| .subresourceRange.layerCount = 1, | |
| .subresourceRange.levelCount = 1, | |
| }; | |
| int32_t mipWidth = texWidth; | |
| int32_t mipHeight = texHeight; | |
| //starting at 1 | |
| for (uint32_t i = 1; i < karaage::mipLevels; i++) { | |
| uint32_t srcMipLevel = i - 1; | |
| uint32_t dstMipLevel = i; | |
| barrier.subresourceRange.baseMipLevel = srcMipLevel; | |
| barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | |
| barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; | |
| barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | |
| barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | |
| vkCmdPipelineBarrier( | |
| commandBuffer, | |
| VK_PIPELINE_STAGE_TRANSFER_BIT, | |
| VK_PIPELINE_STAGE_TRANSFER_BIT, | |
| 0, | |
| 0, nullptr, | |
| 0, nullptr, | |
| 1, &barrier | |
| ); | |
| VkImageBlit blit{ | |
| .srcOffsets[0] = {0,0,0}, | |
| .srcOffsets[1] = { | |
| mipWidth, | |
| mipHeight, | |
| 1 | |
| }, | |
| .srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | |
| .srcSubresource.mipLevel = srcMipLevel, | |
| .srcSubresource.baseArrayLayer = 0, | |
| .srcSubresource.layerCount = 1, | |
| //DST config | |
| .dstOffsets[0] = { 0, 0, 0 }, | |
| .dstOffsets[1] = { | |
| mipWidth > 1 ? mipWidth / 2 : 1, | |
| mipHeight > 1 ? mipHeight / 2 : 1, | |
| 1 | |
| }, | |
| .dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | |
| .dstSubresource.mipLevel = dstMipLevel, | |
| .dstSubresource.baseArrayLayer = 0, | |
| .dstSubresource.layerCount = 1 | |
| }; | |
| vkCmdBlitImage( | |
| commandBuffer, | |
| image, | |
| VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | |
| image, | |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | |
| 1, | |
| &blit, | |
| VK_FILTER_LINEAR | |
| ); | |
| //barrier to make it shader ready; | |
| barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; | |
| barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; | |
| barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | |
| barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | |
| vkCmdPipelineBarrier( | |
| commandBuffer, | |
| VK_PIPELINE_STAGE_TRANSFER_BIT, | |
| VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, | |
| 0, | |
| 0, nullptr, | |
| 0, nullptr, | |
| 1, &barrier | |
| ); | |
| if (mipWidth > 1) mipWidth /= 2; | |
| if (mipHeight > 1) mipHeight /= 2; | |
| } | |
| //last miplevel transferred | |
| barrier.subresourceRange.baseMipLevel = mipLevels - 1; | |
| barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | |
| barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; | |
| barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | |
| barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | |
| vkCmdPipelineBarrier(commandBuffer, | |
| VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, | |
| 0, nullptr, | |
| 0, nullptr, | |
| 1, &barrier); | |
| EndSingleTimeCommands(commandBuffer); | |
| } | |
| /** | |
| * @brief Load image and create vulkan object | |
| */ | |
| void createTextureImage(){ | |
| int texWidth, texHeight, texChannels; | |
| stbi_uc* pixels = stbi_load( | |
| karaage::texturePath, | |
| &texWidth, | |
| &texHeight, | |
| &texChannels, | |
| STBI_rgb_alpha | |
| ); | |
| karaage::mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(texWidth,texHeight)))) + 1; | |
| VkDeviceSize imageSize = texWidth*texHeight*STBI_rgb_alpha; | |
| if(!pixels){ | |
| throw std::runtime_error("[EXCEPTION] Failed to load tex image"); | |
| } | |
| 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 | |
| ); | |
| //mmap | |
| void* data; | |
| vkMapMemory(karaage::logicalDevice, stagingBufferMemory, 0, imageSize, 0, &data); | |
| memcpy(data,pixels,static_cast<size_t>(imageSize)); | |
| vkUnmapMemory(karaage::logicalDevice, stagingBufferMemory); | |
| //cleanup | |
| stbi_image_free(pixels); | |
| //create image2D | |
| createImage2D( | |
| texWidth, | |
| texHeight, | |
| karaage::mipLevels, | |
| VK_SAMPLE_COUNT_1_BIT, | |
| VK_FORMAT_R8G8B8A8_SRGB, | |
| VK_IMAGE_TILING_OPTIMAL, | |
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, | |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
| karaage::textureImage, | |
| karaage::textureImageMemory | |
| ); | |
| //optimize for copy | |
| transitionImageLayout( | |
| karaage::textureImage, | |
| VK_FORMAT_R8G8B8A8_SRGB, | |
| VK_IMAGE_LAYOUT_UNDEFINED, | |
| VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | |
| karaage::mipLevels | |
| ); | |
| //copy | |
| copyBufferToImage( | |
| stagingBuffer, | |
| karaage::textureImage, | |
| static_cast<uint32_t>(texWidth), | |
| static_cast<uint32_t>(texHeight) | |
| ); | |
| //transition and mips gen; | |
| generateMipmaps(karaage::textureImage, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, karaage::mipLevels); | |
| //transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating mipmaps | |
| //optimize for sampling | |
| // transitionImageLayout( | |
| // karaage::textureImage, | |
| // VK_FORMAT_R8G8B8A8_SRGB, | |
| // VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | |
| // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, | |
| // karaage::mipLevels | |
| // ); | |
| //cleanup staging data; | |
| vkDestroyBuffer(karaage::logicalDevice, stagingBuffer, nullptr); | |
| vkFreeMemory(karaage::logicalDevice, stagingBufferMemory, nullptr); | |
| } | |
| void createTextureImageView(){ | |
| karaage::textureImageView = createImageView( | |
| karaage::textureImage, | |
| VK_FORMAT_R8G8B8A8_SRGB, | |
| VK_IMAGE_ASPECT_COLOR_BIT, | |
| karaage::mipLevels | |
| ); | |
| } | |
| void createTextureSampler(){ | |
| VkPhysicalDeviceProperties properties; | |
| vkGetPhysicalDeviceProperties(karaage::physicalDevice, &properties); | |
| VkSamplerCreateInfo createInfo{ | |
| .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | |
| .magFilter = VK_FILTER_LINEAR, //magnified oversampling | |
| .minFilter = VK_FILTER_LINEAR, //minified undersampling | |
| //mode | |
| .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, | |
| .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, | |
| .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, | |
| .anisotropyEnable = VK_TRUE, | |
| .maxAnisotropy = properties.limits.maxSamplerAnisotropy, | |
| .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, | |
| .unnormalizedCoordinates = VK_FALSE, | |
| .compareEnable = VK_FALSE, | |
| .compareOp = VK_COMPARE_OP_ALWAYS, | |
| .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, | |
| .mipLodBias = 0.0f, | |
| .minLod = 0.0f, | |
| .maxLod = static_cast<float>(karaage::mipLevels), | |
| }; | |
| if(vkCreateSampler( | |
| karaage::logicalDevice, | |
| &createInfo, | |
| nullptr, | |
| &karaage::textureSampler | |
| )!=VK_SUCCESS){ | |
| throw std::runtime_error("failed to create sampler"); | |
| } | |
| } | |
| void loadModel(){ | |
| tinyobj::attrib_t attrib; | |
| std::vector<tinyobj::shape_t> shapes; | |
| std::vector<tinyobj::material_t> materials; | |
| std::string warn, err; | |
| if(!tinyobj::LoadObj(&attrib,&shapes,&materials,&warn,&err,karaage::meshPath)){ | |
| throw std::runtime_error(warn+err); | |
| } | |
| std::unordered_map<vertex, uint32_t> uniqueVertices{}; | |
| for(const auto& shape: shapes){ | |
| for(const auto& index : shape.mesh.indices){ | |
| vertex v{ | |
| { | |
| attrib.vertices[3 * index.vertex_index + 0], | |
| attrib.vertices[3 * index.vertex_index + 1], | |
| attrib.vertices[3 * index.vertex_index + 2], | |
| }, | |
| { | |
| 1.0f,1.0f,1.0f | |
| }, | |
| { | |
| attrib.texcoords[2 * index.texcoord_index + 0], | |
| 1.0f - attrib.texcoords[2 * index.texcoord_index + 1], | |
| } | |
| }; | |
| if(uniqueVertices.count(v) == 0){ | |
| uniqueVertices[v] = static_cast<uint32_t>(karaage::vertices.size()); | |
| karaage::vertices.push_back(v); | |
| } | |
| karaage::indices.push_back(uniqueVertices[v]); | |
| } | |
| } | |
| } | |
| void createVertexBuffers(){ | |
| VkDeviceSize bufferSize = sizeof(karaage::vertices[0]) * karaage::vertices.size(); | |
| VkBuffer stagingBuffer; | |
| VkDeviceMemory stagingBufferMemory; | |
| std::cout << "creating staging buffer started" << std::endl; | |
| createBuffer( | |
| bufferSize, | |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, //src in memory transfer operation; | |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, | |
| stagingBuffer, | |
| stagingBufferMemory | |
| ); | |
| std::cout << "creating staging buffer success" << std::endl; | |
| //map the buffer memory | |
| //allows accessing region of specified mem defined by offset and size; | |
| void* data; | |
| vkMapMemory( | |
| karaage::logicalDevice, //that owns mem | |
| stagingBufferMemory, | |
| 0, //offset | |
| bufferSize, //size, can be used VK_WHOLE_SIZE to map all of memory; | |
| 0, //flags none for now in the API | |
| &data //must be aligned to minMemoryMapAlignment | |
| ); | |
| memcpy(data, karaage::vertices.data(), (size_t) bufferSize); | |
| vkUnmapMemory(karaage::logicalDevice, stagingBufferMemory); | |
| createBuffer( | |
| bufferSize, | |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, //dst bit in transfer | |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
| karaage::vertexBuffer, | |
| karaage::vertexBufferMemory | |
| ); | |
| copyBuffer(stagingBuffer, karaage::vertexBuffer, bufferSize); | |
| vkDestroyBuffer(karaage::logicalDevice, stagingBuffer, nullptr); | |
| vkFreeMemory(karaage::logicalDevice, stagingBufferMemory, nullptr); | |
| } | |
| void createIndexBuffer(){ | |
| VkDeviceSize bufferSize = sizeof(karaage::indices[0]) * karaage::indices.size(); | |
| VkBuffer stagingBuffer; | |
| VkDeviceMemory stagingBufferMemory; | |
| createBuffer( | |
| bufferSize, | |
| VK_BUFFER_USAGE_TRANSFER_SRC_BIT, | |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, | |
| stagingBuffer, | |
| stagingBufferMemory | |
| ); | |
| void* data; | |
| vkMapMemory(karaage::logicalDevice, stagingBufferMemory, 0, bufferSize, 0, &data); | |
| memcpy(data, karaage::indices.data(), (size_t) bufferSize); | |
| vkUnmapMemory(karaage::logicalDevice, stagingBufferMemory); | |
| createBuffer( | |
| bufferSize, | |
| VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, | |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | |
| karaage::indexBuffer, | |
| karaage::indexBufferMemory | |
| ); | |
| copyBuffer(stagingBuffer, karaage::indexBuffer, bufferSize); | |
| vkDestroyBuffer(karaage::logicalDevice, stagingBuffer, nullptr); | |
| vkFreeMemory(karaage::logicalDevice, stagingBufferMemory, nullptr); | |
| } | |
| void createComputeUniformBuffers() { | |
| VkDeviceSize bufferSize = sizeof(UniformBufferObject); | |
| karaage::computeUBOs.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::computeUBOsMemory.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::computeUBOsMapped.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| for (size_t i = 0; i < karaage::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, | |
| karaage::computeUBOs[i], | |
| karaage::computeUBOsMemory[i]); | |
| vkMapMemory( | |
| karaage::logicalDevice, | |
| karaage::computeUBOsMemory[i], | |
| 0, | |
| bufferSize, | |
| 0, | |
| &karaage::computeUBOsMapped[i] | |
| ); | |
| } | |
| } | |
| void createUniformBuffers(){ | |
| VkDeviceSize bufferSize = sizeof(UniformBufferObject); | |
| karaage::uniformBuffers.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::uniformBuffersMemory.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::uniformBuffersMapped.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| for(size_t i = 0; i < karaage::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, | |
| karaage::uniformBuffers[i], | |
| karaage::uniformBuffersMemory[i] | |
| ); | |
| //using persistent mapping here | |
| if(vkMapMemory( | |
| karaage::logicalDevice, | |
| karaage::uniformBuffersMemory[i], | |
| 0, | |
| bufferSize, | |
| 0, | |
| &karaage::uniformBuffersMapped[i] | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create UBO"); | |
| } | |
| } | |
| } | |
| void createComputeDescriptorPool(){ | |
| std::array<VkDescriptorPoolSize, 2> poolSizes{{ | |
| { | |
| .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | |
| .descriptorCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT) | |
| }, | |
| { | |
| .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, | |
| .descriptorCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT) * 2 | |
| } | |
| }}; | |
| VkDescriptorPoolCreateInfo poolInfo{ | |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | |
| .poolSizeCount = static_cast<uint32_t>(poolSizes.size()), | |
| .pPoolSizes = poolSizes.data(), | |
| .maxSets = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT) | |
| }; | |
| if(vkCreateDescriptorPool( | |
| karaage::logicalDevice, | |
| &poolInfo, | |
| nullptr, | |
| &karaage::computeDescriptorPool | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create descriptor pool"); | |
| } | |
| } | |
| void createDescriptorPool(){ | |
| std::array<VkDescriptorPoolSize, 2> poolSizes{{ | |
| { | |
| .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | |
| .descriptorCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT) | |
| }, | |
| { | |
| .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | |
| .descriptorCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT) | |
| } | |
| }}; | |
| VkDescriptorPoolCreateInfo poolInfo{ | |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | |
| .poolSizeCount = static_cast<uint32_t>(poolSizes.size()), | |
| .pPoolSizes = poolSizes.data(), | |
| .maxSets = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT) | |
| }; | |
| if(vkCreateDescriptorPool( | |
| karaage::logicalDevice, | |
| &poolInfo, | |
| nullptr, | |
| &karaage::descriptorPool | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create descriptor pool"); | |
| } | |
| } | |
| void createDescriptorSets(){ | |
| std::vector<VkDescriptorSetLayout> layouts( | |
| karaage::MAX_FRAMES_IN_FLIGHT, | |
| karaage::descriptorSetLayout | |
| ); | |
| VkDescriptorSetAllocateInfo allocInfo{ | |
| .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, | |
| .descriptorPool = karaage::descriptorPool, | |
| .descriptorSetCount = static_cast<uint32_t>(karaage::MAX_FRAMES_IN_FLIGHT), | |
| .pSetLayouts = layouts.data() | |
| }; | |
| karaage::descriptorSets.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| if(vkAllocateDescriptorSets( | |
| karaage::logicalDevice, | |
| &allocInfo, | |
| karaage::descriptorSets.data() | |
| )!= VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] failed to allocate descriptor sets"); | |
| } | |
| std::cout << "Descriptor set allocated" << std::endl; | |
| for(size_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){ | |
| VkDescriptorBufferInfo bufferInfo{ | |
| .buffer = karaage::uniformBuffers[i], | |
| .offset = 0, | |
| .range = sizeof(UniformBufferObject) | |
| }; | |
| VkDescriptorImageInfo imageInfo{ | |
| .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, | |
| .imageView = karaage::textureImageView, | |
| .sampler = karaage::textureSampler | |
| }; | |
| std::array<VkWriteDescriptorSet, 2> descriptorWrites{ | |
| { | |
| { | |
| .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | |
| .dstSet = karaage::descriptorSets[i], | |
| .dstBinding = 0, | |
| .dstArrayElement = 0, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | |
| .descriptorCount = 1, | |
| .pBufferInfo = &bufferInfo, | |
| }, | |
| { | |
| .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | |
| .dstSet = karaage::descriptorSets[i], | |
| .dstBinding = 1, | |
| .dstArrayElement = 0, | |
| .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | |
| .descriptorCount = 1, | |
| .pImageInfo = &imageInfo, | |
| } | |
| } | |
| }; | |
| vkUpdateDescriptorSets( | |
| karaage::logicalDevice, | |
| static_cast<uint32_t>(descriptorWrites.size()), | |
| descriptorWrites.data(), | |
| 0, | |
| nullptr | |
| ); | |
| } | |
| } | |
| void createComputeCommandBuffers() { | |
| karaage::computeCommandBuffers.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| VkCommandBufferAllocateInfo allocInfo{ | |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| .commandPool = karaage::commandPool, | |
| .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
| .commandBufferCount = (uint32_t)karaage::computeCommandBuffers.size(), | |
| }; | |
| if (vkAllocateCommandBuffers( | |
| karaage::logicalDevice, | |
| &allocInfo, | |
| karaage::computeCommandBuffers.data() | |
| ) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to allocate compute command buffers!"); | |
| } | |
| } | |
| void createCommandBuffers(){ | |
| karaage::commandBuffers.resize(karaage::MAX_FRAMES_IN_FLIGHT); | |
| VkCommandBufferAllocateInfo cbAllocInfo{ | |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| .commandPool = karaage::commandPool, | |
| .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,//(primary/secondary; secondary can be reused); | |
| .commandBufferCount = karaage::MAX_FRAMES_IN_FLIGHT | |
| }; | |
| if(vkAllocateCommandBuffers( | |
| karaage::logicalDevice, | |
| &cbAllocInfo, | |
| karaage::commandBuffers.data() | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create command buffer"); | |
| } | |
| } | |
| void createSyncObjects(){ | |
| karaage::imageAvailableSemaphores.reserve(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::renderFinishedSemaphores.reserve(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::inFlightFences.reserve(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::computeFinishedSemaphores.reserve(karaage::MAX_FRAMES_IN_FLIGHT); | |
| karaage::computeInFlightFences.reserve(karaage::MAX_FRAMES_IN_FLIGHT); | |
| VkSemaphoreCreateInfo semaphoreInfo{ | |
| .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO | |
| }; | |
| VkFenceCreateInfo fenceInfo{ | |
| .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, | |
| .flags = VK_FENCE_CREATE_SIGNALED_BIT | |
| }; | |
| for(uint32_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){ | |
| //GRAPHICS PIPE SYNC OBJECTS | |
| if(vkCreateSemaphore( | |
| karaage::logicalDevice, | |
| &semaphoreInfo, | |
| nullptr, | |
| &karaage::imageAvailableSemaphores[i] | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create image available semaphore"); | |
| } | |
| if(vkCreateSemaphore( | |
| karaage::logicalDevice, | |
| &semaphoreInfo, | |
| nullptr, | |
| &karaage::renderFinishedSemaphores[i] | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create render finished semaphore"); | |
| } | |
| if(vkCreateFence( | |
| karaage::logicalDevice, | |
| &fenceInfo, | |
| nullptr, | |
| &karaage::inFlightFences[i] | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create inFlight fence"); | |
| } | |
| // COMPUTE SYNC objects | |
| if(vkCreateSemaphore( | |
| karaage::logicalDevice, | |
| &semaphoreInfo, | |
| nullptr, | |
| &karaage::computeFinishedSemaphores[i] | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create compute finished semaphore"); | |
| } | |
| if(vkCreateFence( | |
| karaage::logicalDevice, | |
| &fenceInfo, | |
| nullptr, | |
| &karaage::computeInFlightFences[i] | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to create conpute inFlight fence"); | |
| } | |
| } | |
| } | |
| void recordComputeCommandBuffer(VkCommandBuffer commandBuffer){ | |
| VkCommandBufferBeginInfo beginInfo{ | |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO | |
| }; | |
| if(vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to begin recording command buffer"); | |
| } | |
| vkCmdBindPipeline( | |
| commandBuffer, | |
| VK_PIPELINE_BIND_POINT_COMPUTE, | |
| karaage::computePipeline | |
| ); | |
| vkCmdBindDescriptorSets( | |
| commandBuffer, | |
| VK_PIPELINE_BIND_POINT_COMPUTE, | |
| karaage::computePipelineLayout, | |
| 0, | |
| 1, | |
| &karaage::computeDescriptorSets[karaage::currentFrame], | |
| 0, | |
| nullptr | |
| ); | |
| vkCmdDispatch( | |
| commandBuffer, | |
| karaage::MAX_PARTICLES / 256, | |
| 1, | |
| 1 | |
| ); | |
| if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to record compute command buffer!"); | |
| } | |
| } | |
| void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){ | |
| VkCommandBufferBeginInfo beginInfo{ | |
| .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| .flags = 0, //opt | |
| .pInheritanceInfo = nullptr//opt | |
| }; | |
| if(vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to begin recording command buffer"); | |
| } | |
| std::array<VkClearValue,2> clearValues{}; | |
| clearValues[0].color = {{0.0f,0.0f,0.0f,1.0f}}; | |
| clearValues[1].depthStencil = {1.0f,0}; | |
| VkRenderPassBeginInfo renderPassInfo{ | |
| .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | |
| .renderPass = karaage::renderPass, | |
| .framebuffer = karaage::swapChainFramebuffers[imageIndex], | |
| //size of render area; | |
| .renderArea.offset = {0,0}, | |
| .renderArea.extent = karaage::swapChainExtent, | |
| //clear | |
| .clearValueCount = static_cast<uint32_t>(clearValues.size()), | |
| .pClearValues = clearValues.data() | |
| }; | |
| //to, renderpass detail, how commands will be provided; (inline or secondary buffer); | |
| vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); | |
| // | |
| //--------------------------------COMMANDS GO HERE: | |
| // | |
| //Bind pipe (graphics or compute) | |
| //particle system draw | |
| VkDeviceSize offsets[] = {0}; | |
| //set scissor and viewport (they are dynamic) | |
| VkViewport viewport{ | |
| .x = 0.0f, | |
| .y = 0.0f, | |
| .width = static_cast<float>(karaage::swapChainExtent.width), | |
| .height = static_cast<float>(karaage::swapChainExtent.height), | |
| .minDepth = 0.0f, | |
| .maxDepth = 1.0f | |
| }; | |
| vkCmdSetViewport(commandBuffer, 0, 1, &viewport); | |
| // scissor | |
| VkRect2D scissor{ | |
| .offset = {0,0}, | |
| .extent = karaage::swapChainExtent | |
| }; | |
| vkCmdSetScissor(commandBuffer, 0, 1, &scissor); | |
| //mesh draw | |
| vkCmdBindPipeline( | |
| commandBuffer, | |
| VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| karaage::graphicsPipeline[0] | |
| ); | |
| //bind vertex buffer | |
| VkBuffer vertexBuffers[] = {karaage::vertexBuffer}; | |
| vkCmdBindVertexBuffers( | |
| commandBuffer, | |
| 0, | |
| 1, | |
| vertexBuffers, //old render | |
| offsets | |
| ); | |
| vkCmdBindIndexBuffer( | |
| commandBuffer, | |
| karaage::indexBuffer, | |
| 0, | |
| VK_INDEX_TYPE_UINT32 | |
| ); | |
| vkCmdBindDescriptorSets( | |
| commandBuffer, | |
| VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| karaage::pipelineLayout, | |
| 0, | |
| 1, | |
| &karaage::descriptorSets[karaage::currentFrame], | |
| 0, | |
| nullptr | |
| ); | |
| //draw (buf, vtxCount, instanceCount, firstVtx (offset), firstInstance (offset)) | |
| vkCmdDrawIndexed( | |
| commandBuffer, | |
| static_cast<uint32_t>(karaage::indices.size()), | |
| 1, | |
| 0, | |
| 0, | |
| 0 | |
| ); | |
| vkCmdBindPipeline( | |
| commandBuffer, | |
| VK_PIPELINE_BIND_POINT_GRAPHICS, | |
| karaage::graphicsPipeline[1] | |
| ); | |
| vkCmdBindVertexBuffers( | |
| commandBuffer, | |
| 0, | |
| 1, | |
| &karaage::shaderStorageBuffers[karaage::currentFrame], | |
| offsets | |
| ); | |
| vkCmdDraw( | |
| commandBuffer, | |
| karaage::MAX_PARTICLES, | |
| 1, | |
| 0, | |
| 0 | |
| ); | |
| //Finished recording commands to pass; | |
| vkCmdEndRenderPass(commandBuffer); | |
| if(vkEndCommandBuffer(commandBuffer) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to record command buffer"); | |
| } | |
| } | |
| //ALL init code | |
| void initVulkan(){ | |
| createVKInstance();//done | |
| setupDebugMessenger();//done | |
| createSurface();//done | |
| pickPhysicalDevice();//done | |
| createLogicalDevice();//done | |
| createSwapChain();//done (SC obj) | |
| createImageViews();//done (SC image views) | |
| createRenderPass();//done (where to render) | |
| createComputeDescriptorSetLayout();//done | |
| createDescriptorSetLayout();//done | |
| createGraphicsPipeline();//done (default) | |
| createComputePipeline();//done (default compute) | |
| createCommandPool(); | |
| createShaderStorageBuffers(); //particles; | |
| // passes stuff | |
| createColorResource(); | |
| createDepthResource(); | |
| createFramebuffers(); | |
| //model tex | |
| createTextureImage(); | |
| createTextureImageView(); | |
| createTextureSampler(); | |
| //model | |
| loadModel(); | |
| createVertexBuffers(); | |
| createIndexBuffer(); | |
| //shader stuff | |
| createUniformBuffers(); | |
| createDescriptorPool(); | |
| createDescriptorSets(); | |
| createComputeUniformBuffers(); | |
| createComputeDescriptorPool(); | |
| createComputeDescriptorSets(); | |
| createCommandBuffers(); | |
| createComputeCommandBuffers(); | |
| createSyncObjects(); | |
| } | |
| void updateComputeUniformBuffer(uint32_t currentImage) { | |
| UniformBufferObjectTime ubo{}; | |
| ubo.deltaTime = karaage::lastFrameTime * 2.0f; | |
| memcpy(karaage::computeUBOsMapped[currentImage], &ubo, sizeof(ubo)); | |
| } | |
| //ticked update | |
| void updateUniformBuffer(uint32_t currentFrame){ | |
| static auto startTime = std::chrono::high_resolution_clock::now(); | |
| auto currentTime = std::chrono::high_resolution_clock::now(); | |
| float time = std::chrono::duration<float,std::chrono::seconds::period>(currentTime-startTime).count(); | |
| UniformBufferObject ubo{}; | |
| ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f,0.0f,1.0f)); | |
| ubo.view = glm::lookAt(glm::vec3(2.0f,2.0f,2.0f), glm::vec3(0.0f,0.0f,0.0f), glm::vec3(0.0f,0.0f,1.0f)); | |
| ubo.proj = glm::perspective( | |
| glm::radians(45.0f), | |
| karaage::swapChainExtent.width/ (float) karaage::swapChainExtent.height, | |
| 0.1f, | |
| 10.0f | |
| ); | |
| //Y flip | |
| ubo.proj[1][1] *= -1; | |
| //mmcpy | |
| memcpy(karaage::uniformBuffersMapped[currentFrame],&ubo, sizeof(ubo)); | |
| } | |
| //Sync using binary semaphores | |
| void drawFrame(){ | |
| // Compute submission | |
| vkWaitForFences( | |
| karaage::logicalDevice, | |
| 1, | |
| &karaage::computeInFlightFences[karaage::currentFrame], | |
| VK_TRUE, | |
| UINT64_MAX | |
| ); | |
| //update compute uniform buffer; | |
| updateComputeUniformBuffer(karaage::currentFrame); | |
| vkResetFences( | |
| karaage::logicalDevice, | |
| 1, | |
| &karaage::computeInFlightFences[karaage::currentFrame] | |
| ); | |
| vkResetCommandBuffer( | |
| karaage::computeCommandBuffers[karaage::currentFrame], | |
| /*VkCommandBufferResetFlagBits*/ 0 | |
| ); | |
| recordComputeCommandBuffer( | |
| karaage::computeCommandBuffers[karaage::currentFrame] | |
| ); | |
| VkSubmitInfo submitInfo{ | |
| .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| .commandBufferCount = 1, | |
| .pCommandBuffers = &karaage::computeCommandBuffers[karaage::currentFrame], | |
| .signalSemaphoreCount = 1, | |
| .pSignalSemaphores = &karaage::computeFinishedSemaphores[karaage::currentFrame], | |
| }; | |
| if (vkQueueSubmit( | |
| karaage::computeQueue, | |
| 1, | |
| &submitInfo, | |
| karaage::computeInFlightFences[karaage::currentFrame] | |
| ) != VK_SUCCESS) { | |
| throw std::runtime_error("failed to submit compute command buffer!"); | |
| }; | |
| // | |
| //GRAPHICS SUBMIT | |
| // | |
| vkWaitForFences( | |
| karaage::logicalDevice, | |
| 1, | |
| &karaage::inFlightFences[karaage::currentFrame], | |
| VK_TRUE, | |
| UINT64_MAX); | |
| // | |
| //acquire image from swapchain (async) | |
| // | |
| uint32_t imageIndex; | |
| VkResult acquireResult = vkAcquireNextImageKHR( | |
| karaage::logicalDevice, | |
| karaage::swapChain, | |
| UINT64_MAX, | |
| karaage::imageAvailableSemaphores[karaage::currentFrame], | |
| VK_NULL_HANDLE, | |
| &imageIndex | |
| ); | |
| //check if acquired image is still valid and recreate | |
| if( | |
| acquireResult == VK_ERROR_OUT_OF_DATE_KHR | |
| || acquireResult == VK_SUBOPTIMAL_KHR | |
| ){ | |
| recreateSwapChain(); | |
| return; | |
| }else if( | |
| acquireResult != VK_SUCCESS | |
| && acquireResult != VK_SUBOPTIMAL_KHR | |
| ){ | |
| throw std::runtime_error("[Exception] Failed to acquire swapchain image"); | |
| } | |
| //update GFX UBOs (here we rotate image) | |
| updateUniformBuffer(karaage::currentFrame); | |
| //resetting later as we might not yet sumbitted any work which will cause deadlock | |
| vkResetFences( | |
| karaage::logicalDevice, | |
| 1, | |
| &karaage::inFlightFences[karaage::currentFrame]); | |
| // | |
| //record commandbuffer | |
| // | |
| vkResetCommandBuffer(karaage::commandBuffers[karaage::currentFrame], 0); | |
| recordCommandBuffer(karaage::commandBuffers[karaage::currentFrame], imageIndex); | |
| //which semaphores to fait for | |
| VkSemaphore waitSemaphores[] = { | |
| karaage::computeFinishedSemaphores[karaage::currentFrame], | |
| karaage::imageAvailableSemaphores[karaage::currentFrame] | |
| }; | |
| //waiting for both stages | |
| VkPipelineStageFlags waitStages[] = { | |
| VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, | |
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | |
| }; | |
| //signal render finished | |
| VkSemaphore signalSemaphores[] = { | |
| karaage::renderFinishedSemaphores[karaage::currentFrame] | |
| }; | |
| //submit recorded buffer (async) | |
| submitInfo = { | |
| .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| //what semaphores to wait for at which stage | |
| .waitSemaphoreCount = 2, | |
| .pWaitSemaphores = waitSemaphores, | |
| .pWaitDstStageMask = waitStages, | |
| //setting command buffer | |
| .commandBufferCount = 1, | |
| .pCommandBuffers = &karaage::commandBuffers[karaage::currentFrame], | |
| //what sempaphores to signal once commandbuffers finished execution; | |
| .signalSemaphoreCount = 1, | |
| .pSignalSemaphores = signalSemaphores, | |
| }; | |
| if(vkQueueSubmit( | |
| karaage::graphicsQueue, | |
| 1, | |
| &submitInfo, | |
| karaage::inFlightFences[karaage::currentFrame] | |
| ) != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] Failed to submit graphics queue"); | |
| } | |
| VkSwapchainKHR swapChains[] = { | |
| karaage::swapChain | |
| }; | |
| //present swapchain image (async) | |
| VkPresentInfoKHR presentInfo{ | |
| .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, | |
| .waitSemaphoreCount = 1, | |
| .pWaitSemaphores = signalSemaphores, | |
| .swapchainCount = 1, | |
| .pSwapchains = swapChains, | |
| .pImageIndices = &imageIndex, | |
| .pResults = nullptr | |
| }; | |
| //PRESENT! | |
| VkResult presentResult = vkQueuePresentKHR( | |
| karaage::presentQueue, | |
| &presentInfo | |
| ); | |
| if(presentResult == VK_ERROR_OUT_OF_DATE_KHR | |
| || presentResult == VK_SUBOPTIMAL_KHR | |
| || karaage::framebufferResized | |
| ){ | |
| karaage::framebufferResized = false; | |
| recreateSwapChain(); | |
| }else if(presentResult != VK_SUCCESS){ | |
| throw std::runtime_error("[EXCEPTION] failed to present swap chain image"); | |
| } | |
| karaage::currentFrame = (karaage::currentFrame + 1) % karaage::MAX_FRAMES_IN_FLIGHT; | |
| } | |
| void mainLoop(){ | |
| while (!glfwWindowShouldClose(karaage::window)) { | |
| glfwPollEvents(); | |
| drawFrame(); | |
| } | |
| vkDeviceWaitIdle(karaage::logicalDevice); | |
| } | |
| //dealloc resources | |
| void cleanup(){ | |
| std::cout << "[Karaage] Cleanup started" << std::endl; | |
| cleanupSwapChain(); | |
| vkDestroySampler(karaage::logicalDevice, karaage::textureSampler, nullptr); | |
| vkDestroyImageView(karaage::logicalDevice, karaage::textureImageView, nullptr); | |
| vkDestroyImage(karaage::logicalDevice, karaage::textureImage, nullptr); | |
| vkFreeMemory(karaage::logicalDevice, karaage::textureImageMemory, nullptr); | |
| for(size_t i = 0; i<2; i++){ | |
| vkDestroyPipeline(karaage::logicalDevice, karaage::graphicsPipeline[i], nullptr); | |
| } | |
| vkDestroyPipelineLayout(karaage::logicalDevice, karaage::pipelineLayout, nullptr); | |
| vkDestroyRenderPass(karaage::logicalDevice, karaage::renderPass, nullptr); | |
| for(size_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){ | |
| vkDestroyBuffer(karaage::logicalDevice, karaage::uniformBuffers[i], nullptr); | |
| vkFreeMemory(karaage::logicalDevice, karaage::uniformBuffersMemory[i], nullptr); | |
| } | |
| vkDestroyDescriptorPool(karaage::logicalDevice, karaage::computeDescriptorPool, nullptr); | |
| vkDestroyDescriptorSetLayout(karaage::logicalDevice, karaage::computeDescriptorSetLayout, nullptr); | |
| vkDestroyDescriptorPool(karaage::logicalDevice, karaage::descriptorPool, nullptr); | |
| vkDestroyDescriptorSetLayout(karaage::logicalDevice, karaage::descriptorSetLayout, nullptr); | |
| vkDestroyBuffer(karaage::logicalDevice, karaage::indexBuffer, nullptr); | |
| vkFreeMemory(karaage::logicalDevice, karaage::indexBufferMemory, nullptr); | |
| vkDestroyBuffer(karaage::logicalDevice, karaage::vertexBuffer, nullptr); | |
| vkFreeMemory(karaage::logicalDevice, karaage::vertexBufferMemory, nullptr); | |
| for(uint32_t i = 0; i<karaage::MAX_FRAMES_IN_FLIGHT; i++){ | |
| vkDestroySemaphore(karaage::logicalDevice, karaage::imageAvailableSemaphores[i], nullptr); | |
| vkDestroySemaphore(karaage::logicalDevice, karaage::renderFinishedSemaphores[i], nullptr); | |
| vkDestroyFence(karaage::logicalDevice, karaage::inFlightFences[i], nullptr); | |
| } | |
| vkDestroyCommandPool(karaage::logicalDevice, karaage::commandPool, nullptr); | |
| vkDestroyDevice(karaage::logicalDevice, nullptr); | |
| if(karaage::enableValidationLayers){ | |
| DestroyDebugUtilsMessengerEXT( | |
| karaage::vkInstance, | |
| karaage::debugMessenger, | |
| nullptr | |
| ); | |
| } | |
| vkDestroySurfaceKHR(karaage::vkInstance, karaage::surface, nullptr); | |
| vkDestroyInstance(karaage::vkInstance, nullptr); | |
| glfwDestroyWindow(karaage::window); | |
| glfwTerminate(); | |
| } | |
| void run(){ | |
| initCLI(); | |
| initWindow(); | |
| initVulkan(); | |
| mainLoop(); | |
| cleanup(); | |
| } | |
| int main() { | |
| try{ | |
| run(); | |
| }catch( const std::exception& e){ | |
| std::cerr << e.what() << std::endl; | |
| return EXIT_FAILURE; | |
| } | |
| return EXIT_SUCCESS; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment