1
0
Fork 0
mirror of https://github.com/ocornut/imgui.git synced 2026-01-11 00:04:24 +00:00
This commit is contained in:
Ronan 2025-12-14 08:13:42 +03:00 committed by GitHub
commit 140691e942
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 367 additions and 138 deletions

View file

@ -278,16 +278,22 @@ struct ImGui_ImplVulkan_ViewportData
// Vulkan data // Vulkan data
struct ImGui_ImplVulkan_Data struct ImGui_ImplVulkan_Data
{ {
struct CachedPipelineLayout
{
uint32_t CustomPushConstantSize;
VkShaderStageFlags CustomPushConstantStages;
VkPipelineLayout Handle;
};
ImGui_ImplVulkan_InitInfo VulkanInitInfo; ImGui_ImplVulkan_InitInfo VulkanInitInfo;
VkDeviceSize BufferMemoryAlignment; VkDeviceSize BufferMemoryAlignment;
VkDeviceSize NonCoherentAtomSize; VkDeviceSize NonCoherentAtomSize;
VkPipelineCreateFlags PipelineCreateFlags; VkPipelineCreateFlags PipelineCreateFlags;
VkDescriptorSetLayout DescriptorSetLayout; VkDescriptorSetLayout DescriptorSetLayout;
VkPipelineLayout PipelineLayout; CachedPipelineLayout PipelineLayout;
VkPipeline Pipeline; // pipeline for main render pass (created by app) VkPipeline Pipeline; // pipeline for main render pass (created by app)
VkPipeline PipelineForViewports; // pipeline for secondary viewports (created by backend) VkShaderModule ShaderModuleVert; // Default module, created by backend when needed and kept alive until shutdown
VkShaderModule ShaderModuleVert; VkShaderModule ShaderModuleFrag; // Default module, created by backend when needed and kept alive until shutdown
VkShaderModule ShaderModuleFrag;
VkDescriptorPool DescriptorPool; VkDescriptorPool DescriptorPool;
ImVector<VkFormat> PipelineRenderingCreateInfoColorAttachmentFormats; // Deep copy of format array ImVector<VkFormat> PipelineRenderingCreateInfoColorAttachmentFormats; // Deep copy of format array
@ -299,6 +305,16 @@ struct ImGui_ImplVulkan_Data
// Render buffers for main window // Render buffers for main window
ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers; ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers;
// Common secondary viewports data
// May differ from the init info's desired values
// .format == VK_FORMAT_UNDEFINED => We should call ImGui_ImplVulkan_PrepareViewportsRendering()
VkSurfaceFormatKHR ViewportsFormat;
// Filled during ImGui_ImplVulkan_PrepareViewportsRendering() based on ViewportsFormat and VulkanInitInfo->SecondaryViewportsInfo
ImGui_ImplVulkan_PipelineInfo PipelineInfoForViewports;
CachedPipelineLayout PipelineLayoutForViewports;
VkPipeline PipelineForViewports; // Common pipeline for secondary viewports (created by backend)
ImGui_ImplVulkan_Data() ImGui_ImplVulkan_Data()
{ {
memset((void*)this, 0, sizeof(*this)); memset((void*)this, 0, sizeof(*this));
@ -494,7 +510,7 @@ static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory
buffer_size = buffer_size_aligned; buffer_size = buffer_size_aligned;
} }
static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkan_FrameRenderBuffers* rb, int fb_width, int fb_height) static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkPipelineLayout layout, VkCommandBuffer command_buffer, ImGui_ImplVulkan_FrameRenderBuffers* rb, int fb_width, int fb_height)
{ {
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
@ -533,13 +549,13 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
float translate[2]; float translate[2];
translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0];
translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1];
vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); vkCmdPushConstants(command_buffer, layout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale);
vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); vkCmdPushConstants(command_buffer, layout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate);
} }
} }
// Render function // Render function
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline) void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline, VkPipelineLayout layout)
{ {
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
@ -558,6 +574,8 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
if (pipeline == VK_NULL_HANDLE) if (pipeline == VK_NULL_HANDLE)
pipeline = bd->Pipeline; pipeline = bd->Pipeline;
if (layout == VK_NULL_HANDLE)
layout = bd->PipelineLayout.Handle;
// Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage. // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage.
ImGui_ImplVulkan_ViewportData* viewport_renderer_data = (ImGui_ImplVulkan_ViewportData*)draw_data->OwnerViewport->RendererUserData; ImGui_ImplVulkan_ViewportData* viewport_renderer_data = (ImGui_ImplVulkan_ViewportData*)draw_data->OwnerViewport->RendererUserData;
@ -612,14 +630,14 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
} }
// Setup desired Vulkan state // Setup desired Vulkan state
ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, layout, command_buffer, rb, fb_width, fb_height);
// Setup render state structure (for callbacks and custom texture bindings) // Setup render state structure (for callbacks and custom texture bindings)
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
ImGui_ImplVulkan_RenderState render_state; ImGui_ImplVulkan_RenderState render_state;
render_state.CommandBuffer = command_buffer; render_state.CommandBuffer = command_buffer;
render_state.Pipeline = pipeline; render_state.Pipeline = pipeline;
render_state.PipelineLayout = bd->PipelineLayout; render_state.PipelineLayout = layout;
platform_io.Renderer_RenderState = &render_state; platform_io.Renderer_RenderState = &render_state;
// Will project scissor/clipping rectangles into framebuffer space // Will project scissor/clipping rectangles into framebuffer space
@ -641,7 +659,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
// User callback, registered via ImDrawList::AddCallback() // User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, layout, command_buffer, rb, fb_width, fb_height);
else else
pcmd->UserCallback(draw_list, pcmd); pcmd->UserCallback(draw_list, pcmd);
last_desc_set = VK_NULL_HANDLE; last_desc_set = VK_NULL_HANDLE;
@ -671,7 +689,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
// Bind DescriptorSet with font or user texture // Bind DescriptorSet with font or user texture
VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID(); VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID();
if (desc_set != last_desc_set) if (desc_set != last_desc_set)
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr); vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, 1, &desc_set, 0, nullptr);
last_desc_set = desc_set; last_desc_set = desc_set;
// Draw // Draw
@ -928,22 +946,20 @@ static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAlloca
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
if (bd->ShaderModuleVert == VK_NULL_HANDLE) if (bd->ShaderModuleVert == VK_NULL_HANDLE)
{ {
VkShaderModuleCreateInfo default_vert_info = {}; VkShaderModuleCreateInfo vert_info = {};
default_vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
default_vert_info.codeSize = sizeof(__glsl_shader_vert_spv); vert_info.codeSize = sizeof(__glsl_shader_vert_spv);
default_vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv;
VkShaderModuleCreateInfo* p_vert_info = (v->CustomShaderVertCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) ? &v->CustomShaderVertCreateInfo : &default_vert_info; VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &bd->ShaderModuleVert);
VkResult err = vkCreateShaderModule(device, p_vert_info, allocator, &bd->ShaderModuleVert);
check_vk_result(err); check_vk_result(err);
} }
if (bd->ShaderModuleFrag == VK_NULL_HANDLE) if (bd->ShaderModuleFrag == VK_NULL_HANDLE)
{ {
VkShaderModuleCreateInfo default_frag_info = {}; VkShaderModuleCreateInfo frag_info = {};
default_frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
default_frag_info.codeSize = sizeof(__glsl_shader_frag_spv); frag_info.codeSize = sizeof(__glsl_shader_frag_spv);
default_frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv;
VkShaderModuleCreateInfo* p_frag_info = (v->CustomShaderFragCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) ? &v->CustomShaderFragCreateInfo : &default_frag_info; VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &bd->ShaderModuleFrag);
VkResult err = vkCreateShaderModule(device, p_frag_info, allocator, &bd->ShaderModuleFrag);
check_vk_result(err); check_vk_result(err);
} }
} }
@ -952,20 +968,93 @@ static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAlloca
typedef void VkPipelineRenderingCreateInfoKHR; typedef void VkPipelineRenderingCreateInfoKHR;
#endif #endif
static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, const ImGui_ImplVulkan_PipelineInfo* info) template <class T>
static T const& FirstValid(T const& a, T const& b)
{
return a ? a : b;
}
static VkPipelineLayout ImGui_ImplVulkan_CreatePipelineLayout(uint32_t customPushConstantSize, VkShaderStageFlags customPushConstantStages)
{ {
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_CreateShaderModules(device, allocator); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
// Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
VkPipelineLayoutCreateInfo layout_info = {};
VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout };
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = set_layout;
VkPushConstantRange push_constants[2] = {};
layout_info.pPushConstantRanges = push_constants;
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constants[0].offset = sizeof(float) * 0;
push_constants[0].size = sizeof(float) * 4;
layout_info.pushConstantRangeCount = 1;
if (customPushConstantSize)
{
layout_info.pushConstantRangeCount = 2;
push_constants[1].stageFlags = customPushConstantStages;
push_constants[1].offset = push_constants[0].size;
push_constants[1].size = customPushConstantSize;
}
VkPipelineLayout res = VK_NULL_HANDLE;
VkResult err = vkCreatePipelineLayout(bd->VulkanInitInfo.Device, &layout_info, v->Allocator, &res);
check_vk_result(err);
return res;
}
static VkPipelineLayout ImGui_ImplVulkan_GetPipelineLayout(ImGui_ImplVulkan_Data::CachedPipelineLayout& cache, uint32_t customPushConstantSize, VkShaderStageFlags customPushConstantStages)
{
if (cache.Handle != VK_NULL_HANDLE && (cache.CustomPushConstantSize != customPushConstantSize || cache.CustomPushConstantStages != customPushConstantStages))
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
vkDeviceWaitIdle(v->Device);
vkDestroyPipelineLayout(v->Device, cache.Handle, v->Allocator);
cache.Handle = VK_NULL_HANDLE;
}
if (!cache.Handle)
{
cache.CustomPushConstantSize = customPushConstantSize;
cache.CustomPushConstantStages = customPushConstantStages;
cache.Handle = ImGui_ImplVulkan_CreatePipelineLayout(customPushConstantSize, customPushConstantStages);
}
return cache.Handle;
}
static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, const ImGui_ImplVulkan_PipelineInfo* info, ImGui_ImplVulkan_Data::CachedPipelineLayout& pipelineLayout)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (!info->CustomShadersInfo.CustomShaderVert || !info->CustomShadersInfo.CustomShaderFrag)
{
ImGui_ImplVulkan_CreateShaderModules(device, allocator);
}
VkPipelineShaderStageCreateInfo stage[2] = {}; VkPipelineShaderStageCreateInfo stage[2] = {};
stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
stage[0].module = bd->ShaderModuleVert; stage[0].module = FirstValid(info->CustomShadersInfo.CustomShaderVert, bd->ShaderModuleVert);
stage[0].pName = "main"; stage[0].pName = "main";
if (info->CustomShadersInfo.CustomShaderVert)
{
stage[0].pSpecializationInfo = info->CustomShadersInfo.SpecializationInfoVert;
}
stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
stage[1].module = bd->ShaderModuleFrag; stage[1].module = FirstValid(info->CustomShadersInfo.CustomShaderFrag, bd->ShaderModuleFrag);
stage[1].pName = "main"; stage[1].pName = "main";
if (info->CustomShadersInfo.CustomShaderFrag)
{
stage[1].pSpecializationInfo = info->CustomShadersInfo.SpecializationInfoFrag;
}
VkVertexInputBindingDescription binding_desc[1] = {}; VkVertexInputBindingDescription binding_desc[1] = {};
binding_desc[0].stride = sizeof(ImDrawVert); binding_desc[0].stride = sizeof(ImDrawVert);
@ -1049,7 +1138,7 @@ static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAlloc
create_info.pDepthStencilState = &depth_info; create_info.pDepthStencilState = &depth_info;
create_info.pColorBlendState = &blend_info; create_info.pColorBlendState = &blend_info;
create_info.pDynamicState = &dynamic_state; create_info.pDynamicState = &dynamic_state;
create_info.layout = bd->PipelineLayout; create_info.layout = ImGui_ImplVulkan_GetPipelineLayout(pipelineLayout, info->CustomShadersInfo.PushConstantSize, info->CustomShadersInfo.PushConstantStages);
create_info.renderPass = info->RenderPass; create_info.renderPass = info->RenderPass;
create_info.subpass = info->Subpass; create_info.subpass = info->Subpass;
@ -1121,24 +1210,6 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
check_vk_result(err); check_vk_result(err);
} }
if (!bd->PipelineLayout)
{
// Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
VkPushConstantRange push_constants[1] = {};
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constants[0].offset = sizeof(float) * 0;
push_constants[0].size = sizeof(float) * 4;
VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout };
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants;
err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &bd->PipelineLayout);
check_vk_result(err);
}
// Create pipeline // Create pipeline
bool create_main_pipeline = (v->PipelineInfoMain.RenderPass != VK_NULL_HANDLE); bool create_main_pipeline = (v->PipelineInfoMain.RenderPass != VK_NULL_HANDLE);
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
@ -1195,7 +1266,13 @@ void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_PipelineInfo* pi
pipeline_rendering_create_info->pColorAttachmentFormats = bd->PipelineRenderingCreateInfoColorAttachmentFormats.Data; pipeline_rendering_create_info->pColorAttachmentFormats = bd->PipelineRenderingCreateInfoColorAttachmentFormats.Data;
} }
#endif #endif
bd->Pipeline = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, pipeline_info); bd->Pipeline = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, pipeline_info, bd->PipelineLayout);
}
VkPipelineLayout ImGui_ImplVulkan_GetMainPipelineLayout()
{
ImGui_ImplVulkan_Data * bd = ImGui_ImplVulkan_GetBackendData();
return bd->PipelineLayout.Handle;
} }
void ImGui_ImplVulkan_DestroyDeviceObjects() void ImGui_ImplVulkan_DestroyDeviceObjects()
@ -1215,9 +1292,11 @@ void ImGui_ImplVulkan_DestroyDeviceObjects()
if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; }
if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; }
if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; }
if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } if (bd->PipelineLayout.Handle){ vkDestroyPipelineLayout(v->Device, bd->PipelineLayout.Handle, v->Allocator); bd->PipelineLayout.Handle = VK_NULL_HANDLE; }
if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; }
if (bd->PipelineLayoutForViewports.Handle) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayoutForViewports.Handle, v->Allocator); bd->PipelineLayoutForViewports.Handle = VK_NULL_HANDLE; }
if (bd->PipelineForViewports) { vkDestroyPipeline(v->Device, bd->PipelineForViewports, v->Allocator); bd->PipelineForViewports = VK_NULL_HANDLE; } if (bd->PipelineForViewports) { vkDestroyPipeline(v->Device, bd->PipelineForViewports, v->Allocator); bd->PipelineForViewports = VK_NULL_HANDLE; }
if (bd->PipelineInfoForViewports.RenderPass) { vkDestroyRenderPass(v->Device, bd->PipelineInfoForViewports.RenderPass, v->Allocator); bd->PipelineInfoForViewports.RenderPass = VK_NULL_HANDLE; }
if (bd->DescriptorPool) { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; } if (bd->DescriptorPool) { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; }
} }
@ -1280,6 +1359,24 @@ bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(
return true; return true;
} }
void ImGui_ImplVulkan_SanitizeCustomShadersInfo(ImGui_ImplVulkan_CustomShadersInfo & info)
{
if (!info.CustomShaderVert)
{
info.PushConstantStages &= ~VK_SHADER_STAGE_VERTEX_BIT;
info.SpecializationInfoVert = nullptr;
}
if (!info.CustomShaderFrag)
{
info.PushConstantStages &= ~VK_SHADER_STAGE_FRAGMENT_BIT;
info.SpecializationInfoFrag = nullptr;
}
if (!info.CustomShaderVert && !info.CustomShaderFrag)
{
info.PushConstantSize = 0;
}
}
bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
{ {
IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!");
@ -1324,10 +1421,12 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
else else
IM_ASSERT(info->DescriptorPoolSize > 0); IM_ASSERT(info->DescriptorPoolSize > 0);
if (info->UseDynamicRendering) if (info->UseDynamicRendering)
IM_ASSERT(info->PipelineInfoMain.RenderPass == VK_NULL_HANDLE && info->PipelineInfoForViewports.RenderPass == VK_NULL_HANDLE); IM_ASSERT(info->PipelineInfoMain.RenderPass == VK_NULL_HANDLE);
bd->VulkanInitInfo = *info; bd->VulkanInitInfo = *info;
ImGui_ImplVulkan_SanitizeCustomShadersInfo(bd->VulkanInitInfo.PipelineInfoMain.CustomShadersInfo);
VkPhysicalDeviceProperties properties; VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(info->PhysicalDevice, &properties); vkGetPhysicalDeviceProperties(info->PhysicalDevice, &properties);
bd->NonCoherentAtomSize = properties.limits.nonCoherentAtomSize; bd->NonCoherentAtomSize = properties.limits.nonCoherentAtomSize;
@ -1457,6 +1556,16 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk
buffers->Count = 0; buffers->Count = 0;
} }
void ImGui_ImplVulkan_SetSecondaryViewportsOptions(const ImGui_ImplVulkan_SecondaryViewportsInfo* info)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
// Will trigger a call to ImGui_ImplVulkan_PrepareViewportsRendering()
bd->ViewportsFormat = {};
v->SecondaryViewportsInfo = *info;
}
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Internal / Miscellaneous Vulkan Helpers // Internal / Miscellaneous Vulkan Helpers
// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.) // (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
@ -1646,6 +1755,45 @@ int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_m
return 1; return 1;
} }
VkRenderPass ImGui_ImplVulkanH_CreateRenderPass(VkDevice device, const VkAllocationCallbacks* allocator, VkFormat format, bool clear)
{
VkAttachmentDescription attachment = {};
attachment.format = format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference color_attachment = {};
color_attachment.attachment = 0;
color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
info.attachmentCount = 1;
info.pAttachments = &attachment;
info.subpassCount = 1;
info.pSubpasses = &subpass;
info.dependencyCount = 1;
info.pDependencies = &dependency;
VkRenderPass res;
VkResult err = vkCreateRenderPass(device, &info, allocator, &res);
check_vk_result(err);
return res;
}
// Also destroy old swap chain and in-flight frames data, if any. // Also destroy old swap chain and in-flight frames data, if any.
void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage) void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage)
{ {
@ -1734,43 +1882,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V
// Create the Render Pass // Create the Render Pass
if (wd->UseDynamicRendering == false) if (wd->UseDynamicRendering == false)
{ {
VkAttachmentDescription attachment = {}; wd->RenderPass = ImGui_ImplVulkanH_CreateRenderPass(device, allocator, wd->SurfaceFormat.format, wd->ClearEnable);
attachment.format = wd->SurfaceFormat.format;
attachment.samples = VK_SAMPLE_COUNT_1_BIT;
attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference color_attachment = {};
color_attachment.attachment = 0;
color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkRenderPassCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
info.attachmentCount = 1;
info.pAttachments = &attachment;
info.subpassCount = 1;
info.pSubpasses = &subpass;
info.dependencyCount = 1;
info.pDependencies = &dependency;
err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass);
check_vk_result(err);
// We do not create a pipeline by default as this is also used by examples' main.cpp,
// but secondary viewport in multi-viewport mode may want to create one with:
//ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, v->Subpass);
} }
// Create The Image Views // Create The Image Views
@ -1960,6 +2072,90 @@ ImGui_ImplVulkanH_Window* ImGui_ImplVulkanH_GetWindowDataFromViewport(ImGuiViewp
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//-------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------
// Prepare common viewports rendering objects.
// Requires a sample surface (assuming any viewport surface behaves the same).
// - Select a surface format
// - Create the common RenderPass and Pipeline
static void ImGui_ImplVulkan_PrepareViewportsRendering(VkSurfaceKHR surface)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
// Select Surface Format
const VkFormat request_surface_formats[] = { v->SecondaryViewportsInfo.DesiredFormat.format, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
const VkFormat* p_request_surface_formats = request_surface_formats;
uint32_t request_surface_formats_count = IM_ARRAYSIZE(request_surface_formats);
if (v->SecondaryViewportsInfo.DesiredFormat.format == VK_FORMAT_UNDEFINED)
{
++p_request_surface_formats;
--request_surface_formats_count;
}
const VkColorSpaceKHR requestSurfaceColorSpace = v->SecondaryViewportsInfo.DesiredFormat.colorSpace;
bd->ViewportsFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(v->PhysicalDevice, surface, p_request_surface_formats, request_surface_formats_count, requestSurfaceColorSpace);
ImGui_ImplVulkan_PipelineInfo* pipeline_info = &bd->PipelineInfoForViewports;
pipeline_info->MSAASamples = VK_SAMPLE_COUNT_1_BIT;
if (v->UseDynamicRendering)
{
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
pipeline_info->PipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
pipeline_info->PipelineRenderingCreateInfo.colorAttachmentCount = 1;
pipeline_info->PipelineRenderingCreateInfo.pColorAttachmentFormats = &bd->ViewportsFormat.format;
#endif
}
else
{
// Create a reference RenderPass
// Viewports will create their own RenderPass, compatible with this one (same format, different clear option)
if (pipeline_info->RenderPass)
{
vkDeviceWaitIdle(v->Device);
vkDestroyRenderPass(v->Device, pipeline_info->RenderPass, v->Allocator);
}
pipeline_info->RenderPass = ImGui_ImplVulkanH_CreateRenderPass(v->Device, v->Allocator, bd->ViewportsFormat.format, true);
pipeline_info->Subpass = 0;
}
if (v->SecondaryViewportsInfo.GetCustomShadersInfo)
{
pipeline_info->CustomShadersInfo = v->SecondaryViewportsInfo.GetCustomShadersInfo(v->SecondaryViewportsInfo.UserData, bd->ViewportsFormat);
ImGui_ImplVulkan_SanitizeCustomShadersInfo(pipeline_info->CustomShadersInfo);
}
else
{
memset(&pipeline_info->CustomShadersInfo, 0, sizeof(ImGui_ImplVulkan_CustomShadersInfo));
}
// Create pipeline (shared by all secondary viewports)
if (bd->PipelineForViewports)
{
vkDeviceWaitIdle(v->Device);
vkDestroyPipeline(v->Device, bd->PipelineForViewports, v->Allocator);
}
bd->PipelineForViewports = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, VK_NULL_HANDLE, pipeline_info, bd->PipelineLayoutForViewports);
}
static void ImGui_ImplVulkan_SelectPresentMode(ImGuiViewport* viewport)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData;
ImGui_ImplVulkanH_Window* wd = &vd->Window;
wd->DesiredPresentMode = v->SecondaryViewportsInfo.DesiredPresentMode;
// FIXME-VULKAN: Even thought mailbox seems to get us maximum framerate with a single window, it halves framerate with a second window etc. (w/ Nvidia and SDK 1.82.1)
VkPresentModeKHR present_modes[] = { wd->DesiredPresentMode, VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
const VkPresentModeKHR* p_present_modes = present_modes;
uint32_t present_modes_count = IM_ARRAYSIZE(present_modes);
if (wd->DesiredPresentMode == VK_PRESENT_MODE_MAX_ENUM_KHR)
{
++p_present_modes;
--present_modes_count;
}
wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(v->PhysicalDevice, wd->Surface, p_present_modes, present_modes_count);
//printf("[vulkan] Secondary window selected PresentMode = %d\n", wd->PresentMode);
}
static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport)
{ {
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
@ -1990,49 +2186,21 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport)
} }
viewport->RendererUserData = vd; viewport->RendererUserData = vd;
// Select Surface Format if (bd->ViewportsFormat.format == VK_FORMAT_UNDEFINED)
ImGui_ImplVulkan_PipelineInfo* pipeline_info = &v->PipelineInfoForViewports; {
ImVector<VkFormat> requestSurfaceImageFormats; ImGui_ImplVulkan_PrepareViewportsRendering(wd->Surface);
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING }
for (uint32_t n = 0; n < pipeline_info->PipelineRenderingCreateInfo.colorAttachmentCount; n++) wd->SurfaceFormat = bd->ViewportsFormat;
requestSurfaceImageFormats.push_back(pipeline_info->PipelineRenderingCreateInfo.pColorAttachmentFormats[n]); wd->DesiredSurfaceFormat = v->SecondaryViewportsInfo.DesiredFormat;
#endif
const VkFormat defaultFormats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
for (VkFormat format : defaultFormats)
requestSurfaceImageFormats.push_back(format);
const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(v->PhysicalDevice, wd->Surface, requestSurfaceImageFormats.Data, (size_t)requestSurfaceImageFormats.Size, requestSurfaceColorSpace);
// Select Present Mode // Select Present Mode
// FIXME-VULKAN: Even thought mailbox seems to get us maximum framerate with a single window, it halves framerate with a second window etc. (w/ Nvidia and SDK 1.82.1) ImGui_ImplVulkan_SelectPresentMode(viewport);
VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(v->PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
//printf("[vulkan] Secondary window selected PresentMode = %d\n", wd->PresentMode);
// Create SwapChain, RenderPass, Framebuffer, etc. // Create SwapChain, RenderPass, Framebuffer, etc.
wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true;
wd->UseDynamicRendering = v->UseDynamicRendering; wd->UseDynamicRendering = v->UseDynamicRendering;
ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount, pipeline_info->SwapChainImageUsage); ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount, v->SecondaryViewportsInfo.SwapChainImageUsage);
vd->WindowOwned = true; vd->WindowOwned = true;
// Create pipeline (shared by all secondary viewports)
if (bd->PipelineForViewports == VK_NULL_HANDLE)
{
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
if (wd->UseDynamicRendering)
{
pipeline_info->PipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
pipeline_info->PipelineRenderingCreateInfo.colorAttachmentCount = 1;
pipeline_info->PipelineRenderingCreateInfo.pColorAttachmentFormats = &wd->SurfaceFormat.format;
}
else
{
pipeline_info->RenderPass = wd->RenderPass;
}
#endif
bd->PipelineForViewports = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, VK_NULL_HANDLE, &v->PipelineInfoForViewports);
}
} }
static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport)
@ -2058,10 +2226,10 @@ static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
return; return;
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
vd->Window.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; vd->Window.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true;
ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount, v->PipelineInfoForViewports.SwapChainImageUsage); ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount, v->SecondaryViewportsInfo.SwapChainImageUsage);
} }
static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void* custom_push_constant_data)
{ {
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData;
@ -2071,9 +2239,26 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*)
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
VkResult err; VkResult err;
if (vd->SwapChainNeedRebuild || vd->SwapChainSuboptimal) bool create_or_resize = vd->SwapChainNeedRebuild || vd->SwapChainSuboptimal;
if (wd->DesiredPresentMode != v->SecondaryViewportsInfo.DesiredPresentMode)
{ {
ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount, v->PipelineInfoForViewports.SwapChainImageUsage); ImGui_ImplVulkan_SelectPresentMode(viewport);
create_or_resize |= true;
}
if (bd->ViewportsFormat.format == VK_FORMAT_UNDEFINED)
{
ImGui_ImplVulkan_PrepareViewportsRendering(wd->Surface);
create_or_resize |= true;
}
create_or_resize |= memcmp(&wd->DesiredSurfaceFormat, &v->SecondaryViewportsInfo.DesiredFormat, sizeof(VkSurfaceFormatKHR)) != 0;
if (create_or_resize)
{
wd->SurfaceFormat = bd->ViewportsFormat;
wd->DesiredSurfaceFormat = v->SecondaryViewportsInfo.DesiredFormat;
ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount, v->SecondaryViewportsInfo.SwapChainImageUsage);
vd->SwapChainNeedRebuild = vd->SwapChainSuboptimal = false; vd->SwapChainNeedRebuild = vd->SwapChainSuboptimal = false;
} }
@ -2163,6 +2348,11 @@ static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*)
} }
} }
if (custom_push_constant_data && bd->PipelineInfoForViewports.CustomShadersInfo.PushConstantSize > 0)
{
vkCmdPushConstants(fd->CommandBuffer, bd->PipelineLayoutForViewports.Handle, bd->PipelineInfoForViewports.CustomShadersInfo.PushConstantStages, 4 * sizeof(float), bd->PipelineInfoForViewports.CustomShadersInfo.PushConstantSize, custom_push_constant_data);
}
ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, bd->PipelineForViewports); ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, bd->PipelineForViewports);
{ {

View file

@ -75,7 +75,25 @@
// Backend uses a small number of descriptors per font atlas + as many as additional calls done to ImGui_ImplVulkan_AddTexture(). // Backend uses a small number of descriptors per font atlas + as many as additional calls done to ImGui_ImplVulkan_AddTexture().
#define IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE (8) // Minimum per atlas #define IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE (8) // Minimum per atlas
// Specify settings to create pipeline and swapchain struct ImGui_ImplVulkan_CustomShadersInfo
{
// (Optional) Customize default vertex/fragment shaders if not VK_NULL_HANDLE, otherwise we use defaults.
// - Shader inputs/outputs need to match ours.
// - Specialization used only if the relevant custom shader is provided
VkShaderModule CustomShaderVert;
VkSpecializationInfo* SpecializationInfoVert;
VkShaderModule CustomShaderFrag;
VkSpecializationInfo* SpecializationInfoFrag;
// Optional if any of the custom shaders is used
// The vertex shader already uses push constant range [0, 4 * sizeof(float)[
// If PushConstantSize is non zero, it will be registered at offset 4 * sizeof(float)
uint32_t PushConstantSize;
VkShaderStageFlags PushConstantStages;
};
// Specify settings to create the main rendering pipeline
struct ImGui_ImplVulkan_PipelineInfo struct ImGui_ImplVulkan_PipelineInfo
{ {
// For Main viewport only // For Main viewport only
@ -88,8 +106,27 @@ struct ImGui_ImplVulkan_PipelineInfo
VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Optional, valid if .sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Optional, valid if .sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR
#endif #endif
// For Secondary viewports only (created/managed by backend) // Optional
VkImageUsageFlags SwapChainImageUsage; // Extra flags for vkCreateSwapchainKHR() calls for secondary viewports. We automatically add VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT. You can add e.g. VK_IMAGE_USAGE_TRANSFER_SRC_BIT if you need to capture from viewports. ImGui_ImplVulkan_CustomShadersInfo CustomShadersInfo;
};
struct ImGui_ImplVulkan_SecondaryViewportsInfo
{
// Ignored if .format == VK_FORMAT_UNDEFINED
VkSurfaceFormatKHR DesiredFormat;
// Ignored if set to VK_PRESENT_MODE_MAX_ENUM_KHR
// The default zero initialized value is VK_PRESENT_MODE_IMMEDIATE_KHR!
VkPresentModeKHR DesiredPresentMode;
// Extra flags for vkCreateSwapchainKHR() calls for secondary viewports. We automatically add VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT. You can add e.g. VK_IMAGE_USAGE_TRANSFER_SRC_BIT if you need to capture from viewports.
VkImageUsageFlags SwapChainImageUsage;
// Optional
typedef ImGui_ImplVulkan_CustomShadersInfo (*GetCustomShaderCallback)(const void*, VkSurfaceFormatKHR);
// If provided, called back to optionaly provide viewports custom shaders based on their surface format
GetCustomShaderCallback GetCustomShadersInfo;
// User data for GetCustomShaderCallback
const void* UserData;
}; };
// Initialization data, for ImGui_ImplVulkan_Init() // Initialization data, for ImGui_ImplVulkan_Init()
@ -116,11 +153,12 @@ struct ImGui_ImplVulkan_InitInfo
// Pipeline // Pipeline
ImGui_ImplVulkan_PipelineInfo PipelineInfoMain; // Infos for Main Viewport (created by app/user) ImGui_ImplVulkan_PipelineInfo PipelineInfoMain; // Infos for Main Viewport (created by app/user)
ImGui_ImplVulkan_PipelineInfo PipelineInfoForViewports; // Infos for Secondary Viewports (created by backend)
//VkRenderPass RenderPass; // --> Since 2025/09/26: set 'PipelineInfoMain.RenderPass' instead //VkRenderPass RenderPass; // --> Since 2025/09/26: set 'PipelineInfoMain.RenderPass' instead
//uint32_t Subpass; // --> Since 2025/09/26: set 'PipelineInfoMain.Subpass' instead //uint32_t Subpass; // --> Since 2025/09/26: set 'PipelineInfoMain.Subpass' instead
//VkSampleCountFlagBits MSAASamples; // --> Since 2025/09/26: set 'PipelineInfoMain.MSAASamples' instead //VkSampleCountFlagBits MSAASamples; // --> Since 2025/09/26: set 'PipelineInfoMain.MSAASamples' instead
//VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Since 2025/09/26: set 'PipelineInfoMain.PipelineRenderingCreateInfo' instead //VkPipelineRenderingCreateInfoKHR PipelineRenderingCreateInfo; // Since 2025/09/26: set 'PipelineInfoMain.PipelineRenderingCreateInfo' instead
// (Optional) Secondary viewports init info
ImGui_ImplVulkan_SecondaryViewportsInfo SecondaryViewportsInfo;
// (Optional) Dynamic Rendering // (Optional) Dynamic Rendering
// Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3 + setup PipelineInfoMain.PipelineRenderingCreateInfo and PipelineInfoViewports.PipelineRenderingCreateInfo. // Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3 + setup PipelineInfoMain.PipelineRenderingCreateInfo and PipelineInfoViewports.PipelineRenderingCreateInfo.
@ -130,25 +168,20 @@ struct ImGui_ImplVulkan_InitInfo
const VkAllocationCallbacks* Allocator; const VkAllocationCallbacks* Allocator;
void (*CheckVkResultFn)(VkResult err); 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.
// (Optional) Customize default vertex/fragment shaders.
// - if .sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO we use specified structs, otherwise we use defaults.
// - Shader inputs/outputs need to match ours. Code/data pointed to by the structure needs to survive for whole during of backend usage.
VkShaderModuleCreateInfo CustomShaderVertCreateInfo;
VkShaderModuleCreateInfo CustomShaderFragCreateInfo;
}; };
// Follow "Getting Started" link and check examples/ folder to learn about using backends! // Follow "Getting Started" link and check examples/ folder to learn about using backends!
IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info); IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info);
IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown();
IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); 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_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE, VkPipelineLayout layout = 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) 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) // (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)) // 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. // Else, the pipeline can be created, or re-created, using ImGui_ImplVulkan_CreateMainPipeline() before rendering.
IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_PipelineInfo* info); IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_PipelineInfo* info);
IMGUI_IMPL_API VkPipelineLayout ImGui_ImplVulkan_GetMainPipelineLayout();
// (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. // (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); IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex);
@ -163,6 +196,10 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet d
// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES // This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES
IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr); IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr);
// (Advanced) If you want to control secondary viewports without reinitializing the backend
// Secondary viewports equivalent of ImGui_ImplVulkan_CreateMainPipeline()
IMGUI_IMPL_API void ImGui_ImplVulkan_SetSecondaryViewportsOptions(const ImGui_ImplVulkan_SecondaryViewportsInfo* info);
// [BETA] Selected render state data shared with callbacks. // [BETA] Selected render state data shared with callbacks.
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplVulkan_RenderDrawData() call. // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplVulkan_RenderDrawData() call.
// (Please open an issue if you feel you need access to more data) // (Please open an issue if you feel you need access to more data)
@ -236,7 +273,9 @@ struct ImGui_ImplVulkanH_Window
VkSwapchainKHR Swapchain; VkSwapchainKHR Swapchain;
VkSurfaceKHR Surface; VkSurfaceKHR Surface;
VkSurfaceFormatKHR SurfaceFormat; VkSurfaceFormatKHR SurfaceFormat;
VkSurfaceFormatKHR DesiredSurfaceFormat;
VkPresentModeKHR PresentMode; VkPresentModeKHR PresentMode;
VkPresentModeKHR DesiredPresentMode;
VkRenderPass RenderPass; VkRenderPass RenderPass;
bool UseDynamicRendering; bool UseDynamicRendering;
bool ClearEnable; bool ClearEnable;