Skip to content

Integrating with Vulkan

Timothy Gibbons edited this page Jul 28, 2021 · 4 revisions

Introduction

Vulkan is a modern low level graphics api. With vulkan there are changes that is different from OpenGL and DirectX.

This wiki page is inspired by the article from (François Guthmann)[https://frguthmann.github.io/posts/vulkan_imgui/]

This is no way a Vulkan tutorial. To get an idea of the Vulkan API please follow the (vulkan-tutorial)[https://vulkan-tutorial.com/]

Note: I am using Vulkan-HPP headers, but you can certainly use the vulkan.h headers.

Implementation

ImGui Header Files

  • imconfig.h
  • imgui.h
  • imgui_internal.h
  • imstb_rectpack.h
  • imstb_textedit.h
  • imstb_truetype.h

ImGui Source Files

The ImGUI source files you're going to need is.

  • imgui.cpp
  • imgui_demo.cpp (optional)
  • imgui_draw.cpp
  • imgui_tables.cpp
  • imgui_widgets.cpp

Vulkan Components that is needed for ImGui

  • VkInstance
  • VkPhysicalDevice
  • VkDevice
  • VkQueueFamily
  • VkQueue
  • VkDescriptorPool
  • VkCommandPool
  • VkCommandBuffer
  • VkRenderPass
  • VkFramebuffer

Initialize

First we need to initialize Vulkan. You should already have done so if you're following the (vulkan-tutorial)[https://vulkan-tutorial.com/]. After getting Vulkan running, we can start an ImGui context. It's recommended to create a seperate VkDescriptorPool, VkRenderPass, VkCommandBuffer, and VkFramebuffer.

ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
ImGui_Impl{platform}_InitForVulkan(window, true);

After starting an ImGui context, we give ImGui some of the application Vulkan bindings through an ImGui_ImplVulkan_InitInfo struct. We then call ImGui_ImplVulkan_Init() passing in a pointer to the ImGui_ImplVulkan_InitInfo and a VkRenderPass. This will initalize Vulkan for ImGui.

We then upload font textures to the GPU.

// Allocate a command buffer

// Record our command buffer
cb.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
	ImGui_ImplVulkan_CreateFontsTexture(cb);
cb.end();

// Submit to the GPU
vk::SubmitInfo si;
si.commandBufferCount = cb.size();
si.pCommandBuffers = cb.data();
vk::Result res = device->getGraphicsQueue().submit(1, &si, {});
device->getGraphicsQueue().waitIdle(); // Wait for cb is complete

Begin Frame

We need to call the following every frame.

ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();

Ending Frame

When we end the frame, we want to build the model so we can render this to the screen.

ImGuiIO& io = ::ImGui::GetIO();
ImGui::Render();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable){
	ImGui::UpdatePlatformWindows();
	ImGui::RenderPlatformWindowsDefault();
}

Render

Once we have the model we can finally submit the draw call.

vk::ClearValue clearValue;
clearValue.color = vk::ClearColorValue(std::array<float, 4>({0.0f, 0.0f, 0.0f, 1.0f}));
cb.begin({vk::CommandBufferUsageFlags()});
	cb.beginRenderPass({
		imguiRenderPass, 
		imguiFramebuffers[swapchainCurrentImage], 
		{{0,0}, swapchainExtent}, 
		1, &clearValue
	}, vk::SubpassContents::eInline);
	ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cb);
	cb.endRenderPass();
cb.end();

vk::SubmitInfo si;
si.commandBufferCount = 1;
si.pCommandBuffers = &cb;
graphicsQueue->submit(si);

Resize

When the window viewport resizes, we must create a new VkFramebuffer and VkRenderPass

Clean up

At the end of the application life-cycle, we must destroy our ImGui context.

  • ImGui_ImplVulkan_Shutdown()
  • ImGui_ImplGlfw_Shutdown()
  • ImGui::DestroyContext()

Using the API

In our application life cycle we should have

while(true){
    updateWindow();
    
    rendererBeginFrame(); // Acquire images
    imguiBeginFrame();
    ImGui::ShowDemoWindow(); // Issue ImGui commands
    imguiEndFrame();

    imguiRender(); // Submit Command buffers to screen

    rendererPresent(); // Present to the screen
}
Clone this wiki locally