vulkan: Add a throttler before present for precise frame rates.

This commit is contained in:
BearOso 2023-02-23 11:00:06 -06:00
parent 1d0dab45da
commit b82edaac96
7 changed files with 85 additions and 6 deletions

View File

@ -126,6 +126,8 @@ if(USE_SLANG)
../vulkan/vulkan_shader_chain.hpp ../vulkan/vulkan_shader_chain.hpp
../vulkan/vulkan_simple_output.hpp ../vulkan/vulkan_simple_output.hpp
../vulkan/vulkan_simple_output.cpp ../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.cpp
src/gtk_display_driver_vulkan.h) src/gtk_display_driver_vulkan.h)

View File

@ -92,6 +92,7 @@ int S9xVulkanDisplayDriver::init()
else else
{ {
window->enable_widget("shader_parameters_item", true); window->enable_widget("shader_parameters_item", true);
return 0; return 0;
} }
} }
@ -117,21 +118,21 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int
if (!context) if (!context)
return; return;
if (gui_config->reduce_input_lag)
context->wait_idle();
auto viewport = S9xApplyAspect(width, height, current_width, current_height); auto viewport = S9xApplyAspect(width, height, current_width, current_height);
context->swapchain->set_max_frame_rate(60.09881389744051);
if (shaderchain) 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); 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) else if (simple_output)
{ {
simple_output->set_filter(Settings.BilinearFilter); 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); 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() int S9xVulkanDisplayDriver::query_availability()

View File

@ -0,0 +1,37 @@
#include "std_chrono_throttle.hpp"
#include <thread>
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<microseconds>(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<microseconds>(steady_clock::now());
then = now;
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <chrono>
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<std::chrono::steady_clock, std::chrono::microseconds> then;
};

View File

@ -1,5 +1,6 @@
#include "vulkan_swapchain.hpp" #include "vulkan_swapchain.hpp"
#include "vulkan/vulkan_structs.hpp" #include "vulkan/vulkan_structs.hpp"
#include <thread>
namespace Vulkan namespace Vulkan
{ {
@ -230,6 +231,11 @@ bool Swapchain::begin_frame()
return true; return true;
} }
void Swapchain::set_max_frame_rate(double frame_rate)
{
throttle.set_frame_rate(frame_rate);
}
bool Swapchain::end_frame() bool Swapchain::end_frame()
{ {
auto &frame = frames[current_frame]; auto &frame = frames[current_frame];
@ -249,6 +255,19 @@ bool Swapchain::end_frame()
.setSwapchains(swapchain_object.get()) .setSwapchains(swapchain_object.get())
.setImageIndices(current_swapchain_image); .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); auto result = queue.presentKHR(present_info);
current_frame = (current_frame + 1) % max_latency; current_frame = (current_frame + 1) % max_latency;

View File

@ -3,7 +3,7 @@
#include "vulkan/vulkan.hpp" #include "vulkan/vulkan.hpp"
#include "vulkan/vulkan_handles.hpp" #include "vulkan/vulkan_handles.hpp"
#include "vulkan/vulkan_structs.hpp" #include "vulkan/vulkan_structs.hpp"
#include <deque> #include "std_chrono_throttle.hpp"
namespace Vulkan namespace Vulkan
{ {
@ -28,6 +28,7 @@ class Swapchain
bool end_frame(); bool end_frame();
// Returns true if vsync setting was changed, false if it was the same // Returns true if vsync setting was changed, false if it was the same
bool set_vsync(bool on); bool set_vsync(bool on);
void set_max_frame_rate(double frame_rate);
vk::Image get_image(); vk::Image get_image();
vk::Framebuffer get_framebuffer(); vk::Framebuffer get_framebuffer();
@ -61,6 +62,7 @@ class Swapchain
vk::UniqueRenderPass render_pass; vk::UniqueRenderPass render_pass;
Throttle throttle;
unsigned int current_frame = 0; unsigned int current_frame = 0;
unsigned int current_swapchain_image = 0; unsigned int current_swapchain_image = 0;
unsigned int num_swapchain_images = 0; unsigned int num_swapchain_images = 0;

View File

@ -449,6 +449,7 @@
<ClInclude Include="..\vulkan\vulkan_slang_pipeline.hpp" /> <ClInclude Include="..\vulkan\vulkan_slang_pipeline.hpp" />
<ClInclude Include="..\vulkan\vulkan_swapchain.hpp" /> <ClInclude Include="..\vulkan\vulkan_swapchain.hpp" />
<ClInclude Include="..\vulkan\vulkan_texture.hpp" /> <ClInclude Include="..\vulkan\vulkan_texture.hpp" />
<ClInclude Include="..\vulkan\std_chrono_throttle.hpp" />
<ClInclude Include="AVIOutput.h" /> <ClInclude Include="AVIOutput.h" />
<ClInclude Include="CCGShader.h" /> <ClInclude Include="CCGShader.h" />
<ClInclude Include="CD3DCG.h" /> <ClInclude Include="CD3DCG.h" />
@ -612,6 +613,7 @@
<ClCompile Include="..\vulkan\vulkan_slang_pipeline.cpp" /> <ClCompile Include="..\vulkan\vulkan_slang_pipeline.cpp" />
<ClCompile Include="..\vulkan\vulkan_swapchain.cpp" /> <ClCompile Include="..\vulkan\vulkan_swapchain.cpp" />
<ClCompile Include="..\vulkan\vulkan_texture.cpp" /> <ClCompile Include="..\vulkan\vulkan_texture.cpp" />
<ClCompile Include="..\vulkan\std_chrono_throttle.cpp" />
<ClCompile Include="AVIOutput.cpp" /> <ClCompile Include="AVIOutput.cpp" />
<ClCompile Include="CCGShader.cpp" /> <ClCompile Include="CCGShader.cpp" />
<ClCompile Include="CD3DCG.cpp" /> <ClCompile Include="CD3DCG.cpp" />
@ -688,4 +690,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>
</Project> </Project>