vulkan: Move throttle out of swapchain.

Make the swapchain wrapper more flexible, allowing deferring the
swap from the end_frame function.
This commit is contained in:
BearOso 2023-02-23 15:24:01 -06:00
parent b82edaac96
commit 309f9e650d
15 changed files with 89 additions and 43 deletions

View File

@ -22,6 +22,7 @@ class S9xDisplayDriver
virtual void *get_parameters() = 0; virtual void *get_parameters() = 0;
virtual void save(const char *filename) = 0; virtual void save(const char *filename) = 0;
virtual bool is_ready() = 0; virtual bool is_ready() = 0;
virtual bool can_throttle() { return false; };
protected: protected:
Snes9xWindow *window; Snes9xWindow *window;

View File

@ -125,11 +125,11 @@ void S9xOpenGLDisplayDriver::update(uint16_t *buffer, int width, int height, int
if (using_glsl_shaders) if (using_glsl_shaders)
{ {
glsl_shader->render(texmap, width, height, content.x, allocation.get_height() - content.y - content.h, content.w, content.h, S9xViewportCallback); glsl_shader->render(texmap, width, height, content.x, allocation.get_height() - content.y - content.h, content.w, content.h, S9xViewportCallback);
swap_buffers();
return;
} }
else
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); {
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
swap_buffers(); swap_buffers();
} }
@ -426,6 +426,12 @@ int S9xOpenGLDisplayDriver::init()
void S9xOpenGLDisplayDriver::swap_buffers() void S9xOpenGLDisplayDriver::swap_buffers()
{ {
if (Settings.SkipFrames == THROTTLE_TIMER_FRAMESKIP || Settings.SkipFrames == THROTTLE_TIMER)
{
throttle.set_frame_rate(Settings.PAL ? PAL_PROGRESSIVE_FRAME_RATE : NTSC_PROGRESSIVE_FRAME_RATE);
throttle.wait_for_frame_and_rebase_time();
}
context->swap_buffers(); context->swap_buffers();
if (config->reduce_input_lag) if (config->reduce_input_lag)

View File

@ -23,6 +23,7 @@
#endif #endif
#include "shaders/glsl.h" #include "shaders/glsl.h"
#include "vulkan/std_chrono_throttle.hpp"
#define BUFFER_OFFSET(i) ((char *)(i)) #define BUFFER_OFFSET(i) ((char *)(i))
@ -30,14 +31,15 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver
{ {
public: public:
S9xOpenGLDisplayDriver(Snes9xWindow *window, Snes9xConfig *config); S9xOpenGLDisplayDriver(Snes9xWindow *window, Snes9xConfig *config);
void refresh(int width, int height); void refresh(int width, int height) override;
int init(); int init() override;
void deinit(); void deinit() override;
void update(uint16_t *buffer, int width, int height, int stride_in_pixels); void update(uint16_t *buffer, int width, int height, int stride_in_pixels) override;
void *get_parameters(); void *get_parameters() override;
void save(const char *filename); void save(const char *filename) override;
static int query_availability(); static int query_availability();
bool is_ready(); bool is_ready() override;
bool can_throttle() override { return true; };
private: private:
bool opengl_defaults(); bool opengl_defaults();
@ -68,6 +70,8 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver
OpenGLContext *context; OpenGLContext *context;
Throttle throttle;
#ifdef GDK_WINDOWING_X11 #ifdef GDK_WINDOWING_X11
GTKGLXContext glx; GTKGLXContext glx;
#endif #endif

View File

@ -119,20 +119,27 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int
return; return;
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_without_swap((uint8_t *)buffer, width, height, stride_in_pixels << 1, vk::Format::eR5G6B5UnormPack16, viewport.x, viewport.y, viewport.w, viewport.h);
} }
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_without_swap((uint8_t *)buffer, width, height, stride_in_pixels << 1, viewport.x, viewport.y, viewport.w, viewport.h);
} }
if (Settings.SkipFrames == THROTTLE_TIMER || Settings.SkipFrames == THROTTLE_TIMER_FRAMESKIP)
{
throttle.set_frame_rate(Settings.PAL ? PAL_PROGRESSIVE_FRAME_RATE : NTSC_PROGRESSIVE_FRAME_RATE);
throttle.wait_for_frame_and_rebase_time();
}
context->swapchain->swap();
if (gui_config->reduce_input_lag) if (gui_config->reduce_input_lag)
context->wait_idle(); context->wait_idle();
} }
int S9xVulkanDisplayDriver::query_availability() int S9xVulkanDisplayDriver::query_availability()

View File

