Browse Source

Added Compute Pipeline & ImGUI

Jef Belmans 2 years ago
parent
commit
aee71e1144
10 changed files with 641 additions and 54 deletions
  1. 83 1
      src/vk_descriptors.cpp
  2. 26 0
      src/vk_descriptors.h
  3. 353 41
      src/vk_engine.cpp
  4. 32 10
      src/vk_engine.h
  5. 34 0
      src/vk_images.cpp
  6. 3 0
      src/vk_images.h
  7. 2 1
      src/vk_initializers.cpp
  8. 53 0
      src/vk_pipelines.cpp
  9. 3 1
      src/vk_pipelines.h
  10. 52 0
      src/vk_types.h

+ 83 - 1
src/vk_descriptors.cpp

@@ -1 +1,83 @@
-#include <vk_descriptors.h>
+#include <vk_descriptors.h>
+
+DescriptorLayoutBuilder& DescriptorLayoutBuilder::add_binding(uint32_t binding, VkDescriptorType type)
+{
+    VkDescriptorSetLayoutBinding bind {};
+    bind.binding = binding;
+    bind.descriptorCount = 1;
+    bind.descriptorType = type;
+
+    bindings.push_back(bind);
+
+    return *this;
+}
+
+void DescriptorLayoutBuilder::clear()
+{
+    bindings.clear();
+}
+
+VkDescriptorSetLayout DescriptorLayoutBuilder::build(VkDevice device, VkShaderStageFlags shaderStages)
+{
+    for (auto& b : bindings)
+    {
+        b.stageFlags |= shaderStages;
+    }
+
+    VkDescriptorSetLayoutCreateInfo info = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
+    info.pNext = nullptr;
+
+    info.pBindings = bindings.data();
+    info.bindingCount = (uint32_t)bindings.size();
+    info.flags = 0;
+
+    VkDescriptorSetLayout set;
+    VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &set));
+
+    return set;
+}
+
+void DescriptorAllocator::init_pool(VkDevice device, uint32_t maxSets, std::span<PoolSizeRatio> poolRatios)
+{
+    std::vector<VkDescriptorPoolSize> poolSizes;
+    for (PoolSizeRatio ratio : poolRatios)
+    {
+        poolSizes.push_back(VkDescriptorPoolSize
+        {
+                .type = ratio.type,
+                .descriptorCount = uint32_t(ratio.ratio * maxSets)
+        });
+    }
+
+    VkDescriptorPoolCreateInfo pool_info = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
+    pool_info.flags = 0;
+    pool_info.maxSets = maxSets;
+    pool_info.poolSizeCount = (uint32_t)poolSizes.size();
+    pool_info.pPoolSizes = poolSizes.data();
+
+    vkCreateDescriptorPool(device, &pool_info, nullptr, &pool);
+}
+
+void DescriptorAllocator::clear_descriptors(VkDevice device)
+{
+    vkResetDescriptorPool(device, pool, 0);
+}
+
+void DescriptorAllocator::destroy_pool(VkDevice device)
+{
+    vkDestroyDescriptorPool(device,pool,nullptr);
+}
+
+VkDescriptorSet DescriptorAllocator::allocate(VkDevice device, VkDescriptorSetLayout layout)
+{
+    VkDescriptorSetAllocateInfo allocInfo = {.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
+    allocInfo.pNext = nullptr;
+    allocInfo.descriptorPool = pool;
+    allocInfo.descriptorSetCount = 1;
+    allocInfo.pSetLayouts = &layout;
+
+    VkDescriptorSet ds;
+    VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &ds));
+
+    return ds;
+}

+ 26 - 0
src/vk_descriptors.h

@@ -1,3 +1,29 @@
 #pragma once
 
 #include <vk_types.h>
