Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 132 additions & 17 deletions backends/imgui_impl_vulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ static PFN_vkCmdBeginRenderingKHR ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR;
static PFN_vkCmdEndRenderingKHR ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR;
#endif

#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
static PFN_vkCmdPushDescriptorSetKHR ImGuiImplVulkanFuncs_vkCmdPushDescriptorSetKHR;
#endif

// Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData()
// [Please zero-clear before use!]
struct ImGui_ImplVulkan_FrameRenderBuffers
Expand Down Expand Up @@ -293,7 +297,7 @@ struct ImGui_ImplVulkan_ViewportData
bool SwapChainSuboptimal; // Flag when VK_SUBOPTIMAL_KHR was returned.

ImGui_ImplVulkan_ViewportData() { WindowOwned = SwapChainNeedRebuild = SwapChainSuboptimal = false; memset((void*)&RenderBuffers, 0, sizeof(RenderBuffers)); }
~ImGui_ImplVulkan_ViewportData() { }
~ImGui_ImplVulkan_ViewportData() {}
};

// Vulkan data
Expand Down Expand Up @@ -565,9 +569,9 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
}

// Draw callbacks
static void ImGui_ImplVulkan_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way.
static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr); }
static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr); }
static void ImGui_ImplVulkan_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way.
static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr); }
static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr); }

// Render function
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline)
Expand Down Expand Up @@ -660,6 +664,9 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
VkDescriptorSet last_image_view = VK_NULL_HANDLE;
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
VkImageView last_push_image_view = VK_NULL_HANDLE;
#endif
int global_vtx_offset = 0;
int global_idx_offset = 0;
for (const ImDrawList* draw_list : draw_data->CmdLists)
Expand All @@ -674,6 +681,9 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
{
ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height);
last_image_view = VK_NULL_HANDLE;
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
last_push_image_view = VK_NULL_HANDLE;
#endif
}
else
pcmd->UserCallback(draw_list, pcmd);
Expand All @@ -700,11 +710,41 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y);
vkCmdSetScissor(command_buffer, 0, 1, &scissor);

// PushDescriptorSets for image view(font or user texture)
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
if (bd->VulkanInitInfo.UsePushDescriptorSet)
{
VkImageView image_view = (VkImageView)pcmd->GetTexID();
if (image_view != last_push_image_view)
{
// The user application is strictly responsible for transitioning the
// image layout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL prior to rendering.
VkDescriptorImageInfo image_info = {};
image_info.imageView = image_view;
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;

VkWriteDescriptorSet write_desc[1] = {};
write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_desc[0].dstSet = 0;
write_desc[0].dstBinding = 0;
write_desc[0].descriptorCount = 1;
write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
write_desc[0].pImageInfo = &image_info;

if (ImGuiImplVulkanFuncs_vkCmdPushDescriptorSetKHR)
ImGuiImplVulkanFuncs_vkCmdPushDescriptorSetKHR(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, write_desc);
}
last_push_image_view = image_view;
}
else
#endif
// Bind DescriptorSets for image view (font or user texture) and samplers
VkDescriptorSet image_view = (VkDescriptorSet)pcmd->GetTexID();
if (image_view != last_image_view)
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &image_view, 0, nullptr);
last_image_view = image_view;
{
VkDescriptorSet image_view = (VkDescriptorSet)pcmd->GetTexID();
if (image_view != last_image_view)
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &image_view, 0, nullptr);
last_image_view = image_view;
}

// Draw
vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
Expand All @@ -730,10 +770,20 @@ static void ImGui_ImplVulkan_DestroyTexture(ImTextureData* tex)
{
if (ImGui_ImplVulkan_Texture* backend_tex = (ImGui_ImplVulkan_Texture*)tex->BackendUserData)
{
IM_ASSERT(backend_tex->DescriptorSet == (VkDescriptorSet)tex->TexID);
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
if (bd->VulkanInitInfo.UsePushDescriptorSet)
{
IM_ASSERT(backend_tex->ImageView == (VkImageView)tex->TexID);
}
else
#endif
{
IM_ASSERT(backend_tex->DescriptorSet == (VkDescriptorSet)tex->TexID);
ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet);
}
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet);

vkDestroyImageView(v->Device, backend_tex->ImageView, v->Allocator);
vkDestroyImage(v->Device, backend_tex->Image, v->Allocator);
vkFreeMemory(v->Device, backend_tex->Memory, v->Allocator);
Expand Down Expand Up @@ -806,11 +856,20 @@ void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex)
check_vk_result(err);
}

// Create the Descriptor Set
backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
if (bd->VulkanInitInfo.UsePushDescriptorSet)
{
tex->SetTexID((ImTextureID)backend_tex->ImageView);
}
else
#endif
{
// Create the Descriptor Set
backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

// Store identifiers
tex->SetTexID((ImTextureID)backend_tex->DescriptorSet);
// Store identifiers
tex->SetTexID((ImTextureID)backend_tex->DescriptorSet);
}
tex->BackendUserData = backend_tex;
}

Expand Down Expand Up @@ -1115,6 +1174,11 @@ static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDescriptorSetLayout* p_
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
info.bindingCount = 1;
info.pBindings = binding;
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
if (bd->VulkanInitInfo.UsePushDescriptorSet && descriptor_type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)
info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
#endif

VkResult err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, p_layout);
check_vk_result(err);
}
Expand Down Expand Up @@ -1159,7 +1223,12 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()

