662 lines
22 KiB
C++
662 lines
22 KiB
C++
/* file VulkanApp.cpp */
|
|
|
|
#include "VulkanApp.hpp"
|
|
#include <SDL_vulkan.h>
|
|
#include <imgui.h>
|
|
#include <backends/imgui_impl_sdl2.h>
|
|
#include <backends/imgui_impl_vulkan.h>
|
|
#include <cstdint>
|
|
|
|
constexpr std::size_t c_max_frames_in_flight = 2;
|
|
|
|
void
|
|
VulkanApp::run() {
|
|
this->init_window();
|
|
this->init_vulkan();
|
|
this->init_imgui();
|
|
this->main_loop();
|
|
this->cleanup();
|
|
}
|
|
|
|
void
|
|
VulkanApp::init_window() {
|
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
|
throw std::runtime_error("Failed to initialize SDL!");
|
|
}
|
|
|
|
window = SDL_CreateWindow(
|
|
"Xo ImGui Vulkan SDL2 Frame",
|
|
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
|
|
VulkanApp::init_vulkan()
|
|
{
|
|
this->create_instance();
|
|
this->create_surface();
|
|
this->pick_physical_device();
|
|
this->create_logical_device();
|
|
this->create_swap_chain();
|
|
this->create_image_views();
|
|
this->create_render_pass();
|
|
this->create_framebuffers();
|
|
this->create_command_pool();
|
|
this->create_command_buffers();
|
|
this->create_sync_objects();
|
|
this->create_descriptor_pool();
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_instance()
|
|
{
|
|
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!");
|
|
}
|
|
|
|
// Add portability extension for MoltenVK (macOS)
|
|
extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
|
|
|
createInfo.enabledExtensionCount = extensions.size();
|
|
createInfo.ppEnabledExtensionNames = extensions.data();
|
|
createInfo.enabledLayerCount = 0;
|
|
|
|
// CRITICAL: Enable portability enumeration flag for MoltenVK
|
|
createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
|
|
|
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
|
|
throw std::runtime_error("Failed to create instance!");
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_surface() {
|
|
if (!SDL_Vulkan_CreateSurface(window, instance, &(this->surface_))) {
|
|
throw std::runtime_error("Failed to create SDL Vulkan surface!");
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::pick_physical_device()
|
|
{
|
|
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) {
|
|
graphics_queue_family_ = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_logical_device()
|
|
{
|
|
VkDeviceQueueCreateInfo queueCreateInfo{};
|
|
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queueCreateInfo.queueFamilyIndex = graphics_queue_family_;
|
|
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_, graphics_queue_family_, 0, &graphics_queue_);
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_swap_chain()
|
|
{
|
|
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];
|
|
swapchain_image_format_ = surfaceFormat.format;
|
|
|
|
int width, height;
|
|
SDL_Vulkan_GetDrawableSize(window, &width, &height);
|
|
swapchain_extent_ =
|
|
{
|
|
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 = swapchain_extent_;
|
|
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
|
|
VulkanApp::create_image_views()
|
|
{
|
|
swapchain_image_views_.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 = swapchain_image_format_;
|
|
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,
|
|
&swapchain_image_views_[i]) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create image views!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_render_pass()
|
|
{
|
|
VkAttachmentDescription colorAttachment{};
|
|
colorAttachment.format = swapchain_image_format_;
|
|
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, &render_pass_) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create render pass!");
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_framebuffers()
|
|
{
|
|
framebuffers_.resize(swapchain_image_views_.size());
|
|
|
|
for (size_t i = 0; i < swapchain_image_views_.size(); i++) {
|
|
VkImageView attachments[] = { swapchain_image_views_[i] };
|
|
|
|
|
|
VkFramebufferCreateInfo framebufferInfo{};
|
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
framebufferInfo.renderPass = render_pass_;
|
|
framebufferInfo.attachmentCount = 1;
|
|
framebufferInfo.pAttachments = attachments;
|
|
framebufferInfo.width = swapchain_extent_.width;
|
|
framebufferInfo.height = swapchain_extent_.height;
|
|
framebufferInfo.layers = 1;
|
|
|
|
if (vkCreateFramebuffer
|
|
(device_, &framebufferInfo, nullptr,
|
|
&framebuffers_[i]) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create framebuffer!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_command_pool()
|
|
{
|
|
VkCommandPoolCreateInfo poolInfo{};
|
|
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
poolInfo.queueFamilyIndex = graphics_queue_family_;
|
|
|
|
if (vkCreateCommandPool (device_, &poolInfo,
|
|
nullptr, &command_pool_) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create command pool!");
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_command_buffers()
|
|
{
|
|
this->command_buffers_.resize(c_max_frames_in_flight);
|
|
|
|
VkCommandBufferAllocateInfo allocInfo{};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
allocInfo.commandPool = command_pool_;
|
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
allocInfo.commandBufferCount = static_cast<uint32_t>(command_buffers_.size());
|
|
|
|
if (vkAllocateCommandBuffers
|
|
(device_, &allocInfo, command_buffers_.data()) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to allocate command buffers!");
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_sync_objects()
|
|
{
|
|
this->image_available_semaphores_.resize(c_max_frames_in_flight);
|
|
this->render_finished_semaphores_.resize(c_max_frames_in_flight);
|
|
this->in_flight_fences_.resize(c_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 (std::size_t i = 0; i < c_max_frames_in_flight; i++) {
|
|
if ((vkCreateSemaphore(device_, &semaphoreInfo,
|
|
nullptr, &image_available_semaphores_[i]) != VK_SUCCESS)
|
|
|| (vkCreateSemaphore(device_, &semaphoreInfo,
|
|
nullptr, &render_finished_semaphores_[i]) != VK_SUCCESS)
|
|
|| (vkCreateFence(device_, &fenceInfo,
|
|
nullptr, &in_flight_fences_[i]) != VK_SUCCESS))
|
|
{
|
|
throw std::runtime_error("Failed to create synchronization objects!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::create_descriptor_pool() {
|
|
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, &descriptor_pool_) != VK_SUCCESS)
|
|
{
|
|
throw std::runtime_error("Failed to create descriptor pool!");
|
|
}
|
|
}
|
|
|
|
void
|
|
VulkanApp::init_imgui()
|
|
{
|
|
// 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 = graphics_queue_family_;
|
|
init_info.Queue = graphics_queue_;
|
|
init_info.PipelineCache = VK_NULL_HANDLE;
|
|
init_info.DescriptorPool = descriptor_pool_;
|
|
init_info.RenderPass = render_pass_;
|
|
init_info.Subpass = 0;
|
|
init_info.MinImageCount = c_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 = begin_single_time_commands();
|
|
ImGui_ImplVulkan_CreateFontsTexture();
|
|
this->end_single_time_commands(command_buffer);
|
|
|
|
//ImGui_ImplVulkan_DestroyFontUploadObjects();
|
|
} /*init_imgui*/
|
|
|
|
VkCommandBuffer
|
|
VulkanApp::begin_single_time_commands()
|
|
{
|
|
VkCommandBufferAllocateInfo allocInfo{};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
allocInfo.commandPool = command_pool_;
|
|
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
|
|
VulkanApp::end_single_time_commands(VkCommandBuffer commandBuffer)
|
|
{
|
|
vkEndCommandBuffer(commandBuffer);
|
|
|
|
VkSubmitInfo submitInfo{};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &commandBuffer;
|
|
|
|
vkQueueSubmit(graphics_queue_, 1, &submitInfo, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(graphics_queue_);
|
|
|
|
vkFreeCommandBuffers(device_, command_pool_, 1, &commandBuffer);
|
|
}
|
|
|
|
void
|
|
VulkanApp::main_loop()
|
|
{
|
|
SDL_Event event;
|
|
|
|
while (!quit_) {
|
|
while (SDL_PollEvent(&event)) {
|
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
|
|
|
if (event.type == SDL_QUIT) {
|
|
this->quit_ = true;
|
|
}
|
|
}
|
|
|
|
this->draw_frame();
|
|
}
|
|
|
|
vkDeviceWaitIdle(device_);
|
|
}
|
|
|
|
void
|
|
VulkanApp::draw_frame()
|
|
{
|
|
vkWaitForFences(device_, 1,
|
|
&in_flight_fences_[current_frame_], VK_TRUE, UINT64_MAX);
|
|
|
|
uint32_t imageIndex;
|
|
vkAcquireNextImageKHR(device_, swapchain_, UINT64_MAX,
|
|
image_available_semaphores_[current_frame_],
|
|
VK_NULL_HANDLE, &imageIndex);
|
|
|
|
vkResetFences(device_, 1, &in_flight_fences_[current_frame_]);
|
|
|
|
vkResetCommandBuffer(command_buffers_[current_frame_], 0);
|
|
this->record_command_buffer(command_buffers_[current_frame_], imageIndex);
|
|
|
|
VkSubmitInfo submitInfo{};
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
|
|
VkSemaphore waitSemaphores[] = {image_available_semaphores_[current_frame_]};
|
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
submitInfo.pWaitSemaphores = waitSemaphores;
|
|
submitInfo.pWaitDstStageMask = waitStages;
|
|
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &command_buffers_[current_frame_];
|
|
|
|
VkSemaphore signalSemaphores[]
|
|
= {render_finished_semaphores_[current_frame_]};
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
submitInfo.pSignalSemaphores = signalSemaphores;
|
|
|
|
if (vkQueueSubmit(graphics_queue_, 1,
|
|
&submitInfo,
|
|
in_flight_fences_[current_frame_]) != 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;
|
|
|
|
vkQueuePresentKHR(graphics_queue_, &presentInfo);
|
|
|
|
current_frame_ = (current_frame_ + 1) % c_max_frames_in_flight;
|
|
} /*draw_frame*/
|
|
|
|
void
|
|
VulkanApp::record_command_buffer(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 = render_pass_;
|
|
renderPassInfo.framebuffer = framebuffers_[imageIndex];
|
|
renderPassInfo.renderArea.offset = {0, 0};
|
|
renderPassInfo.renderArea.extent = swapchain_extent_;
|
|
|
|
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
|
|
VulkanApp::cleanup()
|
|
{
|
|
ImGui_ImplVulkan_Shutdown();
|
|
ImGui_ImplSDL2_Shutdown();
|
|
ImGui::DestroyContext();
|
|
|
|
for (size_t i = 0; i < c_max_frames_in_flight; ++i) {
|
|
vkDestroySemaphore(device_, render_finished_semaphores_[i], nullptr);
|
|
vkDestroySemaphore(device_, image_available_semaphores_[i], nullptr);
|
|
vkDestroyFence(device_, in_flight_fences_[i], nullptr);
|
|
}
|
|
|
|
vkDestroyCommandPool(device_, command_pool_, nullptr);
|
|
|
|
for (auto framebuffer : framebuffers_) {
|
|
vkDestroyFramebuffer(device_, framebuffer, nullptr);
|
|
}
|
|
|
|
vkDestroyRenderPass(device_, render_pass_, nullptr);
|
|
|
|
for (auto imageView : swapchain_image_views_) {
|
|
vkDestroyImageView(device_, imageView, nullptr);
|
|
}
|
|
|
|
vkDestroySwapchainKHR(device_, swapchain_, nullptr);
|
|
vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr);
|
|
vkDestroyDevice(device_, nullptr);
|
|
vkDestroySurfaceKHR(instance, surface_, nullptr);
|
|
vkDestroyInstance(instance, nullptr);
|
|
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
}
|
|
|
|
/* end VulkanApp.cpp */
|