+struct DescriptorLayoutBuilder
+{
+    std::vector<VkDescriptorSetLayoutBinding> bindings;
+
+    DescriptorLayoutBuilder& add_binding(uint32_t binding, VkDescriptorType type);
+    VkDescriptorSetLayout build(VkDevice device, VkShaderStageFlags shaderStages);
+    void clear();
+};
+
+struct DescriptorAllocator
+{
+
+    struct PoolSizeRatio
+    {
+        VkDescriptorType type;
+        float ratio;
+    };
+
+    VkDescriptorPool pool;
+
+    void init_pool(VkDevice device, uint32_t maxSets, std::span<PoolSizeRatio> poolRatios);
+    void clear_descriptors(VkDevice device);
+    void destroy_pool(VkDevice device);
+
+    VkDescriptorSet allocate(VkDevice device, VkDescriptorSetLayout layout);
+};

+ 353 - 41
src/vk_engine.cpp

@@ -4,10 +4,17 @@
 #include <SDL_vulkan.h>
 
 #include <vk_initializers.h>
-#include <vk_types.h>
+#include <vk_pipelines.h>
 #include <vk_images.h>
 
-#include "VkBootstrap.h"
+#define VMA_IMPLEMENTATION
+#include <vk_mem_alloc.h>
+#include <VkBootstrap.h>
+
+// IMGUI
+#include <imgui.h>
+#include <imgui_impl_sdl2.h>
+#include <imgui_impl_vulkan.h>
 
 #include <chrono>
 #include <thread>
@@ -40,6 +47,10 @@ void VulkanEngine::init()
         _windowExtent.height,
         window_flags);
 
+    _mainDeletionQueue.push([&](){
+        SDL_DestroyWindow(_window);
+    });
+
     init_vulkan();
 
     init_swapchain();
@@ -48,6 +59,12 @@ void VulkanEngine::init()
 
     init_sync_structures();
 
+    init_descriptors();
+
+    init_pipelines();
+
+    init_imgui();
+
     // everything went fine
     _isInitialized = true;
 }
@@ -62,22 +79,10 @@ void VulkanEngine::cleanup()
         // Cleanup frame data
         for (auto & frame : _frames)
         {
-            vkDestroyCommandPool(_device, frame._commandPool, nullptr);
-
-            // Sync structures
-            vkDestroyFence(_device, frame._renderFence, nullptr);
-            vkDestroySemaphore(_device, frame._renderSemaphore, nullptr);
-            vkDestroySemaphore(_device, frame._swapchainSemaphore, nullptr);
+            frame._deletionQueue.flush();
         }
 
-        destroy_swapchain();
-
-        vkDestroySurfaceKHR(_instance, _surface, nullptr);
-        vkDestroyDevice(_device, nullptr);
-
-        vkb::destroy_debug_utils_messenger(_instance, _debugMessenger);
-        vkDestroyInstance(_instance, nullptr);
-        SDL_DestroyWindow(_window);
+        _mainDeletionQueue.flush();
     }
 
     loadedEngine = nullptr;
@@ -89,6 +94,8 @@ void VulkanEngine::draw()
     VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000));
     VK_CHECK(vkResetFences(_device, 1, &get_current_frame()._renderFence));
 
+    get_current_frame()._deletionQueue.flush();
+
     // Request swapchain image
     uint32_t swapchainImageIndex;
     VK_CHECK(vkAcquireNextImageKHR(_device, _swapchain, 1000000000, get_current_frame()._swapchainSemaphore, nullptr,
@@ -103,37 +110,44 @@ void VulkanEngine::draw()
     // Command buffer will be used exactly once
     VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
 
-    // Start recording
+    _drawExtent.width = _drawImage.imageExtent.width;
+    _drawExtent.height = _drawImage.imageExtent.height;
+
+    // Begin recording
     VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
 
-    // Transition swapchain image to writable
-    vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex],  VK_IMAGE_LAYOUT_UNDEFINED,
-                             VK_IMAGE_LAYOUT_GENERAL);
+    // Transition draw image to general layout for writing into it
+    vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
 
-    // Flash clear color every 120 frames
-    VkClearColorValue clearValue;
-    float flash = abs(sin(_frameNumber / 120.f));
-    clearValue = { { 0.0f, 0.0f, flash, 1.0f } };
+    draw_clear_color(cmd);
 
