From 14f307b1b3e0a25a53a76a9f7e4ff3a1581b997f Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Mon, 1 Dec 2025 23:53:16 -0500 Subject: [PATCH] ImGUI is now working in the program. --- meson.build | 13 ++++- vk_engine.cpp | 135 ++++++++++++++++++++++++++++++++++++++++++-- vk_engine.h | 7 +++ vk_initializers.cpp | 37 ++++++++++++ vk_initializers.h | 5 ++ 5 files changed, 190 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index 3baab95..deacd3f 100644 --- a/meson.build +++ b/meson.build @@ -5,7 +5,7 @@ project('hellovulk', 'cpp', default_options: [ 'cpp_std=c++23', 'cpp_args=-D_GLIBCXX_DEBUG=1 -D_GLIBCXX_DEBUG_PEDANTIC=1', - ]) +]) cmake = import('cmake') @@ -17,7 +17,7 @@ cpp_args=[ '-Wno-unused-variable', '-Wno-conversion', '-Wno-missing-field-initializers' - ] +] link_args=[] # these are passed as override_defaults exe_defaults = [ 'warning_level=2' ] @@ -70,7 +70,14 @@ endif vma = subproject('vulkan-memory-allocator').get_variable('vma_allocator_dep') # vulkan_headers = subproject('vulkan-headers').get_variable('vulkan_headers_dep') -imgui = subproject('imgui').get_variable('imgui_dep') + +imgui = subproject('imgui', + default_options: { + 'vulkan': 'enabled', + 'sdl2_renderer': 'enabled' + }, + ).get_variable('imgui_dep') + sdl2 = subproject('sdl2').get_variable('sdl2_dep') diff --git a/vk_engine.cpp b/vk_engine.cpp index 30ba62c..bf61633 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -12,11 +12,14 @@ #include #include +#include "imgui.h" +#include "imgui_impl_sdl2.h" +#include "imgui_impl_vulkan.h" #define VMA_IMPLEMENTATION #include "vk_mem_alloc.h" -constexpr bool bUseValidationLayers = true; +constexpr bool bUseValidationLayers = false; VulkanEngine* loadedEngine = nullptr; @@ -48,6 +51,7 @@ void VulkanEngine::init() init_sync_structures(); init_descriptors(); init_pipelines(); + init_imgui(); //everything went fine _isInitialized = true; @@ -119,6 +123,10 @@ void VulkanEngine::draw() _swapchainImages[swapchainImageIndex], _drawExtent, _swapchainExtent); + vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + draw_imgui(cmd, _swapchainImageViews[swapchainImageIndex]); + vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); VK_CHECK(vkEndCommandBuffer(cmd)); @@ -142,7 +150,7 @@ void VulkanEngine::draw() // this will put the image we just rendered to into the visible window. // we want to wait on the _renderSemaphore for that, // as its necessary that drawing commands have finished before the image is displayed to the user - VkPresentInfoKHR presentInfo = {}; + VkPresentInfoKHR presentInfo{}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.pNext = nullptr; presentInfo.pSwapchains = &_swapchain; @@ -185,6 +193,8 @@ void VulkanEngine::run() stop_rendering = false; } } + + ImGui_ImplSDL2_ProcessEvent(&e); } if(stop_rendering) { @@ -192,6 +202,13 @@ void VulkanEngine::run() continue; } + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + ImGui::ShowDemoWindow(); + ImGui::Render(); + draw(); } } @@ -345,6 +362,16 @@ void VulkanEngine::init_commands() { VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, &_frames[i]._mainCommandBuffer)); } + + VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, &_immCommandPool)); + + VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(_immCommandPool, 1); + + VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, &_immCommandBuffer)); + + _mainDeletionQueue.push_function([=,this]() { + vkDestroyCommandPool(_device, _immCommandPool, nullptr); + }); } void VulkanEngine::init_sync_structures() { @@ -357,6 +384,12 @@ void VulkanEngine::init_sync_structures() { VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, &_frames[i]._swapchainSemaphore)); VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, &_frames[i]._renderSemaphore)); } + + VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &_immFence)); + + _mainDeletionQueue.push_function([=,this]() { + vkDestroyFence(_device, _immFence, nullptr); + }); } void VulkanEngine::draw_background(VkCommandBuffer cmd) @@ -405,14 +438,14 @@ void VulkanEngine::init_descriptors() { drawImageWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; drawImageWrite.pImageInfo = &imgInfo; - vkUpdateDescriptorSets(_device, 1, &drawImageWrite, 0, nullptr); + vkUpdateDescriptorSets(_device, 1, &drawImageWrite, 0, nullptr), //make sure both the descriptor allocator and the new layout get cleaned up properly _mainDeletionQueue.push_function([&]() { globalDescriptorAllocator.destroy_pool(_device); vkDestroyDescriptorSetLayout(_device, _drawImageDescriptorLayout, nullptr); - }); + }); } void VulkanEngine::init_pipelines() @@ -458,3 +491,97 @@ void VulkanEngine::init_background_pipelines() vkDestroyPipeline(_device, _gradientPipeline, nullptr); }); } + + +void VulkanEngine::immediate_submit(std::function&& function) +{ + VK_CHECK(vkResetFences(_device, 1, &_immFence)); + VK_CHECK(vkResetCommandBuffer(_immCommandBuffer, 0)); + + VkCommandBuffer cmd = _immCommandBuffer; + + 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); + + VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, _immFence)); + VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true,9999999999)); + +} + + +void VulkanEngine::init_imgui() +{ +// 1: create descriptor pool for IMGUI + // the size of the pool is very oversize, but it's copied from imgui demo + // itself. + 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)); + + // 2: initialize the imgui library + ImGui::CreateContext(); + + ImGui_ImplSDL2_InitForVulkan(_window); + ImGui_ImplVulkan_InitInfo init_info{}; + init_info.Instance = _instance; + init_info.PhysicalDevice = _chosenGPU; + 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.PipelineRenderingCreateInfo = {.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO}; + init_info.PipelineRenderingCreateInfo.colorAttachmentCount = 1; + init_info.PipelineRenderingCreateInfo.pColorAttachmentFormats = &_swapchainImageFormat; + + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + ImGui_ImplVulkan_Init(&init_info); + ImGui_ImplVulkan_CreateFontsTexture(); + + _mainDeletionQueue.push_function([=,this]() { + ImGui_ImplVulkan_Shutdown(); + vkDestroyDescriptorPool(_device, imguiPool, nullptr); + }); +} + + +void VulkanEngine::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) +{ + VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + VkRenderingInfo renderInfo = vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr); + + vkCmdBeginRendering(cmd, &renderInfo); + + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); + + vkCmdEndRendering(cmd); +} diff --git a/vk_engine.h b/vk_engine.h index d029dcc..712beac 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -36,6 +36,9 @@ constexpr unsigned int FRAME_OVERLAP=2; class VulkanEngine { public: + VkFence _immFence; + VkCommandBuffer _immCommandBuffer; + VkCommandPool _immCommandPool; VkPipeline _gradientPipeline; VkPipelineLayout _gradientPipelineLayout; @@ -96,6 +99,8 @@ public: //run main loop void run(); + void immediate_submit(std::function&& function); + private: void init_vulkan(); void init_swapchain(); @@ -104,8 +109,10 @@ private: 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_background(VkCommandBuffer cmd); + void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView); }; diff --git a/vk_initializers.cpp b/vk_initializers.cpp index 2c0d48c..3c30974 100644 --- a/vk_initializers.cpp +++ b/vk_initializers.cpp @@ -148,3 +148,40 @@ VkImageViewCreateInfo vkinit::imageview_create_info(VkFormat format, VkImage ima return info; } + +VkRenderingAttachmentInfo vkinit::attachment_info( + VkImageView view, VkClearValue* clear ,VkImageLayout layout /*= VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL*/) +{ + VkRenderingAttachmentInfo colorAttachment {}; + colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + colorAttachment.pNext = nullptr; + + colorAttachment.imageView = view; + 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) { + colorAttachment.clearValue = *clear; + } + + return colorAttachment; +} + + +VkRenderingInfo vkinit::rendering_info(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment, + VkRenderingAttachmentInfo* depthAttachment) +{ + VkRenderingInfo renderInfo {}; + renderInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + renderInfo.pNext = nullptr; + + renderInfo.renderArea = VkRect2D { VkOffset2D { 0, 0 }, renderExtent }; + renderInfo.layerCount = 1; + renderInfo.colorAttachmentCount = 1; + renderInfo.pColorAttachments = colorAttachment; + renderInfo.pDepthAttachment = depthAttachment; + renderInfo.pStencilAttachment = nullptr; + + return renderInfo; +} diff --git a/vk_initializers.h b/vk_initializers.h index 3fd01ed..000b378 100644 --- a/vk_initializers.h +++ b/vk_initializers.h @@ -30,5 +30,10 @@ namespace vkinit { VkImageViewCreateInfo imageview_create_info(VkFormat format, VkImage image, VkImageAspectFlags aspectFlags); VkImageCreateInfo image_create_info(VkFormat format, VkImageUsageFlags usageFlags, VkExtent3D extent); + + VkRenderingAttachmentInfo attachment_info( + VkImageView view, VkClearValue* clear ,VkImageLayout layout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + VkRenderingInfo rendering_info(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment, VkRenderingAttachmentInfo* depthAttachment); }