System: Add 'Reduce Input Latency' option

i.e. pre-frame sleep.
This commit is contained in:
Stenzek
2024-04-13 19:56:08 +10:00
parent 6258cb9e0e
commit c149d66d4d
12 changed files with 320 additions and 114 deletions

View File

@ -3350,32 +3350,62 @@ void FullscreenUI::DrawEmulationSettingsPage()
MenuHeading(FSUI_CSTR("Speed Control"));
DrawFloatListSetting(
bsi, FSUI_CSTR("Emulation Speed"),
bsi, FSUI_ICONSTR(ICON_FA_STOPWATCH, "Emulation Speed"),
FSUI_CSTR("Sets the target emulation speed. It is not guaranteed that this speed will be reached on all systems."),
"Main", "EmulationSpeed", 1.0f, emulation_speed_titles.data(), emulation_speed_values.data(),
emulation_speed_titles.size(), true);
DrawFloatListSetting(
bsi, FSUI_CSTR("Fast Forward Speed"),
bsi, FSUI_ICONSTR(ICON_FA_BOLT, "Fast Forward Speed"),
FSUI_CSTR("Sets the fast forward speed. It is not guaranteed that this speed will be reached on all systems."),
"Main", "FastForwardSpeed", 0.0f, emulation_speed_titles.data(), emulation_speed_values.data(),
emulation_speed_titles.size(), true);
DrawFloatListSetting(
bsi, FSUI_CSTR("Turbo Speed"),
bsi, FSUI_ICONSTR(ICON_FA_BOLT, "Turbo Speed"),
FSUI_CSTR("Sets the turbo speed. It is not guaranteed that this speed will be reached on all systems."), "Main",
"TurboSpeed", 2.0f, emulation_speed_titles.data(), emulation_speed_values.data(), emulation_speed_titles.size(),
true);
MenuHeading(FSUI_CSTR("Latency Control"));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_TV, "Vertical Sync (VSync)"),
FSUI_CSTR("Synchronizes presentation of the console's frames to the host. GSync/FreeSync users "
"should enable Optimal Frame Pacing instead."),
"Display", "VSync", false);
DrawToggleSetting(
bsi, FSUI_ICONSTR(ICON_FA_LIGHTBULB, "Sync To Host Refresh Rate"),
FSUI_CSTR("Adjusts the emulation speed so the console's refresh rate matches the host when VSync is enabled."),
"Main", "SyncToHostRefreshRate", false);
DrawToggleSetting(
bsi, FSUI_ICONSTR(ICON_FA_TACHOMETER_ALT, "Optimal Frame Pacing"),
FSUI_CSTR("Ensures every frame generated is displayed for optimal pacing. Enable for variable refresh displays, "
"such as GSync/FreeSync. Disable if you are having speed or sound issues."),
"Display", "OptimalFramePacing", false);
const bool optimal_frame_pacing_active = GetEffectiveBoolSetting(bsi, "Display", "OptimalFramePacing", false);
DrawToggleSetting(
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",
"PreFrameSleep", false, optimal_frame_pacing_active);
const bool pre_frame_sleep_active =
(optimal_frame_pacing_active && GetEffectiveBoolSetting(bsi, "Display", "PreFrameSleep", false));
DrawFloatRangeSetting(
bsi, FSUI_ICONSTR(ICON_FA_BATTERY_FULL, "Frame Time Buffer"),
FSUI_CSTR("Specifies the amount of buffer time added, which reduces the additional sleep time introduced."),
"Display", "PreFrameSleepBuffer", Settings::DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER, 0.0f, 20.0f, "%.1f", 1.0f,
pre_frame_sleep_active);
MenuHeading(FSUI_CSTR("Runahead/Rewind"));
DrawToggleSetting(bsi, FSUI_CSTR("Enable Rewinding"),
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_BACKWARD, "Enable Rewinding"),
FSUI_CSTR("Saves state periodically so you can rewind any mistakes while playing."), "Main",
"RewindEnable", false);
DrawFloatRangeSetting(
bsi, FSUI_CSTR("Rewind Save Frequency"),
bsi, FSUI_ICONSTR(ICON_FA_SAVE, "Rewind Save Frequency"),
FSUI_CSTR("How often a rewind state will be created. Higher frequencies have greater system requirements."), "Main",
"RewindFrequency", 10.0f, 0.0f, 3600.0f, FSUI_CSTR("%.2f Seconds"));
DrawIntRangeSetting(
bsi, FSUI_CSTR("Rewind Save Slots"),
bsi, FSUI_ICONSTR(ICON_FA_GLASS_WHISKEY, "Rewind Save Slots"),
FSUI_CSTR("How many saves will be kept for rewinding. Higher values have greater memory requirements."), "Main",
"RewindSaveSlots", 10, 1, 10000, FSUI_CSTR("%d Frames"));
@ -3389,7 +3419,7 @@ void FullscreenUI::DrawEmulationSettingsPage()
FSUI_NSTR("8 Frames"), FSUI_NSTR("9 Frames"), FSUI_NSTR("10 Frames")};
DrawIntListSetting(
bsi, FSUI_CSTR("Runahead"),
bsi, FSUI_ICONSTR(ICON_FA_RUNNING, "Runahead"),
FSUI_CSTR(
"Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements."),
"Main", "RunaheadFrameCount", 0, runahead_options.data(), runahead_options.size(), true);
@ -4184,23 +4214,6 @@ void FullscreenUI::DrawDisplaySettingsPage()
"GPU", "UseSoftwareRendererForReadbacks", false);
}
DrawToggleSetting(bsi, FSUI_CSTR("VSync"),
FSUI_CSTR("Synchronizes presentation of the console's frames to the host. GSync/FreeSync users "
"should enable Optimal Frame Pacing instead."),
"Display", "VSync", false);
DrawToggleSetting(
bsi, FSUI_CSTR("Sync To Host Refresh Rate"),
FSUI_CSTR("Adjusts the emulation speed so the console's refresh rate matches the host when VSync and Audio "
"Resampling are enabled."),
"Main", "SyncToHostRefreshRate", false);
DrawToggleSetting(
bsi, FSUI_CSTR("Optimal Frame Pacing"),
FSUI_CSTR("Ensures every frame generated is displayed for optimal pacing. Enable for variable refresh displays, "
"such as GSync/FreeSync. Disable if you are having speed or sound issues."),
"Display", "OptimalFramePacing", false);
MenuHeading(FSUI_CSTR("Rendering"));
DrawIntListSetting(
@ -6983,7 +6996,7 @@ TRANSLATE_NOOP("FullscreenUI", "Add Shader");
TRANSLATE_NOOP("FullscreenUI", "Adds a new directory to the game search list.");
TRANSLATE_NOOP("FullscreenUI", "Adds a new shader to the chain.");
TRANSLATE_NOOP("FullscreenUI", "Adds additional precision to PGXP data post-projection. May improve visuals in some games.");
TRANSLATE_NOOP("FullscreenUI", "Adjusts the emulation speed so the console's refresh rate matches the host when VSync and Audio Resampling are enabled.");
TRANSLATE_NOOP("FullscreenUI", "Adjusts the emulation speed so the console's refresh rate matches the host when VSync is enabled.");
TRANSLATE_NOOP("FullscreenUI", "Advanced Settings");
TRANSLATE_NOOP("FullscreenUI", "All Time: {}");
TRANSLATE_NOOP("FullscreenUI", "Allow Booting Without SBI File");
@ -7178,6 +7191,7 @@ TRANSLATE_NOOP("FullscreenUI", "Force NTSC Timings");
TRANSLATE_NOOP("FullscreenUI", "Forces PAL games to run at NTSC timings, i.e. 60hz. Some PAL games will run at their \"normal\" speeds, while others will break.");
TRANSLATE_NOOP("FullscreenUI", "Forces a full rescan of all games previously identified.");
TRANSLATE_NOOP("FullscreenUI", "Forcibly mutes both CD-DA and XA audio from the CD-ROM. Can be used to disable background music in some games.");
TRANSLATE_NOOP("FullscreenUI", "Frame Time Buffer");
TRANSLATE_NOOP("FullscreenUI", "From File...");
TRANSLATE_NOOP("FullscreenUI", "Fullscreen Resolution");
TRANSLATE_NOOP("FullscreenUI", "GPU Adapter");
@ -7226,6 +7240,7 @@ TRANSLATE_NOOP("FullscreenUI", "Interface Settings");
TRANSLATE_NOOP("FullscreenUI", "Internal Resolution");
TRANSLATE_NOOP("FullscreenUI", "Last Played");
TRANSLATE_NOOP("FullscreenUI", "Last Played: %s");
TRANSLATE_NOOP("FullscreenUI", "Latency Control");
TRANSLATE_NOOP("FullscreenUI", "Launch Options");
TRANSLATE_NOOP("FullscreenUI", "Launch a game by selecting a file/disc image.");
TRANSLATE_NOOP("FullscreenUI", "Launch a game from a file, disc, or starts the console without any disc inserted.");
@ -7335,8 +7350,10 @@ TRANSLATE_NOOP("FullscreenUI", "RAIntegration is being used instead of the built
TRANSLATE_NOOP("FullscreenUI", "Read Speedup");
TRANSLATE_NOOP("FullscreenUI", "Readahead Sectors");
TRANSLATE_NOOP("FullscreenUI", "Recompiler Fast Memory Access");
TRANSLATE_NOOP("FullscreenUI", "Reduce Input Latency");
TRANSLATE_NOOP("FullscreenUI", "Reduces \"wobbly\" polygons by attempting to preserve the fractional component through memory transfers.");
TRANSLATE_NOOP("FullscreenUI", "Reduces hitches in emulation by reading/decompressing CD data asynchronously on a worker thread.");
TRANSLATE_NOOP("FullscreenUI", "Reduces input latency by delaying the start of frame until closer to the presentation time.");
TRANSLATE_NOOP("FullscreenUI", "Reduces polygon Z-fighting through depth testing. Low compatibility with games.");
TRANSLATE_NOOP("FullscreenUI", "Region");
TRANSLATE_NOOP("FullscreenUI", "Region: ");
@ -7455,6 +7472,7 @@ TRANSLATE_NOOP("FullscreenUI", "Smooths out the blockiness of magnified textures
TRANSLATE_NOOP("FullscreenUI", "Sort By");
TRANSLATE_NOOP("FullscreenUI", "Sort Reversed");
TRANSLATE_NOOP("FullscreenUI", "Sound Effects");
TRANSLATE_NOOP("FullscreenUI", "Specifies the amount of buffer time added, which reduces the additional sleep time introduced.");
TRANSLATE_NOOP("FullscreenUI", "Spectator Mode");
TRANSLATE_NOOP("FullscreenUI", "Speed Control");
TRANSLATE_NOOP("FullscreenUI", "Speeds up CD-ROM reads by the specified factor. May improve loading speeds in some games, and break others.");
@ -7523,8 +7541,8 @@ TRANSLATE_NOOP("FullscreenUI", "Uses game-specific settings for controllers for
TRANSLATE_NOOP("FullscreenUI", "Uses perspective-correct interpolation for colors, which can improve visuals in some games.");
TRANSLATE_NOOP("FullscreenUI", "Uses perspective-correct interpolation for texture coordinates, straightening out warped textures.");
TRANSLATE_NOOP("FullscreenUI", "Uses screen positions to resolve PGXP data. May improve visuals in some games.");
TRANSLATE_NOOP("FullscreenUI", "VSync");
TRANSLATE_NOOP("FullscreenUI", "Value: {} | Default: {} | Minimum: {} | Maximum: {}");
TRANSLATE_NOOP("FullscreenUI", "Vertical Sync (VSync)");
TRANSLATE_NOOP("FullscreenUI", "When enabled and logged in, DuckStation will scan for achievements on startup.");
TRANSLATE_NOOP("FullscreenUI", "When enabled, DuckStation will assume all achievements are locked and not send any unlock notifications to the server.");
TRANSLATE_NOOP("FullscreenUI", "When enabled, DuckStation will list achievements from unofficial sets. These achievements are not tracked by RetroAchievements.");

View File

@ -349,6 +349,12 @@ void ImGuiManager::DrawPerformanceOverlay()
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
if (g_settings.display_show_latency_stats)
{
System::FormatLatencyStats(text);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
if (g_settings.display_show_cpu_usage)
{
text.format("{:.2f}ms | {:.2f}ms | {:.2f}ms", System::GetMinimumFrameTime(), System::GetAverageFrameTime(),
@ -410,15 +416,6 @@ void ImGuiManager::DrawPerformanceOverlay()
FormatProcessorStat(text, System::GetSWThreadUsage(), System::GetSWThreadAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
#if 0
{
AudioStream* stream = g_spu.GetOutputStream();
const u32 frames = stream->GetBufferedFramesRelaxed();
text.fmt("Audio: {:<4u}f/{:<3u}ms", frames, AudioStream::GetMSForBufferSize(stream->GetSampleRate(), frames));
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
#endif
}
if (g_settings.display_show_gpu_usage && g_gpu_device->IsGPUTimingEnabled())

View File

@ -267,6 +267,9 @@ void Settings::Load(SettingsInterface& si)
display_screenshot_quality = static_cast<u8>(
std::clamp<u32>(si.GetUIntValue("Display", "ScreenshotQuality", DEFAULT_DISPLAY_SCREENSHOT_QUALITY), 1, 100));
display_optimal_frame_pacing = si.GetBoolValue("Display", "OptimalFramePacing", false);
display_pre_frame_sleep = si.GetBoolValue("Display", "PreFrameSleep", false);
display_pre_frame_sleep_buffer =
si.GetFloatValue("Display", "PreFrameSleepBuffer", DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER);
display_vsync = si.GetBoolValue("Display", "VSync", 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));
@ -278,6 +281,7 @@ void Settings::Load(SettingsInterface& si)
display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
display_show_gpu_stats = si.GetBoolValue("Display", "ShowGPUStatistics", false);
display_show_resolution = si.GetBoolValue("Display", "ShowResolution", false);
display_show_latency_stats = si.GetBoolValue("Display", "ShowLatencyStatistics", false);
display_show_cpu_usage = si.GetBoolValue("Display", "ShowCPU", false);
display_show_gpu_usage = si.GetBoolValue("Display", "ShowGPU", false);
display_show_frame_times = si.GetBoolValue("Display", "ShowFrameTimes", false);
@ -521,6 +525,8 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
si.SetStringValue("Display", "Alignment", GetDisplayAlignmentName(display_alignment));
si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling));
si.SetBoolValue("Display", "OptimalFramePacing", display_optimal_frame_pacing);
si.SetBoolValue("Display", "PreFrameSleep", display_pre_frame_sleep);
si.SetFloatValue("Display", "PreFrameSleepBuffer", display_pre_frame_sleep_buffer);
si.SetBoolValue("Display", "VSync", display_vsync);
si.SetStringValue("Display", "ExclusiveFullscreenControl",
GetDisplayExclusiveFullscreenControlName(display_exclusive_fullscreen_control));
@ -535,6 +541,7 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
si.SetBoolValue("Display", "ShowFPS", display_show_fps);
si.SetBoolValue("Display", "ShowSpeed", display_show_speed);
si.SetBoolValue("Display", "ShowResolution", display_show_resolution);
si.SetBoolValue("Display", "ShowLatencyStatistics", display_show_latency_stats);
si.SetBoolValue("Display", "ShowGPUStatistics", display_show_gpu_stats);
si.SetBoolValue("Display", "ShowCPU", display_show_cpu_usage);
si.SetBoolValue("Display", "ShowGPU", display_show_gpu_usage);

View File

@ -146,6 +146,7 @@ struct Settings
s8 display_line_start_offset = 0;
s8 display_line_end_offset = 0;
bool display_optimal_frame_pacing : 1 = false;
bool display_pre_frame_sleep : 1 = false;
bool display_vsync : 1 = false;
bool display_force_4_3_for_24bit : 1 = false;
bool gpu_24bit_chroma_smoothing : 1 = false;
@ -154,6 +155,7 @@ struct Settings
bool display_show_speed : 1 = false;
bool display_show_gpu_stats : 1 = false;
bool display_show_resolution : 1 = false;
bool display_show_latency_stats : 1 = false;
bool display_show_cpu_usage : 1 = false;
bool display_show_gpu_usage : 1 = false;
bool display_show_frame_times : 1 = false;
@ -161,8 +163,9 @@ struct Settings
bool display_show_inputs : 1 = false;
bool display_show_enhancements : 1 = false;
bool display_stretch_vertically : 1 = false;
float display_osd_scale = 100.0f;
float display_pre_frame_sleep_buffer = DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER;
float display_max_fps = DEFAULT_DISPLAY_MAX_FPS;
float display_osd_scale = 100.0f;
float gpu_pgxp_tolerance = -1.0f;
float gpu_pgxp_depth_clear_threshold = DEFAULT_GPU_PGXP_DEPTH_THRESHOLD / GPU_PGXP_DEPTH_THRESHOLD_SCALE;
@ -498,6 +501,7 @@ struct Settings
static constexpr DisplayScreenshotMode DEFAULT_DISPLAY_SCREENSHOT_MODE = DisplayScreenshotMode::ScreenResolution;
static constexpr DisplayScreenshotFormat DEFAULT_DISPLAY_SCREENSHOT_FORMAT = DisplayScreenshotFormat::PNG;
static constexpr u8 DEFAULT_DISPLAY_SCREENSHOT_QUALITY = 85;
static constexpr float DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER = 2.0f;
static constexpr float DEFAULT_OSD_SCALE = 100.0f;
static constexpr u8 DEFAULT_CDROM_READAHEAD_SECTORS = 8;

View File

@ -45,6 +45,7 @@
#include "util/postprocessing.h"
#include "util/state_wrapper.h"
#include "common/align.h"
#include "common/error.h"
#include "common/file_system.h"
#include "common/log.h"
@ -113,6 +114,9 @@ static void LogUnsafeSettingsToConsole(const std::string& messages);
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
static void Throttle(Common::Timer::Value current_time);
static void UpdatePerformanceCounters();
static void AccumulatePreFrameSleepTime();
static void UpdatePreFrameSleepTime();
static void SetRewinding(bool enabled);
static bool SaveRewindState();
@ -166,12 +170,6 @@ static const GameDatabase::Entry* s_running_game_entry = nullptr;
static System::GameHash s_running_game_hash;
static bool s_was_fast_booted;
static float s_throttle_frequency = 0.0f;
static float s_target_speed = 0.0f;
static Common::Timer::Value s_frame_period = 0;
static Common::Timer::Value s_next_frame_time = 0;
static bool s_last_frame_skipped = false;
static bool s_system_executing = false;
static bool s_system_interrupted = false;
static bool s_frame_step_request = false;
@ -179,7 +177,20 @@ static bool s_fast_forward_enabled = false;
static bool s_turbo_enabled = false;
static bool s_throttler_enabled = false;
static bool s_optimal_frame_pacing = false;
static bool s_pre_frame_sleep = false;
static bool s_syncing_to_host = false;
static bool s_last_frame_skipped = false;
static float s_throttle_frequency = 0.0f;
static float s_target_speed = 0.0f;
static Common::Timer::Value s_frame_period = 0;
static Common::Timer::Value s_next_frame_time = 0;
static Common::Timer::Value s_frame_start_time = 0;
static Common::Timer::Value s_last_active_frame_time = 0;
static Common::Timer::Value s_pre_frame_sleep_time = 0;
static Common::Timer::Value s_max_active_frame_time = 0;
static float s_average_frame_time_accumulator = 0.0f;
static float s_minimum_frame_time_accumulator = 0.0f;
@ -1861,7 +1872,15 @@ void System::FrameDone()
SaveRunaheadState();
}
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
// pre-frame sleep accounting (input lag reduction)
const Common::Timer::Value pre_frame_sleep_until = s_next_frame_time + s_pre_frame_sleep_time;
s_last_active_frame_time = current_time - s_frame_start_time;
if (s_pre_frame_sleep)
AccumulatePreFrameSleepTime();
// 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 throttle_before_present = (s_optimal_frame_pacing && s_throttler_enabled && !IsExecutionInterrupted());
@ -1890,6 +1909,25 @@ void System::FrameDone()
Throttle(current_time);
}
// pre-frame sleep (input lag reduction)
current_time = Common::Timer::GetCurrentValue();
if (s_pre_frame_sleep)
{
// don't sleep if it's under 1ms, because we're just going to overshoot (or spin).
if (pre_frame_sleep_until > current_time &&
Common::Timer::ConvertValueToMilliseconds(pre_frame_sleep_until - current_time) >= 1)
{
Common::Timer::SleepUntil(pre_frame_sleep_until, true);
current_time = Common::Timer::GetCurrentValue();
}
else
{
Log_WarningPrint("Skipping pre-frame sleep");
}
}
s_frame_start_time = current_time;
// Input poll already done above
if (s_runahead_frames == 0)
{
@ -1940,6 +1978,7 @@ void System::UpdateThrottlePeriod()
void System::ResetThrottler()
{
s_next_frame_time = Common::Timer::GetCurrentValue() + s_frame_period;
s_pre_frame_sleep_time = 0;
}
void System::Throttle(Common::Timer::Value current_time)
@ -2624,6 +2663,9 @@ void System::UpdatePerformanceCounters()
if (g_settings.display_show_gpu_stats)
g_gpu->UpdateStatistics(frames_run);
if (s_pre_frame_sleep)
UpdatePreFrameSleepTime();
Log_VerbosePrintf("FPS: %.2f VPS: %.2f CPU: %.2f GPU: %.2f Average: %.2fms Min: %.2fms Max: %.2f ms", s_fps, s_vps,
s_cpu_thread_usage, s_gpu_usage, s_average_frame_time, s_minimum_frame_time, s_maximum_frame_time);
@ -2649,6 +2691,56 @@ void System::ResetPerformanceCounters()
ResetThrottler();
}
void System::AccumulatePreFrameSleepTime()
{
DebugAssert(s_pre_frame_sleep);
s_max_active_frame_time = std::max(s_max_active_frame_time, s_last_active_frame_time);
// in case one frame runs over, adjust to compensate
const Common::Timer::Value max_sleep_time_for_this_frame =
s_frame_period - std::min(s_last_active_frame_time, s_frame_period);
if (max_sleep_time_for_this_frame < s_pre_frame_sleep_time)
{
s_pre_frame_sleep_time = Common::AlignDown(max_sleep_time_for_this_frame,
static_cast<unsigned int>(Common::Timer::ConvertMillisecondsToValue(1)));
Log_DevFmt("Adjust pre-frame time to {} ms due to overrun of {} ms",
Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
Common::Timer::ConvertValueToMilliseconds(s_last_active_frame_time));
}
}
void System::UpdatePreFrameSleepTime()
{
DebugAssert(s_pre_frame_sleep);
const Common::Timer::Value expected_frame_time =
s_max_active_frame_time + Common::Timer::ConvertMillisecondsToValue(g_settings.display_pre_frame_sleep_buffer);
s_pre_frame_sleep_time = Common::AlignDown(s_frame_period - std::min(expected_frame_time, s_frame_period),
static_cast<unsigned int>(Common::Timer::ConvertMillisecondsToValue(1)));
Log_DevFmt("Set pre-frame time to {} ms (expected frame time of {} ms)",
Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
Common::Timer::ConvertValueToMilliseconds(expected_frame_time));
s_max_active_frame_time = 0;
}
void System::FormatLatencyStats(SmallStringBase& str)
{
AudioStream* audio_stream = SPU::GetOutputStream();
const u32 audio_latency =
AudioStream::GetMSForBufferSize(audio_stream->GetSampleRate(), audio_stream->GetBufferedFramesRelaxed());
const double active_frame_time = std::ceil(Common::Timer::ConvertValueToMilliseconds(s_last_active_frame_time));
const double pre_frame_time = std::ceil(Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time));
const double input_latency = std::ceil(
Common::Timer::ConvertValueToMilliseconds(s_frame_period - s_pre_frame_sleep_time) -
Common::Timer::ConvertValueToMilliseconds(static_cast<Common::Timer::Value>(s_runahead_frames) * s_frame_period));
str.format("AF: {:.0f}ms | PF: {:.0f}ms | IL: {:.0f}ms | AL: {}ms", active_frame_time, pre_frame_time, input_latency,
audio_latency);
}
void System::UpdateSpeedLimiterState()
{
const float old_target_speed = s_target_speed;
@ -2657,6 +2749,7 @@ void System::UpdateSpeedLimiterState()
(s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
s_throttler_enabled = (s_target_speed != 0.0f);
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;
s_syncing_to_host = false;
if (g_settings.sync_to_host_refresh_rate && (g_settings.audio_stretch_mode != AudioStretchMode::Off) &&
@ -3786,6 +3879,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
g_settings.fast_forward_speed != old_settings.fast_forward_speed ||
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_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_vsync != old_settings.display_vsync ||
g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate)
{

View File

@ -2,10 +2,13 @@
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "common/timer.h"
#include "settings.h"
#include "timing_event.h"
#include "types.h"
#include "common/timer.h"
#include <memory>
#include <optional>
#include <string>
@ -13,6 +16,7 @@
class ByteStream;
class CDImage;
class Error;
class SmallStringBase;
class StateWrapper;
class Controller;
@ -230,6 +234,7 @@ float GetGPUUsage();
float GetGPUAverageTime();
const FrameTimeHistory& GetFrameTimeHistory();
u32 GetFrameTimeHistoryPos();
void FormatLatencyStats(SmallStringBase& str);
/// Loads global settings (i.e. EmuConfig).
void LoadSettings(bool display_osd_messages);
@ -282,8 +287,6 @@ void SetThrottleFrequency(float frequency);
/// Updates the throttle period, call when target emulation speed changes.
void UpdateThrottlePeriod();
void ResetThrottler();
void UpdatePerformanceCounters();
void ResetPerformanceCounters();
/// Resets vsync/max present fps state.