-    VkImageSubresourceRange clearRange = vkinit::image_subresource_range(VK_IMAGE_ASPECT_COLOR_BIT);
+    // Transition draw image and swapchain image to correct layout for transferring
+    vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+    vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
 
-    // Clear image
-    vkCmdClearColorImage(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_GENERAL, &clearValue, 1, &clearRange);
+    // Copy draw image to swapchain image
+    vkutil::copy_image_to_image(cmd, _drawImage.image, _swapchainImages[swapchainImageIndex], _drawExtent, _swapchainExtent);
 
-    // Transition swapchain image to presentable
-    vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex],VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+    // Set swapchain image to Attachment Optimal, so we can directly draw to it
+    vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
 
-    // Finalize command buffer for execution
+    // Draw IMGUI into swapchain image
+    draw_imgui(cmd, _swapchainImageViews[swapchainImageIndex]);
+
+    // Transition swapchain image to presentable layout
+    vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
+
+    // End recording to command buffer
     VK_CHECK(vkEndCommandBuffer(cmd));
 
     // Wait for swapchain to be ready (_presentSemaphore)
-    // Signal when renderign has finished (_renderSemaphore)
-    VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd);
+    // Signal when rendering has finished (_renderSemaphore)
+    VkCommandBufferSubmitInfo cmdInfo = vkinit::command_buffer_submit_info(cmd);
 
     VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR,get_current_frame()._swapchainSemaphore);
     VkSemaphoreSubmitInfo signalInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, get_current_frame()._renderSemaphore);
 
-    VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo,&signalInfo,&waitInfo);
+    VkSubmitInfo2 submit = vkinit::submit_info(&cmdInfo, &signalInfo, &waitInfo);
 
     // Submit to queue and execute
     // Block until queue finishes execution (_renderFence)
@@ -180,6 +194,8 @@ void VulkanEngine::run()
                     _stopRendering = false;
                 }
             }
+
+            ImGui_ImplSDL2_ProcessEvent(&e);
         }
 
         // do not draw if we are minimized
@@ -189,6 +205,17 @@ void VulkanEngine::run()
             continue;
         }
 
+        // New IMGUI frame
+        ImGui_ImplVulkan_NewFrame();
+        ImGui_ImplSDL2_NewFrame(_window);
+        ImGui::NewFrame();
+
+        // Debug
+        ImGui::ShowDemoWindow();
+
+        // Render IMGUI
+        ImGui::Render();
+
         draw();
     }
 }
@@ -243,28 +270,101 @@ void VulkanEngine::init_vulkan()
 
     _graphicsQueue = vkbDevice.get_queue(vkb::QueueType::graphics).value();
     _graphicsQueueFamily = vkbDevice.get_queue_index(vkb::QueueType::graphics).value();
