From adb4faf58e327a5b8e56e3dbe948e219e763d64a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 10 Nov 2025 18:17:20 -0500 Subject: [PATCH] xo-imgui: refactor: separate VulkanApp TU for ex4a --- xo-imgui/example/ex4a/CMakeLists.txt | 1 + xo-imgui/example/ex4a/VulkanApp.cpp | 14 + xo-imgui/example/ex4a/VulkanApp.hpp | 687 ++++++++++++++++++++++++++ xo-imgui/example/ex4a/imgui_ex4a.cpp | 689 +-------------------------- 4 files changed, 703 insertions(+), 688 deletions(-) create mode 100644 xo-imgui/example/ex4a/VulkanApp.cpp create mode 100644 xo-imgui/example/ex4a/VulkanApp.hpp diff --git a/xo-imgui/example/ex4a/CMakeLists.txt b/xo-imgui/example/ex4a/CMakeLists.txt index 9e824999..1d0d4575 100644 --- a/xo-imgui/example/ex4a/CMakeLists.txt +++ b/xo-imgui/example/ex4a/CMakeLists.txt @@ -19,6 +19,7 @@ if (XO_ENABLE_EXAMPLES) set(SELF_EXE imgui_ex4a) add_executable(${SELF_EXE} imgui_ex4a.cpp + VulkanApp.cpp ${IMGUI_INCLUDE_DIR}/imgui/imgui.cpp ${IMGUI_INCLUDE_DIR}/imgui/imgui_demo.cpp ${IMGUI_INCLUDE_DIR}/imgui/imgui_draw.cpp diff --git a/xo-imgui/example/ex4a/VulkanApp.cpp b/xo-imgui/example/ex4a/VulkanApp.cpp new file mode 100644 index 00000000..17056b00 --- /dev/null +++ b/xo-imgui/example/ex4a/VulkanApp.cpp @@ -0,0 +1,14 @@ +/* VulkanApp.cpp */ + +#include "VulkanApp.hpp" + +void MinimalImGuiVulkan::run() +{ + this->initWindow(); + this->initVulkan(); + this->initImGui(); + this->mainLoop(); + this->cleanup(); +} + +/* end VulkanApp.cpp */ diff --git a/xo-imgui/example/ex4a/VulkanApp.hpp b/xo-imgui/example/ex4a/VulkanApp.hpp new file mode 100644 index 00000000..5ee5c098 --- /dev/null +++ b/xo-imgui/example/ex4a/VulkanApp.hpp @@ -0,0 +1,687 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class MinimalImGuiVulkan { +public: + /* run application; borrows calling thread for event loop, + * until application exit + */ + void run(); + +private: + void initVulkan() { + createInstance(); + createSurface(); + pickPhysicalDevice(); + createLogicalDevice(); + this->createSwapchain(); + this->createImageViews(); + this->createRenderPass(); // must come before createFrameBuffers + this->createFramebuffers(); + createCommandPool(); + createCommandBuffers(); + createSyncObjects(); + createDescriptorPool(); + } + + void initWindow() { + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + throw std::runtime_error("Failed to initialize SDL!"); + } + + this->window_ = SDL_CreateWindow( + "ImGui Vulkan SDL2 Example", + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + 800, 600, + SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE + ); + + if (!window_) { + throw std::runtime_error("Failed to create SDL window!"); + } + } + + void createInstance() { + VkApplicationInfo appInfo{}; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pApplicationName = "ImGui Vulkan App"; + appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.pEngineName = "No Engine"; + appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + + uint32_t extensionCount = 0; + if (!SDL_Vulkan_GetInstanceExtensions(window_, &extensionCount, nullptr)) { + throw std::runtime_error("Failed to get SDL Vulkan extensions!"); + } + + std::vector extensions(extensionCount); + if (!SDL_Vulkan_GetInstanceExtensions(window_, &extensionCount, extensions.data())) { + throw std::runtime_error("Failed to get SDL Vulkan extensions!"); + } + +#ifdef __apple__ + // Add portability extension for MoltenVK (macOS) + extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); +#endif + + createInfo.enabledExtensionCount = extensions.size(); + createInfo.ppEnabledExtensionNames = extensions.data(); + createInfo.enabledLayerCount = 0; + +#ifdef __apple__ + // CRITICAL: Enable portability enumeration flag for MoltenVK + createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; +#endif + + int result = vkCreateInstance(&createInfo, nullptr, &(this->instance_)); + if (result != VK_SUCCESS) { + printf("vkCreateInstance failed with error: %d\n", result); + throw std::runtime_error("Failed to create instance!"); + } + } + + void createSurface() { + if (!SDL_Vulkan_CreateSurface(window_, instance_, &surface)) { + throw std::runtime_error("Failed to create SDL Vulkan surface!"); + } + } + + void pickPhysicalDevice() { + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance_, &deviceCount, nullptr); + + if (deviceCount == 0) { + throw std::runtime_error("Failed to find GPUs with Vulkan support!"); + } + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance_, &deviceCount, devices.data()); + + this->physical_device_ = devices[0]; // Just pick the first one for simplicity + + // Find graphics queue family + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &queueFamilyCount, nullptr); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &queueFamilyCount, queueFamilies.data()); + + for (uint32_t i = 0; i < queueFamilies.size(); i++) { + if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(physical_device_, i, surface, &presentSupport); + if (presentSupport) { + graphicsQueueFamily = i; + break; + } + } + } + } + + void createLogicalDevice() { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = graphicsQueueFamily; + queueCreateInfo.queueCount = 1; + float queuePriority = 1.0f; + queueCreateInfo.pQueuePriorities = &queuePriority; + + VkPhysicalDeviceFeatures deviceFeatures{}; + + VkDeviceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.pQueueCreateInfos = &queueCreateInfo; + createInfo.queueCreateInfoCount = 1; + createInfo.pEnabledFeatures = &deviceFeatures; + + const char* deviceExtensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; + createInfo.enabledExtensionCount = 1; + createInfo.ppEnabledExtensionNames = deviceExtensions; + + if (vkCreateDevice(physical_device_, &createInfo, nullptr, &device) != VK_SUCCESS) { + throw std::runtime_error("Failed to create logical device!"); + } + + vkGetDeviceQueue(device, graphicsQueueFamily, 0, &graphicsQueue); + } + + void createSwapchain() { + VkSurfaceCapabilitiesKHR capabilities; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device_, surface, &capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface, &formatCount, nullptr); + std::vector formats(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface, &formatCount, formats.data()); + + VkSurfaceFormatKHR surfaceFormat = formats[0]; + swapchainImageFormat = surfaceFormat.format; + + int width, height; + SDL_Vulkan_GetDrawableSize(window_, &width, &height); + swapchainExtent = {static_cast(width), static_cast(height)}; + + uint32_t imageCount = capabilities.minImageCount + 1; + if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) { + imageCount = capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = surface; + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = swapchainExtent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.preTransform = capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; + createInfo.clipped = VK_TRUE; + + if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapchain) != VK_SUCCESS) { + throw std::runtime_error("Failed to create swap chain!"); + } + + vkGetSwapchainImagesKHR(device, swapchain, &imageCount, nullptr); + swapchainImages.resize(imageCount); + vkGetSwapchainImagesKHR(device, swapchain, &imageCount, swapchainImages.data()); + } + + void createImageViews() { + swapchainImageViews.resize(swapchainImages.size()); + + for (size_t i = 0; i < swapchainImages.size(); i++) { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = swapchainImages[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = swapchainImageFormat; + createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.baseArrayLayer = 0; + createInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(device, &createInfo, nullptr, &swapchainImageViews[i]) != VK_SUCCESS) { + throw std::runtime_error("Failed to create image views!"); + } + } + } + + void createRenderPass() { + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = swapchainImageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = 1; + renderPassInfo.pAttachments = &colorAttachment; + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpass; + renderPassInfo.dependencyCount = 1; + renderPassInfo.pDependencies = &dependency; + + if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { + throw std::runtime_error("Failed to create render pass!"); + } + } + + void createFramebuffers() { + framebuffers.resize(swapchainImageViews.size()); + + for (size_t i = 0; i < swapchainImageViews.size(); i++) { + VkImageView attachments[] = { swapchainImageViews[i] }; + + VkFramebufferCreateInfo framebufferInfo{}; + framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebufferInfo.renderPass = renderPass; + framebufferInfo.attachmentCount = 1; + framebufferInfo.pAttachments = attachments; + framebufferInfo.width = swapchainExtent.width; + framebufferInfo.height = swapchainExtent.height; + framebufferInfo.layers = 1; + + if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) { + throw std::runtime_error("Failed to create framebuffer!"); + } + } + } + + void createCommandPool() { + VkCommandPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + poolInfo.queueFamilyIndex = graphicsQueueFamily; + + if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create command pool!"); + } + } + + void createCommandBuffers() { + commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.commandPool = commandPool; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandBufferCount = static_cast(commandBuffers.size()); + + if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { + throw std::runtime_error("Failed to allocate command buffers!"); + } + } + + void createSyncObjects() { + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + + VkSemaphoreCreateInfo semaphoreInfo{}; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || + vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || + vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { + throw std::runtime_error("Failed to create synchronization objects!"); + } + } + } + + void createDescriptorPool() { + VkDescriptorPoolSize pool_sizes[] = { + { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } + }; + + 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 = 1000 * IM_ARRAYSIZE(pool_sizes); + pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); + pool_info.pPoolSizes = pool_sizes; + + if (vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptorPool) != VK_SUCCESS) { + throw std::runtime_error("Failed to create descriptor pool!"); + } + } + + void initImGui() { + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + + // Setup Platform/Renderer backends + ImGui_ImplSDL2_InitForVulkan(window_); + ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = instance_; + init_info.PhysicalDevice = physical_device_; + init_info.Device = device; + init_info.QueueFamily = graphicsQueueFamily; + init_info.Queue = graphicsQueue; + init_info.PipelineCache = VK_NULL_HANDLE; + init_info.DescriptorPool = descriptorPool; + init_info.RenderPass = renderPass; + init_info.Subpass = 0; + init_info.MinImageCount = MAX_FRAMES_IN_FLIGHT; + init_info.ImageCount = static_cast(swapchainImages.size()); + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + init_info.Allocator = nullptr; + init_info.CheckVkResultFn = nullptr; + ImGui_ImplVulkan_Init(&init_info); + //ImGui_ImplVulkan_Init(&init_info, renderPass); + + // Upload Fonts + VkCommandBuffer command_buffer = beginSingleTimeCommands(); + ImGui_ImplVulkan_CreateFontsTexture(); + endSingleTimeCommands(command_buffer); + + //ImGui_ImplVulkan_DestroyFontUploadObjects(); + } + + VkCommandBuffer beginSingleTimeCommands() { + VkCommandBufferAllocateInfo allocInfo{}; + allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocInfo.commandPool = commandPool; + allocInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + return commandBuffer; + } + + void endSingleTimeCommands(VkCommandBuffer commandBuffer) { + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(graphicsQueue); + + vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); + } + + void mainLoop() { + SDL_Event event; + + while (!quit) { + while (SDL_PollEvent(&event)) { + ImGui_ImplSDL2_ProcessEvent(&event); + + if (event.type == SDL_QUIT) { + quit = true; + } + } + + drawFrame(); + } + + vkDeviceWaitIdle(device); + } + + void drawFrame() { + vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); + + uint32_t imageIndex; + VkResult result = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, + imageAvailableSemaphores[currentFrame], + VK_NULL_HANDLE, &imageIndex); + + switch (result) { + case VK_SUCCESS: + case VK_SUBOPTIMAL_KHR: + break; + case VK_ERROR_OUT_OF_DATE_KHR: + recreateSwapchain(); + // deliberate earlyexit + return; + default: + throw std::runtime_error("failed to acquire swapchain image!"); + break; + } + + vkResetFences(device, 1, &inFlightFences[currentFrame]); + + vkResetCommandBuffer(commandBuffers[currentFrame], 0); + recordCommandBuffer(commandBuffers[currentFrame], imageIndex); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + + VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitDstStageMask = waitStages; + + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; + + VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { + throw std::runtime_error("Failed to submit draw command buffer!"); + } + + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = signalSemaphores; + + VkSwapchainKHR swapChains[] = {swapchain}; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = swapChains; + + presentInfo.pImageIndices = &imageIndex; + + result = vkQueuePresentKHR(graphicsQueue, &presentInfo); + + if (framebuffer_resized_flag_) + result = VK_ERROR_OUT_OF_DATE_KHR; + + switch (result) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_DATE_KHR: + case VK_SUBOPTIMAL_KHR: + framebuffer_resized_flag_ = false; + this->recreateSwapchain(); + break; + default: + throw std::runtime_error("failed to present swapchain image!"); + } + + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + } + + void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + + if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { + throw std::runtime_error("Failed to begin recording command buffer!"); + } + + VkRenderPassBeginInfo renderPassInfo{}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = renderPass; + renderPassInfo.framebuffer = framebuffers[imageIndex]; + renderPassInfo.renderArea.offset = {0, 0}; + renderPassInfo.renderArea.extent = swapchainExtent; + + VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearColor; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + // Start the Dear ImGui frame + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + // Create a simple ImGui window + ImGui::Begin("Hello, Vulkan + SDL2!"); + ImGui::Text("This is a minimal ImGui + Vulkan + SDL2 example!"); + static float f = 0.0f; + static int counter = 0; + ImGui::SliderFloat("float", &f, 0.0f, 1.0f); + if (ImGui::Button("Button")) + counter++; + ImGui::SameLine(); + ImGui::Text("counter = %d", counter); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::End(); + + // Rendering + ImGui::Render(); + ImDrawData* draw_data = ImGui::GetDrawData(); + ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffer); + + vkCmdEndRenderPass(commandBuffer); + + if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { + throw std::runtime_error("Failed to record command buffer!"); + } + } + + void recreateSwapchain() { + // handle window minimization: wait until window has valid size + int width = 0; + int height = 0; + SDL_GetWindowSize(window_, &width, &height); + while (width == 0 || height == 0) { + SDL_GetWindowSize(window_, &width, &height); + SDL_WaitEvent(nullptr); + } + + // wait until device idle before cleaning up resources + vkDeviceWaitIdle(device); + + // cleanup old swapchain + this->cleanupFrameBuffers(); + this->cleanupImageViews(); + this->cleanupSwapchain(); + + // create new swapchain + this->createSwapchain(); + this->createImageViews(); + this->createFramebuffers(); + } + + void cleanupFrameBuffers() { + for (auto framebuffer : framebuffers) { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + framebuffers.clear(); + } + + void cleanupImageViews() { + for (auto imageView : swapchainImageViews) { + vkDestroyImageView(device, imageView, nullptr); + } + swapchainImageViews.clear(); + } + + void cleanupSwapchain() { + vkDestroySwapchainKHR(device, swapchain, nullptr); + } + + void cleanup() { + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + ImGui::DestroyContext(); + + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + vkDestroyFence(device, inFlightFences[i], nullptr); + } + + vkDestroyCommandPool(device, commandPool, nullptr); + + this->cleanupFrameBuffers(); + this->cleanupImageViews(); + this->cleanupSwapchain(); + + vkDestroyRenderPass(device, renderPass, nullptr); + vkDestroyDescriptorPool(device, descriptorPool, nullptr); + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance_, surface, nullptr); + vkDestroyInstance(instance_, nullptr); + this->instance_ = nullptr; + + SDL_DestroyWindow(window_); + this->window_ = nullptr; + + SDL_Quit(); + } + +private: + SDL_Window* window_ = nullptr; + + VkInstance instance_; + VkPhysicalDevice physical_device_; + VkDevice device; + VkQueue graphicsQueue; + VkSurfaceKHR surface; + VkSwapchainKHR swapchain; + VkFormat swapchainImageFormat; + VkExtent2D swapchainExtent; + std::vector swapchainImages; + std::vector swapchainImageViews; + VkRenderPass renderPass; + std::vector framebuffers; + VkCommandPool commandPool; + std::vector commandBuffers; + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; + VkDescriptorPool descriptorPool; + + uint32_t currentFrame = 0; + const int MAX_FRAMES_IN_FLIGHT = 2; + uint32_t graphicsQueueFamily = 0; + /* true when window resize behavior detected, + * until swapchain in consistent state. + */ + bool framebuffer_resized_flag_ = false; + bool quit = false; +}; /*MinimalImGuiVulkan*/ diff --git a/xo-imgui/example/ex4a/imgui_ex4a.cpp b/xo-imgui/example/ex4a/imgui_ex4a.cpp index 21324a91..d0091b50 100644 --- a/xo-imgui/example/ex4a/imgui_ex4a.cpp +++ b/xo-imgui/example/ex4a/imgui_ex4a.cpp @@ -4284,694 +4284,7 @@ int main(int, char **) /* imgui_ex4a.cpp */ #endif //DEBUG -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class MinimalImGuiVulkan { -public: - void run() { - initWindow(); - initVulkan(); - initImGui(); - mainLoop(); - cleanup(); - } - -private: - void initVulkan() { - createInstance(); - createSurface(); - pickPhysicalDevice(); - createLogicalDevice(); - this->createSwapchain(); - this->createImageViews(); - this->createRenderPass(); // must come before createFrameBuffers - this->createFramebuffers(); - createCommandPool(); - createCommandBuffers(); - createSyncObjects(); - createDescriptorPool(); - } - - void initWindow() { - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - throw std::runtime_error("Failed to initialize SDL!"); - } - - this->window_ = SDL_CreateWindow( - "ImGui Vulkan SDL2 Example", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - 800, 600, - SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE - ); - - if (!window_) { - throw std::runtime_error("Failed to create SDL window!"); - } - } - - void createInstance() { - VkApplicationInfo appInfo{}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = "ImGui Vulkan App"; - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.pEngineName = "No Engine"; - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.apiVersion = VK_API_VERSION_1_0; - - VkInstanceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pApplicationInfo = &appInfo; - - uint32_t extensionCount = 0; - if (!SDL_Vulkan_GetInstanceExtensions(window_, &extensionCount, nullptr)) { - throw std::runtime_error("Failed to get SDL Vulkan extensions!"); - } - - std::vector extensions(extensionCount); - if (!SDL_Vulkan_GetInstanceExtensions(window_, &extensionCount, extensions.data())) { - throw std::runtime_error("Failed to get SDL Vulkan extensions!"); - } - -#ifdef __apple__ - // Add portability extension for MoltenVK (macOS) - extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); -#endif - - createInfo.enabledExtensionCount = extensions.size(); - createInfo.ppEnabledExtensionNames = extensions.data(); - createInfo.enabledLayerCount = 0; - -#ifdef __apple__ - // CRITICAL: Enable portability enumeration flag for MoltenVK - createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; -#endif - - int result = vkCreateInstance(&createInfo, nullptr, &(this->instance_)); - if (result != VK_SUCCESS) { - printf("vkCreateInstance failed with error: %d\n", result); - throw std::runtime_error("Failed to create instance!"); - } - } - - void createSurface() { - if (!SDL_Vulkan_CreateSurface(window_, instance_, &surface)) { - throw std::runtime_error("Failed to create SDL Vulkan surface!"); - } - } - - void pickPhysicalDevice() { - uint32_t deviceCount = 0; - vkEnumeratePhysicalDevices(instance_, &deviceCount, nullptr); - - if (deviceCount == 0) { - throw std::runtime_error("Failed to find GPUs with Vulkan support!"); - } - - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance_, &deviceCount, devices.data()); - - this->physical_device_ = devices[0]; // Just pick the first one for simplicity - - // Find graphics queue family - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &queueFamilyCount, nullptr); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &queueFamilyCount, queueFamilies.data()); - - for (uint32_t i = 0; i < queueFamilies.size(); i++) { - if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(physical_device_, i, surface, &presentSupport); - if (presentSupport) { - graphicsQueueFamily = i; - break; - } - } - } - } - - void createLogicalDevice() { - VkDeviceQueueCreateInfo queueCreateInfo{}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = graphicsQueueFamily; - queueCreateInfo.queueCount = 1; - float queuePriority = 1.0f; - queueCreateInfo.pQueuePriorities = &queuePriority; - - VkPhysicalDeviceFeatures deviceFeatures{}; - - VkDeviceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.pQueueCreateInfos = &queueCreateInfo; - createInfo.queueCreateInfoCount = 1; - createInfo.pEnabledFeatures = &deviceFeatures; - - const char* deviceExtensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - createInfo.enabledExtensionCount = 1; - createInfo.ppEnabledExtensionNames = deviceExtensions; - - if (vkCreateDevice(physical_device_, &createInfo, nullptr, &device) != VK_SUCCESS) { - throw std::runtime_error("Failed to create logical device!"); - } - - vkGetDeviceQueue(device, graphicsQueueFamily, 0, &graphicsQueue); - } - - void createSwapchain() { - VkSurfaceCapabilitiesKHR capabilities; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device_, surface, &capabilities); - - uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface, &formatCount, nullptr); - std::vector formats(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface, &formatCount, formats.data()); - - VkSurfaceFormatKHR surfaceFormat = formats[0]; - swapchainImageFormat = surfaceFormat.format; - - int width, height; - SDL_Vulkan_GetDrawableSize(window_, &width, &height); - swapchainExtent = {static_cast(width), static_cast(height)}; - - uint32_t imageCount = capabilities.minImageCount + 1; - if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) { - imageCount = capabilities.maxImageCount; - } - - VkSwapchainCreateInfoKHR createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.surface = surface; - createInfo.minImageCount = imageCount; - createInfo.imageFormat = surfaceFormat.format; - createInfo.imageColorSpace = surfaceFormat.colorSpace; - createInfo.imageExtent = swapchainExtent; - createInfo.imageArrayLayers = 1; - createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.preTransform = capabilities.currentTransform; - createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; - createInfo.clipped = VK_TRUE; - - if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapchain) != VK_SUCCESS) { - throw std::runtime_error("Failed to create swap chain!"); - } - - vkGetSwapchainImagesKHR(device, swapchain, &imageCount, nullptr); - swapchainImages.resize(imageCount); - vkGetSwapchainImagesKHR(device, swapchain, &imageCount, swapchainImages.data()); - } - - void createImageViews() { - swapchainImageViews.resize(swapchainImages.size()); - - for (size_t i = 0; i < swapchainImages.size(); i++) { - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = swapchainImages[i]; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = swapchainImageFormat; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - if (vkCreateImageView(device, &createInfo, nullptr, &swapchainImageViews[i]) != VK_SUCCESS) { - throw std::runtime_error("Failed to create image views!"); - } - } - } - - void createRenderPass() { - VkAttachmentDescription colorAttachment{}; - colorAttachment.format = swapchainImageFormat; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef{}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkSubpassDependency dependency{}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - VkRenderPassCreateInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassInfo.attachmentCount = 1; - renderPassInfo.pAttachments = &colorAttachment; - renderPassInfo.subpassCount = 1; - renderPassInfo.pSubpasses = &subpass; - renderPassInfo.dependencyCount = 1; - renderPassInfo.pDependencies = &dependency; - - if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) { - throw std::runtime_error("Failed to create render pass!"); - } - } - - void createFramebuffers() { - framebuffers.resize(swapchainImageViews.size()); - - for (size_t i = 0; i < swapchainImageViews.size(); i++) { - VkImageView attachments[] = { swapchainImageViews[i] }; - - VkFramebufferCreateInfo framebufferInfo{}; - framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - framebufferInfo.renderPass = renderPass; - framebufferInfo.attachmentCount = 1; - framebufferInfo.pAttachments = attachments; - framebufferInfo.width = swapchainExtent.width; - framebufferInfo.height = swapchainExtent.height; - framebufferInfo.layers = 1; - - if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &framebuffers[i]) != VK_SUCCESS) { - throw std::runtime_error("Failed to create framebuffer!"); - } - } - } - - void createCommandPool() { - VkCommandPoolCreateInfo poolInfo{}; - poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - poolInfo.queueFamilyIndex = graphicsQueueFamily; - - if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) { - throw std::runtime_error("Failed to create command pool!"); - } - } - - void createCommandBuffers() { - commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); - - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.commandPool = commandPool; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandBufferCount = static_cast(commandBuffers.size()); - - if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) { - throw std::runtime_error("Failed to allocate command buffers!"); - } - } - - void createSyncObjects() { - imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); - - VkSemaphoreCreateInfo semaphoreInfo{}; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - - VkFenceCreateInfo fenceInfo{}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS || - vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS || - vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) { - throw std::runtime_error("Failed to create synchronization objects!"); - } - } - } - - void createDescriptorPool() { - VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } - }; - - 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 = 1000 * IM_ARRAYSIZE(pool_sizes); - pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); - pool_info.pPoolSizes = pool_sizes; - - if (vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptorPool) != VK_SUCCESS) { - throw std::runtime_error("Failed to create descriptor pool!"); - } - } - - void initImGui() { - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - - // Setup Platform/Renderer backends - ImGui_ImplSDL2_InitForVulkan(window_); - ImGui_ImplVulkan_InitInfo init_info = {}; - init_info.Instance = instance_; - init_info.PhysicalDevice = physical_device_; - init_info.Device = device; - init_info.QueueFamily = graphicsQueueFamily; - init_info.Queue = graphicsQueue; - init_info.PipelineCache = VK_NULL_HANDLE; - init_info.DescriptorPool = descriptorPool; - init_info.RenderPass = renderPass; - init_info.Subpass = 0; - init_info.MinImageCount = MAX_FRAMES_IN_FLIGHT; - init_info.ImageCount = static_cast(swapchainImages.size()); - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; - init_info.Allocator = nullptr; - init_info.CheckVkResultFn = nullptr; - ImGui_ImplVulkan_Init(&init_info); - //ImGui_ImplVulkan_Init(&init_info, renderPass); - - // Upload Fonts - VkCommandBuffer command_buffer = beginSingleTimeCommands(); - ImGui_ImplVulkan_CreateFontsTexture(); - endSingleTimeCommands(command_buffer); - - //ImGui_ImplVulkan_DestroyFontUploadObjects(); - } - - VkCommandBuffer beginSingleTimeCommands() { - VkCommandBufferAllocateInfo allocInfo{}; - allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocInfo.commandPool = commandPool; - allocInfo.commandBufferCount = 1; - - VkCommandBuffer commandBuffer; - vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); - - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - - vkBeginCommandBuffer(commandBuffer, &beginInfo); - - return commandBuffer; - } - - void endSingleTimeCommands(VkCommandBuffer commandBuffer) { - vkEndCommandBuffer(commandBuffer); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - - vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); - vkQueueWaitIdle(graphicsQueue); - - vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); - } - - void mainLoop() { - SDL_Event event; - - while (!quit) { - while (SDL_PollEvent(&event)) { - ImGui_ImplSDL2_ProcessEvent(&event); - - if (event.type == SDL_QUIT) { - quit = true; - } - } - - drawFrame(); - } - - vkDeviceWaitIdle(device); - } - - void drawFrame() { - vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); - - uint32_t imageIndex; - VkResult result = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, - imageAvailableSemaphores[currentFrame], - VK_NULL_HANDLE, &imageIndex); - - switch (result) { - case VK_SUCCESS: - case VK_SUBOPTIMAL_KHR: - break; - case VK_ERROR_OUT_OF_DATE_KHR: - recreateSwapchain(); - // deliberate earlyexit - return; - default: - throw std::runtime_error("failed to acquire swapchain image!"); - break; - } - - vkResetFences(device, 1, &inFlightFences[currentFrame]); - - vkResetCommandBuffer(commandBuffers[currentFrame], 0); - recordCommandBuffer(commandBuffers[currentFrame], imageIndex); - - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - - VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; - VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; - - VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { - throw std::runtime_error("Failed to submit draw command buffer!"); - } - - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = signalSemaphores; - - VkSwapchainKHR swapChains[] = {swapchain}; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = swapChains; - - presentInfo.pImageIndices = &imageIndex; - - result = vkQueuePresentKHR(graphicsQueue, &presentInfo); - - if (framebuffer_resized_flag_) - result = VK_ERROR_OUT_OF_DATE_KHR; - - switch (result) { - case VK_SUCCESS: - break; - case VK_ERROR_OUT_OF_DATE_KHR: - case VK_SUBOPTIMAL_KHR: - framebuffer_resized_flag_ = false; - this->recreateSwapchain(); - break; - default: - throw std::runtime_error("failed to present swapchain image!"); - } - - currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; - } - - void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - - if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { - throw std::runtime_error("Failed to begin recording command buffer!"); - } - - VkRenderPassBeginInfo renderPassInfo{}; - renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassInfo.renderPass = renderPass; - renderPassInfo.framebuffer = framebuffers[imageIndex]; - renderPassInfo.renderArea.offset = {0, 0}; - renderPassInfo.renderArea.extent = swapchainExtent; - - VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; - renderPassInfo.clearValueCount = 1; - renderPassInfo.pClearValues = &clearColor; - - vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); - - // Start the Dear ImGui frame - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - ImGui::NewFrame(); - - // Create a simple ImGui window - ImGui::Begin("Hello, Vulkan + SDL2!"); - ImGui::Text("This is a minimal ImGui + Vulkan + SDL2 example!"); - static float f = 0.0f; - static int counter = 0; - ImGui::SliderFloat("float", &f, 0.0f, 1.0f); - if (ImGui::Button("Button")) - counter++; - ImGui::SameLine(); - ImGui::Text("counter = %d", counter); - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::End(); - - // Rendering - ImGui::Render(); - ImDrawData* draw_data = ImGui::GetDrawData(); - ImGui_ImplVulkan_RenderDrawData(draw_data, commandBuffer); - - vkCmdEndRenderPass(commandBuffer); - - if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { - throw std::runtime_error("Failed to record command buffer!"); - } - } - - void recreateSwapchain() { - // handle window minimization: wait until window has valid size - int width = 0; - int height = 0; - SDL_GetWindowSize(window_, &width, &height); - while (width == 0 || height == 0) { - SDL_GetWindowSize(window_, &width, &height); - SDL_WaitEvent(nullptr); - } - - // wait until device idle before cleaning up resources - vkDeviceWaitIdle(device); - - // cleanup old swapchain - this->cleanupFrameBuffers(); - this->cleanupImageViews(); - this->cleanupSwapchain(); - - // create new swapchain - this->createSwapchain(); - this->createImageViews(); - this->createFramebuffers(); - } - - void cleanupFrameBuffers() { - for (auto framebuffer : framebuffers) { - vkDestroyFramebuffer(device, framebuffer, nullptr); - } - framebuffers.clear(); - } - - void cleanupImageViews() { - for (auto imageView : swapchainImageViews) { - vkDestroyImageView(device, imageView, nullptr); - } - swapchainImageViews.clear(); - } - - void cleanupSwapchain() { - vkDestroySwapchainKHR(device, swapchain, nullptr); - } - - void cleanup() { - ImGui_ImplVulkan_Shutdown(); - ImGui_ImplSDL2_Shutdown(); - ImGui::DestroyContext(); - - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - vkDestroyFence(device, inFlightFences[i], nullptr); - } - - vkDestroyCommandPool(device, commandPool, nullptr); - - this->cleanupFrameBuffers(); - this->cleanupImageViews(); - this->cleanupSwapchain(); - - vkDestroyRenderPass(device, renderPass, nullptr); - vkDestroyDescriptorPool(device, descriptorPool, nullptr); - vkDestroyDevice(device, nullptr); - vkDestroySurfaceKHR(instance_, surface, nullptr); - vkDestroyInstance(instance_, nullptr); - this->instance_ = nullptr; - - SDL_DestroyWindow(window_); - this->window_ = nullptr; - - SDL_Quit(); - } - -private: - SDL_Window* window_ = nullptr; - - VkInstance instance_; - VkPhysicalDevice physical_device_; - VkDevice device; - VkQueue graphicsQueue; - VkSurfaceKHR surface; - VkSwapchainKHR swapchain; - VkFormat swapchainImageFormat; - VkExtent2D swapchainExtent; - std::vector swapchainImages; - std::vector swapchainImageViews; - VkRenderPass renderPass; - std::vector framebuffers; - VkCommandPool commandPool; - std::vector commandBuffers; - std::vector imageAvailableSemaphores; - std::vector renderFinishedSemaphores; - std::vector inFlightFences; - VkDescriptorPool descriptorPool; - - uint32_t currentFrame = 0; - const int MAX_FRAMES_IN_FLIGHT = 2; - uint32_t graphicsQueueFamily = 0; - /* true when window resize behavior detected, - * until swapchain in consistent state. - */ - bool framebuffer_resized_flag_ = false; - bool quit = false; -}; /*MinimalImGuiVulkan*/ +#include "VulkanApp.hpp" int main() { MinimalImGuiVulkan app;