if (v->DescriptorPoolSize != 0)
{
IM_ASSERT(v->DescriptorPoolSize >= IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE);
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
if (!bd->VulkanInitInfo.UsePushDescriptorSet)
#endif
{
IM_ASSERT(v->DescriptorPoolSize >= IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE);
}
VkDescriptorPoolSize pool_sizes[] =
{
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, v->DescriptorPoolSize },
Expand Down Expand Up @@ -1323,6 +1392,21 @@ static void ImGui_ImplVulkan_LoadDynamicRenderingFunctions(uint32_t api_version,
}
#endif

#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
static void ImGui_ImplVulkan_LoadPushDescriptorSetFunction(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data)
{
IM_UNUSED(api_version);

ImGuiImplVulkanFuncs_vkCmdPushDescriptorSetKHR = reinterpret_cast<PFN_vkCmdPushDescriptorSetKHR>(loader_func("vkCmdPushDescriptorSet", user_data));

// - Fallback to KHR versions if core not available (this will work if KHR extension is available and enabled and also the device supports push descriptors)
if (ImGuiImplVulkanFuncs_vkCmdPushDescriptorSetKHR == nullptr)
{
ImGuiImplVulkanFuncs_vkCmdPushDescriptorSetKHR = reinterpret_cast<PFN_vkCmdPushDescriptorSetKHR>(loader_func("vkCmdPushDescriptorSetKHR", user_data));
}
}
#endif

// If unspecified by user, assume that ApiVersion == HeaderVersion
// We don't care about other versions than 1.3 for our checks, so don't need to make this exhaustive (e.g. with all #ifdef VK_VERSION_1_X checks)
static uint32_t ImGui_ImplVulkan_GetDefaultApiVersion()
Expand Down Expand Up @@ -1352,7 +1436,11 @@ bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(
#undef IMGUI_VULKAN_FUNC_LOAD

#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
ImGui_ImplVulkan_LoadDynamicRenderingFunctions(api_version, loader_func, user_data);
ImGui_ImplVulkan_LoadDynamicRenderingFunctions(api_version, loader_func, user_data);
#endif

#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
ImGui_ImplVulkan_LoadPushDescriptorSetFunction(api_version, loader_func, user_data);
#endif
#else
IM_UNUSED(loader_func);
Expand Down Expand Up @@ -1383,6 +1471,18 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
#endif
}

if (info->UsePushDescriptorSet)
{
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
#ifndef IMGUI_IMPL_VULKAN_USE_LOADER
ImGui_ImplVulkan_LoadPushDescriptorSetFunction(info->ApiVersion, [](const char* function_name, void* user_data) { return vkGetDeviceProcAddr((VkDevice)user_data, function_name); }, (void*)info->Device);
#endif
IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdPushDescriptorSetKHR != nullptr);
#else
IM_ASSERT(0 && "Can't use Push Descriptors when neither VK_VERSION_1_4 or VK_KHR_push_descriptor is defined.");
#endif
}

ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
Expand Down Expand Up @@ -1487,6 +1587,13 @@ VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkImageView image_view, VkImageLayou
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;

#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
IM_ASSERT(v->UsePushDescriptorSet == false && "ImGui_ImplVulkan_AddTexture() is not needed and should not be called when UsePushDescriptorSet is enabled. Simply cast your VkImageView to ImTextureID directly.");
if (v->UsePushDescriptorSet)
return VK_NULL_HANDLE;
#endif

VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;

// Create Descriptor Set:
Expand Down Expand Up @@ -1530,10 +1637,18 @@ void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;

#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
IM_ASSERT(v->UsePushDescriptorSet == false && "ImGui_ImplVulkan_RemoveTexture() should not be called when UsePushDescriptorSet is enabled.");
if (v->UsePushDescriptorSet)
return;
#endif

VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;
vkFreeDescriptorSets(v->Device, pool, 1, &descriptor_set);
}


void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator)
{
if (buffers->VertexBuffer) { vkDestroyBuffer(device, buffers->VertexBuffer, allocator); buffers->VertexBuffer = VK_NULL_HANDLE; }
Expand Down Expand Up @@ -2065,7 +2180,7 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport)
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
VkResult err = (VkResult)platform_io.Platform_CreateVkSurface(viewport, (ImU64)v->Instance, (const void*)v->Allocator, (ImU64*)&wd->Surface);
check_vk_result(err);

// Check if surface creation failed
if (err != VK_SUCCESS || wd->Surface == VK_NULL_HANDLE)
{
Expand Down
11 changes: 11 additions & 0 deletions backends/imgui_impl_vulkan.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
#endif

#if defined(VK_VERSION_1_4) || defined(VK_KHR_push_descriptor)
#define IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR_SET
#endif

// 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_SAMPLED_IMAGE_POOL_SIZE (8) // Minimum per atlas
#define IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE (2) // Minimum for linear + nearest
Expand Down Expand Up @@ -134,6 +138,12 @@ struct ImGui_ImplVulkan_InitInfo
// Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3 + setup PipelineInfoMain.PipelineRenderingCreateInfo and PipelineInfoViewports.PipelineRenderingCreateInfo.
bool UseDynamicRendering;

// (Optional) PushDescriptorSet
// Need to explicitly enable VK_KHR_push_descriptor extension to use this.
// When enabled, you must pass VkImageView as ImTextureID instead of VkDescriptorSet,
// eliminating the need for descriptor pool allocations for textures.
bool UsePushDescriptorSet;

// (Optional) Allocation, Debugging
const VkAllocationCallbacks* Allocator;
void (*CheckVkResultFn)(VkResult err);
Expand Down Expand Up @@ -162,6 +172,7 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_
IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex);

// Register a texture (VkDescriptorSet for a VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE == ImTextureID)
// (Note: If using InitInfo.UsePushDescriptorSet, this function is NOT needed. You can directly pass your VkImageView casted to ImTextureID.)
IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkImageView image_view, VkImageLayout image_layout);
IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);

Expand Down