From b4a8ecd7d609663893018bd61d08ceea092dbbf7 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 12 Nov 2025 13:33:20 -0500 Subject: [PATCH] xo-imgui: support dynamic vsync_enabled setting + demo in ex4 --- xo-imgui/example/ex4/DrawState.cpp | 4 ++-- xo-imgui/example/ex4/DrawState.hpp | 2 ++ xo-imgui/example/ex4/imgui_ex4.cpp | 15 +++++++++++- xo-imgui/include/xo/imgui/VulkanApp.hpp | 31 +++++++++++++++++++++---- xo-imgui/src/imgui/VulkanApp.cpp | 18 ++++++++++---- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/xo-imgui/example/ex4/DrawState.cpp b/xo-imgui/example/ex4/DrawState.cpp index fa68af10..72e00202 100644 --- a/xo-imgui/example/ex4/DrawState.cpp +++ b/xo-imgui/example/ex4/DrawState.cpp @@ -741,7 +741,7 @@ DrawState::draw_gc_state(const AppState & app_state, /* TODO: does this reset coord space? */ ImRect alloc_rect; { - ImGui::BeginChild("top pane", ImVec2(0, 105), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY); + ImGui::BeginChild("top pane", ImVec2(0, 200), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeY); alloc_rect = ImRect(canvas_rect.top_left() + ImGui::GetWindowContentRegionMin(), canvas_rect.top_left() + ImGui::GetWindowContentRegionMax()); @@ -817,7 +817,7 @@ DrawState::draw_gc_state(const AppState & app_state, ImGui::EndChild(); } - ImGui::Text("placeholder text"); + //ImGui::Text("placeholder text"); // appears below history_rect /* BeginChild() again ? */ diff --git a/xo-imgui/example/ex4/DrawState.hpp b/xo-imgui/example/ex4/DrawState.hpp index ea9ee3ab..cfdd6545 100644 --- a/xo-imgui/example/ex4/DrawState.hpp +++ b/xo-imgui/example/ex4/DrawState.hpp @@ -85,6 +85,8 @@ struct DrawState { public: /** when true display imgui demo window **/ bool show_demo_window_ = false; + /** whether vsync feature enabled (throttle to ~60 fps) **/ + bool vsync_enabled_ = true; draw_state_type state_type_ = draw_state_type::alloc; diff --git a/xo-imgui/example/ex4/imgui_ex4.cpp b/xo-imgui/example/ex4/imgui_ex4.cpp index bb2ad9a9..38afebf9 100644 --- a/xo-imgui/example/ex4/imgui_ex4.cpp +++ b/xo-imgui/example/ex4/imgui_ex4.cpp @@ -80,7 +80,7 @@ namespace { *p_f = 0.0f; *p_counter = 0; - return [p_app_state, p_draw_state, p_f, p_counter](ImGuiContext * imgui_cx) + return [p_app_state, p_draw_state, p_f, p_counter](VulkanApp * vulkan_app, ImGuiContext * imgui_cx) { scope log(XO_DEBUG(false)); @@ -108,6 +108,19 @@ namespace { ImGui::End(); # endif + // 0. main menubar + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("View")) { + bool x = vulkan_app->vsync_enabled_flag(); + + if (ImGui::MenuItem("vsync", nullptr, &x)) { + vulkan_app->update_vsync_enabled(x); + } + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } + // 1. create a simple ImGui window ImGui::Begin("Hello, Vulkan + SDL2!"); ImGui::Text("This is a minimal ImGui + Vulkan + SDL2 example!"); diff --git a/xo-imgui/include/xo/imgui/VulkanApp.hpp b/xo-imgui/include/xo/imgui/VulkanApp.hpp index 8f47edd7..f20194d8 100644 --- a/xo-imgui/include/xo/imgui/VulkanApp.hpp +++ b/xo-imgui/include/xo/imgui/VulkanApp.hpp @@ -13,7 +13,7 @@ class VulkanApp { public: - using ImguiDrawFn = std::function; + using ImguiDrawFn = std::function; public: /* create single-window vulkan application; @@ -21,9 +21,17 @@ public: */ VulkanApp(ImguiDrawFn fn); + bool vsync_enabled_flag() const { return vsync_enabled_flag_; } + + /* update vsync enabled (will recreate xswapchain at next opportunity) + */ + void update_vsync_enabled(bool flag); + /* run application; borrows calling thread for event loop, * until application exit. * Invoke load_fonts once imgui context established + * @ref vsync_enabled_flag_. true for VK_PRESENT_MODE_FIFO_KHR; + * false for VK_PRESENT_MODE_IMMEDIATE_KHR */ void run(std::function load_fonts); @@ -47,7 +55,10 @@ private: */ void init_sdl_window(); - /* setup vulkan state. swapchain, command buffers etc */ + /* setup vulkan state. swapchain, command buffers etc + * @ref vsync_enabled_flag_. true for VK_PRESENT_MODE_FIFO_KHR; + * false for VK_PRESENT_MODE_IMMEDIATE_KHR + */ void init_vulkan(); /* create vulkan instance. @@ -74,6 +85,8 @@ private: /* populates @ref swapchain_, @ref swapchain_images_, * @ref swapchain_image_views_, @ref framebuffers_. * Also $ref render_pass_ iff @p create_render_pass_flag + * @ref vsync_enabled_flag_. true for VK_PRESENT_MODE_FIFO_KHR; + * false for VK_PRESENT_MODE_IMMEDIATE_KHR */ void create_xswapchain(bool create_render_pass_flag); @@ -175,6 +188,10 @@ private: /* abstraction for presentation area (?) */ VkSurfaceKHR surface_; + /* true -> VK_PRESENT_MODE_FIFO_KHR + * false -> VK_PRESENT_MODE_IMMEDIATE_KHR + */ + bool vsync_enabled_flag_ = true; /* physical device (graphics card) */ VkPhysicalDevice physical_device_; @@ -210,10 +227,14 @@ private: 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. + /* true if xswapchain is out-of-date. + * reset when xswapchain recreated (and therefore in consistent state). + * + * Triggered by: + * - window resize + * - vsync toggled */ - bool framebuffer_resized_flag_ = false; + bool xswapchain_recreate_flag_ = false; bool quit_flag_ = false; /** draw imgui **/ diff --git a/xo-imgui/src/imgui/VulkanApp.cpp b/xo-imgui/src/imgui/VulkanApp.cpp index 94db4884..b298da86 100644 --- a/xo-imgui/src/imgui/VulkanApp.cpp +++ b/xo-imgui/src/imgui/VulkanApp.cpp @@ -216,7 +216,7 @@ VulkanApp::create_swapchain() 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.presentMode = (vsync_enabled_flag_ ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR); create_info.clipped = VK_TRUE; if (vkCreateSwapchainKHR(device_, &create_info, nullptr, &(this->swapchain_)) != VK_SUCCESS) { @@ -564,7 +564,7 @@ VulkanApp::draw_frame() result = vkQueuePresentKHR(graphics_queue_, &presentInfo); - if (framebuffer_resized_flag_) + if (xswapchain_recreate_flag_) result = VK_ERROR_OUT_OF_DATE_KHR; switch (result) { @@ -572,7 +572,6 @@ VulkanApp::draw_frame() break; case VK_ERROR_OUT_OF_DATE_KHR: case VK_SUBOPTIMAL_KHR: - framebuffer_resized_flag_ = false; this->recreate_xswapchain(); break; default: @@ -582,6 +581,12 @@ VulkanApp::draw_frame() this->current_frame_ = (current_frame_ + 1) % c_max_frames_in_flight; } +void +VulkanApp::update_vsync_enabled(bool flag) { + this->vsync_enabled_flag_ = flag; + this->xswapchain_recreate_flag_ = true; +} + void VulkanApp::record_command_buffer(VkCommandBuffer cmdbuf, uint32_t image_ix) { @@ -633,7 +638,7 @@ VulkanApp::record_command_buffer(VkCommandBuffer cmdbuf, uint32_t image_ix) } #endif - ImDrawData * draw_data = imgui_draw_frame_(imgui_cx_); (void)draw_data; + ImDrawData * draw_data = imgui_draw_frame_(this, imgui_cx_); ImGui_ImplVulkan_RenderDrawData(draw_data, cmdbuf); @@ -671,11 +676,14 @@ VulkanApp::recreate_xswapchain() // wait until device idle before cleaning up resources vkDeviceWaitIdle(device_); + // remember consistent swapchain state restored + this->xswapchain_recreate_flag_ = false; + // cleanup old xswapchain this->cleanup_xswapchain(); // create new swapchain - this->create_xswapchain(false); + this->create_xswapchain(false /*create_render_pass_flag*/); } void