diff --git a/VulkanTutorial.vcxproj b/VulkanTutorial.vcxproj index ba8a931..ecc4e22 100644 --- a/VulkanTutorial.vcxproj +++ b/VulkanTutorial.vcxproj @@ -61,8 +61,12 @@ - D:\Code\GLFW3\include;C:\VulkanSDK\1.0.46.0\Include;$(IncludePath) - C:\VulkanSDK\1.0.46.0\Lib;D:\Code\GLFW3\win64\lib-vc2015;$(LibraryPath) + D:\Code\GLFW3\include;C:\VulkanSDK\1.1.70.1\Include;$(IncludePath) + C:\VulkanSDK\1.1.70.1\Lib;D:\Code\GLFW3\win64\lib-vc2015;$(LibraryPath) + + + D:\Code\GLFW3\include;C:\VulkanSDK\1.1.70.1\Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); + C:\VulkanSDK\1.1.70.1\Lib;D:\Code\GLFW3\win64\lib-vc2015;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64 diff --git a/main.cpp b/main.cpp index 136eb3c..be1a971 100644 --- a/main.cpp +++ b/main.cpp @@ -61,26 +61,26 @@ public: private: // Instance variables: GLFWwindow* window; - VDeleter instance {vkDestroyInstance}; - VDeleter callback {instance, DestroyDebugReportCallbackEXT}; + VkInstance instance; + VkDebugReportCallbackEXT callback; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkQueue graphicsQueue; - VDeleter device {vkDestroyDevice}; - VDeleter surface {instance, vkDestroySurfaceKHR}; + VkDevice device; + VkSurfaceKHR surface; VkQueue presentQueue; - VDeleter swapChain {device, vkDestroySwapchainKHR}; + VkSwapchainKHR swapChain; std::vector swapChainImages; VkFormat swapChainImageFormat; VkExtent2D swapChainExtent; - std::vector> swapChainImageViews; - VDeleter renderPass {device, vkDestroyRenderPass}; - VDeleter pipelineLayout {device, vkDestroyPipelineLayout}; - VDeleter graphicsPipeline {device, vkDestroyPipeline}; - std::vector> swapChainFramebuffers; - VDeleter commandPool {device, vkDestroyCommandPool}; + std::vector swapChainImageViews; + VkRenderPass renderPass; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + std::vector swapChainFramebuffers; + VkCommandPool commandPool; std::vector commandBuffers; - VDeleter imageAvailableSemaphore {device, vkDestroySemaphore}; - VDeleter renderFinishedSemaphore {device, vkDestroySemaphore}; + VkSemaphore imageAvailableSemaphore; + VkSemaphore renderFinishedSemaphore; // Initialize our GLFW window. @@ -175,7 +175,7 @@ private: createInfo.enabledExtensionCount = reqExtensions.size(); createInfo.ppEnabledExtensionNames = reqExtensions.data(); - if(vkCreateInstance(&createInfo, nullptr, instance.replace()) != VK_SUCCESS) { + if(vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create Vulkan instance!"); } @@ -192,6 +192,10 @@ private: } } + void destroyInstance() { + vkDestroyInstance(instance, nullptr); + } + // Check if the validation layers are supported.s bool checkValidationLayerSupport() { uint32_t layerCount; @@ -227,11 +231,17 @@ private: createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; createInfo.pfnCallback = debugCallback; - if(CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, callback.replace()) != VK_SUCCESS) { + if(CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { throw std::runtime_error("failed to setup debug callback!"); } } + void cleanupDebugCallback() { + if (!enableValidationLayers) return; + + DestroyDebugReportCallbackEXT(instance, callback, nullptr); + } + // Pick the first suitable physical device, using isDeviceSuitable(VkPhysicalDevice) void pickPhysicalDevice() { uint32_t deviceCount = 0; @@ -368,7 +378,7 @@ private: createInfo.enabledLayerCount = 0; } - if(vkCreateDevice(physicalDevice, &createInfo, nullptr, device.replace()) != VK_SUCCESS) { + if(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { throw std::runtime_error("failed to create logical device!"); } @@ -376,14 +386,22 @@ private: vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue); } + void destroyLogicalDevice() { + vkDestroyDevice(device, nullptr); + } + // Have GLFW create a surface for us, this lets us not be concerned with the platform-specifics // involved in surfaces. void createSurface() { - if(glfwCreateWindowSurface(instance, window, nullptr, surface.replace()) != VK_SUCCESS) { + if(glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { throw std::runtime_error("failed to create window surface!"); } } + void destroySurface() { + vkDestroySurfaceKHR(instance, surface, nullptr); + } + // Find out what formats and present modes the physical device supports. SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { SwapChainSupportDetails details; @@ -457,7 +475,7 @@ private: // Create the swap chain that will be used to submit completed frames to void createSwapChain() { - SwapChainSupportDetails swapChainSupport= querySwapChainSupport(physicalDevice); + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); @@ -532,8 +550,12 @@ private: swapChainExtent = extent; } + void destroySwapChain() { + vkDestroySwapchainKHR(device, swapChain, nullptr); + } + void createImageViews() { - swapChainImageViews.resize(swapChainImages.size(), VDeleter{device, vkDestroyImageView}); + swapChainImageViews.resize(swapChainImages.size(), VkImageView{}); // For each image in the swap chain create a VkImageView for(uint32_t i = 0; i < swapChainImages.size(); i++) { @@ -556,22 +578,28 @@ private: createInfo.subresourceRange.baseArrayLayer = 0; createInfo.subresourceRange.layerCount = 1; - if(vkCreateImageView(device, &createInfo, nullptr, swapChainImageViews[i].replace()) != VK_SUCCESS) { + if(vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create image views!"); } } } + void destroyImageViews() { + for (uint32_t i = 0; i < swapChainImages.size(); i++) { + vkDestroyImageView(device, swapChainImageViews[i], nullptr); + } + } + void createGraphicsPipeline() { auto vertShaderCode = readFile("shaders/vert.spv"); auto fragShaderCode = readFile("shaders/frag.spv"); - VDeleter vertShaderModule{device, vkDestroyShaderModule}; - VDeleter fragShaderModule{device, vkDestroyShaderModule}; + VkShaderModule vertShaderModule; + VkShaderModule fragShaderModule; - createShaderModule(vertShaderCode, vertShaderModule); - createShaderModule(fragShaderCode, fragShaderModule); + createShaderModule(vertShaderCode, &vertShaderModule); + createShaderModule(fragShaderCode, &fragShaderModule); VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -670,7 +698,7 @@ private: pipelineLayoutInfo.setLayoutCount = 0; pipelineLayoutInfo.pSetLayouts = nullptr; - if(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, pipelineLayout.replace()) != VK_SUCCESS) { + if(vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) { throw std::runtime_error("failed to create pipeline layout!"); } @@ -696,11 +724,16 @@ private: pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = -1; - if(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, graphicsPipeline.replace()) != VK_SUCCESS) { + if(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("failed to create graphics pipeline!"); } } + void destroyGraphicsPipeline() { + vkDestroyPipeline(device, graphicsPipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + } + static std::vector readFile(const std::string& filename) { // Start reading at end of the file, as binary. std::ifstream file(filename, std::ios::ate | std::ios::binary); @@ -718,13 +751,13 @@ private: return buffer; } - void createShaderModule(const std::vector& code, VDeleter& shaderModule) { + void createShaderModule(const std::vector& code, VkShaderModule* shaderModule) { VkShaderModuleCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.codeSize = code.size(); createInfo.pCode = (uint32_t*) code.data(); - if(vkCreateShaderModule(device, &createInfo, nullptr, shaderModule.replace()) != VK_SUCCESS) { + if(vkCreateShaderModule(device, &createInfo, nullptr, shaderModule) != VK_SUCCESS) { throw std::runtime_error("failed to create shader module!"); } } @@ -770,13 +803,17 @@ private: renderPassInfo.dependencyCount = 1; renderPassInfo.pDependencies = &dependency; - if(vkCreateRenderPass(device, &renderPassInfo, nullptr, renderPass.replace()) != VK_SUCCESS) { + if(vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { throw std::runtime_error("failed to create render pass!"); } } + + void destroyRenderPass() { + vkDestroyRenderPass(device, renderPass, nullptr); + } void createFramebuffers() { - swapChainFramebuffers.resize(swapChainImageViews.size(), VDeleter {device, vkDestroyFramebuffer}); + swapChainFramebuffers.resize(swapChainImageViews.size(), VkFramebuffer{}); for(size_t i = 0; i < swapChainImageViews.size(); i++) { VkImageView attachments[] = { swapChainImageViews[i] }; @@ -790,13 +827,19 @@ private: framebufferInfo.height = swapChainExtent.height; framebufferInfo.layers = 1; - if(vkCreateFramebuffer(device, &framebufferInfo, nullptr, swapChainFramebuffers[i].replace()) != VK_SUCCESS) { + if(vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) { throw std::runtime_error("failed to create framebuffer!"); } } } + void destroyFramebuffers() { + for (size_t i = 0; i < swapChainImageViews.size(); i++) { + vkDestroyFramebuffer(device, swapChainFramebuffers[i], nullptr); + } + } + void createCommandPool() { QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); @@ -805,11 +848,15 @@ private: poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; poolInfo.flags = 0; - if(vkCreateCommandPool(device, &poolInfo, nullptr, commandPool.replace()) != VK_SUCCESS) { + if(vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { throw std::runtime_error("failed to create command pool!"); } } + void destroyCommandPool() { + vkDestroyCommandPool(device, commandPool, nullptr); + } + void createCommandBuffers() { if(commandBuffers.size() > 0) { vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); @@ -859,17 +906,28 @@ private: } } } + + void destroyCommandBuffers() { + if (commandBuffers.size() > 0) { + vkFreeCommandBuffers(device, commandPool, commandBuffers.size(), commandBuffers.data()); + } + } 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) { + if(vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphore) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphore) != VK_SUCCESS) { throw std::runtime_error("failed to create semaphores!"); } } + void destroySemaphores() { + vkDestroySemaphore(device, imageAvailableSemaphore, nullptr); + vkDestroySemaphore(device, renderFinishedSemaphore, nullptr); + } + void mainLoop() { // While we shouldn't close, have glfw poll for events while(!glfwWindowShouldClose(window)) { @@ -878,6 +936,22 @@ private: } vkDeviceWaitIdle(device); + cleanup(); + } + + void cleanup() { + destroySemaphores(); + destroyCommandBuffers(); + destroyCommandPool(); + destroyFramebuffers(); + destroyGraphicsPipeline(); + destroyRenderPass(); + destroyImageViews(); + destroySwapChain(); + destroyLogicalDevice(); + destroySurface(); + cleanupDebugCallback(); + destroyInstance(); } void drawFrame() { diff --git a/main.h b/main.h index 9cc1fd1..89467da 100644 --- a/main.h +++ b/main.h @@ -16,65 +16,6 @@ void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT } } -// VDeleter utility class that automatically destroys objects when they fall out of scope. -template -class VDeleter { -public: - VDeleter() : VDeleter([](T, VkAllocationCallbacks*) {}) {} - - VDeleter(std::function deletef) { - this->deleter = [=](T obj) { deletef(obj, nullptr); }; - } - - VDeleter(const VDeleter& instance, std::function deletef) { - this->deleter = [&instance, deletef](T obj) { deletef(instance, obj, nullptr); }; - } - - VDeleter(const VDeleter& device, std::function deletef) { - this->deleter = [&device, deletef](T obj) { deletef(device, obj, nullptr); }; - } - - ~VDeleter() { - cleanup(); - } - - const T* operator &() const { - return &object; - } - - T* replace() { - cleanup(); - return &object; - } - - operator T() const { - return object; - } - - void operator=(T rhs) { - if (rhs != object) { - cleanup(); - object = rhs; - } - } - - template - bool operator==(V rhs) { - return object == T(rhs); - } - -private: - T object{VK_NULL_HANDLE}; - std::function deleter; - - void cleanup() { - if (object != VK_NULL_HANDLE) { - deleter(object); - } - object = VK_NULL_HANDLE; - } -}; - struct QueueFamilyIndices { int graphicsFamily = -1; int presentFamily = -1;