From a6eb297a78b4ed215d82c21c0efd6594ef86a8fe Mon Sep 17 00:00:00 2001 From: Benjamin Kraft Date: Wed, 4 Sep 2024 21:05:44 +0200 Subject: [PATCH] refactoring everything into multiple classes --- CMakeLists.txt | 2 +- src/main.cpp | 920 +----------------------------------- src/pipeline.cpp | 2 - src/pipeline.hpp | 0 src/vulkan/application.cpp | 107 +++++ src/vulkan/application.hpp | 103 ++++ src/vulkan/buffer.cpp | 55 +++ src/vulkan/buffer.hpp | 19 + src/vulkan/command_pool.cpp | 32 ++ src/vulkan/command_pool.hpp | 16 + src/vulkan/instance.cpp | 237 ++++++++++ src/vulkan/instance.hpp | 46 ++ src/vulkan/pipeline.cpp | 149 ++++++ src/vulkan/pipeline.hpp | 15 + src/vulkan/swapchain.cpp | 215 +++++++++ src/vulkan/swapchain.hpp | 45 ++ src/vulkan/vertex.cpp | 1 + src/vulkan/vertex.hpp | 34 ++ 18 files changed, 1078 insertions(+), 920 deletions(-) delete mode 100644 src/pipeline.cpp delete mode 100644 src/pipeline.hpp create mode 100644 src/vulkan/application.cpp create mode 100644 src/vulkan/application.hpp create mode 100644 src/vulkan/buffer.cpp create mode 100644 src/vulkan/buffer.hpp create mode 100644 src/vulkan/command_pool.cpp create mode 100644 src/vulkan/command_pool.hpp create mode 100644 src/vulkan/instance.cpp create mode 100644 src/vulkan/instance.hpp create mode 100644 src/vulkan/pipeline.cpp create mode 100644 src/vulkan/pipeline.hpp create mode 100644 src/vulkan/swapchain.cpp create mode 100644 src/vulkan/swapchain.hpp create mode 100644 src/vulkan/vertex.cpp create mode 100644 src/vulkan/vertex.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f84f1b..4bfe38b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(VulkanSimulation) set(CMAKE_CXX_STANDARD 20) -file(GLOB SRC_FILES src/*.cpp) +file(GLOB_RECURSE SRC_FILES src/**.cpp) add_executable(VulkanSimulation ${SRC_FILES}) find_package(Vulkan REQUIRED) diff --git a/src/main.cpp b/src/main.cpp index 4d9d464..42520e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,921 +1,7 @@ -#define GLFW_INCLUDE_VULKAN -#include +#include "vulkan/application.hpp" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const std::vector deviceExtensions = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME -}; - -#ifndef NDEBUG -#define ENABLE_VALIDATION_LAYERS -#endif - -#ifdef ENABLE_VALIDATION_LAYERS -const std::vector validationLayers = { - "VK_LAYER_KHRONOS_validation" -}; -bool checkValidationLayerSupport(){ - - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - for (const char* layerName : validationLayers){ - bool layerFound = false; - - for (const auto& layerProperties : availableLayers){ - if (strcmp(layerName, layerProperties.layerName) == 0){ - layerFound = true; - break; - } - } - - if (!layerFound){ - return false; - } - } - - return true; -} -#endif - -constexpr int MAX_FRAMES_IN_FLIGHT = 2; - -class Timer { -public: - explicit Timer(){ - start = std::chrono::system_clock::now(); - } - ~Timer(){ - size_t nanoseconds = (std::chrono::system_clock::now() - start).count(); - printf("Timer: %zu mus\n", nanoseconds / 1000); - } -private: - std::chrono::time_point start; -}; - -std::vector readFile(const std::string& fileName){ - std::ifstream file(fileName, std::ios::ate | std::ios::binary); - - if (!file.is_open()){ - throw std::runtime_error("failed to open file!"); - } - - size_t fileSize = file.tellg(); - std::vector buffer(fileSize); - - file.seekg(0); - file.read(buffer.data(), static_cast(fileSize)); - file.close(); - - return buffer; -} - -struct Vertex { - glm::vec2 pos; - glm::vec3 color; - - static VkVertexInputBindingDescription getBindingDescription(){ - VkVertexInputBindingDescription bindingDescription {}; - bindingDescription.binding = 0; - bindingDescription.stride = sizeof(Vertex); - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - return bindingDescription; - } - - static std::array getAttributeDescriptions(){ - std::array attributeDescriptions {}; - - attributeDescriptions[0].binding = 0; - attributeDescriptions[0].location = 0; - attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; - attributeDescriptions[0].offset = offsetof(Vertex, pos); - - attributeDescriptions[1].binding = 0; - attributeDescriptions[1].location = 1; - attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; - attributeDescriptions[1].offset = offsetof(Vertex, color); - - return attributeDescriptions; - } -}; - -const std::vector vertices = { - {{0.0, -0.5}, {1, 0, 0}}, - {{0.5, 0.5}, {0, 1, 0}}, - {{-0.5, 0.5}, {0, 0, 1}} -}; - -class MyApp { -public: - void run(){ - initWindow(); - initVulkan(); - mainLoop(); - cleanup(); - } -private: - GLFWwindow *window = nullptr; - - void initWindow(){ - glfwInit(); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - window = glfwCreateWindow(800, 600, "Vulkan Simulation", nullptr, nullptr); - } - void initVulkan(){ - createInstance(); - createSurface(); - pickPhysicalDevice(); - createLogicalDevice(); - createSwapchain(); - createImageViews(); - createRenderPass(); - createGraphicsPipeline(); - createFramebuffers(); - createVertexBuffer(); - createCommandPool(); - createCommandBuffers(); - createSyncObjects(); - } - - VkInstance instance = VK_NULL_HANDLE; - void createInstance(){ -#ifdef ENABLE_VALIDATION_LAYERS - if (!checkValidationLayerSupport()){ - throw std::runtime_error("validation layers requested, but not available!"); - } -#endif - - VkApplicationInfo applicationInfo {}; - applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - applicationInfo.pApplicationName = "Coole Simulation"; - applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - applicationInfo.pEngineName = "No Engine"; - applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); - applicationInfo.apiVersion = VK_API_VERSION_1_0; - - uint32_t glfwExtensionCount = 0; - const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - - VkInstanceCreateInfo instanceCreateInfo {}; - instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instanceCreateInfo.pApplicationInfo = &applicationInfo; - instanceCreateInfo.enabledExtensionCount = glfwExtensionCount; - instanceCreateInfo.ppEnabledExtensionNames = glfwExtensions; -#ifdef ENABLE_VALIDATION_LAYERS - instanceCreateInfo.enabledLayerCount = validationLayers.size(); - instanceCreateInfo.ppEnabledLayerNames = validationLayers.data(); -#else - instanceCreateInfo.enabledLayerCount = 0; -#endif - - if (vkCreateInstance(&instanceCreateInfo, nullptr, &instance) != VK_SUCCESS){ - throw std::runtime_error("failed to create instance!"); - } - } - - VkSurfaceKHR surface = VK_NULL_HANDLE; - void createSurface(){ - if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS){ - throw std::runtime_error("failed to create window surface!"); - } - } - - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - void pickPhysicalDevice(){ - uint32_t deviceCount = 0; - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); - if (deviceCount == 0){ - throw std::runtime_error("failed to find GPUs with Vulkan support!"); - } - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); - - for (const VkPhysicalDevice &device : devices){ - if (isDeviceSuitable(device)){ - physicalDevice = device; - break; - } - } - - if (physicalDevice == VK_NULL_HANDLE){ - throw std::runtime_error("failed to find a suitable GPU!"); - } - - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(physicalDevice, &properties); - printf("Picked physical device: %s\n", properties.deviceName); - } - - struct QueueFamilyIndices { - std::optional graphicsFamily; - std::optional computeFamily; - std::optional presentFamily; - bool isComplete() const { - return graphicsFamily.has_value() && computeFamily.has_value() && presentFamily.has_value(); - } - std::set uniqueQueueFamilies(){ - return {graphicsFamily.value(), presentFamily.value(), computeFamily.value()}; - } - }; - QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device){ - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - QueueFamilyIndices indices {}; - - uint32_t i = 0; - for (const VkQueueFamilyProperties& queueFamilyProperties : queueFamilies){ - if (queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT){ - indices.graphicsFamily = i; - } - if (queueFamilyProperties.queueFlags & VK_QUEUE_COMPUTE_BIT){ - indices.computeFamily = i; - } - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (presentSupport){ - indices.presentFamily = i; - } - if (indices.isComplete()){ - break; - } - i++; - } - return indices; - } - - bool isDeviceSuitable(VkPhysicalDevice device){ - VkPhysicalDeviceProperties deviceProperties; - vkGetPhysicalDeviceProperties(device, &deviceProperties); - - VkPhysicalDeviceFeatures deviceFeatures; - vkGetPhysicalDeviceFeatures(device, &deviceFeatures); - - QueueFamilyIndices indices = findQueueFamilies(device); - - bool extensionsSupported = checkDeviceExtensionSupport(device); - - bool swapChainAdequate = false; - if (extensionsSupported){ - SwapchainSupportDetails details = querySwapchainSupport(device); - swapChainAdequate = !details.formats.empty() && !details.presentModes.empty(); - } - - return indices.isComplete() && extensionsSupported && swapChainAdequate; - } - - static bool checkDeviceExtensionSupport(VkPhysicalDevice device){ - std::set required(deviceExtensions.begin(), deviceExtensions.end()); - - uint32_t extensionCount; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); - - std::vector availableExtensions(extensionCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); - - for (const VkExtensionProperties& extension : availableExtensions){ - required.erase(extension.extensionName); - } - - return required.empty(); - } - - struct SwapchainSupportDetails { - VkSurfaceCapabilitiesKHR capabilities {}; - std::vector formats; - std::vector presentModes; - }; - SwapchainSupportDetails querySwapchainSupport(VkPhysicalDevice device){ - SwapchainSupportDetails details; - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); - - uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); - if (formatCount != 0){ - details.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); - } - - uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); - if (presentModeCount != 0){ - details.presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); - } - - return details; - } - - VkDevice device = VK_NULL_HANDLE; - VkQueue graphicsQueue = VK_NULL_HANDLE; - VkQueue presentQueue = VK_NULL_HANDLE; - void createLogicalDevice(){ - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); - - std::vector queueCreateInfos; - - float queuePriority = 1.0f; - for (uint32_t queueFamily : indices.uniqueQueueFamilies()){ - VkDeviceQueueCreateInfo queueCreateInfo {}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = queueFamily; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - queueCreateInfos.push_back(queueCreateInfo); - } - - VkPhysicalDeviceFeatures deviceFeatures {}; - - VkDeviceCreateInfo createInfo {}; - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = queueCreateInfos.size(); - createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = deviceExtensions.size(); - createInfo.ppEnabledExtensionNames = deviceExtensions.data(); -#ifdef ENABLE_VALIDATION_LAYERS - createInfo.enabledLayerCount = validationLayers.size(); - createInfo.ppEnabledLayerNames = validationLayers.data(); -#else - createInfo.enabledLayerCount = 0; -#endif - - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS){ - throw std::runtime_error("failed to create logical device!"); - } - - vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); - vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); - } - - static VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats){ - for (const auto& availableFormat : availableFormats){ - if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR){ - return availableFormat; - } - } - return availableFormats[0]; - } - - static VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes){ - return VK_PRESENT_MODE_FIFO_KHR; - } - - VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities){ - if (capabilities.currentExtent.width != std::numeric_limits::max()){ - return capabilities.currentExtent; - } else { - int width, height; - glfwGetFramebufferSize(window, &width, &height); - - VkExtent2D actualExtent = { - static_cast(width), - static_cast(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 createSwapchain(){ - SwapchainSupportDetails swapchainSupport = querySwapchainSupport(physicalDevice); - - VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapchainSupport.formats); - VkPresentModeKHR presentMode = chooseSwapPresentMode(swapchainSupport.presentModes); - VkExtent2D extent = chooseSwapExtent(swapchainSupport.capabilities); - - uint32_t imageCount = swapchainSupport.capabilities.minImageCount + 1; - if (swapchainSupport.capabilities.maxImageCount > 0 && imageCount > swapchainSupport.capabilities.maxImageCount){ - imageCount = swapchainSupport.capabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR createInfo {}; - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.surface = surface; - createInfo.minImageCount = imageCount; - createInfo.imageFormat = surfaceFormat.format; - createInfo.imageColorSpace = surfaceFormat.colorSpace; - createInfo.imageExtent = extent; - createInfo.imageArrayLayers = 1; - createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); - uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; - - if (indices.graphicsFamily != indices.presentFamily){ - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = 2; - createInfo.pQueueFamilyIndices = queueFamilyIndices; - } else { - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - } - - createInfo.preTransform = swapchainSupport.capabilities.currentTransform; - createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - createInfo.presentMode = presentMode; - createInfo.clipped = VK_TRUE; - createInfo.oldSwapchain = VK_NULL_HANDLE; - - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapchain) != VK_SUCCESS){ - throw std::runtime_error("failed to create swapchain!"); - } - - vkGetSwapchainImagesKHR(device, swapchain, &imageCount, nullptr); - swapchainImages.resize(imageCount); - vkGetSwapchainImagesKHR(device, swapchain, &imageCount, swapchainImages.data()); - - swapchainImageFormat = surfaceFormat.format; - swapchainExtent = extent; - } - - void cleanupSwapchain(){ - for (auto framebuffer : swapchainFramebuffers){ - vkDestroyFramebuffer(device, framebuffer, nullptr); - } - for (auto imageView : swapchainImageViews){ - vkDestroyImageView(device, imageView, nullptr); - } - vkDestroySwapchainKHR(device, swapchain, nullptr); - } - - void recreateSwapchain(){ - vkDeviceWaitIdle(device); - - cleanupSwapchain(); - - createSwapchain(); - createImageViews(); - createFramebuffers(); - } - - VkSwapchainKHR swapchain = VK_NULL_HANDLE; - std::vector swapchainImages; - VkFormat swapchainImageFormat {}; - VkExtent2D swapchainExtent {}; - - std::vector swapchainImageViews; - - void createImageViews(){ - swapchainImageViews.resize(swapchainImages.size()); - for (size_t i = 0; i < swapchainImages.size(); i++){ - VkImageViewCreateInfo createInfo {}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.format = swapchainImageFormat; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.image = swapchainImages[i]; - createInfo.components = { - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - VK_COMPONENT_SWIZZLE_IDENTITY, - }; - createInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - if (vkCreateImageView(device, &createInfo, nullptr, &swapchainImageViews[i]) != VK_SUCCESS){ - throw std::runtime_error("failed to create image views!"); - } - } - } - - VkShaderModule createShaderModule(const std::vector &code){ - VkShaderModuleCreateInfo createInfo {}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.codeSize = code.size(); - createInfo.pCode = reinterpret_cast(code.data()); - - VkShaderModule shaderModule; - if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS){ - throw std::runtime_error("failed to create shader module!"); - } - - return shaderModule; - } - - void createRenderPass(){ - VkAttachmentDescription colorAttachment {}; - colorAttachment.format = swapchainImageFormat; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef {}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass {}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkSubpassDependency dependency {}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - VkRenderPassCreateInfo renderPassInfo {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = 1; - renderPassInfo.pDependencies = &dependency; - - vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass); - } - - VkRenderPass renderPass = VK_NULL_HANDLE; - VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; - - void createGraphicsPipeline(){ - auto vertShaderCode = readFile("shaders/vert.spv"); - auto fragShaderCode = readFile("shaders/frag.spv"); - - VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); - VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); - - - VkPipelineShaderStageCreateInfo vertShaderStageInfo {}; - vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertShaderStageInfo.module = vertShaderModule; - vertShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo fragShaderStageInfo {}; - fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragShaderStageInfo.module = fragShaderModule; - fragShaderStageInfo.pName = "main"; - - VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; - - auto bindingDescription = Vertex::getBindingDescription(); - auto attributeDescriptions = Vertex::getAttributeDescriptions(); - - VkPipelineVertexInputStateCreateInfo vertexInputInfo {}; - vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 1; - vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; - vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); - vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); - - VkPipelineInputAssemblyStateCreateInfo inputAssembly {}; - inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssembly.primitiveRestartEnable = VK_FALSE; - - std::vector dynamicStates = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; - - VkPipelineDynamicStateCreateInfo dynamicState {}; - dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicState.dynamicStateCount = dynamicStates.size(); - dynamicState.pDynamicStates = dynamicStates.data(); - - VkPipelineViewportStateCreateInfo viewportState {}; - viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportState.viewportCount = 1; - viewportState.scissorCount = 1; - - VkPipelineRasterizationStateCreateInfo rasterizer {}; - 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 = 1; - rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; - rasterizer.depthBiasClamp = VK_FALSE; - - VkPipelineMultisampleStateCreateInfo multisample {}; - multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisample.sampleShadingEnable = VK_FALSE; - multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - - VkPipelineColorBlendAttachmentState colorBlendAttachment {}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - - VkPipelineColorBlendStateCreateInfo colorBlending {}; - colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlending.logicOpEnable = VK_FALSE; - colorBlending.attachmentCount = 1; - colorBlending.pAttachments = &colorBlendAttachment; - - VkPipelineLayoutCreateInfo pipelineLayoutInfo {}; - pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - - vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout); - - VkGraphicsPipelineCreateInfo pipelineInfo {}; - { - pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - - pipelineInfo.stageCount = 2; - pipelineInfo.pStages = shaderStages; - - pipelineInfo.pVertexInputState = &vertexInputInfo; - pipelineInfo.pInputAssemblyState = &inputAssembly; - pipelineInfo.pViewportState = &viewportState; - pipelineInfo.pRasterizationState = &rasterizer; - pipelineInfo.pMultisampleState = &multisample; - pipelineInfo.pColorBlendState = &colorBlending; - pipelineInfo.pDynamicState = &dynamicState; - - pipelineInfo.layout = pipelineLayout; - - pipelineInfo.renderPass = renderPass; - pipelineInfo.subpass = 0; - } - - vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline); - - vkDestroyShaderModule(device, vertShaderModule, nullptr); - vkDestroyShaderModule(device, fragShaderModule, nullptr); - } - - VkPipeline graphicsPipeline = VK_NULL_HANDLE; - - std::vector swapchainFramebuffers; - - void createFramebuffers(){ - swapchainFramebuffers.resize(swapchainImageViews.size()); - for (size_t i = 0; i < swapchainImageViews.size(); i++){ - VkImageView attachments[] = {swapchainImageViews[i]}; - - VkFramebufferCreateInfo framebufferInfo {}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = swapchainExtent.width; - framebufferInfo.height = swapchainExtent.height; - framebufferInfo.layers = 1; - - vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapchainFramebuffers[i]); - } - } - - VkBuffer vertexBuffer = VK_NULL_HANDLE; - VkDeviceMemory vertexBufferMemory = VK_NULL_HANDLE; - - uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags propertyFlags){ - VkPhysicalDeviceMemoryProperties memoryProperties; - vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); - - for (uint32_t type = 0; type < memoryProperties.memoryTypeCount; type++){ - if ((typeFilter & (1 << type)) && (memoryProperties.memoryTypes[type].propertyFlags & propertyFlags)){ - return type; - } - } - - throw std::runtime_error("failed to find suitable memory type!"); - } - - void createVertexBuffer(){ - VkBufferCreateInfo bufferInfo{}; - bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - bufferInfo.size = vertices.size() * sizeof(Vertex); - bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - vkCreateBuffer(device, &bufferInfo, nullptr, &vertexBuffer); - - VkMemoryRequirements memoryRequirements; - vkGetBufferMemoryRequirements(device, vertexBuffer, &memoryRequirements); - - VkMemoryAllocateInfo allocateInfo {}; - allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - allocateInfo.allocationSize = memoryRequirements.size; - allocateInfo.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - vkAllocateMemory(device, &allocateInfo, nullptr, &vertexBufferMemory); - - vkBindBufferMemory(device, vertexBuffer, vertexBufferMemory, 0); - - void* data; - vkMapMemory(device, vertexBufferMemory, 0, bufferInfo.size, 0, &data); - memcpy(data, vertices.data(), bufferInfo.size); - vkUnmapMemory(device, vertexBufferMemory); - } - - - VkCommandPool commandPool = VK_NULL_HANDLE; - - std::vector commandBuffers; - - void createCommandPool(){ - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); - - VkCommandPoolCreateInfo poolInfo {}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = indices.graphicsFamily.value(); - - vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool); - } - - void createCommandBuffers(){ - commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); - - VkCommandBufferAllocateInfo allocateInfo {}; - allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocateInfo.commandPool = commandPool; - allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocateInfo.commandBufferCount = commandBuffers.size(); - - vkAllocateCommandBuffers(device, &allocateInfo, commandBuffers.data()); - } - - void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){ - VkCommandBufferBeginInfo beginInfo {}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - - vkBeginCommandBuffer(commandBuffer, &beginInfo); - - VkRenderPassBeginInfo renderPassInfo {}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = renderPass; - renderPassInfo.framebuffer = swapchainFramebuffers[imageIndex]; - renderPassInfo.renderArea.offset = {0, 0}; - renderPassInfo.renderArea.extent = swapchainExtent; - - VkClearValue clearColor = {{{0, 0, 0, 1}}}; - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; - - vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); - - VkViewport viewport {}; - viewport.x = 0; - viewport.y = 0; - viewport.width = static_cast(swapchainExtent.width); - viewport.height = static_cast(swapchainExtent.height); - viewport.minDepth = 0; - viewport.maxDepth = 1; - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - - VkRect2D scissor {}; - scissor.offset = {0, 0}; - scissor.extent = swapchainExtent; - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - - VkBuffer buffers[] = {vertexBuffer}; - VkDeviceSize offsets[] = {0}; - vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets); - - vkCmdDraw(commandBuffer, vertices.size(), 1, 0, 0); - - vkCmdEndRenderPass(commandBuffer); - vkEndCommandBuffer(commandBuffer); - } - - std::vector imageAvailableSemaphores; - std::vector renderFinishedSemaphores; - std::vector inFlightFences; - - void createSyncObjects(){ - imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); - - VkSemaphoreCreateInfo semaphoreInfo {}; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VkFenceCreateInfo fenceInfo {}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ - vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]); - vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]); - vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]); - } - } - - size_t currentFrame = 0; - - void drawFrame(){ - vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); - - uint32_t imageIndex; - VkResult result = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR){ - recreateSwapchain(); - return; - } - - vkResetFences(device, 1, &inFlightFences[currentFrame]); - - vkResetCommandBuffer(commandBuffers[currentFrame], 0); - recordCommandBuffer(commandBuffers[currentFrame], imageIndex); - - VkSubmitInfo submitInfo {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; - VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; - - VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]); - - VkPresentInfoKHR presentInfo {}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = signalSemaphores; - - VkSwapchainKHR swapchains[] = {swapchain}; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = swapchains; - presentInfo.pImageIndices = &imageIndex; - - result = vkQueuePresentKHR(presentQueue, &presentInfo); - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR){ - recreateSwapchain(); - return; - } - - currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; - } - - void mainLoop(){ - while (!glfwWindowShouldClose(window)){ - glfwPollEvents(); - drawFrame(); - } - - vkDeviceWaitIdle(device); - } - void cleanup(){ - cleanupSwapchain(); - vkDestroyBuffer(device, vertexBuffer, nullptr); - vkFreeMemory(device, vertexBufferMemory, nullptr); - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroyFence(device, inFlightFences[i], nullptr); - } - vkDestroyCommandPool(device, commandPool, nullptr); - vkDestroyPipeline(device, graphicsPipeline, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyRenderPass(device, renderPass, nullptr); - vkDestroyDevice(device, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - vkDestroyInstance(instance, nullptr); - - glfwDestroyWindow(window); - glfwTerminate(); - } -}; +#include int main() { - - MyApp app; - try { - app.run(); - } catch (const std::exception& e){ - std::cerr << e.what() << "\n"; - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; + Application application; } diff --git a/src/pipeline.cpp b/src/pipeline.cpp deleted file mode 100644 index 2069174..0000000 --- a/src/pipeline.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "pipeline.hpp" - diff --git a/src/pipeline.hpp b/src/pipeline.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/vulkan/application.cpp b/src/vulkan/application.cpp new file mode 100644 index 0000000..fa1c138 --- /dev/null +++ b/src/vulkan/application.cpp @@ -0,0 +1,107 @@ +#include "application.hpp" + +Application::Application() { + instance = new Instance; + swapchain = new Swapchain(instance); + pipeline = new Pipeline(instance, swapchain->renderPass); + buffer = new Buffer(instance); + commandPool = new CommandPool(instance); + createSyncObjects(); + + mainLoop(); +} + +void Application::mainLoop() { + while (!glfwWindowShouldClose(instance->window)){ + glfwPollEvents(); + drawFrame(); + } + vkDeviceWaitIdle(instance->device); +} + +void Application::drawFrame() { + vkWaitForFences(instance->device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + VkResult result = vkAcquireNextImageKHR(instance->device, swapchain->handle, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR){ + swapchain->recreateSwapchain(); + return; + } + + vkResetFences(instance->device, 1, &inFlightFences[currentFrame]); + + vkResetCommandBuffer(commandPool->buffers[currentFrame], 0); + recordCommandBuffer(commandPool->buffers[currentFrame], imageIndex); + + VkSubmitInfo submitInfo {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandPool->buffers[currentFrame]; + + VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + vkQueueSubmit(instance->graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]); + + VkPresentInfoKHR presentInfo {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapchains[] = {swapchain->handle}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapchains; + presentInfo.pImageIndices = &imageIndex; + + result = vkQueuePresentKHR(instance->presentQueue, &presentInfo); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR){ + swapchain->recreateSwapchain(); + return; + } + + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; +} + +void Application::createSyncObjects() { + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + + VkSemaphoreCreateInfo semaphoreInfo {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo {}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ + vkCreateSemaphore(instance->device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]); + vkCreateSemaphore(instance->device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]); + vkCreateFence(instance->device, &fenceInfo, nullptr, &inFlightFences[i]); + } +} + +Application::~Application() { + delete swapchain; + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ + vkDestroySemaphore(instance->device, imageAvailableSemaphores[i], nullptr); + vkDestroySemaphore(instance->device, renderFinishedSemaphores[i], nullptr); + vkDestroyFence(instance->device, inFlightFences[i], nullptr); + } + delete commandPool; + delete buffer; + delete pipeline; + delete instance; +} + + + + diff --git a/src/vulkan/application.hpp b/src/vulkan/application.hpp new file mode 100644 index 0000000..433351a --- /dev/null +++ b/src/vulkan/application.hpp @@ -0,0 +1,103 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vertex.hpp" +#include "swapchain.hpp" +#include "pipeline.hpp" +#include "instance.hpp" +#include "buffer.hpp" +#include "command_pool.hpp" + +constexpr int MAX_FRAMES_IN_FLIGHT = 2; + +class Timer { +public: + explicit Timer(){ + start = std::chrono::system_clock::now(); + } + ~Timer(){ + size_t nanoseconds = (std::chrono::system_clock::now() - start).count(); + printf("Timer: %zu mus\n", nanoseconds / 1000); + } +private: + std::chrono::time_point start; +}; + +class Application { +public: + explicit Application(); + ~Application(); +private: + Instance* instance = nullptr; + Swapchain* swapchain = nullptr; + Pipeline* pipeline = nullptr; + Buffer* buffer = nullptr; + CommandPool* commandPool = nullptr; + + void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){ + VkCommandBufferBeginInfo beginInfo {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkRenderPassBeginInfo renderPassInfo {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = swapchain->renderPass; + renderPassInfo.framebuffer = swapchain->frameBuffers[imageIndex]; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = swapchain->extent; + + VkClearValue clearColor = {{{0, 0, 0, 1}}}; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->graphicsPipeline); + + VkViewport viewport {}; + viewport.x = 0; + viewport.y = 0; + viewport.width = static_cast(swapchain->extent.width); + viewport.height = static_cast(swapchain->extent.height); + viewport.minDepth = 0; + viewport.maxDepth = 1; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor {}; + scissor.offset = {0, 0}; + scissor.extent = swapchain->extent; + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + VkBuffer buffers[] = {buffer->vertexHandle}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, buffers, offsets); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + vkEndCommandBuffer(commandBuffer); + } + + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + + size_t currentFrame = 0; + + void drawFrame(); + void createSyncObjects(); + + void mainLoop(); +}; diff --git a/src/vulkan/buffer.cpp b/src/vulkan/buffer.cpp new file mode 100644 index 0000000..d75c638 --- /dev/null +++ b/src/vulkan/buffer.cpp @@ -0,0 +1,55 @@ +#include +#include +#include "buffer.hpp" +#include "vertex.hpp" + +const std::vector vertices = { + {{0.0, -0.5}, {1, 0, 0}}, + {{0.5, 0.5}, {0, 1, 0}}, + {{-0.5, 0.5}, {0, 0, 1}} +}; + +Buffer::Buffer(Instance* instance) : instance(instance) { + VkBufferCreateInfo bufferInfo{}; + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = vertices.size() * sizeof(Vertex); + bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + vkCreateBuffer(instance->device, &bufferInfo, nullptr, &vertexHandle); + + VkMemoryRequirements memoryRequirements; + vkGetBufferMemoryRequirements(instance->device, vertexHandle, &memoryRequirements); + + VkMemoryAllocateInfo allocateInfo {}; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.allocationSize = memoryRequirements.size; + allocateInfo.memoryTypeIndex = findMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + vkAllocateMemory(instance->device, &allocateInfo, nullptr, &vertexMemory); + + vkBindBufferMemory(instance->device, vertexHandle, vertexMemory, 0); + + void* data; + vkMapMemory(instance->device, vertexMemory, 0, bufferInfo.size, 0, &data); + memcpy(data, vertices.data(), bufferInfo.size); + vkUnmapMemory(instance->device, vertexMemory); +} + +uint32_t Buffer::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags propertyFlags) { + VkPhysicalDeviceMemoryProperties memoryProperties; + vkGetPhysicalDeviceMemoryProperties(instance->physicalDevice, &memoryProperties); + + for (uint32_t type = 0; type < memoryProperties.memoryTypeCount; type++){ + if ((typeFilter & (1 << type)) && (memoryProperties.memoryTypes[type].propertyFlags & propertyFlags)){ + return type; + } + } + + throw std::runtime_error("failed to find suitable memory type!"); +} + +Buffer::~Buffer() { + vkFreeMemory(instance->device, vertexMemory, nullptr); + vkDestroyBuffer(instance->device, vertexHandle, nullptr); +} diff --git a/src/vulkan/buffer.hpp b/src/vulkan/buffer.hpp new file mode 100644 index 0000000..6911a74 --- /dev/null +++ b/src/vulkan/buffer.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include "instance.hpp" + + +class Buffer { +public: + explicit Buffer(Instance* instance); + ~Buffer(); + VkBuffer vertexHandle = VK_NULL_HANDLE; + +private: + Instance* instance = nullptr; + VkDeviceMemory vertexMemory = VK_NULL_HANDLE; + + uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags propertyFlags); +}; \ No newline at end of file diff --git a/src/vulkan/command_pool.cpp b/src/vulkan/command_pool.cpp new file mode 100644 index 0000000..024b656 --- /dev/null +++ b/src/vulkan/command_pool.cpp @@ -0,0 +1,32 @@ +#include "command_pool.hpp" +#include "application.hpp" + +CommandPool::CommandPool(Instance *instance) : instance(instance) { + Instance::QueueFamilyIndices indices = Instance::findQueueFamilies(instance->physicalDevice, instance->surface); + + VkCommandPoolCreateInfo poolInfo {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = indices.graphicsFamily.value(); + + vkCreateCommandPool(instance->device, &poolInfo, nullptr, &handle); + + createBuffers(); +} + +void CommandPool::createBuffers() { + buffers.resize(MAX_FRAMES_IN_FLIGHT); + + VkCommandBufferAllocateInfo allocateInfo {}; + allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocateInfo.commandPool = handle; + allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocateInfo.commandBufferCount = buffers.size(); + + vkAllocateCommandBuffers(instance->device, &allocateInfo, buffers.data()); +} + +CommandPool::~CommandPool() { + vkFreeCommandBuffers(instance->device, handle, buffers.size(), buffers.data()); + vkDestroyCommandPool(instance->device, handle, nullptr); +} diff --git a/src/vulkan/command_pool.hpp b/src/vulkan/command_pool.hpp new file mode 100644 index 0000000..f913606 --- /dev/null +++ b/src/vulkan/command_pool.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "instance.hpp" + +class CommandPool { +public: + explicit CommandPool(Instance* instance); + ~CommandPool(); + std::vector buffers; +private: + Instance* instance; + VkCommandPool handle = VK_NULL_HANDLE; + + void createBuffers(); +}; \ No newline at end of file diff --git a/src/vulkan/instance.cpp b/src/vulkan/instance.cpp new file mode 100644 index 0000000..1151fc8 --- /dev/null +++ b/src/vulkan/instance.cpp @@ -0,0 +1,237 @@ +#include +#include +#include "instance.hpp" +#include "swapchain.hpp" +#include + +const std::vector deviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME +}; + +#ifndef NDEBUG +#define ENABLE_VALIDATION_LAYERS +#endif + +#ifdef ENABLE_VALIDATION_LAYERS +const std::vector validationLayers = { + "VK_LAYER_KHRONOS_validation" +}; +bool checkValidationLayerSupport(); +#endif + +#ifdef ENABLE_VALIDATION_LAYERS +bool checkValidationLayerSupport(){ + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char* layerName : validationLayers){ + bool layerFound = false; + + for (const auto& layerProperties : availableLayers){ + if (strcmp(layerName, layerProperties.layerName) == 0){ + layerFound = true; + break; + } + } + + if (!layerFound){ + return false; + } + } + + return true; +} +#endif + +Instance::Instance() { + initWindow(); + + createInstance(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); +} + +void Instance::initWindow() { + glfwInit(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + window = glfwCreateWindow(800, 600, "Vulkan Simulation", nullptr, nullptr); +} + +void Instance::createInstance() { +#ifdef ENABLE_VALIDATION_LAYERS + if (!checkValidationLayerSupport()){ + throw std::runtime_error("validation layers requested, but not available!"); + } +#endif + + VkApplicationInfo applicationInfo {}; + applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + applicationInfo.pApplicationName = "Coole Simulation"; + applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + applicationInfo.pEngineName = "No Engine"; + applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + applicationInfo.apiVersion = VK_API_VERSION_1_0; + + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + VkInstanceCreateInfo instanceCreateInfo {}; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pApplicationInfo = &applicationInfo; + instanceCreateInfo.enabledExtensionCount = glfwExtensionCount; + instanceCreateInfo.ppEnabledExtensionNames = glfwExtensions; +#ifdef ENABLE_VALIDATION_LAYERS + instanceCreateInfo.enabledLayerCount = validationLayers.size(); + instanceCreateInfo.ppEnabledLayerNames = validationLayers.data(); +#else + instanceCreateInfo.enabledLayerCount = 0; +#endif + + vkCreateInstance(&instanceCreateInfo, nullptr, &handle); +} + +void Instance::createSurface() { + glfwCreateWindowSurface(handle, window, nullptr, &surface); +} + +void Instance::pickPhysicalDevice() { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(handle, &deviceCount, nullptr); + if (deviceCount == 0){ + throw std::runtime_error("failed to find GPUs with Vulkan support!"); + } + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(handle, &deviceCount, devices.data()); + + for (const VkPhysicalDevice &device : devices){ + if (isDeviceSuitable(device)){ + physicalDevice = device; + break; + } + } + + if (physicalDevice == VK_NULL_HANDLE){ + throw std::runtime_error("failed to find a suitable GPU!"); + } + + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + printf("Picked physical device: %s\n", properties.deviceName); +} + +void Instance::createLogicalDevice() { + QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface); + + std::vector queueCreateInfos; + + float queuePriority = 1.0f; + for (uint32_t queueFamily : indices.uniqueQueueFamilies()){ + VkDeviceQueueCreateInfo queueCreateInfo {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.push_back(queueCreateInfo); + } + + VkPhysicalDeviceFeatures deviceFeatures {}; + + VkDeviceCreateInfo createInfo {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.queueCreateInfoCount = queueCreateInfos.size(); + createInfo.pEnabledFeatures = &deviceFeatures; + createInfo.enabledExtensionCount = deviceExtensions.size(); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); +#ifdef ENABLE_VALIDATION_LAYERS + createInfo.enabledLayerCount = validationLayers.size(); + createInfo.ppEnabledLayerNames = validationLayers.data(); +#else + createInfo.enabledLayerCount = 0; +#endif + + vkCreateDevice(physicalDevice, &createInfo, nullptr, &device); + + vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); +} + +Instance::QueueFamilyIndices Instance::findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface) { + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + QueueFamilyIndices indices {}; + + uint32_t i = 0; + for (const VkQueueFamilyProperties& queueFamilyProperties : queueFamilies){ + if (queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT){ + indices.graphicsFamily = i; + } + if (queueFamilyProperties.queueFlags & VK_QUEUE_COMPUTE_BIT){ + indices.computeFamily = i; + } + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + if (presentSupport){ + indices.presentFamily = i; + } + if (indices.isComplete()){ + break; + } + i++; + } + return indices; +} + +bool Instance::isDeviceSuitable(VkPhysicalDevice physicalDevice) { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties); + + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceFeatures(physicalDevice, &deviceFeatures); + + QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface); + + bool extensionsSupported = checkDeviceExtensionSupport(physicalDevice); + + bool swapChainAdequate = false; + if (extensionsSupported){ + SwapchainSupportDetails details = querySwapchainSupport(physicalDevice, surface); + swapChainAdequate = !details.formats.empty() && !details.presentModes.empty(); + } + + return indices.isComplete() && extensionsSupported && swapChainAdequate; +} + +bool Instance::checkDeviceExtensionSupport(VkPhysicalDevice device) { + std::set required(deviceExtensions.begin(), deviceExtensions.end()); + + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + for (const VkExtensionProperties& extension : availableExtensions){ + required.erase(extension.extensionName); + } + + return required.empty(); +} + +Instance::~Instance() { + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(handle, surface, nullptr); + vkDestroyInstance(handle, nullptr); + + glfwDestroyWindow(window); + glfwTerminate(); +} diff --git a/src/vulkan/instance.hpp b/src/vulkan/instance.hpp new file mode 100644 index 0000000..d807976 --- /dev/null +++ b/src/vulkan/instance.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include + +class Instance { +public: + explicit Instance(); + ~Instance(); + + GLFWwindow *window = nullptr; + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + VkQueue graphicsQueue = VK_NULL_HANDLE; + VkQueue presentQueue = VK_NULL_HANDLE; + + struct QueueFamilyIndices { + std::optional graphicsFamily; + std::optional computeFamily; + std::optional presentFamily; + bool isComplete() const { + return graphicsFamily.has_value() && computeFamily.has_value() && presentFamily.has_value(); + } + std::set uniqueQueueFamilies(){ + return {graphicsFamily.value(), presentFamily.value(), computeFamily.value()}; + } + }; + static QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface); + +private: + VkInstance handle = VK_NULL_HANDLE; + + void initWindow(); + void createInstance(); + void createSurface(); + void pickPhysicalDevice(); + void createLogicalDevice(); + + bool isDeviceSuitable(VkPhysicalDevice physicalDevice); + + static bool checkDeviceExtensionSupport(VkPhysicalDevice device); + +}; \ No newline at end of file diff --git a/src/vulkan/pipeline.cpp b/src/vulkan/pipeline.cpp new file mode 100644 index 0000000..26c0b6b --- /dev/null +++ b/src/vulkan/pipeline.cpp @@ -0,0 +1,149 @@ +#include +#include +#include "pipeline.hpp" +#include "vertex.hpp" + +std::vector readFile(const std::string& fileName){ + std::ifstream file(fileName, std::ios::ate | std::ios::binary); + + if (!file.is_open()){ + throw std::runtime_error("failed to open file!"); + } + + size_t fileSize = file.tellg(); + std::vector buffer(fileSize); + + file.seekg(0); + file.read(buffer.data(), static_cast(fileSize)); + file.close(); + + return buffer; +} + +Pipeline::Pipeline(Instance* instance, VkRenderPass renderPass) : instance(instance) { + auto vertShaderCode = readFile("shaders/vert.spv"); + auto fragShaderCode = readFile("shaders/frag.spv"); + + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); + + + VkPipelineShaderStageCreateInfo vertShaderStageInfo {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; + + auto bindingDescription = Vertex::getBindingDescription(); + auto attributeDescriptions = Vertex::getAttributeDescriptions(); + + VkPipelineVertexInputStateCreateInfo vertexInputInfo {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssembly {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + inputAssembly.primitiveRestartEnable = VK_FALSE; + + std::vector dynamicStates = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + + VkPipelineDynamicStateCreateInfo dynamicState {}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = dynamicStates.size(); + dynamicState.pDynamicStates = dynamicStates.data(); + + VkPipelineViewportStateCreateInfo viewportState {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterizer {}; + 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 = 1; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizer.depthBiasClamp = VK_FALSE; + + VkPipelineMultisampleStateCreateInfo multisample {}; + multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample.sampleShadingEnable = VK_FALSE; + multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + + VkPipelineColorBlendStateCreateInfo colorBlending {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + + vkCreatePipelineLayout(instance->device, &pipelineLayoutInfo, nullptr, &pipelineLayout); + + VkGraphicsPipelineCreateInfo pipelineInfo {}; + { + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shaderStages; + + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisample; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + + pipelineInfo.layout = pipelineLayout; + + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; + } + + vkCreateGraphicsPipelines(instance->device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline); + + vkDestroyShaderModule(instance->device, vertShaderModule, nullptr); + vkDestroyShaderModule(instance->device, fragShaderModule, nullptr); +} + +VkShaderModule Pipeline::createShaderModule(const std::vector &code) { + VkShaderModuleCreateInfo createInfo {}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = code.size(); + createInfo.pCode = reinterpret_cast(code.data()); + + VkShaderModule shaderModule; + vkCreateShaderModule(instance->device, &createInfo, nullptr, &shaderModule); + + return shaderModule; +} + + +Pipeline::~Pipeline() { + vkDestroyPipeline(instance->device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(instance->device, pipelineLayout, nullptr); +} + diff --git a/src/vulkan/pipeline.hpp b/src/vulkan/pipeline.hpp new file mode 100644 index 0000000..19fcc0c --- /dev/null +++ b/src/vulkan/pipeline.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "instance.hpp" + +class Pipeline { +public: + explicit Pipeline(Instance* instance, VkRenderPass renderPass); + ~Pipeline(); + VkPipeline graphicsPipeline = VK_NULL_HANDLE; +private: + VkShaderModule createShaderModule(const std::vector &code); + VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; + Instance* instance = nullptr; +}; \ No newline at end of file diff --git a/src/vulkan/swapchain.cpp b/src/vulkan/swapchain.cpp new file mode 100644 index 0000000..cf17d26 --- /dev/null +++ b/src/vulkan/swapchain.cpp @@ -0,0 +1,215 @@ +#include +#include +#include "swapchain.hpp" + +SwapchainSupportDetails querySwapchainSupport(VkPhysicalDevice device, VkSurfaceKHR surface){ + SwapchainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + if (formatCount != 0){ + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); + if (presentModeCount != 0){ + details.presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + } + + return details; +} + +Swapchain::Swapchain(Instance* instance) +: instance(instance) { + createSwapchain(); + createImageViews(); + createRenderpass(); + createFramebuffers(); +} + +Swapchain::~Swapchain() { + cleanupSwapchain(); + vkDestroyRenderPass(instance->device, renderPass, nullptr); +} + +VkExtent2D Swapchain::chooseSwapExtent(const VkSurfaceCapabilitiesKHR &capabilities) { + if (capabilities.currentExtent.width != std::numeric_limits::max()){ + return capabilities.currentExtent; + } else { + int width, height; + glfwGetFramebufferSize(instance->window, &width, &height); + + VkExtent2D actualExtent = { + static_cast(width), + static_cast(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 Swapchain::createSwapchain() { + SwapchainSupportDetails swapchainSupport = querySwapchainSupport(instance->physicalDevice, instance->surface); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapchainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapchainSupport.presentModes); + extent = chooseSwapExtent(swapchainSupport.capabilities); + + uint32_t imageCount = swapchainSupport.capabilities.minImageCount + 1; + if (swapchainSupport.capabilities.maxImageCount > 0 && imageCount > swapchainSupport.capabilities.maxImageCount){ + imageCount = swapchainSupport.capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo {}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = instance->surface; + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + Instance::QueueFamilyIndices indices = Instance::findQueueFamilies(instance->physicalDevice, instance->surface); + uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; + + if (indices.graphicsFamily != indices.presentFamily){ + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices; + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + } + + createInfo.preTransform = swapchainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + vkCreateSwapchainKHR(instance->device, &createInfo, nullptr, &handle); + + vkGetSwapchainImagesKHR(instance->device, handle, &imageCount, nullptr); + images.resize(imageCount); + vkGetSwapchainImagesKHR(instance->device, handle, &imageCount, images.data()); + + imageFormat = surfaceFormat.format; +} + +void Swapchain::cleanupSwapchain() { + for (auto framebuffer : frameBuffers){ + vkDestroyFramebuffer(instance->device, framebuffer, nullptr); + } + for (auto imageView : imageViews){ + vkDestroyImageView(instance->device, imageView, nullptr); + } + vkDestroySwapchainKHR(instance->device, handle, nullptr); +} + +void Swapchain::recreateSwapchain() { + vkDeviceWaitIdle(instance->device); + + cleanupSwapchain(); + + createSwapchain(); + createImageViews(); + createFramebuffers(); +} + +void Swapchain::createImageViews() { + imageViews.resize(images.size()); + for (size_t i = 0; i < images.size(); i++){ + VkImageViewCreateInfo createInfo {}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.format = imageFormat; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.image = images[i]; + createInfo.components = { + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_IDENTITY, + }; + createInfo.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + vkCreateImageView(instance->device, &createInfo, nullptr, &imageViews[i]); + } +} + +void Swapchain::createFramebuffers() { + frameBuffers.resize(imageViews.size()); + for (size_t i = 0; i < imageViews.size(); i++){ + VkImageView attachments[] = {imageViews[i]}; + + VkFramebufferCreateInfo framebufferInfo {}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = extent.width; + framebufferInfo.height = extent.height; + framebufferInfo.layers = 1; + + vkCreateFramebuffer(instance->device, &framebufferInfo, nullptr, &frameBuffers[i]); + } +} + +VkSurfaceFormatKHR Swapchain::chooseSwapSurfaceFormat(const std::vector &availableFormats) { + for (const auto& availableFormat : availableFormats){ + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR){ + return availableFormat; + } + } + return availableFormats[0]; +} + +VkPresentModeKHR Swapchain::chooseSwapPresentMode(const std::vector &availablePresentModes) { + return VK_PRESENT_MODE_FIFO_KHR; +} + +void Swapchain::createRenderpass() { + VkAttachmentDescription colorAttachment {}; + colorAttachment.format = imageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef {}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassInfo {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + vkCreateRenderPass(instance->device, &renderPassInfo, nullptr, &renderPass); +} diff --git a/src/vulkan/swapchain.hpp b/src/vulkan/swapchain.hpp new file mode 100644 index 0000000..2756328 --- /dev/null +++ b/src/vulkan/swapchain.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include "instance.hpp" + +struct SwapchainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities {}; + std::vector formats {}; + std::vector presentModes {}; +}; +SwapchainSupportDetails querySwapchainSupport(VkPhysicalDevice device, VkSurfaceKHR surface); + +class Swapchain { +public: + explicit Swapchain(Instance* instance); + ~Swapchain(); + + void recreateSwapchain(); + void cleanupSwapchain(); + + VkRenderPass renderPass = VK_NULL_HANDLE; + VkExtent2D extent {}; + std::vector frameBuffers; + VkSwapchainKHR handle = VK_NULL_HANDLE; +private: + Instance* instance = nullptr; + std::vector images; + VkFormat imageFormat {}; + + void createRenderpass(); + void createFramebuffers(); + + static VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats); + static VkPresentModeKHR chooseSwapPresentMode(const std::vector& availablePresentModes); + + VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities); + + void createSwapchain(); + + std::vector imageViews; + + void createImageViews(); +}; \ No newline at end of file diff --git a/src/vulkan/vertex.cpp b/src/vulkan/vertex.cpp new file mode 100644 index 0000000..450ddb8 --- /dev/null +++ b/src/vulkan/vertex.cpp @@ -0,0 +1 @@ +#include "vertex.hpp" \ No newline at end of file diff --git a/src/vulkan/vertex.hpp b/src/vulkan/vertex.hpp new file mode 100644 index 0000000..c90d343 --- /dev/null +++ b/src/vulkan/vertex.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +struct Vertex { + glm::vec2 pos; + glm::vec3 color; + + static VkVertexInputBindingDescription getBindingDescription(){ + VkVertexInputBindingDescription bindingDescription {}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + return bindingDescription; + } + + static std::array getAttributeDescriptions(){ + std::array attributeDescriptions {}; + + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + + return attributeDescriptions; + } +}; \ No newline at end of file