diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index a660ae927b..09159e5e95 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -186,6 +186,9 @@ void GLGSRender::begin() { rsx::thread::begin(); + if (skip_frame) + return; + init_buffers(); if (!draw_fbo.check()) @@ -319,7 +322,7 @@ namespace void GLGSRender::end() { - if (!draw_fbo || !draw_fbo.check()) + if (skip_frame || !draw_fbo || !draw_fbo.check()) { rsx::thread::end(); return; @@ -688,13 +691,9 @@ void GLGSRender::on_exit() void GLGSRender::clear_surface(u32 arg) { + if (skip_frame) return; if (rsx::method_registers.surface_color_target() == rsx::surface_target::none) return; - - if ((arg & 0xf3) == 0) - { - //do nothing - return; - } + if ((arg & 0xf3) == 0) return; GLbitfield mask = 0; @@ -864,6 +863,23 @@ bool GLGSRender::load_program() void GLGSRender::flip(int buffer) { + if (skip_frame) + { + m_frame->flip(m_context, true); + rsx::thread::flip(buffer); + + if (!skip_frame) + { + m_draw_calls = 0; + m_begin_time = 0; + m_draw_time = 0; + m_vertex_upload_time = 0; + m_textures_upload_time = 0; + } + + return; + } + u32 buffer_width = gcm_buffers[buffer].width; u32 buffer_height = gcm_buffers[buffer].height; u32 buffer_pitch = gcm_buffers[buffer].pitch; @@ -963,24 +979,26 @@ void GLGSRender::flip(int buffer) } m_frame->flip(m_context); + rsx::thread::flip(buffer); + + m_gl_texture_cache.clear_temporary_surfaces(); + + for (auto &tex : m_rtts.invalidated_resources) + tex->remove(); + + m_rtts.invalidated_resources.clear(); + + if (g_cfg.video.invalidate_surface_cache_every_frame) + m_rtts.invalidate_surface_cache_data(nullptr); + + //If we are skipping the next frame, fo not reset perf counters + if (skip_frame) return; m_draw_calls = 0; m_begin_time = 0; m_draw_time = 0; m_vertex_upload_time = 0; m_textures_upload_time = 0; - - m_gl_texture_cache.clear_temporary_surfaces(); - - for (auto &tex : m_rtts.invalidated_resources) - { - tex->remove(); - } - - m_rtts.invalidated_resources.clear(); - - if (g_cfg.video.invalidate_surface_cache_every_frame) - m_rtts.invalidate_surface_cache_data(nullptr); } diff --git a/rpcs3/Emu/RSX/GSRender.h b/rpcs3/Emu/RSX/GSRender.h index 926980d07d..7973281f93 100644 --- a/rpcs3/Emu/RSX/GSRender.h +++ b/rpcs3/Emu/RSX/GSRender.h @@ -36,7 +36,7 @@ public: draw_context_t new_context(); virtual void set_current(draw_context_t ctx) = 0; - virtual void flip(draw_context_t ctx) = 0; + virtual void flip(draw_context_t ctx, bool skip_frame=false) = 0; virtual int client_width() = 0; virtual int client_height() = 0; diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index dba396aad5..98a37465e4 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1228,4 +1228,17 @@ namespace rsx return (m_vertex_streaming_task.remaining_packets == 0 && m_vertex_streaming_task.ready_threads == 0); } + + void thread::flip(int buffer) + { + if (g_cfg.video.frame_skip_enabled) + { + m_skip_frame_ctr++; + + if (m_skip_frame_ctr == g_cfg.video.consequtive_frames_to_draw) + m_skip_frame_ctr = -g_cfg.video.consequtive_frames_to_skip; + + skip_frame = (m_skip_frame_ctr < 0); + } + } } diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 1af33228d8..9f3a12cdbf 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -151,6 +151,10 @@ namespace rsx bool m_textures_dirty[16]; bool m_vertex_attribs_changed; bool m_index_buffer_changed; + + protected: + s32 m_skip_frame_ctr = 0; + bool skip_frame = false; protected: std::array get_color_surface_addresses() const; u32 get_zeta_surface_address() const; diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 53bae2c53d..d653ff9fd2 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -795,6 +795,9 @@ void VKGSRender::begin() { rsx::thread::begin(); + if (skip_frame) + return; + //Ease resource pressure if the number of draw calls becomes too high or we are running low on memory resources if (m_used_descriptors >= DESCRIPTOR_MAX_DRAW_CALLS || m_attrib_ring_info.is_critical() || @@ -895,6 +898,12 @@ void VKGSRender::close_render_pass() void VKGSRender::end() { + if (skip_frame) + { + rsx::thread::end(); + return; + } + std::chrono::time_point program_start = steady_clock::now(); const bool is_instanced = is_probable_instanced_draw() && m_last_descriptor_set != VK_NULL_HANDLE && m_program != nullptr; @@ -1159,6 +1168,8 @@ void VKGSRender::on_exit() void VKGSRender::clear_surface(u32 mask) { + if (skip_frame) return; + // Ignore clear if surface target is set to CELL_GCM_SURFACE_TARGET_NONE if (rsx::method_registers.surface_color_target() == rsx::surface_target::none) return; @@ -1870,6 +1881,24 @@ void VKGSRender::prepare_rtts() void VKGSRender::flip(int buffer) { + if (skip_frame) + { + m_frame->flip(m_context); + rsx::thread::flip(buffer); + + if (!skip_frame) + { + m_draw_calls = 0; + m_instanced_draws = 0; + m_draw_time = 0; + m_setup_time = 0; + m_vertex_upload_time = 0; + m_textures_upload_time = 0; + } + + return; + } + bool resize_screen = false; if (m_client_height != m_frame->client_height() || @@ -2062,7 +2091,13 @@ void VKGSRender::flip(int buffer) m_attrib_ring_info.reset_allocation_stats(); m_texture_upload_buffer_ring_info.reset_allocation_stats(); - //Resource destruction is handled within the real swap handler + //NOTE:Resource destruction is handled within the real swap handler + + m_frame->flip(m_context); + rsx::thread::flip(buffer); + + //Do not reset perf counters if we are skipping the next frame + if (skip_frame) return; m_draw_calls = 0; m_instanced_draws = 0; @@ -2070,5 +2105,4 @@ void VKGSRender::flip(int buffer) m_setup_time = 0; m_vertex_upload_time = 0; m_textures_upload_time = 0; - m_frame->flip(m_context); } diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 3ee136c4e3..78d6260691 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -317,6 +317,10 @@ struct cfg_root : cfg::node cfg::_bool batch_instanced_geometry{this, "Batch Instanced Geometry", false}; cfg::_int<1, 16> vertex_upload_threads{ this, "Vertex Upload Threads", 1 }; + cfg::_bool frame_skip_enabled{this, "Enable Frame Skip"}; + cfg::_int<1, 8> consequtive_frames_to_draw{this, "Consequtive Frames Drawn", 1}; + cfg::_int<1, 8> consequtive_frames_to_skip{this, "Consequtive Frames Skept", 1}; + struct node_d3d12 : cfg::node { node_d3d12(cfg::node* _this) : cfg::node(_this, "D3D12") {} diff --git a/rpcs3/rpcs3qt/gl_gs_frame.cpp b/rpcs3/rpcs3qt/gl_gs_frame.cpp index 390944e43e..2ecccf23bc 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.cpp +++ b/rpcs3/rpcs3qt/gl_gs_frame.cpp @@ -45,10 +45,13 @@ void gl_gs_frame::delete_context(void* ctx) delete (QOpenGLContext*)ctx; } -void gl_gs_frame::flip(draw_context_t context) +void gl_gs_frame::flip(draw_context_t context, bool skip_frame) { gs_frame::flip(context); + //Do not swap buffers if frame skip is active + if (skip_frame) return; + ((QOpenGLContext*)context.get())->makeCurrent(this); ((QOpenGLContext*)context.get())->swapBuffers(this); } diff --git a/rpcs3/rpcs3qt/gl_gs_frame.h b/rpcs3/rpcs3qt/gl_gs_frame.h index ec71d71449..123c0d59a9 100644 --- a/rpcs3/rpcs3qt/gl_gs_frame.h +++ b/rpcs3/rpcs3qt/gl_gs_frame.h @@ -14,5 +14,5 @@ public: void* make_context() override; void set_current(draw_context_t context) override; void delete_context(void* context) override; - void flip(draw_context_t context) override; + void flip(draw_context_t context, bool skip_frame=false) override; }; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 7d46e736eb..13af21a7eb 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -151,7 +151,7 @@ int gs_frame::client_height() #endif } -void gs_frame::flip(draw_context_t) +void gs_frame::flip(draw_context_t, bool /*skip_frame*/) { QString title; diff --git a/rpcs3/rpcs3qt/gs_frame.h b/rpcs3/rpcs3qt/gs_frame.h index bf7b95ceb7..eaf711c4b6 100644 --- a/rpcs3/rpcs3qt/gs_frame.h +++ b/rpcs3/rpcs3qt/gs_frame.h @@ -35,7 +35,7 @@ protected: void* make_context() override; void set_current(draw_context_t context) override; void delete_context(void* context) override; - void flip(draw_context_t context) override; + void flip(draw_context_t context, bool skip_frame=false) override; int client_width() override; int client_height() override;