289 lines
10 KiB
C++
289 lines
10 KiB
C++
/* VulkanApp.cpp */
|
|
|
|
#include "VulkanApp.hpp"
|
|
|
|
void
|
|
MinimalImGuiVulkan::run()
|
|
{
|
|
this->init_sdl_window();
|
|
this->init_vulkan();
|
|
this->init_imgui();
|
|
this->main_loop();
|
|
this->cleanup();
|
|
}
|
|
|
|
void
|
|
MinimalImGuiVulkan::init_sdl_window()
|
|
{
|
|
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,
|
|
1000, 800,
|
|
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE
|
|
);
|
|
|
|
if (!window_) {
|
|
throw std::runtime_error("Failed to create SDL window!");
|
|
}
|
|
}
|
|
|
|
void
|
|
MinimalImGuiVulkan::init_vulkan()
|
|
{
|
|
this->create_instance();
|
|
this->create_surface();
|
|
this->pick_physical_device();
|
|
this->create_logical_device();
|
|
this->create_swapchain();
|
|
this->create_image_views();
|
|
this->create_render_pass(); // must come before createFrameBuffers
|
|
this->create_framebuffers();
|
|
this->create_command_pool();
|
|
this->create_command_buffers();
|
|
this->create_sync_objects();
|
|
this->create_descriptor_pool();
|
|
}
|
|
|
|
void
|
|
MinimalImGuiVulkan::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!");
|
|
}
|
|
|
|
#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
|
|
MinimalImGuiVulkan::create_surface()
|
|
{
|
|
if (!SDL_Vulkan_CreateSurface(window_, instance_, &(this->surface_))) {
|
|
throw std::runtime_error("Failed to create SDL Vulkan surface!");
|
|
}
|
|
}
|
|
|
|
void
|
|
MinimalImGuiVulkan::pick_physical_device()
|
|
{
|
|
uint32_t n_device = 0;
|
|
vkEnumeratePhysicalDevices(instance_, &n_device, nullptr);
|
|
|
|
if (n_device == 0) {
|
|
throw std::runtime_error("Failed to find GPUs with Vulkan support!");
|
|
}
|
|
|
|
std::vector<VkPhysicalDevice> devices(n_device);
|
|
vkEnumeratePhysicalDevices(instance_, &n_device, devices.data());
|
|
|
|
this->physical_device_ = devices[0]; // Just pick the first one for simplicity
|
|
|
|
// Find graphics queue family
|
|
uint32_t n_queue_family = 0;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &n_queue_family, nullptr);
|
|
std::vector<VkQueueFamilyProperties> queue_families(n_queue_family);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &n_queue_family, queue_families.data());
|
|
|
|
for (uint32_t i = 0; i < queue_families.size(); i++) {
|
|
if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
|
VkBool32 present_support = false;
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device_, i, surface_, &present_support);
|
|
if (present_support) {
|
|
this->graphics_queue_family_ = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MinimalImGuiVulkan::create_logical_device()
|
|
{
|
|
VkDeviceQueueCreateInfo queue_create_info{};
|
|
queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queue_create_info.queueFamilyIndex = graphics_queue_family_;
|
|
queue_create_info.queueCount = 1;
|
|
float queuePriority = 1.0f;
|
|
queue_create_info.pQueuePriorities = &queuePriority;
|
|
|
|
VkPhysicalDeviceFeatures deviceFeatures{};
|
|
|
|
VkDeviceCreateInfo create_info{};
|
|
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
create_info.pQueueCreateInfos = &queue_create_info;
|
|
create_info.queueCreateInfoCount = 1;
|
|
create_info.pEnabledFeatures = &deviceFeatures;
|
|
|
|
const char* deviceExtensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
|
|
create_info.enabledExtensionCount = 1;
|
|
create_info.ppEnabledExtensionNames = deviceExtensions;
|
|
|
|
if (vkCreateDevice(physical_device_, &create_info, nullptr, &(this->device_)) != VK_SUCCESS) {
|
|
throw std::runtime_error("Failed to create logical device!");
|
|
}
|
|
|
|
vkGetDeviceQueue(device_, graphics_queue_family_, 0, &graphics_queue_);
|
|
}
|
|
|
|
void
|
|
MinimalImGuiVulkan::create_swapchain()
|
|
{
|
|
VkSurfaceCapabilitiesKHR capabilities;
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device_, surface_, &capabilities);
|
|
|
|
uint32_t n_format;
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface_, &n_format, nullptr);
|
|
std::vector<VkSurfaceFormatKHR> formats(n_format);
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface_, &n_format, formats.data());
|
|
|
|
VkSurfaceFormatKHR surface_format = formats[0];
|
|
this->swapchain_image_format_ = surface_format.format;
|
|
|
|
int width, height;
|
|
SDL_Vulkan_GetDrawableSize(window_, &width, &height);
|
|
this->swapchain_extent_ = {
|
|
static_cast<uint32_t>(width),
|
|
static_cast<uint32_t>(height)
|
|
};
|
|
|
|
uint32_t n_image = capabilities.minImageCount + 1;
|
|
if (capabilities.maxImageCount > 0 && n_image > capabilities.maxImageCount) {
|
|
n_image = capabilities.maxImageCount;
|
|
}
|
|
|
|
VkSwapchainCreateInfoKHR create_info{};
|
|
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
create_info.surface = surface_;
|
|
create_info.minImageCount = n_image;
|
|
create_info.imageFormat = surface_format.format;
|
|
create_info.imageColorSpace = surface_format.colorSpace;
|
|
create_info.imageExtent = swapchain_extent_;
|
|
create_info.imageArrayLayers = 1;
|
|
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
create_info.preTransform = capabilities.currentTransform;
|
|
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
|
create_info.clipped = VK_TRUE;
|
|
|
|
if (vkCreateSwapchainKHR(device_, &create_info, nullptr, &(this->swapchain_)) != VK_SUCCESS) {
|
|
throw std::runtime_error("Failed to create swap chain!");
|
|
}
|
|
|
|
vkGetSwapchainImagesKHR(device_, swapchain_, &n_image, nullptr);
|
|
this->swapchain_images_.resize(n_image);
|
|
vkGetSwapchainImagesKHR(device_, swapchain_, &n_image, this->swapchain_images_.data());
|
|
}
|
|
|
|
void
|
|
MinimalImGuiVulkan::create_image_views()
|
|
{
|
|
swapchain_image_views_.resize(swapchain_images_.size());
|
|
|
|
for (size_t i = 0; i < swapchain_images_.size(); i++) {
|
|
VkImageViewCreateInfo create_info{};
|
|
create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
create_info.image = swapchain_images_[i];
|
|
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
create_info.format = swapchain_image_format_;
|
|
create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
create_info.subresourceRange.baseMipLevel = 0;
|
|
create_info.subresourceRange.levelCount = 1;
|
|
create_info.subresourceRange.baseArrayLayer = 0;
|
|
create_info.subresourceRange.layerCount = 1;
|
|
|
|
if (vkCreateImageView(device_, &create_info, nullptr, &swapchain_image_views_[i]) != VK_SUCCESS) {
|
|
throw std::runtime_error("Failed to create image views!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MinimalImGuiVulkan::create_render_pass()
|
|
{
|
|
VkAttachmentDescription color_attachment{};
|
|
color_attachment.format = swapchain_image_format_;
|
|
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
|
|
VkAttachmentReference color_attachment_ref{};
|
|
color_attachment_ref.attachment = 0;
|
|
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
VkSubpassDescription subpass{};
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.colorAttachmentCount = 1;
|
|
subpass.pColorAttachments = &color_attachment_ref;
|
|
|
|
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 render_pass_info{};
|
|
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
render_pass_info.attachmentCount = 1;
|
|
render_pass_info.pAttachments = &color_attachment;
|
|
render_pass_info.subpassCount = 1;
|
|
render_pass_info.pSubpasses = &subpass;
|
|
render_pass_info.dependencyCount = 1;
|
|
render_pass_info.pDependencies = &dependency;
|
|
|
|
if (vkCreateRenderPass(device_, &render_pass_info, nullptr, &(this->render_pass_)) != VK_SUCCESS) {
|
|
throw std::runtime_error("Failed to create render pass!");
|
|
}
|
|
}
|
|
|
|
/* end VulkanApp.cpp */
|