From 10a8c236f05bc1f03f6f447c67bd36c625837b8f Mon Sep 17 00:00:00 2001 From: Lillian Salehi Date: Tue, 8 Oct 2024 01:57:32 -0500 Subject: [PATCH] Prepare for rendering and presentation --- src/devicelibrary.cpp | 151 +++++++++-------------- src/devicelibrary.h | 18 ++- src/global.cpp | 40 ++++++ src/global.h | 16 +++ src/graphics/graphicspipeline.cpp | 194 ++++++++++++++++++++++++++++-- src/graphics/graphicspipeline.h | 11 +- src/main.cpp | 24 ++-- 7 files changed, 337 insertions(+), 117 deletions(-) diff --git a/src/devicelibrary.cpp b/src/devicelibrary.cpp index 4e7d9b8..4dea9c6 100644 --- a/src/devicelibrary.cpp +++ b/src/devicelibrary.cpp @@ -15,11 +15,10 @@ namespace DeviceControl { - VkSurfaceKHR surface; - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkPhysicalDeviceProperties deviceProperties; VkPhysicalDeviceFeatures deviceFeatures; + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; @@ -29,16 +28,6 @@ namespace DeviceControl { VkQueue graphicsQueue; VkQueue presentQueue; - struct QueueFamilyIndices { - // We need to check that the Queue families support graphics operations and window presentation, sometimes they can support one or the other, - // therefore, we take into account both for completion. - std::optional graphicsFamily; - std::optional presentFamily; - - bool isComplete() { - return graphicsFamily.has_value() && presentFamily.has_value(); - } - }; struct SwapChainSupportDetails { VkSurfaceCapabilitiesKHR capabilities; std::vector formats; @@ -47,55 +36,6 @@ namespace DeviceControl { const std::vector deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { - // First we feed in a integer we want to use to hold the number of queued items, that fills it, then we create that amount of default constructed *VkQueueFamilyProperties* structs. - // These store the flags, the amount of queued items in the family, and timestamp data. Queue families are simply group collections of tasks we want to get done. - // Next, we check the flags of the queueFamily item, use a bitwise and to see if they match, i.e. support graphical operations, then return that to notify that we have at least one family that supports VK_QUEUE_GRAPHICS_BIT. - // Which means this device supports graphical operations! - // We also do the same thing for window presentation, just check to see if its supported. - QueueFamilyIndices indices; - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - int i = 0; - for(const auto& queueFamily : queueFamilies) { - if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { - indices.graphicsFamily = i; - } - - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if(presentSupport) { - indices.presentFamily = i; - } - - if(indices.isComplete()) { - break; - } - i++; - } - return indices; - } - - bool checkDeviceExtensionSupport(VkPhysicalDevice device) { - uint32_t extensionCount; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); - - std::vector availableExtensions(extensionCount); - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); - - std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); - - for(const auto& extension : availableExtensions) { - requiredExtensions.erase(extension.extensionName); - } - - return requiredExtensions.empty(); - } SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { // Swap chains are weird ngl, it's another one of those Vulkan platform agnosticity. The swapchain is basically a wrapper for GDI+, DXGI, X11, Wayland, etc. @@ -104,26 +44,43 @@ namespace DeviceControl { // (still no fucking clue how it works though) SwapChainSupportDetails details; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, Global::surface, &details.capabilities); uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, Global::surface, &formatCount, nullptr); if(formatCount != 0) { details.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, Global::surface, &formatCount, details.formats.data()); } uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, Global::surface, &presentModeCount, details.presentModes.data()); if(presentModeCount != 0) { details.presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, Global::surface, &presentModeCount, details.presentModes.data()); } return details; } + + bool checkDeviceExtensionSupport(VkPhysicalDevice device) { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const auto& extension : availableExtensions) { + requiredExtensions.erase(extension.extensionName); + } + + return requiredExtensions.empty(); + } + bool isDeviceSuitable(VkPhysicalDevice device) { // These two are simple, create a structure to hold the apiVersion, driverVersion, vendorID, deviceID and type, name, and a few other settings. // Then populate it by passing in the device and the structure reference. @@ -133,7 +90,7 @@ namespace DeviceControl { vkGetPhysicalDeviceFeatures(device, &deviceFeatures); // We need to find a device that supports graphical operations, or else we cant do much with it! This function just runs over all the queueFamilies and sees if there // is a queue family with the VK_QUEUE_GRAPHICS_BIT flipped! - QueueFamilyIndices indices = findQueueFamilies(device); + Global::QueueFamilyIndices indices = Global::findQueueFamilies(device); bool extensionSupported = checkDeviceExtensionSupport(device); bool swapChainAdequate = false; @@ -148,7 +105,7 @@ namespace DeviceControl { && extensionSupported && swapChainAdequate; } -// -------------------------------------- Swap Chain Settings -----------------------------------------// +// -------------------------------------- Swap Chain Settings ----------------------------------------- // VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { // One of three settings we can set, Surface Format controls the color space and format. @@ -197,7 +154,7 @@ namespace DeviceControl { return actualExtent; } } -// --------------------------------------- External Functions -----------------------------------------// +// --------------------------------------- External Functions ----------------------------------------- // void devicelibrary::pickPhysicalDevice(VkInstance& instance) { uint32_t deviceCount = 0; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); @@ -212,29 +169,29 @@ namespace DeviceControl { if(isDeviceSuitable(device)) { if(Global::enableValidationLayers) std::cout << "Using device: " << deviceProperties.deviceName << std::endl; //Once we have buttons or such, maybe ask the user or write a config file for which GPU to use? - physicalDevice = device; + Global::physicalDevice = device; break; } } - if(physicalDevice == VK_NULL_HANDLE) { + if(Global::physicalDevice == VK_NULL_HANDLE) { throw std::runtime_error("Failed to find a suitable GPU!"); } } void devicelibrary::destroySurface(VkInstance& instance) { - vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroySurfaceKHR(instance, Global::surface, nullptr); if(Global::enableValidationLayers) std::cout << "Destroyed surface safely\n" << std::endl; } void devicelibrary::createSurface(VkInstance& instance, GLFWwindow* window) { - if(glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { + if(glfwCreateWindowSurface(instance, window, nullptr, &Global::surface) != VK_SUCCESS) { throw std::runtime_error("Failed to create window surface!!"); } if(Global::enableValidationLayers) std::cout << "GLFW Window Surface created successfully\n" << std::endl; } - void devicelibrary::createLogicalDevice(VkDevice& device) { + void devicelibrary::createLogicalDevice() { // Describe how many queues we want for a single family (1) here, right now we are solely interested in graphics capabilites, // but Compute Shaders, transfer ops, decode and encode operations can also queued with setup! We also assign each queue a priority. // We do this by looping over all the queueFamilies and sorting them by indices to fill the queue at the end! - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + Global::QueueFamilyIndices indices = Global::findQueueFamilies(Global::physicalDevice); std::vector queueCreateInfos; std::set uniqueQueueFamilies = { @@ -265,16 +222,16 @@ namespace DeviceControl { } else { createDeviceInfo.enabledLayerCount = 0; } - if(vkCreateDevice(physicalDevice, &createDeviceInfo, nullptr, &device) != VK_SUCCESS) { + if(vkCreateDevice(Global::physicalDevice, &createDeviceInfo, nullptr, &Global::device) != VK_SUCCESS) { throw std::runtime_error("Failed to create logical device"); } if(Global::enableValidationLayers) std::cout << "Created Logical device successfully!\n" << std::endl; - vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); - vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); + vkGetDeviceQueue(Global::device, indices.graphicsFamily.value(), 0, &graphicsQueue); + vkGetDeviceQueue(Global::device, indices.presentFamily.value(), 0, &presentQueue); } - void devicelibrary::createSwapChain(GLFWwindow* window, VkDevice& device) { - SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); + void devicelibrary::createSwapChain(GLFWwindow* window) { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(Global::physicalDevice); VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); @@ -290,7 +247,7 @@ namespace DeviceControl { VkSwapchainCreateInfoKHR createSwapChainInfo{}; createSwapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createSwapChainInfo.surface = surface; + createSwapChainInfo.surface = Global::surface; createSwapChainInfo.minImageCount = imageCount; createSwapChainInfo.imageFormat = surfaceFormat.format; createSwapChainInfo.imageColorSpace = surfaceFormat.colorSpace; @@ -303,7 +260,7 @@ namespace DeviceControl { createSwapChainInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; // This handles swap chain images across multiple queue families, ie, if the graphics queue family is different from the present queue - QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + Global::QueueFamilyIndices indices = Global::findQueueFamilies(Global::physicalDevice); uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; // Usage across multiple queue families without explicit transfer of ownership if they are different queue families. // Otherwise, no sharing without explicit handoffs, faster, but not easily supported with multiple families. @@ -327,23 +284,23 @@ namespace DeviceControl { // require you to recreate it and reference the old one specified here, will revisit in a few days. createSwapChainInfo.oldSwapchain = VK_NULL_HANDLE; - if(vkCreateSwapchainKHR(device, &createSwapChainInfo, nullptr, &swapChain) != VK_SUCCESS) { + if(vkCreateSwapchainKHR(Global::device, &createSwapChainInfo, nullptr, &swapChain) != VK_SUCCESS) { throw std::runtime_error("Failed to create the swap chain!!"); } if(Global::enableValidationLayers) std::cout << "Swap Chain created successfully\n" << std::endl; - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); + vkGetSwapchainImagesKHR(Global::device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); + vkGetSwapchainImagesKHR(Global::device, swapChain, &imageCount, swapChainImages.data()); swapChainImageFormat = surfaceFormat.format; swapChainExtent = extent; } - void devicelibrary::destroySwapChain(VkDevice& device) { - vkDestroySwapchainKHR(device, swapChain, nullptr); + void devicelibrary::destroySwapChain() { + vkDestroySwapchainKHR(Global::device, swapChain, nullptr); if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl; } - void devicelibrary::createImageViews(VkDevice& device) { + void devicelibrary::createImageViews() { swapChainImageViews.resize(swapChainImages.size()); for(size_t i = 0; i < swapChainImages.size(); i++) { VkImageViewCreateInfo createImageViewInfo{}; @@ -365,16 +322,28 @@ namespace DeviceControl { // Yet another setting we would increase for VR applications, and specifically create a swap chain with more layers as well. The other layers would be the eye outputs. createImageViewInfo.subresourceRange.layerCount = 1; - if(vkCreateImageView(device, &createImageViewInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { + if(vkCreateImageView(Global::device, &createImageViewInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } if(Global::enableValidationLayers) std::cout << "Image views created successfully\n" << std::endl; } } - void devicelibrary::destroyImageViews(VkDevice& device) { + void devicelibrary::destroyImageViews() { for (auto imageView : swapChainImageViews) { - vkDestroyImageView(device, imageView, nullptr); + vkDestroyImageView(Global::device, imageView, nullptr); } if(Global::enableValidationLayers) std::cout << "Image destroyed safely\n" << std::endl; } + +// --------------------------------------- Getters & Setters ------------------------------------------ // + VkFormat devicelibrary::getImageFormat() { + return swapChainImageFormat; + } + std::vector devicelibrary::getSwapChainImageViews() { + return swapChainImageViews; + } + VkExtent2D devicelibrary::getSwapChainExtent() { + return swapChainExtent; + } } + diff --git a/src/devicelibrary.h b/src/devicelibrary.h index a4fcbd4..ce2ce22 100644 --- a/src/devicelibrary.h +++ b/src/devicelibrary.h @@ -1,17 +1,23 @@ #pragma once #include "global.h" +#include namespace DeviceControl { class devicelibrary { public: - void pickPhysicalDevice(VkInstance& instance); - void createLogicalDevice(VkDevice& device); + void createLogicalDevice(); void createSurface(VkInstance& instance, GLFWwindow* window); void destroySurface(VkInstance& instance); - void createSwapChain(GLFWwindow* window, VkDevice& device); - void destroySwapChain(VkDevice& device); - void createImageViews(VkDevice& device); - void destroyImageViews(VkDevice& device); + void createSwapChain(GLFWwindow* window); + void destroySwapChain(); + void createImageViews(); + void destroyImageViews(); + void createCommandPool(); + void destroyCommandPool(); + // ---------- Getters & Setters ----------- // + VkFormat getImageFormat(); + std::vector getSwapChainImageViews(); + VkExtent2D getSwapChainExtent(); }; } diff --git a/src/global.cpp b/src/global.cpp index 107495b..dceeacb 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -1,4 +1,5 @@ #include "global.h" +#include "devicelibrary.h" namespace Global { const std::vector validationLayers = { @@ -9,4 +10,43 @@ namespace Global { #else const bool enableValidationLayers = false; #endif + + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; + + Global::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { + // First we feed in a integer we want to use to hold the number of queued items, that fills it, then we create that amount of default constructed *VkQueueFamilyProperties* structs. + // These store the flags, the amount of queued items in the family, and timestamp data. Queue families are simply group collections of tasks we want to get done. + // Next, we check the flags of the queueFamily item, use a bitwise and to see if they match, i.e. support graphical operations, then return that to notify that we have at least one family that supports VK_QUEUE_GRAPHICS_BIT. + // Which means this device supports graphical operations! + // We also do the same thing for window presentation, just check to see if its supported. + DeviceControl::devicelibrary deviceLibs; + Global::QueueFamilyIndices indices; + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for(const auto& queueFamily : queueFamilies) { + if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { + indices.graphicsFamily = i; + } + + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, Global::surface, &presentSupport); + if(presentSupport) { + indices.presentFamily = i; + } + + if(indices.isComplete()) { + break; + } + i++; + } + return indices; + } } diff --git a/src/global.h b/src/global.h index 69727da..079c018 100644 --- a/src/global.h +++ b/src/global.h @@ -2,6 +2,7 @@ #include "debug/vulkandebuglibs.h" #include #include +#include #include #define GLFW_INCLUDE_VULKAN @@ -12,4 +13,19 @@ namespace Global { // so that's one obvious global, as well as the glfw includes! extern const std::vector validationLayers; extern const bool enableValidationLayers; + extern VkDevice device; + struct QueueFamilyIndices { + // We need to check that the Queue families support graphics operations and window presentation, sometimes they can support one or the other, + // therefore, we take into account both for completion. + std::optional graphicsFamily; + std::optional presentFamily; + + bool isComplete() { + return graphicsFamily.has_value() && presentFamily.has_value(); + } + }; + + extern VkSurfaceKHR surface; + extern VkPhysicalDevice physicalDevice; + Global::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); } diff --git a/src/graphics/graphicspipeline.cpp b/src/graphics/graphicspipeline.cpp index 367fb69..3b17d97 100644 --- a/src/graphics/graphicspipeline.cpp +++ b/src/graphics/graphicspipeline.cpp @@ -1,12 +1,24 @@ #include "graphicspipeline.h" +#include "../devicelibrary.h" +#include #include +#include +#include namespace Graphics { std::vector dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - VkPipelineLayout pipelineLayout; + std::vector swapChainFramebuffers; + VkCommandPool commandPool; + VkCommandBuffer commandBuffer; + + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + DeviceControl::devicelibrary deviceLibs; + static std::vector readFile(const std::string& filename) { std::ifstream file(filename, std::ios::ate | std::ios::binary); if (!file.is_open()) { @@ -36,17 +48,20 @@ namespace Graphics { return shaderModule; } - void graphicspipeline::destroyGraphicsPipeline(VkDevice& device) { - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + void graphicspipeline::destroyGraphicsPipeline() { + vkDestroyPipeline(Global::device, graphicsPipeline, nullptr); + if(Global::enableValidationLayers) std::cout << "Destroyed Graphics Pipeline safely\n" << std::endl; + vkDestroyPipelineLayout(Global::device, pipelineLayout, nullptr); if(Global::enableValidationLayers) std::cout << "Destroyed Layout Pipeline safely\n" << std::endl; + } - void graphicspipeline::createGraphicsPipeline(VkDevice& device) { + void graphicspipeline::createGraphicsPipeline() { // Note to self, for some reason the working directory is not where a read file is called from, but the project folder! auto vertShaderCode = readFile("src/shaders/vert.spv"); auto fragShaderCode = readFile("src/shaders/frag.spv"); - VkShaderModule vertShaderModule = createShaderModule(vertShaderCode, device); - VkShaderModule fragShaderModule = createShaderModule(fragShaderCode, device); + VkShaderModule vertShaderModule = createShaderModule(vertShaderCode, Global::device); + VkShaderModule fragShaderModule = createShaderModule(fragShaderCode, Global::device); // ------------------ STAGE 1 - INPUT ASSEMBLER ---------------- // // This can get a little complicated, normally, vertices are loaded in sequential order, with an element buffer however, you can specify the indices yourself! @@ -133,13 +148,174 @@ namespace Graphics { pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pushConstantRangeCount = 0; - if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { + if (vkCreatePipelineLayout(Global::device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } + // Here we combine all of the structures we created to make the final pipeline! + 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 = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.layout = pipelineLayout; + pipelineInfo.renderPass = renderPass; + pipelineInfo.subpass = 0; - vkDestroyShaderModule(device, fragShaderModule, nullptr); - vkDestroyShaderModule(device, vertShaderModule, nullptr); + if (vkCreateGraphicsPipelines(Global::device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { + throw std::runtime_error("failed to create graphics pipeline!"); + } + vkDestroyShaderModule(Global::device, fragShaderModule, nullptr); + vkDestroyShaderModule(Global::device, vertShaderModule, nullptr); if(Global::enableValidationLayers) std::cout << "Pipeline Layout created successfully\n" << std::endl; } + void graphicspipeline::createRenderPass() { + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = deviceLibs.getImageFormat(); + 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; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + + if (vkCreateRenderPass(Global::device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw std::runtime_error("failed to create render pass!"); + } + if(Global::enableValidationLayers) std::cout << "Render pass created successfully\n" << std::endl; + } + void graphicspipeline::destroyRenderPass() { + vkDestroyRenderPass(Global::device, renderPass, nullptr); + std::cout << "Destroyed Render Pass Safely\n" << std::endl; + } + void graphicspipeline::createFramebuffers() { + // Resize the container to hold all the framebuffers. + int framebuffersSize = deviceLibs.getSwapChainImageViews().size(); + swapChainFramebuffers.resize(framebuffersSize); + + for(size_t i = 0; i < framebuffersSize; i++) { + VkImageView attachments[] = { + deviceLibs.getSwapChainImageViews()[i] + }; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = deviceLibs.getSwapChainExtent().width; + framebufferInfo.height = deviceLibs.getSwapChainExtent().height; + framebufferInfo.layers = 1; + + if(vkCreateFramebuffer(Global::device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("Failed to create framebuffer!"); + } + } + } + void graphicspipeline::destroyFramebuffers() { + for (auto framebuffer : swapChainFramebuffers) { + vkDestroyFramebuffer(Global::device, framebuffer, nullptr); + } + } + + void graphicspipeline::createCommandPool() { + Global::QueueFamilyIndices queueFamilyIndices = Global::findQueueFamilies(Global::physicalDevice); + + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); + + if(vkCreateCommandPool(Global::device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create command pool!"); + } + if(Global::enableValidationLayers) std::cout << "Command pool created successfully\n" << std::endl; + } + void graphicspipeline::destroyCommandPool() { + vkDestroyCommandPool(Global::device, commandPool, nullptr); + } + void graphicspipeline::createCommandBuffer() { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = 1; + + if(vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate command buffers"); + } + if(Global::enableValidationLayers) std::cout << "Allocated command buffers successfully\n" << std::endl; + } + + void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; // Optional + beginInfo.pInheritanceInfo = nullptr; // Optional + + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("failed to begin recording command buffer!"); + } + if(Global::enableValidationLayers) std::cout << "Recording command buffer...\n" << std::endl; + + 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 = deviceLibs.getSwapChainExtent(); + + VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + 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.0f; + viewport.y = 0.0f; + viewport.width = (float) deviceLibs.getSwapChainExtent().width; + viewport.height = (float) deviceLibs.getSwapChainExtent().height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = deviceLibs.getSwapChainExtent(); + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + + vkCmdDraw(commandBuffer, 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffer); + + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + throw std::runtime_error("failed to record command buffer!"); + } + } } diff --git a/src/graphics/graphicspipeline.h b/src/graphics/graphicspipeline.h index 7599d6a..bad959a 100644 --- a/src/graphics/graphicspipeline.h +++ b/src/graphics/graphicspipeline.h @@ -4,7 +4,14 @@ namespace Graphics { class graphicspipeline { public: - void createGraphicsPipeline(VkDevice& device); - void destroyGraphicsPipeline(VkDevice& device); + void createGraphicsPipeline(); + void destroyGraphicsPipeline(); + void createRenderPass(); + void destroyRenderPass(); + void createFramebuffers(); + void destroyFramebuffers(); + void createCommandPool(); + void destroyCommandPool(); + void createCommandBuffer(); }; } diff --git a/src/main.cpp b/src/main.cpp index 7937d92..9a3f7c4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,6 @@ private: Graphics::graphicspipeline graphicsPipeline; GLFWwindow* window; VkInstance instance; - VkDevice device; // Initialize GLFW Window. First, Initialize GLFW lib, disable resizing for // now, and create window. @@ -45,10 +44,14 @@ private: debugController.setupDebugMessenger(instance); // The debug messenger is out holy grail, it gives us Vulkan related debug info when built with the -DNDEBUG flag (as per the makefile) deviceLibs.createSurface(instance, window); deviceLibs.pickPhysicalDevice(instance); - deviceLibs.createLogicalDevice(device); - deviceLibs.createSwapChain(window, device); - deviceLibs.createImageViews(device); - graphicsPipeline.createGraphicsPipeline(device); + deviceLibs.createLogicalDevice(); + deviceLibs.createSwapChain(window); + deviceLibs.createImageViews(); + graphicsPipeline.createRenderPass(); + graphicsPipeline.createGraphicsPipeline(); + graphicsPipeline.createFramebuffers(); + graphicsPipeline.createCommandPool(); + graphicsPipeline.createCommandBuffer(); } void createInstance() { @@ -78,10 +81,13 @@ private: } void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library! - graphicsPipeline.destroyGraphicsPipeline(device); - deviceLibs.destroyImageViews(device); - deviceLibs.destroySwapChain(device); - vkDestroyDevice(device, nullptr); + graphicsPipeline.destroyCommandPool(); + graphicsPipeline.destroyFramebuffers(); + graphicsPipeline.destroyGraphicsPipeline(); + graphicsPipeline.destroyRenderPass(); + deviceLibs.destroyImageViews(); + deviceLibs.destroySwapChain(); + vkDestroyDevice(Global::device, nullptr); if(Global::enableValidationLayers) { debugController.DestroyDebugUtilsMessengerEXT(instance, nullptr); }