From 309f9e650d9318c31c6632b19ee317e52ab64fcb Mon Sep 17 00:00:00 2001 From: BearOso Date: Thu, 23 Feb 2023 15:24:01 -0600 Subject: [PATCH] vulkan: Move throttle out of swapchain. Make the swapchain wrapper more flexible, allowing deferring the swap from the end_frame function. --- gtk/src/gtk_display_driver.h | 1 + gtk/src/gtk_display_driver_opengl.cpp | 14 +++++++++---- gtk/src/gtk_display_driver_opengl.h | 18 ++++++++++------- gtk/src/gtk_display_driver_vulkan.cpp | 17 +++++++++++----- gtk/src/gtk_display_driver_vulkan.h | 5 ++++- gtk/src/gtk_s9x.cpp | 3 +++ snes9x.h | 4 ++++ vulkan/std_chrono_throttle.cpp | 14 +++++++++++++ vulkan/std_chrono_throttle.hpp | 1 + vulkan/vulkan_shader_chain.cpp | 9 +++++++-- vulkan/vulkan_shader_chain.hpp | 1 + vulkan/vulkan_simple_output.cpp | 10 +++++++-- vulkan/vulkan_simple_output.hpp | 1 + vulkan/vulkan_swapchain.cpp | 29 +++++++++------------------ vulkan/vulkan_swapchain.hpp | 5 ++--- 15 files changed, 89 insertions(+), 43 deletions(-) diff --git a/gtk/src/gtk_display_driver.h b/gtk/src/gtk_display_driver.h index e84172a3..c02a841b 100644 --- a/gtk/src/gtk_display_driver.h +++ b/gtk/src/gtk_display_driver.h @@ -22,6 +22,7 @@ class S9xDisplayDriver virtual void *get_parameters() = 0; virtual void save(const char *filename) = 0; virtual bool is_ready() = 0; + virtual bool can_throttle() { return false; }; protected: Snes9xWindow *window; diff --git a/gtk/src/gtk_display_driver_opengl.cpp b/gtk/src/gtk_display_driver_opengl.cpp index 9e28bd77..b0173e09 100644 --- a/gtk/src/gtk_display_driver_opengl.cpp +++ b/gtk/src/gtk_display_driver_opengl.cpp @@ -125,11 +125,11 @@ void S9xOpenGLDisplayDriver::update(uint16_t *buffer, int width, int height, int if (using_glsl_shaders) { glsl_shader->render(texmap, width, height, content.x, allocation.get_height() - content.y - content.h, content.w, content.h, S9xViewportCallback); - swap_buffers(); - return; } - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + else + { + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } swap_buffers(); } @@ -426,6 +426,12 @@ int S9xOpenGLDisplayDriver::init() 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(); if (config->reduce_input_lag) diff --git a/gtk/src/gtk_display_driver_opengl.h b/gtk/src/gtk_display_driver_opengl.h index 64dc8996..a8c5087a 100644 --- a/gtk/src/gtk_display_driver_opengl.h +++ b/gtk/src/gtk_display_driver_opengl.h @@ -23,6 +23,7 @@ #endif #include "shaders/glsl.h" +#include "vulkan/std_chrono_throttle.hpp" #define BUFFER_OFFSET(i) ((char *)(i)) @@ -30,14 +31,15 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver { public: S9xOpenGLDisplayDriver(Snes9xWindow *window, Snes9xConfig *config); - void refresh(int width, int height); - int init(); - void deinit(); - void update(uint16_t *buffer, int width, int height, int stride_in_pixels); - void *get_parameters(); - void save(const char *filename); + void refresh(int width, int height) override; + int init() override; + void deinit() override; + void update(uint16_t *buffer, int width, int height, int stride_in_pixels) override; + void *get_parameters() override; + void save(const char *filename) override; static int query_availability(); - bool is_ready(); + bool is_ready() override; + bool can_throttle() override { return true; }; private: bool opengl_defaults(); @@ -68,6 +70,8 @@ class S9xOpenGLDisplayDriver : public S9xDisplayDriver OpenGLContext *context; + Throttle throttle; + #ifdef GDK_WINDOWING_X11 GTKGLXContext glx; #endif diff --git a/gtk/src/gtk_display_driver_vulkan.cpp b/gtk/src/gtk_display_driver_vulkan.cpp index 4882c0de..f1e05a82 100644 --- a/gtk/src/gtk_display_driver_vulkan.cpp +++ b/gtk/src/gtk_display_driver_vulkan.cpp @@ -119,20 +119,27 @@ void S9xVulkanDisplayDriver::update(uint16_t *buffer, int width, int height, int return; 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); + 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) { 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) - context->wait_idle(); + context->wait_idle(); } int S9xVulkanDisplayDriver::query_availability() @@ -156,4 +163,4 @@ void S9xVulkanDisplayDriver::save(const char *filename) bool S9xVulkanDisplayDriver::is_ready() { return true; -} +} \ No newline at end of file diff --git a/gtk/src/gtk_display_driver_vulkan.h b/gtk/src/gtk_display_driver_vulkan.h index 75f6be75..282eae95 100644 --- a/gtk/src/gtk_display_driver_vulkan.h +++ b/gtk/src/gtk_display_driver_vulkan.h @@ -10,6 +10,7 @@ #include "../../vulkan/vulkan_context.hpp" #include "../../vulkan/vulkan_shader_chain.hpp" #include "../../vulkan/vulkan_simple_output.hpp" +#include "../../vulkan/std_chrono_throttle.hpp" #ifdef VK_USE_PLATFORM_WAYLAND_KHR #include "gtk_wayland_surface.h" @@ -27,19 +28,21 @@ class S9xVulkanDisplayDriver : public S9xDisplayDriver void *get_parameters() override; void save(const char *filename) override; bool is_ready() override; + bool can_throttle() override { return true; } static int query_availability(); private: std::unique_ptr context; vk::Device device; - + GdkDisplay *gdk_display; GdkWindow *gdk_window; Display *display; Window xid; int current_width; int current_height; + Throttle throttle; #ifdef VK_USE_PLATFORM_WAYLAND_KHR std::unique_ptr wayland_surface; diff --git a/gtk/src/gtk_s9x.cpp b/gtk/src/gtk_s9x.cpp index eff209e1..0503ccdf 100644 --- a/gtk/src/gtk_s9x.cpp +++ b/gtk/src/gtk_s9x.cpp @@ -450,6 +450,9 @@ static void S9xThrottle(int method) } else // THROTTLE_TIMER or THROTTLE_TIMER_FRAMESKIP { + if (S9xDisplayGetDriver()->can_throttle()) + return; + if (method == THROTTLE_TIMER_FRAMESKIP) { if (now - frame_clock > Settings.FrameTime) diff --git a/snes9x.h b/snes9x.h index e8d890e9..c4fe5e15 100644 --- a/snes9x.h +++ b/snes9x.h @@ -61,6 +61,10 @@ #define NTSC_MASTER_CLOCK 21477272.727272 // 21477272 + 8/11 exact #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_PAL_VCOUNTER 312 diff --git a/vulkan/std_chrono_throttle.cpp b/vulkan/std_chrono_throttle.cpp index cbad37c0..86b95540 100644 --- a/vulkan/std_chrono_throttle.cpp +++ b/vulkan/std_chrono_throttle.cpp @@ -25,6 +25,20 @@ void Throttle::wait_for_frame() 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() { then += frame_duration_us; diff --git a/vulkan/std_chrono_throttle.hpp b/vulkan/std_chrono_throttle.hpp index 2c64e627..cf164173 100644 --- a/vulkan/std_chrono_throttle.hpp +++ b/vulkan/std_chrono_throttle.hpp @@ -8,6 +8,7 @@ struct Throttle void advance(); void reset(); void wait_for_frame(); + void wait_for_frame_and_rebase_time(); std::chrono::microseconds remaining(); double max_frame_rate = 0.0; double frame_duration = 0.0; diff --git a/vulkan/vulkan_shader_chain.cpp b/vulkan/vulkan_shader_chain.cpp index 7ea08a02..de5c0e08 100644 --- a/vulkan/vulkan_shader_chain.cpp +++ b/vulkan/vulkan_shader_chain.cpp @@ -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) +{ + 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()) 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; } } - - context->swapchain->end_frame(); + context->swapchain->end_frame_without_swap(); last_frame_index = current_frame_index; frame_count++; diff --git a/vulkan/vulkan_shader_chain.hpp b/vulkan/vulkan_shader_chain.hpp index ca835211..5311d854 100644 --- a/vulkan/vulkan_shader_chain.hpp +++ b/vulkan/vulkan_shader_chain.hpp @@ -20,6 +20,7 @@ class ShaderChain void update_and_propagate_sizes(int original_width_, int original_height_, int viewport_width_, int viewport_height_); 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_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(vk::CommandBuffer cmd, uint8_t *data, int width, int height, int stride, vk::Format format); void construct_buffer_objects(); diff --git a/vulkan/vulkan_simple_output.cpp b/vulkan/vulkan_simple_output.cpp index b3e2d257..2971cbd0 100644 --- a/vulkan/vulkan_simple_output.cpp +++ b/vulkan/vulkan_simple_output.cpp @@ -215,7 +215,7 @@ void SimpleOutput::set_filter(bool 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) 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); 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 \ No newline at end of file diff --git a/vulkan/vulkan_simple_output.hpp b/vulkan/vulkan_simple_output.hpp index 77de954b..58993427 100644 --- a/vulkan/vulkan_simple_output.hpp +++ b/vulkan/vulkan_simple_output.hpp @@ -11,6 +11,7 @@ class SimpleOutput SimpleOutput(Vulkan::Context *context, vk::Format format); ~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_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); private: diff --git a/vulkan/vulkan_swapchain.cpp b/vulkan/vulkan_swapchain.cpp index 4c5b32ef..329361b7 100644 --- a/vulkan/vulkan_swapchain.cpp +++ b/vulkan/vulkan_swapchain.cpp @@ -231,12 +231,7 @@ 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() +void Swapchain::end_frame_without_swap() { auto &frame = frames[current_frame]; frame.command_buffer->end(); @@ -249,25 +244,15 @@ bool Swapchain::end_frame() frame.complete.get()); queue.submit(submit_info, frame.fence.get()); +} +bool Swapchain::swap() +{ auto present_info = vk::PresentInfoKHR{} .setWaitSemaphores(frames[current_frame].complete.get()) .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; @@ -277,6 +262,12 @@ bool Swapchain::end_frame() return true; } +bool Swapchain::end_frame() +{ + end_frame_without_swap(); + return swap(); +} + vk::Framebuffer Swapchain::get_framebuffer() { return imageviewfbs[current_swapchain_image].framebuffer.get(); diff --git a/vulkan/vulkan_swapchain.hpp b/vulkan/vulkan_swapchain.hpp index d1a2a566..ca1c054a 100644 --- a/vulkan/vulkan_swapchain.hpp +++ b/vulkan/vulkan_swapchain.hpp @@ -3,7 +3,6 @@ #include "vulkan/vulkan.hpp" #include "vulkan/vulkan_handles.hpp" #include "vulkan/vulkan_structs.hpp" -#include "std_chrono_throttle.hpp" namespace Vulkan { @@ -26,9 +25,10 @@ class Swapchain void end_render_pass(); bool wait_on_frame(int frame_num); bool end_frame(); + void end_frame_without_swap(); + bool swap(); // 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(); @@ -62,7 +62,6 @@ 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;