From 75e9921d4565f17a8f72f7fe3213566a03a14a33 Mon Sep 17 00:00:00 2001 From: macmacmac Date: Sat, 31 Aug 2024 13:47:29 -0400 Subject: [PATCH] Complete "Hello Triangle" program with resizing and minimization handled --- .gitignore | 2 +- CMakeLists.txt | 7 ++ include/vulkanapp.hh | 34 +++++--- shaders/triangle.vert | 4 +- src/vulkanapp.cc | 181 +++++++++++++++++++++++++++++++----------- 5 files changed, 168 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index b594ca7..2994ef0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .cache/ +.vscode/ build/ - diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c18426..25706cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,13 @@ add_executable(${EXE_NAME} ${CMAKE_SOURCE_DIR}/src/main.cc ${PROGRAM_SOURCES} ${ target_include_directories(${EXE_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/include) add_dependencies(${EXE_NAME} shaders) +if ( CMAKE_COMPILER_IS_GNUCC ) + target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra) +endif() +if ( MSVC ) + target_compile_options(${EXE_NAME} PRIVATE /W4) +endif() + # Linking target_link_libraries(${EXE_NAME} PRIVATE SDL2::SDL2) target_link_libraries(${EXE_NAME} PRIVATE Vulkan::Vulkan) \ No newline at end of file diff --git a/include/vulkanapp.hh b/include/vulkanapp.hh index 72b3370..d3d6758 100644 --- a/include/vulkanapp.hh +++ b/include/vulkanapp.hh @@ -17,6 +17,8 @@ const std::vector deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; +const int MAX_FRAMES_IN_FLIGHT = 2; + #ifdef NDEBUG const bool enableValidationLayers = false; #else @@ -44,7 +46,7 @@ struct SwapChainSupportDetails { class VulkanApp { public: VulkanApp(const uint32_t& _w, const uint32_t& _h) : - mWidth(_w), mHeight(_h), mWin(nullptr), mActive(false) {} + mWin(nullptr), mWidth(_w), mHeight(_h) {} void init(); void loop(); @@ -55,9 +57,11 @@ private: SDL_Event mEvent; // Not tied to library - const uint32_t mWidth; - const uint32_t mHeight; - bool mActive; + uint32_t mWidth; + uint32_t mHeight; + bool mActive = false; + bool mResized = false; + bool mMinimized = false; // Vulkan VkInstance mInstance; @@ -77,14 +81,14 @@ private: VkPipeline mGraphicsPipeline; VkCommandPool mCommandPool; - VkCommandBuffer mCommandBuffer; + std::vector mCommandBuffers; VkQueue mGraphicsQueue; VkQueue mPresentQueue; - VkSemaphore mImageAvailableSemaphore; - VkSemaphore mRenderFinishedSemaphore; - VkFence mInFlightFence; + std::vector mImageAvailableSemaphores; + std::vector mRenderFinishedSemaphores; + std::vector mInFlightFences; VkDebugUtilsMessengerEXT mDebugMessenger; @@ -92,7 +96,11 @@ private: void createInstance(); void selectPhysicalDevice(); void createLogicalDevice(); + void createSwapChain(); + void recreateSwapChain(); + void cleanupSwapChain(); + void createImageViews(); void createRenderPass(); void createGraphicsPipeline(); @@ -118,7 +126,15 @@ private: static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { - fprintf(stderr, "Validation Layer: %s \n", pCallbackData->pMessage); + (void)pUserData; + + if(messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + fprintf(stderr, "Validation Layer [Error/Warning]: %s \n", pCallbackData->pMessage); + } else if(messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) { + fprintf(stderr, "Validation Layer [General]: %s \n", pCallbackData->pMessage); + } else { + fprintf(stderr, "Validation Layer: %s \n", pCallbackData->pMessage); + } return VK_FALSE; } diff --git a/shaders/triangle.vert b/shaders/triangle.vert index f5b2f8d..af2dac5 100644 --- a/shaders/triangle.vert +++ b/shaders/triangle.vert @@ -2,13 +2,13 @@ layout(location = 0) out vec3 fragColor; -vec2 positions[3] = vec2[]( +const vec2 positions[3] = vec2[]( vec2(0.0, -0.5), vec2(0.5, 0.5), vec2(-0.5, 0.5) ); -vec3 colors[3] = vec3[]( +const vec3 colors[3] = vec3[]( vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0) diff --git a/src/vulkanapp.cc b/src/vulkanapp.cc index 2d1d72d..efc0e3c 100644 --- a/src/vulkanapp.cc +++ b/src/vulkanapp.cc @@ -83,7 +83,7 @@ QueueFamilyIndices VulkanApp::findQueueFamilies(VkPhysicalDevice device) { vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); // Get queue families and check them - for(int i=0;i(&debugCreateInfo); @@ -415,12 +441,17 @@ void VulkanApp::createLogicalDevice() { } VkPhysicalDeviceFeatures deviceFeatures{}; + + vkGetPhysicalDeviceFeatures(mPhysicalDevice, &deviceFeatures); + VkDeviceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.pEnabledFeatures = &deviceFeatures; + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); createInfo.ppEnabledExtensionNames = deviceExtensions.data(); @@ -493,6 +524,28 @@ void VulkanApp::createSwapChain() { mSwapChainExtent = extent; } +void VulkanApp::recreateSwapChain() { + vkDeviceWaitIdle(mLogicalDevice); + + cleanupSwapChain(); + + createSwapChain(); + createImageViews(); + createFramebuffers(); +} + +void VulkanApp::cleanupSwapChain() { + for (VkFramebuffer framebuffer : mSwapChainFramebuffers) { + vkDestroyFramebuffer(mLogicalDevice, framebuffer, nullptr); + } + + for (VkImageView imageView : mSwapChainImageViews) { + vkDestroyImageView(mLogicalDevice, imageView, nullptr); + } + + vkDestroySwapchainKHR(mLogicalDevice, mSwapChain, nullptr); +} + void VulkanApp::createImageViews() { mSwapChainImageViews.resize(mSwapChainImages.size()); @@ -652,7 +705,12 @@ void VulkanApp::createGraphicsPipeline() { pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - if (vkCreateGraphicsPipelines(mLogicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &mGraphicsPipeline) != VK_SUCCESS) { + if (vkCreateGraphicsPipelines(mLogicalDevice, + VK_NULL_HANDLE, + 1, + &pipelineInfo, + nullptr, + &mGraphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("Could not create Vulkan graphics pipeline!"); } @@ -699,18 +757,24 @@ void VulkanApp::createCommandPool() { } void VulkanApp::createCommandBuffer() { + mCommandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + VkCommandBufferAllocateInfo allocInfo{}; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.commandPool = mCommandPool; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = 1; + allocInfo.commandBufferCount = static_cast(mCommandBuffers.size()); - if (vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, &mCommandBuffer) != VK_SUCCESS) { + if (vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, mCommandBuffers.data()) != VK_SUCCESS) { throw std::runtime_error("Could not allocate Vulkan command buffers!"); } } void VulkanApp::createSyncObjects() { + mImageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + mRenderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + mInFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + VkSemaphoreCreateInfo semaphoreInfo{}; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; @@ -718,14 +782,15 @@ void VulkanApp::createSyncObjects() { fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - VkResult imageAvailableResult = vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mImageAvailableSemaphore); - VkResult renderFinishedResult = vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mRenderFinishedSemaphore); - VkResult inFlightResult = vkCreateFence(mLogicalDevice, &fenceInfo, nullptr, &mInFlightFence); + for(size_t i = 0;i