+
+    // Initialize VMA
+    VmaAllocatorCreateInfo allocInfo{};
+    allocInfo.physicalDevice = _physicalGPU;
+    allocInfo.device = _device;
+    allocInfo.instance = _instance;
+    allocInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
+    vmaCreateAllocator(&allocInfo, &_allocator);
+
+    _mainDeletionQueue.push([&](){
+        vmaDestroyAllocator(_allocator);
+        vkDestroySurfaceKHR(_instance, _surface, nullptr);
+
+        vkDestroyDevice(_device, nullptr);
+        vkb::destroy_debug_utils_messenger(_instance, _debugMessenger);
+        vkDestroyInstance(_instance, nullptr);
+    });
 }
 
 void VulkanEngine::init_swapchain()
 {
     create_swapchain(_windowExtent.width, _windowExtent.height);
+
+    // Create draw image
+    _drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
+    _drawImage.imageExtent = {
+            _windowExtent.width,
+            _windowExtent.height,
+            1
+    };;
+
+    VkImageUsageFlags drawImageUsages{};
+    drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+    drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT;
+    drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+
+    VkImageCreateInfo imgCI = vkinit::image_create_info(_drawImage.imageFormat, drawImageUsages,
+                                                          _drawImage.imageExtent);
+
+    // Allocate the draw image from device (GPU) local memory
+    VmaAllocationCreateInfo imgAllocCI{};
+    imgAllocCI.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+    imgAllocCI.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+
+    // Allocate image
+    vmaCreateImage(_allocator, &imgCI, &imgAllocCI, &_drawImage.image, &_drawImage.allocation, nullptr);
+
+    // Build image view for draw image
+    VkImageViewCreateInfo imgViewCI = vkinit::imageview_create_info(_drawImage.imageFormat, _drawImage.image,
+                                                                    VK_IMAGE_ASPECT_COLOR_BIT);
+
+    VK_CHECK(vkCreateImageView(_device, &imgViewCI, nullptr, &_drawImage.imageView));
+
+    _mainDeletionQueue.push([=, this](){
+        vkDestroyImageView(_device, _drawImage.imageView, nullptr);
+        vmaDestroyImage(_allocator, _drawImage.image, _drawImage.allocation);
+    });
 }
 
 void VulkanEngine::init_commands()
 {
     // Create command pool for commands submitted to the graphics queue
     // and allow resetting individual command buffers;
-    VkCommandPoolCreateInfo commandPoolInfo =  vkinit::command_pool_create_info(_graphicsQueueFamily,
-                                                                                VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
+    VkCommandPoolCreateInfo commandPoolInfo
+    {
+        vkinit::command_pool_create_info(_graphicsQueueFamily,
+                                         VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT)
+    };
 
-    for (auto & frame : _frames)
+    // Initialize immediate buffer
+    VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, &_immediateBuffer._commandPool));
+
+    VkCommandBufferAllocateInfo immCmdAllocInfo
+    {
+        vkinit::command_buffer_allocate_info(_immediateBuffer._commandPool,1)
+    };
+    VK_CHECK(vkAllocateCommandBuffers(_device, &immCmdAllocInfo, &_immediateBuffer._commandBuffer));
+
+    _mainDeletionQueue.push([=, this]() {
+        vkDestroyCommandPool(_device, _immediateBuffer._commandPool, nullptr);
+    });
+
+        for (auto & frame : _frames)
     {
         VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, &frame._commandPool));
 
         // Allocate default command buffer used for rendering
-        VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(frame._commandPool, 1);
+        VkCommandBufferAllocateInfo cmdAllocInfo {vkinit::command_buffer_allocate_info(frame._commandPool, 1)};
 
         VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, &frame._mainCommandBuffer));
+
+        _mainDeletionQueue.push([=, this](){
+            vkDestroyCommandPool(_device, frame._commandPool, nullptr);
+        });
     }
 }
 
@@ -276,12 +376,25 @@ void VulkanEngine::init_sync_structures()
     VkFenceCreateInfo fenceCreateInfo = vkinit::fence_create_info(VK_FENCE_CREATE_SIGNALED_BIT);
     VkSemaphoreCreateInfo semaphoreCreateInfo = vkinit::semaphore_create_info();
 
+    VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &_immediateBuffer._fence));
+
+    _mainDeletionQueue.push([=, this](){
+        vkDestroyFence(_device, _immediateBuffer._fence, nullptr);
+    });
+
     for (auto & frame : _frames)
     {
         VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &frame._renderFence));
 
         VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, &frame._swapchainSemaphore));
         VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, &frame._renderSemaphore));
+
+        // Deletion queue
+        _mainDeletionQueue.push([=, this](){
+            vkDestroyFence(_device, frame._renderFence, nullptr);
+            vkDestroySemaphore(_device, frame._renderSemaphore, nullptr);
+            vkDestroySemaphore(_device, frame._swapchainSemaphore, nullptr);
+        });
     }
 }
 
@@ -306,14 +419,213 @@ void VulkanEngine::create_swapchain(uint32_t width, uint32_t height)
     _swapchain = vkbSwapchain.swapchain;
     _swapchainImages = vkbSwapchain.get_images().value();
     _swapchainImageViews = vkbSwapchain.get_image_views().value();
