GPUDevice: Add support for VRR and relaxed vsync

This commit is contained in:
Stenzek
2024-03-03 12:25:37 +10:00
parent d9e496284f
commit a1d7d214cf
27 changed files with 474 additions and 364 deletions

View File

@ -3879,10 +3879,11 @@ void FullscreenUI::DrawDisplaySettingsPage()
"GPU", "UseSoftwareRendererForReadbacks", false);
}
DrawToggleSetting(
bsi, FSUI_CSTR("Enable VSync"),
DrawEnumSetting(
bsi, FSUI_CSTR("VSync"),
FSUI_CSTR("Synchronizes presentation of the console's frames to the host. Enable for smoother animations."),
"Display", "VSync", Settings::DEFAULT_VSYNC_VALUE);
"Display", "SyncMode", Settings::DEFAULT_DISPLAY_SYNC_MODE, &Settings::ParseDisplaySyncMode,
&Settings::GetDisplaySyncModeName, &Settings::GetDisplaySyncModeDisplayName, DisplaySyncMode::Count);
DrawToggleSetting(
bsi, FSUI_CSTR("Sync To Host Refresh Rate"),

View File

@ -265,15 +265,13 @@ bool Host::CreateGPUDevice(RenderAPI api)
if (g_settings.gpu_disable_texture_copy_to_self)
disabled_features |= GPUDevice::FEATURE_MASK_TEXTURE_COPY_TO_SELF;
// TODO: FSUI should always use vsync..
Error error;
const bool vsync = System::IsValid() ? System::ShouldUseVSync() : g_settings.video_sync_enabled;
if (!g_gpu_device || !g_gpu_device->Create(g_settings.gpu_adapter,
g_settings.gpu_disable_shader_cache ? std::string_view() :
std::string_view(EmuFolders::Cache),
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, vsync,
g_settings.gpu_threaded_presentation, exclusive_fullscreen_control,
static_cast<GPUDevice::FeatureMask>(disabled_features), &error))
if (!g_gpu_device || !g_gpu_device->Create(
g_settings.gpu_adapter,
g_settings.gpu_disable_shader_cache ? std::string_view() : std::string_view(EmuFolders::Cache),
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, System::GetEffectiveDisplaySyncMode(),
g_settings.gpu_threaded_presentation, exclusive_fullscreen_control,
static_cast<GPUDevice::FeatureMask>(disabled_features), &error))
{
Log_ErrorPrintf("Failed to create GPU device.");
if (g_gpu_device)

View File

@ -243,6 +243,10 @@ void Settings::Load(SettingsInterface& si)
display_scaling =
ParseDisplayScaling(si.GetStringValue("Display", "Scaling", GetDisplayScalingName(DEFAULT_DISPLAY_SCALING)).c_str())
.value_or(DEFAULT_DISPLAY_SCALING);
display_sync_mode =
ParseDisplaySyncMode(
si.GetStringValue("Display", "SyncMode", GetDisplaySyncModeName(DEFAULT_DISPLAY_SYNC_MODE)).c_str())
.value_or(DEFAULT_DISPLAY_SYNC_MODE);
display_exclusive_fullscreen_control =
ParseDisplayExclusiveFullscreenControl(
si.GetStringValue("Display", "ExclusiveFullscreenControl",
@ -279,7 +283,6 @@ void Settings::Load(SettingsInterface& si)
display_show_enhancements = si.GetBoolValue("Display", "ShowEnhancements", false);
display_all_frames = si.GetBoolValue("Display", "DisplayAllFrames", false);
display_stretch_vertically = si.GetBoolValue("Display", "StretchVertically", false);
video_sync_enabled = si.GetBoolValue("Display", "VSync", DEFAULT_VSYNC_VALUE);
display_max_fps = si.GetFloatValue("Display", "MaxFPS", DEFAULT_DISPLAY_MAX_FPS);
display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
@ -504,6 +507,7 @@ void Settings::Save(SettingsInterface& si) const
si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio));
si.SetStringValue("Display", "Alignment", GetDisplayAlignmentName(display_alignment));
si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling));
si.SetStringValue("Display", "SyncMode", GetDisplaySyncModeName(display_sync_mode));
si.SetStringValue("Display", "ExclusiveFullscreenControl",
GetDisplayExclusiveFullscreenControlName(display_exclusive_fullscreen_control));
si.SetStringValue("Display", "ScreenshotMode", GetDisplayScreenshotModeName(display_screenshot_mode));
@ -524,7 +528,6 @@ void Settings::Save(SettingsInterface& si) const
si.SetBoolValue("Display", "ShowEnhancements", display_show_enhancements);
si.SetBoolValue("Display", "DisplayAllFrames", display_all_frames);
si.SetBoolValue("Display", "StretchVertically", display_stretch_vertically);
si.SetBoolValue("Display", "VSync", video_sync_enabled);
si.SetFloatValue("Display", "MaxFPS", display_max_fps);
si.SetFloatValue("Display", "OSDScale", display_osd_scale);
@ -1357,6 +1360,43 @@ const char* Settings::GetDisplayScalingDisplayName(DisplayScalingMode mode)
return Host::TranslateToCString("DisplayScalingMode", s_display_scaling_display_names[static_cast<int>(mode)]);
}
static constexpr const std::array s_display_sync_mode_names = {
"Disabled",
"VSync",
"VSyncRelaxed",
"VRR",
};
static constexpr const std::array s_display_sync_mode_display_names = {
TRANSLATE_NOOP("Settings", "Disabled"),
TRANSLATE_NOOP("Settings", "VSync"),
TRANSLATE_NOOP("Settings", "Relaxed VSync"),
TRANSLATE_NOOP("Settings", "VRR/FreeSync/GSync"),
};
std::optional<DisplaySyncMode> Settings::ParseDisplaySyncMode(const char* str)
{
int index = 0;
for (const char* name : s_display_sync_mode_names)
{
if (StringUtil::Strcasecmp(name, str) == 0)
return static_cast<DisplaySyncMode>(index);
index++;
}
return std::nullopt;
}
const char* Settings::GetDisplaySyncModeName(DisplaySyncMode mode)
{
return s_display_sync_mode_names[static_cast<size_t>(mode)];
}
const char* Settings::GetDisplaySyncModeDisplayName(DisplaySyncMode mode)
{
return Host::TranslateToCString("Settings", s_display_sync_mode_display_names[static_cast<size_t>(mode)]);
}
static constexpr const std::array s_display_exclusive_fullscreen_mode_names = {
"Automatic",
"Disallowed",

View File

@ -6,6 +6,7 @@
#include "types.h"
#include "util/audio_stream.h"
#include "util/gpu_types.h"
#include "common/log.h"
#include "common/settings_interface.h"
@ -18,8 +19,6 @@
#include <string_view>
#include <vector>
enum class RenderAPI : u32;
struct SettingInfo
{
enum class Type
@ -134,6 +133,7 @@ struct Settings
DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO;
DisplayAlignment display_alignment = DEFAULT_DISPLAY_ALIGNMENT;
DisplayScalingMode display_scaling = DEFAULT_DISPLAY_SCALING;
DisplaySyncMode display_sync_mode = DEFAULT_DISPLAY_SYNC_MODE;
DisplayExclusiveFullscreenControl display_exclusive_fullscreen_control = DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL;
DisplayScreenshotMode display_screenshot_mode = DEFAULT_DISPLAY_SCREENSHOT_MODE;
DisplayScreenshotFormat display_screenshot_format = DEFAULT_DISPLAY_SCREENSHOT_FORMAT;
@ -159,7 +159,6 @@ struct Settings
bool display_show_enhancements : 1 = false;
bool display_all_frames : 1 = false;
bool display_stretch_vertically : 1 = false;
bool video_sync_enabled = DEFAULT_VSYNC_VALUE;
float display_osd_scale = 100.0f;
float display_max_fps = DEFAULT_DISPLAY_MAX_FPS;
float gpu_pgxp_tolerance = -1.0f;
@ -411,6 +410,10 @@ struct Settings
static const char* GetDisplayScalingName(DisplayScalingMode mode);
static const char* GetDisplayScalingDisplayName(DisplayScalingMode mode);
static std::optional<DisplaySyncMode> ParseDisplaySyncMode(const char* str);
static const char* GetDisplaySyncModeName(DisplaySyncMode mode);
static const char* GetDisplaySyncModeDisplayName(DisplaySyncMode mode);
static std::optional<DisplayExclusiveFullscreenControl> ParseDisplayExclusiveFullscreenControl(const char* str);
static const char* GetDisplayExclusiveFullscreenControlName(DisplayExclusiveFullscreenControl mode);
static const char* GetDisplayExclusiveFullscreenControlDisplayName(DisplayExclusiveFullscreenControl mode);
@ -484,6 +487,7 @@ struct Settings
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto;
static constexpr DisplayAlignment DEFAULT_DISPLAY_ALIGNMENT = DisplayAlignment::Center;
static constexpr DisplayScalingMode DEFAULT_DISPLAY_SCALING = DisplayScalingMode::BilinearSmooth;
static constexpr DisplaySyncMode DEFAULT_DISPLAY_SYNC_MODE = DisplaySyncMode::Disabled;
static constexpr DisplayExclusiveFullscreenControl DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL =
DisplayExclusiveFullscreenControl::Automatic;
static constexpr DisplayScreenshotMode DEFAULT_DISPLAY_SCREENSHOT_MODE = DisplayScreenshotMode::ScreenResolution;
@ -525,13 +529,11 @@ struct Settings
#ifndef __ANDROID__
static constexpr bool DEFAULT_SAVE_STATE_COMPRESSION = true;
static constexpr bool DEFAULT_SAVE_STATE_BACKUPS = true;
static constexpr bool DEFAULT_VSYNC_VALUE = false;
static constexpr bool DEFAULT_FAST_BOOT_VALUE = false;
static constexpr float DEFAULT_DISPLAY_MAX_FPS = 0.0f;
#else
static constexpr bool DEFAULT_SAVE_STATE_COMPRESSION = true;
static constexpr bool DEFAULT_SAVE_STATE_BACKUPS = false;
static constexpr bool DEFAULT_VSYNC_VALUE = false;
static constexpr bool DEFAULT_FAST_BOOT_VALUE = true;
static constexpr float DEFAULT_DISPLAY_MAX_FPS = 60.0f;
#endif

View File

@ -1849,6 +1849,12 @@ void System::FrameDone()
SaveRunaheadState();
}
// TODO: Kick cmdbuffer early
const DisplaySyncMode sync_mode = g_gpu_device->GetSyncMode();
const bool throttle_after_present = (sync_mode == DisplaySyncMode::Disabled);
if (!throttle_after_present && s_throttler_enabled && !IsExecutionInterrupted())
Throttle();
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
if (current_time < s_next_frame_time || s_display_all_frames || s_last_frame_skipped)
{
@ -1860,7 +1866,7 @@ void System::FrameDone()
s_last_frame_skipped = true;
}
if (s_throttler_enabled && !IsExecutionInterrupted())
if (throttle_after_present && s_throttler_enabled && !IsExecutionInterrupted())
Throttle();
// Input poll already done above
@ -2638,10 +2644,14 @@ void System::UpdateSpeedLimiterState()
}
// When syncing to host and using vsync, we don't need to sleep.
if (s_syncing_to_host && ShouldUseVSync() && s_display_all_frames)
if (s_syncing_to_host && s_display_all_frames)
{
Log_InfoPrintf("Using host vsync for throttling.");
s_throttler_enabled = false;
const DisplaySyncMode effective_sync_mode = GetEffectiveDisplaySyncMode();
if (effective_sync_mode == DisplaySyncMode::VSync || effective_sync_mode == DisplaySyncMode::VSyncRelaxed)
{
Log_InfoPrintf("Using host vsync for throttling.");
s_throttler_enabled = false;
}
}
Log_VerbosePrintf("Target speed: %f%%", s_target_speed * 100.0f);
@ -2674,21 +2684,25 @@ void System::UpdateSpeedLimiterState()
void System::UpdateDisplaySync()
{
const bool video_sync_enabled = ShouldUseVSync();
const bool syncing_to_host_vsync = (s_syncing_to_host && video_sync_enabled && s_display_all_frames);
const DisplaySyncMode display_sync_mode = GetEffectiveDisplaySyncMode();
const bool syncing_to_host_vsync =
(s_syncing_to_host &&
(display_sync_mode == DisplaySyncMode::VSync || display_sync_mode == DisplaySyncMode::VSyncRelaxed) &&
s_display_all_frames);
const float max_display_fps = (s_throttler_enabled || s_syncing_to_host) ? 0.0f : g_settings.display_max_fps;
Log_VerbosePrintf("Using vsync: %s%s", video_sync_enabled ? "YES" : "NO",
Log_VerbosePrintf("Display sync: %s%s", Settings::GetDisplaySyncModeDisplayName(display_sync_mode),
syncing_to_host_vsync ? " (for throttling)" : "");
Log_VerbosePrintf("Max display fps: %f (%s)", max_display_fps,
s_display_all_frames ? "displaying all frames" : "skipping displaying frames when needed");
g_gpu_device->SetDisplayMaxFPS(max_display_fps);
g_gpu_device->SetVSync(video_sync_enabled);
g_gpu_device->SetSyncMode(display_sync_mode);
}
bool System::ShouldUseVSync()
DisplaySyncMode System::GetEffectiveDisplaySyncMode()
{
return g_settings.video_sync_enabled && !IsRunningAtNonStandardSpeed();
// Disable vsync if running outside 100%.
return (IsValid() && IsRunningAtNonStandardSpeed()) ? DisplaySyncMode::Disabled : g_settings.display_sync_mode;
}
bool System::IsFastForwardEnabled()
@ -3737,7 +3751,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
DMA::SetHaltTicks(g_settings.dma_halt_ticks);
if (g_settings.audio_backend != old_settings.audio_backend ||
g_settings.video_sync_enabled != old_settings.video_sync_enabled ||
g_settings.display_sync_mode != old_settings.display_sync_mode ||
g_settings.increase_timer_resolution != old_settings.increase_timer_resolution ||
g_settings.emulation_speed != old_settings.emulation_speed ||
g_settings.fast_forward_speed != old_settings.fast_forward_speed ||

View File

@ -455,7 +455,7 @@ void ToggleWidescreen();
bool IsRunningAtNonStandardSpeed();
/// Returns true if vsync should be used.
bool ShouldUseVSync();
DisplaySyncMode GetEffectiveDisplaySyncMode();
/// Quick switch between software and hardware rendering.
void ToggleSoftwareRendering();