Vulkan: Add VK_EXT_descriptor_heap example to example_glfw_vulkan, gate heap API with IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP, and load vkCmdPushDataEXT via vkGetDeviceProcAddr.

Fix: Fix pNext chain overwrite in pipeline creation
This commit is contained in:
Jan Aleksander Górski 2026-07-24 00:28:43 +09:00 committed by ocornut
parent 458f8a245d
commit a988e6f173
4 changed files with 374 additions and 211 deletions

View File

@ -171,7 +171,6 @@ 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) \
@ -242,6 +241,11 @@ static PFN_vkCmdBeginRenderingKHR ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR;
static PFN_vkCmdEndRenderingKHR ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR;
#endif
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
// VK_EXT_descriptor_heap (not exported by most Vulkan loaders; resolve like dynamic rendering)
static PFN_vkCmdPushDataEXT ImGuiImplVulkanFuncs_vkCmdPushDataEXT = nullptr;
#endif
// Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData()
// [Please zero-clear before use!]
struct ImGui_ImplVulkan_FrameRenderBuffers
@ -428,6 +432,7 @@ static uint32_t __glsl_shader_frag_spv[] =
// backends/vulkan/glsl_shader_heap.frag, compiled with:
// # glslangValidator -V --target-env spirv1.2 -x -o glsl_shader.frag.u32 glsl_shader.frag
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
/*
#version 460 core
#extension GL_EXT_descriptor_heap : require
@ -494,6 +499,7 @@ static uint32_t __glsl_shader_heap_frag_spv[] =
0x00000032,0x00050057,0x00000007,0x00000034,0x0000002f,0x00000033,0x00050085,0x00000007,
0x00000035,0x00000012,0x00000034,0x0003003e,0x00000009,0x00000035,0x000100fd,0x00010038
};
#endif // IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
//-----------------------------------------------------------------------------
@ -606,6 +612,7 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
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];
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (bd->VulkanInitInfo.DescriptorHeapInfo)
{
pd.ids[0] = 0;
@ -615,9 +622,10 @@ static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline
push.sType = VK_STRUCTURE_TYPE_PUSH_DATA_INFO_EXT;
push.data.address = &pd;
push.data.size = sizeof(pd);
vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
ImGuiImplVulkanFuncs_vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
}
else
#endif
{
// 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.
@ -633,6 +641,7 @@ static void ImGui_ImplVulkan_DrawCallback_ResetRenderState(const ImDrawList*, co
static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (bd->VulkanInitInfo.DescriptorHeapInfo)
{
VkPushDataInfoEXT push{};
@ -640,9 +649,10 @@ static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, co
push.data.address = &bd->DescriptorHeapSamplerLinear;
push.data.size = 4;
push.offset = 20;
vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
ImGuiImplVulkanFuncs_vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
}
else
#endif
{
vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr);
}
@ -650,6 +660,7 @@ static void ImGui_ImplVulkan_DrawCallback_SetSamplerLinear(const ImDrawList*, co
static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*)
{
ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (bd->VulkanInitInfo.DescriptorHeapInfo)
{
VkPushDataInfoEXT push{};
@ -657,9 +668,10 @@ static void ImGui_ImplVulkan_DrawCallback_SetSamplerNearest(const ImDrawList*, c
push.data.address = &bd->DescriptorHeapSamplerNearest;
push.data.size = 4;
push.offset = 20;
vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
ImGuiImplVulkanFuncs_vkCmdPushDataEXT(bd->RenderState->CommandBuffer, &push);
}
else
#endif
{
vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerNearestDS, 0, nullptr);
}
@ -808,6 +820,7 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
VkDescriptorSet image_view = (VkDescriptorSet)image_id;
if (image_view != last_image_view)
{
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (v->DescriptorHeapInfo)
{
VkPushDataInfoEXT push{};
@ -816,9 +829,10 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
push.data.address = &image_id;
push.data.size = 4;
push.offset = 16;
vkCmdPushDataEXT(command_buffer, &push);
ImGuiImplVulkanFuncs_vkCmdPushDataEXT(command_buffer, &push);
}
else
#endif
{
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &image_view, 0, nullptr);
}
@ -855,8 +869,10 @@ static void ImGui_ImplVulkan_DestroyTexture(ImTextureData* tex)
ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
if (backend_tex->DescriptorSet != VK_NULL_HANDLE)
ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet);
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
else
ImGui_ImplVulkan_RemoveHeapTexture(backend_tex->DescriptorHeapIndex);
#endif
if (backend_tex->ImageView != VK_NULL_HANDLE)
vkDestroyImageView(v->Device, backend_tex->ImageView, v->Allocator);
vkDestroyImage(v->Device, backend_tex->Image, v->Allocator);
@ -924,6 +940,7 @@ void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex)
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
info.subresourceRange.levelCount = 1;
info.subresourceRange.layerCount = 1;
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (v->DescriptorHeapInfo)
{
backend_tex->DescriptorHeapIndex =
@ -932,6 +949,7 @@ void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex)
tex->SetTexID((ImTextureID)(backend_tex->DescriptorHeapIndex | 0x80000000));
}
else
#endif
{
// Create the Image View:
err = vkCreateImageView(v->Device, &info, v->Allocator, &backend_tex->ImageView);
@ -1106,11 +1124,13 @@ 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;
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (v->DescriptorHeapInfo)
{
default_frag_info.codeSize = sizeof(__glsl_shader_heap_frag_spv);
default_frag_info.pCode = (uint32_t*)__glsl_shader_heap_frag_spv;
}
#endif
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);
@ -1224,9 +1244,11 @@ static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAlloc
create_info.renderPass = info->RenderPass;
create_info.subpass = info->Subpass;
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
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;
#endif
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
VkPipelineRenderingCreateInfo pipeline_rendering_create_info = {};
@ -1235,16 +1257,23 @@ static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAlloc
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");
pipeline_rendering_create_info = info->PipelineRenderingCreateInfo;
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (bd->VulkanInitInfo.DescriptorHeapInfo)
pipeline_rendering_create_info.pNext = &ci_heap_flags;
#endif
create_info.pNext = &pipeline_rendering_create_info;
create_info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr.
}
#endif
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (bd->VulkanInitInfo.DescriptorHeapInfo)
{
create_info.pNext = &ci_heap_flags;
if (create_info.pNext == nullptr)
{
create_info.pNext = &ci_heap_flags;
}
}
#endif
VkPipeline pipeline;
VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &create_info, allocator, &pipeline);
check_vk_result(err);
@ -1340,11 +1369,13 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
info.maxAnisotropy = 1.0f;
err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->SamplerLinear);
check_vk_result(err);
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (v->DescriptorHeapInfo)
{
bd->DescriptorHeapSamplerLinear =
v->DescriptorHeapInfo->RegisterSampler(v->DescriptorHeapInfo->UserContext, &info);
} else
#endif
{
bd->SamplerLinearDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerLinear);
}
@ -1354,17 +1385,23 @@ bool ImGui_ImplVulkan_CreateDeviceObjects()
info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->SamplerNearest);
check_vk_result(err);
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (v->DescriptorHeapInfo)
{
bd->DescriptorHeapSamplerNearest =
v->DescriptorHeapInfo->RegisterSampler(v->DescriptorHeapInfo->UserContext, &info);
} else
#endif
{
bd->SamplerNearestDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerNearest);
}
}
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (!bd->PipelineLayout && !v->DescriptorHeapInfo)
#else
if (!bd->PipelineLayout)
#endif
{
// Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
VkPushConstantRange push_constants[1] = {};
@ -1463,11 +1500,13 @@ 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; }
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (v->DescriptorHeapInfo)
{
v->DescriptorHeapInfo->UnRegisterSampler(v->DescriptorHeapInfo->UserContext, bd->DescriptorHeapSamplerLinear);
v->DescriptorHeapInfo->UnRegisterSampler(v->DescriptorHeapInfo->UserContext, bd->DescriptorHeapSamplerNearest);
}
#endif
}
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
@ -1520,6 +1559,9 @@ bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(
#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
ImGui_ImplVulkan_LoadDynamicRenderingFunctions(api_version, loader_func, user_data);
#endif
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
ImGuiImplVulkanFuncs_vkCmdPushDataEXT = reinterpret_cast<PFN_vkCmdPushDataEXT>(loader_func("vkCmdPushDataEXT", user_data));
#endif
#else
IM_UNUSED(loader_func);
IM_UNUSED(user_data);
@ -1549,6 +1591,16 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
#endif
}
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
if (info->DescriptorHeapInfo != nullptr)
{
#ifndef IMGUI_IMPL_VULKAN_USE_LOADER
ImGuiImplVulkanFuncs_vkCmdPushDataEXT = reinterpret_cast<PFN_vkCmdPushDataEXT>(vkGetDeviceProcAddr(info->Device, "vkCmdPushDataEXT"));
#endif
IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdPushDataEXT != nullptr && "vkCmdPushDataEXT not available (enable VK_EXT_descriptor_heap)");
}
#endif
ImGuiIO& io = ImGui::GetIO();
IMGUI_CHECKVERSION();
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
@ -1572,9 +1624,12 @@ 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);
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
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!
else
#endif
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);
@ -1683,12 +1738,14 @@ void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set)
vkFreeDescriptorSets(v->Device, pool, 1, &descriptor_set);
}
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
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);
}
#endif
void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator)
{

View File

@ -76,6 +76,9 @@
#if defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering)
#define IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
#endif
#if defined(VK_EXT_descriptor_heap)
#define IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
#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
@ -93,6 +96,7 @@ struct ImGui_ImplVulkan_PipelineInfo
#endif
};
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
struct ImGui_ImplVulkan_DescriptorHeapInfo
{
uint32_t (*RegisterSampler)(void *, const VkSamplerCreateInfo *);
@ -101,6 +105,7 @@ struct ImGui_ImplVulkan_DescriptorHeapInfo
void (*UnRegisterImage)(void *, uint32_t);
void *UserContext;
};
#endif
// Initialization data, for ImGui_ImplVulkan_Init()
// [Please zero-clear before use!]
@ -108,8 +113,10 @@ struct ImGui_ImplVulkan_DescriptorHeapInfo
// - 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.
// - About dynamic rendering:
// - About dynamic rendering:
// - When using dynamic rendering, set UseDynamicRendering=true + fill PipelineInfoMain.PipelineRenderingCreateInfo structure.
// - About descriptor heap (requires VK_EXT_descriptor_heap in your Vulkan headers → IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP):
// - When using descriptor heaps, set DescriptorHeapInfo and leave DescriptorPool / DescriptorPoolSize unset.
struct ImGui_ImplVulkan_InitInfo
{
uint32_t ApiVersion; // Fill with API version of Instance, e.g. VK_API_VERSION_1_3 or your value of VkApplicationInfo::apiVersion. May be lower than header version (VK_HEADER_VERSION_COMPLETE)
@ -146,8 +153,10 @@ struct ImGui_ImplVulkan_InitInfo
VkShaderModuleCreateInfo CustomShaderVertCreateInfo;
VkShaderModuleCreateInfo CustomShaderFragCreateInfo;
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
// (Optional) If set, use VK_EXT_descriptor_heap.
const ImGui_ImplVulkan_DescriptorHeapInfo *DescriptorHeapInfo;
#endif
};
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
@ -168,7 +177,9 @@ 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);
#ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP
IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveHeapTexture(uint32_t id);
#endif
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); // Ignore VkSampler

View File

@ -41,6 +41,141 @@
static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE;
#endif
// Config for example app
static const int APP_RESOURCE_HEAP_SIZE = 64;
static const int APP_SAMPLER_HEAP_SIZE = 64;
static void check_vk_result(VkResult err);
// Explicitly loaded VK_EXT_descriptor_heap entry points.
// Stored as members so names can match the Vulkan entry points without colliding with prototypes.
struct DescriptorHeapFunctions
{
PFN_vkWriteResourceDescriptorsEXT vkWriteResourceDescriptorsEXT = nullptr;
PFN_vkCmdBindResourceHeapEXT vkCmdBindResourceHeapEXT = nullptr;
PFN_vkCmdBindSamplerHeapEXT vkCmdBindSamplerHeapEXT = nullptr;
PFN_vkWriteSamplerDescriptorsEXT vkWriteSamplerDescriptorsEXT = nullptr;
PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress = nullptr;
void Load(VkDevice device)
{
vkWriteResourceDescriptorsEXT = reinterpret_cast<PFN_vkWriteResourceDescriptorsEXT>(vkGetDeviceProcAddr(device, "vkWriteResourceDescriptorsEXT"));
vkCmdBindResourceHeapEXT = reinterpret_cast<PFN_vkCmdBindResourceHeapEXT>(vkGetDeviceProcAddr(device, "vkCmdBindResourceHeapEXT"));
vkCmdBindSamplerHeapEXT = reinterpret_cast<PFN_vkCmdBindSamplerHeapEXT>(vkGetDeviceProcAddr(device, "vkCmdBindSamplerHeapEXT"));
vkWriteSamplerDescriptorsEXT = reinterpret_cast<PFN_vkWriteSamplerDescriptorsEXT>(vkGetDeviceProcAddr(device, "vkWriteSamplerDescriptorsEXT"));
vkGetBufferDeviceAddress = reinterpret_cast<PFN_vkGetBufferDeviceAddress>(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddress"));
if (vkGetBufferDeviceAddress == nullptr)
vkGetBufferDeviceAddress = reinterpret_cast<PFN_vkGetBufferDeviceAddress>(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR"));
IM_ASSERT(vkWriteResourceDescriptorsEXT && vkCmdBindResourceHeapEXT && vkCmdBindSamplerHeapEXT && vkWriteSamplerDescriptorsEXT && vkGetBufferDeviceAddress);
}
};
static DescriptorHeapFunctions g_DescHeapFns;
// Simple free-list allocator modeled after example_win32_directx12
struct ExampleDescriptorHeapAllocator
{
VkBuffer Buffer = VK_NULL_HANDLE;
VkDeviceMemory Memory = VK_NULL_HANDLE;
void* Mapped = nullptr;
VkBindHeapInfoEXT BindInfo = {};
VkDeviceSize Stride = 0;
int Capacity = 0;
ImVector<int> FreeIndices;
void Create(VkDevice device, VkPhysicalDevice physical_device, VkDeviceSize descriptor_size, VkDeviceSize alignment, VkDeviceSize reserved_range, int capacity, const VkAllocationCallbacks* allocator)
{
IM_ASSERT(Buffer == VK_NULL_HANDLE && FreeIndices.empty());
Capacity = capacity;
Stride = descriptor_size;
VkDeviceSize descriptors_bytes = descriptor_size * (VkDeviceSize)capacity;
VkDeviceSize total_size = descriptors_bytes + reserved_range;
VkDeviceSize buffer_size_aligned = (total_size + alignment - 1) & ~(alignment - 1);
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(device, &buffer_info, allocator, &Buffer);
check_vk_result(err);
VkMemoryRequirements mem_req;
vkGetBufferMemoryRequirements(device, Buffer, &mem_req);
VkPhysicalDeviceMemoryProperties mem_props;
vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_props);
const VkMemoryPropertyFlags required_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
uint32_t mem_index = (uint32_t)-1;
for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++)
{
if ((mem_req.memoryTypeBits & (1u << i)) == 0)
continue;
if ((mem_props.memoryTypes[i].propertyFlags & required_flags) == required_flags)
{
mem_index = i;
break;
}
}
IM_ASSERT(mem_index != (uint32_t)-1 && "No HOST_VISIBLE|HOST_COHERENT memory type compatible with descriptor heap buffer");
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 = mem_req.size > buffer_size_aligned ? mem_req.size : buffer_size_aligned;
alloc_info.memoryTypeIndex = mem_index;
err = vkAllocateMemory(device, &alloc_info, allocator, &Memory);
check_vk_result(err);
err = vkMapMemory(device, Memory, 0, alloc_info.allocationSize, 0, &Mapped);
check_vk_result(err);
err = vkBindBufferMemory(device, Buffer, Memory, 0);
check_vk_result(err);
VkBufferDeviceAddressInfo addr_info = {};
addr_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO;
addr_info.buffer = Buffer;
BindInfo = {};
BindInfo.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT;
BindInfo.heapRange.address = g_DescHeapFns.vkGetBufferDeviceAddress(device, &addr_info);
BindInfo.heapRange.size = total_size;
BindInfo.reservedRangeOffset = descriptors_bytes;
BindInfo.reservedRangeSize = reserved_range;
FreeIndices.reserve(capacity);
for (int n = capacity; n > 0; n--)
FreeIndices.push_back(n - 1);
}
void Destroy(VkDevice device, const VkAllocationCallbacks* allocator)
{
if (Buffer) { vkDestroyBuffer(device, Buffer, allocator); Buffer = VK_NULL_HANDLE; }
if (Memory) { vkFreeMemory(device, Memory, allocator); Memory = VK_NULL_HANDLE; }
Mapped = nullptr;
FreeIndices.clear();
Capacity = 0;
Stride = 0;
BindInfo = {};
}
int Alloc()
{
IM_ASSERT(FreeIndices.Size > 0);
int idx = FreeIndices.back();
FreeIndices.pop_back();
return idx;
}
void Free(int idx)
{
FreeIndices.push_back(idx);
}
VkHostAddressRangeEXT DescriptorAddress(int idx) const
{
return VkHostAddressRangeEXT{ (char*)Mapped + (VkDeviceSize)idx * Stride, Stride };
}
};
// Data
static VkAllocationCallbacks* g_Allocator = nullptr;
static VkInstance g_Instance = VK_NULL_HANDLE;
@ -54,6 +189,10 @@ 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;
static ExampleDescriptorHeapAllocator g_ResourceHeapAlloc;
static ExampleDescriptorHeapAllocator g_SamplerHeapAlloc;
static ImGui_ImplVulkan_DescriptorHeapInfo g_DescriptorHeapInfo;
static void glfw_error_callback(int error, const char* description)
{
@ -85,7 +224,17 @@ static bool IsExtensionAvailable(const ImVector<VkExtensionProperties>& properti
return false;
}
static void SetupVulkan(ImVector<const char*> instance_extensions)
static bool PhysicalDeviceHasExtension(VkPhysicalDevice physical_device, const char* extension)
{
uint32_t properties_count = 0;
vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &properties_count, nullptr);
ImVector<VkExtensionProperties> properties;
properties.resize(properties_count);
vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &properties_count, properties.Data);
return IsExtensionAvailable(properties, extension);
}
static void SetupVulkan(ImVector<const char*> instance_extensions, bool require_descriptor_heap)
{
VkResult err;
#ifdef IMGUI_IMPL_VULKAN_USE_VOLK
@ -127,6 +276,10 @@ 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.sType = VK_STRUCTURE_TYPE_APPLICATION_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
@ -147,10 +300,41 @@ static void SetupVulkan(ImVector<const char*> instance_extensions)
#endif
}
// Select Physical Device (GPU)
g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(g_Instance);
if (require_descriptor_heap)
{
uint32_t device_count = 0;
vkEnumeratePhysicalDevices(g_Instance, &device_count, nullptr);
ImVector<VkPhysicalDevice> physical_devices;
physical_devices.resize(device_count);
vkEnumeratePhysicalDevices(g_Instance, &device_count, physical_devices.Data);
VkPhysicalDevice device_with_heap = VK_NULL_HANDLE;
for (uint32_t i = 0; i < device_count; i++)
{
VkPhysicalDeviceProperties props;
vkGetPhysicalDeviceProperties(physical_devices[i], &props);
const bool has_heap = PhysicalDeviceHasExtension(physical_devices[i], VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME);
if (has_heap && device_with_heap == VK_NULL_HANDLE)
device_with_heap = physical_devices[i];
}
if (device_with_heap == VK_NULL_HANDLE)
{
fprintf(stderr, "Error: --require-descriptor-heap requested but no device with VK_EXT_descriptor_heap was found.\n");
exit(1);
}
g_PhysicalDevice = device_with_heap;
}
else
{
g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(g_Instance);
}
IM_ASSERT(g_PhysicalDevice != VK_NULL_HANDLE);
VkPhysicalDeviceProperties chosen_props;
vkGetPhysicalDeviceProperties(g_PhysicalDevice, &chosen_props);
// Select graphics queue family
g_QueueFamily = ImGui_ImplVulkanH_SelectQueueFamilyIndex(g_PhysicalDevice);
IM_ASSERT(g_QueueFamily != (uint32_t)-1);
@ -170,6 +354,19 @@ 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] = {};
@ -183,14 +380,71 @@ static void SetupVulkan(ImVector<const char*> instance_extensions)
create_info.pQueueCreateInfos = queue_info;
create_info.enabledExtensionCount = (uint32_t)device_extensions.Size;
create_info.ppEnabledExtensionNames = device_extensions.Data;
VkPhysicalDeviceBufferDeviceAddressFeatures buffer_device_addr = {};
buffer_device_addr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES;
buffer_device_addr.bufferDeviceAddress = VK_TRUE;
VkPhysicalDeviceDescriptorHeapFeaturesEXT desc_heap_feat = {};
desc_heap_feat.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT;
desc_heap_feat.pNext = &buffer_device_addr;
desc_heap_feat.descriptorHeap = VK_TRUE;
VkPhysicalDeviceShaderUntypedPointersFeaturesKHR untyped_pointers = {};
untyped_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR;
untyped_pointers.pNext = &desc_heap_feat;
untyped_pointers.shaderUntypedPointers = VK_TRUE;
if (g_UsingDescriptorHeap)
create_info.pNext = &untyped_pointers;
err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device);
check_vk_result(err);
vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue);
}
// Create Descriptor Pool
// If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets.
if (g_UsingDescriptorHeap)
{
g_DescHeapFns.Load(g_Device);
VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptor_heap_properties = {};
descriptor_heap_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT;
VkPhysicalDeviceProperties2 properties2 = {};
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
properties2.pNext = &descriptor_heap_properties;
vkGetPhysicalDeviceProperties2(g_PhysicalDevice, &properties2);
g_ResourceHeapAlloc.Create(g_Device, g_PhysicalDevice, descriptor_heap_properties.imageDescriptorSize, descriptor_heap_properties.resourceHeapAlignment,
descriptor_heap_properties.minResourceHeapReservedRange, APP_RESOURCE_HEAP_SIZE, g_Allocator);
g_SamplerHeapAlloc.Create(g_Device, g_PhysicalDevice, descriptor_heap_properties.samplerDescriptorSize, descriptor_heap_properties.samplerHeapAlignment,
descriptor_heap_properties.minSamplerHeapReservedRange, APP_SAMPLER_HEAP_SIZE, g_Allocator);
// Allocating descriptors is up to the application, so we provide callbacks (same idea as DX12 SrvDescriptorAllocFn/FreeFn).
g_DescriptorHeapInfo.UserContext = nullptr;
g_DescriptorHeapInfo.RegisterImage = [](void*, const VkImageViewCreateInfo* ci) -> uint32_t {
int idx = g_ResourceHeapAlloc.Alloc();
VkImageDescriptorInfoEXT image = {};
image.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT;
image.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image.pView = ci;
VkResourceDescriptorInfoEXT resource = {};
resource.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT;
resource.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
resource.data.pImage = &image;
VkHostAddressRangeEXT addr = g_ResourceHeapAlloc.DescriptorAddress(idx);
check_vk_result(g_DescHeapFns.vkWriteResourceDescriptorsEXT(g_Device, 1, &resource, &addr));
return (uint32_t)idx;
};
g_DescriptorHeapInfo.UnRegisterImage = [](void*, uint32_t idx) { g_ResourceHeapAlloc.Free((int)idx); };
g_DescriptorHeapInfo.RegisterSampler = [](void*, const VkSamplerCreateInfo* ci) -> uint32_t {
int idx = g_SamplerHeapAlloc.Alloc();
VkHostAddressRangeEXT addr = g_SamplerHeapAlloc.DescriptorAddress(idx);
check_vk_result(g_DescHeapFns.vkWriteSamplerDescriptorsEXT(g_Device, 1, ci, &addr));
return (uint32_t)idx;
};
g_DescriptorHeapInfo.UnRegisterSampler = [](void*, uint32_t idx) { g_SamplerHeapAlloc.Free((int)idx); };
}
else
{
// Create Descriptor Pool
// If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets.
VkDescriptorPoolSize pool_sizes[] =
{
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE },
@ -244,7 +498,15 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface
static void CleanupVulkan()
{
vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
if (g_UsingDescriptorHeap)
{
g_ResourceHeapAlloc.Destroy(g_Device, g_Allocator);
g_SamplerHeapAlloc.Destroy(g_Device, g_Allocator);
}
else
{
vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
}
#ifdef APP_USE_VULKAN_DEBUG_REPORT
// Remove the debug report callback
@ -303,6 +565,12 @@ static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
}
if (g_UsingDescriptorHeap)
{
g_DescHeapFns.vkCmdBindResourceHeapEXT(fd->CommandBuffer, &g_ResourceHeapAlloc.BindInfo);
g_DescHeapFns.vkCmdBindSamplerHeapEXT(fd->CommandBuffer, &g_SamplerHeapAlloc.BindInfo);
}
// Record dear imgui primitives into command buffer
ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
@ -350,8 +618,27 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd)
}
// Main code
int main(int, char**)
int main(int argc, char** argv)
{
bool require_descriptor_heap = false;
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "--require-descriptor-heap") == 0)
require_descriptor_heap = true;
else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)
{
printf("Usage: %s [--require-descriptor-heap]\n", argv[0]);
printf(" --require-descriptor-heap Require a device with VK_EXT_descriptor_heap (exit on failure).\n");
printf(" (default) Use ImGui_ImplVulkanH_SelectPhysicalDevice; enable heap if available.\n");
return 0;
}
else
{
fprintf(stderr, "Unknown argument: %s (try --help)\n", argv[i]);
return 1;
}
}
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
return 1;
@ -371,7 +658,7 @@ int main(int, char**)
const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count);
for (uint32_t i = 0; i < extensions_count; i++)
extensions.push_back(glfw_extensions[i]);
SetupVulkan(extensions);
SetupVulkan(extensions, require_descriptor_heap);
// Create Window Surface
VkSurfaceKHR surface;
@ -418,6 +705,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

View File

@ -47,38 +47,6 @@ 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)
{
@ -106,52 +74,6 @@ 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;
@ -194,9 +116,6 @@ 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
@ -240,20 +159,6 @@ 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] = {};
@ -262,15 +167,6 @@ 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;
@ -281,80 +177,8 @@ 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[] =
{
@ -409,17 +233,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface
static void CleanupVulkan()
{
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);
}
vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
#ifdef APP_USE_VULKAN_DEBUG_REPORT
// Remove the debug report callback
@ -478,12 +292,6 @@ 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);
@ -603,8 +411,6 @@ 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