+
+    _mainDeletionQueue.push([=, this](){
+        vkDestroySwapchainKHR(_device, _swapchain, nullptr);
+        for (int i = 0; i < _swapchainImageViews.size(); ++i)
+        {
+            vkDestroyImageView(_device, _swapchainImageViews[i], nullptr);
+        }
+    });
+}
+
+void VulkanEngine::draw_clear_color(VkCommandBuffer cmd)
+{
+    // bind the gradient drawing compute pipeline
+    vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _computePipeline);
+
+    // bind the descriptor set containing the draw image for the compute pipeline
+    vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _computePipelineLayout, 0, 1, &_drawImageDescriptors, 0, nullptr);
+
+    // execute the compute pipeline dispatch. We are using 16x16 workgroup size so we need to divide by it
+    vkCmdDispatch(cmd, std::ceil(_drawExtent.width / 16.0), std::ceil(_drawExtent.height / 16.0), 1);
+}
+
+void VulkanEngine::init_descriptors()
+{
+    std::vector<DescriptorAllocator::PoolSizeRatio> sizes
+            {
+                    {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}
+            };
+
+    _globalDescriptorAllocator.init_pool(_device, 10, sizes);
+
+    {
+        DescriptorLayoutBuilder layoutBuilder;
+        layoutBuilder.add_binding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
+        _drawImageDescriptorLayout = layoutBuilder.build(_device, VK_SHADER_STAGE_COMPUTE_BIT);
+    }
+
+    // Allocate descriptor for draw image
+    _drawImageDescriptors = _globalDescriptorAllocator.allocate(_device, _drawImageDescriptorLayout);
+
+    // The DescImageInfo holds a "pointer" to the resource that will get bound to the descriptor
+    VkDescriptorImageInfo imgInfo
+    {
+        .imageView = _drawImage.imageView,
+        .imageLayout = VK_IMAGE_LAYOUT_GENERAL
+    };
+
+    // A write is used to "write" the resource into the descriptor set
+    VkWriteDescriptorSet drawImageWrite
+    {
+        .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
+        .pNext = nullptr,
+
+        .dstSet = _drawImageDescriptors,
+        .dstBinding = 0,
+        .descriptorCount = 1,
+        .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+        .pImageInfo = &imgInfo
+    };
+
+    vkUpdateDescriptorSets(_device, 1, &drawImageWrite, 0, nullptr);
+
+    _mainDeletionQueue.push([&](){
+       _globalDescriptorAllocator.destroy_pool(_device);
+        vkDestroyDescriptorSetLayout(_device, _drawImageDescriptorLayout, nullptr);
+    });
 }
 
-void VulkanEngine::destroy_swapchain()
+void VulkanEngine::init_pipelines()
 {
-    vkDestroySwapchainKHR(_device, _swapchain, nullptr);
+    init_background_pipelines();
+}
 
-    for (int i = 0; i < _swapchainImageViews.size(); ++i)
+void VulkanEngine::init_background_pipelines()
+{
+    VkPipelineLayoutCreateInfo computeLayout{};
+    computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+    computeLayout.pNext = nullptr;
+    computeLayout.pSetLayouts = &_drawImageDescriptorLayout;
+    computeLayout.setLayoutCount = 1;
+
+    VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_computePipelineLayout));
+
+    VkShaderModule computeDrawShader;
+    if (!vkutil::load_shader_module("../shaders/gradient.comp.spv", _device, &computeDrawShader))
     {
-        vkDestroyImageView(_device, _swapchainImageViews[i], nullptr);
+        fmt::print("Error when building the compute shader \n");
     }