@ -10,6 +10,7 @@
#include "../../vulkan/vulkan_context.hpp" #include "../../vulkan/vulkan_context.hpp"
#include "../../vulkan/vulkan_shader_chain.hpp" #include "../../vulkan/vulkan_shader_chain.hpp"
#include "../../vulkan/vulkan_simple_output.hpp" #include "../../vulkan/vulkan_simple_output.hpp"
#include "../../vulkan/std_chrono_throttle.hpp"
#ifdef VK_USE_PLATFORM_WAYLAND_KHR #ifdef VK_USE_PLATFORM_WAYLAND_KHR
#include "gtk_wayland_surface.h" #include "gtk_wayland_surface.h"
@ -27,6 +28,7 @@ class S9xVulkanDisplayDriver : public S9xDisplayDriver
void *get_parameters() override; void *get_parameters() override;
void save(const char *filename) override; void save(const char *filename) override;
bool is_ready() override; bool is_ready() override;
bool can_throttle() override { return true; }
static int query_availability(); static int query_availability();
@ -40,6 +42,7 @@ class S9xVulkanDisplayDriver : public S9xDisplayDriver
Window xid; Window xid;
int current_width; int current_width;
int current_height; int current_height;
Throttle throttle;
#ifdef VK_USE_PLATFORM_WAYLAND_KHR #ifdef VK_USE_PLATFORM_WAYLAND_KHR
std::unique_ptr<WaylandSurface> wayland_surface; std::unique_ptr<WaylandSurface> wayland_surface;

View File

@ -450,6 +450,9 @@ static void S9xThrottle(int method)
} }
else // THROTTLE_TIMER or THROTTLE_TIMER_FRAMESKIP else // THROTTLE_TIMER or THROTTLE_TIMER_FRAMESKIP
{ {
if (S9xDisplayGetDriver()->can_throttle())
return;
if (method == THROTTLE_TIMER_FRAMESKIP) if (method == THROTTLE_TIMER_FRAMESKIP)
{ {
if (now - frame_clock > Settings.FrameTime) if (now - frame_clock > Settings.FrameTime)

View File

@ -61,6 +61,10 @@
#define NTSC_MASTER_CLOCK 21477272.727272 // 21477272 + 8/11 exact #define NTSC_MASTER_CLOCK 21477272.727272 // 21477272 + 8/11 exact
#define PAL_MASTER_CLOCK 21281370.0 #define PAL_MASTER_CLOCK 21281370.0
#define NTSC_PROGRESSIVE_FRAME_RATE 60.09881389744051
#define NTSC_INTERLACED_FRAME_RATE 59.94005994
#define PAL_PROGRESSIVE_FRAME_RATE 50.006977968
#define SNES_MAX_NTSC_VCOUNTER 262 #define SNES_MAX_NTSC_VCOUNTER 262
#define SNES_MAX_PAL_VCOUNTER 312 #define SNES_MAX_PAL_VCOUNTER 312

View File

@ -25,6 +25,20 @@ void Throttle::wait_for_frame()
std::this_thread::sleep_for(time_to_wait); std::this_thread::sleep_for(time_to_wait);
} }
void Throttle::wait_for_frame_and_rebase_time()
{
auto time_to_wait = remaining();
if (time_to_wait < -frame_duration_us / 10)
reset();
if (time_to_wait.count() > 0)
{
std::this_thread::sleep_for(time_to_wait);
advance();
}
}
void Throttle::advance() void Throttle::advance()
{ {
then += frame_duration_us; then += frame_duration_us;

View File

@ -8,6 +8,7 @@ struct Throttle
void advance(); void advance();
void reset(); void reset();
void wait_for_frame(); void wait_for_frame();
void wait_for_frame_and_rebase_time();
std::chrono::microseconds remaining(); std::chrono::microseconds remaining();
double max_frame_rate = 0.0; double max_frame_rate = 0.0;
double frame_duration = 0.0; double frame_duration = 0.0;

View File

@ -390,6 +390,12 @@ void ShaderChain::update_descriptor_set(vk::CommandBuffer cmd, int pipe_num, int
} }
void ShaderChain::do_frame(uint8_t *data, int width, int height, int stride, vk::Format format, int viewport_x, int viewport_y, int viewport_width, int viewport_height) void ShaderChain::do_frame(uint8_t *data, int width, int height, int stride, vk::Format format, int viewport_x, int viewport_y, int viewport_width, int viewport_height)
{
do_frame_without_swap(data, width, height, stride, format, viewport_x, viewport_y, viewport_width, viewport_height);
context->swapchain->swap();
}
void ShaderChain::do_frame_without_swap(uint8_t *data, int width, int height, int stride, vk::Format format, int viewport_x, int viewport_y, int viewport_width, int viewport_height)
{ {
if (!context->swapchain->begin_frame()) if (!context->swapchain->begin_frame())
return; return;
@ -508,8 +514,7 @@ void ShaderChain::do_frame(uint8_t *data, int width, int height, int stride, vk:
frame.image.current_layout = vk::ImageLayout::eTransferDstOptimal; frame.image.current_layout = vk::ImageLayout::eTransferDstOptimal;
} }
} }
context->swapchain->end_frame_without_swap();
context->swapchain->end_frame();
last_frame_index = current_frame_index; last_frame_index = current_frame_index;
frame_count++; frame_count++;

View File

@ -20,6 +20,7 @@ class ShaderChain
void update_and_propagate_sizes(int original_width_, int original_height_, int viewport_width_, int viewport_height_); void update_and_propagate_sizes(int original_width_, int original_height_, int viewport_width_, int viewport_height_);
bool load_lookup_textures(); bool load_lookup_textures();
void do_frame(uint8_t *data, int width, int height, int stride, vk::Format format, int viewport_x, int viewport_y, int viewport_width, int viewport_height); void do_frame(uint8_t *data, int width, int height, int stride, vk::Format format, int viewport_x, int viewport_y, int viewport_width, int viewport_height);
void do_frame_without_swap(uint8_t *data, int width, int height, int stride, vk::Format format, int viewport_x, int viewport_y, int viewport_width, int viewport_height);
void upload_original(uint8_t *data, int width, int height, int stride, vk::Format format); void upload_original(uint8_t *data, int width, int height, int stride, vk::Format format);
void upload_original(vk::CommandBuffer cmd, uint8_t *data, int width, int height, int stride, vk::Format format); void upload_original(vk::CommandBuffer cmd, uint8_t *data, int width, int height, int stride, vk::Format format);
void construct_buffer_objects(); void construct_buffer_objects();

View File

@ -215,7 +215,7 @@ void SimpleOutput::set_filter(bool on)
filter = on; filter = on;
} }
void SimpleOutput::do_frame(uint8_t *buffer, int width, int height, int byte_stride, int viewport_x, int viewport_y, int viewport_width, int viewport_height) void SimpleOutput::do_frame_without_swap(uint8_t *buffer, int width, int height, int byte_stride, int viewport_x, int viewport_y, int viewport_width, int viewport_height)
{ {
if (!context) if (!context)
return; return;
@ -252,7 +252,13 @@ void SimpleOutput::do_frame(uint8_t *buffer, int width, int height, int byte_str
cmd.draw(3, 1, 0, 0); cmd.draw(3, 1, 0, 0);
swapchain->end_render_pass(); swapchain->end_render_pass();
swapchain->end_frame(); swapchain->end_frame_without_swap();
}
void SimpleOutput::do_frame(uint8_t *buffer, int width, int height, int byte_stride, int viewport_x, int viewport_y, int viewport_width, int viewport_height)
{
do_frame_without_swap(buffer, width, height, byte_stride, viewport_x, viewport_y, viewport_width, viewport_height);
swapchain->swap();
} }
} // namespace Vulkan } // namespace Vulkan

