Skip to content

Integrating with Vulkan

omar edited this page Feb 29, 2024 · 4 revisions

(Note This page is not maintained. Consider referring to Getting Started article.)

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

This is no way a Vulkan tutorial. To get an idea of the Vulkan API please follow the vulkan-tutorial

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

Implementation

Dear ImGui Header Files

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

Dear ImGui Source Files

The source files you're going to need are:

  • imgui.cpp
  • imgui_demo.cpp
  • imgui_draw.cpp
  • imgui_tables.cpp
  • imgui_widgets.cpp

Vulkan Components that are needed for Dear 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. After getting Vulkan running, we can start an Dear 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 its context, we give Dear 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 Dear 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
}