mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-22 22:05:38 +00:00
System: Add "Skip Duplicate Frame Display" option
Skips the presentation/display of frames that are not unique. Can be combined with driver-level frame generation to increase perceptible frame rate. Can result in worse frame pacing, and is not compatible with syncing to host refresh.
This commit is contained in:
parent
7b8f9506c9
commit
5b4f74122c
|
@ -3454,6 +3454,14 @@ void FullscreenUI::DrawEmulationSettingsPage()
|
||||||
bsi, FSUI_ICONSTR(ICON_FA_STOPWATCH_20, "Reduce Input Latency"),
|
bsi, FSUI_ICONSTR(ICON_FA_STOPWATCH_20, "Reduce Input Latency"),
|
||||||
FSUI_CSTR("Reduces input latency by delaying the start of frame until closer to the presentation time."), "Display",
|
FSUI_CSTR("Reduces input latency by delaying the start of frame until closer to the presentation time."), "Display",
|
||||||
"PreFrameSleep", false, optimal_frame_pacing_active);
|
"PreFrameSleep", false, optimal_frame_pacing_active);
|
||||||
|
|
||||||
|
DrawToggleSetting(
|
||||||
|
bsi, FSUI_ICONSTR(ICON_FA_CHARGING_STATION, "Skip Duplicate Frame Display"),
|
||||||
|
FSUI_CSTR("Skips the presentation/display of frames that are not unique. Can result in worse frame pacing."),
|
||||||
|
"Display", "SkipPresentingDuplicateFrames", false,
|
||||||
|
!(GetEffectiveBoolSetting(bsi, "Display", "VSync", false) &&
|
||||||
|
GetEffectiveBoolSetting(bsi, "Main", "SyncToHostRefreshRate", false)));
|
||||||
|
|
||||||
const bool pre_frame_sleep_active =
|
const bool pre_frame_sleep_active =
|
||||||
(optimal_frame_pacing_active && GetEffectiveBoolSetting(bsi, "Display", "PreFrameSleep", false));
|
(optimal_frame_pacing_active && GetEffectiveBoolSetting(bsi, "Display", "PreFrameSleep", false));
|
||||||
DrawFloatRangeSetting(
|
DrawFloatRangeSetting(
|
||||||
|
@ -7652,6 +7660,8 @@ TRANSLATE_NOOP("FullscreenUI", "Shows the number of frames (or v-syncs) displaye
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Simulates the CPU's instruction cache in the recompiler. Can help with games running too fast.");
|
TRANSLATE_NOOP("FullscreenUI", "Simulates the CPU's instruction cache in the recompiler. Can help with games running too fast.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Simulates the region check present in original, unmodified consoles.");
|
TRANSLATE_NOOP("FullscreenUI", "Simulates the region check present in original, unmodified consoles.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements.");
|
TRANSLATE_NOOP("FullscreenUI", "Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements.");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Skip Duplicate Frame Display");
|
||||||
|
TRANSLATE_NOOP("FullscreenUI", "Skips the presentation/display of frames that are not unique. Can result in worse frame pacing.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Slow Boot");
|
TRANSLATE_NOOP("FullscreenUI", "Slow Boot");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Smooths out blockyness between colour transitions in 24-bit content, usually FMVs. Only applies to the hardware renderers.");
|
TRANSLATE_NOOP("FullscreenUI", "Smooths out blockyness between colour transitions in 24-bit content, usually FMVs. Only applies to the hardware renderers.");
|
||||||
TRANSLATE_NOOP("FullscreenUI", "Smooths out the blockiness of magnified textures on 3D objects.");
|
TRANSLATE_NOOP("FullscreenUI", "Smooths out the blockiness of magnified textures on 3D objects.");
|
||||||
|
|
|
@ -271,6 +271,7 @@ void Settings::Load(SettingsInterface& si)
|
||||||
display_pre_frame_sleep = si.GetBoolValue("Display", "PreFrameSleep", false);
|
display_pre_frame_sleep = si.GetBoolValue("Display", "PreFrameSleep", false);
|
||||||
display_pre_frame_sleep_buffer =
|
display_pre_frame_sleep_buffer =
|
||||||
si.GetFloatValue("Display", "PreFrameSleepBuffer", DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER);
|
si.GetFloatValue("Display", "PreFrameSleepBuffer", DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER);
|
||||||
|
display_skip_presenting_duplicate_frames = si.GetBoolValue("Display", "SkipPresentingDuplicateFrames", false);
|
||||||
display_vsync = si.GetBoolValue("Display", "VSync", false);
|
display_vsync = si.GetBoolValue("Display", "VSync", false);
|
||||||
display_force_4_3_for_24bit = si.GetBoolValue("Display", "Force4_3For24Bit", false);
|
display_force_4_3_for_24bit = si.GetBoolValue("Display", "Force4_3For24Bit", false);
|
||||||
display_active_start_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveStartOffset", 0));
|
display_active_start_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveStartOffset", 0));
|
||||||
|
@ -519,6 +520,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
|
||||||
si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling));
|
si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling));
|
||||||
si.SetBoolValue("Display", "OptimalFramePacing", display_optimal_frame_pacing);
|
si.SetBoolValue("Display", "OptimalFramePacing", display_optimal_frame_pacing);
|
||||||
si.SetBoolValue("Display", "PreFrameSleep", display_pre_frame_sleep);
|
si.SetBoolValue("Display", "PreFrameSleep", display_pre_frame_sleep);
|
||||||
|
si.SetBoolValue("Display", "SkipPresentingDuplicateFrames", display_skip_presenting_duplicate_frames);
|
||||||
si.SetFloatValue("Display", "PreFrameSleepBuffer", display_pre_frame_sleep_buffer);
|
si.SetFloatValue("Display", "PreFrameSleepBuffer", display_pre_frame_sleep_buffer);
|
||||||
si.SetBoolValue("Display", "VSync", display_vsync);
|
si.SetBoolValue("Display", "VSync", display_vsync);
|
||||||
si.SetStringValue("Display", "ExclusiveFullscreenControl",
|
si.SetStringValue("Display", "ExclusiveFullscreenControl",
|
||||||
|
|
|
@ -148,6 +148,7 @@ struct Settings
|
||||||
s8 display_line_end_offset = 0;
|
s8 display_line_end_offset = 0;
|
||||||
bool display_optimal_frame_pacing : 1 = false;
|
bool display_optimal_frame_pacing : 1 = false;
|
||||||
bool display_pre_frame_sleep : 1 = false;
|
bool display_pre_frame_sleep : 1 = false;
|
||||||
|
bool display_skip_presenting_duplicate_frames : 1 = false;
|
||||||
bool display_vsync : 1 = false;
|
bool display_vsync : 1 = false;
|
||||||
bool display_force_4_3_for_24bit : 1 = false;
|
bool display_force_4_3_for_24bit : 1 = false;
|
||||||
bool gpu_24bit_chroma_smoothing : 1 = false;
|
bool gpu_24bit_chroma_smoothing : 1 = false;
|
||||||
|
|
|
@ -150,6 +150,7 @@ static void PollDiscordPresence();
|
||||||
|
|
||||||
static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f;
|
static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f;
|
||||||
static constexpr const char FALLBACK_EXE_NAME[] = "PSX.EXE";
|
static constexpr const char FALLBACK_EXE_NAME[] = "PSX.EXE";
|
||||||
|
static constexpr u32 MAX_SKIPPED_FRAME_COUNT = 2; // 20fps minimum
|
||||||
|
|
||||||
static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
|
static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
|
||||||
static std::unique_ptr<INISettingsInterface> s_input_settings_interface;
|
static std::unique_ptr<INISettingsInterface> s_input_settings_interface;
|
||||||
|
@ -183,7 +184,10 @@ static bool s_throttler_enabled = false;
|
||||||
static bool s_optimal_frame_pacing = false;
|
static bool s_optimal_frame_pacing = false;
|
||||||
static bool s_pre_frame_sleep = false;
|
static bool s_pre_frame_sleep = false;
|
||||||
static bool s_syncing_to_host = false;
|
static bool s_syncing_to_host = false;
|
||||||
static bool s_last_frame_skipped = false;
|
static bool s_syncing_to_host_with_vsync = false;
|
||||||
|
static bool s_skip_presenting_duplicate_frames = false;
|
||||||
|
static u32 s_skipped_frame_count = 0;
|
||||||
|
static u32 s_last_presented_internal_frame_number = 0;
|
||||||
|
|
||||||
static float s_throttle_frequency = 0.0f;
|
static float s_throttle_frequency = 0.0f;
|
||||||
static float s_target_speed = 0.0f;
|
static float s_target_speed = 0.0f;
|
||||||
|
@ -1963,32 +1967,43 @@ void System::FrameDone()
|
||||||
AccumulatePreFrameSleepTime();
|
AccumulatePreFrameSleepTime();
|
||||||
|
|
||||||
// explicit present (frame pacing)
|
// explicit present (frame pacing)
|
||||||
if (current_time < s_next_frame_time || s_syncing_to_host || s_optimal_frame_pacing || s_last_frame_skipped)
|
const bool is_unique_frame = (s_last_presented_internal_frame_number != s_internal_frame_number);
|
||||||
|
s_last_presented_internal_frame_number = s_internal_frame_number;
|
||||||
|
|
||||||
|
const bool skip_this_frame =
|
||||||
|
(((s_skip_presenting_duplicate_frames && !is_unique_frame) ||
|
||||||
|
(!s_optimal_frame_pacing && (current_time > s_next_frame_time || g_gpu_device->ShouldSkipDisplayingFrame()))) &&
|
||||||
|
!s_syncing_to_host_with_vsync && (s_skipped_frame_count < MAX_SKIPPED_FRAME_COUNT) && !IsExecutionInterrupted());
|
||||||
|
if (!skip_this_frame)
|
||||||
{
|
{
|
||||||
|
s_skipped_frame_count = 0;
|
||||||
|
|
||||||
const bool throttle_before_present = (s_optimal_frame_pacing && s_throttler_enabled && !IsExecutionInterrupted());
|
const bool throttle_before_present = (s_optimal_frame_pacing && s_throttler_enabled && !IsExecutionInterrupted());
|
||||||
const bool explicit_present = (throttle_before_present && g_gpu_device->GetFeatures().explicit_present);
|
const bool explicit_present = (throttle_before_present && g_gpu_device->GetFeatures().explicit_present);
|
||||||
if (explicit_present)
|
if (explicit_present)
|
||||||
{
|
{
|
||||||
s_last_frame_skipped = !PresentDisplay(!throttle_before_present, true);
|
const bool do_present = PresentDisplay(false, true);
|
||||||
Throttle(current_time);
|
Throttle(current_time);
|
||||||
g_gpu_device->SubmitPresent();
|
if (do_present)
|
||||||
|
g_gpu_device->SubmitPresent();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (throttle_before_present)
|
if (throttle_before_present)
|
||||||
Throttle(current_time);
|
Throttle(current_time);
|
||||||
|
|
||||||
s_last_frame_skipped = !PresentDisplay(!throttle_before_present, false);
|
PresentDisplay(false, false);
|
||||||
|
|
||||||
if (!throttle_before_present && s_throttler_enabled && !IsExecutionInterrupted())
|
if (!throttle_before_present && s_throttler_enabled && !IsExecutionInterrupted())
|
||||||
Throttle(current_time);
|
Throttle(current_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (current_time >= s_next_frame_time)
|
else
|
||||||
{
|
{
|
||||||
Log_DebugPrintf("Skipping displaying frame");
|
Log_DebugPrint("Skipping displaying frame");
|
||||||
s_last_frame_skipped = true;
|
s_skipped_frame_count++;
|
||||||
Throttle(current_time);
|
if (s_throttler_enabled)
|
||||||
|
Throttle(current_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pre-frame sleep (input lag reduction)
|
// pre-frame sleep (input lag reduction)
|
||||||
|
@ -2810,15 +2825,20 @@ void System::FormatLatencyStats(SmallStringBase& str)
|
||||||
|
|
||||||
void System::UpdateSpeedLimiterState()
|
void System::UpdateSpeedLimiterState()
|
||||||
{
|
{
|
||||||
|
DebugAssert(IsValid());
|
||||||
|
|
||||||
const float old_target_speed = s_target_speed;
|
const float old_target_speed = s_target_speed;
|
||||||
s_target_speed = s_turbo_enabled ?
|
s_target_speed = s_turbo_enabled ?
|
||||||
g_settings.turbo_speed :
|
g_settings.turbo_speed :
|
||||||
(s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
|
(s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
|
||||||
s_throttler_enabled = (s_target_speed != 0.0f);
|
s_throttler_enabled = (s_target_speed != 0.0f);
|
||||||
s_optimal_frame_pacing = s_throttler_enabled && g_settings.display_optimal_frame_pacing;
|
s_optimal_frame_pacing = (s_throttler_enabled && g_settings.display_optimal_frame_pacing) ||
|
||||||
s_pre_frame_sleep = s_throttler_enabled && g_settings.display_pre_frame_sleep;
|
g_gpu_device->GetWindowInfo().IsSurfaceless(); // surfaceless check for regtest
|
||||||
|
s_skip_presenting_duplicate_frames = s_throttler_enabled && g_settings.display_skip_presenting_duplicate_frames;
|
||||||
|
s_pre_frame_sleep = s_optimal_frame_pacing && g_settings.display_pre_frame_sleep;
|
||||||
|
|
||||||
s_syncing_to_host = false;
|
s_syncing_to_host = false;
|
||||||
|
s_syncing_to_host_with_vsync = false;
|
||||||
if (g_settings.sync_to_host_refresh_rate &&
|
if (g_settings.sync_to_host_refresh_rate &&
|
||||||
(g_settings.audio_stream_parameters.stretch_mode != AudioStretchMode::Off) && s_target_speed == 1.0f && IsValid())
|
(g_settings.audio_stream_parameters.stretch_mode != AudioStretchMode::Off) && s_target_speed == 1.0f && IsValid())
|
||||||
{
|
{
|
||||||
|
@ -2835,7 +2855,8 @@ void System::UpdateSpeedLimiterState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// When syncing to host and using vsync, we don't need to sleep.
|
// When syncing to host and using vsync, we don't need to sleep.
|
||||||
if (s_syncing_to_host && IsVSyncEffectivelyEnabled())
|
s_syncing_to_host_with_vsync = (s_syncing_to_host && IsVSyncEffectivelyEnabled());
|
||||||
|
if (s_syncing_to_host_with_vsync)
|
||||||
{
|
{
|
||||||
Log_InfoPrintf("Using host vsync for throttling.");
|
Log_InfoPrintf("Using host vsync for throttling.");
|
||||||
s_throttler_enabled = false;
|
s_throttler_enabled = false;
|
||||||
|
@ -2843,25 +2864,21 @@ void System::UpdateSpeedLimiterState()
|
||||||
|
|
||||||
Log_VerbosePrintf("Target speed: %f%%", s_target_speed * 100.0f);
|
Log_VerbosePrintf("Target speed: %f%%", s_target_speed * 100.0f);
|
||||||
|
|
||||||
if (IsValid())
|
// Update audio output.
|
||||||
{
|
AudioStream* stream = SPU::GetOutputStream();
|
||||||
// Update audio output.
|
stream->SetOutputVolume(GetAudioOutputVolume());
|
||||||
AudioStream* stream = SPU::GetOutputStream();
|
|
||||||
stream->SetOutputVolume(GetAudioOutputVolume());
|
|
||||||
|
|
||||||
// Adjust nominal rate when resampling, or syncing to host.
|
// Adjust nominal rate when resampling, or syncing to host.
|
||||||
const bool rate_adjust =
|
const bool rate_adjust =
|
||||||
(s_syncing_to_host || g_settings.audio_stream_parameters.stretch_mode == AudioStretchMode::Resample) &&
|
(s_syncing_to_host || g_settings.audio_stream_parameters.stretch_mode == AudioStretchMode::Resample) &&
|
||||||
s_target_speed > 0.0f;
|
s_target_speed > 0.0f;
|
||||||
stream->SetNominalRate(rate_adjust ? s_target_speed : 1.0f);
|
stream->SetNominalRate(rate_adjust ? s_target_speed : 1.0f);
|
||||||
|
|
||||||
if (old_target_speed < s_target_speed)
|
if (old_target_speed < s_target_speed)
|
||||||
stream->UpdateTargetTempo(s_target_speed);
|
stream->UpdateTargetTempo(s_target_speed);
|
||||||
|
|
||||||
UpdateThrottlePeriod();
|
|
||||||
ResetThrottler();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
UpdateThrottlePeriod();
|
||||||
|
ResetThrottler();
|
||||||
UpdateDisplaySync();
|
UpdateDisplaySync();
|
||||||
|
|
||||||
if (g_settings.increase_timer_resolution)
|
if (g_settings.increase_timer_resolution)
|
||||||
|
@ -3959,6 +3976,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
|
||||||
g_settings.fast_forward_speed != old_settings.fast_forward_speed ||
|
g_settings.fast_forward_speed != old_settings.fast_forward_speed ||
|
||||||
g_settings.display_max_fps != old_settings.display_max_fps ||
|
g_settings.display_max_fps != old_settings.display_max_fps ||
|
||||||
g_settings.display_optimal_frame_pacing != old_settings.display_optimal_frame_pacing ||
|
g_settings.display_optimal_frame_pacing != old_settings.display_optimal_frame_pacing ||
|
||||||
|
g_settings.display_skip_presenting_duplicate_frames != old_settings.display_skip_presenting_duplicate_frames ||
|
||||||
g_settings.display_pre_frame_sleep != old_settings.display_pre_frame_sleep ||
|
g_settings.display_pre_frame_sleep != old_settings.display_pre_frame_sleep ||
|
||||||
g_settings.display_pre_frame_sleep_buffer != old_settings.display_pre_frame_sleep_buffer ||
|
g_settings.display_pre_frame_sleep_buffer != old_settings.display_pre_frame_sleep_buffer ||
|
||||||
g_settings.display_vsync != old_settings.display_vsync ||
|
g_settings.display_vsync != old_settings.display_vsync ||
|
||||||
|
@ -5082,10 +5100,8 @@ void System::HostDisplayResized()
|
||||||
g_gpu->UpdateResolutionScale();
|
g_gpu->UpdateResolutionScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::PresentDisplay(bool allow_skip_present, bool explicit_present)
|
bool System::PresentDisplay(bool skip_present, bool explicit_present)
|
||||||
{
|
{
|
||||||
const bool skip_present = allow_skip_present && g_gpu_device->ShouldSkipDisplayingFrame();
|
|
||||||
|
|
||||||
Host::BeginPresentFrame();
|
Host::BeginPresentFrame();
|
||||||
|
|
||||||
// acquire for IO.MousePos.
|
// acquire for IO.MousePos.
|
||||||
|
|
|
@ -478,7 +478,7 @@ void RequestDisplaySize(float scale = 0.0f);
|
||||||
void HostDisplayResized();
|
void HostDisplayResized();
|
||||||
|
|
||||||
/// Renders the display.
|
/// Renders the display.
|
||||||
bool PresentDisplay(bool allow_skip_present, bool explicit_present);
|
bool PresentDisplay(bool skip_present, bool explicit_present);
|
||||||
void InvalidateDisplay();
|
void InvalidateDisplay();
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -20,6 +20,8 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToHostRefreshRate, "Main", "SyncToHostRefreshRate", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToHostRefreshRate, "Main", "SyncToHostRefreshRate", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.optimalFramePacing, "Display", "OptimalFramePacing", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.optimalFramePacing, "Display", "OptimalFramePacing", false);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preFrameSleep, "Display", "PreFrameSleep", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preFrameSleep, "Display", "PreFrameSleep", false);
|
||||||
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.skipPresentingDuplicateFrames, "Display",
|
||||||
|
"SkipPresentingDuplicateFrames", false);
|
||||||
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.preFrameSleepBuffer, "Display", "PreFrameSleepBuffer",
|
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.preFrameSleepBuffer, "Display", "PreFrameSleepBuffer",
|
||||||
Settings::DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER);
|
Settings::DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER);
|
||||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.rewindEnable, "Main", "RewindEnable", false);
|
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.rewindEnable, "Main", "RewindEnable", false);
|
||||||
|
@ -71,7 +73,9 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
||||||
}
|
}
|
||||||
connect(m_ui.turboSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
connect(m_ui.turboSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||||
&EmulationSettingsWidget::onTurboSpeedIndexChanged);
|
&EmulationSettingsWidget::onTurboSpeedIndexChanged);
|
||||||
connect(m_ui.vsync, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::onVSyncChanged);
|
connect(m_ui.vsync, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::updateSkipDuplicateFramesEnabled);
|
||||||
|
connect(m_ui.syncToHostRefreshRate, &QCheckBox::checkStateChanged, this,
|
||||||
|
&EmulationSettingsWidget::updateSkipDuplicateFramesEnabled);
|
||||||
connect(m_ui.optimalFramePacing, &QCheckBox::checkStateChanged, this,
|
connect(m_ui.optimalFramePacing, &QCheckBox::checkStateChanged, this,
|
||||||
&EmulationSettingsWidget::onOptimalFramePacingChanged);
|
&EmulationSettingsWidget::onOptimalFramePacingChanged);
|
||||||
connect(m_ui.preFrameSleep, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::onPreFrameSleepChanged);
|
connect(m_ui.preFrameSleep, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::onPreFrameSleepChanged);
|
||||||
|
@ -121,6 +125,11 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
||||||
tr("Specifies the amount of buffer time added, which reduces the additional sleep time "
|
tr("Specifies the amount of buffer time added, which reduces the additional sleep time "
|
||||||
"introduced. Higher values increase input latency, but decrease the risk of overrun, "
|
"introduced. Higher values increase input latency, but decrease the risk of overrun, "
|
||||||
"or missed frames. Lower values require faster hardware."));
|
"or missed frames. Lower values require faster hardware."));
|
||||||
|
dialog->registerWidgetHelp(
|
||||||
|
m_ui.skipPresentingDuplicateFrames, tr("Skip Duplicate Frame Display"), tr("Unchecked"),
|
||||||
|
tr("Skips the presentation/display of frames that are not unique. Can be combined with driver-level frame "
|
||||||
|
"generation to increase perceptible frame rate. Can result in worse frame pacing, and is not compatible with "
|
||||||
|
"syncing to host refresh."));
|
||||||
dialog->registerWidgetHelp(
|
dialog->registerWidgetHelp(
|
||||||
m_ui.rewindEnable, tr("Rewinding"), tr("Unchecked"),
|
m_ui.rewindEnable, tr("Rewinding"), tr("Unchecked"),
|
||||||
tr("<b>Enable Rewinding:</b> Saves state periodically so you can rewind any mistakes while playing.<br> "
|
tr("<b>Enable Rewinding:</b> Saves state periodically so you can rewind any mistakes while playing.<br> "
|
||||||
|
@ -133,8 +142,8 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
||||||
tr(
|
tr(
|
||||||
"Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements."));
|
"Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements."));
|
||||||
|
|
||||||
onVSyncChanged();
|
|
||||||
onOptimalFramePacingChanged();
|
onOptimalFramePacingChanged();
|
||||||
|
updateSkipDuplicateFramesEnabled();
|
||||||
updateRewind();
|
updateRewind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,12 +209,6 @@ void EmulationSettingsWidget::onTurboSpeedIndexChanged(int index)
|
||||||
m_dialog->setFloatSettingValue("Main", "TurboSpeed", okay ? value : 0.0f);
|
m_dialog->setFloatSettingValue("Main", "TurboSpeed", okay ? value : 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulationSettingsWidget::onVSyncChanged()
|
|
||||||
{
|
|
||||||
const bool vsync = m_dialog->getEffectiveBoolValue("Display", "VSync", false);
|
|
||||||
m_ui.syncToHostRefreshRate->setEnabled(vsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EmulationSettingsWidget::onOptimalFramePacingChanged()
|
void EmulationSettingsWidget::onOptimalFramePacingChanged()
|
||||||
{
|
{
|
||||||
const bool optimal_frame_pacing_enabled = m_dialog->getEffectiveBoolValue("Display", "OptimalFramePacing", false);
|
const bool optimal_frame_pacing_enabled = m_dialog->getEffectiveBoolValue("Display", "OptimalFramePacing", false);
|
||||||
|
@ -221,6 +224,13 @@ void EmulationSettingsWidget::onPreFrameSleepChanged()
|
||||||
m_ui.preFrameSleepBufferLabel->setVisible(show_buffer_size);
|
m_ui.preFrameSleepBufferLabel->setVisible(show_buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmulationSettingsWidget::updateSkipDuplicateFramesEnabled()
|
||||||
|
{
|
||||||
|
const bool vsync = m_dialog->getEffectiveBoolValue("Display", "VSync", false);
|
||||||
|
const bool sync_to_host = m_dialog->getEffectiveBoolValue("Main", "SyncToHostRefreshRate", false) && vsync;
|
||||||
|
m_ui.skipPresentingDuplicateFrames->setEnabled(!sync_to_host);
|
||||||
|
}
|
||||||
|
|
||||||
void EmulationSettingsWidget::updateRewind()
|
void EmulationSettingsWidget::updateRewind()
|
||||||
{
|
{
|
||||||
const bool rewind_enabled = m_dialog->getEffectiveBoolValue("Main", "RewindEnable", false);
|
const bool rewind_enabled = m_dialog->getEffectiveBoolValue("Main", "RewindEnable", false);
|
||||||
|
|
|
@ -21,9 +21,9 @@ private Q_SLOTS:
|
||||||
void onEmulationSpeedIndexChanged(int index);
|
void onEmulationSpeedIndexChanged(int index);
|
||||||
void onFastForwardSpeedIndexChanged(int index);
|
void onFastForwardSpeedIndexChanged(int index);
|
||||||
void onTurboSpeedIndexChanged(int index);
|
void onTurboSpeedIndexChanged(int index);
|
||||||
void onVSyncChanged();
|
|
||||||
void onOptimalFramePacingChanged();
|
void onOptimalFramePacingChanged();
|
||||||
void onPreFrameSleepChanged();
|
void onPreFrameSleepChanged();
|
||||||
|
void updateSkipDuplicateFramesEnabled();
|
||||||
void updateRewind();
|
void updateRewind();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -68,10 +68,10 @@
|
||||||
<string>Latency Control</string>
|
<string>Latency Control</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<item row="0" column="0">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="vsync">
|
<widget class="QCheckBox" name="preFrameSleep">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Vertical Sync (VSync)</string>
|
<string>Reduce Input Latency</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -82,6 +82,13 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="vsync">
|
||||||
|
<property name="text">
|
||||||
|
<string>Vertical Sync (VSync)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="optimalFramePacing">
|
<widget class="QCheckBox" name="optimalFramePacing">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -89,23 +96,23 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="2" column="0">
|
||||||
<widget class="QCheckBox" name="preFrameSleep">
|
<widget class="QCheckBox" name="skipPresentingDuplicateFrames">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Reduce Input Latency</string>
|
<string>Skip Duplicate Frame Display</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="2">
|
<item row="2" column="1">
|
||||||
<layout class="QHBoxLayout" name="preFrameSleepBufferLayout" stretch="0,1">
|
<layout class="QFormLayout" name="formLayout_3">
|
||||||
<item>
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="preFrameSleepBufferLabel">
|
<widget class="QLabel" name="preFrameSleepBufferLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Frame Time Buffer:</string>
|
<string>Frame Time Buffer:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="0" column="1">
|
||||||
<widget class="QDoubleSpinBox" name="preFrameSleepBuffer">
|
<widget class="QDoubleSpinBox" name="preFrameSleepBuffer">
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string> Milliseconds</string>
|
<string> Milliseconds</string>
|
||||||
|
|
Loading…
Reference in a new issue