Backends: Vulkan: Add VK_EXT_descriptor_heap support (#9374)

Fix: applied coding style

Fix: Fixed Vulkan pNext chain when using both Dynamic Rendering and Descriptor Heaps.
This commit is contained in:
Albert 2026-06-08 22:50:36 -04:00 committed by ocornut
parent 6a6d7bc3ca
commit 458f8a245d
5 changed files with 482 additions and 41 deletions

View File

@ -171,6 +171,7 @@ static bool g_FunctionsLoaded = true;
IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdDrawIndexed) \
IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPipelineBarrier) \
IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPushConstants) \
IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPushDataEXT) \
IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetScissor) \
IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \
IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \
@ -268,6 +269,7 @@ struct ImGui_ImplVulkan_Texture
VkImage Image;
VkImageView ImageView;
VkDescriptorSet DescriptorSet;
uint32_t DescriptorHeapIndex;
ImGui_ImplVulkan_Texture() { memset((void*)this, 0, sizeof(*this)); }
};
@ -296,6 +298,8 @@ struct ImGui_ImplVulkan_Data
VkDescriptorSet SamplerNearestDS;
VkCommandPool TexCommandPool;
VkCommandBuffer TexCommandBuffer;
uint32_t DescriptorHeapSamplerLinear;
uint32_t DescriptorHeapSamplerNearest;
// Render buffers for main window
ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers;
@ -422,6 +426,76 @@ static uint32_t __glsl_shader_frag_spv[] =
0x00010038
};
// backends/vulkan/glsl_shader_heap.frag, compiled with:
// # glslangValidator -V --target-env spirv1.2 -x -o glsl_shader.frag.u32 glsl_shader.frag
/*
#version 460 core
#extension GL_EXT_descriptor_heap : require
#extension GL_EXT_nonuniform_qualifier : require
layout(location = 0) out vec4 fColor;
layout(descriptor_heap) uniform texture2D _Textures[];
layout(descriptor_heap) uniform sampler _Samplers[];
layout(location = 0) in struct { vec4 Color; vec2 UV; } In;
layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; uvec2 uID; } pc;
void main()
{
fColor = In.Color * texture(sampler2D(_Textures[pc.uID.x], _Samplers[pc.uID.y]), In.UV.st);
}
*/
static uint32_t __glsl_shader_heap_frag_spv[] =
{
0x07230203,0x00010200,0x0008000b,0x00000036,0x00000000,0x00020011,0x00000001,0x00020011,
0x00001179,0x00020011,0x00001408,0x0007000a,0x5f565053,0x5f545845,0x63736564,0x74706972,
0x685f726f,0x00706165,0x0008000a,0x5f565053,0x5f52484b,0x79746e75,0x5f646570,0x6e696f70,
0x73726574,0x00000000,0x0006000b,0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,
0x0003000e,0x00000000,0x00000001,0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,
0x00000009,0x0000000d,0x00030010,0x00000004,0x00000007,0x00030003,0x00000002,0x000001cc,
0x00070004,0x455f4c47,0x645f5458,0x72637365,0x6f747069,0x65685f72,0x00007061,0x00080004,
0x455f4c47,0x6e5f5458,0x6e756e6f,0x726f6669,0x75715f6d,0x66696c61,0x00726569,0x00040005,
0x00000004,0x6e69616d,0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,
0x0000000b,0x00000000,0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,
0x0000000b,0x00000001,0x00005655,0x00030005,0x0000000d,0x00006e49,0x00060005,0x00000014,
0x6f736572,0x65637275,0x6165685f,0x00000070,0x00060005,0x00000017,0x73755075,0x6e6f4368,
0x6e617473,0x00000074,0x00050006,0x00000017,0x00000000,0x61635375,0x0000656c,0x00060006,
0x00000017,0x00000001,0x61725475,0x616c736e,0x00006574,0x00040006,0x00000017,0x00000002,
0x00444975,0x00030005,0x00000019,0x00006370,0x00060005,0x00000025,0x706d6173,0x5f72656c,
0x70616568,0x00000000,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d,
0x0000001e,0x00000000,0x00040047,0x00000014,0x0000000b,0x00001403,0x00030047,0x00000017,
0x00000002,0x00050048,0x00000017,0x00000000,0x00000023,0x00000000,0x00050048,0x00000017,
0x00000001,0x00000023,0x00000008,0x00050048,0x00000017,0x00000002,0x00000023,0x00000010,
0x0004014c,0x00000023,0x00001404,0x00000022,0x00040047,0x00000025,0x0000000b,0x00001402,
0x0004014c,0x0000002c,0x00001404,0x0000002b,0x00020013,0x00000002,0x00030021,0x00000003,
0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,
0x00040020,0x00000008,0x00000003,0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,
0x00040017,0x0000000a,0x00000006,0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,
0x00040020,0x0000000c,0x00000001,0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,
0x00040015,0x0000000e,0x00000020,0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,
0x00040020,0x00000010,0x00000001,0x00000007,0x00031141,0x00000013,0x00000000,0x00041142,
0x00000013,0x00000014,0x00000000,0x00040015,0x00000015,0x00000020,0x00000000,0x00040017,
0x00000016,0x00000015,0x00000002,0x0005001e,0x00000017,0x0000000a,0x0000000a,0x00000016,
0x00040020,0x00000018,0x00000009,0x00000017,0x0004003b,0x00000018,0x00000019,0x00000009,
0x0004002b,0x0000000e,0x0000001a,0x00000002,0x0004002b,0x00000015,0x0000001b,0x00000000,
0x00040020,0x0000001c,0x00000009,0x00000015,0x00090019,0x0000001f,0x00000006,0x00000001,
0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x00031141,0x00000020,0x00000002,
0x00041409,0x0000000e,0x00000022,0x0000001f,0x0003001d,0x00000023,0x0000001f,0x00041142,
0x00000013,0x00000025,0x00000000,0x0004002b,0x00000015,0x00000026,0x00000001,0x0002001a,
0x00000029,0x00041409,0x0000000e,0x0000002b,0x00000029,0x0003001d,0x0000002c,0x00000029,
0x0003001b,0x0000002e,0x0000001f,0x0004002b,0x0000000e,0x00000030,0x00000001,0x00040020,
0x00000031,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,
0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d,0x0000000f,0x0004003d,
0x00000007,0x00000012,0x00000011,0x00060041,0x0000001c,0x0000001d,0x00000019,0x0000001a,
0x0000001b,0x0004003d,0x00000015,0x0000001e,0x0000001d,0x00061143,0x00000013,0x00000021,
0x00000023,0x00000014,0x0000001e,0x0004003d,0x0000001f,0x00000024,0x00000021,0x00060041,
0x0000001c,0x00000027,0x00000019,0x0000001a,0x00000026,0x0004003d,0x00000015,0x00000028,
0x00000027,0x00061143,0x00000013,0x0000002a,0x0000002c,0x00000025,0x00000028,0x0004003d,
0x00000029,0x0000002d,0x0000002a,0x00050056,0x0000002e,0x0000002f,0x00000024,0x0000002d,
0x00050041,0x00000031,0x00000032,0x0000000d,0x00000030,0x0004003d,0x0000000a,0x00000033,
0x00000032,0x00050057,0x00000007,0x00000034,0x0000002f,0x00000033,0x00050085,0x00000007,
0x00000035,0x00000012,0x00000034,0x0003003e,0x00000009,0x00000035,0x000100fd,0x00010038
};
//-----------------------------------------------------------------------------
// FUNCTIONS
//-----------------------------------------------------------------------------
@ -522,23 +596,74 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
viewport.maxDepth = 1.0f;
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
// Setup scale and translation:
// Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
float constants[4];
constants[0] = 2.0f / draw_data->DisplaySize.x; // Scale
constants[1] = 2.0f / draw_data->DisplaySize.y;
constants[2] = -1.0f - draw_data->DisplayPos.x * constants[0]; // Translate
constants[3] = -1.0f - draw_data->DisplayPos.y * constants[1];
vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 4, constants);
struct push_data
{
float constants[4];
// First ID is texture, second is sampler.
uint32_t ids[2];
} pd;
pd.constants[0] = 2.0f / draw_data->DisplaySize.x; // Scale
pd.constants[1] = 2.0f / draw_data->DisplaySize.y;
pd.constants[2] = -1.0f - draw_data->DisplayPos.x * pd.constants[0]; // Translate
pd.constants[3] = -1.0f - draw_data->DisplayPos.y * pd.constants[1];
if (bd->VulkanInitInfo.DescriptorHeapInfo)
{
pd.ids[0] = 0;
pd.ids[1] = bd->DescriptorHeapSamplerLinear;
// Setup sampler
vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr);
VkPushDataInfoEXT push{};
push.sType = VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT;
push.data.address = &pd;
push.data.size = sizeof(pd);
vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
}
else
{
// Setup scale and translation:
// Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 4, pd.constants);
// Setup sampler
vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr);
}
}
// 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->RenderState->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->RenderState->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr); }
static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
if (bd->VulkanInitInfo.DescriptorHeapInfo)
{
VkPushDataInfoEXT push{};
push.sType = VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT;
push.data.address = &bd->DescriptorHeapSamplerLinear;
push.data.size = 4;
push.offset = 20;
vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
}
else
{
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();
if (bd->VulkanInitInfo.DescriptorHeapInfo)
{
VkPushDataInfoEXT push{};
push.sType = VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT;
push.data.address = &bd->DescriptorHeapSamplerNearest;
push.data.size = 4;
push.offset = 20;
vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
}
else
{
vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr);
}
}
// If you want to use your own sampler, you can create your own callback, access ImGui_ImplVulkan_RenderState for command-buffer and pipeline layout, e.g.
/*
@ -679,9 +804,25 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
// Bind DescriptorSets for image view (font or user texture) and samplers
VkDescriptorSet image_view = (VkDescriptorSet)pcmd->GetTexID();
ImTextureID image_id = pcmd->GetTexID();
VkDescriptorSet image_view = (VkDescriptorSet)image_id;
if (image_view != last_image_view)
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &image_view, 0, nullptr);
{
if (v->DescriptorHeapInfo)
{
VkPushDataInfoEXT push{};
push.sType = VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT;
image_id &= 0x7FFFFFFF;
push.data.address = &image_id;
push.data.size = 4;
push.offset = 16;
vkCmdPushDataEXT(command_buffer, &push);
}
else
{
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &image_view, 0, nullptr);
}
}
last_image_view = image_view;
// Draw
@ -708,11 +849,16 @@ 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);
IM_ASSERT(backend_tex->DescriptorSet == (VkDescriptorSet)tex->TexID ||
backend_tex->DescriptorHeapIndex == (tex->TexID & 0x7FFFFFFF));
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet);
vkDestroyImageView(v->Device, backend_tex->ImageView, v->Allocator);
if (backend_tex->DescriptorSet != VK_NULL_HANDLE)
ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet);
else
ImGui_ImplVulkan_RemoveHeapTexture(backend_tex->DescriptorHeapIndex);
if (backend_tex->ImageView != VK_NULL_HANDLE)
vkDestroyImageView(v->Device, backend_tex->ImageView, v->Allocator);
vkDestroyImage(v->Device, backend_tex->Image, v->Allocator);
vkFreeMemory(v->Device, backend_tex->Memory, v->Allocator);
IM_DELETE(backend_tex);
@ -770,25 +916,33 @@ void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex)
check_vk_result(err);
}
// Create the Image View:
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = backend_tex->Image;
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_R8G8B8A8_UNORM;
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.levelCount = 1;
info.subresourceRange.layerCount = 1;
if (v->DescriptorHeapInfo)
{
VkImageViewCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.image = backend_tex->Image;
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.format = VK_FORMAT_R8G8B8A8_UNORM;
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.levelCount = 1;
info.subresourceRange.layerCount = 1;
backend_tex->DescriptorHeapIndex =
v->DescriptorHeapInfo->RegisterImage(v->DescriptorHeapInfo->UserContext, &info);
// Store identifiers
tex->SetTexID((ImTextureID)(backend_tex->DescriptorHeapIndex | 0x80000000));
}
else
{
// Create the Image View:
err = vkCreateImageView(v->Device, &info, v->Allocator, &backend_tex->ImageView);
check_vk_result(err);
// 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);
}
// 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);
tex->BackendUserData = backend_tex;
}
@ -952,6 +1106,11 @@ static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAlloca
default_frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
default_frag_info.codeSize = sizeof(__glsl_shader_frag_spv);
default_frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv;
if (v->DescriptorHeapInfo)
{
default_frag_info.codeSize = sizeof(__glsl_shader_heap_frag_spv);
default_frag_info.pCode = (uint32_t*)__glsl_shader_heap_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, p_frag_info, allocator, &bd->ShaderModuleFrag);
check_vk_result(err);
@ -1065,15 +1224,27 @@ static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAlloc
create_info.renderPass = info->RenderPass;
create_info.subpass = info->Subpass;
VkPipelineCreateFlags2CreateInfo ci_heap_flags{};
ci_heap_flags.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO;
ci_heap_flags.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT;
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
VkPipelineRenderingCreateInfo pipeline_rendering_create_info = {};
if (bd->VulkanInitInfo.UseDynamicRendering)
{
IM_ASSERT(info->PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR && "PipelineRenderingCreateInfo::sType must be VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR");
IM_ASSERT(info->PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo::pNext must be nullptr");
create_info.pNext = &info->PipelineRenderingCreateInfo;
pipeline_rendering_create_info = info->PipelineRenderingCreateInfo;
if (bd->VulkanInitInfo.DescriptorHeapInfo)
pipeline_rendering_create_info.pNext = &ci_heap_flags;
create_info.pNext = &pipeline_rendering_create_info;
create_info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr.
}
#endif
if (bd->VulkanInitInfo.DescriptorHeapInfo)
{
create_info.pNext = &ci_heap_flags;
}
VkPipeline pipeline;
VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &create_info, allocator, &pipeline);
check_vk_result(err);
@ -1169,17 +1340,31 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
info.maxAnisotropy = 1.0f;
err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->SamplerLinear);
check_vk_result(err);
bd->SamplerLinearDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerLinear);
if (v->DescriptorHeapInfo)
{
bd->DescriptorHeapSamplerLinear =
v->DescriptorHeapInfo->RegisterSampler(v->DescriptorHeapInfo->UserContext, &info);
} else
{
bd->SamplerLinearDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerLinear);
}
info.magFilter = VK_FILTER_NEAREST;
info.minFilter = VK_FILTER_NEAREST;
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->SamplerNearest);
check_vk_result(err);
bd->SamplerNearestDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerNearest);
if (v->DescriptorHeapInfo)
{
bd->DescriptorHeapSamplerNearest =
v->DescriptorHeapInfo->RegisterSampler(v->DescriptorHeapInfo->UserContext, &info);
} else
{
bd->SamplerNearestDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerNearest);
}
}
if (!bd->PipelineLayout)
if (!bd->PipelineLayout && !v->DescriptorHeapInfo)
{
// Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
VkPushConstantRange push_constants[1] = {};
@ -1278,6 +1463,11 @@ void ImGui_ImplVulkan_DestroyDeviceObjects()
if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; }
if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; }
if (bd->DescriptorPool) { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; }
if (v->DescriptorHeapInfo)
{
v->DescriptorHeapInfo->UnRegisterSampler(v->DescriptorHeapInfo->UserContext, bd->DescriptorHeapSamplerLinear);
v->DescriptorHeapInfo->UnRegisterSampler(v->DescriptorHeapInfo->UserContext, bd->DescriptorHeapSamplerNearest);
}
}
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
@ -1382,7 +1572,9 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
IM_ASSERT(info->Queue != VK_NULL_HANDLE);
IM_ASSERT(info->MinImageCount >= 2);
IM_ASSERT(info->ImageCount >= info->MinImageCount);
if (info->DescriptorPool != VK_NULL_HANDLE) // Either DescriptorPool or DescriptorPoolSize must be set, not both!
if (info->DescriptorHeapInfo != nullptr) // If using descriptor heap then DescriptorPool and DescriptorPoolSize must be unset.
IM_ASSERT(info->DescriptorPool == VK_NULL_HANDLE && 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);
@ -1491,12 +1683,35 @@ void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set)
vkFreeDescriptorSets(v->Device, pool, 1, &descriptor_set);
}
void ImGui_ImplVulkan_RemoveHeapTexture(uint32_t id)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
v->DescriptorHeapInfo->UnRegisterImage(v->DescriptorHeapInfo->UserContext, id);
}
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; }
if (buffers->VertexBufferMemory) { vkFreeMemory(device, buffers->VertexBufferMemory, allocator); buffers->VertexBufferMemory = VK_NULL_HANDLE; }
if (buffers->IndexBuffer) { vkDestroyBuffer(device, buffers->IndexBuffer, allocator); buffers->IndexBuffer = VK_NULL_HANDLE; }
if (buffers->IndexBufferMemory) { vkFreeMemory(device, buffers->IndexBufferMemory, allocator); buffers->IndexBufferMemory = VK_NULL_HANDLE; }
if (buffers->VertexBuffer)
{
vkDestroyBuffer(device, buffers->VertexBuffer, allocator);
buffers->VertexBuffer = VK_NULL_HANDLE;
}
if (buffers->VertexBufferMemory)
{
vkFreeMemory(device, buffers->VertexBufferMemory, allocator);
buffers->VertexBufferMemory = VK_NULL_HANDLE;
}
if (buffers->IndexBuffer)
{
vkDestroyBuffer(device, buffers->IndexBuffer, allocator);
buffers->IndexBuffer = VK_NULL_HANDLE;
}
if (buffers->IndexBufferMemory)
{
vkFreeMemory(device, buffers->IndexBufferMemory, allocator);
buffers->IndexBufferMemory = VK_NULL_HANDLE;
}
buffers->VertexBufferSize = 0;
buffers->IndexBufferSize = 0;
}

View File

@ -93,6 +93,15 @@ struct ImGui_ImplVulkan_PipelineInfo
#endif
};
struct ImGui_ImplVulkan_DescriptorHeapInfo
{
uint32_t (*RegisterSampler)(void *, const VkSamplerCreateInfo *);
void (*UnRegisterSampler)(void *, uint32_t);
uint32_t (*RegisterImage)(void *, const VkImageViewCreateInfo *);
void (*UnRegisterImage)(void *, uint32_t);
void *UserContext;
};
// Initialization data, for ImGui_ImplVulkan_Init()
// [Please zero-clear before use!]
// - About descriptor pool:
@ -136,6 +145,9 @@ struct ImGui_ImplVulkan_InitInfo
// - 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;
// (Optional) If set, use VK_EXT_descriptor_heap.
const ImGui_ImplVulkan_DescriptorHeapInfo *DescriptorHeapInfo;
};
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
@ -156,6 +168,7 @@ IMGUI_IMPL_API void ImGui_ImplVulkan_UpdateTexture(ImTextureData* te
// Register a texture (VkDescriptorSet for a VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE == ImTextureID)
IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkImageView image_view, VkImageLayout image_layout);
IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set);
IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveHeapTexture(uint32_t id);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); // Ignore VkSampler

View File

@ -4,3 +4,4 @@
## -o: output file
glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag
glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert
glslangValidator -V --target-env spirv1.2 -x -o glsl_shader_heap.vert.u32 glsl_shader_heap.vert

View File

@ -0,0 +1,18 @@
#version 460 core
#extension GL_EXT_descriptor_heap : require
#extension GL_EXT_nonuniform_qualifier : require
layout(location = 0) out vec4 fColor;
layout(descriptor_heap) uniform texture2D _Textures[];
layout(descriptor_heap) uniform sampler _Samplers[];
layout(location = 0) in struct {
vec4 Color;
vec2 UV;
} In;
layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; uvec2 uID; } pc;
void main()
{
fColor = In.Color * texture(sampler2D(_Textures[pc.uID.x], _Samplers[pc.uID.y]), In.UV.st);
}

View File

@ -47,6 +47,38 @@ static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE;
static ImGui_ImplVulkanH_Window g_MainWindowData;
static uint32_t g_MinImageCount = 2;
static bool g_SwapChainRebuild = false;
static bool g_UsingDescriptorHeap = false;
struct HeapInfo
{
VkBuffer Buffer;
VkDeviceMemory Memory;
void* Mapped;
VkBindHeapInfoEXT BindInfo;
uint64_t FreeList;
VkDeviceSize Stride;
uint32_t Allocate()
{
IM_ASSERT(FreeList != 0);
for(uint32_t i = 0; i < 8 * sizeof(FreeList); i++)
{
if(FreeList & (1ull << i))
{
FreeList ^= (1ull << i);
return i;
}
}
return 0;
}
void Free(uint32_t idx)
{
FreeList |= 1ull << idx;
}
};
static HeapInfo g_ResourceHeap;
static HeapInfo g_SamplerHeap;
static ImGui_ImplVulkan_DescriptorHeapInfo g_DescriptorHeapInfo;
static void check_vk_result(VkResult err)
{
@ -74,6 +106,52 @@ static bool IsExtensionAvailable(const ImVector<VkExtensionProperties>& properti
return false;
}
// Same as IM_MEMALIGN(). 'alignment' must be a power of two.
static inline VkDeviceSize AlignBufferSize(VkDeviceSize size, VkDeviceSize alignment)
{
return (size + alignment - 1) & ~(alignment - 1);
}
static void SetupHeap(
HeapInfo& heap, VkDeviceSize size, VkDeviceSize alignment, VkDeviceSize stride, uint32_t mem_index)
{
heap.Stride = stride;
VkDeviceSize buffer_size_aligned = AlignBufferSize(size, alignment);
VkBufferCreateInfo buffer_info = {};
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = buffer_size_aligned;
buffer_info.usage = VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VkResult err = vkCreateBuffer(g_Device, &buffer_info, g_Allocator, &heap.Buffer);
check_vk_result(err);
VkMemoryRequirements mem_req;
vkGetBufferMemoryRequirements(g_Device, heap.Buffer, &mem_req);
VkMemoryAllocateFlagsInfo alloc_flags = {};
alloc_flags.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT;
VkMemoryAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
alloc_info.pNext = &alloc_flags;
alloc_info.allocationSize = buffer_size_aligned;
alloc_info.memoryTypeIndex = mem_index;
err = vkAllocateMemory(g_Device, &alloc_info, g_Allocator, &heap.Memory);
check_vk_result(err);
err = vkMapMemory(g_Device, heap.Memory, 0, buffer_size_aligned, 0, &heap.Mapped);
check_vk_result(err);
err = vkBindBufferMemory(g_Device, heap.Buffer, heap.Memory, 0);
check_vk_result(err);
const VkBufferDeviceAddressInfo addr = {
VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, nullptr, heap.Buffer
};
heap.BindInfo.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT;
heap.BindInfo.heapRange.address = vkGetBufferDeviceAddressKHR(g_Device, &addr);
heap.BindInfo.heapRange.size = size;
}
static void SetupVulkan(ImVector<const char*> instance_extensions)
{
VkResult err;
@ -116,6 +194,9 @@ static void SetupVulkan(ImVector<const char*> instance_extensions)
// Create Vulkan Instance
create_info.enabledExtensionCount = (uint32_t)instance_extensions.Size;
create_info.ppEnabledExtensionNames = instance_extensions.Data;
VkApplicationInfo app_info{};
app_info.apiVersion = VK_API_VERSION_1_1;
create_info.pApplicationInfo = &app_info;
err = vkCreateInstance(&create_info, g_Allocator, &g_Instance);
check_vk_result(err);
#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
@ -159,6 +240,20 @@ static void SetupVulkan(ImVector<const char*> instance_extensions)
if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME))
device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);
#endif
if (IsExtensionAvailable(properties, VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME))
{
device_extensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME);
device_extensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
device_extensions.push_back(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
device_extensions.push_back(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME);
device_extensions.push_back(VK_KHR_MAINTENANCE_2_EXTENSION_NAME);
device_extensions.push_back(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
device_extensions.push_back(VK_KHR_MULTIVIEW_EXTENSION_NAME);
device_extensions.push_back(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME);
device_extensions.push_back(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
g_UsingDescriptorHeap = true;
}
const float queue_priority[] = { 1.0f };
VkDeviceQueueCreateInfo queue_info[1] = {};
@ -167,6 +262,15 @@ static void SetupVulkan(ImVector<const char*> instance_extensions)
queue_info[0].queueCount = 1;
queue_info[0].pQueuePriorities = queue_priority;
VkDeviceCreateInfo create_info = {};
VkPhysicalDeviceBufferDeviceAddressFeatures buffer_device_addr{
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, nullptr, VK_TRUE};
VkPhysicalDeviceDescriptorHeapFeaturesEXT desc_heap_feat{
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT, &buffer_device_addr, VK_TRUE};
VkPhysicalDeviceShaderUntypedPointersFeaturesKHR untyped_pointers{
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR, &desc_heap_feat, VK_TRUE};
if (g_UsingDescriptorHeap)
create_info.pNext = &untyped_pointers;
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]);
create_info.pQueueCreateInfos = queue_info;
@ -177,8 +281,80 @@ static void SetupVulkan(ImVector<const char*> instance_extensions)
vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue);
}
// Create descriptor heaps
if (g_UsingDescriptorHeap)
{
VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptor_heap_properties{
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT};
VkPhysicalDeviceProperties2 properties2{
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &descriptor_heap_properties};
vkGetPhysicalDeviceProperties2(g_PhysicalDevice, &properties2);
size_t resource_size =
descriptor_heap_properties.imageDescriptorSize * IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE +
descriptor_heap_properties.minResourceHeapReservedRange;
size_t sampler_size =
descriptor_heap_properties.samplerDescriptorSize * IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE +
descriptor_heap_properties.minSamplerHeapReservedRange;
VkPhysicalDeviceMemoryProperties prop;
vkGetPhysicalDeviceMemoryProperties(g_PhysicalDevice, &prop);
uint32_t mem_index = 0;
for (uint32_t i = 0; i < prop.memoryTypeCount; i++)
{
if ((prop.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ==
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
{
mem_index = i;
break;
}
}
SetupHeap(g_ResourceHeap, resource_size, descriptor_heap_properties.resourceHeapAlignment, descriptor_heap_properties.imageDescriptorSize, mem_index);
SetupHeap(g_SamplerHeap, sampler_size, descriptor_heap_properties.samplerHeapAlignment, descriptor_heap_properties.samplerDescriptorSize, mem_index);
g_ResourceHeap.BindInfo.reservedRangeOffset =
descriptor_heap_properties.imageDescriptorSize * IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE;
g_ResourceHeap.BindInfo.reservedRangeSize = descriptor_heap_properties.minResourceHeapReservedRange;
g_SamplerHeap.BindInfo.reservedRangeOffset =
descriptor_heap_properties.samplerDescriptorSize * IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE;
g_SamplerHeap.BindInfo.reservedRangeSize = descriptor_heap_properties.minSamplerHeapReservedRange;
g_ResourceHeap.FreeList = (1ull << IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE) - 1;
g_SamplerHeap.FreeList = (1ull << IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE) - 1;
g_DescriptorHeapInfo.RegisterImage = [](void*, const VkImageViewCreateInfo* ci) -> uint32_t {
uint32_t idx = g_ResourceHeap.Allocate();
VkImageDescriptorInfoEXT image{};
image.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT;
image.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image.pView = ci;
VkResourceDescriptorInfoEXT r{};
r.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT;
r.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
r.data.pImage = &image;
VkHostAddressRangeEXT addr{
(char*)g_ResourceHeap.Mapped + idx * g_ResourceHeap.Stride, g_ResourceHeap.Stride};
vkWriteResourceDescriptorsEXT(g_Device, 1, &r, &addr);
return idx;
};
g_DescriptorHeapInfo.UnRegisterImage = [](void*, uint32_t idx) { g_ResourceHeap.Free(idx); };
g_DescriptorHeapInfo.RegisterSampler = [](void*, const VkSamplerCreateInfo* ci) -> uint32_t {
uint32_t idx = g_SamplerHeap.Allocate();
VkHostAddressRangeEXT addr{(char*)g_SamplerHeap.Mapped + idx * g_SamplerHeap.Stride, g_SamplerHeap.Stride};
vkWriteSamplerDescriptorsEXT(g_Device, 1, ci, &addr);
return idx;
};
g_DescriptorHeapInfo.UnRegisterSampler = [](void*, uint32_t idx) { g_SamplerHeap.Free(idx); };
}
// Create Descriptor Pool
// If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets.
if (!g_UsingDescriptorHeap)
{
VkDescriptorPoolSize pool_sizes[] =
{
@ -233,7 +409,17 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface
static void CleanupVulkan()
{
vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
if (g_UsingDescriptorHeap)
{
vkDestroyBuffer(g_Device, g_ResourceHeap.Buffer, g_Allocator);
vkDestroyBuffer(g_Device, g_SamplerHeap.Buffer, g_Allocator);
vkFreeMemory(g_Device, g_ResourceHeap.Memory, g_Allocator);
vkFreeMemory(g_Device, g_SamplerHeap.Memory, g_Allocator);
}
else
{
vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
}
#ifdef APP_USE_VULKAN_DEBUG_REPORT
// Remove the debug report callback
@ -292,6 +478,12 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
}
if (g_UsingDescriptorHeap)
{
vkCmdBindResourceHeapEXT(fd->CommandBuffer, &g_ResourceHeap.BindInfo);
vkCmdBindSamplerHeapEXT(fd->CommandBuffer, &g_SamplerHeap.BindInfo);
}
// Record dear imgui primitives into command buffer
ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
@ -411,6 +603,8 @@ int main(int, char**)
init_info.PipelineInfoMain.Subpass = 0;
init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
init_info.CheckVkResultFn = check_vk_result;
if (g_UsingDescriptorHeap)
init_info.DescriptorHeapInfo = &g_DescriptorHeapInfo;
ImGui_ImplVulkan_Init(&init_info);
// Load Fonts