xo-imgui: refactor: separate VulkanApp TU for ex4a
This commit is contained in:
parent
e3b1a69eb5
commit
adb4faf58e
4 changed files with 703 additions and 688 deletions
|
|
@ -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
|
||||
|
|
|
|||
14
xo-imgui/example/ex4a/VulkanApp.cpp
Normal file
14
xo-imgui/example/ex4a/VulkanApp.cpp
Normal file
|
|
@ -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 */
|
||||
687
xo-imgui/example/ex4a/VulkanApp.hpp
Normal file
687
xo-imgui/example/ex4a/VulkanApp.hpp
Normal file
|
|
@ -0,0 +1,687 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_vulkan.h>
|
||||
#include <imgui.h>
|
||||
#include <backends/imgui_impl_sdl2.h>
|
||||
#include <backends/imgui_impl_vulkan.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
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<const char*> 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<VkPhysicalDevice> 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<VkQueueFamilyProperties> 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<VkSurfaceFormatKHR> 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<uint32_t>(width), static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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<VkImage> swapchainImages;
|
||||
std::vector<VkImageView> swapchainImageViews;
|
||||
VkRenderPass renderPass;
|
||||
std::vector<VkFramebuffer> framebuffers;
|
||||
VkCommandPool commandPool;
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||
std::vector<VkFence> 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*/
|
||||
|
|
@ -4284,694 +4284,7 @@ int main(int, char **)
|
|||
/* imgui_ex4a.cpp */
|
||||
#endif //DEBUG
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#include <SDL.h>
|
||||
#include <SDL_vulkan.h>
|
||||
#include <imgui.h>
|
||||
#include <backends/imgui_impl_sdl2.h>
|
||||
#include <backends/imgui_impl_vulkan.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
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<const char*> 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<VkPhysicalDevice> 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<VkQueueFamilyProperties> 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<VkSurfaceFormatKHR> 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<uint32_t>(width), static_cast<uint32_t>(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<uint32_t>(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<uint32_t>(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<VkImage> swapchainImages;
|
||||
std::vector<VkImageView> swapchainImageViews;
|
||||
VkRenderPass renderPass;
|
||||
std::vector<VkFramebuffer> framebuffers;
|
||||
VkCommandPool commandPool;
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||
std::vector<VkFence> 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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue