From 755936eee8c70ffd0b725c77344a1fc3e73c6232 Mon Sep 17 00:00:00 2001 From: Slendi Date: Wed, 3 Dec 2025 02:31:38 +0200 Subject: [PATCH] Oh dear, imgui! Signed-off-by: Slendi --- imgui.ini | 8 ++ meson.build | 36 ++++++++- src/Application.cpp | 14 ++++ src/Application.h | 4 + src/Util.cpp | 60 ++++++++++++++ src/Util.h | 10 +++ src/VulkanRenderer.cpp | 176 +++++++++++++++++++++++++++++++++++++---- src/VulkanRenderer.h | 19 +++-- thirdparty/imgui | 1 + 9 files changed, 305 insertions(+), 23 deletions(-) create mode 100644 imgui.ini create mode 160000 thirdparty/imgui diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..0b4e9c4 --- /dev/null +++ b/imgui.ini @@ -0,0 +1,8 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 + +[Window][Dear ImGui Demo] +Pos=551,12 +Size=550,680 + diff --git a/meson.build b/meson.build index d2599f7..bbeb95c 100644 --- a/meson.build +++ b/meson.build @@ -14,6 +14,14 @@ vulkan_dep = dependency('vulkan') openxr_dep = dependency('openxr') zlib_dep = dependency('zlib') sdl3_dep = dependency('sdl3') +imgui_src = files( + 'thirdparty/imgui/imgui.cpp', + 'thirdparty/imgui/imgui_draw.cpp', + 'thirdparty/imgui/imgui_tables.cpp', + 'thirdparty/imgui/imgui_widgets.cpp', + 'thirdparty/imgui/backends/imgui_impl_vulkan.cpp', + 'thirdparty/imgui/backends/imgui_impl_sdl3.cpp', +) vkbootstrap_dev = get_option('vkbootstrap_dev') vkbootstrap_lib = get_option('vkbootstrap_lib') @@ -38,12 +46,37 @@ add_project_arguments( '-Wno-padded', '-Wno-unsafe-buffer-usage', '-Wno-c23-extensions', + '-Wno-old-style-cast', ], language : 'cpp' ) subdir('shaders') +imgui_src = files( + 'thirdparty/imgui/imgui.cpp', + 'thirdparty/imgui/imgui_draw.cpp', + 'thirdparty/imgui/imgui_tables.cpp', + 'thirdparty/imgui/imgui_widgets.cpp', + 'thirdparty/imgui/imgui_demo.cpp', + 'thirdparty/imgui/backends/imgui_impl_vulkan.cpp', + 'thirdparty/imgui/backends/imgui_impl_sdl3.cpp', +) + +imgui_inc = include_directories('thirdparty/imgui', 'thirdparty/imgui/backends') + +imgui_lib = static_library('imgui', + imgui_src, + include_directories: imgui_inc, + dependencies: [ + vulkan_dep, + sdl3_dep, + ], + cpp_args: [ + '-w', + ], +) + exe = executable('vr-compositor', [ 'src/main.cpp', @@ -55,7 +88,8 @@ exe = executable('vr-compositor', 'src/VulkanRenderer.cpp', 'src/Application.cpp', ], - include_directories: vkbootstrap_inc, + include_directories: [ vkbootstrap_inc, imgui_inc ], + link_with: imgui_lib, dependencies: [ wayland_dep, vulkan_dep, diff --git a/src/Application.cpp b/src/Application.cpp index 5e946cc..cc4b9bf 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -8,6 +8,9 @@ #include #include +#include +#include + #include "VulkanRenderer.h" namespace Lunar { @@ -35,6 +38,8 @@ Application::~Application() { m_renderer.reset(); + ImGui::DestroyContext(m_imgui_context); + SDL_DestroyWindow(m_window); SDL_Quit(); @@ -55,8 +60,17 @@ auto Application::run() -> void m_renderer->resize(static_cast(width), static_cast(height)); } + + ImGui_ImplSDL3_ProcessEvent(&e); } + ImGui_ImplSDL3_NewFrame(); + ImGui_ImplVulkan_NewFrame(); + + ImGui::NewFrame(); + ImGui::ShowDemoWindow(); + ImGui::Render(); + m_renderer->render(); } } diff --git a/src/Application.h b/src/Application.h index bc26624..b25d6e2 100644 --- a/src/Application.h +++ b/src/Application.h @@ -6,6 +6,8 @@ #include "Logger.h" +#include + namespace Lunar { class VulkanRenderer; @@ -20,6 +22,8 @@ private: Logger m_logger { "Lunar" }; std::unique_ptr m_renderer; + ImGuiContext *m_imgui_context; + bool m_running { true }; }; diff --git a/src/Util.cpp b/src/Util.cpp index 2707d72..877b609 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -149,4 +149,64 @@ auto imageview_create_info(VkFormat format, VkImage image, return info; } +auto command_buffer_submit_info(VkCommandBuffer cmd) + -> VkCommandBufferSubmitInfo +{ + VkCommandBufferSubmitInfo info {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO; + info.pNext = nullptr; + info.commandBuffer = cmd; + info.deviceMask = 0; + return info; +} + +auto semaphore_submit_info(VkPipelineStageFlags2 stage_mask, + VkSemaphore semaphore) -> VkSemaphoreSubmitInfo +{ + VkSemaphoreSubmitInfo info {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; + info.pNext = nullptr; + info.semaphore = semaphore; + info.value = 0; + info.stageMask = stage_mask; + info.deviceIndex = 0; + return info; +} + +auto submit_info2(VkCommandBufferSubmitInfo *cmd_info, + VkSemaphoreSubmitInfo *wait_semaphore_info, + VkSemaphoreSubmitInfo *signal_semaphore_info) -> VkSubmitInfo2 +{ + VkSubmitInfo2 info {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2; + info.pNext = nullptr; + info.flags = 0; + info.waitSemaphoreInfoCount = wait_semaphore_info ? 1u : 0u; + info.pWaitSemaphoreInfos = wait_semaphore_info; + info.commandBufferInfoCount = cmd_info ? 1u : 0u; + info.pCommandBufferInfos = cmd_info; + info.signalSemaphoreInfoCount = signal_semaphore_info ? 1u : 0u; + info.pSignalSemaphoreInfos = signal_semaphore_info; + return info; +} + +auto attachment_info(VkImageView view, VkClearValue *clear, + VkImageLayout layout) -> VkRenderingAttachmentInfo +{ + VkRenderingAttachmentInfo color_at {}; + color_at.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + color_at.pNext = nullptr; + + color_at.imageView = view; + color_at.imageLayout = layout; + color_at.loadOp + = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; + color_at.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + if (clear) { + color_at.clearValue = *clear; + } + + return color_at; +} + } // namespace vkinit diff --git a/src/Util.h b/src/Util.h index 2e347e8..91ac7d8 100644 --- a/src/Util.h +++ b/src/Util.h @@ -53,5 +53,15 @@ auto image_create_info(VkFormat format, VkImageUsageFlags usage_flags, VkExtent3D extent) -> VkImageCreateInfo; auto imageview_create_info(VkFormat format, VkImage image, VkImageAspectFlags aspect_flags) -> VkImageViewCreateInfo; +auto command_buffer_submit_info(VkCommandBuffer cmd) + -> VkCommandBufferSubmitInfo; +auto semaphore_submit_info(VkPipelineStageFlags2 stage_mask, + VkSemaphore semaphore) -> VkSemaphoreSubmitInfo; +auto submit_info2(VkCommandBufferSubmitInfo *cmd_info, + VkSemaphoreSubmitInfo *wait_semaphore_info, + VkSemaphoreSubmitInfo *signal_semaphore_info) -> VkSubmitInfo2; +auto attachment_info(VkImageView view, VkClearValue *clear, + VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + -> VkRenderingAttachmentInfo; } // namespace vkinit diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp index df9d641..c54a95f 100644 --- a/src/VulkanRenderer.cpp +++ b/src/VulkanRenderer.cpp @@ -9,10 +9,12 @@ #include #include #include +#include +#include #include +#include "DescriptorLayoutBuilder.h" #include "Util.h" -#include "src/DescriptorLayoutBuilder.h" namespace Lunar { @@ -30,6 +32,7 @@ VulkanRenderer::VulkanRenderer(SDL_Window *window, Logger &logger) sync_init(); descriptors_init(); pipelines_init(); + imgui_init(); } VulkanRenderer::~VulkanRenderer() @@ -61,6 +64,34 @@ auto VulkanRenderer::resize(uint32_t width, uint32_t height) -> void recreate_swapchain(width, height); } +auto VulkanRenderer::immediate_submit( + std::function &&function) -> void +{ + VK_CHECK(m_logger, vkResetFences(m_vkb.dev, 1, &m_vk.imm_fence)); + VK_CHECK(m_logger, vkResetCommandBuffer(m_vk.imm_command_buffer, 0)); + + auto cmd { m_vk.imm_command_buffer }; + VkCommandBufferBeginInfo cmd_begin_info { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = nullptr, + }; + VK_CHECK(m_logger, vkBeginCommandBuffer(cmd, &cmd_begin_info)); + + function(cmd); + + VK_CHECK(m_logger, vkEndCommandBuffer(cmd)); + + auto cmd_info { vkinit::command_buffer_submit_info(cmd) }; + auto submit { vkinit::submit_info2(&cmd_info, nullptr, nullptr) }; + VK_CHECK(m_logger, + vkQueueSubmit2(m_vk.graphics_queue, 1, &submit, m_vk.imm_fence)); + + VK_CHECK(m_logger, + vkWaitForFences(m_vkb.dev, 1, &m_vk.imm_fence, true, 9999999999)); +} + auto VulkanRenderer::vk_init() -> void { vkb::InstanceBuilder instance_builder {}; @@ -113,6 +144,11 @@ auto VulkanRenderer::vk_init() -> void } vkb::PhysicalDeviceSelector phys_device_selector { m_vkb.instance }; + VkPhysicalDeviceVulkan13Features features_13 {}; + features_13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + features_13.pNext = nullptr; + features_13.synchronization2 = VK_TRUE; + features_13.dynamicRendering = VK_TRUE; phys_device_selector.set_surface(m_vk.surface) .add_desired_extensions({ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, @@ -126,7 +162,8 @@ auto VulkanRenderer::vk_init() -> void VK_KHR_MAINTENANCE1_EXTENSION_NAME, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, VK_KHR_COPY_COMMANDS_2_EXTENSION_NAME, - }); + }) + .set_required_features_13(features_13); auto physical_device_selector_return { phys_device_selector.select() }; if (!physical_device_selector_return) { std::println(std::cerr, @@ -208,6 +245,23 @@ auto VulkanRenderer::commands_init() -> void vkAllocateCommandBuffers( m_vkb.dev, &ai, &frame_data.main_command_buffer)); } + + VK_CHECK(m_logger, + vkCreateCommandPool(m_vkb.dev, &ci, nullptr, &m_vk.imm_command_pool)); + + VkCommandBufferAllocateInfo ai { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = nullptr, + .commandPool = m_vk.imm_command_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + VK_CHECK(m_logger, + vkAllocateCommandBuffers(m_vkb.dev, &ai, &m_vk.imm_command_buffer)); + + m_vk.deletion_queue.emplace([this]() { + vkDestroyCommandPool(m_vkb.dev, m_vk.imm_command_pool, nullptr); + }); } auto VulkanRenderer::sync_init() -> void @@ -231,6 +285,11 @@ auto VulkanRenderer::sync_init() -> void vkCreateSemaphore(m_vkb.dev, &semaphore_ci, nullptr, &frame_data.swapchain_semaphore)); } + + VK_CHECK(m_logger, + vkCreateFence(m_vkb.dev, &fence_ci, nullptr, &m_vk.imm_fence)); + m_vk.deletion_queue.emplace( + [this]() { vkDestroyFence(m_vkb.dev, m_vk.imm_fence, nullptr); }); } auto VulkanRenderer::descriptors_init() -> void @@ -306,6 +365,65 @@ auto VulkanRenderer::background_pipelines_init() -> void }); } +auto VulkanRenderer::imgui_init() -> void +{ + 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 imgui_pool; + VK_CHECK(m_logger, + vkCreateDescriptorPool(m_vkb.dev, &pool_info, nullptr, &imgui_pool)); + + ImGui::CreateContext(); + + ImGui_ImplSDL3_InitForVulkan(m_window); + + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = m_vkb.instance; + init_info.PhysicalDevice = m_vkb.phys_dev; + init_info.Device = m_vkb.dev; + init_info.Queue = m_vk.graphics_queue; + init_info.DescriptorPool = imgui_pool; + init_info.MinImageCount = 3; + init_info.ImageCount = 3; + init_info.UseDynamicRendering = true; + + init_info.PipelineInfoMain.PipelineRenderingCreateInfo.sType + = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO; + init_info.PipelineInfoMain.PipelineRenderingCreateInfo.colorAttachmentCount + = 1; + init_info.PipelineInfoMain.PipelineRenderingCreateInfo + .pColorAttachmentFormats + = &m_vk.swapchain_image_format; + + init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + + ImGui_ImplVulkan_Init(&init_info); + + m_vk.deletion_queue.emplace([&]() { + ImGui_ImplVulkan_Shutdown(); + vkDestroyDescriptorPool(m_vkb.dev, imgui_pool, nullptr); + }); +} + auto VulkanRenderer::render() -> void { defer(m_vk.frame_number++); @@ -365,28 +483,31 @@ auto VulkanRenderer::render() -> void m_vk.swapchain_extent); vkutil::transition_image(cmd, m_vk.swapchain_images[swapchain_image_idx], - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + draw_imgui(cmd, m_vk.swapchain_image_views.at(swapchain_image_idx)); + + vkutil::transition_image(cmd, m_vk.swapchain_images[swapchain_image_idx], + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); VK_CHECK(m_logger, vkEndCommandBuffer(cmd)); VkSemaphore render_semaphore = m_vk.present_semaphores.at(swapchain_image_idx); - VkPipelineStageFlags wait_stage - = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkSubmitInfo submit_info { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .pNext = nullptr, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &m_vk.get_current_frame().swapchain_semaphore, - .pWaitDstStageMask = &wait_stage, - .commandBufferCount = 1, - .pCommandBuffers = &cmd, - .signalSemaphoreCount = 1, - .pSignalSemaphores = &render_semaphore, - }; + VkPipelineStageFlags2 wait_stage + = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + auto wait_info { vkinit::semaphore_submit_info( + wait_stage, m_vk.get_current_frame().swapchain_semaphore) }; + auto command_buffer_info { vkinit::command_buffer_submit_info(cmd) }; + auto signal_info { vkinit::semaphore_submit_info( + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, render_semaphore) }; + auto submit_info { vkinit::submit_info2( + &command_buffer_info, &wait_info, &signal_info) }; VK_CHECK(m_logger, - vkQueueSubmit(m_vk.graphics_queue, 1, &submit_info, + vkQueueSubmit2(m_vk.graphics_queue, 1, &submit_info, m_vk.get_current_frame().render_fence)); VkPresentInfoKHR present_info = {}; @@ -425,6 +546,27 @@ auto VulkanRenderer::draw_background(VkCommandBuffer cmd) -> void static_cast(std::ceil(m_vk.draw_extent.height / 16.0)), 1); } +auto VulkanRenderer::draw_imgui( + VkCommandBuffer cmd, VkImageView target_image_view) -> void +{ + auto const color_attachment { vkinit::attachment_info( + target_image_view, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) }; + VkRenderingInfo render_info {}; + render_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + render_info.pNext = nullptr; + render_info.flags = 0; + render_info.renderArea = { {}, m_vk.draw_extent }; + render_info.layerCount = 1; + render_info.colorAttachmentCount = 1; + render_info.pColorAttachments = &color_attachment; + + vkCmdBeginRendering(cmd, &render_info); + + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); + + vkCmdEndRendering(cmd); +} + auto VulkanRenderer::create_swapchain(uint32_t width, uint32_t height) -> void { vkb::SwapchainBuilder builder { m_vkb.phys_dev, m_vkb.dev, m_vk.surface }; diff --git a/src/VulkanRenderer.h b/src/VulkanRenderer.h index 89688df..92c69fb 100644 --- a/src/VulkanRenderer.h +++ b/src/VulkanRenderer.h @@ -34,6 +34,9 @@ public: auto render() -> void; auto resize(uint32_t width, uint32_t height) -> void; + auto immediate_submit(std::function &&function) + -> void; + private: auto vk_init() -> void; auto swapchain_init() -> void; @@ -42,8 +45,10 @@ private: auto descriptors_init() -> void; auto pipelines_init() -> void; auto background_pipelines_init() -> void; + auto imgui_init() -> void; auto draw_background(VkCommandBuffer cmd) -> void; + auto draw_imgui(VkCommandBuffer cmd, VkImageView target_image_view) -> void; auto create_swapchain(uint32_t width, uint32_t height) -> void; auto create_draw_image(uint32_t width, uint32_t height) -> void; @@ -60,6 +65,11 @@ private: } m_vkb; struct { + auto get_current_frame() -> FrameData & + { + return frames.at(frame_number % frames.size()); + } + VkSwapchainKHR swapchain { VK_NULL_HANDLE }; VkSurfaceKHR surface { nullptr }; VkFormat swapchain_image_format; @@ -73,11 +83,6 @@ private: VkExtent2D swapchain_extent; std::array frames; - auto get_current_frame() -> FrameData & - { - return frames.at(frame_number % frames.size()); - } - AllocatedImage draw_image {}; VkExtent2D draw_extent {}; @@ -92,6 +97,10 @@ private: DeletionQueue deletion_queue; + VkFence imm_fence {}; + VkCommandBuffer imm_command_buffer {}; + VkCommandPool imm_command_pool {}; + uint64_t frame_number { 0 }; } m_vk; diff --git a/thirdparty/imgui b/thirdparty/imgui new file mode 160000 index 0000000..bfe1378 --- /dev/null +++ b/thirdparty/imgui @@ -0,0 +1 @@ +Subproject commit bfe137893abd0d08fa6617ae247d5a3379bdd571