127.3 KB、2375行 生 Blame 履歴
1// dear imgui: Renderer Backend for Vulkan
2// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
3
4// Implemented features:
5// [!] Renderer: User texture binding. Use a VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE 'VkDescriptorSet' as texture identifier. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID/ImTextureRef + https://github.com/ocornut/imgui/pull/914 for discussions.
6// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
7// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
8// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
9// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport).
10
11// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification.
12// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
13
14// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
15// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
16// Learn about Dear ImGui:
17// - FAQ https://dearimgui.com/faq
18// - Getting Started https://dearimgui.com/getting-started
19// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
20// - Introduction, links and more at the top of imgui.cpp
21
22// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
23// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
24// You will use those if you want to use this rendering backend in your engine/app.
25// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
26// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
27// Read comments in imgui_impl_vulkan.h.
28
29// CHANGELOG
30// (minor and older changes stripped away, please see git history for details)
31// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
32// 2026-04-23: Added support for standard draw callbacks (in platform_io): DrawCallback_ResetRenderState, DrawCallback_SetSamplerLinear, DrawCallback_SetSamplerNearest. (#9378)
33// 2026-04-22: *BREAKING CHANGE* redesigned to use separate ImageView + Sampler instead of Combined Image Sampler. This change allows us to facilitate changing samplers, in line with other backends.
34// - When registering custom textures: changed ImGui_ImplVulkan_AddTexture() signature to remove Sampler.
35// - Before: ImGui_ImplVulkan_AddTexture(VkSampler, VkImageView, VkImageLayout)
36// - After: ImGui_ImplVulkan_AddTexture(VkImageView, VkImageLayout)
37// - Kept inline redirection function that ignores the sampler.
38// - When creating your own descriptor pool (instead of letting backend creates its own):
39// - Before: need at least IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER.
40// - After: need at least IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE.
41// + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE descriptors of type VK_DESCRIPTOR_TYPE_SAMPLER.
42// 2026-03-11: Vulkan: Added ImGui_ImplVulkan_PipelineInfo::ExtraDynamicStates[] to allow specifying extra dynamic states to add when creating the VkPipeline. (#9211)
43// 2025-09-26: [Helpers] *BREAKING CHANGE*: Vulkan: Helper ImGui_ImplVulkanH_DestroyWindow() does not call vkDestroySurfaceKHR(): as surface is created by caller of ImGui_ImplVulkanH_CreateOrResizeWindow(), it is more consistent that we don't destroy it. (#9163)
44// 2026-01-05: [Helpers] *BREAKING CHANGE*: Vulkan: Helper for creating render pass uses ImGui_ImplVulkanH_Window::AttachmentDesc to create render pass. Removed ClearEnabled. (#9152)
45// 2025-11-24: [Helpers] Vulkan: Helper for creating a swap-chain (used by examples and multi-viewports) selects VkSwapchainCreateInfoKHR's compositeAlpha based on cap.supportedCompositeAlpha. (#8784)
46// 2025-11-13: [Docking] Handle viewport surface creation failure without crashing. (#9068)
47// 2025-10-15: Vulkan: Added IMGUI_IMPL_VULKAN_VOLK_FILENAME to configure path to volk.h header. (#9008)
48// 2025-09-26: *BREAKING CHANGE*: moved some fields in ImGui_ImplVulkan_InitInfo: init_info.RenderPass --> init_info.PipelineInfoMain.RenderPass, init_info.Subpass --> init_info.PipelineInfoMain.Subpass, init_info.MSAASamples --> init_info.PipelineInfoMain.MSAASamples, init_info.PipelineRenderingCreateInfo --> init_info.PipelineInfoMain.PipelineRenderingCreateInfo.
49// 2025-09-26: *BREAKING CHANGE*: renamed ImGui_ImplVulkan_MainPipelineCreateInfo to ImGui_ImplVulkan_PipelineInfo. Introduced very recently so shouldn't affect many users.
50// 2025-09-26: [Helpers] *BREAKING CHANGE*: Helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a VkImageUsageFlags image_usage` argument, default to VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT if 0.
51// 2025-09-26: Vulkan: Added a way to customize shaders by filling ImGui_ImplVulkan_InitInfo::CustomShaderVertCreateInfo/CustomShaderFragCreateInfo. (#8585)
52// 2025-09-22: [Docking] Added ImGui_ImplVulkanH_GetWindowDataFromViewport() accessor/helper. (#8946, #8940)
53// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
54// 2025-09-04: Vulkan: Added ImGui_ImplVulkan_CreateMainPipeline(). (#8110, #8111)
55// 2025-07-27: Vulkan: Fixed texture update corruption introduced on 2025-06-11. (#8801, #8755, #8840)
56// 2025-07-07: Vulkan: Fixed texture synchronization issue introduced on 2025-06-11. (#8772)
57// 2025-06-27: Vulkan: Fixed validation errors during texture upload/update by aligning upload size to 'nonCoherentAtomSize'. (#8743, #8744)
58// 2025-06-11: Vulkan: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplVulkan_CreateFontsTexture() and ImGui_ImplVulkan_DestroyFontsTexture().
59// 2025-05-07: Vulkan: Fixed validation errors during window detach in multi-viewport mode. (#8600, #8176)
60// 2025-05-07: Vulkan: Load dynamic rendering functions using vkGetDeviceProcAddr() + try both non-KHR and KHR versions. (#8600, #8326, #8365)
61// 2025-04-07: Vulkan: Deep-copy ImGui_ImplVulkan_InitInfo::PipelineRenderingCreateInfo's pColorAttachmentFormats buffer when set, in order to reduce common user-error of specifying a pointer to data that gets out of scope. (#8282)
62// 2025-02-14: *BREAKING CHANGE*: Added uint32_t api_version to ImGui_ImplVulkan_LoadFunctions().
63// 2025-02-13: Vulkan: Added ApiVersion field in ImGui_ImplVulkan_InitInfo. Default to header version if unspecified. Dynamic rendering path loads "vkCmdBeginRendering/vkCmdEndRendering" (without -KHR suffix) on API 1.3. (#8326)
64// 2025-01-09: Vulkan: Added IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE to clarify how many image sampler descriptors are expected to be available in descriptor pool. (#6642)
65// 2025-01-06: [Helpers] Vulkan: Added more ImGui_ImplVulkanH_XXXX helper functions to simplify our examples.
66// 2024-12-11: [Helpers] Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222)
67// 2024-11-27: Vulkan: Make user-provided descriptor pool optional. As a convenience, when setting init_info->DescriptorPoolSize the backend will create one itself. (#8172, #4867)
68// 2024-10-07: Vulkan: Changed default texture sampler to Clamp instead of Repeat/Wrap.
69// 2024-10-07: Vulkan: Expose selected render state in ImGui_ImplVulkan_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
70// 2024-10-07: Vulkan: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
71// 2024-04-19: Vulkan: Added convenience support for Volk via IMGUI_IMPL_VULKAN_USE_VOLK define (you can also use IMGUI_IMPL_VULKAN_NO_PROTOTYPES + wrap Volk via ImGui_ImplVulkan_LoadFunctions().)
72// 2024-02-14: *BREAKING CHANGE*: Moved RenderPass parameter from ImGui_ImplVulkan_Init() function to ImGui_ImplVulkan_InitInfo structure. Not required when using dynamic rendering.
73// 2024-02-12: *BREAKING CHANGE*: Dynamic rendering now require filling PipelineRenderingCreateInfo structure.
74// 2024-01-19: [Helpers] Vulkan: Fixed vkAcquireNextImageKHR() validation errors in VulkanSDK 1.3.275 by allocating one extra semaphore than in-flight frames. (#7236)
75// 2024-01-11: Vulkan: Fixed vkMapMemory() calls unnecessarily using full buffer size (#3957). Fixed MinAllocationSize handing (#7189).
76// 2024-01-03: Vulkan: Added MinAllocationSize field in ImGui_ImplVulkan_InitInfo to workaround zealous "best practice" validation layer. (#7189, #4238)
77// 2024-01-03: Vulkan: Stopped creating command pools with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT as we don't reset them.
78// 2023-11-29: Vulkan: Fixed mismatching allocator passed to vkCreateCommandPool() vs vkDestroyCommandPool(). (#7075)
79// 2023-11-10: *BREAKING CHANGE*: Removed parameter from ImGui_ImplVulkan_CreateFontsTexture(): backend now creates its own command-buffer to upload fonts.
80// *BREAKING CHANGE*: Removed ImGui_ImplVulkan_DestroyFontUploadObjects() which is now unnecessary as we create and destroy those objects in the backend.
81// ImGui_ImplVulkan_CreateFontsTexture() is automatically called by NewFrame() the first time.
82// You can call ImGui_ImplVulkan_CreateFontsTexture() again to recreate the font atlas texture.
83// Added ImGui_ImplVulkan_DestroyFontsTexture() but you probably never need to call this.
84// 2023-07-04: Vulkan: Added optional support for VK_KHR_dynamic_rendering. User needs to set init_info->UseDynamicRendering = true and init_info->ColorAttachmentFormat.
85// 2023-01-02: Vulkan: Fixed sampler passed to ImGui_ImplVulkan_AddTexture() not being honored + removed a bunch of duplicate code.
86// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
87// 2022-10-04: Vulkan: Added experimental ImGui_ImplVulkan_RemoveTexture() for api symmetry. (#914, #5738).
88// 2022-01-20: Vulkan: Added support for ImTextureID as VkDescriptorSet. User need to call ImGui_ImplVulkan_AddTexture(). Building for 32-bit targets requires '#define ImTextureID ImU64'. (#914).
89// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likelihood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame.
90// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
91// 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize.
92// 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer.
93// 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions().
94// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation.
95// 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init).
96// 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices.
97// 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices.
98// 2019-08-01: Vulkan: Added support for specifying multisample count. Set ImGui_ImplVulkan_InitInfo::MSAASamples to one of the VkSampleCountFlagBits values to use, default is non-multisampled as before.
99// 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
100// 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
101// 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount().
102// 2019-04-04: [Helpers] Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper.
103// 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like.
104// 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int).
105// 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display.
106// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
107// 2018-08-25: [Helpers] Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case.
108// 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends.
109// 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example.
110// 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
111// 2018-03-03: [Helpers] Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use.
112// 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology.
113// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself.
114// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
115// 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy.
116// 2016-11-13: Vulkan: Fix validation layer warnings and errors and redeclare gl_PerVertex.
117// 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources.
118// 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active.
119
120#include "imgui.h"
121#ifndef IMGUI_DISABLE
122#include "imgui_impl_vulkan.h"
123#include <stdio.h>
124#ifndef IM_MAX
125#define IM_MAX(A, B) (((A) >= (B)) ? (A) : (B))
126#endif
127#undef Status // X11 headers are leaking this.
128
129// Visual Studio warnings
130#ifdef _MSC_VER
131#pragma warning (disable: 4127) // condition expression is constant
132#endif
133
134// Clang/GCC warnings with -Weverything
135#if defined(__clang__)
136#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
137#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
138#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
139#pragma clang diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader)
140#endif
141
142// Forward Declarations
143struct ImGui_ImplVulkan_FrameRenderBuffers;
144struct ImGui_ImplVulkan_WindowRenderBuffers;
145bool ImGui_ImplVulkan_CreateDeviceObjects();
146void ImGui_ImplVulkan_DestroyDeviceObjects();
147void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator);
148void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator);
149void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator);
150void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator);
151void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator);
152void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage);
153void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator);
154
155// Vulkan prototypes for use with custom loaders
156// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h)
157#if defined(VK_NO_PROTOTYPES) && !defined(VOLK_H_)
158#define IMGUI_IMPL_VULKAN_USE_LOADER
159static bool g_FunctionsLoaded = false;
160#else
161static bool g_FunctionsLoaded = true;
162#endif
163#ifdef IMGUI_IMPL_VULKAN_USE_LOADER
164#define IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_MAP_MACRO) \
165 IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \
166 IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateDescriptorSets) \
167 IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateMemory) \
168 IMGUI_VULKAN_FUNC_MAP_MACRO(vkAcquireNextImageKHR) \
169 IMGUI_VULKAN_FUNC_MAP_MACRO(vkBeginCommandBuffer) \
170 IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindBufferMemory) \
171 IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindImageMemory) \
172 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBeginRenderPass) \
173 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindDescriptorSets) \
174 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindIndexBuffer) \
175 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindPipeline) \
176 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindVertexBuffers) \
177 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdCopyBufferToImage) \
178 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdDrawIndexed) \
179 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdEndRenderPass) \
180 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPipelineBarrier) \
181 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPushConstants) \
182 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetScissor) \
183 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \
184 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \
185 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \
186 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorPool) \
187 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \
188 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFence) \
189 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \
190 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateGraphicsPipelines) \
191 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImage) \
192 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImageView) \
193 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreatePipelineLayout) \
194 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateRenderPass) \
195 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSampler) \
196 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSemaphore) \
197 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateShaderModule) \
198 IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \
199 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \
200 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \
201 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorPool) \
202 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \
203 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \
204 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \
205 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImage) \
206 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImageView) \
207 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipeline) \
208 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipelineLayout) \
209 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyRenderPass) \
210 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySampler) \
211 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySemaphore) \
212 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyShaderModule) \
213 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySurfaceKHR) \
214 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySwapchainKHR) \
215 IMGUI_VULKAN_FUNC_MAP_MACRO(vkDeviceWaitIdle) \
216 IMGUI_VULKAN_FUNC_MAP_MACRO(vkEnumeratePhysicalDevices) \
217 IMGUI_VULKAN_FUNC_MAP_MACRO(vkEndCommandBuffer) \
218 IMGUI_VULKAN_FUNC_MAP_MACRO(vkFlushMappedMemoryRanges) \
219 IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeCommandBuffers) \
220 IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeDescriptorSets) \
221 IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeMemory) \
222 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetBufferMemoryRequirements) \
223 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetDeviceQueue) \
224 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetImageMemoryRequirements) \
225 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceProperties) \
226 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \
227 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceQueueFamilyProperties) \
228 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \
229 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \
230 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \
231 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceSupportKHR) \
232 IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetSwapchainImagesKHR) \
233 IMGUI_VULKAN_FUNC_MAP_MACRO(vkMapMemory) \
234 IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueuePresentKHR) \
235 IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueSubmit) \
236 IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueWaitIdle) \
237 IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetCommandPool) \
238 IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetFences) \
239 IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \
240 IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) \
241 IMGUI_VULKAN_FUNC_MAP_MACRO(vkWaitForFences)
242
243// Define function pointers
244#define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func;
245IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_DEF)
246#undef IMGUI_VULKAN_FUNC_DEF
247#endif // IMGUI_IMPL_VULKAN_USE_LOADER
248
249#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
250static PFN_vkCmdBeginRenderingKHR ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR;
251static PFN_vkCmdEndRenderingKHR ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR;
252#endif
253
254// Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData()
255// [Please zero-clear before use!]
256struct ImGui_ImplVulkan_FrameRenderBuffers
257{
258 VkDeviceMemory VertexBufferMemory;
259 VkDeviceMemory IndexBufferMemory;
260 VkDeviceSize VertexBufferSize;
261 VkDeviceSize IndexBufferSize;
262 VkBuffer VertexBuffer;
263 VkBuffer IndexBuffer;
264};
265
266// Each viewport will hold 1 ImGui_ImplVulkanH_WindowRenderBuffers
267// [Please zero-clear before use!]
268struct ImGui_ImplVulkan_WindowRenderBuffers
269{
270 uint32_t Index;
271 uint32_t Count;
272 ImVector<ImGui_ImplVulkan_FrameRenderBuffers> FrameRenderBuffers;
273};
274
275struct ImGui_ImplVulkan_Texture
276{
277 VkDeviceMemory Memory;
278 VkImage Image;
279 VkImageView ImageView;
280 VkDescriptorSet DescriptorSet;
281
282 ImGui_ImplVulkan_Texture() { memset((void*)this, 0, sizeof(*this)); }
283};
284
285// For multi-viewport support:
286// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
287struct ImGui_ImplVulkan_ViewportData
288{
289 ImGui_ImplVulkanH_Window Window; // Used by secondary viewports only
290 ImGui_ImplVulkan_WindowRenderBuffers RenderBuffers; // Used by all viewports
291 bool WindowOwned;
292 bool SwapChainNeedRebuild; // Flag when viewport swapchain resized in the middle of processing a frame
293 bool SwapChainSuboptimal; // Flag when VK_SUBOPTIMAL_KHR was returned.
294
295 ImGui_ImplVulkan_ViewportData() { WindowOwned = SwapChainNeedRebuild = SwapChainSuboptimal = false; memset((void*)&RenderBuffers, 0, sizeof(RenderBuffers)); }
296 ~ImGui_ImplVulkan_ViewportData() { }
297};
298
299// Vulkan data
300struct ImGui_ImplVulkan_Data
301{
302 ImGui_ImplVulkan_InitInfo VulkanInitInfo;
303 ImGui_ImplVulkan_RenderState* RenderState; // == ImGui::GetPlatformIO().Renderer_RenderState during rendering.
304 VkDeviceSize BufferMemoryAlignment;
305 VkDeviceSize NonCoherentAtomSize;
306 VkPipelineCreateFlags PipelineCreateFlags;
307 VkDescriptorSetLayout DescriptorSetLayoutTexture;
308 VkDescriptorSetLayout DescriptorSetLayoutSampler;
309 VkPipelineLayout PipelineLayout;
310 VkPipeline Pipeline; // pipeline for main render pass (created by app)
311 VkPipeline PipelineForViewports; // pipeline for secondary viewports (created by backend)
312 VkShaderModule ShaderModuleVert;
313 VkShaderModule ShaderModuleFrag;
314 VkDescriptorPool DescriptorPool;
315 ImVector<VkFormat> PipelineRenderingCreateInfoColorAttachmentFormats; // Deep copy of format array
316
317 // Texture management
318 VkSampler SamplerLinear;
319 VkSampler SamplerNearest;
320 VkDescriptorSet SamplerLinearDS;
321 VkDescriptorSet SamplerNearestDS;
322 VkCommandPool TexCommandPool;
323 VkCommandBuffer TexCommandBuffer;
324
325 // Render buffers for main window
326 ImGui_ImplVulkan_WindowRenderBuffers MainWindowRenderBuffers;
327
328 ImGui_ImplVulkan_Data()
329 {
330 memset((void*)this, 0, sizeof(*this));
331 BufferMemoryAlignment = 256;
332 NonCoherentAtomSize = 64;
333 }
334};
335
336//-----------------------------------------------------------------------------
337// SHADERS
338//-----------------------------------------------------------------------------
339
340// Forward Declarations
341static void ImGui_ImplVulkan_InitMultiViewportSupport();
342static void ImGui_ImplVulkan_ShutdownMultiViewportSupport();
343
344// backends/vulkan/glsl_shader.vert, compiled with:
345// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert
346/*
347#version 450 core
348layout(location = 0) in vec2 aPos;
349layout(location = 1) in vec2 aUV;
350layout(location = 2) in vec4 aColor;
351layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc;
352
353out gl_PerVertex { vec4 gl_Position; };
354layout(location = 0) out struct { vec4 Color; vec2 UV; } Out;
355
356void main()
357{
358 Out.Color = aColor;
359 Out.UV = aUV;
360 gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1);
361}
362*/
363static uint32_t __glsl_shader_vert_spv[] =
364{
365 0x07230203,0x00010000,0x0008000b,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b,
366 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
367 0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015,
368 0x0000001b,0x0000001c,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
369 0x00000000,0x00030005,0x00000009,0x00000000,0x00050006,0x00000009,0x00000000,0x6f6c6f43,
370 0x00000072,0x00040006,0x00000009,0x00000001,0x00005655,0x00030005,0x0000000b,0x0074754f,
371 0x00040005,0x0000000f,0x6c6f4361,0x0000726f,0x00030005,0x00000015,0x00565561,0x00060005,
372 0x00000019,0x505f6c67,0x65567265,0x78657472,0x00000000,0x00060006,0x00000019,0x00000000,
373 0x505f6c67,0x7469736f,0x006e6f69,0x00030005,0x0000001b,0x00000000,0x00040005,0x0000001c,
374 0x736f5061,0x00000000,0x00060005,0x0000001e,0x73755075,0x6e6f4368,0x6e617473,0x00000074,
375 0x00050006,0x0000001e,0x00000000,0x61635375,0x0000656c,0x00060006,0x0000001e,0x00000001,
376 0x61725475,0x616c736e,0x00006574,0x00030005,0x00000020,0x00006370,0x00040047,0x0000000b,
377 0x0000001e,0x00000000,0x00040047,0x0000000f,0x0000001e,0x00000002,0x00040047,0x00000015,
378 0x0000001e,0x00000001,0x00030047,0x00000019,0x00000002,0x00050048,0x00000019,0x00000000,
379 0x0000000b,0x00000000,0x00040047,0x0000001c,0x0000001e,0x00000000,0x00030047,0x0000001e,
380 0x00000002,0x00050048,0x0000001e,0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,
381 0x00000001,0x00000023,0x00000008,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,
382 0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017,
383 0x00000008,0x00000006,0x00000002,0x0004001e,0x00000009,0x00000007,0x00000008,0x00040020,
384 0x0000000a,0x00000003,0x00000009,0x0004003b,0x0000000a,0x0000000b,0x00000003,0x00040015,
385 0x0000000c,0x00000020,0x00000001,0x0004002b,0x0000000c,0x0000000d,0x00000000,0x00040020,
386 0x0000000e,0x00000001,0x00000007,0x0004003b,0x0000000e,0x0000000f,0x00000001,0x00040020,
387 0x00000011,0x00000003,0x00000007,0x0004002b,0x0000000c,0x00000013,0x00000001,0x00040020,
388 0x00000014,0x00000001,0x00000008,0x0004003b,0x00000014,0x00000015,0x00000001,0x00040020,
389 0x00000017,0x00000003,0x00000008,0x0003001e,0x00000019,0x00000007,0x00040020,0x0000001a,
390 0x00000003,0x00000019,0x0004003b,0x0000001a,0x0000001b,0x00000003,0x0004003b,0x00000014,
391 0x0000001c,0x00000001,0x0004001e,0x0000001e,0x00000008,0x00000008,0x00040020,0x0000001f,
392 0x00000009,0x0000001e,0x0004003b,0x0000001f,0x00000020,0x00000009,0x00040020,0x00000021,
393 0x00000009,0x00000008,0x0004002b,0x00000006,0x00000028,0x00000000,0x0004002b,0x00000006,
394 0x00000029,0x3f800000,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,
395 0x00000005,0x0004003d,0x00000007,0x00000010,0x0000000f,0x00050041,0x00000011,0x00000012,
396 0x0000000b,0x0000000d,0x0003003e,0x00000012,0x00000010,0x0004003d,0x00000008,0x00000016,
397 0x00000015,0x00050041,0x00000017,0x00000018,0x0000000b,0x00000013,0x0003003e,0x00000018,
398 0x00000016,0x0004003d,0x00000008,0x0000001d,0x0000001c,0x00050041,0x00000021,0x00000022,
399 0x00000020,0x0000000d,0x0004003d,0x00000008,0x00000023,0x00000022,0x00050085,0x00000008,
400 0x00000024,0x0000001d,0x00000023,0x00050041,0x00000021,0x00000025,0x00000020,0x00000013,
401 0x0004003d,0x00000008,0x00000026,0x00000025,0x00050081,0x00000008,0x00000027,0x00000024,
402 0x00000026,0x00050051,0x00000006,0x0000002a,0x00000027,0x00000000,0x00050051,0x00000006,
403 0x0000002b,0x00000027,0x00000001,0x00070050,0x00000007,0x0000002c,0x0000002a,0x0000002b,
404 0x00000028,0x00000029,0x00050041,0x00000011,0x0000002d,0x0000001b,0x0000000d,0x0003003e,
405 0x0000002d,0x0000002c,0x000100fd,0x00010038
406};
407
408// backends/vulkan/glsl_shader.frag, compiled with:
409// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag
410/*
411#version 450 core
412layout(location = 0) out vec4 fColor;
413layout(set=0, binding=0) uniform texture2D _Texture;
414layout(set=1, binding=0) uniform sampler _Sampler;
415layout(location = 0) in struct { vec4 Color; vec2 UV; } In;
416void main()
417{
418 fColor = In.Color * texture(sampler2D(_Texture, _Sampler), In.UV.st);
419}
420*/
421static uint32_t __glsl_shader_frag_spv[] =
422{
423 0x07230203,0x00010000,0x0008000b,0x00000023,0x00000000,0x00020011,0x00000001,0x0006000b,
424 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001,
425 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010,
426 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d,
427 0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000,
428 0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001,
429 0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000015,0x7865545f,0x65727574,
430 0x00000000,0x00050005,0x00000019,0x6d61535f,0x72656c70,0x00000000,0x00040047,0x00000009,
431 0x0000001e,0x00000000,0x00040047,0x0000000d,0x0000001e,0x00000000,0x00040047,0x00000015,
432 0x00000021,0x00000000,0x00040047,0x00000015,0x00000022,0x00000000,0x00040047,0x00000019,
433 0x00000021,0x00000000,0x00040047,0x00000019,0x00000022,0x00000001,0x00020013,0x00000002,
434 0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,
435 0x00000006,0x00000004,0x00040020,0x00000008,0x00000003,0x00000007,0x0004003b,0x00000008,
436 0x00000009,0x00000003,0x00040017,0x0000000a,0x00000006,0x00000002,0x0004001e,0x0000000b,
437 0x00000007,0x0000000a,0x00040020,0x0000000c,0x00000001,0x0000000b,0x0004003b,0x0000000c,
438 0x0000000d,0x00000001,0x00040015,0x0000000e,0x00000020,0x00000001,0x0004002b,0x0000000e,
439 0x0000000f,0x00000000,0x00040020,0x00000010,0x00000001,0x00000007,0x00090019,0x00000013,
440 0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x00040020,
441 0x00000014,0x00000000,0x00000013,0x0004003b,0x00000014,0x00000015,0x00000000,0x0002001a,
442 0x00000017,0x00040020,0x00000018,0x00000000,0x00000017,0x0004003b,0x00000018,0x00000019,
443 0x00000000,0x0003001b,0x0000001b,0x00000013,0x0004002b,0x0000000e,0x0000001d,0x00000001,
444 0x00040020,0x0000001e,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004,0x00000000,
445 0x00000003,0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d,0x0000000f,
446 0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000013,0x00000016,0x00000015,
447 0x0004003d,0x00000017,0x0000001a,0x00000019,0x00050056,0x0000001b,0x0000001c,0x00000016,
448 0x0000001a,0x00050041,0x0000001e,0x0000001f,0x0000000d,0x0000001d,0x0004003d,0x0000000a,
449 0x00000020,0x0000001f,0x00050057,0x00000007,0x00000021,0x0000001c,0x00000020,0x00050085,
450 0x00000007,0x00000022,0x00000012,0x00000021,0x0003003e,0x00000009,0x00000022,0x000100fd,
451 0x00010038
452};
453
454//-----------------------------------------------------------------------------
455// FUNCTIONS
456//-----------------------------------------------------------------------------
457
458// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
459// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
460// FIXME: multi-context support is not tested and probably dysfunctional in this backend.
461static ImGui_ImplVulkan_Data* ImGui_ImplVulkan_GetBackendData()
462{
463 return ImGui::GetCurrentContext() ? (ImGui_ImplVulkan_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
464}
465
466static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, uint32_t type_bits)
467{
468 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
469 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
470 VkPhysicalDeviceMemoryProperties prop;
471 vkGetPhysicalDeviceMemoryProperties(v->PhysicalDevice, &prop);
472 for (uint32_t i = 0; i < prop.memoryTypeCount; i++)
473 if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1 << i))
474 return i;
475 return 0xFFFFFFFF; // Unable to find memoryType
476}
477
478static void check_vk_result(VkResult err)
479{
480 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
481 if (!bd)
482 return;
483 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
484 if (v->CheckVkResultFn)
485 v->CheckVkResultFn(err);
486}
487
488// Same as IM_MEMALIGN(). 'alignment' must be a power of two.
489static inline VkDeviceSize AlignBufferSize(VkDeviceSize size, VkDeviceSize alignment)
490{
491 return (size + alignment - 1) & ~(alignment - 1);
492}
493
494static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& buffer_size, VkDeviceSize new_size, VkBufferUsageFlagBits usage)
495{
496 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
497 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
498 VkResult err;
499 if (buffer != VK_NULL_HANDLE)
500 vkDestroyBuffer(v->Device, buffer, v->Allocator);
501 if (buffer_memory != VK_NULL_HANDLE)
502 vkFreeMemory(v->Device, buffer_memory, v->Allocator);
503
504 VkDeviceSize buffer_size_aligned = AlignBufferSize(IM_MAX(v->MinAllocationSize, new_size), bd->BufferMemoryAlignment);
505 VkBufferCreateInfo buffer_info = {};
506 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
507 buffer_info.size = buffer_size_aligned;
508 buffer_info.usage = usage;
509 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
510 err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &buffer);
511 check_vk_result(err);
512
513 VkMemoryRequirements req;
514 vkGetBufferMemoryRequirements(v->Device, buffer, &req);
515 bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment;
516 VkMemoryAllocateInfo alloc_info = {};
517 alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
518 alloc_info.allocationSize = req.size;
519 alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits);
520 err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &buffer_memory);
521 check_vk_result(err);
522
523 err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0);
524 check_vk_result(err);
525 buffer_size = buffer_size_aligned;
526}
527
528static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkan_FrameRenderBuffers* rb, int fb_width, int fb_height)
529{
530 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
531
532 // Bind pipeline:
533 vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
534
535 // Bind Vertex And Index Buffer:
536 if (draw_data->TotalVtxCount > 0)
537 {
538 VkBuffer vertex_buffers[1] = { rb->VertexBuffer };
539 VkDeviceSize vertex_offset[1] = { 0 };
540 vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, vertex_offset);
541 vkCmdBindIndexBuffer(command_buffer, rb->IndexBuffer, 0, sizeof(ImDrawIdx) == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32);
542 }
543
544 // Setup viewport:
545 VkViewport viewport;
546 viewport.x = 0;
547 viewport.y = 0;
548 viewport.width = (float)fb_width;
549 viewport.height = (float)fb_height;
550 viewport.minDepth = 0.0f;
551 viewport.maxDepth = 1.0f;
552 vkCmdSetViewport(command_buffer, 0, 1, &viewport);
553
554 // Setup scale and translation:
555 // 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.
556 float constants[4];
557 constants[0] = 2.0f / draw_data->DisplaySize.x; // Scale
558 constants[1] = 2.0f / draw_data->DisplaySize.y;
559 constants[2] = -1.0f - draw_data->DisplayPos.x * constants[0]; // Translate
560 constants[3] = -1.0f - draw_data->DisplayPos.y * constants[1];
561 vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(float) * 4, constants);
562
563 // Setup sampler
564 vkCmdBindDescriptorSets(bd->RenderState->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 1, 1, &bd->SamplerLinearDS, 0, nullptr);
565}
566
567// Draw callbacks
568static 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.
569static 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); }
570static 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); }
571
572// 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.
573/*
574void ImGui_ImplVulkan_DrawCallback_SetSamplerCustom(const ImDrawList*, const ImDrawCmd* cmd)
575{
576 ImGui_ImplVulkan_RenderState* render_state = (ImGui_ImplVulkan_RenderState*)ImGui::GetPlatformIO().Renderer_RenderState;
577 vkCmdBindDescriptorSets(render_state->CommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, render_state->PipelineLayout, 1, 1, (VkDescriptorSet*)cmd->UserCallbackData, 0, nullptr);
578}
579*/
580
581// Render function
582void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline)
583{
584 // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
585 int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
586 int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
587 if (fb_width <= 0 || fb_height <= 0)
588 return;
589
590 // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
591 // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
592 if (draw_data->Textures != nullptr)
593 for (ImTextureData* tex : *draw_data->Textures)
594 if (tex->Status != ImTextureStatus_OK)
595 ImGui_ImplVulkan_UpdateTexture(tex);
596
597 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
598 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
599 if (pipeline == VK_NULL_HANDLE)
600 pipeline = bd->Pipeline;
601
602 // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage.
603 ImGui_ImplVulkan_ViewportData* viewport_renderer_data = (ImGui_ImplVulkan_ViewportData*)draw_data->OwnerViewport->RendererUserData;
604 IM_ASSERT(viewport_renderer_data != nullptr);
605 ImGui_ImplVulkan_WindowRenderBuffers* wrb = &viewport_renderer_data->RenderBuffers;
606 if (wrb->FrameRenderBuffers.Size == 0)
607 {
608 wrb->Index = 0;
609 wrb->Count = v->ImageCount;
610 wrb->FrameRenderBuffers.resize(wrb->Count);
611 memset((void*)wrb->FrameRenderBuffers.Data, 0, wrb->FrameRenderBuffers.size_in_bytes());
612 }
613 IM_ASSERT(wrb->Count == v->ImageCount);
614 wrb->Index = (wrb->Index + 1) % wrb->Count;
615 ImGui_ImplVulkan_FrameRenderBuffers* rb = &wrb->FrameRenderBuffers[wrb->Index];
616
617 if (draw_data->TotalVtxCount > 0)
618 {
619 // Create or resize the vertex/index buffers
620 VkDeviceSize vertex_size = AlignBufferSize(draw_data->TotalVtxCount * sizeof(ImDrawVert), bd->BufferMemoryAlignment);
621 VkDeviceSize index_size = AlignBufferSize(draw_data->TotalIdxCount * sizeof(ImDrawIdx), bd->BufferMemoryAlignment);
622 if (rb->VertexBuffer == VK_NULL_HANDLE || rb->VertexBufferSize < vertex_size)
623 CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
624 if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size)
625 CreateOrResizeBuffer(rb->IndexBuffer, rb->IndexBufferMemory, rb->IndexBufferSize, index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
626
627 // Upload vertex/index data into a single contiguous GPU buffer
628 ImDrawVert* vtx_dst = nullptr;
629 ImDrawIdx* idx_dst = nullptr;
630 VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, vertex_size, 0, (void**)&vtx_dst);
631 check_vk_result(err);
632 err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, index_size, 0, (void**)&idx_dst);
633 check_vk_result(err);
634 for (const ImDrawList* draw_list : draw_data->CmdLists)
635 {
636 memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
637 memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
638 vtx_dst += draw_list->VtxBuffer.Size;
639 idx_dst += draw_list->IdxBuffer.Size;
640 }
641 VkMappedMemoryRange range[2] = {};
642 range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
643 range[0].memory = rb->VertexBufferMemory;
644 range[0].size = VK_WHOLE_SIZE;
645 range[1].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
646 range[1].memory = rb->IndexBufferMemory;
647 range[1].size = VK_WHOLE_SIZE;
648 err = vkFlushMappedMemoryRanges(v->Device, 2, range);
649 check_vk_result(err);
650 vkUnmapMemory(v->Device, rb->VertexBufferMemory);
651 vkUnmapMemory(v->Device, rb->IndexBufferMemory);
652 }
653
654 // Setup render state structure (for callbacks and custom texture bindings)
655 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
656 ImGui_ImplVulkan_RenderState render_state;
657 render_state.CommandBuffer = command_buffer;
658 render_state.Pipeline = pipeline;
659 render_state.PipelineLayout = bd->PipelineLayout;
660 platform_io.Renderer_RenderState = bd->RenderState = &render_state;
661
662 // Setup desired Vulkan state
663 ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height);
664
665 // Will project scissor/clipping rectangles into framebuffer space
666 ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
667 ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
668
669 // Render command lists
670 // (Because we merged all buffers into a single one, we maintain our own offset into them)
671 VkDescriptorSet last_image_view = VK_NULL_HANDLE;
672 int global_vtx_offset = 0;
673 int global_idx_offset = 0;
674 for (const ImDrawList* draw_list : draw_data->CmdLists)
675 {
676 for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
677 {
678 const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
679 if (pcmd->UserCallback != nullptr)
680 {
681 // User callback, registered via ImDrawList::AddCallback()
682 if (pcmd->UserCallback == ImGui_ImplVulkan_DrawCallback_ResetRenderState)
683 {
684 ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height);
685 last_image_view = VK_NULL_HANDLE;
686 }
687 else
688 pcmd->UserCallback(draw_list, pcmd);
689 }
690 else
691 {
692 // Project scissor/clipping rectangles into framebuffer space
693 ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
694 ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
695
696 // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds
697 if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
698 if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
699 if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; }
700 if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; }
701 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
702 continue;
703
704 // Apply scissor/clipping rectangle
705 VkRect2D scissor;
706 scissor.offset.x = (int32_t)(clip_min.x);
707 scissor.offset.y = (int32_t)(clip_min.y);
708 scissor.extent.width = (uint32_t)(clip_max.x - clip_min.x);
709 scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y);
710 vkCmdSetScissor(command_buffer, 0, 1, &scissor);
711
712 // Bind DescriptorSets for image view (font or user texture) and samplers
713 VkDescriptorSet image_view = (VkDescriptorSet)pcmd->GetTexID();
714 if (image_view != last_image_view)
715 vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &image_view, 0, nullptr);
716 last_image_view = image_view;
717
718 // Draw
719 vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
720 }
721 }
722 global_idx_offset += draw_list->IdxBuffer.Size;
723 global_vtx_offset += draw_list->VtxBuffer.Size;
724 }
725 platform_io.Renderer_RenderState = bd->RenderState = nullptr;
726
727 // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called.
728 // Our last values will leak into user/application rendering IF:
729 // - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state
730 // - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitly set that state.
731 // If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering.
732 // In theory we should aim to backup/restore those values but I am not sure this is possible.
733 // We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644)
734 VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } };
735 vkCmdSetScissor(command_buffer, 0, 1, &scissor);
736}
737
738static void ImGui_ImplVulkan_DestroyTexture(ImTextureData* tex)
739{
740 if (ImGui_ImplVulkan_Texture* backend_tex = (ImGui_ImplVulkan_Texture*)tex->BackendUserData)
741 {
742 IM_ASSERT(backend_tex->DescriptorSet == (VkDescriptorSet)tex->TexID);
743 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
744 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
745 ImGui_ImplVulkan_RemoveTexture(backend_tex->DescriptorSet);
746 vkDestroyImageView(v->Device, backend_tex->ImageView, v->Allocator);
747 vkDestroyImage(v->Device, backend_tex->Image, v->Allocator);
748 vkFreeMemory(v->Device, backend_tex->Memory, v->Allocator);
749 IM_DELETE(backend_tex);
750
751 // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
752 tex->SetTexID(ImTextureID_Invalid);
753 tex->BackendUserData = nullptr;
754 }
755 tex->SetStatus(ImTextureStatus_Destroyed);
756}
757
758void ImGui_ImplVulkan_UpdateTexture(ImTextureData* tex)
759{
760 if (tex->Status == ImTextureStatus_OK)
761 return;
762 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
763 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
764 VkResult err;
765
766 if (tex->Status == ImTextureStatus_WantCreate)
767 {
768 // Create and upload new texture to graphics system
769 //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
770 IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
771 IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
772 ImGui_ImplVulkan_Texture* backend_tex = IM_NEW(ImGui_ImplVulkan_Texture)();
773
774 // Create the Image:
775 {
776 VkImageCreateInfo info = {};
777 info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
778 info.imageType = VK_IMAGE_TYPE_2D;
779 info.format = VK_FORMAT_R8G8B8A8_UNORM;
780 info.extent.width = tex->Width;
781 info.extent.height = tex->Height;
782 info.extent.depth = 1;
783 info.mipLevels = 1;
784 info.arrayLayers = 1;
785 info.samples = VK_SAMPLE_COUNT_1_BIT;
786 info.tiling = VK_IMAGE_TILING_OPTIMAL;
787 info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
788 info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
789 info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
790 err = vkCreateImage(v->Device, &info, v->Allocator, &backend_tex->Image);
791 check_vk_result(err);
792 VkMemoryRequirements req;
793 vkGetImageMemoryRequirements(v->Device, backend_tex->Image, &req);
794 VkMemoryAllocateInfo alloc_info = {};
795 alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
796 alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size);
797 alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits);
798 err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &backend_tex->Memory);
799 check_vk_result(err);
800 err = vkBindImageMemory(v->Device, backend_tex->Image, backend_tex->Memory, 0);
801 check_vk_result(err);
802 }
803
804 // Create the Image View:
805 {
806 VkImageViewCreateInfo info = {};
807 info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
808 info.image = backend_tex->Image;
809 info.viewType = VK_IMAGE_VIEW_TYPE_2D;
810 info.format = VK_FORMAT_R8G8B8A8_UNORM;
811 info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
812 info.subresourceRange.levelCount = 1;
813 info.subresourceRange.layerCount = 1;
814 err = vkCreateImageView(v->Device, &info, v->Allocator, &backend_tex->ImageView);
815 check_vk_result(err);
816 }
817
818 // Create the Descriptor Set
819 backend_tex->DescriptorSet = ImGui_ImplVulkan_AddTexture(backend_tex->ImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
820
821 // Store identifiers
822 tex->SetTexID((ImTextureID)backend_tex->DescriptorSet);
823 tex->BackendUserData = backend_tex;
824 }
825
826 if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates)
827 {
828 ImGui_ImplVulkan_Texture* backend_tex = (ImGui_ImplVulkan_Texture*)tex->BackendUserData;
829
830 // Update full texture or selected blocks. We only ever write to textures regions which have never been used before!
831 // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions.
832 // We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture.
833 const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x;
834 const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y;
835 const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w;
836 const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h;
837
838 // Create the Upload Buffer:
839 VkDeviceMemory upload_buffer_memory;
840
841 VkBuffer upload_buffer;
842 VkDeviceSize upload_pitch = upload_w * tex->BytesPerPixel;
843 VkDeviceSize upload_size = AlignBufferSize(upload_h * upload_pitch, bd->NonCoherentAtomSize);
844 {
845 VkBufferCreateInfo buffer_info = {};
846 buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
847 buffer_info.size = upload_size;
848 buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
849 buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
850 err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &upload_buffer);
851 check_vk_result(err);
852 VkMemoryRequirements req;
853 vkGetBufferMemoryRequirements(v->Device, upload_buffer, &req);
854 bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment;
855 VkMemoryAllocateInfo alloc_info = {};
856 alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
857 alloc_info.allocationSize = IM_MAX(v->MinAllocationSize, req.size);
858 alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits);
859 err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &upload_buffer_memory);
860 check_vk_result(err);
861 err = vkBindBufferMemory(v->Device, upload_buffer, upload_buffer_memory, 0);
862 check_vk_result(err);
863 }
864
865 // Upload to Buffer:
866 {
867 char* map = nullptr;
868 err = vkMapMemory(v->Device, upload_buffer_memory, 0, upload_size, 0, (void**)(&map));
869 check_vk_result(err);
870 for (int y = 0; y < upload_h; y++)
871 memcpy(map + upload_pitch * y, tex->GetPixelsAt(upload_x, upload_y + y), (size_t)upload_pitch);
872 VkMappedMemoryRange range[1] = {};
873 range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
874 range[0].memory = upload_buffer_memory;
875 range[0].size = upload_size;
876 err = vkFlushMappedMemoryRanges(v->Device, 1, range);
877 check_vk_result(err);
878 vkUnmapMemory(v->Device, upload_buffer_memory);
879 }
880
881 // Start command buffer
882 {
883 err = vkResetCommandPool(v->Device, bd->TexCommandPool, 0);
884 check_vk_result(err);
885 VkCommandBufferBeginInfo begin_info = {};
886 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
887 begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
888 err = vkBeginCommandBuffer(bd->TexCommandBuffer, &begin_info);
889 check_vk_result(err);
890 }
891
892 // Copy to Image:
893 {
894 VkBufferMemoryBarrier upload_barrier[1] = {};
895 upload_barrier[0].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
896 upload_barrier[0].srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
897 upload_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
898 upload_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
899 upload_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
900 upload_barrier[0].buffer = upload_buffer;
901 upload_barrier[0].offset = 0;
902 upload_barrier[0].size = upload_size;
903
904 VkImageMemoryBarrier copy_barrier[1] = {};
905 copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
906 copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
907 copy_barrier[0].oldLayout = (tex->Status == ImTextureStatus_WantCreate) ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
908 copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
909 copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
910 copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
911 copy_barrier[0].image = backend_tex->Image;
912 copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
913 copy_barrier[0].subresourceRange.levelCount = 1;
914 copy_barrier[0].subresourceRange.layerCount = 1;
915 vkCmdPipelineBarrier(bd->TexCommandBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1, upload_barrier, 1, copy_barrier);
916
917 VkBufferImageCopy region = {};
918 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
919 region.imageSubresource.layerCount = 1;
920 region.imageExtent.width = upload_w;
921 region.imageExtent.height = upload_h;
922 region.imageExtent.depth = 1;
923 region.imageOffset.x = upload_x;
924 region.imageOffset.y = upload_y;
925 vkCmdCopyBufferToImage(bd->TexCommandBuffer, upload_buffer, backend_tex->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
926
927 VkImageMemoryBarrier use_barrier[1] = {};
928 use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
929 use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
930 use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
931 use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
932 use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
933 use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
934 use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
935 use_barrier[0].image = backend_tex->Image;
936 use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
937 use_barrier[0].subresourceRange.levelCount = 1;
938 use_barrier[0].subresourceRange.layerCount = 1;
939 vkCmdPipelineBarrier(bd->TexCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier);
940 }
941
942 // End command buffer
943 {
944 VkSubmitInfo end_info = {};
945 end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
946 end_info.commandBufferCount = 1;
947 end_info.pCommandBuffers = &bd->TexCommandBuffer;
948 err = vkEndCommandBuffer(bd->TexCommandBuffer);
949 check_vk_result(err);
950 err = vkQueueSubmit(v->Queue, 1, &end_info, VK_NULL_HANDLE);
951 check_vk_result(err);
952 }
953
954 err = vkQueueWaitIdle(v->Queue); // FIXME-OPT: Suboptimal!
955 check_vk_result(err);
956 vkDestroyBuffer(v->Device, upload_buffer, v->Allocator);
957 vkFreeMemory(v->Device, upload_buffer_memory, v->Allocator);
958
959 tex->SetStatus(ImTextureStatus_OK);
960 }
961
962 if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames >= (int)bd->VulkanInitInfo.ImageCount)
963 ImGui_ImplVulkan_DestroyTexture(tex);
964}
965
966static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator)
967{
968 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
969 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
970 if (bd->ShaderModuleVert == VK_NULL_HANDLE)
971 {
972 VkShaderModuleCreateInfo default_vert_info = {};
973 default_vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
974 default_vert_info.codeSize = sizeof(__glsl_shader_vert_spv);
975 default_vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv;
976 VkShaderModuleCreateInfo* p_vert_info = (v->CustomShaderVertCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) ? &v->CustomShaderVertCreateInfo : &default_vert_info;
977 VkResult err = vkCreateShaderModule(device, p_vert_info, allocator, &bd->ShaderModuleVert);
978 check_vk_result(err);
979 }
980 if (bd->ShaderModuleFrag == VK_NULL_HANDLE)
981 {
982 VkShaderModuleCreateInfo default_frag_info = {};
983 default_frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
984 default_frag_info.codeSize = sizeof(__glsl_shader_frag_spv);
985 default_frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv;
986 VkShaderModuleCreateInfo* p_frag_info = (v->CustomShaderFragCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) ? &v->CustomShaderFragCreateInfo : &default_frag_info;
987 VkResult err = vkCreateShaderModule(device, p_frag_info, allocator, &bd->ShaderModuleFrag);
988 check_vk_result(err);
989 }
990}
991
992#if !defined(IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING) && !(defined(VK_VERSION_1_3) || defined(VK_KHR_dynamic_rendering))
993typedef void VkPipelineRenderingCreateInfoKHR;
994#endif
995
996static VkPipeline ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, const ImGui_ImplVulkan_PipelineInfo* info)
997{
998 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
999 ImGui_ImplVulkan_CreateShaderModules(device, allocator);
1000
1001 VkPipelineShaderStageCreateInfo stage[2] = {};
1002 stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1003 stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
1004 stage[0].module = bd->ShaderModuleVert;
1005 stage[0].pName = "main";
1006 stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
1007 stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
1008 stage[1].module = bd->ShaderModuleFrag;
1009 stage[1].pName = "main";
1010
1011 VkVertexInputBindingDescription binding_desc[1] = {};
1012 binding_desc[0].stride = sizeof(ImDrawVert);
1013 binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
1014
1015 VkVertexInputAttributeDescription attribute_desc[3] = {};
1016 attribute_desc[0].location = 0;
1017 attribute_desc[0].binding = binding_desc[0].binding;
1018 attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT;
1019 attribute_desc[0].offset = offsetof(ImDrawVert, pos);
1020 attribute_desc[1].location = 1;
1021 attribute_desc[1].binding = binding_desc[0].binding;
1022 attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT;
1023 attribute_desc[1].offset = offsetof(ImDrawVert, uv);
1024 attribute_desc[2].location = 2;
1025 attribute_desc[2].binding = binding_desc[0].binding;
1026 attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM;
1027 attribute_desc[2].offset = offsetof(ImDrawVert, col);
1028
1029 VkPipelineVertexInputStateCreateInfo vertex_info = {};
1030 vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
1031 vertex_info.vertexBindingDescriptionCount = 1;
1032 vertex_info.pVertexBindingDescriptions = binding_desc;
1033 vertex_info.vertexAttributeDescriptionCount = 3;
1034 vertex_info.pVertexAttributeDescriptions = attribute_desc;
1035
1036 VkPipelineInputAssemblyStateCreateInfo ia_info = {};
1037 ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
1038 ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
1039
1040 VkPipelineViewportStateCreateInfo viewport_info = {};
1041 viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
1042 viewport_info.viewportCount = 1;
1043 viewport_info.scissorCount = 1;
1044
1045 VkPipelineRasterizationStateCreateInfo raster_info = {};
1046 raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
1047 raster_info.polygonMode = VK_POLYGON_MODE_FILL;
1048 raster_info.cullMode = VK_CULL_MODE_NONE;
1049 raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
1050 raster_info.lineWidth = 1.0f;
1051
1052 VkPipelineMultisampleStateCreateInfo ms_info = {};
1053 ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
1054 ms_info.rasterizationSamples = (info->MSAASamples != 0) ? info->MSAASamples : VK_SAMPLE_COUNT_1_BIT;
1055
1056 VkPipelineColorBlendAttachmentState color_attachment[1] = {};
1057 color_attachment[0].blendEnable = VK_TRUE;
1058 color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
1059 color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1060 color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD;
1061 color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
1062 color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
1063 color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD;
1064 color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
1065
1066 VkPipelineDepthStencilStateCreateInfo depth_info = {};
1067 depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
1068
1069 VkPipelineColorBlendStateCreateInfo blend_info = {};
1070 blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
1071 blend_info.attachmentCount = 1;
1072 blend_info.pAttachments = color_attachment;
1073
1074 ImVector<VkDynamicState> dynamic_states = info->ExtraDynamicStates;
1075 dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT);
1076 dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR);
1077 VkPipelineDynamicStateCreateInfo dynamic_state = {};
1078 dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
1079 dynamic_state.dynamicStateCount = dynamic_states.Size;
1080 dynamic_state.pDynamicStates = dynamic_states.Data;
1081
1082 VkGraphicsPipelineCreateInfo create_info = {};
1083 create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
1084 create_info.flags = bd->PipelineCreateFlags;
1085 create_info.stageCount = 2;
1086 create_info.pStages = stage;
1087 create_info.pVertexInputState = &vertex_info;
1088 create_info.pInputAssemblyState = &ia_info;
1089 create_info.pViewportState = &viewport_info;
1090 create_info.pRasterizationState = &raster_info;
1091 create_info.pMultisampleState = &ms_info;
1092 create_info.pDepthStencilState = &depth_info;
1093 create_info.pColorBlendState = &blend_info;
1094 create_info.pDynamicState = &dynamic_state;
1095 create_info.layout = bd->PipelineLayout;
1096 create_info.renderPass = info->RenderPass;
1097 create_info.subpass = info->Subpass;
1098
1099#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
1100 if (bd->VulkanInitInfo.UseDynamicRendering)
1101 {
1102 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");
1103 IM_ASSERT(info->PipelineRenderingCreateInfo.pNext == nullptr && "PipelineRenderingCreateInfo::pNext must be nullptr");
1104 create_info.pNext = &info->PipelineRenderingCreateInfo;
1105 create_info.renderPass = VK_NULL_HANDLE; // Just make sure it's actually nullptr.
1106 }
1107#endif
1108 VkPipeline pipeline;
1109 VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &create_info, allocator, &pipeline);
1110 check_vk_result(err);
1111 return pipeline;
1112}
1113
1114static void ImGui_ImplVulkan_CreateDescriptorSetLayout(VkDescriptorSetLayout* p_layout, VkDescriptorType descriptor_type)
1115{
1116 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1117 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
1118
1119 VkDescriptorSetLayoutBinding binding[1] = {};
1120 binding[0].descriptorType = descriptor_type;
1121 binding[0].descriptorCount = 1;
1122 binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
1123 VkDescriptorSetLayoutCreateInfo info = {};
1124 info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1125 info.bindingCount = 1;
1126 info.pBindings = binding;
1127 VkResult err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, p_layout);
1128 check_vk_result(err);
1129}
1130
1131static VkDescriptorSet ImGui_ImplVulkan_CreateSamplerDS(VkSampler sampler)
1132{
1133 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1134 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
1135
1136 VkDescriptorSet descriptor_set;
1137 VkDescriptorSetAllocateInfo alloc_info = {};
1138 alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
1139 alloc_info.descriptorPool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;
1140 alloc_info.descriptorSetCount = 1;
1141 alloc_info.pSetLayouts = &bd->DescriptorSetLayoutSampler;
1142 VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set);
1143 check_vk_result(err);
1144
1145 VkDescriptorImageInfo desc_image = {};
1146 desc_image.sampler = sampler;
1147 VkWriteDescriptorSet sampler_write_desc = {};
1148 sampler_write_desc.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1149 sampler_write_desc.dstSet = descriptor_set;
1150 sampler_write_desc.descriptorCount = 1;
1151 sampler_write_desc.dstBinding = 0;
1152 sampler_write_desc.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
1153 sampler_write_desc.pImageInfo = &desc_image;
1154 vkUpdateDescriptorSets(v->Device, 1, &sampler_write_desc, 0, nullptr);
1155 return descriptor_set;
1156}
1157
1158bool ImGui_ImplVulkan_CreateDeviceObjects()
1159{
1160 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1161 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
1162 VkResult err;
1163
1164 if (!bd->DescriptorSetLayoutTexture)
1165 ImGui_ImplVulkan_CreateDescriptorSetLayout(&bd->DescriptorSetLayoutTexture, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
1166 if (!bd->DescriptorSetLayoutSampler)
1167 ImGui_ImplVulkan_CreateDescriptorSetLayout(&bd->DescriptorSetLayoutSampler, VK_DESCRIPTOR_TYPE_SAMPLER);
1168
1169 if (v->DescriptorPoolSize != 0)
1170 {
1171 IM_ASSERT(v->DescriptorPoolSize >= IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE);
1172 VkDescriptorPoolSize pool_sizes[] =
1173 {
1174 { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, v->DescriptorPoolSize },
1175 { VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE },
1176 };
1177 VkDescriptorPoolCreateInfo pool_info = {};
1178 pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1179 pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
1180 pool_info.maxSets = v->DescriptorPoolSize + IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE;
1181 pool_info.poolSizeCount = (uint32_t)IM_COUNTOF(pool_sizes);
1182 pool_info.pPoolSizes = pool_sizes;
1183 err = vkCreateDescriptorPool(v->Device, &pool_info, v->Allocator, &bd->DescriptorPool);
1184 check_vk_result(err);
1185 }
1186
1187 // Create samplers
1188 // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
1189 if (!bd->SamplerLinear || !bd->SamplerNearest)
1190 {
1191 VkSamplerCreateInfo info = {};
1192 info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1193 info.magFilter = VK_FILTER_LINEAR;
1194 info.minFilter = VK_FILTER_LINEAR;
1195 info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1196 info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
1197 info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
1198 info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
1199 info.minLod = -1000;
1200 info.maxLod = 1000;
1201 info.maxAnisotropy = 1.0f;
1202 err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->SamplerLinear);
1203 check_vk_result(err);
1204 bd->SamplerLinearDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerLinear);
1205
1206 info.magFilter = VK_FILTER_NEAREST;
1207 info.minFilter = VK_FILTER_NEAREST;
1208 info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
1209 err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->SamplerNearest);
1210 check_vk_result(err);
1211 bd->SamplerNearestDS = ImGui_ImplVulkan_CreateSamplerDS(bd->SamplerNearest);
1212 }
1213
1214 if (!bd->PipelineLayout)
1215 {
1216 // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
1217 VkPushConstantRange push_constants[1] = {};
1218 push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
1219 push_constants[0].offset = sizeof(float) * 0;
1220 push_constants[0].size = sizeof(float) * 4;
1221 VkDescriptorSetLayout set_layout[2] = { bd->DescriptorSetLayoutTexture, bd->DescriptorSetLayoutSampler };
1222 VkPipelineLayoutCreateInfo layout_info = {};
1223 layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
1224 layout_info.setLayoutCount = 2;
1225 layout_info.pSetLayouts = set_layout;
1226 layout_info.pushConstantRangeCount = 1;
1227 layout_info.pPushConstantRanges = push_constants;
1228 err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &bd->PipelineLayout);
1229 check_vk_result(err);
1230 }
1231
1232 // Create pipeline
1233 bool create_main_pipeline = (v->PipelineInfoMain.RenderPass != VK_NULL_HANDLE);
1234#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
1235 create_main_pipeline |= (v->UseDynamicRendering && v->PipelineInfoMain.PipelineRenderingCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR);
1236#endif
1237 if (create_main_pipeline)
1238 ImGui_ImplVulkan_CreateMainPipeline(&v->PipelineInfoMain);
1239
1240 // Create command pool/buffer for texture upload
1241 if (!bd->TexCommandPool)
1242 {
1243 VkCommandPoolCreateInfo info = {};
1244 info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1245 info.flags = 0;
1246 info.queueFamilyIndex = v->QueueFamily;
1247 err = vkCreateCommandPool(v->Device, &info, v->Allocator, &bd->TexCommandPool);
1248 check_vk_result(err);
1249 }
1250 if (!bd->TexCommandBuffer)
1251 {
1252 VkCommandBufferAllocateInfo info = {};
1253 info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1254 info.commandPool = bd->TexCommandPool;
1255 info.commandBufferCount = 1;
1256 err = vkAllocateCommandBuffers(v->Device, &info, &bd->TexCommandBuffer);
1257 check_vk_result(err);
1258 }
1259
1260 return true;
1261}
1262
1263void ImGui_ImplVulkan_CreateMainPipeline(const ImGui_ImplVulkan_PipelineInfo* pipeline_info_in)
1264{
1265 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1266 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
1267 if (bd->Pipeline)
1268 {
1269 vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator);
1270 bd->Pipeline = VK_NULL_HANDLE;
1271 }
1272 ImGui_ImplVulkan_PipelineInfo* pipeline_info = &v->PipelineInfoMain;
1273 if (pipeline_info != pipeline_info_in)
1274 *pipeline_info = *pipeline_info_in;
1275
1276#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
1277 VkPipelineRenderingCreateInfoKHR* pipeline_rendering_create_info = &pipeline_info->PipelineRenderingCreateInfo;
1278 if (v->UseDynamicRendering && pipeline_rendering_create_info->pColorAttachmentFormats != nullptr)
1279 {
1280 // Deep copy buffer to reduce error-rate for end user (#8282)
1281 ImVector<VkFormat> formats;
1282 formats.resize((int)pipeline_rendering_create_info->colorAttachmentCount);
1283 memcpy(formats.Data, pipeline_rendering_create_info->pColorAttachmentFormats, (size_t)formats.size_in_bytes());
1284 formats.swap(bd->PipelineRenderingCreateInfoColorAttachmentFormats);
1285 pipeline_rendering_create_info->pColorAttachmentFormats = bd->PipelineRenderingCreateInfoColorAttachmentFormats.Data;
1286 }
1287#endif
1288 bd->Pipeline = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, pipeline_info);
1289}
1290
1291void ImGui_ImplVulkan_DestroyDeviceObjects()
1292{
1293 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1294 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
1295 ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator);
1296
1297 // Destroy all textures
1298 for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
1299 if (tex->RefCount == 1)
1300 ImGui_ImplVulkan_DestroyTexture(tex);
1301
1302 if (bd->TexCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->TexCommandPool, 1, &bd->TexCommandBuffer); bd->TexCommandBuffer = VK_NULL_HANDLE; }
1303 if (bd->TexCommandPool) { vkDestroyCommandPool(v->Device, bd->TexCommandPool, v->Allocator); bd->TexCommandPool = VK_NULL_HANDLE; }
1304 if (bd->SamplerLinear) { vkDestroySampler(v->Device, bd->SamplerLinear, v->Allocator); bd->SamplerLinear = VK_NULL_HANDLE; }
1305 if (bd->SamplerNearest) { vkDestroySampler(v->Device, bd->SamplerNearest, v->Allocator); bd->SamplerNearest = VK_NULL_HANDLE; }
1306 if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; }
1307 if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; }
1308 if (bd->DescriptorSetLayoutTexture) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayoutTexture, v->Allocator); bd->DescriptorSetLayoutTexture = VK_NULL_HANDLE; }
1309 if (bd->DescriptorSetLayoutSampler) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayoutSampler, v->Allocator); bd->DescriptorSetLayoutSampler = VK_NULL_HANDLE; }
1310 if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; }
1311 if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; }
1312 if (bd->PipelineForViewports) { vkDestroyPipeline(v->Device, bd->PipelineForViewports, v->Allocator); bd->PipelineForViewports = VK_NULL_HANDLE; }
1313 if (bd->DescriptorPool) { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; }
1314}
1315
1316#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
1317static void ImGui_ImplVulkan_LoadDynamicRenderingFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data)
1318{
1319 IM_UNUSED(api_version);
1320
1321 // Manually load those two (see #5446, #8326, #8365, #8600)
1322 // - Try loading core (non-KHR) versions first (this will work for Vulkan 1.3+ and the device supports dynamic rendering)
1323 ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast<PFN_vkCmdBeginRenderingKHR>(loader_func("vkCmdBeginRendering", user_data));
1324 ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast<PFN_vkCmdEndRenderingKHR>(loader_func("vkCmdEndRendering", user_data));
1325
1326 // - Fallback to KHR versions if core not available (this will work if KHR extension is available and enabled and also the device supports dynamic rendering)
1327 if (ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR == nullptr || ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR == nullptr)
1328 {
1329 ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast<PFN_vkCmdBeginRenderingKHR>(loader_func("vkCmdBeginRenderingKHR", user_data));
1330 ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast<PFN_vkCmdEndRenderingKHR>(loader_func("vkCmdEndRenderingKHR", user_data));
1331 }
1332}
1333#endif
1334
1335// If unspecified by user, assume that ApiVersion == HeaderVersion
1336 // 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)
1337static uint32_t ImGui_ImplVulkan_GetDefaultApiVersion()
1338{
1339#ifdef VK_HEADER_VERSION_COMPLETE
1340 return VK_HEADER_VERSION_COMPLETE;
1341#else
1342 return VK_API_VERSION_1_0;
1343#endif
1344}
1345
1346bool ImGui_ImplVulkan_LoadFunctions(uint32_t api_version, PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data)
1347{
1348 // Load function pointers
1349 // You can use the default Vulkan loader using:
1350 // ImGui_ImplVulkan_LoadFunctions(VK_API_VERSION_1_3, [](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); });
1351 // But this would be roughly equivalent to not setting VK_NO_PROTOTYPES.
1352 if (api_version == 0)
1353 api_version = ImGui_ImplVulkan_GetDefaultApiVersion();
1354
1355#ifdef IMGUI_IMPL_VULKAN_USE_LOADER
1356#define IMGUI_VULKAN_FUNC_LOAD(func) \
1357 func = reinterpret_cast<decltype(func)>(loader_func(#func, user_data)); \
1358 if (func == nullptr) \
1359 return false;
1360 IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_LOAD)
1361#undef IMGUI_VULKAN_FUNC_LOAD
1362
1363#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
1364 ImGui_ImplVulkan_LoadDynamicRenderingFunctions(api_version, loader_func, user_data);
1365#endif
1366#else
1367 IM_UNUSED(loader_func);
1368 IM_UNUSED(user_data);
1369#endif
1370
1371 g_FunctionsLoaded = true;
1372 return true;
1373}
1374
1375bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info)
1376{
1377 IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!");
1378
1379 if (info->ApiVersion == 0)
1380 info->ApiVersion = ImGui_ImplVulkan_GetDefaultApiVersion();
1381
1382 if (info->UseDynamicRendering)
1383 {
1384#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
1385#ifndef IMGUI_IMPL_VULKAN_USE_LOADER
1386 ImGui_ImplVulkan_LoadDynamicRenderingFunctions(info->ApiVersion, [](const char* function_name, void* user_data) { return vkGetDeviceProcAddr((VkDevice)user_data, function_name); }, (void*)info->Device);
1387#endif
1388 IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR != nullptr);
1389 IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR != nullptr);
1390#else
1391 IM_ASSERT(0 && "Can't use dynamic rendering when neither VK_VERSION_1_3 or VK_KHR_dynamic_rendering is defined.");
1392#endif
1393 }
1394
1395 ImGuiIO& io = ImGui::GetIO();
1396 IMGUI_CHECKVERSION();
1397 IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
1398
1399 // Setup backend capabilities flags
1400 ImGui_ImplVulkan_Data* bd = IM_NEW(ImGui_ImplVulkan_Data)();
1401 io.BackendRendererUserData = (void*)bd;
1402 io.BackendRendererName = "imgui_impl_vulkan";
1403 io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
1404 io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
1405 io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
1406
1407 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1408 platform_io.DrawCallback_ResetRenderState = ImGui_ImplVulkan_DrawCallback_ResetRenderState;
1409 platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplVulkan_DrawCallback_SetSamplerLinear;
1410 platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplVulkan_DrawCallback_SetSamplerNearest;
1411
1412 // Sanity checks
1413 IM_ASSERT(info->Instance != VK_NULL_HANDLE);
1414 IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE);
1415 IM_ASSERT(info->Device != VK_NULL_HANDLE);
1416 IM_ASSERT(info->Queue != VK_NULL_HANDLE);
1417 IM_ASSERT(info->MinImageCount >= 2);
1418 IM_ASSERT(info->ImageCount >= info->MinImageCount);
1419 if (info->DescriptorPool != VK_NULL_HANDLE) // Either DescriptorPool or DescriptorPoolSize must be set, not both!
1420 IM_ASSERT(info->DescriptorPoolSize == 0);
1421 else
1422 IM_ASSERT(info->DescriptorPoolSize > 0);
1423 if (info->UseDynamicRendering)
1424 IM_ASSERT(info->PipelineInfoMain.RenderPass == VK_NULL_HANDLE && info->PipelineInfoForViewports.RenderPass == VK_NULL_HANDLE);
1425
1426 bd->VulkanInitInfo = *info;
1427
1428 VkPhysicalDeviceProperties properties;
1429 vkGetPhysicalDeviceProperties(info->PhysicalDevice, &properties);
1430 bd->NonCoherentAtomSize = properties.limits.nonCoherentAtomSize;
1431
1432 if (!ImGui_ImplVulkan_CreateDeviceObjects())
1433 IM_ASSERT(0 && "ImGui_ImplVulkan_CreateDeviceObjects() failed!"); // <- Can't be hit yet.
1434
1435 // Our render function expect RendererUserData to be storing the window render buffer we need (for the main viewport we won't use ->Window)
1436 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1437 main_viewport->RendererUserData = IM_NEW(ImGui_ImplVulkan_ViewportData)();
1438
1439 ImGui_ImplVulkan_InitMultiViewportSupport();
1440
1441 return true;
1442}
1443
1444void ImGui_ImplVulkan_Shutdown()
1445{
1446 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1447 IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
1448 ImGuiIO& io = ImGui::GetIO();
1449 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1450
1451 // First destroy objects in all viewports
1452 ImGui_ImplVulkan_DestroyDeviceObjects();
1453
1454 // Manually delete main viewport render data in-case we haven't initialized for viewports
1455 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1456 if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)main_viewport->RendererUserData)
1457 IM_DELETE(vd);
1458 main_viewport->RendererUserData = nullptr;
1459
1460 // Clean up windows
1461 ImGui_ImplVulkan_ShutdownMultiViewportSupport();
1462
1463 io.BackendRendererName = nullptr;
1464 io.BackendRendererUserData = nullptr;
1465 io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);
1466 platform_io.ClearRendererHandlers();
1467 IM_DELETE(bd);
1468}
1469
1470void ImGui_ImplVulkan_NewFrame()
1471{
1472 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1473 IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplVulkan_Init()?");
1474 IM_UNUSED(bd);
1475}
1476
1477void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count)
1478{
1479 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1480 IM_ASSERT(min_image_count >= 2);
1481 if (bd->VulkanInitInfo.MinImageCount == min_image_count)
1482 return;
1483
1484 IM_ASSERT(0); // FIXME-VIEWPORT: Unsupported. Need to recreate all swap chains!
1485 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
1486 VkResult err = vkDeviceWaitIdle(v->Device);
1487 check_vk_result(err);
1488 ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator);
1489
1490 bd->VulkanInitInfo.MinImageCount = min_image_count;
1491}
1492
1493// Register a texture by creating a descriptor
1494// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions.
1495VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkImageView image_view, VkImageLayout image_layout)
1496{
1497 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1498 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
1499 VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;
1500
1501 // Create Descriptor Set:
1502 VkDescriptorSet descriptor_set;
1503 {
1504 VkDescriptorSetAllocateInfo alloc_info = {};
1505 alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
1506 alloc_info.descriptorPool = pool;
1507 alloc_info.descriptorSetCount = 1;
1508 alloc_info.pSetLayouts = &bd->DescriptorSetLayoutTexture;
1509 VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set);
1510 check_vk_result(err);
1511 }
1512
1513 // Update the Descriptor Set:
1514 if (descriptor_set != VK_NULL_HANDLE)
1515 {
1516 VkDescriptorImageInfo desc_image[1] = {};
1517 desc_image[0].imageView = image_view;
1518 desc_image[0].imageLayout = image_layout;
1519 VkWriteDescriptorSet write_desc[1] = {};
1520 write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1521 write_desc[0].dstSet = descriptor_set;
1522 write_desc[0].descriptorCount = 1;
1523 write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
1524 write_desc[0].pImageInfo = desc_image;
1525 vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, nullptr);
1526 }
1527 return descriptor_set;
1528}
1529
1530#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
1531VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout)
1532{
1533 IM_UNUSED(sampler);
1534 return ImGui_ImplVulkan_AddTexture(image_view, image_layout);
1535}
1536#endif
1537
1538void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set)
1539{
1540 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
1541 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
1542 VkDescriptorPool pool = bd->DescriptorPool ? bd->DescriptorPool : v->DescriptorPool;
1543 vkFreeDescriptorSets(v->Device, pool, 1, &descriptor_set);
1544}
1545
1546void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkan_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator)
1547{
1548 if (buffers->VertexBuffer) { vkDestroyBuffer(device, buffers->VertexBuffer, allocator); buffers->VertexBuffer = VK_NULL_HANDLE; }
1549 if (buffers->VertexBufferMemory) { vkFreeMemory(device, buffers->VertexBufferMemory, allocator); buffers->VertexBufferMemory = VK_NULL_HANDLE; }
1550 if (buffers->IndexBuffer) { vkDestroyBuffer(device, buffers->IndexBuffer, allocator); buffers->IndexBuffer = VK_NULL_HANDLE; }
1551 if (buffers->IndexBufferMemory) { vkFreeMemory(device, buffers->IndexBufferMemory, allocator); buffers->IndexBufferMemory = VK_NULL_HANDLE; }
1552 buffers->VertexBufferSize = 0;
1553 buffers->IndexBufferSize = 0;
1554}
1555
1556void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator)
1557{
1558 for (uint32_t n = 0; n < buffers->Count; n++)
1559 ImGui_ImplVulkan_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator);
1560 buffers->FrameRenderBuffers.clear();
1561 buffers->Index = 0;
1562 buffers->Count = 0;
1563}
1564
1565//-------------------------------------------------------------------------
1566// Internal / Miscellaneous Vulkan Helpers
1567// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.)
1568//-------------------------------------------------------------------------
1569// You probably do NOT need to use or care about those functions.
1570// Those functions only exist because:
1571// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files.
1572// 2) the upcoming multi-viewport feature will need them internally.
1573// Generally we avoid exposing any kind of superfluous high-level helpers in the backends,
1574// but it is too much code to duplicate everywhere so we exceptionally expose them.
1575//
1576// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.).
1577// You may read this code to learn about Vulkan, but it is recommended you use your own custom tailored code to do equivalent work.
1578// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions)
1579//-------------------------------------------------------------------------
1580
1581VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space)
1582{
1583 IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!");
1584 IM_ASSERT(request_formats != nullptr);
1585 IM_ASSERT(request_formats_count > 0);
1586
1587 // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation
1588 // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format
1589 // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40,
1590 // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used.
1591 uint32_t avail_count;
1592 vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, nullptr);
1593 ImVector<VkSurfaceFormatKHR> avail_format;
1594 avail_format.resize((int)avail_count);
1595 vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, avail_format.Data);
1596
1597 // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available
1598 if (avail_count == 1)
1599 {
1600 if (avail_format[0].format == VK_FORMAT_UNDEFINED)
1601 {
1602 VkSurfaceFormatKHR ret;
1603 ret.format = request_formats[0];
1604 ret.colorSpace = request_color_space;
1605 return ret;
1606 }
1607 else
1608 {
1609 // No point in searching another format
1610 return avail_format[0];
1611 }
1612 }
1613 else
1614 {
1615 // Request several formats, the first found will be used
1616 for (int request_i = 0; request_i < request_formats_count; request_i++)
1617 for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++)
1618 if (avail_format[avail_i].format == request_formats[request_i] && avail_format[avail_i].colorSpace == request_color_space)
1619 return avail_format[avail_i];
1620
1621 // If none of the requested image formats could be found, use the first available
1622 return avail_format[0];
1623 }
1624}
1625
1626VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count)
1627{
1628 IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!");
1629 IM_ASSERT(request_modes != nullptr);
1630 IM_ASSERT(request_modes_count > 0);
1631
1632 // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory
1633 uint32_t avail_count = 0;
1634 vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, nullptr);
1635 ImVector<VkPresentModeKHR> avail_modes;
1636 avail_modes.resize((int)avail_count);
1637 vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, avail_modes.Data);
1638 //for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++)
1639 // printf("[vulkan] avail_modes[%d] = %d\n", avail_i, avail_modes[avail_i]);
1640
1641 for (int request_i = 0; request_i < request_modes_count; request_i++)
1642 for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++)
1643 if (request_modes[request_i] == avail_modes[avail_i])
1644 return request_modes[request_i];
1645
1646 return VK_PRESENT_MODE_FIFO_KHR; // Always available
1647}
1648
1649VkPhysicalDevice ImGui_ImplVulkanH_SelectPhysicalDevice(VkInstance instance)
1650{
1651 uint32_t gpu_count;
1652 VkResult err = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr);
1653 check_vk_result(err);
1654 IM_ASSERT(gpu_count > 0);
1655
1656 ImVector<VkPhysicalDevice> gpus;
1657 gpus.resize(gpu_count);
1658 err = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.Data);
1659 check_vk_result(err);
1660
1661 // If a number >1 of GPUs got reported, find discrete GPU if present, or use first one available. This covers
1662 // most common cases (multi-gpu/integrated+dedicated graphics). Handling more complicated setups (multiple
1663 // dedicated GPUs) is out of scope of this sample.
1664 for (VkPhysicalDevice& device : gpus)
1665 {
1666 VkPhysicalDeviceProperties properties;
1667 vkGetPhysicalDeviceProperties(device, &properties);
1668 if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
1669 return device;
1670 }
1671
1672 // Use first GPU (Integrated) is a Discrete one is not available.
1673 if (gpu_count > 0)
1674 return gpus[0];
1675 return VK_NULL_HANDLE;
1676}
1677
1678
1679uint32_t ImGui_ImplVulkanH_SelectQueueFamilyIndex(VkPhysicalDevice physical_device)
1680{
1681 uint32_t count;
1682 vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, nullptr);
1683 ImVector<VkQueueFamilyProperties> queues_properties;
1684 queues_properties.resize((int)count);
1685 vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, queues_properties.Data);
1686 for (uint32_t i = 0; i < count; i++)
1687 if (queues_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
1688 return i;
1689 return (uint32_t)-1;
1690}
1691
1692void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator)
1693{
1694 IM_ASSERT(physical_device != VK_NULL_HANDLE && device != VK_NULL_HANDLE);
1695 IM_UNUSED(physical_device);
1696
1697 // Create Command Buffers
1698 VkResult err;
1699 for (uint32_t i = 0; i < wd->ImageCount; i++)
1700 {
1701 ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
1702 {
1703 VkCommandPoolCreateInfo info = {};
1704 info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1705 info.flags = 0;
1706 info.queueFamilyIndex = queue_family;
1707 err = vkCreateCommandPool(device, &info, allocator, &fd->CommandPool);
1708 check_vk_result(err);
1709 }
1710 {
1711 VkCommandBufferAllocateInfo info = {};
1712 info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1713 info.commandPool = fd->CommandPool;
1714 info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1715 info.commandBufferCount = 1;
1716 err = vkAllocateCommandBuffers(device, &info, &fd->CommandBuffer);
1717 check_vk_result(err);
1718 }
1719 {
1720 VkFenceCreateInfo info = {};
1721 info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1722 info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1723 err = vkCreateFence(device, &info, allocator, &fd->Fence);
1724 check_vk_result(err);
1725 }
1726 }
1727
1728 for (uint32_t i = 0; i < wd->SemaphoreCount; i++)
1729 {
1730 ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[i];
1731 {
1732 VkSemaphoreCreateInfo info = {};
1733 info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1734 err = vkCreateSemaphore(device, &info, allocator, &fsd->ImageAcquiredSemaphore);
1735 check_vk_result(err);
1736 err = vkCreateSemaphore(device, &info, allocator, &fsd->RenderCompleteSemaphore);
1737 check_vk_result(err);
1738 }
1739 }
1740}
1741
1742int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode)
1743{
1744 if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR)
1745 return 3;
1746 if (present_mode == VK_PRESENT_MODE_FIFO_KHR || present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)
1747 return 2;
1748 if (present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR)
1749 return 1;
1750 IM_ASSERT(0);
1751 return 1;
1752}
1753
1754// Also destroy old swap chain and in-flight frames data, if any.
1755void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count, VkImageUsageFlags image_usage)
1756{
1757 VkResult err;
1758 VkSwapchainKHR old_swapchain = wd->Swapchain;
1759 wd->Swapchain = VK_NULL_HANDLE;
1760 err = vkDeviceWaitIdle(device);
1761 check_vk_result(err);
1762
1763 // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one.
1764 // Destroy old Framebuffer
1765 for (uint32_t i = 0; i < wd->ImageCount; i++)
1766 ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator);
1767 for (uint32_t i = 0; i < wd->SemaphoreCount; i++)
1768 ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator);
1769 wd->Frames.clear();
1770 wd->FrameSemaphores.clear();
1771 wd->ImageCount = 0;
1772 if (wd->RenderPass)
1773 vkDestroyRenderPass(device, wd->RenderPass, allocator);
1774
1775 // If min image count was not specified, request different count of images dependent on selected present mode
1776 if (min_image_count == 0)
1777 min_image_count = ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(wd->PresentMode);
1778
1779 // Create Swapchain
1780 {
1781 VkSurfaceCapabilitiesKHR cap;
1782 err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap);
1783 check_vk_result(err);
1784
1785 VkSwapchainCreateInfoKHR info = {};
1786 info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1787 info.surface = wd->Surface;
1788 info.minImageCount = min_image_count;
1789 info.imageFormat = wd->SurfaceFormat.format;
1790 info.imageColorSpace = wd->SurfaceFormat.colorSpace;
1791 info.imageArrayLayers = 1;
1792 info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | image_usage;
1793 info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family
1794 info.preTransform = (cap.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : cap.currentTransform;
1795 if (cap.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
1796 info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1797 else if (cap.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
1798 info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
1799 else
1800 IM_ASSERT(false && "No supported composite alpha mode found!");
1801 info.presentMode = wd->PresentMode;
1802 info.clipped = VK_TRUE;
1803 info.oldSwapchain = old_swapchain;
1804 if (info.minImageCount < cap.minImageCount)
1805 info.minImageCount = cap.minImageCount;
1806 else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount)
1807 info.minImageCount = cap.maxImageCount;
1808 if (cap.currentExtent.width == 0xffffffff)
1809 {
1810 info.imageExtent.width = wd->Width = w;
1811 info.imageExtent.height = wd->Height = h;
1812 }
1813 else
1814 {
1815 info.imageExtent.width = wd->Width = cap.currentExtent.width;
1816 info.imageExtent.height = wd->Height = cap.currentExtent.height;
1817 }
1818 err = vkCreateSwapchainKHR(device, &info, allocator, &wd->Swapchain);
1819 check_vk_result(err);
1820 err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, nullptr);
1821 check_vk_result(err);
1822 VkImage backbuffers[16] = {};
1823 IM_ASSERT(wd->ImageCount >= min_image_count);
1824 IM_ASSERT(wd->ImageCount < IM_COUNTOF(backbuffers));
1825 err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers);
1826 check_vk_result(err);
1827
1828 wd->SemaphoreCount = wd->ImageCount + 1;
1829 wd->Frames.resize(wd->ImageCount);
1830 wd->FrameSemaphores.resize(wd->SemaphoreCount);
1831 memset(wd->Frames.Data, 0, wd->Frames.size_in_bytes());
1832 memset(wd->FrameSemaphores.Data, 0, wd->FrameSemaphores.size_in_bytes());
1833 for (uint32_t i = 0; i < wd->ImageCount; i++)
1834 wd->Frames[i].Backbuffer = backbuffers[i];
1835 }
1836 if (old_swapchain)
1837 vkDestroySwapchainKHR(device, old_swapchain, allocator);
1838
1839 // Create the Render Pass
1840 if (wd->UseDynamicRendering == false)
1841 {
1842 VkAttachmentDescription attachment = wd->AttachmentDesc;
1843 if (attachment.format == VK_FORMAT_UNDEFINED)
1844 attachment.format = wd->SurfaceFormat.format;
1845 VkAttachmentReference color_attachment = {};
1846 color_attachment.attachment = 0;
1847 color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1848 VkSubpassDescription subpass = {};
1849 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1850 subpass.colorAttachmentCount = 1;
1851 subpass.pColorAttachments = &color_attachment;
1852 VkSubpassDependency dependency = {};
1853 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1854 dependency.dstSubpass = 0;
1855 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1856 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1857 dependency.srcAccessMask = 0;
1858 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1859 VkRenderPassCreateInfo info = {};
1860 info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1861 info.attachmentCount = 1;
1862 info.pAttachments = &attachment;
1863 info.subpassCount = 1;
1864 info.pSubpasses = &subpass;
1865 info.dependencyCount = 1;
1866 info.pDependencies = &dependency;
1867 err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass);
1868 check_vk_result(err);
1869
1870 // We do not create a pipeline by default as this is also used by examples' main.cpp,
1871 // but secondary viewport in multi-viewport mode may want to create one with:
1872 //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, v->Subpass);
1873 }
1874
1875 // Create The Image Views
1876 {
1877 VkImageViewCreateInfo info = {};
1878 info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1879 info.viewType = VK_IMAGE_VIEW_TYPE_2D;
1880 info.format = wd->SurfaceFormat.format;
1881 info.components.r = VK_COMPONENT_SWIZZLE_R;
1882 info.components.g = VK_COMPONENT_SWIZZLE_G;
1883 info.components.b = VK_COMPONENT_SWIZZLE_B;
1884 info.components.a = VK_COMPONENT_SWIZZLE_A;
1885 VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
1886 info.subresourceRange = image_range;
1887 for (uint32_t i = 0; i < wd->ImageCount; i++)
1888 {
1889 ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
1890 info.image = fd->Backbuffer;
1891 err = vkCreateImageView(device, &info, allocator, &fd->BackbufferView);
1892 check_vk_result(err);
1893 }
1894 }
1895
1896 // Create Framebuffer
1897 if (wd->UseDynamicRendering == false)
1898 {
1899 VkImageView attachment[1];
1900 VkFramebufferCreateInfo info = {};
1901 info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
1902 info.renderPass = wd->RenderPass;
1903 info.attachmentCount = 1;
1904 info.pAttachments = attachment;
1905 info.width = wd->Width;
1906 info.height = wd->Height;
1907 info.layers = 1;
1908 for (uint32_t i = 0; i < wd->ImageCount; i++)
1909 {
1910 ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
1911 attachment[0] = fd->BackbufferView;
1912 err = vkCreateFramebuffer(device, &info, allocator, &fd->Framebuffer);
1913 check_vk_result(err);
1914 }
1915 }
1916}
1917
1918// Create or resize window
1919// - 2025/09/26: v1.92.4 added a trailing 'VkImageUsageFlags image_usage' parameter which is usually VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT.
1920void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count, VkImageUsageFlags image_usage)
1921{
1922 IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!");
1923 IM_ASSERT(wd->Surface != VK_NULL_HANDLE);
1924 IM_UNUSED(instance);
1925
1926 ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count, image_usage);
1927 ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator);
1928
1929 // FIXME: to submit the command buffer, we need a queue. In the examples folder, the ImGui_ImplVulkanH_CreateOrResizeWindow function is called
1930 // before the ImGui_ImplVulkan_Init function, so we don't have access to the queue yet. Here we have the queue_family that we can use to grab
1931 // a queue from the device and submit the command buffer. It would be better to have access to the queue as suggested in the FIXME below.
1932 VkCommandPool command_pool;
1933 VkCommandPoolCreateInfo pool_info = {};
1934 pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1935 pool_info.queueFamilyIndex = queue_family;
1936 VkResult err = vkCreateCommandPool(device, &pool_info, allocator, &command_pool);
1937 check_vk_result(err);
1938
1939 VkFenceCreateInfo fence_info = {};
1940 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1941 VkFence fence;
1942 err = vkCreateFence(device, &fence_info, allocator, &fence);
1943 check_vk_result(err);
1944
1945 VkCommandBufferAllocateInfo alloc_info = {};
1946 alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1947 alloc_info.commandPool = command_pool;
1948 alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1949 alloc_info.commandBufferCount = 1;
1950 VkCommandBuffer command_buffer;
1951 err = vkAllocateCommandBuffers(device, &alloc_info, &command_buffer);
1952 check_vk_result(err);
1953
1954 VkCommandBufferBeginInfo begin_info = {};
1955 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1956 begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1957 err = vkBeginCommandBuffer(command_buffer, &begin_info);
1958 check_vk_result(err);
1959
1960 // Transition the images to the correct layout for rendering
1961 for (uint32_t i = 0; i < wd->ImageCount; i++)
1962 {
1963 VkImageMemoryBarrier barrier = {};
1964 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1965 barrier.image = wd->Frames[i].Backbuffer;
1966 barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1967 barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1968 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1969 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1970 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1971 barrier.subresourceRange.levelCount = 1;
1972 barrier.subresourceRange.layerCount = 1;
1973 vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
1974 }
1975
1976 err = vkEndCommandBuffer(command_buffer);
1977 check_vk_result(err);
1978 VkSubmitInfo submit_info = {};
1979 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1980 submit_info.commandBufferCount = 1;
1981 submit_info.pCommandBuffers = &command_buffer;
1982
1983 VkQueue queue;
1984 vkGetDeviceQueue(device, queue_family, 0, &queue);
1985 err = vkQueueSubmit(queue, 1, &submit_info, fence);
1986 check_vk_result(err);
1987 err = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
1988 check_vk_result(err);
1989 err = vkResetFences(device, 1, &fence);
1990 check_vk_result(err);
1991
1992 err = vkResetCommandPool(device, command_pool, 0);
1993 check_vk_result(err);
1994
1995 // Destroy command buffer and fence and command pool
1996 vkFreeCommandBuffers(device, command_pool, 1, &command_buffer);
1997 vkDestroyCommandPool(device, command_pool, allocator);
1998 vkDestroyFence(device, fence, allocator);
1999 command_pool = VK_NULL_HANDLE;
2000 command_buffer = VK_NULL_HANDLE;
2001 fence = VK_NULL_HANDLE;
2002 queue = VK_NULL_HANDLE;
2003}
2004
2005void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator)
2006{
2007 IM_UNUSED(instance);
2008 vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals)
2009 //vkQueueWaitIdle(bd->Queue);
2010
2011 for (uint32_t i = 0; i < wd->ImageCount; i++)
2012 ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator);
2013 for (uint32_t i = 0; i < wd->SemaphoreCount; i++)
2014 ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator);
2015 wd->Frames.clear();
2016 wd->FrameSemaphores.clear();
2017 vkDestroyRenderPass(device, wd->RenderPass, allocator);
2018 vkDestroySwapchainKHR(device, wd->Swapchain, allocator);
2019 wd->RenderPass = VK_NULL_HANDLE;
2020 wd->Swapchain = VK_NULL_HANDLE;
2021 wd->Width = wd->Height = 0;
2022 wd->FrameIndex = wd->ImageCount = wd->SemaphoreCount = wd->SemaphoreIndex = 0;
2023 //vkDestroySurfaceKHR(instance, wd->Surface, allocator); // v1.92.6 (~2026-01-16): because wd->Surface is user provided we don't attempt to destroy it ourself.
2024}
2025
2026void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator)
2027{
2028 vkDestroyFence(device, fd->Fence, allocator);
2029 vkFreeCommandBuffers(device, fd->CommandPool, 1, &fd->CommandBuffer);
2030 vkDestroyCommandPool(device, fd->CommandPool, allocator);
2031 fd->Fence = VK_NULL_HANDLE;
2032 fd->CommandBuffer = VK_NULL_HANDLE;
2033 fd->CommandPool = VK_NULL_HANDLE;
2034
2035 vkDestroyImageView(device, fd->BackbufferView, allocator);
2036 vkDestroyFramebuffer(device, fd->Framebuffer, allocator);
2037}
2038
2039void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator)
2040{
2041 vkDestroySemaphore(device, fsd->ImageAcquiredSemaphore, allocator);
2042 vkDestroySemaphore(device, fsd->RenderCompleteSemaphore, allocator);
2043 fsd->ImageAcquiredSemaphore = fsd->RenderCompleteSemaphore = VK_NULL_HANDLE;
2044}
2045
2046void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator)
2047{
2048 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
2049 for (int n = 0; n < platform_io.Viewports.Size; n++)
2050 if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)platform_io.Viewports[n]->RendererUserData)
2051 ImGui_ImplVulkan_DestroyWindowRenderBuffers(device, &vd->RenderBuffers, allocator);
2052}
2053
2054ImGui_ImplVulkanH_Window* ImGui_ImplVulkanH_GetWindowDataFromViewport(ImGuiViewport* viewport)
2055{
2056 ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData;
2057 return vd ? &vd->Window : nullptr;
2058}
2059
2060//--------------------------------------------------------------------------------------------------------
2061// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
2062// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
2063// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
2064//--------------------------------------------------------------------------------------------------------
2065
2066static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport)
2067{
2068 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
2069 ImGui_ImplVulkan_ViewportData* vd = IM_NEW(ImGui_ImplVulkan_ViewportData)();
2070 ImGui_ImplVulkanH_Window* wd = &vd->Window;
2071 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
2072
2073 // Create surface
2074 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
2075 VkResult err = (VkResult)platform_io.Platform_CreateVkSurface(viewport, (ImU64)v->Instance, (const void*)v->Allocator, (ImU64*)&wd->Surface);
2076 check_vk_result(err);
2077
2078 // Check if surface creation failed
2079 if (err != VK_SUCCESS || wd->Surface == VK_NULL_HANDLE)
2080 {
2081 IM_DELETE(vd);
2082 return;
2083 }
2084
2085 // Check for WSI support
2086 VkBool32 res;
2087 vkGetPhysicalDeviceSurfaceSupportKHR(v->PhysicalDevice, v->QueueFamily, wd->Surface, &res);
2088 if (res != VK_TRUE)
2089 {
2090 vkDestroySurfaceKHR(v->Instance, wd->Surface, v->Allocator); // Error: no WSI support on physical device, clean up and return
2091 IM_DELETE(vd);
2092 return;
2093 }
2094 viewport->RendererUserData = vd;
2095
2096 // Select Surface Format
2097 ImGui_ImplVulkan_PipelineInfo* pipeline_info = &v->PipelineInfoForViewports;
2098 ImVector<VkFormat> requestSurfaceImageFormats;
2099#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
2100 for (uint32_t n = 0; n < pipeline_info->PipelineRenderingCreateInfo.colorAttachmentCount; n++)
2101 requestSurfaceImageFormats.push_back(pipeline_info->PipelineRenderingCreateInfo.pColorAttachmentFormats[n]);
2102#endif
2103 const VkFormat defaultFormats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
2104 for (VkFormat format : defaultFormats)
2105 requestSurfaceImageFormats.push_back(format);
2106
2107 const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
2108 wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(v->PhysicalDevice, wd->Surface, requestSurfaceImageFormats.Data, requestSurfaceImageFormats.Size, requestSurfaceColorSpace);
2109
2110 // Select Present Mode
2111 // FIXME-VULKAN: Even thought mailbox seems to get us maximum framerate with a single window, it halves framerate with a second window etc. (w/ Nvidia and SDK 1.82.1)
2112 VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
2113 wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(v->PhysicalDevice, wd->Surface, &present_modes[0], IM_COUNTOF(present_modes));
2114 //printf("[vulkan] Secondary window selected PresentMode = %d\n", wd->PresentMode);
2115
2116 // Create SwapChain, RenderPass, Framebuffer, etc.
2117 wd->UseDynamicRendering = v->UseDynamicRendering;
2118 wd->AttachmentDesc.loadOp = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : VK_ATTACHMENT_LOAD_OP_CLEAR;
2119 ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount, pipeline_info->SwapChainImageUsage);
2120 vd->WindowOwned = true;
2121
2122 // Create pipeline (shared by all secondary viewports)
2123 if (bd->PipelineForViewports == VK_NULL_HANDLE)
2124 {
2125#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
2126 if (wd->UseDynamicRendering)
2127 {
2128 pipeline_info->PipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
2129 pipeline_info->PipelineRenderingCreateInfo.colorAttachmentCount = 1;
2130 pipeline_info->PipelineRenderingCreateInfo.pColorAttachmentFormats = &wd->SurfaceFormat.format;
2131 }
2132 else
2133 {
2134 pipeline_info->RenderPass = wd->RenderPass;
2135 }
2136#endif
2137 bd->PipelineForViewports = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, VK_NULL_HANDLE, &v->PipelineInfoForViewports);
2138 }
2139}
2140
2141static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport)
2142{
2143 // The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it.
2144 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
2145 if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData)
2146 {
2147 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
2148 if (vd->WindowOwned)
2149 {
2150 ImGui_ImplVulkanH_DestroyWindow(v->Instance, v->Device, &vd->Window, v->Allocator);
2151 vkDestroySurfaceKHR(v->Instance, vd->Window.Surface, v->Allocator);
2152 }
2153 ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &vd->RenderBuffers, v->Allocator);
2154 IM_DELETE(vd);
2155 }
2156 viewport->RendererUserData = nullptr;
2157}
2158
2159static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
2160{
2161 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
2162 ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData;
2163 if (vd == nullptr) // This is nullptr for the main viewport (which is left to the user/app to handle)
2164 return;
2165 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
2166 ImGui_ImplVulkanH_Window* wd = &vd->Window;
2167 wd->AttachmentDesc.loadOp = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : VK_ATTACHMENT_LOAD_OP_CLEAR;
2168 ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount, v->PipelineInfoForViewports.SwapChainImageUsage);
2169}
2170
2171static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*)
2172{
2173 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
2174 ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData;
2175 if (vd == nullptr)
2176 return;
2177 ImGui_ImplVulkanH_Window* wd = &vd->Window;
2178 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
2179 VkResult err;
2180
2181 if (vd->SwapChainNeedRebuild || vd->SwapChainSuboptimal)
2182 {
2183 ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount, v->PipelineInfoForViewports.SwapChainImageUsage);
2184 vd->SwapChainNeedRebuild = vd->SwapChainSuboptimal = false;
2185 }
2186
2187 ImGui_ImplVulkanH_Frame* fd = nullptr;
2188 ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex];
2189 {
2190 {
2191 err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fsd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex);
2192 if (err == VK_ERROR_OUT_OF_DATE_KHR)
2193 {
2194 vd->SwapChainNeedRebuild = true; // Since we are not going to swap this frame anyway, it's ok that recreation happens on next frame.
2195 return;
2196 }
2197 if (err == VK_SUBOPTIMAL_KHR)
2198 vd->SwapChainSuboptimal = true;
2199 else
2200 check_vk_result(err);
2201 fd = &wd->Frames[wd->FrameIndex];
2202 }
2203 for (;;)
2204 {
2205 err = vkWaitForFences(v->Device, 1, &fd->Fence, VK_TRUE, 100);
2206 if (err == VK_SUCCESS) break;
2207 if (err == VK_TIMEOUT) continue;
2208 check_vk_result(err);
2209 }
2210 {
2211 err = vkResetCommandPool(v->Device, fd->CommandPool, 0);
2212 check_vk_result(err);
2213 VkCommandBufferBeginInfo info = {};
2214 info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2215 info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
2216 err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
2217 check_vk_result(err);
2218 }
2219 {
2220 ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
2221 memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float));
2222 }
2223#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
2224 if (v->UseDynamicRendering)
2225 {
2226 // Transition swapchain image to a layout suitable for drawing.
2227 VkImageMemoryBarrier barrier = {};
2228 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2229 barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2230 barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2231 barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2232 barrier.image = fd->Backbuffer;
2233 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2234 barrier.subresourceRange.levelCount = 1;
2235 barrier.subresourceRange.layerCount = 1;
2236 vkCmdPipelineBarrier(fd->CommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_NONE, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
2237
2238 VkRenderingAttachmentInfo attachmentInfo = {};
2239 attachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
2240 attachmentInfo.imageView = fd->BackbufferView;
2241 attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2242 attachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE;
2243 attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
2244 attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
2245 attachmentInfo.clearValue = wd->ClearValue;
2246
2247 VkRenderingInfo renderingInfo = {};
2248 renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR;
2249 renderingInfo.renderArea.extent.width = wd->Width;
2250 renderingInfo.renderArea.extent.height = wd->Height;
2251 renderingInfo.layerCount = 1;
2252 renderingInfo.viewMask = 0;
2253 renderingInfo.colorAttachmentCount = 1;
2254 renderingInfo.pColorAttachments = &attachmentInfo;
2255
2256 ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR(fd->CommandBuffer, &renderingInfo);
2257 }
2258 else
2259#endif
2260 {
2261 VkRenderPassBeginInfo info = {};
2262 info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
2263 info.renderPass = wd->RenderPass;
2264 info.framebuffer = fd->Framebuffer;
2265 info.renderArea.extent.width = wd->Width;
2266 info.renderArea.extent.height = wd->Height;
2267 info.clearValueCount = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? 0 : 1;
2268 info.pClearValues = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? nullptr : &wd->ClearValue;
2269 vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
2270 }
2271 }
2272
2273 ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, bd->PipelineForViewports);
2274
2275 {
2276#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING
2277 if (v->UseDynamicRendering)
2278 {
2279 ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR(fd->CommandBuffer);
2280
2281 // Transition image to a layout suitable for presentation
2282 VkImageMemoryBarrier barrier = {};
2283 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2284 barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
2285 barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
2286 barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
2287 barrier.image = fd->Backbuffer;
2288 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2289 barrier.subresourceRange.levelCount = 1;
2290 barrier.subresourceRange.layerCount = 1;
2291 vkCmdPipelineBarrier(fd->CommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
2292 }
2293 else
2294#endif
2295 {
2296 vkCmdEndRenderPass(fd->CommandBuffer);
2297 }
2298 {
2299 VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
2300 VkSubmitInfo info = {};
2301 info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2302 info.waitSemaphoreCount = 1;
2303 info.pWaitSemaphores = &fsd->ImageAcquiredSemaphore;
2304 info.pWaitDstStageMask = &wait_stage;
2305 info.commandBufferCount = 1;
2306 info.pCommandBuffers = &fd->CommandBuffer;
2307 info.signalSemaphoreCount = 1;
2308 info.pSignalSemaphores = &fsd->RenderCompleteSemaphore;
2309
2310 err = vkEndCommandBuffer(fd->CommandBuffer);
2311 check_vk_result(err);
2312 err = vkResetFences(v->Device, 1, &fd->Fence);
2313 check_vk_result(err);
2314 err = vkQueueSubmit(v->Queue, 1, &info, fd->Fence);
2315 check_vk_result(err);
2316 }
2317 }
2318}
2319
2320static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*)
2321{
2322 ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData();
2323 ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData;
2324 if (vd == nullptr)
2325 return;
2326 ImGui_ImplVulkanH_Window* wd = &vd->Window;
2327 ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo;
2328
2329 if (vd->SwapChainNeedRebuild) // Frame data became invalid in the middle of rendering
2330 return;
2331
2332 VkResult err;
2333 uint32_t present_index = wd->FrameIndex;
2334
2335 ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex];
2336 VkPresentInfoKHR info = {};
2337 info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2338 info.waitSemaphoreCount = 1;
2339 info.pWaitSemaphores = &fsd->RenderCompleteSemaphore;
2340 info.swapchainCount = 1;
2341 info.pSwapchains = &wd->Swapchain;
2342 info.pImageIndices = &present_index;
2343 err = vkQueuePresentKHR(v->Queue, &info);
2344 if (err == VK_ERROR_OUT_OF_DATE_KHR)
2345 {
2346 vd->SwapChainNeedRebuild = true;
2347 return;
2348 }
2349 if (err == VK_SUBOPTIMAL_KHR)
2350 vd->SwapChainSuboptimal = true;
2351 else
2352 check_vk_result(err);
2353 wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores
2354}
2355
2356void ImGui_ImplVulkan_InitMultiViewportSupport()
2357{
2358 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
2359 if (ImGui::GetIO().BackendFlags & ImGuiBackendFlags_PlatformHasViewports)
2360 IM_ASSERT(platform_io.Platform_CreateVkSurface != nullptr && "Platform needs to setup the CreateVkSurface handler.");
2361 platform_io.Renderer_CreateWindow = ImGui_ImplVulkan_CreateWindow;
2362 platform_io.Renderer_DestroyWindow = ImGui_ImplVulkan_DestroyWindow;
2363 platform_io.Renderer_SetWindowSize = ImGui_ImplVulkan_SetWindowSize;
2364 platform_io.Renderer_RenderWindow = ImGui_ImplVulkan_RenderWindow;
2365 platform_io.Renderer_SwapBuffers = ImGui_ImplVulkan_SwapBuffers;
2366}
2367
2368void ImGui_ImplVulkan_ShutdownMultiViewportSupport()
2369{
2370 ImGui::DestroyPlatformWindows();
2371}
2372
2373//-----------------------------------------------------------------------------
2374
2375#endif // #ifndef IMGUI_DISABLE