From d3cc173d9715ebf5d0ad565429d76519180f1ad2 Mon Sep 17 00:00:00 2001 From: Alexandre Date: Tue, 5 May 2026 11:44:41 +0200 Subject: [PATCH] Splitted code into folder (see previous commit) --- src/Common/timer.h | 40 ++ src/Common/types.h | 76 ++++ src/Engine/engine/callbacks.h | 25 ++ src/Engine/engine/descriptors.h | 98 +++++ src/Engine/engine/engine.h | 649 +++++++++++++++++++++++++++++++ src/Engine/engine/images.h | 129 ++++++ src/Engine/engine/initializers.h | 247 ++++++++++++ src/Engine/engine/pipelines.h | 13 + src/Engine/main.cpp | 15 + src/Server/main.cpp | 3 + 10 files changed, 1295 insertions(+) create mode 100644 src/Common/timer.h create mode 100644 src/Common/types.h create mode 100644 src/Engine/engine/callbacks.h create mode 100644 src/Engine/engine/descriptors.h create mode 100644 src/Engine/engine/engine.h create mode 100644 src/Engine/engine/images.h create mode 100644 src/Engine/engine/initializers.h create mode 100644 src/Engine/engine/pipelines.h create mode 100644 src/Engine/main.cpp create mode 100644 src/Server/main.cpp diff --git a/src/Common/timer.h b/src/Common/timer.h new file mode 100644 index 0000000..1778d32 --- /dev/null +++ b/src/Common/timer.h @@ -0,0 +1,40 @@ +#ifndef TIMER_H +#define TIMER_H + +// Custom includes +#include + + +class RunTimer { + public: + void startTimer(); + int endTimer(); + + private: + // Aliases + using Clock = std::chrono::steady_clock; + using TimePoint = std::chrono::time_point; + + TimePoint startTime; + TimePoint endTime; +}; + +#endif // End of header + + +#ifdef TIMER_IMPL +#ifndef TIMER_IMPL_H +#define TIMER_IMPL_H +// Implementation + +void RunTimer::startTimer(){ + startTime = Clock::now(); +} + +int RunTimer::endTimer() { + auto elapsed = std::chrono::duration_cast(endTime - startTime); + return elapsed.count(); +} + +#endif +#endif diff --git a/src/Common/types.h b/src/Common/types.h new file mode 100644 index 0000000..038d64f --- /dev/null +++ b/src/Common/types.h @@ -0,0 +1,76 @@ +#ifndef TYPES_H +#define TYPES_H +// Normal header +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define GLFW_INCLUDE_VULKAN +#include + +#include +#include + +// Custom headers +//#include "callbacks.h" +#include "timer.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 + + +#ifdef TYPES_IMPL +#ifndef TYPES_IMPL_H +#define TYPES_IMPL_H +// Implementation + +#endif +#endif diff --git a/src/Engine/engine/callbacks.h b/src/Engine/engine/callbacks.h new file mode 100644 index 0000000..cd861da --- /dev/null +++ b/src/Engine/engine/callbacks.h @@ -0,0 +1,25 @@ +#ifndef CALLBACK_H +#define CALLBACK_H +// Header +// Other includes +#include "Common/types.h" + +class Callback { + public: + static void keyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods); +}; + +#endif + +#ifdef CALLBACK_IMPL +#ifndef CALLBACK_IMPL_H +#define CALLBACK_IMPL_H + +void Callback::keyboardCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { + #ifdef DEBUG + fmt::println("Key pressed, key {}", key); + #endif +} + +#endif +#endif diff --git a/src/Engine/engine/descriptors.h b/src/Engine/engine/descriptors.h new file mode 100644 index 0000000..5ddfa8b --- /dev/null +++ b/src/Engine/engine/descriptors.h @@ -0,0 +1,98 @@ +#ifndef DESCRIPTORS_H +#define DESCRIPTORS_H +// Header +// Other includes +#include "Common/types.h" + + +struct DescriptorLayoutBuilder { + std::vector bindings; + + void addBinding( + uint32_t binding, + VkDescriptorType type + ); + + void clear(); + + VkDescriptorSetLayout build ( + VkDevice device, + VkShaderStageFlags shaderStages, + void* pNext = nullptr, + VkDescriptorSetLayoutCreateFlags flags = 0 + ); +}; + +#endif + +#ifdef DESCRIPTORS_IMPL +#ifndef DESCRIPTORS_IMPL_H +#define DESCRIPTORS_IMPL_H +// The actual implementation + +void DescriptorLayoutBuilder::addBinding( + uint32_t binding, + VkDescriptorType type +){ + #ifdef DEBUG + fmt::println("Adding a descriptor set binding..."); + #endif + VkDescriptorSetLayoutBinding newBind { + .binding = binding, + .descriptorType = type, + .descriptorCount = 1 + }; + + bindings.push_back(newBind); +} + +void DescriptorLayoutBuilder::clear() { + #ifdef DEBUG + fmt::println("Clearing a descriptor set..."); + #endif + + bindings.clear(); + + +} + +VkDescriptorSetLayout DescriptorLayoutBuilder::build ( + VkDevice device, + VkShaderStageFlags shaderStages, + void* pNext = nullptr, + VkDescriptorSetLayoutCreateFlags flags = 0 +) { + #ifdef DEBUG + fmt::println("Building a descriptor set..."); + #endif + + for (auto& b : bindings) { + b.stageFlags |= shaderStages; + } + + VkDescriptorSetLayoutCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = pNext, + + .flags = flags, + .bindingCount = (uint32_t)bindings.size(), + .pBindings = bindings.data(), + }; + + VkDescriptorSet set; + + VK_CHECK( + vkCreateDescriptorSetLayout( + device, + &info, + nullptr, + &set + ) + ); + + return set; + +} + +#endif +#endif diff --git a/src/Engine/engine/engine.h b/src/Engine/engine/engine.h new file mode 100644 index 0000000..a5e4a2c --- /dev/null +++ b/src/Engine/engine/engine.h @@ -0,0 +1,649 @@ +#ifndef ENGINE_H +#define ENGINE_H +// Normal header + +#include "Common/types.h" + + + +struct FrameData { + VkCommandPool _commandPool; + VkCommandBuffer _mainCommandBuffer; + VkSemaphore _swapchainSemaphore; + VkSemaphore _renderSemaphore; + VkFence _renderFence; + DeletionQueue _deletionQueue; +}; + +constexpr uint32_t FRAME_OVERLAP = 2; + +class Engine { + public: + //========== Class Members ========== + bool _isInitialized { false }; + int _frameNumber { 0 }; + VkExtent2D _windowExtent { 1700, 900 }; + + GLFWwindow *window { nullptr }; + + // Vulkan specific class members + VkInstance _instance; + VkDebugUtilsMessengerEXT _debugMessenger; + VkPhysicalDevice _choosenGPU; + VkDevice _device; + VkSurfaceKHR _surface; + + VkSwapchainKHR _swapchain; + VkFormat _swapchainImageFormat; + + std::vector _swapchainImages; + std::vector _swapchainImageViews; + VkExtent2D _swapchainExtent; + + FrameData _frames[FRAME_OVERLAP]; + + FrameData& getCurrentFrame() { return _frames[_frameNumber % FRAME_OVERLAP]; }; + + VkQueue _graphicsQueue; + uint32_t _graphicsQueueFamily; + + // Deletion queue + DeletionQueue _mainDeletionQueue; + VmaAllocator _allocator; + + // Draw resources + AllocatedImage _drawImage; + VkExtent2D _drawExtent; + + + void init(); + + void cleanup(); + + void draw(); + + void run(); + + private: + void initRender(); + void initVulkan(); + void initSwapchain(); + void initCommands(); + void initSyncStructures(); + + void createSwapchain( + uint32_t width, + uint32_t height + ); + void destroySwapchain(); + void drawBackground( + VkCommandBuffer cmd + ); +}; + +#endif + + +#ifdef ENGINE_IMPL +#ifndef ENGINE_IMPL_H +#define ENGINE_IMPL_H +//========== Implementation ========== +// Implementation defines +#define IMAGES_IMPL +#define INITIALIZERS_IMPL +#define CALLBACK_IMPL +#define TIMER_IMPL +#define VMA_IMPLEMENTATION + +// Custom includes +#include "images.h" +#include "initializers.h" +#include "callbacks.h" +#include "Common/timer.h" +#include "Common/types.h" + +#include "VkBootstrap.h" +#include "vk_mem_alloc.h" + + +#ifndef DEBUG +constexpr bool bUseValidationLayers = false; +#else +constexpr bool bUseValidationLayers = true; +#endif + +// Abort when there is an error +// Todo : Give an error message or crash dump + +using namespace std; +#define VK_CHECK(x) \ + do { \ + VkResult err = x; \ + if (err) { \ + fmt::println("Vulkan error : {}", string_VkResult(err)); \ + abort(); \ + } \ + } while (0) \ + +void Engine::init() { + #ifdef DEBUG + fmt::println("Initializing the engine..."); + fmt::println("Current engine version {}", BUILD_ID); + #endif + + glfwInit(); + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + + window = glfwCreateWindow( + static_cast(_windowExtent.width), + static_cast(_windowExtent.height), + "Engine", + nullptr, + nullptr + ); + + glfwSetKeyCallback(window, Callback::keyboardCallback); + + _isInitialized = true; + + initRender(); + + #ifdef DEBUG + fmt::println("Engine Initialized !"); + #endif +} + +void Engine::cleanup() { + #ifdef DEBUG + fmt::println("Cleaning up..."); + #endif + if (_isInitialized) { + // Wait for the GPU to stop working + vkDeviceWaitIdle(_device); + + for (int i = 0; i < FRAME_OVERLAP; i++) { + vkDestroyCommandPool(_device, _frames[i]._commandPool, nullptr); + // Destroy sync objects + 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); + vkDestroyDevice(_device, nullptr); + + vkb::destroy_debug_utils_messenger(_instance, _debugMessenger); + vkDestroyInstance(_instance, nullptr); + + /* + + vkDestroyCommandPool(_device, _commandPool, nullptr); + + + + vkDestroySwapchainKHR(_device, _swapchain, nullptr); + // Change to use dynamic rendering + vkDestroyRenderPass(_device, _renderPass, nullptr); + + // Destroy swapchain ressources + for (uint32_t i = 0; i < _framebuffers.size(); i++) { + vkDestroyFramebuffer(_device, _framebuffers[i], nullptr); + + vkDestroyImageView(_device, _swapchainImageViews[i], nullptr); + } + */ + + // Destroy the window (at the end) + glfwDestroyWindow(window); + glfwTerminate(); + } + + #ifdef DEBUG + fmt::println("Engine cleaned up !"); + #endif +} + +void Engine::draw() { + // wait until the gpu has finished rendering the last frame. Timeout of 1s + VK_CHECK(vkWaitForFences(_device, 1, &getCurrentFrame()._renderFence, true, UINT64_MAX)); + + // Flush frame data + getCurrentFrame()._deletionQueue.flush(); + + // Request image from the swapchain + uint32_t swapchainImageIndex; + VK_CHECK(vkAcquireNextImageKHR(_device, _swapchain, UINT64_MAX, getCurrentFrame()._swapchainSemaphore, nullptr, &swapchainImageIndex)); + + // Reset fences to redo sync + VK_CHECK(vkResetFences(_device, 1, &getCurrentFrame()._renderFence)); + + VkCommandBuffer cmd = getCurrentFrame()._mainCommandBuffer; + + _drawExtent.width = _drawImage.imageExtent.width; + _drawExtent.height = _drawImage.imageExtent.height; + + VK_CHECK(vkResetCommandBuffer(cmd, 0)); + + VkCommandBufferBeginInfo cmdBeginInfo = { + vkinit::commandBufferBeginInfo( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT + ) + }; + + VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); + + vkutil::transitionImage( + cmd, + _drawImage.image, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_GENERAL + ); + + drawBackground(cmd); + + // Transfer the draw image and swapchain image to the correct layouts + vkutil::transitionImage( + cmd, + _drawImage.image, + VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL + ); + + vkutil::transitionImage( + cmd, + _swapchainImages[swapchainImageIndex], + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL + ); + + // Copy image -> swapchain + vkutil::copyImageToImage( + cmd, + _drawImage.image, + _swapchainImages[swapchainImageIndex], + _drawExtent, + _swapchainExtent + ); + + // Uses the present layout to display into the screen + vkutil::transitionImage( + cmd, + _swapchainImages[swapchainImageIndex], + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + ); + + // Locks the buffer, allowing it to be executed + VK_CHECK(vkEndCommandBuffer(cmd)); + + VkCommandBufferSubmitInfo cmdinfo = vkinit::commandBufferSubmitInfo(cmd); + + VkSemaphoreSubmitInfo waitInfo = vkinit::semaphoreSubmitInfo(VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR, getCurrentFrame()._swapchainSemaphore); + VkSemaphoreSubmitInfo signalInfo = vkinit::semaphoreSubmitInfo(VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, getCurrentFrame()._renderSemaphore); + + VkSubmitInfo2 submit = vkinit::submitInfo(&cmdinfo, &signalInfo, &waitInfo); + + VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, getCurrentFrame()._renderFence)); + + VkPresentInfoKHR presentInfo = {}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.pNext = nullptr; + presentInfo.pSwapchains = &_swapchain; + presentInfo.swapchainCount = 1; + + presentInfo.pWaitSemaphores = &getCurrentFrame()._renderSemaphore; + presentInfo.waitSemaphoreCount = 1; + + presentInfo.pImageIndices = &swapchainImageIndex; + + VK_CHECK(vkQueuePresentKHR(_graphicsQueue, &presentInfo)); + + _frameNumber++; +} + +void Engine::run() { + #ifdef DEBUG + fmt::println("Running..."); + RunTimer timer; + timer.startTimer(); + #endif + bool stopRendering = false; + + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + + if (glfwGetWindowAttrib(window, GLFW_ICONIFIED)) { + stopRendering = true; + } else { + stopRendering = false; + } + + if (stopRendering) { + // Minimized, pause + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + draw(); + } + #ifdef DEBUG + fmt::println("Engine has finished running after {} seconds!", timer.endTimer()); + #endif +} + +void Engine::initRender() { + initVulkan(); + initSwapchain(); + //initDefaultRenderpass(); + //initFramebuffers(); + initCommands(); + initSyncStructures(); +} + +void Engine::initVulkan() { + #ifdef DEBUG + fmt::println("Initializing Vulkan..."); + #endif + vkb::InstanceBuilder builder; + + // Create a basic VK instance, with debuging features + auto instRet = builder.set_app_name("Engine") + .request_validation_layers(bUseValidationLayers) + .use_default_debug_messenger() + .require_api_version(1, 3, 0) + .build(); + + vkb::Instance vkbInst = instRet.value(); + + // Grab the instance + _instance = vkbInst.instance; + _debugMessenger = vkbInst.debug_messenger; + + // Create the surface + if (glfwCreateWindowSurface(_instance, window, nullptr, & _surface) != VK_SUCCESS) { + throw std::runtime_error("Failed to create window surface !\nAborting..."); + } + + // Features + // Vulkan 1.3 + VkPhysicalDeviceVulkan13Features features13 { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + .synchronization2 = true, + .dynamicRendering = true + }; + + VkPhysicalDeviceVulkan12Features features12 { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .descriptorIndexing = true, + .bufferDeviceAddress = true + }; + + // Use vkBootstrap to select the GPU + vkb::PhysicalDeviceSelector selector { vkbInst }; + vkb::PhysicalDevice physicalDevice = selector + .set_minimum_version(1, 3) + .set_required_features_13(features13) + .set_required_features_12(features12) + .set_surface(_surface) + .select() + .value(); + + vkb::DeviceBuilder deviceBuilder { physicalDevice }; + #ifdef DEBUG + fmt::println("Device {}", physicalDevice.name); + #endif + vkb::Device vkbDevice { deviceBuilder.build().value() }; + + _device = { vkbDevice.device }; + _choosenGPU = { physicalDevice.physical_device }; + + _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 +} + +void Engine::initSwapchain() { + #ifdef DEBUG + fmt::println("Initializing the swapchain..."); + #endif + + createSwapchain(_windowExtent.width, _windowExtent.height); + + VkExtent3D drawImageExtent = { + _windowExtent.width, + _windowExtent.height, + 1 + }; + + // Hardcoding draw format to a 32 bit float + + _drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + _drawImage.imageExtent = drawImageExtent; + + VkImageUsageFlags drawImageUsages { + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + }; + + VkImageCreateInfo rImgInfo = { + vkinit::imageCreateInfo( + _drawImage.imageFormat, + drawImageUsages, + drawImageExtent + ) + }; + + // Use the GPU local memory for the draw image + VmaAllocationCreateInfo rImgAllocInfo = { + .usage = VMA_MEMORY_USAGE_GPU_ONLY, + .requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + }; + + vmaCreateImage( + _allocator, + &rImgInfo, + &rImgAllocInfo, + &_drawImage.image, + &_drawImage.allocation, + nullptr + ); + + VkImageViewCreateInfo rViewInfo = { + vkinit::imageViewCreateInfo( + _drawImage.imageFormat, + _drawImage.image, + VK_IMAGE_ASPECT_COLOR_BIT + ) + }; + + VK_CHECK( + vkCreateImageView( + _device, + &rViewInfo, + nullptr, + &_drawImage.imageView + ) + ); + + // Add to deletion queues + _mainDeletionQueue.pushFunction([this]() { + vkDestroyImageView( + _device, + _drawImage.imageView, + nullptr + ); + vmaDestroyImage( + _allocator, + _drawImage.image, + _drawImage.allocation + ); + }); + + #ifdef DEBUG + fmt::println("Swapchain initialized !"); + #endif +} + +void Engine::initCommands() { + #ifdef DEBUG + fmt::println("Initializing vulkan commands.."); + #endif + + VkCommandPoolCreateInfo commandPoolInfo = { + vkinit::commandPoolCreateInfo( + _graphicsQueueFamily, + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT + ) + }; + + for ( uint32_t i = 0; i < FRAME_OVERLAP; i++ ) { + VK_CHECK( + vkCreateCommandPool( + _device, + &commandPoolInfo, + nullptr, + &_frames[i]._commandPool + ) + ); + + VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::commandBufferAllocateInfo(_frames[i]._commandPool, 1); + + VK_CHECK( + vkAllocateCommandBuffers( + _device, + &cmdAllocInfo, + &_frames[i]._mainCommandBuffer + ) + ); + } + #ifdef DEBUG + fmt::println("Vulan commands initialized !"); + #endif +} + +void Engine::initSyncStructures() { + #ifdef DEBUG + fmt::println("Creating sync structures..."); + #endif + + VkFenceCreateInfo fenceCreateInfo = vkinit::fenceCreateInfo(VK_FENCE_CREATE_SIGNALED_BIT); + VkSemaphoreCreateInfo semaphoreCreateInfo = vkinit::semaphoreCreateInfo(); + + for (int i = 0; i < FRAME_OVERLAP; i++) { + VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &_frames[i]._renderFence)); + + VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, &_frames[i]._swapchainSemaphore)); + VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, &_frames[i]._renderSemaphore)); + } + + #ifdef DEBUG + fmt::println("Sync structures created !"); + #endif +} + +void Engine::createSwapchain( + uint32_t width, + uint32_t height +) { + #ifdef DEBUG + fmt::println("Creating the Swapchain..."); + #endif + vkb::SwapchainBuilder swapchainBuilder { + _choosenGPU, + _device, + _surface + }; + + _swapchainImageFormat = VK_FORMAT_B8G8R8A8_UNORM; + + vkb::Swapchain vkbSwapchain = swapchainBuilder + .set_desired_format( + VkSurfaceFormatKHR { + .format = _swapchainImageFormat, + .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + } + ) + .set_desired_present_mode(VK_PRESENT_MODE_FIFO_KHR) // VSync + .set_desired_extent(width, height) + .add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT) + .build() + .value(); + + _swapchainExtent = { vkbSwapchain.extent }; + // Store swapchain and it's related images + _swapchain = { vkbSwapchain.swapchain }; + _swapchainImages = { vkbSwapchain.get_images().value() }; + _swapchainImageViews = { vkbSwapchain.get_image_views().value() }; + + #ifdef DEBUG + fmt::println("Swapchain created with dimentions {} by {}", vkbSwapchain.extent.width, vkbSwapchain.extent.height); + #endif +} + +void Engine::destroySwapchain() { + #ifdef DEBUG + fmt::println("Destroying the swapchain..."); + #endif + vkDestroySwapchainKHR(_device, _swapchain, nullptr); + + for (uint32_t i = 0; i < _swapchainImageViews.size(); i++) { + vkDestroyImageView(_device, _swapchainImageViews[i], nullptr); + } + + #ifdef DEBUG + fmt::println("Swapchain destroyed !"); + #endif +} + +void Engine::drawBackground( + VkCommandBuffer cmd +) { + VkClearColorValue clearValue; + + float flash = { std::abs(std::sin(_frameNumber / 120.0f)) }; + + clearValue = { { 0.0f, 0.0f, flash, 1.0f } }; + + VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT); + + // Clear Image + vkCmdClearColorImage( + cmd, + _drawImage.image, + VK_IMAGE_LAYOUT_GENERAL, + &clearValue, + 1, + &clearRange + ); +} + + +#endif +#endif diff --git a/src/Engine/engine/images.h b/src/Engine/engine/images.h new file mode 100644 index 0000000..39ebcfc --- /dev/null +++ b/src/Engine/engine/images.h @@ -0,0 +1,129 @@ +#ifndef IMAGES_H +#define IMAGES_H +// Normal header +#include "Common/types.h" + +namespace vkutil { + void transitionImage( + VkCommandBuffer cmd, + VkImage image, + VkImageLayout currentLayout, + VkImageLayout newLayout + ); + + void copyImageToImage( + VkCommandBuffer cmd, + VkImage source, + VkImage destination, + VkExtent2D srcSize, + VkExtent2D dstSize + ); +} + +#endif + +#ifdef IMAGES_IMPL +#ifndef IMAGES_IMPL_H +#define IMAGES_IMPL_H +// Implementation +#include "initializers.h" + +namespace vkutil { + // Todo : + // Switch structs to designated initializers + void transitionImage( + VkCommandBuffer cmd, + VkImage image, + VkImageLayout currentLayout, + VkImageLayout newLayout + ) { + VkImageMemoryBarrier2 imageBarrier {.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2}; + imageBarrier.pNext = nullptr; + + imageBarrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; + imageBarrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; + imageBarrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; + imageBarrier.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT; + + imageBarrier.oldLayout = currentLayout; + imageBarrier.newLayout = newLayout; + + VkImageAspectFlags aspectMask = (newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; + imageBarrier.subresourceRange = vkinit::imageSubresourceRange(aspectMask); + imageBarrier.image = image; + + VkDependencyInfo depInfo {}; + depInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + depInfo.pNext = nullptr; + + depInfo.imageMemoryBarrierCount = 1; + depInfo.pImageMemoryBarriers = &imageBarrier; + + vkCmdPipelineBarrier2(cmd, &depInfo); + } + + + void copyImageToImage( + VkCommandBuffer cmd, + VkImage source, + VkImage destination, + VkExtent2D srcSize, + VkExtent2D dstSize + ) { + VkImageBlit2 blitRegion { + .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, + .pNext = nullptr, + .srcSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + + .srcOffsets = { + VkOffset3D { 0, 0, 0 }, + VkOffset3D { + (int32_t)srcSize.width, + (int32_t)srcSize.height, + 1 + } + }, + + .dstSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1 + }, + + .dstOffsets = { + VkOffset3D { 0, 0, 0 }, + VkOffset3D { + (int32_t)dstSize.width, + (int32_t)dstSize.height, + 1 + } + }, + }; + + VkBlitImageInfo2 blitInfo = { + .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, + .pNext = nullptr, + + .srcImage = source, + .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + + .dstImage = destination, + .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + + .regionCount = 1, + .pRegions = &blitRegion, + .filter = VK_FILTER_LINEAR, + }; + + vkCmdBlitImage2(cmd, &blitInfo); + } +} + +#endif +#endif diff --git a/src/Engine/engine/initializers.h b/src/Engine/engine/initializers.h new file mode 100644 index 0000000..7aa37e7 --- /dev/null +++ b/src/Engine/engine/initializers.h @@ -0,0 +1,247 @@ +#ifndef INITIALIZERS_H +#define INITIALIZERS_H +// Normal header +#include "Common/types.h" + +namespace vkinit { + VkCommandPoolCreateInfo commandPoolCreateInfo( + uint32_t queueFamilyIndex, + VkCommandPoolCreateFlags flags = 0 + ); + + VkCommandBufferAllocateInfo commandBufferAllocateInfo( + VkCommandPool pool, + uint32_t count + ); + + VkFenceCreateInfo fenceCreateInfo( + VkFenceCreateFlags flags = 0 + ); + + VkSemaphoreCreateInfo semaphoreCreateInfo ( + VkSemaphoreCreateFlags flags = 0 + ); + + VkCommandBufferBeginInfo commandBufferBeginInfo ( + VkCommandBufferUsageFlags flags = 0 + ); + + VkImageSubresourceRange imageSubresourceRange( + VkImageAspectFlags aspectMask + ); + + VkSemaphoreSubmitInfo semaphoreSubmitInfo( + VkPipelineStageFlags2 stageMask, + VkSemaphore semaphore + ); + + VkCommandBufferSubmitInfo commandBufferSubmitInfo ( + VkCommandBuffer cmd + ); + + VkSubmitInfo2 submitInfo( + VkCommandBufferSubmitInfo* cmd, + VkSemaphoreSubmitInfo* signalSemaphoreInfo, + VkSemaphoreSubmitInfo* waitSemaphoreInfo + ); + + VkImageCreateInfo imageCreateInfo( + VkFormat format, + VkImageUsageFlags usageFlags, + VkExtent3D extent + ); + + VkImageViewCreateInfo imageViewCreateInfo( + VkFormat format, + VkImage image, + VkImageAspectFlags aspectFlags + ); + +} + + +#endif + +#ifdef INITIALIZERS_IMPL +#ifndef INITIALIZERS_IMPL_H +#define INITIALIZERS_IMPL_H +// Implementation +namespace vkinit { + // Todo : + // Switch structs to designated initializers + + VkCommandPoolCreateInfo commandPoolCreateInfo( + uint32_t queueFamilyIndex, + VkCommandPoolCreateFlags flags + ) { + VkCommandPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.pNext = nullptr; + info.queueFamilyIndex = queueFamilyIndex; + info.flags = flags; + return info; + } + + VkCommandBufferAllocateInfo commandBufferAllocateInfo( + VkCommandPool pool, + uint32_t count + ) { + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.pNext = nullptr; + info.commandPool = pool; + info.commandBufferCount = count; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + return info; + } + + VkFenceCreateInfo fenceCreateInfo( + VkFenceCreateFlags flags /*= 0 */ + ) { + VkFenceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info.pNext = nullptr; + + info.flags = flags; + + return info; + } + + VkSemaphoreCreateInfo semaphoreCreateInfo ( + VkSemaphoreCreateFlags flags /*= 0*/ + ) { + VkSemaphoreCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + info.pNext = nullptr; + info.flags = flags; + + return info; + } + + VkCommandBufferBeginInfo commandBufferBeginInfo ( + VkCommandBufferUsageFlags flags /*= 0*/ + ) { + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.pNext = nullptr; + + info.pInheritanceInfo = nullptr; + info.flags = flags; + return info; + } + + VkImageSubresourceRange imageSubresourceRange( + VkImageAspectFlags aspectMask + ) { + VkImageSubresourceRange subImage {}; + subImage.aspectMask = aspectMask; + subImage.baseMipLevel = 0; + subImage.levelCount = VK_REMAINING_MIP_LEVELS; + subImage.baseArrayLayer = 0; + subImage.layerCount = VK_REMAINING_ARRAY_LAYERS; + + return subImage; + } + + VkSemaphoreSubmitInfo semaphoreSubmitInfo( + VkPipelineStageFlags2 stageMask, + VkSemaphore semaphore + ) { + VkSemaphoreSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; + submitInfo.pNext = nullptr; + submitInfo.semaphore = semaphore; + submitInfo.stageMask = stageMask; + submitInfo.deviceIndex = 0; + submitInfo.value = 1; + + return submitInfo; + } + + VkCommandBufferSubmitInfo commandBufferSubmitInfo ( + VkCommandBuffer cmd + ) { + VkCommandBufferSubmitInfo info {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO; + info.pNext = nullptr; + info.commandBuffer = cmd; + info.deviceMask = 0; + + return info; + } + + VkSubmitInfo2 submitInfo( + VkCommandBufferSubmitInfo* cmd, + VkSemaphoreSubmitInfo* signalSemaphoreInfo, + VkSemaphoreSubmitInfo* waitSemaphoreInfo + ) { + VkSubmitInfo2 info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2; + info.pNext = nullptr; + + info.waitSemaphoreInfoCount = waitSemaphoreInfo == nullptr ? 0 : 1; + info.pWaitSemaphoreInfos = waitSemaphoreInfo; + + info.signalSemaphoreInfoCount = signalSemaphoreInfo == nullptr ? 0 : 1; + info.pSignalSemaphoreInfos = signalSemaphoreInfo; + + info.commandBufferInfoCount = 1; + info.pCommandBufferInfos = cmd; + + 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/engine/pipelines.h b/src/Engine/engine/pipelines.h new file mode 100644 index 0000000..d662f5d --- /dev/null +++ b/src/Engine/engine/pipelines.h @@ -0,0 +1,13 @@ +#ifndef PIPELINES_H +#define PIPELINES_H +// Normal header + +#endif + +#ifdef PIPELINES_IMPL +#ifndef PIPELINES_IMPL_H +#define PIPELINES_IMPL_H +// Implementation + +#endif +#endif diff --git a/src/Engine/main.cpp b/src/Engine/main.cpp new file mode 100644 index 0000000..544c0b6 --- /dev/null +++ b/src/Engine/main.cpp @@ -0,0 +1,15 @@ +#define ENGINE_IMPL +#include "engine/engine.h" + + +int main(int argc, char* argv[]) { + Engine engine; + + engine.init(); + + engine.run(); + + engine.cleanup(); + + return 0; +} diff --git a/src/Server/main.cpp b/src/Server/main.cpp new file mode 100644 index 0000000..4cce7f6 --- /dev/null +++ b/src/Server/main.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; +}