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 save(const char *filename) = 0;
virtual bool is_ready() = 0;
virtual bool can_throttle() { return false; };
protected:
Snes9xWindow *window;

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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,6 +28,7 @@ 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();
@ -40,6 +42,7 @@ class S9xVulkanDisplayDriver : public S9xDisplayDriver
Window xid;
int current_width;
int current_height;
Throttle throttle;
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
std::unique_ptr<WaylandSurface> wayland_surface;

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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;

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)
{
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++;

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_);
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();

View File

@ -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

View File

@ -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:

View File

@ -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();

View File

@ -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;