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