// ImGuiUtil.h
#pragma once
import vulkan_hpp;
#include <imgui.h>
#include <functional>
#include <memory>
class ImGuiUtil {
public:
// Initialize ImGui with Vulkan using dynamic rendering
static void Init(
vk::raii::Instance& instance,
vk::raii::PhysicalDevice& physicalDevice,
vk::raii::Device& device,
uint32_t queueFamily,
vk::raii::Queue& queue,
uint32_t minImageCount,
uint32_t imageCount,
vk::Format swapChainImageFormat,
vk::SampleCountFlagBits msaaSamples = vk::SampleCountFlagBits::e1
);
// Shutdown ImGui
static void Shutdown();
// Start a new frame
static void NewFrame();
// Render ImGui draw data to a command buffer
static void Render(vk::raii::CommandBuffer& commandBuffer);
// Update display size
static void UpdateDisplaySize(int width, int height, float scaleX = 1.0f, float scaleY = 1.0f);
// Process platform-specific input event
static bool ProcessInputEvent(void* event);
// Set input callback
static void SetInputCallback(std::function<void(ImGuiIO&)> callback);
private:
// Create descriptor pool for ImGui
static void createDescriptorPool();
// Upload fonts
static void uploadFonts();
// Begin single-time commands
static vk::raii::CommandBuffer beginSingleTimeCommands();
// End single-time commands
static void endSingleTimeCommands(vk::raii::CommandBuffer& commandBuffer);
// Vulkan objects - using inline static initialization (C++17)
inline static vk::raii::Instance* instance = nullptr;
inline static vk::raii::PhysicalDevice* physicalDevice = nullptr;
inline static vk::raii::Device* device = nullptr;
inline static uint32_t queueFamily = 0;
inline static vk::raii::Queue* queue = nullptr;
inline static vk::raii::DescriptorPool descriptorPool = nullptr;
inline static vk::raii::CommandPool commandPool = nullptr;
inline static vk::PipelineRenderingCreateInfo renderingInfo{};
// Input callback
inline static std::function<void(ImGuiIO&)> inputCallback = nullptr;
// Initialization state
inline static bool initialized = false;
};
// ImGuiUtil.cpp
#include "ImGuiUtil.h"
void ImGuiUtil::Init(
vk::raii::Instance& instance,
vk::raii::PhysicalDevice& physicalDevice,
vk::raii::Device& device,
uint32_t queueFamily,
vk::raii::Queue& queue,
uint32_t minImageCount,
uint32_t imageCount,
vk::Format swapChainImageFormat,
vk::SampleCountFlagBits msaaSamples
) {
ImGuiUtil::instance = &instance;
ImGuiUtil::physicalDevice = &physicalDevice;
ImGuiUtil::device = &device;
ImGuiUtil::queueFamily = queueFamily;
ImGuiUtil::queue = &queue;
// Set up dynamic rendering info
renderingInfo.colorAttachmentCount = 1;
vk::Format formats[] = { swapChainImageFormat };
renderingInfo.pColorAttachmentFormats = formats;
// Create command pool for font upload
vk::CommandPoolCreateInfo poolInfo{
.flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = queueFamily
};
commandPool = vk::raii::CommandPool(device, poolInfo);
// Create descriptor pool
createDescriptorPool();
// Initialize ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
// Set up ImGui style
ImGui::StyleColorsDark();
// Initialize our custom Vulkan renderer with dynamic rendering
renderer = ImGuiVulkanRenderer();
renderer.initialize(
*instance,
*physicalDevice,
*device,
queueFamily,
*queue,
*descriptorPool,
minImageCount,
imageCount,
msaaSamples
);
// Set dynamic rendering info
renderer.setDynamicRenderingInfo(renderingInfo);
// Upload fonts
uploadFonts();
initialized = true;
}
void ImGuiUtil::Shutdown() {
if (!initialized) return;
// Wait for device to finish operations
device->waitIdle();
// Cleanup ImGui
renderer.cleanup();
ImGui::DestroyContext();
// Cleanup Vulkan resources
commandPool = nullptr;
descriptorPool = nullptr;
// Reset pointers
instance = nullptr;
physicalDevice = nullptr;
device = nullptr;
queue = nullptr;
initialized = false;
}
void ImGuiUtil::NewFrame() {
if (!initialized) return;
// Update ImGui IO with platform-specific input
ImGuiIO& io = ImGui::GetIO();
// Call input callback if registered
if (inputCallback) {
inputCallback(io);
}
ImGui::NewFrame();
}
void ImGuiUtil::Render(vk::raii::CommandBuffer& commandBuffer) {
if (!initialized) return;
ImGui::Render();
renderer.render(ImGui::GetDrawData(), commandBuffer);
}
void ImGuiUtil::UpdateDisplaySize(int width, int height, float scaleX, float scaleY) {
if (!initialized) return;
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2(static_cast<float>(width), static_cast<float>(height));
io.DisplayFramebufferScale = ImVec2(scaleX, scaleY);
}
bool ImGuiUtil::ProcessInputEvent(void* event) {
// Platform-specific event processing would go here
// This is a placeholder for the actual implementation
return false;
}
void ImGuiUtil::SetInputCallback(std::function<void(ImGuiIO&)> callback) {
inputCallback = callback;
}
void ImGuiUtil::createDescriptorPool() {
// Tune these to match your expected number of UI textures and buffers.
vk::DescriptorPoolSize poolSizes[] =
{
{ vk::DescriptorType::eSampler, 8 },
{ vk::DescriptorType::eCombinedImageSampler, 128 },
{ vk::DescriptorType::eSampledImage, 128 },
{ vk::DescriptorType::eStorageImage, 8 },
{ vk::DescriptorType::eUniformTexelBuffer, 8 },
{ vk::DescriptorType::eStorageTexelBuffer, 8 },
{ vk::DescriptorType::eUniformBuffer, 32 },
{ vk::DescriptorType::eStorageBuffer, 32 },
{ vk::DescriptorType::eUniformBufferDynamic, 16 },
{ vk::DescriptorType::eStorageBufferDynamic, 16 },
{ vk::DescriptorType::eInputAttachment, 8 }
};
uint32_t maxSets = 0;
for (const auto& ps : poolSizes) maxSets += ps.descriptorCount;
vk::DescriptorPoolCreateInfo poolInfo{
.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
.maxSets = maxSets,
.poolSizeCount = static_cast<uint32_t>(std::size(poolSizes)),
.pPoolSizes = poolSizes
};
descriptorPool = vk::raii::DescriptorPool(*device, poolInfo);
}
void ImGuiUtil::uploadFonts() {
vk::raii::CommandBuffer commandBuffer = beginSingleTimeCommands();
renderer.uploadFonts(commandBuffer);
endSingleTimeCommands(commandBuffer);
}
vk::raii::CommandBuffer ImGuiUtil::beginSingleTimeCommands() {
vk::CommandBufferAllocateInfo allocInfo{
.commandPool = *commandPool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1
};
vk::raii::CommandBuffer commandBuffer = vk::raii::CommandBuffers(*device, allocInfo).front();
vk::CommandBufferBeginInfo beginInfo{
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit
};
commandBuffer.begin(beginInfo);
return commandBuffer;
}
void ImGuiUtil::endSingleTimeCommands(vk::raii::CommandBuffer& commandBuffer) {
commandBuffer.end();
vk::SubmitInfo submitInfo{
.commandBufferCount = 1,
.pCommandBuffers = &*commandBuffer
};
queue->submit(submitInfo);
queue->waitIdle();
}