// Dear ImGui: standalone example application for Glfw + Vulkan // Learn about Dear ImGui: // - FAQ https://dearimgui.com/faq // - Getting Started https://dearimgui.com/getting-started // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). // - Introduction, links and more at the top of imgui.cpp // Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. // - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. // You will use those if you want to use this rendering backend in your engine/app. // - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by // the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. // Read comments in imgui_impl_vulkan.h. #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_vulkan.h" #include // printf, fprintf #include // abort #define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_VULKAN #include // Volk headers #ifdef IMGUI_IMPL_VULKAN_USE_VOLK #define VOLK_IMPLEMENTATION #include #endif // [Win32] Our example includes a copy of glfw3.lib pre-compiled with VS2010 to maximize ease of testing and compatibility with old VS compilers. // To link with VS2010-era libraries, VS2015+ requires linking with legacy_stdio_definitions.lib, which we do using this pragma. // Your own project should not be affected, as you are likely to link with a newer binary of GLFW that is adequate for your version of Visual Studio. #if defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) #pragma comment(lib, "legacy_stdio_definitions") #endif //#define APP_USE_UNLIMITED_FRAME_RATE #ifdef _DEBUG #define APP_USE_VULKAN_DEBUG_REPORT static VkDebugReportCallbackEXT g_DebugReport = VK_NULL_HANDLE; #endif static void check_vk_result(VkResult err); #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP // Config for example app static const int APP_RESOURCE_HEAP_SIZE = 64; static const int APP_SAMPLER_HEAP_SIZE = 64; // Explicitly loaded VK_EXT_descriptor_heap entry points. // Stored as members so names can match the Vulkan entry points without colliding with prototypes. struct DescriptorHeapFunctions { PFN_vkWriteResourceDescriptorsEXT vkWriteResourceDescriptorsEXT = nullptr; PFN_vkCmdBindResourceHeapEXT vkCmdBindResourceHeapEXT = nullptr; PFN_vkCmdBindSamplerHeapEXT vkCmdBindSamplerHeapEXT = nullptr; PFN_vkWriteSamplerDescriptorsEXT vkWriteSamplerDescriptorsEXT = nullptr; PFN_vkGetBufferDeviceAddress vkGetBufferDeviceAddress = nullptr; void Load(VkDevice device) { vkWriteResourceDescriptorsEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkWriteResourceDescriptorsEXT")); vkCmdBindResourceHeapEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBindResourceHeapEXT")); vkCmdBindSamplerHeapEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdBindSamplerHeapEXT")); vkWriteSamplerDescriptorsEXT = reinterpret_cast(vkGetDeviceProcAddr(device, "vkWriteSamplerDescriptorsEXT")); vkGetBufferDeviceAddress = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddress")); if (vkGetBufferDeviceAddress == nullptr) vkGetBufferDeviceAddress = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR")); IM_ASSERT(vkWriteResourceDescriptorsEXT && vkCmdBindResourceHeapEXT && vkCmdBindSamplerHeapEXT && vkWriteSamplerDescriptorsEXT && vkGetBufferDeviceAddress); } }; static DescriptorHeapFunctions g_DescHeapFns; // Simple free-list allocator modeled after example_win32_directx12 struct ExampleDescriptorHeapAllocator { VkBuffer Buffer = VK_NULL_HANDLE; VkDeviceMemory Memory = VK_NULL_HANDLE; void* Mapped = nullptr; VkBindHeapInfoEXT BindInfo = {}; VkDeviceSize Stride = 0; int Capacity = 0; ImVector FreeIndices; void Create(VkDevice device, VkPhysicalDevice physical_device, VkDeviceSize descriptor_size, VkDeviceSize alignment, VkDeviceSize reserved_range, int capacity, const VkAllocationCallbacks* allocator) { IM_ASSERT(Buffer == VK_NULL_HANDLE && FreeIndices.empty()); Capacity = capacity; Stride = descriptor_size; VkDeviceSize descriptors_bytes = descriptor_size * (VkDeviceSize)capacity; VkDeviceSize total_size = descriptors_bytes + reserved_range; VkDeviceSize buffer_size_aligned = (total_size + alignment - 1) & ~(alignment - 1); VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = buffer_size_aligned; buffer_info.usage = VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; VkResult err = vkCreateBuffer(device, &buffer_info, allocator, &Buffer); check_vk_result(err); VkMemoryRequirements mem_req; vkGetBufferMemoryRequirements(device, Buffer, &mem_req); VkPhysicalDeviceMemoryProperties mem_props; vkGetPhysicalDeviceMemoryProperties(physical_device, &mem_props); const VkMemoryPropertyFlags required_flags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; uint32_t mem_index = (uint32_t)-1; for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { if ((mem_req.memoryTypeBits & (1u << i)) == 0) continue; if ((mem_props.memoryTypes[i].propertyFlags & required_flags) == required_flags) { mem_index = i; break; } } IM_ASSERT(mem_index != (uint32_t)-1 && "No HOST_VISIBLE|HOST_COHERENT memory type compatible with descriptor heap buffer"); VkMemoryAllocateFlagsInfo alloc_flags = {}; alloc_flags.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; alloc_flags.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT; VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.pNext = &alloc_flags; alloc_info.allocationSize = mem_req.size > buffer_size_aligned ? mem_req.size : buffer_size_aligned; alloc_info.memoryTypeIndex = mem_index; err = vkAllocateMemory(device, &alloc_info, allocator, &Memory); check_vk_result(err); err = vkMapMemory(device, Memory, 0, alloc_info.allocationSize, 0, &Mapped); check_vk_result(err); err = vkBindBufferMemory(device, Buffer, Memory, 0); check_vk_result(err); VkBufferDeviceAddressInfo addr_info = {}; addr_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; addr_info.buffer = Buffer; BindInfo = {}; BindInfo.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT; BindInfo.heapRange.address = g_DescHeapFns.vkGetBufferDeviceAddress(device, &addr_info); BindInfo.heapRange.size = total_size; BindInfo.reservedRangeOffset = descriptors_bytes; BindInfo.reservedRangeSize = reserved_range; FreeIndices.reserve(capacity); for (int n = capacity - 1; n >= 0; n--) FreeIndices.push_back(n); } void Destroy(VkDevice device, const VkAllocationCallbacks* allocator) { if (Buffer) { vkDestroyBuffer(device, Buffer, allocator); Buffer = VK_NULL_HANDLE; } if (Memory) { vkFreeMemory(device, Memory, allocator); Memory = VK_NULL_HANDLE; } Mapped = nullptr; FreeIndices.clear(); Capacity = 0; Stride = 0; BindInfo = {}; } int Alloc() { IM_ASSERT(FreeIndices.Size > 0); int idx = FreeIndices.back(); FreeIndices.pop_back(); return idx; } void Free(int idx) { FreeIndices.push_back(idx); } VkDeviceAddress GpuHandleFromIndex(int idx) const { return BindInfo.heapRange.address + (VkDeviceAddress)idx * (VkDeviceAddress)Stride; } int IndexFromGpuHandle(VkDeviceAddress gpu_handle) const { IM_ASSERT(gpu_handle >= BindInfo.heapRange.address); IM_ASSERT(((gpu_handle - BindInfo.heapRange.address) % Stride) == 0); return (int)((gpu_handle - BindInfo.heapRange.address) / Stride); } VkHostAddressRangeEXT DescriptorAddress(int idx) const { return VkHostAddressRangeEXT{ (char*)Mapped + (VkDeviceSize)idx * Stride, Stride }; } }; #endif // IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP // Data static VkAllocationCallbacks* g_Allocator = nullptr; static VkInstance g_Instance = VK_NULL_HANDLE; static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE; static VkDevice g_Device = VK_NULL_HANDLE; static uint32_t g_QueueFamily = (uint32_t)-1; static VkQueue g_Queue = VK_NULL_HANDLE; static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE; static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static ImGui_ImplVulkanH_Window g_MainWindowData; static uint32_t g_MinImageCount = 2; static bool g_SwapChainRebuild = false; #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP static bool g_UsingDescriptorHeap = false; static ExampleDescriptorHeapAllocator g_ResourceHeapAlloc; static ExampleDescriptorHeapAllocator g_SamplerHeapAlloc; static ImGui_ImplVulkan_DescriptorHeapInfo g_DescriptorHeapInfo; #endif static void glfw_error_callback(int error, const char* description) { fprintf(stderr, "GLFW Error %d: %s\n", error, description); } static void check_vk_result(VkResult err) { if (err == VK_SUCCESS) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) abort(); } #ifdef APP_USE_VULKAN_DEBUG_REPORT 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) { (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } #endif // APP_USE_VULKAN_DEBUG_REPORT static bool IsExtensionAvailable(const ImVector& properties, const char* extension) { for (const VkExtensionProperties& p : properties) if (strcmp(p.extensionName, extension) == 0) return true; return false; } #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP static bool PhysicalDeviceHasExtension(VkPhysicalDevice physical_device, const char* extension) { uint32_t properties_count = 0; vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &properties_count, nullptr); ImVector properties; properties.resize(properties_count); vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &properties_count, properties.Data); return IsExtensionAvailable(properties, extension); } // Extensions required by this example's VK_EXT_descriptor_heap path (Vulkan 1.1 instance → BDA is an extension). // Formal deps of VK_EXT_descriptor_heap: maintenance5 + (buffer_device_address | Vulkan 1.2). // shader_untyped_pointers is required by ImGui's default layout(descriptor_heap) shaders, not by the Vulkan extension itself. // See "About descriptor heap" in imgui_impl_vulkan.h. static bool PhysicalDeviceSupportsImGuiDescriptorHeap(VkPhysicalDevice physical_device) { return PhysicalDeviceHasExtension(physical_device, VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME) && PhysicalDeviceHasExtension(physical_device, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) && PhysicalDeviceHasExtension(physical_device, VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME) && PhysicalDeviceHasExtension(physical_device, VK_KHR_MAINTENANCE_5_EXTENSION_NAME); } #endif // IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP static void SetupVulkan(ImVector instance_extensions, bool require_descriptor_heap) { VkResult err; #ifdef IMGUI_IMPL_VULKAN_USE_VOLK volkInitialize(); #endif // Create Vulkan Instance { VkInstanceCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; // Enumerate available extensions uint32_t properties_count; ImVector properties; vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, nullptr); properties.resize(properties_count); err = vkEnumerateInstanceExtensionProperties(nullptr, &properties_count, properties.Data); check_vk_result(err); // Enable required extensions if (IsExtensionAvailable(properties, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) instance_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); #ifdef VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; } #endif // Enabling validation layers #ifdef APP_USE_VULKAN_DEBUG_REPORT const char* layers[] = { "VK_LAYER_KHRONOS_validation" }; create_info.enabledLayerCount = 1; create_info.ppEnabledLayerNames = layers; instance_extensions.push_back("VK_EXT_debug_report"); #endif // Create Vulkan Instance create_info.enabledExtensionCount = (uint32_t)instance_extensions.Size; create_info.ppEnabledExtensionNames = instance_extensions.Data; VkApplicationInfo app_info = {}; app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; app_info.apiVersion = VK_API_VERSION_1_1; create_info.pApplicationInfo = &app_info; err = vkCreateInstance(&create_info, g_Allocator, &g_Instance); check_vk_result(err); #ifdef IMGUI_IMPL_VULKAN_USE_VOLK volkLoadInstance(g_Instance); #endif // Setup the debug report callback #ifdef APP_USE_VULKAN_DEBUG_REPORT auto f_vkCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkCreateDebugReportCallbackEXT"); IM_ASSERT(f_vkCreateDebugReportCallbackEXT != nullptr); VkDebugReportCallbackCreateInfoEXT debug_report_ci = {}; debug_report_ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debug_report_ci.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; debug_report_ci.pfnCallback = debug_report; debug_report_ci.pUserData = nullptr; err = f_vkCreateDebugReportCallbackEXT(g_Instance, &debug_report_ci, g_Allocator, &g_DebugReport); check_vk_result(err); #endif } #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP if (require_descriptor_heap) { uint32_t device_count = 0; vkEnumeratePhysicalDevices(g_Instance, &device_count, nullptr); ImVector physical_devices; physical_devices.resize(device_count); vkEnumeratePhysicalDevices(g_Instance, &device_count, physical_devices.Data); VkPhysicalDevice device_with_heap = VK_NULL_HANDLE; for (uint32_t i = 0; i < device_count; i++) { if (PhysicalDeviceSupportsImGuiDescriptorHeap(physical_devices[i])) { device_with_heap = physical_devices[i]; break; } } if (device_with_heap == VK_NULL_HANDLE) { fprintf(stderr, "Error: --require-descriptor-heap requested but no device with VK_EXT_descriptor_heap and its required dependencies was found.\n"); fprintf(stderr, " Required: %s, %s, %s, %s\n", VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME, VK_KHR_MAINTENANCE_5_EXTENSION_NAME); exit(1); } g_PhysicalDevice = device_with_heap; } else #endif { (void)require_descriptor_heap; g_PhysicalDevice = ImGui_ImplVulkanH_SelectPhysicalDevice(g_Instance); } IM_ASSERT(g_PhysicalDevice != VK_NULL_HANDLE); VkPhysicalDeviceProperties chosen_props; vkGetPhysicalDeviceProperties(g_PhysicalDevice, &chosen_props); // Select graphics queue family g_QueueFamily = ImGui_ImplVulkanH_SelectQueueFamilyIndex(g_PhysicalDevice); IM_ASSERT(g_QueueFamily != (uint32_t)-1); // Create Logical Device (with 1 queue) { ImVector device_extensions; device_extensions.push_back("VK_KHR_swapchain"); // Enumerate physical device extension uint32_t properties_count; ImVector properties; vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, nullptr); properties.resize(properties_count); vkEnumerateDeviceExtensionProperties(g_PhysicalDevice, nullptr, &properties_count, properties.Data); #ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME if (IsExtensionAvailable(properties, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) device_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); #endif #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP if (PhysicalDeviceSupportsImGuiDescriptorHeap(g_PhysicalDevice)) { device_extensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); device_extensions.push_back(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); device_extensions.push_back(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME); device_extensions.push_back(VK_EXT_DESCRIPTOR_HEAP_EXTENSION_NAME); g_UsingDescriptorHeap = true; } else if (require_descriptor_heap) { fprintf(stderr, "Error: --require-descriptor-heap but selected device is missing a required dependency.\n"); exit(1); } #endif const float queue_priority[] = { 1.0f }; VkDeviceQueueCreateInfo queue_info[1] = {}; queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queue_info[0].queueFamilyIndex = g_QueueFamily; queue_info[0].queueCount = 1; queue_info[0].pQueuePriorities = queue_priority; VkDeviceCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; create_info.queueCreateInfoCount = sizeof(queue_info) / sizeof(queue_info[0]); create_info.pQueueCreateInfos = queue_info; create_info.enabledExtensionCount = (uint32_t)device_extensions.Size; create_info.ppEnabledExtensionNames = device_extensions.Data; #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP VkPhysicalDeviceBufferDeviceAddressFeatures buffer_device_addr = {}; buffer_device_addr.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES; buffer_device_addr.bufferDeviceAddress = VK_TRUE; VkPhysicalDeviceDescriptorHeapFeaturesEXT desc_heap_feat = {}; desc_heap_feat.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT; desc_heap_feat.pNext = &buffer_device_addr; desc_heap_feat.descriptorHeap = VK_TRUE; VkPhysicalDeviceShaderUntypedPointersFeaturesKHR untyped_pointers = {}; untyped_pointers.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR; untyped_pointers.pNext = &desc_heap_feat; untyped_pointers.shaderUntypedPointers = VK_TRUE; if (g_UsingDescriptorHeap) create_info.pNext = &untyped_pointers; #endif err = vkCreateDevice(g_PhysicalDevice, &create_info, g_Allocator, &g_Device); check_vk_result(err); vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue); } #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP if (g_UsingDescriptorHeap) { g_DescHeapFns.Load(g_Device); VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptor_heap_properties = {}; descriptor_heap_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT; VkPhysicalDeviceProperties2 properties2 = {}; properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; properties2.pNext = &descriptor_heap_properties; vkGetPhysicalDeviceProperties2(g_PhysicalDevice, &properties2); g_ResourceHeapAlloc.Create(g_Device, g_PhysicalDevice, descriptor_heap_properties.imageDescriptorSize, descriptor_heap_properties.resourceHeapAlignment, descriptor_heap_properties.minResourceHeapReservedRange, APP_RESOURCE_HEAP_SIZE, g_Allocator); g_SamplerHeapAlloc.Create(g_Device, g_PhysicalDevice, descriptor_heap_properties.samplerDescriptorSize, descriptor_heap_properties.samplerHeapAlignment, descriptor_heap_properties.minSamplerHeapReservedRange, APP_SAMPLER_HEAP_SIZE, g_Allocator); // Allocating descriptors is up to the application, so we provide callbacks. ImTextureID = GPU descriptor device address. g_DescriptorHeapInfo = {}; g_DescriptorHeapInfo.ResourceHeapAddress = g_ResourceHeapAlloc.BindInfo.heapRange.address; g_DescriptorHeapInfo.ImageDescriptorSize = g_ResourceHeapAlloc.Stride; g_DescriptorHeapInfo.UserContext = nullptr; g_DescriptorHeapInfo.RegisterImage = [](void*, const VkImageViewCreateInfo* ci) -> uint64_t { int idx = g_ResourceHeapAlloc.Alloc(); VkImageDescriptorInfoEXT image = {}; image.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT; image.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image.pView = ci; VkResourceDescriptorInfoEXT resource = {}; resource.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT; resource.type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; resource.data.pImage = ℑ VkHostAddressRangeEXT addr = g_ResourceHeapAlloc.DescriptorAddress(idx); check_vk_result(g_DescHeapFns.vkWriteResourceDescriptorsEXT(g_Device, 1, &resource, &addr)); return (uint64_t)g_ResourceHeapAlloc.GpuHandleFromIndex(idx); }; g_DescriptorHeapInfo.UnRegisterImage = [](void*, uint64_t gpu_handle) { g_ResourceHeapAlloc.Free(g_ResourceHeapAlloc.IndexFromGpuHandle((VkDeviceAddress)gpu_handle)); }; g_DescriptorHeapInfo.RegisterSampler = [](void*, const VkSamplerCreateInfo* ci) -> uint32_t { int idx = g_SamplerHeapAlloc.Alloc(); VkHostAddressRangeEXT addr = g_SamplerHeapAlloc.DescriptorAddress(idx); check_vk_result(g_DescHeapFns.vkWriteSamplerDescriptorsEXT(g_Device, 1, ci, &addr)); return (uint32_t)idx; }; g_DescriptorHeapInfo.UnRegisterSampler = [](void*, uint32_t idx) { g_SamplerHeapAlloc.Free((int)idx); }; } else #endif { // Create Descriptor Pool // If you wish to load e.g. additional textures you may need to alter pools sizes and maxSets. VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLED_IMAGE_POOL_SIZE }, { VK_DESCRIPTOR_TYPE_SAMPLER, IMGUI_IMPL_VULKAN_MINIMUM_SAMPLER_POOL_SIZE }, }; VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; pool_info.maxSets = 0; for (VkDescriptorPoolSize& pool_size : pool_sizes) pool_info.maxSets += pool_size.descriptorCount; pool_info.poolSizeCount = (uint32_t)IM_COUNTOF(pool_sizes); pool_info.pPoolSizes = pool_sizes; err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool); check_vk_result(err); } } // All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo. // Your real engine/app may not use them. static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height) { // Check for WSI support VkBool32 res; vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, surface, &res); if (res != VK_TRUE) { fprintf(stderr, "Error no WSI support on physical device 0\n"); exit(-1); } // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; wd->Surface = surface; wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_COUNTOF(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode #ifdef APP_USE_UNLIMITED_FRAME_RATE VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; #else VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; #endif wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_COUNTOF(present_modes)); //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode); // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); } static void CleanupVulkan() { #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP if (g_UsingDescriptorHeap) { g_ResourceHeapAlloc.Destroy(g_Device, g_Allocator); g_SamplerHeapAlloc.Destroy(g_Device, g_Allocator); } else #endif { vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator); } #ifdef APP_USE_VULKAN_DEBUG_REPORT // Remove the debug report callback auto f_vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(g_Instance, "vkDestroyDebugReportCallbackEXT"); f_vkDestroyDebugReportCallbackEXT(g_Instance, g_DebugReport, g_Allocator); #endif // APP_USE_VULKAN_DEBUG_REPORT vkDestroyDevice(g_Device, g_Allocator); vkDestroyInstance(g_Instance, g_Allocator); } static void CleanupVulkanWindow(ImGui_ImplVulkanH_Window* wd) { ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, wd, g_Allocator); vkDestroySurfaceKHR(g_Instance, wd->Surface, g_Allocator); } static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) g_SwapChainRebuild = true; if (err == VK_ERROR_OUT_OF_DATE_KHR) return; if (err != VK_SUBOPTIMAL_KHR) check_vk_result(err); ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking check_vk_result(err); err = vkResetFences(g_Device, 1, &fd->Fence); check_vk_result(err); } { err = vkResetCommandPool(g_Device, fd->CommandPool, 0); check_vk_result(err); VkCommandBufferBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; err = vkBeginCommandBuffer(fd->CommandBuffer, &info); check_vk_result(err); } { VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = wd->RenderPass; info.framebuffer = fd->Framebuffer; info.renderArea.extent.width = wd->Width; info.renderArea.extent.height = wd->Height; info.clearValueCount = 1; info.pClearValues = &wd->ClearValue; vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); } #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP if (g_UsingDescriptorHeap) { g_DescHeapFns.vkCmdBindResourceHeapEXT(fd->CommandBuffer, &g_ResourceHeapAlloc.BindInfo); g_DescHeapFns.vkCmdBindSamplerHeapEXT(fd->CommandBuffer, &g_SamplerHeapAlloc.BindInfo); } #endif // Record dear imgui primitives into command buffer ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); // Submit command buffer vkCmdEndRenderPass(fd->CommandBuffer); { VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &image_acquired_semaphore; info.pWaitDstStageMask = &wait_stage; info.commandBufferCount = 1; info.pCommandBuffers = &fd->CommandBuffer; info.signalSemaphoreCount = 1; info.pSignalSemaphores = &render_complete_semaphore; err = vkEndCommandBuffer(fd->CommandBuffer); check_vk_result(err); err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence); check_vk_result(err); } } static void FramePresent(ImGui_ImplVulkanH_Window* wd) { if (g_SwapChainRebuild) return; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; VkPresentInfoKHR info = {}; info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; info.waitSemaphoreCount = 1; info.pWaitSemaphores = &render_complete_semaphore; info.swapchainCount = 1; info.pSwapchains = &wd->Swapchain; info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) g_SwapChainRebuild = true; if (err == VK_ERROR_OUT_OF_DATE_KHR) return; if (err != VK_SUBOPTIMAL_KHR) check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } // Main code int main(int argc, char** argv) { bool require_descriptor_heap = false; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--require-descriptor-heap") == 0) { #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP require_descriptor_heap = true; #else fprintf(stderr, "Error: --require-descriptor-heap requested but Vulkan headers lack VK_EXT_descriptor_heap (IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP undefined).\n"); return 1; #endif } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { printf("Usage: %s [--require-descriptor-heap]\n", argv[0]); #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP printf(" --require-descriptor-heap Require a device with VK_EXT_descriptor_heap (exit on failure).\n"); printf(" (default) Use ImGui_ImplVulkanH_SelectPhysicalDevice; enable heap if available.\n"); #else printf(" --require-descriptor-heap Unavailable (Vulkan headers lack VK_EXT_descriptor_heap).\n"); #endif return 0; } else { fprintf(stderr, "Unknown argument: %s (try --help)\n", argv[i]); return 1; } } glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) return 1; // Create window with Vulkan context glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only GLFWwindow* window = glfwCreateWindow((int)(1280 * main_scale), (int)(800 * main_scale), "Dear ImGui GLFW+Vulkan example", nullptr, nullptr); if (!glfwVulkanSupported()) { printf("GLFW: Vulkan Not Supported\n"); return 1; } ImVector extensions; uint32_t extensions_count = 0; const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&extensions_count); for (uint32_t i = 0; i < extensions_count; i++) extensions.push_back(glfw_extensions[i]); SetupVulkan(extensions, require_descriptor_heap); // Create Window Surface VkSurfaceKHR surface; VkResult err = glfwCreateWindowSurface(g_Instance, window, g_Allocator, &surface); check_vk_result(err); // Create Framebuffers int w, h; glfwGetFramebufferSize(window, &w, &h); ImGui_ImplVulkanH_Window* wd = &g_MainWindowData; SetupVulkanWindow(wd, surface, w, h); // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); // Setup scaling ImGuiStyle& style = ImGui::GetStyle(); style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again) style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor) // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForVulkan(window, true); ImGui_ImplVulkan_InitInfo init_info = {}; //init_info.ApiVersion = VK_API_VERSION_1_3; // Pass in your value of VkApplicationInfo::apiVersion, otherwise will default to header version. init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; init_info.Device = g_Device; init_info.QueueFamily = g_QueueFamily; init_info.Queue = g_Queue; init_info.PipelineCache = g_PipelineCache; init_info.DescriptorPool = g_DescriptorPool; init_info.MinImageCount = g_MinImageCount; init_info.ImageCount = wd->ImageCount; init_info.Allocator = g_Allocator; init_info.PipelineInfoMain.RenderPass = wd->RenderPass; init_info.PipelineInfoMain.Subpass = 0; init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; init_info.CheckVkResultFn = check_vk_result; #ifdef IMGUI_IMPL_VULKAN_HAS_DESCRIPTOR_HEAP if (g_UsingDescriptorHeap) init_info.DescriptorHeapInfo = &g_DescriptorHeapInfo; #endif ImGui_ImplVulkan_Init(&init_info); // Load Fonts // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap(). // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold. // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit). // - Read 'docs/FONTS.md' for more instructions and details. // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! //style.FontSizeBase = 20.0f; //io.Fonts->AddFontDefaultVector(); //io.Fonts->AddFontDefaultBitmap(); //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf"); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf"); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf"); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf"); //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf"); //IM_ASSERT(font != nullptr); // Our state bool show_demo_window = true; bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop while (!glfwWindowShouldClose(window)) { // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); // Resize swap chain? int fb_width, fb_height; glfwGetFramebufferSize(window, &fb_width, &fb_height); if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) { ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0) { ImGui_ImplGlfw_Sleep(10); continue; } // Start the Dear ImGui frame ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); // 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!). if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. { static float f = 0.0f; static int counter = 0; ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) counter++; ImGui::SameLine(); ImGui::Text("counter = %d", counter); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::End(); } // 3. Show another simple window. if (show_another_window) { ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) ImGui::Text("Hello from another window!"); if (ImGui::Button("Close Me")) show_another_window = false; ImGui::End(); } // Rendering ImGui::Render(); ImDrawData* draw_data = ImGui::GetDrawData(); const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); if (!is_minimized) { wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; wd->ClearValue.color.float32[3] = clear_color.w; FrameRender(wd, draw_data); FramePresent(wd); } } // Cleanup err = vkDeviceWaitIdle(g_Device); check_vk_result(err); ImGui_ImplVulkan_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); CleanupVulkanWindow(&g_MainWindowData); CleanupVulkan(); glfwDestroyWindow(window); glfwTerminate(); return 0; }