diff --git a/Makefile b/Makefile index bbd65b7..2fe73b3 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ VULKAN_SDK_PATH = /mnt/storage/Downloads/VulkanSDK/1.0.30.0/x86_64 CFLAGS = -std=c++11 -I$(VULKAN_SDK_PATH)/include LDFLAGS = -L$(VULKAN_SDK_PATH)/lib -L/usr/local/lib -lglfw3 -lrt -lm -ldl -lXrandr -lXinerama -lXxf86vm -lXext -lXcursor -lXrender -lXfixes -lX11 -lpthread -lxcb -lXau -lvulkan +all: VulkanTest + VulkanTest: main.cpp g++ $(CFLAGS) -g -o VulkanTest main.cpp $(LDFLAGS) diff --git a/main.cpp b/main.cpp index c7ac506..04bf053 100644 --- a/main.cpp +++ b/main.cpp @@ -46,6 +46,13 @@ public: return VK_FALSE; } + static void onWindowResized(GLFWwindow* window, int width, int height) { + if(width == 0 || height == 0) return; + + HelloTriangleApplication* app = reinterpret_cast(glfwGetWindowUserPointer(window)); + app->recreateSwapChain(); + } + private: // Instance variables: GLFWwindow* window; @@ -66,13 +73,21 @@ private: VDeleter graphicsPipeline {device, vkDestroyPipeline}; std::vector> swapChainFramebuffers; VDeleter commandPool {device, vkDestroyCommandPool}; + std::vector commandBuffers; + VDeleter imageAvailableSemaphore {device, vkDestroySemaphore}; + VDeleter renderFinishedSemaphore {device, vkDestroySemaphore}; + // Initialize our GLFW window. void initWindow() { glfwInit(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); + + glfwSetWindowUserPointer(window, this); + glfwSetWindowSizeCallback(window, HelloTriangleApplication::onWindowResized); } void initVulkan() { @@ -86,6 +101,20 @@ private: createRenderPass(); createGraphicsPipeline(); createFramebuffers(); + createCommandPool(); + createCommandBuffers(); + createSemaphores(); + } + + void recreateSwapChain() { + vkDeviceWaitIdle(device); + + createSwapChain(); + createImageViews(); + createRenderPass(); + createGraphicsPipeline(); + createFramebuffers(); + createCommandBuffers(); } // Figure out what extensions GLFW requires to make a Vulkan surface. @@ -478,10 +507,16 @@ private: // and we want to create a new swap chain to match the new window size. createInfo.oldSwapchain = VK_NULL_HANDLE; - if(vkCreateSwapchainKHR(device, &createInfo, nullptr, swapChain.replace()) != VK_SUCCESS) { + VkSwapchainKHR oldSwapChain = swapChain; + createInfo.oldSwapchain = oldSwapChain; + + VkSwapchainKHR newSwapChain; + if(vkCreateSwapchainKHR(device, &createInfo, nullptr, &newSwapChain) != VK_SUCCESS) { throw std::runtime_error("failed to create swap chain!"); } + swapChain = newSwapChain; + // Get the swapchain images. vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); swapChainImages.resize(imageCount); @@ -719,6 +754,17 @@ private: renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subPass; + 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_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + if(vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } @@ -746,11 +792,125 @@ private: } } + void createCommandPool() { + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); + + VkCommandPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; + poolInfo.flags = 0; + + if(vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + throw std::runtime_error("failed to create command pool!"); + } + } + + void createCommandBuffers() { + if(commandBuffers.size() > 0) { + vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); + } + + commandBuffers.resize(swapChainFramebuffers.size()); + + VkCommandBufferAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = (uint32_t) commandBuffers.size(); + + if(vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate command buffers!"); + } + + for(size_t i = 0; i < commandBuffers.size(); i++) { + VkCommandBufferBeginInfo beginInfo = {}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; + beginInfo.pInheritanceInfo = nullptr; + + vkBeginCommandBuffer(commandBuffers[i], &beginInfo); + + VkRenderPassBeginInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = swapChainFramebuffers[i]; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = swapChainExtent; + + VkClearValue clearColor = {0.0f, 0.0f, 0.0f, 1.0f}; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + vkCmdDraw(commandBuffers[i], 3, 1, 0, 0); + + vkCmdEndRenderPass(commandBuffers[i]); + + if(vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("failed to record command buffer!"); + } + } + } + + void createSemaphores() { + VkSemaphoreCreateInfo semaphoreInfo = {}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + if(vkCreateSemaphore(device, &semaphoreInfo, nullptr, imageAvailableSemaphore.replace()) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, renderFinishedSemaphore.replace()) != VK_SUCCESS) { + throw std::runtime_error("failed to create semaphores!"); + } + } + void mainLoop() { // While we shouldn't close, have glfw poll for events while(!glfwWindowShouldClose(window)) { glfwPollEvents(); + drawFrame(); } + + vkDeviceWaitIdle(device); + } + + void drawFrame() { + uint32_t imageIndex; + vkAcquireNextImageKHR(device, swapChain, std::numeric_limits::max(), imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex); + + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {imageAvailableSemaphore}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[imageIndex]; + + VkSemaphore signalSemaphores[] = {renderFinishedSemaphore}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + if(vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != 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[] = {swapChain}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + + vkQueuePresentKHR(presentQueue, &presentInfo); } };