From 3fe5739db488434bc0368577615ea7275b0f43a5 Mon Sep 17 00:00:00 2001 From: Angus Wang Date: Tue, 22 Mar 2022 15:37:15 -0400 Subject: 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: Daniel Wheeler Reviewed-by: Anthony Koo Reviewed-by: Aric Cyr Acked-by: Tom Chung Signed-off-by: Angus Wang Signed-off-by: Alex Deucher --- .../drm/amd/display/modules/freesync/freesync.c | 74 +++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/amd/display/modules/freesync') diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index bd1d1dc93629..d72566c6928a 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -29,6 +29,7 @@ #include "dc.h" #include "mod_freesync.h" #include "core_types.h" +#include "dm_services.h" #define MOD_FREESYNC_MAX_CONCURRENT_STREAMS 32 @@ -46,6 +47,10 @@ /* Number of consecutive frames to check before entering/exiting fixed refresh */ #define FIXED_REFRESH_ENTER_FRAME_COUNT 5 #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 mod_freesync public; @@ -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, struct mod_freesync_config *in_config, unsigned int min_refresh_in_uhz, @@ -1179,6 +1219,9 @@ void mod_freesync_handle_preflip(struct mod_freesync *mod_freesync, 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, struct mod_vrr_params *in_out_vrr) { struct core_freesync *core_freesync = NULL; + unsigned int cur_timestamp_in_us; if ((mod_freesync == NULL) || (stream == NULL) || (in_out_vrr == NULL)) return; @@ -1196,6 +1240,34 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync, if (in_out_vrr->supported == false) 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 */ /* Only execute if in fullscreen mode */ @@ -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, 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. -- cgit v1.2.3