diff --git a/xo-imgui/example/ex4/imgui_ex4.cpp b/xo-imgui/example/ex4/imgui_ex4.cpp index 3aef0fa9..bb2ad9a9 100644 --- a/xo-imgui/example/ex4/imgui_ex4.cpp +++ b/xo-imgui/example/ex4/imgui_ex4.cpp @@ -1,9 +1,6 @@ /* imgui_ex4.cpp */ #include "xo/imgui/VulkanApp.hpp" -#ifdef TEMPORARILY_REMOVE -#include "xo/imgui/ImRect.hpp" -#endif #include "AppState.hpp" #include "DrawState.hpp" #include @@ -87,9 +84,7 @@ namespace { { scope log(XO_DEBUG(false)); -#ifdef TEMPORARILY_REMOVE app_duty_cycle_top(p_app_state, p_draw_state); -#endif log && log(xtag("imgui_cx", (void*)ImGui::GetCurrentContext())); @@ -98,7 +93,6 @@ namespace { ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); -#ifdef TEMPORARILY_REMOVE log && log("after NewFrame", xtag("imgui_cx", (void*)ImGui::GetCurrentContext())); ImGuiIO & io = ImGui::GetIO(); (void)io; @@ -113,12 +107,10 @@ namespace { | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoDecoration); ImGui::End(); # endif -#endif // 1. create a simple ImGui window ImGui::Begin("Hello, Vulkan + SDL2!"); ImGui::Text("This is a minimal ImGui + Vulkan + SDL2 example!"); -#ifdef TEMPORARILY_REMOVE ImGui::Text("appl average %.3f ms/frame (%.1f fps)", 1000.0f / io.Framerate, io.Framerate); @@ -212,15 +204,12 @@ namespace { p_app_state->copy_detail_tenured_dest_size_ = 0; } } -#endif ImGui::End(); -#ifdef TEMPORARILY_REMOVE // 2. big demo window if (p_draw_state->show_demo_window_) ImGui::ShowDemoWindow(&p_draw_state->show_demo_window_); -#endif // Rendering ImGui::Render(); @@ -296,13 +285,10 @@ int main() { = make_imgui_draw_frame(&app_state, &draw_state, &f, &counter); VulkanApp vk_app(draw_fn); - /* establishes imgui context */ vk_app.setup(app_imgui_load_fonts); -#ifdef NOT_YET app_state.gc_->add_gc_copy_callback (draw_state.make_gc_copy_animation(&app_state)); -#endif try { vk_app.main_loop(); diff --git a/xo-imgui/example/ex4a/VulkanApp.cpp b/xo-imgui/example/ex4a/VulkanApp.cpp index bb2f32b2..4779f038 100644 --- a/xo-imgui/example/ex4a/VulkanApp.cpp +++ b/xo-imgui/example/ex4a/VulkanApp.cpp @@ -41,10 +41,7 @@ MinimalImGuiVulkan::init_vulkan() 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_xswapchain(true /*create_render_pass_flag*/); this->create_command_pool(); this->create_command_buffers(); this->create_sync_objects(); @@ -503,7 +500,7 @@ MinimalImGuiVulkan::draw_frame() case VK_SUBOPTIMAL_KHR: break; case VK_ERROR_OUT_OF_DATE_KHR: - recreate_swapchain(); + this->recreate_xswapchain(); // deliberate earlyexit return; default: @@ -559,7 +556,7 @@ MinimalImGuiVulkan::draw_frame() case VK_ERROR_OUT_OF_DATE_KHR: case VK_SUBOPTIMAL_KHR: framebuffer_resized_flag_ = false; - this->recreate_swapchain(); + this->recreate_xswapchain(); break; default: throw std::runtime_error("failed to present swapchain image!"); @@ -624,7 +621,25 @@ MinimalImGuiVulkan::record_command_buffer(VkCommandBuffer cmdbuf, uint32_t image } void -MinimalImGuiVulkan::recreate_swapchain() +MinimalImGuiVulkan::create_xswapchain(bool create_render_pass_flag) +{ + this->create_swapchain(); + this->create_image_views(); + if(create_render_pass_flag) + this->create_render_pass(); + this->create_framebuffers(); +} + +void +MinimalImGuiVulkan::cleanup_xswapchain() +{ + this->cleanup_framebuffers(); + this->cleanup_image_views(); + this->cleanup_swapchain(); +} + +void +MinimalImGuiVulkan::recreate_xswapchain() { // handle window minimization: wait until window has valid size this->wait_not_minimized(); @@ -632,15 +647,11 @@ MinimalImGuiVulkan::recreate_swapchain() // wait until device idle before cleaning up resources vkDeviceWaitIdle(device_); - // cleanup old swapchain - this->cleanup_framebuffers(); - this->cleanup_image_views(); - this->cleanup_swapchain(); + // cleanup old xswapchain + this->cleanup_xswapchain(); // create new swapchain - this->create_swapchain(); - this->create_image_views(); - this->create_framebuffers(); + this->create_xswapchain(false); } void @@ -694,9 +705,7 @@ MinimalImGuiVulkan::cleanup() vkDestroyCommandPool(device_, command_pool_, nullptr); - this->cleanup_framebuffers(); - this->cleanup_image_views(); - this->cleanup_swapchain(); + this->cleanup_xswapchain(); vkDestroyRenderPass(device_, render_pass_, nullptr); vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr); diff --git a/xo-imgui/example/ex4a/VulkanApp.hpp b/xo-imgui/example/ex4a/VulkanApp.hpp index 3d7971d0..f9a6931b 100644 --- a/xo-imgui/example/ex4a/VulkanApp.hpp +++ b/xo-imgui/example/ex4a/VulkanApp.hpp @@ -47,6 +47,12 @@ private: */ void create_logical_device(); + /* populates @ref swapchain_, @ref swapchain_images_, + * @ref swapchain_image_views_, @ref framebuffers_. + * Also $ref render_pass_ iff @p create_render_pass_flag + */ + void create_xswapchain(bool create_render_pass_flag); + /* * populates @ref swapchain_, @ref swapchain_images_ */ @@ -123,11 +129,14 @@ private: /* Teardown + create swapchain (swapchain + framebuffers + image views). * Need this after window size changes */ - void recreate_swapchain(); + void recreate_xswapchain(); /* wait until non-minimized window */ void wait_not_minimized(); + /* orderly disposal of swapchin + image_views + framebuffers */ + void cleanup_xswapchain(); + /* orderly disposal of @ref framebuffers_ */ void cleanup_framebuffers(); diff --git a/xo-imgui/include/xo/imgui/VulkanApp.hpp b/xo-imgui/include/xo/imgui/VulkanApp.hpp index e8dfe2f0..8f47edd7 100644 --- a/xo-imgui/include/xo/imgui/VulkanApp.hpp +++ b/xo-imgui/include/xo/imgui/VulkanApp.hpp @@ -1,115 +1,221 @@ -/* file VulkanApp.hpp */ - #pragma once -#include -#include #include -//#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include class VulkanApp { public: using ImguiDrawFn = std::function; - using time_point = std::chrono::steady_clock::time_point; public: + /* create single-window vulkan application; + * use @p fn to draw each frame + */ VulkanApp(ImguiDrawFn fn); -#ifdef NOPE - /** set imgui draw function **/ - void assign_imgui_draw_frame(ImguiDrawFn fn); -#endif + /* run application; borrows calling thread for event loop, + * until application exit. + * Invoke load_fonts once imgui context established + */ + void run(std::function load_fonts); - /** frames per second since inception **/ - float lifetime_fps() const; - - /** equivalent to sequence setup(), main_loop(), cleanup() **/ - void run(); - - /** setup before main loop. idempotent **/ + /* application setup (sdl -> vulkan -> imgui) + * Invoke load_fonts once imgui context established. + */ void setup(std::function load_fonts); + + /* borrow calling thread, run until application end + * Require: app state initialized. see init_sdl_window(), init_vulkan(), init_imgui() + */ void main_loop(); - /** cleanup before shutdown. idempotent **/ + + /* orderly shutdown + */ void cleanup(); private: - void init_window(); + /* create SDL window for application. + * populates @ref window_ + */ + void init_sdl_window(); + + /* setup vulkan state. swapchain, command buffers etc */ void init_vulkan(); + + /* create vulkan instance. + * populates @ref instance_ + */ void create_instance(); + + /* create vulkan surface. + * populates @ref surface_ + */ void create_surface(); + + /* choose physical device (1:1 with graphics card, presumably) + * populates @ref physical_device_, @ref graphics_queue_family_ + */ void pick_physical_device(); + + /* + * require: pick_physical_device() has run successfully + * populates @ref device_, @ref graphics_queue_ + */ void create_logical_device(); + + /* populates @ref swapchain_, @ref swapchain_images_, + * @ref swapchain_image_views_, @ref framebuffers_. + * Also $ref render_pass_ iff @p create_render_pass_flag + */ + void create_xswapchain(bool create_render_pass_flag); + + /* + * populates @ref swapchain_, @ref swapchain_images_ + */ void create_swapchain(); + + /* + * populate @ref swapchain_image_views_ + */ void create_image_views(); + + /* + * populate @ref render_pass_ + */ void create_render_pass(); + + /* + * populate @ref framebuffers_ + */ void create_framebuffers(); + + /* + * populate @ref command_pool_ + */ void create_command_pool(); + + /* + * populate @ref command_buffers_ + */ void create_command_buffers(); + + /* + * populate + * @ref image_available_semaphores_, + * @ref render_finished_semaphores_, + * @ref inflight_fences_ + */ void create_sync_objects(); + + /* + * populate @ref descriptor_pool_ + */ void create_descriptor_pool(); - void cleanup_framebuffers(); - void cleanup_render_pass(); - void cleanup_image_views(); - void cleanup_swapchain(); - - void cleanup_swapchain_deps(); - void recreate_swapchain_deps(); - + /* Setup imgui "framework". + * Invoke @p load_fonts with ImGuiContext + */ void init_imgui(std::function load_fonts); + + /* Allocate buffer for 'single-time' commands (to be run once?). + * Pair with call to end_single_time_commands() + */ VkCommandBuffer begin_single_time_commands(); + + /* complete command buffer begun with begin_single_time_commands(); + * also submit, wait for completion + cleanup + */ void end_single_time_commands(VkCommandBuffer commandBuffer); - void record_command_buffer(VkCommandBuffer commandBuffer, - uint32_t imageIndex); - /** TODO: replace with some generic mechanism **/ + + /* Accumulate drawing command for next frame. + * Reserves and modifies the @ref current_frame_ element of + * @ref command_buffers_ + * @ref image_available_semaphores_ + * @ref inflight_fences_ + * Advances current_frame_ so that it refers to available resources + */ void draw_frame(); -private: - bool setup_done_ = false; - bool cleanup_done_ = false; + /* record draw instructions into cmdbuf, framebuffers_[image_ix] */ + void record_command_buffer(VkCommandBuffer commandBuffer, uint32_t image_ix); + /* Teardown + create swapchain (swapchain + framebuffers + image views). + * Need this after window size changes + */ + void recreate_xswapchain(); + + /* wait until non-minimized window */ + void wait_not_minimized(); + + /* orderly disposal of swapchin + image_views + framebuffers */ + void cleanup_xswapchain(); + + /* orderly disposal of @ref framebuffers_ */ + void cleanup_framebuffers(); + + /* orderly disposal of @ref swapchain_image_views_ */ + void cleanup_image_views(); + + /* orderly disposal of @ref swapchain_ */ + void cleanup_swapchain(); + +private: SDL_Window* window_ = nullptr; ImGuiContext* imgui_cx_ = nullptr; - VkInstance instance; + + VkInstance instance_; + + /* abstraction for presentation area (?) */ + VkSurfaceKHR surface_; + + /* physical device (graphics card) */ VkPhysicalDevice physical_device_; + uint32_t graphics_queue_family_ = 0; + + /* logical device (graphics card, abstract api (?)) */ VkDevice device_; VkQueue graphics_queue_; - VkSurfaceKHR surface_; - VkSwapchainKHR swapchain_; + + /* drawing state, dependent on window size */ VkFormat swapchain_image_format_; VkExtent2D swapchain_extent_; - std::vector swapchainImages; + VkSwapchainKHR swapchain_; + std::vector swapchain_images_; + std::vector swapchain_image_views_; + VkRenderPass render_pass_; + std::vector framebuffers_; + VkCommandPool command_pool_; + std::vector command_buffers_; - /** one per frame (up to MAX_FRAMES_IN_FLIGHT) **/ + std::vector image_available_semaphores_; - /** one per frame (up to MAX_FRAMES_IN_FLIGHT) **/ std::vector render_finished_semaphores_; - std::vector in_flight_fences_; + std::vector inflight_fences_; + VkDescriptorPool descriptor_pool_; - /** frame counter, monotonic **/ - uint32_t n_frame_ = 0; - /** VulkanApp start time. Captured in 1st render loop, see @ref record_command_buffer **/ - time_point start_tm_; - /** time asof most recent render loop **/ - time_point now_tm_; - /** time of last console report of fps **/ - time_point last_log_fps_tm_; - - /** image index of current frame **/ uint32_t current_frame_ = 0; - uint32_t graphics_queue_family_ = 0; - bool quit_ = false; + const size_t c_max_frames_in_flight = 2; + /* true iff @ref setup has been called */ + bool setup_flag_ = false; + /* true when window resize behavior detected, + * until swapchain in consistent state. + */ + bool framebuffer_resized_flag_ = false; + bool quit_flag_ = false; /** draw imgui **/ const ImguiDrawFn imgui_draw_frame_; -}; - -/* end VulkanApp.hpp */ +}; /*VulkanApp*/ diff --git a/xo-imgui/src/imgui/VulkanApp.cpp b/xo-imgui/src/imgui/VulkanApp.cpp index f33d45df..94db4884 100644 --- a/xo-imgui/src/imgui/VulkanApp.cpp +++ b/xo-imgui/src/imgui/VulkanApp.cpp @@ -1,74 +1,47 @@ -/* file VulkanApp.cpp */ +/* VulkanApp.cpp */ #include "VulkanApp.hpp" -#include "xo/indentlog/scope.hpp" -#include -#include -#include -#include -#include -#ifdef __linux__ -# include -#endif - -using xo::scope; -using xo::xtag; - -constexpr std::size_t c_max_frames_in_flight = 2; VulkanApp::VulkanApp(ImguiDrawFn draw_fn) - : imgui_draw_frame_{ - draw_fn - } + : imgui_draw_frame_{draw_fn} +{} + +void +VulkanApp::setup(std::function load_fonts) { -} + if (!setup_flag_) { + this->setup_flag_ = true; -float -VulkanApp::lifetime_fps() const { - if (n_frame_ > 0) { - float elapsed_sec = 1.0e-3f * std::chrono::duration_cast(now_tm_ - start_tm_).count(); - - return n_frame_ / elapsed_sec; - } else { - return 0.0; - } -} - -void -VulkanApp::setup(std::function load_fonts) { - if (!setup_done_) { - this->init_window(); + this->init_sdl_window(); this->init_vulkan(); - this->init_imgui(load_fonts); - this->setup_done_ = true; + this->init_imgui(std::move(load_fonts)); } } void -VulkanApp::run() { - this->setup(nullptr); +VulkanApp::run(std::function load_fonts) +{ + this->setup(std::move(load_fonts)); + this->main_loop(); + this->cleanup(); } void -VulkanApp::init_window() { -#ifdef __linux__ - // on linux (at least for wsl2) force x11 with xlib instead of xcb - // alternatively could set env var SDL_VIDEODRIVER - SDL_SetHint(SDL_HINT_VIDEODRIVER, "x11"); -#endif - +VulkanApp::init_sdl_window() +{ if (SDL_Init(SDL_INIT_VIDEO) != 0) { throw std::runtime_error("Failed to initialize SDL!"); } - this->window_ = SDL_CreateWindow("Xo ImGui Vulkan SDL2 Frame", - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - 600, 600, - //1250, 1200, - SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE); + 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!"); @@ -82,10 +55,7 @@ VulkanApp::init_vulkan() this->create_surface(); this->pick_physical_device(); this->create_logical_device(); - this->create_swapchain(); - this->create_image_views(); - this->create_render_pass(); - this->create_framebuffers(); + this->create_xswapchain(true /*create_render_pass_flag*/); this->create_command_pool(); this->create_command_buffers(); this->create_sync_objects(); @@ -95,8 +65,6 @@ VulkanApp::init_vulkan() void VulkanApp::create_instance() { - scope log(XO_DEBUG(true)); - VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = "ImGui Vulkan App"; @@ -119,42 +87,31 @@ VulkanApp::create_instance() throw std::runtime_error("Failed to get SDL Vulkan extensions!"); } -#ifdef __APPLE__ +#ifdef __apple__ // Add portability extension for MoltenVK (macOS) extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); - // claude.ai says: - // extensions.push_back("VK_KHR_get_physical_device_properties2") #endif -#ifdef __linux__ - extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); - // native wayland drivers. or use VK_KHR_XLIB_SURFACE_EXTENSION_NAME here - //extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); - //extensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); -#endif - - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - 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 - log && log(xtag("vkCreateInstance.layers", createInfo.enabledLayerCount)); - for (uint32_t i = 0; i < createInfo.enabledLayerCount; ++i) { - log && log(xtag("i", i), xtag("layer[i]", createInfo.ppEnabledLayerNames[i])); - } - - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { + 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 -VulkanApp::create_surface() { - if (!SDL_Vulkan_CreateSurface(window_, instance, &(this->surface_))) { +VulkanApp::create_surface() +{ + if (!SDL_Vulkan_CreateSurface(window_, instance_, &(this->surface_))) { throw std::runtime_error("Failed to create SDL Vulkan surface!"); } } @@ -162,33 +119,30 @@ VulkanApp::create_surface() { void VulkanApp::pick_physical_device() { - uint32_t deviceCount = 0; - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + uint32_t n_device = 0; + vkEnumeratePhysicalDevices(instance_, &n_device, nullptr); - if (deviceCount == 0) { + if (n_device == 0) { throw std::runtime_error("Failed to find GPUs with Vulkan support!"); } - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + std::vector 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 queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &queueFamilyCount, nullptr); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties - (physical_device_, &queueFamilyCount, queueFamilies.data()); + uint32_t n_queue_family = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &n_queue_family, nullptr); + std::vector queue_families(n_queue_family); + vkGetPhysicalDeviceQueueFamilyProperties(physical_device_, &n_queue_family, queue_families.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; + 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; } } @@ -198,164 +152,104 @@ VulkanApp::pick_physical_device() void VulkanApp::create_logical_device() { - VkDeviceQueueCreateInfo queueCreateInfo{}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = graphics_queue_family_; - queueCreateInfo.queueCount = 1; + 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; - queueCreateInfo.pQueuePriorities = &queuePriority; + queue_create_info.pQueuePriorities = &queuePriority; VkPhysicalDeviceFeatures deviceFeatures{}; - VkDeviceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.pQueueCreateInfos = &queueCreateInfo; - createInfo.queueCreateInfoCount = 1; - createInfo.pEnabledFeatures = &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 }; - createInfo.enabledExtensionCount = 1; - createInfo.ppEnabledExtensionNames = deviceExtensions; + create_info.enabledExtensionCount = 1; + create_info.ppEnabledExtensionNames = deviceExtensions; - if (vkCreateDevice(physical_device_, &createInfo, nullptr, &device_) != VK_SUCCESS) { + 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_); } -const char * -present_mode_descr(VkPresentModeKHR m) { - switch(m) { - case VK_PRESENT_MODE_IMMEDIATE_KHR: return "immediate"; - case VK_PRESENT_MODE_MAILBOX_KHR: return "mailbox"; - case VK_PRESENT_MODE_FIFO_KHR: return "fifo"; - case VK_PRESENT_MODE_FIFO_RELAXED_KHR: return "fifo-relaxed"; - case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR: return "shared-demand-refresh"; - case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR: return "shared-continuous-refresh"; - case VK_PRESENT_MODE_FIFO_LATEST_READY_EXT: return "fifo-latest-ready"; - case VK_PRESENT_MODE_MAX_ENUM_KHR: break; - } - - return "?present-mode"; -} - void VulkanApp::create_swapchain() { - scope log(XO_DEBUG(true)); - VkSurfaceCapabilitiesKHR capabilities; - vkGetPhysicalDeviceSurfaceCapabilitiesKHR - (physical_device_, surface_, &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()); + uint32_t n_format; + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface_, &n_format, nullptr); + std::vector formats(n_format); + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device_, surface_, &n_format, formats.data()); - VkSurfaceFormatKHR surfaceFormat = formats[0]; - swapchain_image_format_ = surfaceFormat.format; + VkSurfaceFormatKHR surface_format = formats[0]; + this->swapchain_image_format_ = surface_format.format; int width, height; SDL_Vulkan_GetDrawableSize(window_, &width, &height); - swapchain_extent_ = - { - static_cast(width), - static_cast(height) - }; + this->swapchain_extent_ = { + static_cast(width), + static_cast(height) + }; - uint32_t imageCount = capabilities.minImageCount + 1; - if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) { - imageCount = capabilities.maxImageCount; + uint32_t n_image = capabilities.minImageCount + 1; + if (capabilities.maxImageCount > 0 && n_image > capabilities.maxImageCount) { + n_image = capabilities.maxImageCount; } - VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; - { - uint32_t n_avail_mode = 0; - vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device_, surface_, &n_avail_mode, nullptr); + 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; - std::vector avail_mode_v(n_avail_mode); - vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device_, surface_, &n_avail_mode, avail_mode_v.data()); - - if (log) { - log(xtag("n_avail_present_mode", n_avail_mode)); - for (uint32_t i = 0; i < n_avail_mode; ++i) - log(xtag("i", i), xtag("avail_mode_v[i]", present_mode_descr(avail_mode_v[i]))); - } - - std::vector prefer_mode_v = { - VK_PRESENT_MODE_IMMEDIATE_KHR, - VK_PRESENT_MODE_MAILBOX_KHR, - VK_PRESENT_MODE_FIFO_KHR // forces vsync, v.slow on wsl - }; - - bool found_flag = false; - for (uint32_t i = 0; i < prefer_mode_v.size() && !found_flag; ++i) { - for (uint32_t j = 0; j < avail_mode_v.size() && !found_flag; ++j) { - if (prefer_mode_v[i] == avail_mode_v[j]) { - present_mode = prefer_mode_v[i]; - found_flag = true; - - } - } - } - - log && log(xtag("present_mode", present_mode_descr(present_mode))); - } - - 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 = present_mode; - createInfo.clipped = VK_TRUE; - - if (vkCreateSwapchainKHR - (device_, &createInfo, nullptr, &swapchain_) != VK_SUCCESS) - { + if (vkCreateSwapchainKHR(device_, &create_info, nullptr, &(this->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()); + vkGetSwapchainImagesKHR(device_, swapchain_, &n_image, nullptr); + this->swapchain_images_.resize(n_image); + vkGetSwapchainImagesKHR(device_, swapchain_, &n_image, this->swapchain_images_.data()); } void VulkanApp::create_image_views() { - swapchain_image_views_.resize(swapchainImages.size()); + swapchain_image_views_.resize(swapchain_images_.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; + 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_, &createInfo, nullptr, - &swapchain_image_views_[i]) != VK_SUCCESS) - { + if (vkCreateImageView(device_, &create_info, nullptr, &swapchain_image_views_[i]) != VK_SUCCESS) { throw std::runtime_error("Failed to create image views!"); } } @@ -364,24 +258,24 @@ VulkanApp::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; + 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 colorAttachmentRef{}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + 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 = &colorAttachmentRef; + subpass.pColorAttachments = &color_attachment_ref; VkSubpassDependency dependency{}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; @@ -391,18 +285,16 @@ VulkanApp::create_render_pass() 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; + 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_, &renderPassInfo, nullptr, &render_pass_) != VK_SUCCESS) - { + if (vkCreateRenderPass(device_, &render_pass_info, nullptr, &(this->render_pass_)) != VK_SUCCESS) { throw std::runtime_error("Failed to create render pass!"); } } @@ -415,20 +307,16 @@ VulkanApp::create_framebuffers() for (size_t i = 0; i < swapchain_image_views_.size(); i++) { VkImageView attachments[] = { swapchain_image_views_[i] }; + VkFramebufferCreateInfo framebuffer_info{}; + framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_info.renderPass = render_pass_; + framebuffer_info.attachmentCount = 1; + framebuffer_info.pAttachments = attachments; + framebuffer_info.width = swapchain_extent_.width; + framebuffer_info.height = swapchain_extent_.height; + framebuffer_info.layers = 1; - 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) - { + if (vkCreateFramebuffer(device_, &framebuffer_info, nullptr, &(this->framebuffers_[i])) != VK_SUCCESS) { throw std::runtime_error("Failed to create framebuffer!"); } } @@ -442,9 +330,7 @@ VulkanApp::create_command_pool() poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; poolInfo.queueFamilyIndex = graphics_queue_family_; - if (vkCreateCommandPool (device_, &poolInfo, - nullptr, &command_pool_) != VK_SUCCESS) - { + if (vkCreateCommandPool(device_, &poolInfo, nullptr, &(this->command_pool_)) != VK_SUCCESS) { throw std::runtime_error("Failed to create command pool!"); } } @@ -452,17 +338,15 @@ VulkanApp::create_command_pool() void VulkanApp::create_command_buffers() { - this->command_buffers_.resize(c_max_frames_in_flight); + 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(command_buffers_.size()); + VkCommandBufferAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = command_pool_; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = static_cast(command_buffers_.size()); - if (vkAllocateCommandBuffers - (device_, &allocInfo, command_buffers_.data()) != VK_SUCCESS) - { + if (vkAllocateCommandBuffers(device_, &alloc_info, command_buffers_.data()) != VK_SUCCESS) { throw std::runtime_error("Failed to allocate command buffers!"); } } @@ -472,30 +356,27 @@ 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); + this->inflight_fences_.resize(c_max_frames_in_flight); - VkSemaphoreCreateInfo semaphoreInfo{}; - semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + VkSemaphoreCreateInfo semaphore_info{}; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - VkFenceCreateInfo fenceInfo{}; - fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + VkFenceCreateInfo fence_info{}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.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)) - { + for (size_t i = 0; i < c_max_frames_in_flight; i++) { + if (vkCreateSemaphore(device_, &semaphore_info, nullptr, &(this->image_available_semaphores_[i])) != VK_SUCCESS || + vkCreateSemaphore(device_, &semaphore_info, nullptr, &(this->render_finished_semaphores_[i])) != VK_SUCCESS || + vkCreateFence(device_, &fence_info, nullptr, &(this->inflight_fences_[i])) != VK_SUCCESS) { throw std::runtime_error("Failed to create synchronization objects!"); } } } void -VulkanApp::create_descriptor_pool() { +VulkanApp::create_descriptor_pool() +{ VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, @@ -517,9 +398,7 @@ VulkanApp::create_descriptor_pool() { pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); pool_info.pPoolSizes = pool_sizes; - if (vkCreateDescriptorPool(device_, &pool_info, - nullptr, &descriptor_pool_) != VK_SUCCESS) - { + if (vkCreateDescriptorPool(device_, &pool_info, nullptr, &(this->descriptor_pool_)) != VK_SUCCESS) { throw std::runtime_error("Failed to create descriptor pool!"); } } @@ -527,14 +406,10 @@ VulkanApp::create_descriptor_pool() { void VulkanApp::init_imgui(std::function load_fonts) { - scope log(XO_DEBUG(true)); - // Setup Dear ImGui context IMGUI_CHECKVERSION(); this->imgui_cx_ = ImGui::CreateContext(); - log && log(xtag("imgui_cx", (void*)imgui_cx_)); - ImGuiIO& io = ImGui::GetIO(); (void)io; if (load_fonts) @@ -546,50 +421,49 @@ VulkanApp::init_imgui(std::function load_fonts) // 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(swapchainImages.size()); - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; - init_info.Allocator = nullptr; + 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(swapchain_images_.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); + //ImGui_ImplVulkan_Init(&init_info, render_pass_); // Upload Fonts VkCommandBuffer command_buffer = begin_single_time_commands(); ImGui_ImplVulkan_CreateFontsTexture(); this->end_single_time_commands(command_buffer); - ImGui_ImplVulkan_DestroyFontsTexture(); -} /*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; + VkCommandBufferAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandPool = command_pool_; + alloc_info.commandBufferCount = 1; - VkCommandBuffer commandBuffer; - vkAllocateCommandBuffers(device_, &allocInfo, &commandBuffer); + VkCommandBuffer cmdbuf; + vkAllocateCommandBuffers(device_, &alloc_info, &cmdbuf); - VkCommandBufferBeginInfo beginInfo{}; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + VkCommandBufferBeginInfo begin_info{}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - vkBeginCommandBuffer(commandBuffer, &beginInfo); + vkBeginCommandBuffer(cmdbuf, &begin_info); - return commandBuffer; + return cmdbuf; } void @@ -597,124 +471,29 @@ VulkanApp::end_single_time_commands(VkCommandBuffer commandBuffer) { vkEndCommandBuffer(commandBuffer); - VkSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; + VkSubmitInfo submit_info{}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &commandBuffer; - vkQueueSubmit(graphics_queue_, 1, &submitInfo, VK_NULL_HANDLE); + vkQueueSubmit(graphics_queue_, 1, &submit_info, VK_NULL_HANDLE); vkQueueWaitIdle(graphics_queue_); vkFreeCommandBuffers(device_, command_pool_, 1, &commandBuffer); } -void -VulkanApp::cleanup_framebuffers() -{ - // cleanup framebuffers - for (size_t i = 0; i < framebuffers_.size(); ++i) { - vkDestroyFramebuffer(device_, framebuffers_[i], nullptr); - } - framebuffers_.resize(0); -} - -void -VulkanApp::cleanup_render_pass() -{ - // cleanup render pass (see also .create_render_pass()) - vkDestroyRenderPass(device_, render_pass_, nullptr); -} - -void -VulkanApp::cleanup_image_views() -{ - // cleanup swapchain image views - for (size_t i = 0; i < swapchain_image_views_.size(); ++i) { - vkDestroyImageView(device_, swapchain_image_views_[i], nullptr); - } -} - -void -VulkanApp::cleanup_swapchain() -{ - vkDestroySwapchainKHR(device_, swapchain_, nullptr); -} - -void -VulkanApp::cleanup_swapchain_deps() -{ - // destroy in reverse order w.r.t create - this->cleanup_framebuffers(); - this->cleanup_render_pass(); - this->cleanup_image_views(); - this->cleanup_swapchain(); -} - -void -VulkanApp::recreate_swapchain_deps() -{ - // handle possibly-minimzed window - { - int width = 0, height = 0; - SDL_GetWindowSize(window_, &width, &height); - while (width == 0 || height == 0) { - SDL_GetWindowSize(window_, &width, &height); - SDL_WaitEvent(nullptr); - } - } - - vkDeviceWaitIdle(device_); - - // retire old swapchain - this->cleanup_swapchain_deps(); - - // recreate swapchain - //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(); - this->create_framebuffers(); - //this->create_command_pool(); - //this->create_command_buffers(); - //this->create_sync_objects(); - //this->create_descriptor_pool(); -} - void VulkanApp::main_loop() { SDL_Event event; - while (!quit_) { - bool resize_pending = false; - + while (!quit_flag_) { while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) { - this->quit_ = true; + this->quit_flag_ = true; } - - if (event.type == SDL_WINDOWEVENT) { - if (event.window.event == SDL_WINDOWEVENT_CLOSE) { -#ifdef NOT_YET - if (event.window.windowID == SDL_GetWindowID(window)) - { - done = true; - } -#endif - } else if (event.window.event == SDL_WINDOWEVENT_RESIZED) { - resize_pending = true; - } - } - } - - if (resize_pending) { - vkDeviceWaitIdle(device_); - this->recreate_swapchain_deps(); } this->draw_frame(); @@ -726,18 +505,30 @@ VulkanApp::main_loop() void VulkanApp::draw_frame() { - vkWaitForFences(device_, 1, - &in_flight_fences_[current_frame_], VK_TRUE, UINT64_MAX); + vkWaitForFences(device_, 1, &inflight_fences_[current_frame_], VK_TRUE, UINT64_MAX); - uint32_t imageIndex; - vkAcquireNextImageKHR(device_, swapchain_, UINT64_MAX, - image_available_semaphores_[current_frame_], - VK_NULL_HANDLE, &imageIndex); + uint32_t image_ix = 0; + VkResult result = vkAcquireNextImageKHR(device_, swapchain_, UINT64_MAX, + image_available_semaphores_[current_frame_], + VK_NULL_HANDLE, &image_ix); - vkResetFences(device_, 1, &in_flight_fences_[current_frame_]); + switch (result) { + case VK_SUCCESS: + case VK_SUBOPTIMAL_KHR: + break; + case VK_ERROR_OUT_OF_DATE_KHR: + this->recreate_xswapchain(); + // deliberate earlyexit + return; + default: + throw std::runtime_error("failed to acquire swapchain image!"); + break; + } + + vkResetFences(device_, 1, &inflight_fences_[current_frame_]); vkResetCommandBuffer(command_buffers_[current_frame_], 0); - this->record_command_buffer(command_buffers_[current_frame_], imageIndex); + this->record_command_buffer(command_buffers_[current_frame_], image_ix); VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; @@ -751,15 +542,11 @@ VulkanApp::draw_frame() submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &command_buffers_[current_frame_]; - VkSemaphore signalSemaphores[] - = {render_finished_semaphores_[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) - { + if (vkQueueSubmit(graphics_queue_, 1, &submitInfo, inflight_fences_[current_frame_]) != VK_SUCCESS) { throw std::runtime_error("Failed to submit draw command buffer!"); } @@ -773,33 +560,42 @@ VulkanApp::draw_frame() presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapChains; - presentInfo.pImageIndices = &imageIndex; + presentInfo.pImageIndices = &image_ix; - vkQueuePresentKHR(graphics_queue_, &presentInfo); + result = vkQueuePresentKHR(graphics_queue_, &presentInfo); - this->current_frame_ = (current_frame_ + 1) % c_max_frames_in_flight; -} /*draw_frame*/ + if (framebuffer_resized_flag_) + result = VK_ERROR_OUT_OF_DATE_KHR; -void -VulkanApp::record_command_buffer(VkCommandBuffer command_buffer, - uint32_t imageIndex) -{ - // as long as we do this per-frame, independent of swapchain - if (n_frame_ == 0) { - this->start_tm_ = std::chrono::steady_clock::now(); + switch (result) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_DATE_KHR: + case VK_SUBOPTIMAL_KHR: + framebuffer_resized_flag_ = false; + this->recreate_xswapchain(); + break; + default: + throw std::runtime_error("failed to present swapchain image!"); } + this->current_frame_ = (current_frame_ + 1) % c_max_frames_in_flight; +} + +void +VulkanApp::record_command_buffer(VkCommandBuffer cmdbuf, uint32_t image_ix) +{ VkCommandBufferBeginInfo begin_info{}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) { + if (vkBeginCommandBuffer(cmdbuf, &begin_info) != VK_SUCCESS) { throw std::runtime_error("Failed to begin recording command buffer!"); } VkRenderPassBeginInfo render_pass_info{}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_info.renderPass = render_pass_; - render_pass_info.framebuffer = framebuffers_[imageIndex]; + render_pass_info.framebuffer = framebuffers_[image_ix]; render_pass_info.renderArea.offset = {0, 0}; render_pass_info.renderArea.extent = swapchain_extent_; @@ -807,82 +603,145 @@ VulkanApp::record_command_buffer(VkCommandBuffer command_buffer, render_pass_info.clearValueCount = 1; render_pass_info.pClearValues = &clear_color; - vkCmdBeginRenderPass(command_buffer, - &render_pass_info, - VK_SUBPASS_CONTENTS_INLINE); + vkCmdBeginRenderPass(cmdbuf, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); - ImDrawData * draw_data = this->imgui_draw_frame_(imgui_cx_); (void)draw_data; +#ifdef OBSOLETE + // Start the Dear ImGui frame + { + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); -#ifdef TEMPORARILY_REMOVE - ImGui_ImplVulkan_RenderDrawData(draw_data, command_buffer); + // 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, cmdbuf); + + } #endif - vkCmdEndRenderPass(command_buffer); + ImDrawData * draw_data = imgui_draw_frame_(imgui_cx_); (void)draw_data; - { - ++(this->n_frame_); - this->now_tm_ = std::chrono::steady_clock::now(); + ImGui_ImplVulkan_RenderDrawData(draw_data, cmdbuf); - if ((this->n_frame_ == 1) - || (std::chrono::duration_cast(now_tm_ - last_log_fps_tm_).count() > 1)) - { - this->last_log_fps_tm_ = now_tm_; + vkCmdEndRenderPass(cmdbuf); - scope log(XO_DEBUG(true)); - - log && log(xtag("lifetime_fps", this->lifetime_fps())); - } - } - - if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) { + if (vkEndCommandBuffer(cmdbuf) != VK_SUCCESS) { throw std::runtime_error("Failed to record command buffer!"); } } +void +VulkanApp::create_xswapchain(bool create_render_pass_flag) +{ + this->create_swapchain(); + this->create_image_views(); + if(create_render_pass_flag) + this->create_render_pass(); + this->create_framebuffers(); +} + +void +VulkanApp::cleanup_xswapchain() +{ + this->cleanup_framebuffers(); + this->cleanup_image_views(); + this->cleanup_swapchain(); +} + +void +VulkanApp::recreate_xswapchain() +{ + // handle window minimization: wait until window has valid size + this->wait_not_minimized(); + + // wait until device idle before cleaning up resources + vkDeviceWaitIdle(device_); + + // cleanup old xswapchain + this->cleanup_xswapchain(); + + // create new swapchain + this->create_xswapchain(false); +} + +void +VulkanApp::wait_not_minimized() +{ + int width = 0; + int height = 0; + SDL_GetWindowSize(window_, &width, &height); + while (width == 0 || height == 0) { + SDL_GetWindowSize(window_, &width, &height); + SDL_WaitEvent(nullptr); + } +} + +void +VulkanApp::cleanup_framebuffers() +{ + for (auto framebuffer : framebuffers_) { + vkDestroyFramebuffer(device_, framebuffer, nullptr); + } + framebuffers_.clear(); +} + +void +VulkanApp::cleanup_image_views() +{ + for (auto imageView : swapchain_image_views_) { + vkDestroyImageView(device_, imageView, nullptr); + } + swapchain_image_views_.clear(); +} + +void +VulkanApp::cleanup_swapchain() +{ + vkDestroySwapchainKHR(device_, this->swapchain_, nullptr); +} + void VulkanApp::cleanup() { - if (cleanup_done_) - return; - - this->cleanup_done_ = true; - ImGui_ImplVulkan_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); - for (size_t i = 0; i < c_max_frames_in_flight; ++i) { + 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); + vkDestroyFence(device_, inflight_fences_[i], nullptr); } vkDestroyCommandPool(device_, command_pool_, nullptr); - this->cleanup_swapchain_deps(); - -#ifdef OBSOLETE - for (auto framebuffer : framebuffers_) { - vkDestroyFramebuffer(device_, framebuffer, nullptr); - } + this->cleanup_xswapchain(); vkDestroyRenderPass(device_, render_pass_, nullptr); - - for (auto imageView : swapchain_image_views_) { - vkDestroyImageView(device_, imageView, nullptr); - } - - vkDestroySwapchainKHR(device_, swapchain_, nullptr); -#endif - vkDestroyDescriptorPool(device_, descriptor_pool_, nullptr); vkDestroyDevice(device_, nullptr); - vkDestroySurfaceKHR(instance, surface_, nullptr); - vkDestroyInstance(instance, nullptr); + vkDestroySurfaceKHR(instance_, this->surface_, nullptr); + vkDestroyInstance(instance_, nullptr); + this->instance_ = nullptr; SDL_DestroyWindow(window_); - SDL_Quit(); + this->window_ = nullptr; + SDL_Quit(); } /* end VulkanApp.cpp */