Triangle finally renderedgit add . Swap ChainRecreation is up next, to allow for resizing. Then, it will be on to buffers for non-hardcoded shaders

This commit is contained in:
Lillian Salehi 2024-10-08 06:02:47 -05:00
parent 10a8c236f0
commit 29599e4b9a
9 changed files with 158 additions and 19 deletions

View File

@ -19,14 +19,12 @@ namespace DeviceControl {
VkPhysicalDeviceFeatures deviceFeatures;
VkSwapchainKHR swapChain;
std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat;
VkExtent2D swapChainExtent;
std::vector<VkImageView> swapChainImageViews;
VkQueue graphicsQueue;
VkQueue presentQueue;
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR capabilities;
@ -227,8 +225,8 @@ namespace DeviceControl {
}
if(Global::enableValidationLayers) std::cout << "Created Logical device successfully!\n" << std::endl;
vkGetDeviceQueue(Global::device, indices.graphicsFamily.value(), 0, &graphicsQueue);
vkGetDeviceQueue(Global::device, indices.presentFamily.value(), 0, &presentQueue);
vkGetDeviceQueue(Global::device, indices.graphicsFamily.value(), 0, &Global::graphicsQueue);
vkGetDeviceQueue(Global::device, indices.presentFamily.value(), 0, &Global::presentQueue);
}
void devicelibrary::createSwapChain(GLFWwindow* window) {
SwapChainSupportDetails swapChainSupport = querySwapChainSupport(Global::physicalDevice);
@ -284,20 +282,20 @@ 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(Global::device, &createSwapChainInfo, nullptr, &swapChain) != VK_SUCCESS) {
if(vkCreateSwapchainKHR(Global::device, &createSwapChainInfo, nullptr, &Global::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(Global::device, swapChain, &imageCount, nullptr);
vkGetSwapchainImagesKHR(Global::device, Global::swapChain, &imageCount, nullptr);
swapChainImages.resize(imageCount);
vkGetSwapchainImagesKHR(Global::device, swapChain, &imageCount, swapChainImages.data());
vkGetSwapchainImagesKHR(Global::device, Global::swapChain, &imageCount, swapChainImages.data());
swapChainImageFormat = surfaceFormat.format;
swapChainExtent = extent;
}
void devicelibrary::destroySwapChain() {
vkDestroySwapchainKHR(Global::device, swapChain, nullptr);
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl;
}
void devicelibrary::createImageViews() {

View File

@ -14,6 +14,7 @@ class devicelibrary {
void destroyImageViews();
void createCommandPool();
void destroyCommandPool();
// ---------- Getters & Setters ----------- //
VkFormat getImageFormat();
std::vector<VkImageView> getSwapChainImageViews();

View File

@ -14,7 +14,11 @@ namespace Global {
VkSurfaceKHR surface = VK_NULL_HANDLE;
VkDevice device = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
VkCommandPool commandPool = VK_NULL_HANDLE;
std::vector<VkCommandBuffer> commandBuffers;
VkQueue graphicsQueue = VK_NULL_HANDLE;
VkQueue presentQueue = 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.

View File

@ -14,6 +14,11 @@ namespace Global {
extern const std::vector<const char*> validationLayers;
extern const bool enableValidationLayers;
extern VkDevice device;
extern VkCommandPool commandPool;
extern std::vector<VkCommandBuffer> commandBuffers;
extern VkQueue graphicsQueue;
extern VkQueue presentQueue;
const int MAX_FRAMES_IN_FLIGHT = 2;
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.
@ -24,7 +29,7 @@ namespace Global {
return graphicsFamily.has_value() && presentFamily.has_value();
}
};
extern VkSwapchainKHR swapChain;
extern VkSurfaceKHR surface;
extern VkPhysicalDevice physicalDevice;
Global::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);

View File

@ -11,8 +11,6 @@ namespace Graphics {
};
std::vector<VkFramebuffer> swapChainFramebuffers;
VkCommandPool commandPool;
VkCommandBuffer commandBuffer;
VkRenderPass renderPass;
VkPipelineLayout pipelineLayout;
@ -242,6 +240,7 @@ namespace Graphics {
}
void graphicspipeline::createCommandPool() {
Global::QueueFamilyIndices queueFamilyIndices = Global::findQueueFamilies(Global::physicalDevice);
VkCommandPoolCreateInfo poolInfo{};
@ -249,28 +248,30 @@ namespace Graphics {
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
if(vkCreateCommandPool(Global::device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
if(vkCreateCommandPool(Global::device, &poolInfo, nullptr, &Global::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);
vkDestroyCommandPool(Global::device, Global::commandPool, nullptr);
}
void graphicspipeline::createCommandBuffer() {
Global::commandBuffers.resize(Global::MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.commandPool = Global::commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = 1;
allocInfo.commandBufferCount = (uint32_t) Global::commandBuffers.size();
if(vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer) != VK_SUCCESS) {
if(vkAllocateCommandBuffers(Global::device, &allocInfo, Global::commandBuffers.data()) != 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) {
void graphicspipeline::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) {
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0; // Optional

View File

@ -13,5 +13,6 @@ namespace Graphics {
void createCommandPool();
void destroyCommandPool();
void createCommandBuffer();
void recordCommandBuffer(VkCommandBuffer cmndBuffer, uint32_t imageIndex);
};
}

113
src/graphics/render.cpp Normal file
View File

@ -0,0 +1,113 @@
#include "render.h"
#include "graphicspipeline.h"
namespace RenderPresent {
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
Graphics::graphicspipeline pipeline;
uint32_t currentFrame = 0;
// At a high level, rendering in Vulkan consists of 5 steps:
// Wait for the previous frame, acquire a image from the swap chain
// record a comman d buffer which draws the scene onto that image
// submit the recorded command buffer and present the image!
void render::drawFrame() {
vkWaitForFences(Global::device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
vkResetFences(Global::device, 1, &inFlightFences[currentFrame]);
uint32_t imageIndex;
vkAcquireNextImageKHR(Global::device, Global::swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
vkResetCommandBuffer(Global::commandBuffers[currentFrame], /*VkCommandBufferResetFlagBits*/ 0);
pipeline.recordCommandBuffer(Global::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 = &Global::commandBuffers[currentFrame];
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]};
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
if (vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
throw std::runtime_error("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = {Global::swapChain};
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
vkQueuePresentKHR(Global::presentQueue, &presentInfo);
currentFrame = (currentFrame + 1) % Global::MAX_FRAMES_IN_FLIGHT;
}
#pragma info
// SEMAPHORES
// Synchronization of execution on the GPU in Vulkan is *explicit* The Order of ops is up to us to
// define the how we want things to run.
// Similarly, Semaphores are used to add order between queue ops. There are 2 kinds of Semaphores; binary, and timeline.
// We are using Binary semaphores, which can be signaled or unsignaled.
// Semaphores are initizalized unsignaled, the way we use them to order queue operations is by providing the same semaphore in one queue op and a wait in another.
// For example:
// VkCommandBuffer QueueOne, QueueTwo = ...
// VkSemaphore semaphore = ...
// enqueue QueueOne, Signal semaphore when done, start now.
// vkQueueSubmit(work: QueueOne, signal: semaphore, wait: none)
// enqueue QueueTwo, wait on semaphore to start
// vkQueueSubmit(work: QueueTwo, signal: None, wait: semaphore)
// FENCES
// Fences are basically semaphores for the CPU! Otherwise known as the host. If the host needs to know when the GPU has finished a task, we use a fence.
// VkCommandBuffer cmndBuf = ...
// VkFence fence = ...
// Start work immediately, signal fence when done.
// vkQueueSubmit(work: cmndBuf, fence: fence)
// vkWaitForFence(fence)
// doStuffOnceFenceDone()
#pragma endinfo
void render::createSyncObject() {
imageAvailableSemaphores.resize(Global::MAX_FRAMES_IN_FLIGHT);
renderFinishedSemaphores.resize(Global::MAX_FRAMES_IN_FLIGHT);
inFlightFences.resize(Global::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 < Global::MAX_FRAMES_IN_FLIGHT; i++) {
if(vkCreateSemaphore(Global::device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
vkCreateSemaphore(Global::device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
vkCreateFence(Global::device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
throw std::runtime_error("Failed to create semaphores!");
}
}
}
void destroyFenceSemaphore() {
for (size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(Global::device, imageAvailableSemaphores[i], nullptr);
vkDestroySemaphore(Global::device, renderFinishedSemaphores[i], nullptr);
vkDestroyFence(Global::device, inFlightFences[i], nullptr);
}
}
}

11
src/graphics/render.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "../global.h"
namespace RenderPresent {
class render {
public:
void drawFrame();
void createSyncObject();
};
}

View File

@ -1,6 +1,7 @@
#include "devicelibrary.h" // Device Library includes global, redundant to include with it here
#include "debug/vulkandebuglibs.h"
#include "graphics/graphicspipeline.h"
#include "graphics/render.h"
#include <cstdint>
#include <cstring>
@ -25,6 +26,7 @@ private:
DeviceControl::devicelibrary deviceLibs;
Debug::vulkandebuglibs debugController;
Graphics::graphicspipeline graphicsPipeline;
RenderPresent::render renderPresentation;
GLFWwindow* window;
VkInstance instance;
@ -52,6 +54,7 @@ private:
graphicsPipeline.createFramebuffers();
graphicsPipeline.createCommandPool();
graphicsPipeline.createCommandBuffer();
renderPresentation.createSyncObject();
}
void createInstance() {
@ -77,7 +80,9 @@ private:
void mainLoop() { // This loop just updates the GLFW window.
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
renderPresentation.drawFrame();
}
vkDeviceWaitIdle(Global::device);
}
void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library!