1
0
Fork 0
mirror of https://github.com/ocornut/imgui.git synced 2026-01-28 02:50:06 +00:00

Implement VK_KHR_push_descriptor support for the Vulkan backend

This commit is contained in:
maverikou 2025-09-22 07:51:03 +03:00
parent 1c544ee941
commit ac9e153dd0
2 changed files with 113 additions and 17 deletions

View file

@ -213,6 +213,10 @@ static PFN_vkCmdBeginRenderingKHR ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR;
static PFN_vkCmdEndRenderingKHR ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR;
#endif
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR
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
@ -239,7 +243,7 @@ struct ImGui_ImplVulkan_Texture
VkDeviceMemory Memory;
VkImage Image;
VkImageView ImageView;
VkDescriptorSet DescriptorSet;
VkDescriptorSet DescriptorSet; // Not used if push descriptor is enabled
ImGui_ImplVulkan_Texture() { memset((void*)this, 0, sizeof(*this)); }
};
@ -502,6 +506,27 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
}
}
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR
static void ImGui_ImplVulkan_PushTextureDescriptor(VkCommandBuffer command_buffer, VkImageView view, VkSampler sampler, VkPipelineLayout pipeline_layout)
{
VkDescriptorImageInfo imageInfo = {};
imageInfo.sampler = sampler;
imageInfo.imageView = view;
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkWriteDescriptorSet writeDescSet = {};
writeDescSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescSet.dstSet = VK_NULL_HANDLE;
writeDescSet.dstBinding = 0;
writeDescSet.descriptorCount = 1;
writeDescSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeDescSet.pImageInfo = &imageInfo;
VkWriteDescriptorSet writes[] = { writeDescSet };
ImGuiImplVulkanFuncs_vkCmdPushDescriptorSetKHR(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, writes);
}
#endif
// Render function
void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline)
{
@ -590,7 +615,7 @@ 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_desc_set = VK_NULL_HANDLE;
ImTextureID last_tex_id = ImTextureID_Invalid;
int global_vtx_offset = 0;
int global_idx_offset = 0;
for (const ImDrawList* draw_list : draw_data->CmdLists)
@ -606,7 +631,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height);
else
pcmd->UserCallback(draw_list, pcmd);
last_desc_set = VK_NULL_HANDLE;
last_tex_id = ImTextureID_Invalid;
}
else
{
@ -630,11 +655,24 @@ 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);
// Bind DescriptorSet with font or user texture
VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID();
if (desc_set != last_desc_set)
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr);
last_desc_set = desc_set;
// Bind font or user texture
ImTextureID tex_id = pcmd->GetTexID();
if (tex_id != last_tex_id)
{
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR
if (v->UsePushDescriptor)
{
VkImageView view = (VkImageView)pcmd->GetTexID();
ImGui_ImplVulkan_PushTextureDescriptor(command_buffer, view, bd->TexSamplerLinear, bd->PipelineLayout);
}
else
#endif
{
VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID();
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr);
}
last_tex_id = tex_id;
}
// Draw
vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
@ -661,10 +699,13 @@ static void ImGui_ImplVulkan_DestroyTexture(ImTextureData* tex)
ImGui_ImplVulkan_Texture* backend_tex = (ImGui_ImplVulkan_Texture*)tex->BackendUserData;
if (backend_tex == nullptr)
return;
IM_ASSERT(backend_tex->DescriptorSet == (VkDescriptorSet)tex->TexID);
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet);
if (!v->UsePushDescriptor)
{
IM_ASSERT(backend_tex->DescriptorSet == (VkDescriptorSet)tex->TexID);
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);
@ -737,10 +778,17 @@ void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex)
}
// Create the Descriptor Set
backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(bd->TexSamplerLinear, backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if (!v->UsePushDescriptor)
{
backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(bd->TexSamplerLinear, backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
tex->SetTexID((ImTextureID)backend_tex->DescriptorSet);
}
else
{
tex->SetTexID((ImTextureID)backend_tex->ImageView);
}
// Store identifiers
tex->SetTexID((ImTextureID)backend_tex->DescriptorSet);
tex->BackendUserData = backend_tex;
}
@ -1065,11 +1113,19 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
info.bindingCount = 1;
info.pBindings = binding;
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR
if (v->UsePushDescriptor)
{
info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT;
}
#endif
err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, &bd->DescriptorSetLayout);
check_vk_result(err);
}
if (v->DescriptorPoolSize != 0)
if (v->DescriptorPoolSize != 0
&& !v->UsePushDescriptor
)
{
IM_ASSERT(v->DescriptorPoolSize >= IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE);
VkDescriptorPoolSize pool_size = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, v->DescriptorPoolSize };
@ -1216,6 +1272,19 @@ static void ImGui_ImplVulkan_LoadDynamicRenderingFunctions(uint32_t api_version,
}
#endif
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR
static void ImGui_ImplVulkan_LoadPushDescriptorFunctions(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));
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()
@ -1276,6 +1345,27 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
#endif
}
if (info->UsePushDescriptor)
{
#ifdef IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR
#ifndef IMGUI_IMPL_VULKAN_USE_LOADER
ImGui_ImplVulkan_LoadPushDescriptorFunctions(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 descriptor when neither VK_VERSION_1_1 or VK_KHR_push_descriptor is defined.");
#endif
IM_ASSERT(info->DescriptorPool == VK_NULL_HANDLE);
IM_ASSERT(info->DescriptorPoolSize == 0);
}
else
{
if (info->DescriptorPool != VK_NULL_HANDLE) // Either DescriptorPool or DescriptorPoolSize must be set, not both!
IM_ASSERT(info->DescriptorPoolSize == 0);
else
IM_ASSERT(info->DescriptorPoolSize > 0);
}
ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
@ -1291,10 +1381,6 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE);
IM_ASSERT(info->Device != VK_NULL_HANDLE);
IM_ASSERT(info->Queue != VK_NULL_HANDLE);
if (info->DescriptorPool != VK_NULL_HANDLE) // Either DescriptorPool or DescriptorPoolSize must be set, not both!
IM_ASSERT(info->DescriptorPoolSize == 0);
else
IM_ASSERT(info->DescriptorPoolSize > 0);
IM_ASSERT(info->MinImageCount >= 2);
IM_ASSERT(info->ImageCount >= info->MinImageCount);
@ -1351,6 +1437,7 @@ VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
IM_ASSERT(v->UsePushDescriptor == false && "Can't use ImGui_ImplVulkan_AddTexture when push descriptor is enabled.");
VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;
// Create Descriptor Set:
@ -1386,6 +1473,7 @@ void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
IM_ASSERT(v->UsePushDescriptor == false && "Can't use ImGui_ImplVulkan_RemoveTexture when push descriptor is enabled.");
VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;
vkFreeDescriptorSets(v->Device, pool, 1, &descriptor_set);
}

