From a05df4d541d4ca6d80e994d9e6931af3a7b13a30 Mon Sep 17 00:00:00 2001 From: Alexandre Date: Wed, 22 Apr 2026 22:54:24 +0200 Subject: [PATCH] Added a deletion queue, and started reworking the render method --- CMake/BuildInfo.cmake | 35 +++++++++++++++++++ CMakeLists.txt | 10 +++++- src/buildInfo.hpp.in | 12 +++++++ src/engine/engine.h | 53 +++++++++++++++++++++++++--- src/engine/initializers.h | 72 ++++++++++++++++++++++++++++++++++++++- src/engine/types.h | 34 ++++++++++++++++++ 6 files changed, 210 insertions(+), 6 deletions(-) create mode 100644 CMake/BuildInfo.cmake create mode 100644 src/buildInfo.hpp.in diff --git a/CMake/BuildInfo.cmake b/CMake/BuildInfo.cmake new file mode 100644 index 0000000..3f20e41 --- /dev/null +++ b/CMake/BuildInfo.cmake @@ -0,0 +1,35 @@ +find_package(Git) + +if (GIT_FOUND) + message("Found git !") + execute_process( + COMMAND ${GIT_EXECUTABLE} describe --tags --allways + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ENGINE_VERSION + RESULT_VARIABLE GIT_DESCRIBE_ERROR_CODE + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ENGINE_HASH + RESULT_VARIABLE GIT_HASH_ERROR_CODE + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) +endif() +# Fallback + +if (NOT ENGINE_VERSION OR GIT_DESCRIBE_ERROR_CODE) + set(ENGINE_VERSION "0.0.0") + message(WARNING "No Git tag found, using default version ${ENGINE_VERSION}, fix this....") +endif() + +if (NOT ENGINE_HASH OR GIT_HASH_ERROR_CODE) + set(ENGINE_HASH "0000000") + message(WARNING "No Git hash found, using default hash ${ENGINE_HASH}") +endif() + +string(REGEX REPLACE "^v" "" ENGINE_VERSION "${ENGINE_VERSION}") diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b41d0a..cec3392 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,15 @@ cmake_minimum_required (VERSION 3.29) +project(VulkanEngine) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake") + +include(BuildInfo) include(FetchContent) #LTO include(CheckIPOSupported) check_ipo_supported(RESULT LTO_SUPPORTED OUTPUT LTO_ERROR) -project(VulkanEngine) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -20,6 +25,7 @@ find_package( glfw3 REQUIRED ) find_package( glm REQUIRED ) find_package( VulkanMemoryAllocator CONFIG REQUIRED ) find_package( fmt REQUIRED ) +find_package( Git QUIET ) # fastgltf because it's not in nixpkgs @@ -59,6 +65,8 @@ function ( addBinary BINARY_NAME ) PRIVATE "VULKAN_HPP_HANDLE_ERROR_OUT_OF_DATE_AS_SUCCESS" $<$:DEBUG> # So CMAKE expose a #define DEBUG for debug builds + # Exposes versions + BUILD_ID="${ENGINE_VERSION}" ) diff --git a/src/buildInfo.hpp.in b/src/buildInfo.hpp.in new file mode 100644 index 0000000..e5624f0 --- /dev/null +++ b/src/buildInfo.hpp.in @@ -0,0 +1,12 @@ +// This file is auto generated by CMAKE +// Do not edit. +#pragma once + +// Short current commit git hash +#define BUILD_GIT_HASH "@GIT_HASH@" + +// UTC Timestamp +#define BUILD_TIMESTAMP "@BUILD_TIMESTAMP@" + +// Build ID +#define BUILD_ID "@GIT_HASH@-@BUILD_TIMESTAMP@" diff --git a/src/engine/engine.h b/src/engine/engine.h index 1dd601f..cdb09d3 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -3,12 +3,15 @@ // Normal header #include "types.h" +#include + struct FrameData { VkCommandPool _commandPool; VkCommandBuffer _mainCommandBuffer; VkSemaphore _swapchainSemaphore, _renderSemaphore; VkFence _renderFence; + DeletionQueue _deletionQueue; }; constexpr uint32_t FRAME_OVERLAP = 2; @@ -43,6 +46,14 @@ class Engine { VkQueue _graphicsQueue; uint32_t _graphicsQueueFamily; + // Deletion queue + DeletionQueue _mainDeletionQueue; + VmaAllocator _allocator; + + // Draw resources + AllocatedImage _drawImage; + VkExtent2D _drawExtent; + void init(); @@ -70,15 +81,21 @@ class Engine { #ifndef ENGINE_IMPL_H #define ENGINE_IMPL_H //========== Implementation ========== - +// Implementation defines #define IMAGES_IMPL -#include "images.h" #define INITIALIZERS_IMPL -#include "initializers.h" -#include "VkBootstrap.h" #define CALLBACK_IMPL +#define VMA_IMPLEMENTATION + +// Custom includes +#include "images.h" +#include "initializers.h" #include "callbacks.h" +#include "VkBootstrap.h" +#include "vk_mem_alloc.h" + + #ifndef DEBUG constexpr bool bUseValidationLayers = false; #else @@ -101,6 +118,7 @@ using namespace std; void Engine::init() { #ifdef DEBUG fmt::println("Initializing the engine..."); + fmt::println("Current engine version {}", BUILD_ID); #endif glfwInit(); @@ -141,8 +159,14 @@ void Engine::cleanup() { vkDestroyFence(_device, _frames[i]._renderFence, nullptr); vkDestroySemaphore(_device, _frames[i]._renderSemaphore, nullptr); vkDestroySemaphore(_device, _frames[i]._swapchainSemaphore, nullptr); + + // Uses the deletion queue to clear ressources + _frames[i]._deletionQueue.flush(); } + // Flush the global deletion queue + _mainDeletionQueue.flush(); + destroySwapchain(); vkDestroySurfaceKHR(_instance, _surface, nullptr); @@ -182,6 +206,9 @@ void Engine::cleanup() { void Engine::draw() { // wait until the gpu has finished rendering the last frame. Timeout of 1s VK_CHECK(vkWaitForFences(_device, 1, &getCurrentFrame()._renderFence, true, 1000000000)); + // Flush frame data + getCurrentFrame()._deletionQueue.flush(); + // Reset fences to redo sync VK_CHECK(vkResetFences(_device, 1, &getCurrentFrame()._renderFence)); // Request image from the swapchain @@ -334,6 +361,18 @@ void Engine::initVulkan() { _graphicsQueue = { vkbDevice.get_queue(vkb::QueueType::graphics).value() }; _graphicsQueueFamily = { vkbDevice.get_queue_index(vkb::QueueType::graphics).value() }; + VmaAllocatorCreateInfo allocatorInfo = {}; + allocatorInfo.physicalDevice = _choosenGPU; + allocatorInfo.device = _device; + allocatorInfo.instance = _instance; + allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; + + vmaCreateAllocator(&allocatorInfo, &_allocator); + + _mainDeletionQueue.pushFunction([&]() { + vmaDestroyAllocator(_allocator); + }); + #ifdef DEBUG fmt::println("Vulkan initialized !"); #endif @@ -346,6 +385,12 @@ void Engine::initSwapchain() { createSwapchain(_windowExtent.width, _windowExtent.height); + VkExtent3D drawImageExtent = { + _windowExtent.width, + _windowExtent.height, + 1 + }; + #ifdef DEBUG fmt::println("Swapchain initialized !"); #endif diff --git a/src/engine/initializers.h b/src/engine/initializers.h index dd207ab..7414a79 100644 --- a/src/engine/initializers.h +++ b/src/engine/initializers.h @@ -2,6 +2,7 @@ #define INITIALIZERS_H // Normal header #include "types.h" +#include namespace vkinit { VkCommandPoolCreateInfo commandPoolCreateInfo( @@ -29,6 +30,7 @@ namespace vkinit { VkImageSubresourceRange imageSubresourceRange( VkImageAspectFlags aspectMask ); + VkSemaphoreSubmitInfo semaphoreSubmitInfo( VkPipelineStageFlags2 stageMask, VkSemaphore semaphore @@ -43,6 +45,19 @@ namespace vkinit { VkSemaphoreSubmitInfo* signalSemaphoreInfo, VkSemaphoreSubmitInfo* waitSemaphoreInfo ); + + VkImageCreateInfo imageCreateInfo( + VkFormat format, + VkImageUsageFlags usageFlags, + VkExtent3D extent + ); + + VkImageViewCreateInfo imageViewCreateInfo( + VkFormat format, + VkImage image, + VkImageAspectFlags aspectFlags + ); + } @@ -53,6 +68,9 @@ namespace vkinit { #define INITIALIZERS_IMPL_H // Implementation namespace vkinit { + // Todo : + // Switch structs to designated initializers + VkCommandPoolCreateInfo commandPoolCreateInfo( uint32_t queueFamilyIndex, VkCommandPoolCreateFlags flags @@ -159,7 +177,7 @@ namespace vkinit { VkSemaphoreSubmitInfo* waitSemaphoreInfo ) { VkSubmitInfo2 info = {}; - info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2; info.pNext = nullptr; info.waitSemaphoreInfoCount = waitSemaphoreInfo == nullptr ? 0 : 1; @@ -173,6 +191,58 @@ namespace vkinit { return info; } + + VkImageCreateInfo imageCreateInfo( + VkFormat format, + VkImageUsageFlags usageFlags, + VkExtent3D extent + ) { + VkImageCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.pNext = nullptr; + + info.imageType = VK_IMAGE_TYPE_2D; + + info.format = format; + info.extent = extent; + + info.mipLevels = 1; + info.arrayLayers = 1; + + //for MSAA. I won't be using it, so only 1 sample per pixel + info.samples = VK_SAMPLE_COUNT_1_BIT; + + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = usageFlags; + + return info; + } + + // Switched to designated initializers, + // because it's more modern, and require less typing + + VkImageViewCreateInfo imageViewCreateInfo( + VkFormat format, + VkImage image, + VkImageAspectFlags aspectFlags + ) { + VkImageViewCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = nullptr, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .subresourceRange = { + .aspectMask = aspectFlags, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; + + return info; + } } #endif #endif diff --git a/src/engine/types.h b/src/engine/types.h index 0a7e9e4..122592c 100644 --- a/src/engine/types.h +++ b/src/engine/types.h @@ -29,6 +29,40 @@ // Custom headers //#include "callbacks.h" + +// Custom types + +struct DeletionQueue { + std::deque> deletors; + + void pushFunction( + std::function&& function + ) { + deletors.push_back(function); + } + + void flush() { + // Reverse iterate the deletion queue to destroy in order + for ( + auto it = deletors.rbegin(); + it != deletors.rend(); + it++ + ) { + (*it)(); + } + deletors.clear(); + } +}; + +struct AllocatedImage { + VkImage image; + VkImageView imageView; + VmaAllocation allocation; + VkExtent3D imageExtent; + VkFormat imageFormat; +}; + + #endif