|
|
@ -12,6 +12,7 @@ |
|
|
|
#include <optional> |
|
|
|
#include <optional> |
|
|
|
#include <set> |
|
|
|
#include <set> |
|
|
|
#include <fstream> |
|
|
|
#include <fstream> |
|
|
|
|
|
|
|
#include <numeric> |
|
|
|
|
|
|
|
|
|
|
|
const std::vector<const char*> deviceExtensions = { |
|
|
|
const std::vector<const char*> deviceExtensions = { |
|
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME |
|
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME |
|
|
@ -52,6 +53,7 @@ bool checkValidationLayerSupport(){ |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constexpr int MAX_FRAMES_IN_FLIGHT = 2; |
|
|
|
|
|
|
|
|
|
|
|
std::vector<char> readFile(const std::string& fileName){ |
|
|
|
std::vector<char> readFile(const std::string& fileName){ |
|
|
|
std::ifstream file(fileName, std::ios::ate | std::ios::binary); |
|
|
|
std::ifstream file(fileName, std::ios::ate | std::ios::binary); |
|
|
@ -87,7 +89,7 @@ private: |
|
|
|
void initWindow(){ |
|
|
|
void initWindow(){ |
|
|
|
glfwInit(); |
|
|
|
glfwInit(); |
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); |
|
|
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); |
|
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); |
|
|
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); |
|
|
|
window = glfwCreateWindow((int)WIDTH, (int)HEIGHT, "Vulkan Simulation", nullptr, nullptr); |
|
|
|
window = glfwCreateWindow((int)WIDTH, (int)HEIGHT, "Vulkan Simulation", nullptr, nullptr); |
|
|
|
} |
|
|
|
} |
|
|
|
void initVulkan(){ |
|
|
|
void initVulkan(){ |
|
|
@ -101,7 +103,7 @@ private: |
|
|
|
createGraphicsPipeline(); |
|
|
|
createGraphicsPipeline(); |
|
|
|
createFramebuffers(); |
|
|
|
createFramebuffers(); |
|
|
|
createCommandPool(); |
|
|
|
createCommandPool(); |
|
|
|
createCommandBuffer(); |
|
|
|
createCommandBuffers(); |
|
|
|
createSyncObjects(); |
|
|
|
createSyncObjects(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -403,6 +405,26 @@ private: |
|
|
|
swapchainExtent = extent; |
|
|
|
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; |
|
|
|
VkSwapchainKHR swapchain = VK_NULL_HANDLE; |
|
|
|
std::vector<VkImage> swapchainImages; |
|
|
|
std::vector<VkImage> swapchainImages; |
|
|
|
VkFormat swapchainImageFormat {}; |
|
|
|
VkFormat swapchainImageFormat {}; |
|
|
@ -615,7 +637,8 @@ private: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VkCommandPool commandPool = VK_NULL_HANDLE; |
|
|
|
VkCommandPool commandPool = VK_NULL_HANDLE; |
|
|
|
VkCommandBuffer commandBuffer = VK_NULL_HANDLE; |
|
|
|
|
|
|
|
|
|
|
|
std::vector<VkCommandBuffer> commandBuffers; |
|
|
|
|
|
|
|
|
|
|
|
void createCommandPool(){ |
|
|
|
void createCommandPool(){ |
|
|
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
|
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice); |
|
|
@ -628,14 +651,16 @@ private: |
|
|
|
vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool); |
|
|
|
vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void createCommandBuffer(){ |
|
|
|
void createCommandBuffers(){ |
|
|
|
|
|
|
|
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); |
|
|
|
|
|
|
|
|
|
|
|
VkCommandBufferAllocateInfo allocateInfo {}; |
|
|
|
VkCommandBufferAllocateInfo allocateInfo {}; |
|
|
|
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
|
|
|
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
|
|
|
allocateInfo.commandPool = commandPool; |
|
|
|
allocateInfo.commandPool = commandPool; |
|
|
|
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
|
|
|
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
|
|
|
allocateInfo.commandBufferCount = 1; |
|
|
|
allocateInfo.commandBufferCount = commandBuffers.size(); |
|
|
|
|
|
|
|
|
|
|
|
vkAllocateCommandBuffers(device, &allocateInfo, &commandBuffer); |
|
|
|
vkAllocateCommandBuffers(device, &allocateInfo, commandBuffers.data()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){ |
|
|
|
void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex){ |
|
|
@ -678,11 +703,15 @@ private: |
|
|
|
vkEndCommandBuffer(commandBuffer); |
|
|
|
vkEndCommandBuffer(commandBuffer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
VkSemaphore imageAvailable = VK_NULL_HANDLE; |
|
|
|
std::vector<VkSemaphore> imageAvailableSemaphores; |
|
|
|
VkSemaphore renderFinished = VK_NULL_HANDLE; |
|
|
|
std::vector<VkSemaphore> renderFinishedSemaphores; |
|
|
|
VkFence inFlight = VK_NULL_HANDLE; |
|
|
|
std::vector<VkFence> inFlightFences; |
|
|
|
|
|
|
|
|
|
|
|
void createSyncObjects(){ |
|
|
|
void createSyncObjects(){ |
|
|
|
|
|
|
|
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); |
|
|
|
|
|
|
|
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); |
|
|
|
|
|
|
|
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); |
|
|
|
|
|
|
|
|
|
|
|
VkSemaphoreCreateInfo semaphoreInfo {}; |
|
|
|
VkSemaphoreCreateInfo semaphoreInfo {}; |
|
|
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
|
|
|
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; |
|
|
|
|
|
|
|
|
|
|
@ -690,36 +719,46 @@ private: |
|
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
|
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; |
|
|
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; |
|
|
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; |
|
|
|
|
|
|
|
|
|
|
|
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailable); |
|
|
|
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ |
|
|
|
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinished); |
|
|
|
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]); |
|
|
|
vkCreateFence(device, &fenceInfo, nullptr, &inFlight); |
|
|
|
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]); |
|
|
|
|
|
|
|
vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
size_t currentFrame = 0; |
|
|
|
|
|
|
|
|
|
|
|
void drawFrame(){ |
|
|
|
void drawFrame(){ |
|
|
|
vkWaitForFences(device, 1, &inFlight, VK_TRUE, UINT64_MAX); |
|
|
|
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); |
|
|
|
vkResetFences(device, 1, &inFlight); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t imageIndex; |
|
|
|
uint32_t imageIndex; |
|
|
|
vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageAvailable, VK_NULL_HANDLE, &imageIndex); |
|
|
|
VkResult result = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); |
|
|
|
vkResetCommandBuffer(commandBuffer, 0); |
|
|
|
if (result == VK_ERROR_OUT_OF_DATE_KHR){ |
|
|
|
recordCommandBuffer(commandBuffer, imageIndex); |
|
|
|
recreateSwapchain(); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkResetFences(device, 1, &inFlightFences[currentFrame]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vkResetCommandBuffer(commandBuffers[currentFrame], 0); |
|
|
|
|
|
|
|
recordCommandBuffer(commandBuffers[currentFrame], imageIndex); |
|
|
|
|
|
|
|
|
|
|
|
VkSubmitInfo submitInfo {}; |
|
|
|
VkSubmitInfo submitInfo {}; |
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
|
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
|
|
|
|
|
|
|
|
|
|
|
VkSemaphore waitSemaphores[] = {imageAvailable}; |
|
|
|
VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; |
|
|
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; |
|
|
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; |
|
|
|
submitInfo.waitSemaphoreCount = 1; |
|
|
|
submitInfo.waitSemaphoreCount = 1; |
|
|
|
submitInfo.pWaitSemaphores = waitSemaphores; |
|
|
|
submitInfo.pWaitSemaphores = waitSemaphores; |
|
|
|
submitInfo.pWaitDstStageMask = waitStages; |
|
|
|
submitInfo.pWaitDstStageMask = waitStages; |
|
|
|
submitInfo.commandBufferCount = 1; |
|
|
|
submitInfo.commandBufferCount = 1; |
|
|
|
submitInfo.pCommandBuffers = &commandBuffer; |
|
|
|
submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; |
|
|
|
|
|
|
|
|
|
|
|
VkSemaphore signalSemaphores[] = {renderFinished}; |
|
|
|
VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; |
|
|
|
submitInfo.signalSemaphoreCount = 1; |
|
|
|
submitInfo.signalSemaphoreCount = 1; |
|
|
|
submitInfo.pSignalSemaphores = signalSemaphores; |
|
|
|
submitInfo.pSignalSemaphores = signalSemaphores; |
|
|
|
|
|
|
|
|
|
|
|
vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlight); |
|
|
|
vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]); |
|
|
|
|
|
|
|
|
|
|
|
VkPresentInfoKHR presentInfo {}; |
|
|
|
VkPresentInfoKHR presentInfo {}; |
|
|
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
|
|
|
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
|
|
@ -731,7 +770,13 @@ private: |
|
|
|
presentInfo.pSwapchains = swapchains; |
|
|
|
presentInfo.pSwapchains = swapchains; |
|
|
|
presentInfo.pImageIndices = &imageIndex; |
|
|
|
presentInfo.pImageIndices = &imageIndex; |
|
|
|
|
|
|
|
|
|
|
|
vkQueuePresentKHR(presentQueue, &presentInfo); |
|
|
|
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(){ |
|
|
|
void mainLoop(){ |
|
|
@ -743,20 +788,16 @@ private: |
|
|
|
vkDeviceWaitIdle(device); |
|
|
|
vkDeviceWaitIdle(device); |
|
|
|
} |
|
|
|
} |
|
|
|
void cleanup(){ |
|
|
|
void cleanup(){ |
|
|
|
vkDestroySemaphore(device, imageAvailable, nullptr); |
|
|
|
cleanupSwapchain(); |
|
|
|
vkDestroySemaphore(device, renderFinished, nullptr); |
|
|
|
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++){ |
|
|
|
vkDestroyFence(device, inFlight, nullptr); |
|
|
|
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); |
|
|
|
vkDestroyCommandPool(device, commandPool, nullptr); |
|
|
|
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); |
|
|
|
for (auto framebuffer : swapchainFramebuffers){ |
|
|
|
vkDestroyFence(device, inFlightFences[i], nullptr); |
|
|
|
vkDestroyFramebuffer(device, framebuffer, nullptr); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
vkDestroyCommandPool(device, commandPool, nullptr); |
|
|
|
vkDestroyPipeline(device, graphicsPipeline, nullptr); |
|
|
|
vkDestroyPipeline(device, graphicsPipeline, nullptr); |
|
|
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); |
|
|
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr); |
|
|
|
vkDestroyRenderPass(device, renderPass, nullptr); |
|
|
|
vkDestroyRenderPass(device, renderPass, nullptr); |
|
|
|
for (auto imageView : swapchainImageViews){ |
|
|
|
|
|
|
|
vkDestroyImageView(device, imageView, nullptr); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
vkDestroySwapchainKHR(device, swapchain, nullptr); |
|
|
|
|
|
|
|
vkDestroyDevice(device, nullptr); |
|
|
|
vkDestroyDevice(device, nullptr); |
|
|
|
vkDestroySurfaceKHR(instance, surface, nullptr); |
|
|
|
vkDestroySurfaceKHR(instance, surface, nullptr); |
|
|
|
vkDestroyInstance(instance, nullptr); |
|
|
|
vkDestroyInstance(instance, nullptr); |
|
|
|