diff --git a/gtk/CMakeLists.txt b/gtk/CMakeLists.txt index 835f2acd..5408d23e 100644 --- a/gtk/CMakeLists.txt +++ b/gtk/CMakeLists.txt @@ -126,6 +126,8 @@ if(USE_SLANG) ../vulkan/vulkan_shader_chain.hpp ../vulkan/vulkan_simple_output.hpp ../vulkan/vulkan_simple_output.cpp + ../vulkan/std_chrono_throttle.cpp + ../vulkan/std_chrono_throttle.hpp src/gtk_display_driver_vulkan.cpp src/gtk_display_driver_vulkan.h) diff --git a/gtk/src/gtk_display_driver_vulkan.cpp b/gtk/src/gtk_display_driver_vulkan.cpp index 79811d70..4882c0de 100644 --- a/gtk/src/gtk_display_driver_vulkan.cpp +++ b/gtk/src/gtk_display_driver_vulkan.cpp @@ -92,6 +92,7 @@ int S9xVulkanDisplayDriver::init() else { window->enable_widget("shader_parameters_item", true); + return 0; } } @@ -117,21 +118,21 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int if (!context) return; - if (gui_config->reduce_input_lag) - context->wait_idle(); - auto viewport = S9xApplyAspect(width, height, current_width, current_height); + context->swapchain->set_max_frame_rate(60.09881389744051); if (shaderchain) { shaderchain->do_frame((uint8_t *)buffer, width, height, stride_in_pixels << 1, vk::Format::eR5G6B5UnormPack16, viewport.x, viewport.y, viewport.w, viewport.h); - return; } else if (simple_output) { simple_output->set_filter(Settings.BilinearFilter); simple_output->do_frame((uint8_t *)buffer, width, height, stride_in_pixels << 1, viewport.x, viewport.y, viewport.w, viewport.h); } + + if (gui_config->reduce_input_lag) + context->wait_idle(); } int S9xVulkanDisplayDriver::query_availability() diff --git a/vulkan/std_chrono_throttle.cpp b/vulkan/std_chrono_throttle.cpp new file mode 100644 index 00000000..cbad37c0 --- /dev/null +++ b/vulkan/std_chrono_throttle.cpp @@ -0,0 +1,37 @@ +#include "std_chrono_throttle.hpp" +#include + +using namespace std::chrono; + +void Throttle::set_frame_rate(double frame_rate) +{ + max_frame_rate = frame_rate; + frame_duration = 1.0 / max_frame_rate; + frame_duration_us = microseconds(int64_t(frame_duration * 1000000.0)); +} + +microseconds Throttle::remaining() +{ + auto now = time_point_cast(steady_clock::now()); + auto diff = (now - then); + return frame_duration_us - diff; +} + +void Throttle::wait_for_frame() +{ + auto time_to_wait = remaining(); + + if (time_to_wait.count() > 0) + std::this_thread::sleep_for(time_to_wait); +} + +void Throttle::advance() +{ + then += frame_duration_us; +} + +void Throttle::reset() +{ + auto now = time_point_cast(steady_clock::now()); + then = now; +} diff --git a/vulkan/std_chrono_throttle.hpp b/vulkan/std_chrono_throttle.hpp new file mode 100644 index 00000000..2c64e627 --- /dev/null +++ b/vulkan/std_chrono_throttle.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +struct Throttle +{ + void set_frame_rate(double frame_rate); + void advance(); + void reset(); + void wait_for_frame(); + std::chrono::microseconds remaining(); + double max_frame_rate = 0.0; + double frame_duration = 0.0; + std::chrono::microseconds frame_duration_us; + std::chrono::time_point then; +}; \ No newline at end of file diff --git a/vulkan/vulkan_swapchain.cpp b/vulkan/vulkan_swapchain.cpp index 60ff5651..4c5b32ef 100644 --- a/vulkan/vulkan_swapchain.cpp +++ b/vulkan/vulkan_swapchain.cpp @@ -1,5 +1,6 @@ #include "vulkan_swapchain.hpp" #include "vulkan/vulkan_structs.hpp" +#include namespace Vulkan { @@ -230,6 +231,11 @@ bool Swapchain::begin_frame() return true; } +void Swapchain::set_max_frame_rate(double frame_rate) +{ + throttle.set_frame_rate(frame_rate); +} + bool Swapchain::end_frame() { auto &frame = frames[current_frame]; @@ -249,6 +255,19 @@ bool Swapchain::end_frame() .setSwapchains(swapchain_object.get()) .setImageIndices(current_swapchain_image); + if (throttle.max_frame_rate != 0.0) + { + auto remaining = throttle.remaining(); + if (remaining < -throttle.frame_duration_us / 10) + throttle.reset(); + else if (remaining.count() > 0) + { + queue.waitIdle(); + throttle.wait_for_frame(); + throttle.advance(); + } + } + auto result = queue.presentKHR(present_info); current_frame = (current_frame + 1) % max_latency; diff --git a/vulkan/vulkan_swapchain.hpp b/vulkan/vulkan_swapchain.hpp index 13e0ec2b..d1a2a566 100644 --- a/vulkan/vulkan_swapchain.hpp +++ b/vulkan/vulkan_swapchain.hpp @@ -3,7 +3,7 @@ #include "vulkan/vulkan.hpp" #include "vulkan/vulkan_handles.hpp" #include "vulkan/vulkan_structs.hpp" -#include +#include "std_chrono_throttle.hpp" namespace Vulkan { @@ -28,6 +28,7 @@ class Swapchain bool end_frame(); // Returns true if vsync setting was changed, false if it was the same bool set_vsync(bool on); + void set_max_frame_rate(double frame_rate); vk::Image get_image(); vk::Framebuffer get_framebuffer(); @@ -61,6 +62,7 @@ class Swapchain vk::UniqueRenderPass render_pass; + Throttle throttle; unsigned int current_frame = 0; unsigned int current_swapchain_image = 0; unsigned int num_swapchain_images = 0; diff --git a/win32/snes9xw.vcxproj b/win32/snes9xw.vcxproj index 31e04fd8..eba6fe01 100644 --- a/win32/snes9xw.vcxproj +++ b/win32/snes9xw.vcxproj @@ -449,6 +449,7 @@ + @@ -612,6 +613,7 @@ + @@ -688,4 +690,4 @@ - \ No newline at end of file +