View File

@ -11,6 +11,7 @@ class SimpleOutput
SimpleOutput(Vulkan::Context *context, vk::Format format); SimpleOutput(Vulkan::Context *context, vk::Format format);
~SimpleOutput(); ~SimpleOutput();
void do_frame(uint8_t *buffer, int width, int height, int byte_stride, int viewport_x, int viewport_y, int viewport_width, int viewport_height); void do_frame(uint8_t *buffer, int width, int height, int byte_stride, int viewport_x, int viewport_y, int viewport_width, int viewport_height);
void do_frame_without_swap(uint8_t *buffer, int width, int height, int byte_stride, int viewport_x, int viewport_y, int viewport_width, int viewport_height);
void set_filter(bool on); void set_filter(bool on);
private: private:

View File

@ -231,12 +231,7 @@ bool Swapchain::begin_frame()
return true; return true;
} }
void Swapchain::set_max_frame_rate(double frame_rate) void Swapchain::end_frame_without_swap()
{
throttle.set_frame_rate(frame_rate);
}
bool Swapchain::end_frame()
{ {
auto &frame = frames[current_frame]; auto &frame = frames[current_frame];
frame.command_buffer->end(); frame.command_buffer->end();
@ -249,25 +244,15 @@ bool Swapchain::end_frame()
frame.complete.get()); frame.complete.get());
queue.submit(submit_info, frame.fence.get()); queue.submit(submit_info, frame.fence.get());
}
bool Swapchain::swap()
{
auto present_info = vk::PresentInfoKHR{} auto present_info = vk::PresentInfoKHR{}
.setWaitSemaphores(frames[current_frame].complete.get()) .setWaitSemaphores(frames[current_frame].complete.get())
.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;
@ -277,6 +262,12 @@ bool Swapchain::end_frame()
return true; return true;
} }
bool Swapchain::end_frame()
{
end_frame_without_swap();
return swap();
}
vk::Framebuffer Swapchain::get_framebuffer() vk::Framebuffer Swapchain::get_framebuffer()
{ {
return imageviewfbs[current_swapchain_image].framebuffer.get(); return imageviewfbs[current_swapchain_image].framebuffer.get();

View File

@ -3,7 +3,6 @@
#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 "std_chrono_throttle.hpp"
namespace Vulkan namespace Vulkan
{ {
@ -26,9 +25,10 @@ class Swapchain
void end_render_pass(); void end_render_pass();
bool wait_on_frame(int frame_num); bool wait_on_frame(int frame_num);
bool end_frame(); bool end_frame();
void end_frame_without_swap();
bool swap();
// 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();
@ -62,7 +62,6 @@ 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;