+
+    VkPipelineShaderStageCreateInfo stageinfo{};
+    stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+    stageinfo.pNext = nullptr;
+    stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
+    stageinfo.module = computeDrawShader;
+    stageinfo.pName = "main";
+
+    VkComputePipelineCreateInfo computePipelineCreateInfo{};
+    computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
+    computePipelineCreateInfo.pNext = nullptr;
+    computePipelineCreateInfo.layout = _computePipelineLayout;
+    computePipelineCreateInfo.stage = stageinfo;
+
+    VK_CHECK(vkCreateComputePipelines(_device,VK_NULL_HANDLE,1,&computePipelineCreateInfo, nullptr, &_computePipeline));
+
+    vkDestroyShaderModule(_device, computeDrawShader, nullptr);
+
+    _mainDeletionQueue.push([&]() {
+        vkDestroyPipelineLayout(_device, _computePipelineLayout, nullptr);
+        vkDestroyPipeline(_device, _computePipeline, nullptr);
+    });
+}
+
+void VulkanEngine::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView)
+{
+    VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_GENERAL);
+    VkRenderingInfo renderInfo = vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr);
+
+    vkCmdBeginRendering(cmd, &renderInfo);
+
+    ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd);
+
+    vkCmdEndRendering(cmd);
+}
+
+void VulkanEngine::immediate_submit(std::function<void(VkCommandBuffer)> &&function)
+{
+    VK_CHECK(vkResetFences(_device, 1, &_immediateBuffer._fence));
+    VK_CHECK(vkResetCommandBuffer(_immediateBuffer._commandBuffer, 0));
+
+    VkCommandBuffer cmd = _immediateBuffer._commandBuffer;
+
+    VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
+
+    VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
+
+    function(cmd);
+
+    VK_CHECK(vkEndCommandBuffer(cmd));
+
+    VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd);
+    VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, nullptr, nullptr);
+
+
+    // Submit and execute command buffer
+    VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, _immediateBuffer._fence));
+
+    // _fence will block until graphic commands execution have finished execution
+    VK_CHECK(vkWaitForFences(_device, 1, &_immediateBuffer._fence, true, 9999999999));
 }
+
+void VulkanEngine::init_imgui()
+{
+    // Create descriptor pool used by IMGUI
+    // Pool size is oversize but as provided by imgui demo
+    VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
+                                          { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } };
+
+    VkDescriptorPoolCreateInfo pool_info = {};
+    pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+    pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
+    pool_info.maxSets = 1000;
+    pool_info.poolSizeCount = (uint32_t)std::size(pool_sizes);
+    pool_info.pPoolSizes = pool_sizes;
+
+    VkDescriptorPool imguiPool;
+    VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &imguiPool));
+
+
+    // Initialize IMGUI structures
+    ImGui::CreateContext();
+
+    // Initialize IMGUI for SDL
+    ImGui_ImplSDL2_InitForVulkan(_window);
+
+    // Initialize IMGUI for Vulkan
+    ImGui_ImplVulkan_InitInfo init_info = {};
+    init_info.Instance = _instance;
+    init_info.PhysicalDevice = _physicalGPU;
+    init_info.Device = _device;
+    init_info.Queue = _graphicsQueue;
+    init_info.DescriptorPool = imguiPool;
+    init_info.MinImageCount = 3;
+    init_info.ImageCount = 3;
+    init_info.UseDynamicRendering = true;
+    init_info.ColorAttachmentFormat = _swapchainImageFormat;
+
+    init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
+
+    ImGui_ImplVulkan_Init(&init_info, VK_NULL_HANDLE);
+
+    // Upload font textures to GPU
+    immediate_submit([&](VkCommandBuffer cmd) { ImGui_ImplVulkan_CreateFontsTexture(cmd); });
+
+    // Clear font textures from CPU
+    ImGui_ImplVulkan_DestroyFontUploadObjects();
+
+    _mainDeletionQueue.push([=, this]() {
+        vkDestroyDescriptorPool(_device, imguiPool, nullptr);
+        ImGui_ImplVulkan_Shutdown();
+    });
+}

+ 32 - 10
src/vk_engine.h

@@ -1,15 +1,7 @@
 #pragma once
 #include <vk_types.h>
+#include <vk_descriptors.h>
 
-struct FrameData
-{
-    VkCommandPool _commandPool;
-    VkCommandBuffer _mainCommandBuffer;
-
-    // Sync structures
-    VkSemaphore _swapchainSemaphore, _renderSemaphore;
-    VkFence _renderFence;
-};
 constexpr unsigned int FRAMES_IN_FLIGHT = 2;
 
 class VulkanEngine
