1 | // Dear ImGui: standalone example application for SDL2 + Vulkan |
2 | |
3 | // Learn about Dear ImGui: |
4 | // - FAQ https://dearimgui.com/faq |
5 | // - Getting Started https://dearimgui.com/getting-started |
6 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). |
7 | // - Introduction, links and more at the top of imgui.cpp |
8 | |
9 | // Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. |
10 | // - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. |
11 | // You will use those if you want to use this rendering backend in your engine/app. |
12 | // - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by |
13 | // the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. |
14 | // Read comments in imgui_impl_vulkan.h. |
15 | |
16 | #include "imgui.h" |
17 | #include "imgui_impl_sdl2.h" |
18 | #include "imgui_impl_vulkan.h" |
19 | #include <stdio.h> // printf, fprintf |
20 | #include <stdlib.h> // abort |
21 | #include <SDL.h> |
22 | #include <SDL_vulkan.h> |
23 | |
24 | // Volk headers |
25 | #ifdef IMGUI_IMPL_VULKAN_USE_VOLK |
26 | #define VOLK_IMPLEMENTATION |
27 | #include <volk.h> |
28 | #endif |
29 | |
30 | //#define APP_USE_UNLIMITED_FRAME_RATE |
31 | #ifdef _DEBUG |
32 | #define APP_USE_VULKAN_DEBUG_REPORT |
33 | #endif |
34 | |
35 | // Data |
36 | static VkAllocationCallbacks* g_Allocator = nullptr; |
37 | static VkInstance g_Instance = VK_NULL_HANDLE; |
38 | static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; |
39 | static VkDevice g_Device = VK_NULL_HANDLE; |
40 | static uint32_t g_QueueFamily = (uint32_t)-1; |
41 | static VkQueue g_Queue = VK_NULL_HANDLE; |
42 | static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; |
43 | static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; |
44 | static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; |
45 | |
46 | static ImGui_ImplVulkanH_Window g_MainWindowData; |
47 | static uint32_t g_MinImageCount = 2; |
48 | static bool g_SwapChainRebuild = false; |
49 | |
50 | static void check_vk_result(VkResult err) |
51 | { |
52 | if (err == VK_SUCCESS) |
53 | return; |
54 | fprintf(stderr, format: "[vulkan] Error: VkResult = %d\n" , err); |
55 | if (err < 0) |
56 | abort(); |
57 | } |
58 | |
59 | #ifdef APP_USE_VULKAN_DEBUG_REPORT |
60 | static VKAPI_ATTR VkBool32 VKAPI_CALL debug_report(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) |
61 | { |
62 | (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments |
63 | fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n" , objectType, pMessage); |
64 | return VK_FALSE; |
65 | } |
66 | #endif // APP_USE_VULKAN_DEBUG_REPORT |
67 | |
68 | static bool IsExtensionAvailable(const ImVector<VkExtensionProperties>& properties, const char* extension) |
69 | { |
70 | for (const VkExtensionProperties& p : properties) |
71 | if (strcmp(s1: p.extensionName, s2: extension) == 0) |
72 | return true; |
73 | return false; |
74 | } |
75 | |
76 | static void SetupVulkan(ImVector<const char*> instance_extensions) |
77 | { |
78 | VkResult err; |
79 | #ifdef IMGUI_IMPL_VULKAN_USE_VOLK |
80 | volkInitialize(); |
81 | #endif |
82 | |
83 | // Create Vulkan Instance |
84 | { |
85 | VkInstanceCreateInfo create_info = {}; |
86 | create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
87 | |
88 | // Enumerate available extensions |
89 | uint32_t properties_count; |
90 | ImVector<VkExtensionProperties> properties; |
91 | vkEnumerateInstanceExtensionProperties(pLayerName: nullptr, pPropertyCount: &properties_count, pProperties: nullptr); |
92 | properties.resize(new_size: properties_count); |
93 | err = vkEnumerateInstanceExtensionProperties(pLayerName: nullptr, pPropertyCount: &properties_count, pProperties: properties.Data); |
94 | check_vk_result(err); |
95 | |
96 | // Enable required extensions |
97 | if (IsExtensionAvailable(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) |
98 | instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); |
99 | #ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME |
100 | if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) |
101 | { |
102 | instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); |
103 | create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; |
104 | } |
105 | #endif |
106 | |
107 | // Enabling validation layers |
108 | #ifdef APP_USE_VULKAN_DEBUG_REPORT |
109 | const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; |
110 | create_info.enabledLayerCount = 1; |
111 | create_info.ppEnabledLayerNames = layers; |
112 | instance_extensions.push_back("VK_EXT_debug_report" ); |
113 | #endif |
114 | |
115 | // Create Vulkan Instance |
116 | create_info.enabledExtensionCount = (uint32_t)instance_extensions.Size; |
117 | create_info.ppEnabledExtensionNames = instance_extensions.Data; |
118 | err = vkCreateInstance(pCreateInfo: &create_info, pAllocator: g_Allocator, pInstance: &g_Instance); |
119 | check_vk_result(err); |
120 | #ifdef IMGUI_IMPL_VULKAN_USE_VOLK |
121 | volkLoadInstance(g_Instance); |
122 | #endif |
123 | |
124 | // Setup the debug report callback |
125 | #ifdef APP_USE_VULKAN_DEBUG_REPORT |
126 | auto f_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT" ); |
127 | IM_ASSERT(f_vkCreateDebugReportCallbackEXT != nullptr); |
128 | VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; |
129 | debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; |
130 | debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; |
131 | debug_report_ci.pfnCallback = debug_report; |
132 | debug_report_ci.pUserData = nullptr; |
133 | err = f_vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); |
134 | check_vk_result(err); |
135 | #endif |
136 | } |
137 | |
138 | // Select Physical Device (GPU) |
139 | g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(instance: g_Instance); |
140 | IM_ASSERT(g_PhysicalDevice != VK_NULL_HANDLE); |
141 | |
142 | // Select graphics queue family |
143 | g_QueueFamily = ImGui_ImplVulkanH_SelectQueueFamilyIndex(physical_device: g_PhysicalDevice); |
144 | IM_ASSERT(g_QueueFamily != (uint32_t)-1); |
145 | |
146 | // Create Logical Device (with 1 queue) |
147 | { |
148 | ImVector<const char*> device_extensions; |
149 | device_extensions.push_back(v: "VK_KHR_swapchain" ); |
150 | |
151 | // Enumerate physical device extension |
152 | uint32_t properties_count; |
153 | ImVector<VkExtensionProperties> properties; |
154 | vkEnumerateDeviceExtensionProperties(physicalDevice: g_PhysicalDevice, pLayerName: nullptr, pPropertyCount: &properties_count, pProperties: nullptr); |
155 | properties.resize(new_size: properties_count); |
156 | vkEnumerateDeviceExtensionProperties(physicalDevice: g_PhysicalDevice, pLayerName: nullptr, pPropertyCount: &properties_count, pProperties: properties.Data); |
157 | #ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME |
158 | if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) |
159 | device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); |
160 | #endif |
161 | |
162 | const float queue_priority[] = { 1.0f }; |
163 | VkDeviceQueueCreateInfo queue_info[1] = {}; |
164 | queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
165 | queue_info[0].queueFamilyIndex = g_QueueFamily; |
166 | queue_info[0].queueCount = 1; |
167 | queue_info[0].pQueuePriorities = queue_priority; |
168 | VkDeviceCreateInfo create_info = {}; |
169 | create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
170 | create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); |
171 | create_info.pQueueCreateInfos = queue_info; |
172 | create_info.enabledExtensionCount = (uint32_t)device_extensions.Size; |
173 | create_info.ppEnabledExtensionNames = device_extensions.Data; |
174 | err = vkCreateDevice(physicalDevice: g_PhysicalDevice, pCreateInfo: &create_info, pAllocator: g_Allocator, pDevice: &g_Device); |
175 | check_vk_result(err); |
176 | vkGetDeviceQueue(device: g_Device, queueFamilyIndex: g_QueueFamily, queueIndex: 0, pQueue: &g_Queue); |
177 | } |
178 | |
179 | // Create Descriptor Pool |
180 | // If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets. |
181 | { |
182 | VkDescriptorPoolSize pool_sizes[] = |
183 | { |
184 | { .type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE }, |
185 | }; |
186 | VkDescriptorPoolCreateInfo pool_info = {}; |
187 | pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; |
188 | pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; |
189 | pool_info.maxSets = 0; |
190 | for (VkDescriptorPoolSize& pool_size : pool_sizes) |
191 | pool_info.maxSets += pool_size.descriptorCount; |
192 | pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); |
193 | pool_info.pPoolSizes = pool_sizes; |
194 | err = vkCreateDescriptorPool(device: g_Device, pCreateInfo: &pool_info, pAllocator: g_Allocator, pDescriptorPool: &g_DescriptorPool); |
195 | check_vk_result(err); |
196 | } |
197 | } |
198 | |
199 | // All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. |
200 | // Your real engine/app may not use them. |
201 | static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) |
202 | { |
203 | wd->Surface = surface; |
204 | |
205 | // Check for WSI support |
206 | VkBool32 res; |
207 | vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice: g_PhysicalDevice, queueFamilyIndex: g_QueueFamily, surface: wd->Surface, pSupported: &res); |
208 | if (res != VK_TRUE) |
209 | { |
210 | fprintf(stderr, format: "Error no WSI support on physical device 0\n" ); |
211 | exit(status: -1); |
212 | } |
213 | |
214 | // Select Surface Format |
215 | const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; |
216 | const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; |
217 | wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(physical_device: g_PhysicalDevice, surface: wd->Surface, request_formats: requestSurfaceImageFormat, request_formats_count: (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), request_color_space: requestSurfaceColorSpace); |
218 | |
219 | // Select Present Mode |
220 | #ifdef APP_USE_UNLIMITED_FRAME_RATE |
221 | VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; |
222 | #else |
223 | VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; |
224 | #endif |
225 | wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(physical_device: g_PhysicalDevice, surface: wd->Surface, request_modes: &present_modes[0], IM_ARRAYSIZE(present_modes)); |
226 | //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); |
227 | |
228 | // Create SwapChain, RenderPass, Framebuffer, etc. |
229 | IM_ASSERT(g_MinImageCount >= 2); |
230 | ImGui_ImplVulkanH_CreateOrResizeWindow(instance: g_Instance, physical_device: g_PhysicalDevice, device: g_Device, wd, queue_family: g_QueueFamily, allocator: g_Allocator, w: width, h: height, min_image_count: g_MinImageCount); |
231 | } |
232 | |
233 | static void CleanupVulkan() |
234 | { |
235 | vkDestroyDescriptorPool(device: g_Device, descriptorPool: g_DescriptorPool, pAllocator: g_Allocator); |
236 | |
237 | #ifdef APP_USE_VULKAN_DEBUG_REPORT |
238 | // Remove the debug report callback |
239 | auto f_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT" ); |
240 | f_vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); |
241 | #endif // APP_USE_VULKAN_DEBUG_REPORT |
242 | |
243 | vkDestroyDevice(device: g_Device, pAllocator: g_Allocator); |
244 | vkDestroyInstance(instance: g_Instance, pAllocator: g_Allocator); |
245 | } |
246 | |
247 | static void CleanupVulkanWindow() |
248 | { |
249 | ImGui_ImplVulkanH_DestroyWindow(instance: g_Instance, device: g_Device, wd: &g_MainWindowData, allocator: g_Allocator); |
250 | } |
251 | |
252 | static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) |
253 | { |
254 | VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; |
255 | VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; |
256 | VkResult err = vkAcquireNextImageKHR(device: g_Device, swapchain: wd->Swapchain, UINT64_MAX, semaphore: image_acquired_semaphore, VK_NULL_HANDLE, pImageIndex: &wd->FrameIndex); |
257 | if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) |
258 | g_SwapChainRebuild = true; |
259 | if (err == VK_ERROR_OUT_OF_DATE_KHR) |
260 | return; |
261 | if (err != VK_SUBOPTIMAL_KHR) |
262 | check_vk_result(err); |
263 | |
264 | ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; |
265 | { |
266 | err = vkWaitForFences(device: g_Device, fenceCount: 1, pFences: &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking |
267 | check_vk_result(err); |
268 | |
269 | err = vkResetFences(device: g_Device, fenceCount: 1, pFences: &fd->Fence); |
270 | check_vk_result(err); |
271 | } |
272 | { |
273 | err = vkResetCommandPool(device: g_Device, commandPool: fd->CommandPool, flags: 0); |
274 | check_vk_result(err); |
275 | VkCommandBufferBeginInfo info = {}; |
276 | info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
277 | info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; |
278 | err = vkBeginCommandBuffer(commandBuffer: fd->CommandBuffer, pBeginInfo: &info); |
279 | check_vk_result(err); |
280 | } |
281 | { |
282 | VkRenderPassBeginInfo info = {}; |
283 | info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; |
284 | info.renderPass = wd->RenderPass; |
285 | info.framebuffer = fd->Framebuffer; |
286 | info.renderArea.extent.width = wd->Width; |
287 | info.renderArea.extent.height = wd->Height; |
288 | info.clearValueCount = 1; |
289 | info.pClearValues = &wd->ClearValue; |
290 | vkCmdBeginRenderPass(commandBuffer: fd->CommandBuffer, pRenderPassBegin: &info, contents: VK_SUBPASS_CONTENTS_INLINE); |
291 | } |
292 | |
293 | // Record dear imgui primitives into command buffer |
294 | ImGui_ImplVulkan_RenderDrawData(draw_data, command_buffer: fd->CommandBuffer); |
295 | |
296 | // Submit command buffer |
297 | vkCmdEndRenderPass(commandBuffer: fd->CommandBuffer); |
298 | { |
299 | VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
300 | VkSubmitInfo info = {}; |
301 | info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
302 | info.waitSemaphoreCount = 1; |
303 | info.pWaitSemaphores = &image_acquired_semaphore; |
304 | info.pWaitDstStageMask = &wait_stage; |
305 | info.commandBufferCount = 1; |
306 | info.pCommandBuffers = &fd->CommandBuffer; |
307 | info.signalSemaphoreCount = 1; |
308 | info.pSignalSemaphores = &render_complete_semaphore; |
309 | |
310 | err = vkEndCommandBuffer(commandBuffer: fd->CommandBuffer); |
311 | check_vk_result(err); |
312 | err = vkQueueSubmit(queue: g_Queue, submitCount: 1, pSubmits: &info, fence: fd->Fence); |
313 | check_vk_result(err); |
314 | } |
315 | } |
316 | |
317 | static void FramePresent(ImGui_ImplVulkanH_Window* wd) |
318 | { |
319 | if (g_SwapChainRebuild) |
320 | return; |
321 | VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; |
322 | VkPresentInfoKHR info = {}; |
323 | info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
324 | info.waitSemaphoreCount = 1; |
325 | info.pWaitSemaphores = &render_complete_semaphore; |
326 | info.swapchainCount = 1; |
327 | info.pSwapchains = &wd->Swapchain; |
328 | info.pImageIndices = &wd->FrameIndex; |
329 | VkResult err = vkQueuePresentKHR(queue: g_Queue, pPresentInfo: &info); |
330 | if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) |
331 | g_SwapChainRebuild = true; |
332 | if (err == VK_ERROR_OUT_OF_DATE_KHR) |
333 | return; |
334 | if (err != VK_SUBOPTIMAL_KHR) |
335 | check_vk_result(err); |
336 | wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores |
337 | } |
338 | |
339 | // Main code |
340 | int main(int, char**) |
341 | { |
342 | // Setup SDL |
343 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) |
344 | { |
345 | printf(format: "Error: %s\n" , SDL_GetError()); |
346 | return -1; |
347 | } |
348 | |
349 | // From 2.0.18: Enable native IME. |
350 | #ifdef SDL_HINT_IME_SHOW_UI |
351 | SDL_SetHint(SDL_HINT_IME_SHOW_UI, value: "1" ); |
352 | #endif |
353 | |
354 | // Create window with Vulkan graphics context |
355 | SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); |
356 | SDL_Window* window = SDL_CreateWindow(title: "Dear ImGui SDL2+Vulkan example" , SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w: 1280, h: 720, flags: window_flags); |
357 | if (window == nullptr) |
358 | { |
359 | printf(format: "Error: SDL_CreateWindow(): %s\n" , SDL_GetError()); |
360 | return -1; |
361 | } |
362 | |
363 | ImVector<const char*> extensions; |
364 | uint32_t extensions_count = 0; |
365 | SDL_Vulkan_GetInstanceExtensions(window, pCount: &extensions_count, pNames: nullptr); |
366 | extensions.resize(new_size: extensions_count); |
367 | SDL_Vulkan_GetInstanceExtensions(window, pCount: &extensions_count, pNames: extensions.Data); |
368 | SetupVulkan(extensions); |
369 | |
370 | // Create Window Surface |
371 | VkSurfaceKHR surface; |
372 | VkResult err; |
373 | if (SDL_Vulkan_CreateSurface(window, instance: g_Instance, surface: &surface) == 0) |
374 | { |
375 | printf(format: "Failed to create Vulkan surface.\n" ); |
376 | return 1; |
377 | } |
378 | |
379 | // Create Framebuffers |
380 | int w, h; |
381 | SDL_GetWindowSize(window, w: &w, h: &h); |
382 | ImGui_ImplVulkanH_Window* wd = &g_MainWindowData; |
383 | SetupVulkanWindow(wd, surface, width: w, height: h); |
384 | |
385 | // Setup Dear ImGui context |
386 | IMGUI_CHECKVERSION(); |
387 | ImGui::CreateContext(); |
388 | ImGuiIO& io = ImGui::GetIO(); (void)io; |
389 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls |
390 | io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls |
391 | io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking |
392 | io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows |
393 | //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; |
394 | //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; |
395 | |
396 | // Setup Dear ImGui style |
397 | ImGui::StyleColorsDark(); |
398 | //ImGui::StyleColorsLight(); |
399 | |
400 | // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. |
401 | ImGuiStyle& style = ImGui::GetStyle(); |
402 | if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) |
403 | { |
404 | style.WindowRounding = 0.0f; |
405 | style.Colors[ImGuiCol_WindowBg].w = 1.0f; |
406 | } |
407 | |
408 | // Setup Platform/Renderer backends |
409 | ImGui_ImplSDL2_InitForVulkan(window); |
410 | ImGui_ImplVulkan_InitInfo init_info = {}; |
411 | //init_info.ApiVersion = VK_API_VERSION_1_3; // Pass in your value of VkApplicationInfo::apiVersion, otherwise will default to header version. |
412 | init_info.Instance = g_Instance; |
413 | init_info.PhysicalDevice = g_PhysicalDevice; |
414 | init_info.Device = g_Device; |
415 | init_info.QueueFamily = g_QueueFamily; |
416 | init_info.Queue = g_Queue; |
417 | init_info.PipelineCache = g_PipelineCache; |
418 | init_info.DescriptorPool = g_DescriptorPool; |
419 | init_info.RenderPass = wd->RenderPass; |
420 | init_info.Subpass = 0; |
421 | init_info.MinImageCount = g_MinImageCount; |
422 | init_info.ImageCount = wd->ImageCount; |
423 | init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; |
424 | init_info.Allocator = g_Allocator; |
425 | init_info.CheckVkResultFn = check_vk_result; |
426 | ImGui_ImplVulkan_Init(info: &init_info); |
427 | |
428 | // Load Fonts |
429 | // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. |
430 | // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. |
431 | // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). |
432 | // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. |
433 | // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. |
434 | // - Read 'docs/FONTS.md' for more instructions and details. |
435 | // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! |
436 | //io.Fonts->AddFontDefault(); |
437 | //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); |
438 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); |
439 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); |
440 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); |
441 | //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); |
442 | //IM_ASSERT(font != nullptr); |
443 | |
444 | // Our state |
445 | bool show_demo_window = true; |
446 | bool show_another_window = false; |
447 | ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); |
448 | |
449 | // Main loop |
450 | bool done = false; |
451 | while (!done) |
452 | { |
453 | // Poll and handle events (inputs, window resize, etc.) |
454 | // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. |
455 | // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. |
456 | // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. |
457 | // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. |
458 | SDL_Event event; |
459 | while (SDL_PollEvent(event: &event)) |
460 | { |
461 | ImGui_ImplSDL2_ProcessEvent(event: &event); |
462 | if (event.type == SDL_QUIT) |
463 | done = true; |
464 | if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) |
465 | done = true; |
466 | } |
467 | if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) |
468 | { |
469 | SDL_Delay(ms: 10); |
470 | continue; |
471 | } |
472 | |
473 | // Resize swap chain? |
474 | int fb_width, fb_height; |
475 | SDL_GetWindowSize(window, w: &fb_width, h: &fb_height); |
476 | if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) |
477 | { |
478 | ImGui_ImplVulkan_SetMinImageCount(min_image_count: g_MinImageCount); |
479 | ImGui_ImplVulkanH_CreateOrResizeWindow(instance: g_Instance, physical_device: g_PhysicalDevice, device: g_Device, wd: &g_MainWindowData, queue_family: g_QueueFamily, allocator: g_Allocator, w: fb_width, h: fb_height, min_image_count: g_MinImageCount); |
480 | g_MainWindowData.FrameIndex = 0; |
481 | g_SwapChainRebuild = false; |
482 | } |
483 | |
484 | // Start the Dear ImGui frame |
485 | ImGui_ImplVulkan_NewFrame(); |
486 | ImGui_ImplSDL2_NewFrame(); |
487 | ImGui::NewFrame(); |
488 | |
489 | // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). |
490 | if (show_demo_window) |
491 | ImGui::ShowDemoWindow(p_open: &show_demo_window); |
492 | |
493 | // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. |
494 | { |
495 | static float f = 0.0f; |
496 | static int counter = 0; |
497 | |
498 | ImGui::Begin(name: "Hello, world!" ); // Create a window called "Hello, world!" and append into it. |
499 | |
500 | ImGui::Text(fmt: "This is some useful text." ); // Display some text (you can use a format strings too) |
501 | ImGui::Checkbox(label: "Demo Window" , v: &show_demo_window); // Edit bools storing our window open/close state |
502 | ImGui::Checkbox(label: "Another Window" , v: &show_another_window); |
503 | |
504 | ImGui::SliderFloat(label: "float" , v: &f, v_min: 0.0f, v_max: 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f |
505 | ImGui::ColorEdit3(label: "clear color" , col: (float*)&clear_color); // Edit 3 floats representing a color |
506 | |
507 | if (ImGui::Button(label: "Button" )) // Buttons return true when clicked (most widgets return true when edited/activated) |
508 | counter++; |
509 | ImGui::SameLine(); |
510 | ImGui::Text(fmt: "counter = %d" , counter); |
511 | |
512 | ImGui::Text(fmt: "Application average %.3f ms/frame (%.1f FPS)" , 1000.0f / io.Framerate, io.Framerate); |
513 | ImGui::End(); |
514 | } |
515 | |
516 | // 3. Show another simple window. |
517 | if (show_another_window) |
518 | { |
519 | ImGui::Begin(name: "Another Window" , p_open: &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) |
520 | ImGui::Text(fmt: "Hello from another window!" ); |
521 | if (ImGui::Button(label: "Close Me" )) |
522 | show_another_window = false; |
523 | ImGui::End(); |
524 | } |
525 | |
526 | // Rendering |
527 | ImGui::Render(); |
528 | ImDrawData* main_draw_data = ImGui::GetDrawData(); |
529 | const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f); |
530 | wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; |
531 | wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; |
532 | wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; |
533 | wd->ClearValue.color.float32[3] = clear_color.w; |
534 | if (!main_is_minimized) |
535 | FrameRender(wd, draw_data: main_draw_data); |
536 | |
537 | // Update and Render additional Platform Windows |
538 | if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) |
539 | { |
540 | ImGui::UpdatePlatformWindows(); |
541 | ImGui::RenderPlatformWindowsDefault(); |
542 | } |
543 | |
544 | // Present Main Platform Window |
545 | if (!main_is_minimized) |
546 | FramePresent(wd); |
547 | } |
548 | |
549 | // Cleanup |
550 | err = vkDeviceWaitIdle(device: g_Device); |
551 | check_vk_result(err); |
552 | ImGui_ImplVulkan_Shutdown(); |
553 | ImGui_ImplSDL2_Shutdown(); |
554 | ImGui::DestroyContext(); |
555 | |
556 | CleanupVulkanWindow(); |
557 | CleanupVulkan(); |
558 | |
559 | SDL_DestroyWindow(window); |
560 | SDL_Quit(); |
561 | |
562 | return 0; |
563 | } |
564 | |