From 072e27de60e010d75d99610dac999b996ad13ad9 Mon Sep 17 00:00:00 2001 From: Retro52 Date: Fri, 6 Feb 2026 18:36:33 +0100 Subject: [PATCH 1/2] add imgui as a submodule w/o proper integration --- .gitmodules | 3 +++ extern/imgui | 1 + 2 files changed, 4 insertions(+) create mode 160000 extern/imgui diff --git a/.gitmodules b/.gitmodules index 209ce90..e104c95 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "extern/cgltf"] path = extern/cgltf url = https://github.com/jkuhlmann/cgltf +[submodule "extern/imgui"] + path = extern/imgui + url = https://github.com/ocornut/imgui.git diff --git a/extern/imgui b/extern/imgui new file mode 160000 index 0000000..913a3c6 --- /dev/null +++ b/extern/imgui @@ -0,0 +1 @@ +Subproject commit 913a3c60561bb07e8fd410ec7d4a8f6f485defd6 From 4ebfa31144492c79a51239ecc8412a3c18c9a36f Mon Sep 17 00:00:00 2001 From: Retro52 Date: Fri, 6 Feb 2026 20:00:19 +0100 Subject: [PATCH 2/2] simple imgui integration This commit implements ImGui initialization/shutdown and implements a new way to change debugging options. ImGui can be disabled by tweaking NIAGARA_ENABLE_IMGUI flag in niagara.cpp (1 by default). --- .gitignore | 2 + CMakeLists.txt | 21 ++++++++- src/imgui_utils.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++ src/imgui_utils.h | 14 ++++++ src/niagara.cpp | 73 ++++++++++++++++++++++++++++++- 5 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 src/imgui_utils.cpp create mode 100644 src/imgui_utils.h diff --git a/.gitignore b/.gitignore index 31d6f31..b7fd0be 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ build.ninja .ninja_* compile_commands.json *.cmake + +imgui.ini diff --git a/CMakeLists.txt b/CMakeLists.txt index f6f8ddf..b034824 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,13 @@ add_executable(niagara set_target_properties(niagara PROPERTIES CXX_STANDARD 11 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) -target_compile_definitions(niagara PRIVATE GLFW_INCLUDE_NONE GLM_FORCE_XYZW_ONLY GLM_FORCE_QUAT_DATA_XYZW GLM_FORCE_QUAT_CTOR_XYZW) +target_compile_definitions(niagara PRIVATE + GLFW_INCLUDE_NONE + GLM_FORCE_XYZW_ONLY + GLM_FORCE_QUAT_DATA_XYZW + GLM_FORCE_QUAT_CTOR_XYZW + CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}" +) target_include_directories(niagara PRIVATE extern/fast_obj extern/cgltf extern/glm) if(WIN32) @@ -40,10 +46,23 @@ endif() add_subdirectory(extern/volk) add_subdirectory(extern/meshoptimizer) +file(GLOB IMGUI_SOURCES "extern/imgui/*.cpp" "extern/imgui/*.c") + +list(APPEND IMGUI_SOURCES "extern/imgui/misc/cpp/imgui_stdlib.cpp") +list(APPEND IMGUI_SOURCES "extern/imgui/backends/imgui_impl_glfw.cpp") +list(APPEND IMGUI_SOURCES "extern/imgui/backends/imgui_impl_vulkan.cpp") + +add_library(imgui STATIC ${IMGUI_SOURCES}) + +target_link_libraries(imgui PUBLIC glfw volk) +target_include_directories(imgui PUBLIC "extern/imgui") +target_compile_definitions(imgui PUBLIC IMGUI_IMPL_VULKAN_USE_VOLK) + target_link_libraries(niagara PRIVATE glfw volk + imgui meshoptimizer) if(UNIX) diff --git a/src/imgui_utils.cpp b/src/imgui_utils.cpp new file mode 100644 index 0000000..b21adf7 --- /dev/null +++ b/src/imgui_utils.cpp @@ -0,0 +1,102 @@ +#include "imgui_utils.h" +#include +#include +#include +#include + +void imInit(GLFWwindow* window, VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, uint32_t queue_family, VkQueue queue, uint32_t sc_images, VkFormat sc_format) +{ + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + (void)io; + + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + + // May trigger validation assertion due to the incorrect sync. implementation on ImGui side + // See https://github.com/ocornut/imgui/issues/8795 + // io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + + // Makes all elements transparent, so the background is seen even when window is open + ImGui::GetStyle().Alpha = 0.75F; + + ImGui_ImplGlfw_InitForVulkan(window, true); + ImGui_ImplVulkan_InitInfo init_info = {}; + + init_info.ApiVersion = VK_API_VERSION_1_4; + init_info.Instance = instance; + init_info.PhysicalDevice = physical_device; + init_info.Device = device; + init_info.QueueFamily = queue_family; + init_info.Queue = queue; + init_info.PipelineCache = VK_NULL_HANDLE; + init_info.UseDynamicRendering = true; + init_info.MinAllocationSize = 1024 * 1024; + init_info.DescriptorPool = VK_NULL_HANDLE; + init_info.MinImageCount = 2; + init_info.ImageCount = sc_images; + init_info.DescriptorPoolSize = IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE; + init_info.Allocator = nullptr; + init_info.PipelineInfoMain.PipelineRenderingCreateInfo = { + VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + nullptr, + 0, + 1, + &sc_format, + }; + + ImGui_ImplVulkan_Init(&init_info); +} + +void imShutdown() +{ + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); +} + +void imBeginFrame() +{ + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); +} + +void imEndAndRender(VkCommandBuffer command_buffer, VkImageView sc_img_view, VkRect2D viewport) +{ + ImGui::Render(); + + VkRenderingAttachmentInfo color_attachment_info{ VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO }; + color_attachment_info.imageView = sc_img_view; + color_attachment_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + color_attachment_info.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + color_attachment_info.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + + VkRenderingInfo rendering_info{ VK_STRUCTURE_TYPE_RENDERING_INFO }; + rendering_info.renderArea = viewport; + rendering_info.layerCount = 1; + rendering_info.colorAttachmentCount = 1; + rendering_info.pColorAttachments = &color_attachment_info; + + vkCmdBeginRendering(command_buffer, &rendering_info); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), command_buffer); + vkCmdEndRendering(command_buffer); + + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } +} + +bool imWantCaptureMouse() +{ + return ImGui::GetIO().WantCaptureMouse; +} + +bool imWantCaptureKeyboard() +{ + return ImGui::GetIO().WantCaptureKeyboard; +} diff --git a/src/imgui_utils.h b/src/imgui_utils.h new file mode 100644 index 0000000..99d10ae --- /dev/null +++ b/src/imgui_utils.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +struct GLFWwindow; + +void imInit(GLFWwindow* window, VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, uint32_t queue_family, VkQueue queue, uint32_t sc_images, VkFormat sc_format); +void imShutdown(); + +void imBeginFrame(); +void imEndAndRender(VkCommandBuffer command_buffer, VkImageView sc_img_view, VkRect2D viewport); + +bool imWantCaptureMouse(); +bool imWantCaptureKeyboard(); diff --git a/src/niagara.cpp b/src/niagara.cpp index 6bae8d1..9ed156f 100644 --- a/src/niagara.cpp +++ b/src/niagara.cpp @@ -27,6 +27,13 @@ #include #endif +#define NIAGARA_ENABLE_IMGUI 1 + +#if NIAGARA_ENABLE_IMGUI +#include "imgui_utils.h" +#include +#endif + bool meshShadingEnabled = true; bool cullingEnabled = true; bool lodEnabled = true; @@ -955,6 +962,10 @@ int main(int argc, const char** argv) uint64_t timestampResults[23] = {}; uint64_t pipelineResults[3] = {}; +#if NIAGARA_ENABLE_IMGUI + imInit(window, instance, physicalDevice, device, familyIndex, queue, swapchain.imageCount, swapchainFormat); +#endif + while (!glfwWindowShouldClose(window)) { double frameDelta = glfwGetTime() - frameTimestamp; @@ -987,8 +998,14 @@ int main(int argc, const char** argv) if (reloadShaders && glfwGetTime() >= reloadShadersTimer) { bool changed = false; - int rc = system("ninja --quiet compile_shaders"); - if (rc == 0) + +#if defined(CMAKE_BINARY_DIR) + int rr = system("cmake --build " CMAKE_BINARY_DIR " --target compile_shaders"); +#else + int rr = system("ninja --quiet compile_shaders"); +#endif + + if (rr == 0) { for (Shader& shader : shaders.shaders) { @@ -1639,6 +1656,54 @@ int main(int argc, const char** argv) } } +#if NIAGARA_ENABLE_IMGUI + imBeginFrame(); + ImGui::SetNextWindowCollapsed(true, ImGuiCond_Appearing); + if (ImGui::Begin("niagara", nullptr)) + { + ImGui::BeginDisabled(!meshShadingSupported); + ImGui::Checkbox("Enable mesh shading (M)", &meshShadingEnabled); + ImGui::Checkbox("Enable task shader (T)", &taskShadingEnabled); + ImGui::EndDisabled(); + + ImGui::Checkbox("Enable frustum culling (C)", &cullingEnabled); + ImGui::Checkbox("Enable mesh occlusion culling (O)", &occlusionEnabled); + ImGui::Checkbox("Enable cluster occlusion culling (K)", &clusterOcclusionEnabled); + ImGui::Checkbox("Enable LODs (L)", &lodEnabled); + ImGui::Checkbox("Enable animation (Space)", &animationEnabled); + ImGui::Checkbox("Debug sleep (Z)", &debugSleep); + + ImGui::Checkbox("Enable RT shadows (F)", &shadowsEnabled); + ImGui::Checkbox("Enable RT shadows blur (B)", &shadowblurEnabled); + ImGui::Checkbox("Enable RT shadows checkerboard (X)", &shadowCheckerboard); + + ImGui::SliderInt("Set RT shadows quality (Q)", &shadowQuality, 0, 1); + ImGui::SliderInt("Set LOD step (0-9)", &debugLodStep, 0, COUNTOF(Mesh::lods)); + + debugGuiMode = debugGuiMode % 3; + ImGui::SliderInt("GUI debug level (G)", &debugGuiMode, 0, 2); + +#if !defined(CMAKE_BINARY_DIR) + ImGui::Text("CMAKE_BINARY_DIR not defined: shaders reload may not work as expected"); +#endif + + if (!reloadShaders && ImGui::Button("Enable shaders live reload")) + { + reloadShaders = !reloadShaders; + reloadShadersTimer = 0; + } + else if (reloadShaders) + { + ImGui::Text("Shaders live reload active..."); + if (ImGui::Button("Stop")) + reloadShaders = !reloadShaders; + } + } + ImGui::End(); + + imEndAndRender(commandBuffer, swapchainImageViews[imageIndex], { { 0, 0 }, { uint32_t(swapchain.width), uint32_t(swapchain.height) } }); +#endif + VkImageMemoryBarrier2 presentBarrier = imageBarrier(swapchain.images[imageIndex], VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL, 0, 0, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); @@ -1706,6 +1771,10 @@ int main(int argc, const char** argv) VK_CHECK(vkDeviceWaitIdle(device)); +#if NIAGARA_ENABLE_IMGUI + imShutdown(); +#endif + vkDestroyDescriptorPool(device, textureSet.first, 0); for (Image& image : images)