@@ -28,18 +20,40 @@ public:
 
 	struct SDL_Window* _window { nullptr };
 
+    // Vulkan Objects
     VkInstance _instance{};
     VkDebugUtilsMessengerEXT _debugMessenger{};
     VkPhysicalDevice _physicalGPU{};
     VkDevice _device{};
     VkSurfaceKHR _surface{};
 
+    // Memory
+    VmaAllocator _allocator;
+
+    // Swapchain
     VkSwapchainKHR _swapchain;
     VkFormat _swapchainImageFormat;
     std::vector<VkImage> _swapchainImages;
     std::vector<VkImageView> _swapchainImageViews;
     VkExtent2D _swapchainExtent;
 
+    AllocatedImage _drawImage;
+    VkExtent2D _drawExtent;
+
+    DeletionQueue _mainDeletionQueue;
+
+    // Descriptors
+    DescriptorAllocator _globalDescriptorAllocator;
+    VkDescriptorSetLayout _drawImageDescriptorLayout;
+    VkDescriptorSet _drawImageDescriptors;
+
+    // Pipelines
+    VkPipeline _computePipeline;
+    VkPipelineLayout _computePipelineLayout;
+
+    // Immediate commands
+    ImmediateBuffer _immediateBuffer;
+
 	static VulkanEngine& Get();
 	void init();
 	void cleanup();
@@ -51,7 +65,15 @@ private:
     void init_swapchain();
     void init_commands();
     void init_sync_structures();
+    void init_descriptors();
+    void init_pipelines();
+    void init_background_pipelines();
+    void init_imgui();
 
     void create_swapchain(uint32_t width, uint32_t height);
-    void destroy_swapchain();
+
+    void draw_clear_color(VkCommandBuffer cmd);
+    void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView);
+    void immediate_submit(std::function<void(VkCommandBuffer cmd)>&& function);
+
 };

+ 34 - 0
src/vk_images.cpp

@@ -26,4 +26,38 @@ void vkutil::transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout
     depInfo.pImageMemoryBarriers = &imageBarrier;
 
     vkCmdPipelineBarrier2(cmd, &depInfo);
+}
+
+void vkutil::copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize)
+{
+    VkImageBlit2 blitRegion{ .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, .pNext = nullptr };
+
+    blitRegion.srcOffsets[1].x = srcSize.width;
+    blitRegion.srcOffsets[1].y = srcSize.height;
+    blitRegion.srcOffsets[1].z = 1;
+
+    blitRegion.dstOffsets[1].x = dstSize.width;
+    blitRegion.dstOffsets[1].y = dstSize.height;
+    blitRegion.dstOffsets[1].z = 1;
+
+    blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    blitRegion.srcSubresource.baseArrayLayer = 0;
+    blitRegion.srcSubresource.layerCount = 1;
+    blitRegion.srcSubresource.mipLevel = 0;
+
+    blitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+    blitRegion.dstSubresource.baseArrayLayer = 0;
+    blitRegion.dstSubresource.layerCount = 1;
+    blitRegion.dstSubresource.mipLevel = 0;
+
+    VkBlitImageInfo2 blitInfo{ .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, .pNext = nullptr };
+    blitInfo.dstImage = destination;
+    blitInfo.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+    blitInfo.srcImage = source;
+    blitInfo.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+    blitInfo.filter = VK_FILTER_LINEAR;
+    blitInfo.regionCount = 1;
+    blitInfo.pRegions = &blitRegion;
+
+    vkCmdBlitImage2(cmd, &blitInfo);
 }

+ 3 - 0
src/vk_images.h

@@ -4,4 +4,7 @@
 namespace vkutil
 {
     void transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout);
+
+    void copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize,
+                             VkExtent2D dstSize);
 };

+ 2 - 1
src/vk_initializers.cpp

