diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 73aba8221..cbdbe9620 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -29,6 +29,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2025-09-04: Vulkan: Added ImGui_ImplVulkan_CreateMainPipeline(). (#8110, #8111) // 2025-07-27: Vulkan: Fixed texture update corruption introduced on 2025-06-11. (#8801, #8755, #8840) // 2025-07-07: Vulkan: Fixed texture synchronization issue introduced on 2025-06-11. (#8772) // 2025-06-27: Vulkan: Fixed validation errors during texture upload/update by aligning upload size to 'nonCoherentAtomSize'. (#8743, #8744) @@ -279,6 +280,7 @@ struct ImGui_ImplVulkan_Data VkShaderModule ShaderModuleVert; VkShaderModule ShaderModuleFrag; VkDescriptorPool DescriptorPool; + ImVector PipelineRenderingCreateInfoColorAttachmentFormats; // Deep copy of format array // Texture management VkSampler TexSampler; @@ -935,7 +937,11 @@ static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAlloca } } -static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) +#if !defined(IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING) && !(defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering)) +typedef void VkPipelineRenderingCreateInfoKHR; +#endif + +static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, uint32_t subpass, const VkPipelineRenderingCreateInfoKHR* pipeline_rendering_create_info) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_CreateShaderModules(device, allocator); @@ -1039,15 +1045,19 @@ static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationC #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING if (bd->VulkanInitInfo.UseDynamicRendering) { - IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); - IM_ASSERT(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo pNext must be nullptr"); - info.pNext = &bd->VulkanInitInfo.PipelineRenderingCreateInfo; + IM_ASSERT(pipeline_rendering_create_info && "PipelineRenderingCreateInfo must not be nullptr when using dynamic rendering"); + IM_ASSERT(pipeline_rendering_create_info->sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo::sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR"); + IM_ASSERT(pipeline_rendering_create_info->pNext == nullptr && "PipelineRenderingCreateInfo::pNext must be nullptr"); + info.pNext = pipeline_rendering_create_info; info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr. } +#else + IM_ASSERT(pipeline_rendering_create_info == nullptr); #endif - - VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); + VkPipeline pipeline; + VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, &pipeline); check_vk_result(err); + return pipeline; } bool ImGui_ImplVulkan_CreateDeviceObjects() @@ -1121,7 +1131,22 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() check_vk_result(err); } - ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, v->RenderPass, v->MSAASamples, &bd->Pipeline, v->Subpass); + // Create pipeline + if (v->RenderPass +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + || (v->UseDynamicRendering && v->PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR) +#endif + ) + { + ImGui_ImplVulkan_MainPipelineCreateInfo mp_info = {}; + mp_info.RenderPass = v->RenderPass; + mp_info.Subpass = v->Subpass; + mp_info.MSAASamples = v->MSAASamples; +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + mp_info.PipelineRenderingCreateInfo = v->PipelineRenderingCreateInfo; +#endif + ImGui_ImplVulkan_CreateMainPipeline(mp_info); + } // Create command pool/buffer for texture upload if (!bd->TexCommandPool) @@ -1146,6 +1171,37 @@ bool ImGui_ImplVulkan_CreateDeviceObjects() return true; } +void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_MainPipelineCreateInfo& info) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (bd->Pipeline) + { + vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); + bd->Pipeline = VK_NULL_HANDLE; + } + v->RenderPass = info.RenderPass; + v->MSAASamples = info.MSAASamples; + v->Subpass = info.Subpass; + + const VkPipelineRenderingCreateInfoKHR* pipeline_rendering_create_info = nullptr; +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + if (v->UseDynamicRendering) + { + v->PipelineRenderingCreateInfo = info.PipelineRenderingCreateInfo; + pipeline_rendering_create_info = &v->PipelineRenderingCreateInfo; + if (v->PipelineRenderingCreateInfo.pColorAttachmentFormats != NULL) + { + // Deep copy buffer to reduce error-rate for end user (#8282) + bd->PipelineRenderingCreateInfoColorAttachmentFormats.resize((int)v->PipelineRenderingCreateInfo.colorAttachmentCount); + memcpy(bd->PipelineRenderingCreateInfoColorAttachmentFormats.Data, v->PipelineRenderingCreateInfo.pColorAttachmentFormats, (size_t)bd->PipelineRenderingCreateInfoColorAttachmentFormats.size_in_bytes()); + v->PipelineRenderingCreateInfo.pColorAttachmentFormats = bd->PipelineRenderingCreateInfoColorAttachmentFormats.Data; + } + } +#endif + bd->Pipeline = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, v->RenderPass, v->MSAASamples, v->Subpass, pipeline_rendering_create_info); +} + void ImGui_ImplVulkan_DestroyDeviceObjects() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); @@ -1270,8 +1326,6 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) IM_ASSERT(info->DescriptorPoolSize > 0); IM_ASSERT(info->MinImageCount >= 2); IM_ASSERT(info->ImageCount >= info->MinImageCount); - if (info->UseDynamicRendering == false) - IM_ASSERT(info->RenderPass != VK_NULL_HANDLE); bd->VulkanInitInfo = *info; @@ -1279,17 +1333,6 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) vkGetPhysicalDeviceProperties(info->PhysicalDevice, &properties); bd->NonCoherentAtomSize = properties.limits.nonCoherentAtomSize; -#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - if (v->PipelineRenderingCreateInfo.pColorAttachmentFormats != NULL) - { - // Deep copy buffer to reduce error-rate for end user (#8282) - VkFormat* formats_copy = (VkFormat*)IM_ALLOC(sizeof(VkFormat) * v->PipelineRenderingCreateInfo.colorAttachmentCount); - memcpy(formats_copy, v->PipelineRenderingCreateInfo.pColorAttachmentFormats, sizeof(VkFormat) * v->PipelineRenderingCreateInfo.colorAttachmentCount); - v->PipelineRenderingCreateInfo.pColorAttachmentFormats = formats_copy; - } -#endif - if (!ImGui_ImplVulkan_CreateDeviceObjects()) IM_ASSERT(0 && "ImGui_ImplVulkan_CreateDeviceObjects() failed!"); // <- Can't be hit yet. @@ -1310,9 +1353,6 @@ void ImGui_ImplVulkan_Shutdown() // First destroy objects in all viewports ImGui_ImplVulkan_DestroyDeviceObjects(); -#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - IM_FREE((void*)const_cast(bd->VulkanInitInfo.PipelineRenderingCreateInfo.pColorAttachmentFormats)); -#endif // Manually delete main viewport render data in-case we haven't initialized for viewports ImGuiViewport* main_viewport = ImGui::GetMainViewport(); diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index dcf6db91a..00c903060 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -83,29 +83,27 @@ struct ImGui_ImplVulkan_InitInfo uint32_t QueueFamily; VkQueue Queue; VkDescriptorPool DescriptorPool; // See requirements in note above; ignored if using DescriptorPoolSize > 0 - VkRenderPass RenderPass; // Ignored if using dynamic rendering + uint32_t DescriptorPoolSize; // Optional: set to create internal descriptor pool automatically instead of using DescriptorPool. uint32_t MinImageCount; // >= 2 uint32_t ImageCount; // >= MinImageCount + VkPipelineCache PipelineCache; // Optional + + // Pipeline + VkRenderPass RenderPass; // Ignored if using dynamic rendering + uint32_t Subpass; VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT - // (Optional) - VkPipelineCache PipelineCache; - uint32_t Subpass; - - // (Optional) Set to create internal descriptor pool instead of using DescriptorPool - uint32_t DescriptorPoolSize; - // (Optional) Dynamic Rendering - // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3. + // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3 + setup PipelineRenderingCreateInfo. bool UseDynamicRendering; #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; + VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Optional, valid if .sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR #endif // (Optional) Allocation, Debugging const VkAllocationCallbacks* Allocator; void (*CheckVkResultFn)(VkResult err); - VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory. + VkDeviceSize MinAllocationSize; // Minimum allocation size. Set to 1024*1024 to satisfy zealous best practices validation layer and waste a little memory. }; // Follow "Getting Started" link and check examples/ folder to learn about using backends! @@ -115,6 +113,20 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) +// (Advanced) Use e.g. if you need to recreate pipeline without reinitializing the backend (see #8110, #8111) +// The main window pipeline will be created by ImGui_ImplVulkan_Init() if possible (== RenderPass xor (UseDynamicRendering && PipelineRenderingCreateInfo->sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR)) +// Else, the pipeline can be created, or re-created, using ImGui_ImplVulkan_CreateMainPipeline() before rendering. +struct ImGui_ImplVulkan_MainPipelineCreateInfo +{ + VkRenderPass RenderPass = VK_NULL_HANDLE; + uint32_t Subpass = 0; + VkSampleCountFlagBits MSAASamples = {}; +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Optional, valid if .sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR +#endif +}; +IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_MainPipelineCreateInfo& info); // (render_pass xor (p_dynamic_rendering && p_dynamic_rendering is correct (sType and pNext)) + // (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d310b5ef3..0c741e1e6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,10 +47,21 @@ Other Changes: ImGuiStyleVar_ScrollbarPadding enum, instead of hardcoded computed default. (#8895) - Fonts: fixed an assertion failure when a rectangle entry has been reused 1024 times (e.g. due to constant change of font types). (#8906) [@cfillion] +- Fonts: fixed merging a font and specifying a font target in DstFont + that's not the last added font (regression in 1.92). (#8912) - Clipper, Tables: added ImGuiListClipperFlags_NoSetTableRowCounters as a way to disable the assumption that 1 clipper item == 1 table row, which breaks when e.g. using clipper with ItemsHeight=1 in order to clip in pixel units. (#8886) -- Fixed Bullet() fixed tesselation amount which looked out of place in very large sizes. +- Nav: fixed Ctrl+Tab window appearing as empty when the sole active and focused + window has the ImGuiWindowFlags_NoNavFocus flag. (#8914) +- Bullet: fixed tesselation amount which looked out of place in very large sizes. +- InputText, InputInt, InputFloat: fixed an issue where using Escape to revert + would not write back the reverted value during the IsItemDeactivatedAfterEdit() + frame if the provided input buffer doesn't store temporary edits. + (regression in 1.91.7) (#8915, #8273) +- InputText: fixed an issue where using Escape with ImGuiInputTextFlags_EscapeClearsAll + would not write back the cleared value during the IsItemDeactivatedAfterEdit() + frame if the provided input buffer doesn't store temporary edits. (#8915, #8273) - InputText: allow passing an empty string with buf_size==0. (#8907) In theory the buffer size should always account for a zero-terminator, but idioms such as using InputTextMultiline() with ImGuiInputTextFlags_ReadOnly to display @@ -70,6 +81,8 @@ Other Changes: - Backends: SDL_GPU: Added ImGui_ImplSDLGPU3_InitInfo::SwapchainComposition and PresentMode to configure how secondary viewports are created. Currently only used multi-viewport mode. (#8892) [@PTSVU] +- Backends: Vulkan: added ImGui_ImplVulkan_CreateMainPipeline() to recreate pipeline + without reinitializing backend. (#8110, #8111) [@SuperRonan] ----------------------------------------------------------------------- diff --git a/docs/FAQ.md b/docs/FAQ.md index fb693c7e3..519a0e8a9 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -19,7 +19,7 @@ or view this file with any Markdown viewer. | **[How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?](#q-how-can-i-tell-whether-to-dispatch-mousekeyboard-to-dear-imgui-or-my-application)** | | [How can I enable keyboard or gamepad controls?](#q-how-can-i-enable-keyboard-or-gamepad-controls) | | [How can I use this on a machine without mouse, keyboard or screen? (input share, remote display)](#q-how-can-i-use-this-on-a-machine-without-mouse-keyboard-or-screen-input-share-remote-display) | -| [How can I create my own backend?](q-how-can-i-create-my-own-backend) +| [How can I create my own backend?](#q-how-can-i-create-my-own-backend) | [I integrated Dear ImGui in my engine and little squares are showing instead of text...](#q-i-integrated-dear-imgui-in-my-engine-and-little-squares-are-showing-instead-of-text) | | [I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-clipping-or-disappearing-when-i-move-windows-around) | | [I integrated Dear ImGui in my engine and some elements are displaying outside their expected windows boundaries...](#q-i-integrated-dear-imgui-in-my-engine-and-some-elements-are-displaying-outside-their-expected-windows-boundaries) | diff --git a/imgui.cpp b/imgui.cpp index 18af39775..49c0b6eb5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -14966,7 +14966,7 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingInputSource = g.NavInputSource = ImGuiInputSource_Gamepad; } if (start_windowing_with_gamepad || start_windowing_with_keyboard) - if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) + if (ImGuiWindow* window = (g.NavWindow && IsWindowNavFocusable(g.NavWindow)) ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { if (start_windowing_with_keyboard || g.ConfigNavWindowingWithGamepad) g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // Current location diff --git a/imgui.h b/imgui.h index db6c0dad7..1b303e5e9 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.92.3 WIP" -#define IMGUI_VERSION_NUM 19224 +#define IMGUI_VERSION_NUM 19225 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 #define IMGUI_HAS_VIEWPORT // In 'docking' WIP branch. @@ -102,8 +102,10 @@ Index of this file: #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. -// (MSVC provides an equivalent mechanism via SAL Annotations but it would require the macros in a different -// location. e.g. #include + void myprintf(_Printf_format_string_ const char* format, ...)) +// (MSVC provides an equivalent mechanism via SAL Annotations but it requires the macros in a different +// location. e.g. #include + void myprintf(_Printf_format_string_ const char* format, ...), +// and only works when using Code Analysis, rather than just normal compiling). +// (see https://github.com/ocornut/imgui/issues/8871 for a patch to enable this for MSVC's Code Analysis) #if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) #define IM_FMTARGS(FMT) __attribute__((format(gnu_printf, FMT, FMT+1))) #define IM_FMTLIST(FMT) __attribute__((format(gnu_printf, FMT, 0))) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index c3e4e5a35..261498f21 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -3032,7 +3032,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg_in) else { IM_ASSERT(Fonts.Size > 0 && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. - font = Fonts.back(); + font = font_cfg_in->DstFont ? font_cfg_in->DstFont : Fonts.back(); } // Add to list diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 8aabd05f5..e49483074 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4972,7 +4972,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { if (flags & ImGuiInputTextFlags_EscapeClearsAll) { - if (buf[0] != 0) + if (state->TextA.Data[0] != 0) { revert_edit = true; } @@ -5064,14 +5064,14 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (flags & ImGuiInputTextFlags_EscapeClearsAll) { // Clear input - IM_ASSERT(buf[0] != 0); + IM_ASSERT(state->TextA.Data[0] != 0); apply_new_text = ""; apply_new_text_length = 0; value_changed = true; IMSTB_TEXTEDIT_CHARTYPE empty_string; stb_textedit_replace(state, state->Stb, &empty_string, 0); } - else if (strcmp(buf, state->TextToRevertTo.Data) != 0) + else if (strcmp(state->TextA.Data, state->TextToRevertTo.Data) != 0) { apply_new_text = state->TextToRevertTo.Data; apply_new_text_length = state->TextToRevertTo.Size - 1;