View file

@ -61,6 +61,9 @@
#if defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering)
#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
#endif
#if defined(VK_VERSION_1_1) || defined(VK_KHR_push_descriptor)
#define IMGUI_IMPL_VULKAN_HAS_PUSH_DESCRIPTOR
#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_IMAGE_SAMPLER_POOL_SIZE (8) // Minimum per atlas
@ -71,6 +74,7 @@
// - A VkDescriptorPool should be created with VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
// and must contain a pool size large enough to hold a small number of VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptors.
// - As an convenience, by setting DescriptorPoolSize > 0 the backend will create one for you.
// - When using push descriptor, pool functionality is disabled.
// - About dynamic rendering:
// - When using dynamic rendering, set UseDynamicRendering=true and fill PipelineRenderingCreateInfo structure.
struct ImGui_ImplVulkan_InitInfo
@ -92,6 +96,10 @@ struct ImGui_ImplVulkan_InitInfo
uint32_t Subpass;
VkSampleCountFlagBits MSAASamples; // 0 defaults to VK_SAMPLE_COUNT_1_BIT
// (Optional) Push Descriptors
// Need to explicitly enable VK_KHR_push_descriptor extension to use this before Vulkan 1.4.
bool UsePushDescriptor;
// (Optional) Dynamic Rendering
// Need to explicitly enable VK_KHR_dynamic_rendering extension to use this, even for Vulkan 1.3 + setup PipelineRenderingCreateInfo.
bool UseDynamicRendering;