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:
parent
10a8c236f0
commit
29599e4b9a
@ -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() {
|
||||
|
@ -14,6 +14,7 @@ class devicelibrary {
|
||||
void destroyImageViews();
|
||||
void createCommandPool();
|
||||
void destroyCommandPool();
|
||||
|
||||
// ---------- Getters & Setters ----------- //
|
||||
VkFormat getImageFormat();
|
||||
std::vector<VkImageView> getSwapChainImageViews();
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
113
src/graphics/render.cpp
Normal 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
11
src/graphics/render.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "../global.h"
|
||||
|
||||
|
||||
namespace RenderPresent {
|
||||
class render {
|
||||
public:
|
||||
void drawFrame();
|
||||
void createSyncObject();
|
||||
};
|
||||
}
|
@ -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!
|
||||
|
Loading…
Reference in New Issue
Block a user