@@ -124,7 +124,8 @@ VkRenderingAttachmentInfo vkinit::attachment_info(
     colorAttachment.imageLayout = layout;
     colorAttachment.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
     colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-    if (clear) {
+    if (clear)
+    {
         colorAttachment.clearValue = *clear;
     }
 

+ 53 - 0
src/vk_pipelines.cpp

@@ -1 +1,54 @@
 #include <vk_pipelines.h>
+#include <vk_pipelines.h>
+#include <fstream>
+#include <vk_initializers.h>
+
+bool vkutil::load_shader_module(const char* filePath,
+                                VkDevice device,
+                                VkShaderModule* outShaderModule)
+{
+    // open the file. With cursor at the end
+    std::ifstream file(filePath, std::ios::ate | std::ios::binary);
+
+    if (!file.is_open())
+    {
+        return false;
+    }
+
+    // find what the size of the file is by looking up the location of the cursor
+    // because the cursor is at the end, it gives the size directly in bytes
+    size_t fileSize = (size_t)file.tellg();
+
+    // spirv expects the buffer to be on uint32, so make sure to reserve a int
+    // vector big enough for the entire file
+    std::vector<uint32_t> buffer(fileSize / sizeof(uint32_t));
+
+    // put file cursor at beginning
+    file.seekg(0);
+
+    // load the entire file into the buffer
+    file.read((char*)buffer.data(), fileSize);
+
+    // now that the file is loaded into the buffer, we can close it
+    file.close();
+
+    // create a new shader module, using the buffer we loaded
+    VkShaderModuleCreateInfo createInfo = {};
+    createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+    createInfo.pNext = nullptr;
+
+    // codeSize has to be in bytes, so multply the ints in the buffer by size of
+    // int to know the real size of the buffer
+    createInfo.codeSize = buffer.size() * sizeof(uint32_t);
+    createInfo.pCode = buffer.data();
+
+    // check that the creation goes well.
+    VkShaderModule shaderModule;
+    if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS)
+    {
+        return false;
+    }
+
+    *outShaderModule = shaderModule;
+    return true;
+}

+ 3 - 1
src/vk_pipelines.h

@@ -3,5 +3,7 @@
 
 namespace vkutil
 {
-
+    bool load_shader_module(const char* filePath,
+                                    VkDevice device,
+                                    VkShaderModule* outShaderModule);
 };

+ 52 - 0
src/vk_types.h

@@ -10,6 +10,7 @@
 #include <array>
 #include <functional>
 #include <deque>
+#include <ranges>
 
 #include <vulkan/vulkan.h>
 #include <vulkan/vk_enum_string_helper.h>
@@ -20,7 +21,57 @@
 #include <glm/mat4x4.hpp>
 #include <glm/vec4.hpp>
 
+// Structs
+struct DeletionQueue
+{
+    std::deque<std::function<void()>> deletors;
 
+    void push(std::function<void()>&& function)
+    {
+        deletors.push_back(function);
+    }
+
+    void flush()
+    {
+        // Iterate queue in reverse and call functors
+        for (auto & deletor : std::ranges::reverse_view(deletors))
+        {
+            deletor(); // Call functor
+        }
+
+        deletors.clear();
+    }
+};
+
+struct FrameData
+{
+    VkCommandPool _commandPool;
+    VkCommandBuffer _mainCommandBuffer;
+
+    // Sync structures
+    VkSemaphore _swapchainSemaphore, _renderSemaphore;
+    VkFence _renderFence;
+
+    DeletionQueue _deletionQueue;
+};
+
+struct ImmediateBuffer
+{
+    VkFence _fence;
+    VkCommandPool _commandPool;
+    VkCommandBuffer _commandBuffer;
+};
+
+struct AllocatedImage
+{
+    VkImage image;
+    VkImageView imageView;
+    VmaAllocation allocation;
+    VkExtent3D imageExtent;
+    VkFormat imageFormat;
+};
+
+// Macros
 #define VK_CHECK(x)                                                     \
     do {                                                                \
         VkResult err = x;                                               \
@@ -29,3 +80,4 @@
             abort();                                                    \
         }                                                               \
     } while (0)
+