Commit 3fe5739d authored by Angus Wang's avatar Angus Wang Committed by Alex Deucher

drm/amd/display: Add flip interval workaround

[WHY]
Some games experience low FPS issues when FreeSync is on and VSync is
toggled to half refresh rate.

[HOW]
First create a function to determine workaround conditions, which is
when we detect 2 or more VSync interrupts between flips and a very short
VSync to flip interval. We do the workaround during VSync interrupts and
set the v_total_max and min to nominal. We also cleanup after we exit
the game.
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Reviewed-by: default avatarAnthony Koo <Anthony.Koo@amd.com>
Reviewed-by: default avatarAric Cyr <Aric.Cyr@amd.com>
Acked-by: default avatarTom Chung <chiahsuan.chung@amd.com>
Signed-off-by: default avatarAngus Wang <Angus.Wang@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 35b2186b
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "dc.h" #include "dc.h"
#include "mod_freesync.h" #include "mod_freesync.h"
#include "core_types.h" #include "core_types.h"
#include "dm_services.h"
#define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32
...@@ -46,6 +47,10 @@ ...@@ -46,6 +47,10 @@
/* Number of consecutive frames to check before entering/exiting fixed refresh */ /* Number of consecutive frames to check before entering/exiting fixed refresh */
#define FIXED_REFRESH_ENTER_FRAME_COUNT 5 #define FIXED_REFRESH_ENTER_FRAME_COUNT 5
#define FIXED_REFRESH_EXIT_FRAME_COUNT 10 #define FIXED_REFRESH_EXIT_FRAME_COUNT 10
/* Flip interval workaround constants */
#define VSYNCS_BETWEEN_FLIP_THRESHOLD 2
#define FREESYNC_CONSEC_FLIP_AFTER_VSYNC 5
#define FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US 500
struct core_freesync { struct core_freesync {
struct mod_freesync public; struct mod_freesync public;
...@@ -466,6 +471,41 @@ static void apply_fixed_refresh(struct core_freesync *core_freesync, ...@@ -466,6 +471,41 @@ static void apply_fixed_refresh(struct core_freesync *core_freesync,
} }
} }
static void determine_flip_interval_workaround_req(struct mod_vrr_params *in_vrr,
unsigned int curr_time_stamp_in_us)
{
in_vrr->flip_interval.vsync_to_flip_in_us = curr_time_stamp_in_us -
in_vrr->flip_interval.v_update_timestamp_in_us;
/* Determine conditions for stopping workaround */
if (in_vrr->flip_interval.flip_interval_workaround_active &&
in_vrr->flip_interval.vsyncs_between_flip < VSYNCS_BETWEEN_FLIP_THRESHOLD &&
in_vrr->flip_interval.vsync_to_flip_in_us > FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) {
in_vrr->flip_interval.flip_interval_detect_counter = 0;
in_vrr->flip_interval.program_flip_interval_workaround = true;
in_vrr->flip_interval.flip_interval_workaround_active = false;
} else {
/* Determine conditions for starting workaround */
if (in_vrr->flip_interval.vsyncs_between_flip >= VSYNCS_BETWEEN_FLIP_THRESHOLD &&
in_vrr->flip_interval.vsync_to_flip_in_us < FREESYNC_VSYNC_TO_FLIP_DELTA_IN_US) {
/* Increase flip interval counter we have 2 vsyncs between flips and
* vsync to flip interval is less than 500us
*/
in_vrr->flip_interval.flip_interval_detect_counter++;
if (in_vrr->flip_interval.flip_interval_detect_counter > FREESYNC_CONSEC_FLIP_AFTER_VSYNC) {
/* Start workaround if we detect 5 consecutive instances of the above case */
in_vrr->flip_interval.program_flip_interval_workaround = true;
in_vrr->flip_interval.flip_interval_workaround_active = true;
}
} else {
/* Reset the flip interval counter if we condition is no longer met */
in_vrr->flip_interval.flip_interval_detect_counter = 0;
}
}
in_vrr->flip_interval.vsyncs_between_flip = 0;
}
static bool vrr_settings_require_update(struct core_freesync *core_freesync, static bool vrr_settings_require_update(struct core_freesync *core_freesync,
struct mod_freesync_config *in_config, struct mod_freesync_config *in_config,
unsigned int min_refresh_in_uhz, unsigned int min_refresh_in_uhz,
...@@ -1179,6 +1219,9 @@ void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync, ...@@ -1179,6 +1219,9 @@ void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync,
in_out_vrr); in_out_vrr);
} }
determine_flip_interval_workaround_req(in_out_vrr,
curr_time_stamp_in_us);
} }
} }
...@@ -1187,6 +1230,7 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, ...@@ -1187,6 +1230,7 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
struct mod_vrr_params *in_out_vrr) struct mod_vrr_params *in_out_vrr)
{ {
struct core_freesync *core_freesync = NULL; struct core_freesync *core_freesync = NULL;
unsigned int cur_timestamp_in_us;
if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL)) if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL))
return; return;
...@@ -1196,6 +1240,34 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, ...@@ -1196,6 +1240,34 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
if (in_out_vrr->supported == false) if (in_out_vrr->supported == false)
return; return;
cur_timestamp_in_us = dm_get_timestamp(core_freesync->dc->ctx)/10;
in_out_vrr->flip_interval.vsyncs_between_flip++;
in_out_vrr->flip_interval.v_update_timestamp_in_us = cur_timestamp_in_us;
if (in_out_vrr->state == VRR_STATE_ACTIVE_VARIABLE &&
(in_out_vrr->flip_interval.flip_interval_workaround_active ||
(!in_out_vrr->flip_interval.flip_interval_workaround_active &&
in_out_vrr->flip_interval.program_flip_interval_workaround))) {
// set freesync vmin vmax to nominal for workaround
in_out_vrr->adjust.v_total_min =
mod_freesync_calc_v_total_from_refresh(
stream, in_out_vrr->max_refresh_in_uhz);
in_out_vrr->adjust.v_total_max =
in_out_vrr->adjust.v_total_min;
in_out_vrr->flip_interval.program_flip_interval_workaround = false;
in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = true;
return;
}
if (in_out_vrr->state != VRR_STATE_ACTIVE_VARIABLE &&
in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup) {
in_out_vrr->flip_interval.do_flip_interval_workaround_cleanup = false;
in_out_vrr->flip_interval.flip_interval_detect_counter = 0;
in_out_vrr->flip_interval.vsyncs_between_flip = 0;
in_out_vrr->flip_interval.vsync_to_flip_in_us = 0;
}
/* Below the Range Logic */ /* Below the Range Logic */
/* Only execute if in fullscreen mode */ /* Only execute if in fullscreen mode */
...@@ -1302,7 +1374,7 @@ unsigned long long mod_freesync_calc_field_rate_from_timing( ...@@ -1302,7 +1374,7 @@ unsigned long long mod_freesync_calc_field_rate_from_timing(
bool mod_freesync_is_valid_range(uint32_t min_refresh_cap_in_uhz, bool mod_freesync_is_valid_range(uint32_t min_refresh_cap_in_uhz,
uint32_t max_refresh_cap_in_uhz, uint32_t max_refresh_cap_in_uhz,
uint32_t nominal_field_rate_in_uhz) uint32_t nominal_field_rate_in_uhz)
{ {
/* Typically nominal refresh calculated can have some fractional part. /* Typically nominal refresh calculated can have some fractional part.
......
...@@ -105,6 +105,16 @@ struct mod_vrr_params_fixed_refresh { ...@@ -105,6 +105,16 @@ struct mod_vrr_params_fixed_refresh {
uint32_t frame_counter; uint32_t frame_counter;
}; };
struct mod_vrr_params_flip_interval {
bool flip_interval_workaround_active;
bool program_flip_interval_workaround;
bool do_flip_interval_workaround_cleanup;
uint32_t flip_interval_detect_counter;
uint32_t vsyncs_between_flip;
uint32_t vsync_to_flip_in_us;
uint32_t v_update_timestamp_in_us;
};
struct mod_vrr_params { struct mod_vrr_params {
bool supported; bool supported;
bool send_info_frame; bool send_info_frame;
...@@ -121,6 +131,8 @@ struct mod_vrr_params { ...@@ -121,6 +131,8 @@ struct mod_vrr_params {
struct mod_vrr_params_fixed_refresh fixed; struct mod_vrr_params_fixed_refresh fixed;
struct mod_vrr_params_btr btr; struct mod_vrr_params_btr btr;
struct mod_vrr_params_flip_interval flip_interval;
}; };
struct mod_freesync *mod_freesync_create(struct dc *dc); struct mod_freesync *mod_freesync_create(struct dc *dc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment