mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 07:35:41 +00:00
System: Combine VRR and Optimal Frame Pacing
GSync/FreeSync display users should: - DISABLE VSync. - ENABLE Optimal Frame Pacing.
This commit is contained in:
parent
2ad67ad3ee
commit
88270771da
|
@ -4180,11 +4180,10 @@ void FullscreenUI::DrawDisplaySettingsPage()
|
|||
"GPU", "UseSoftwareRendererForReadbacks", false);
|
||||
}
|
||||
|
||||
DrawEnumSetting(
|
||||
bsi, FSUI_CSTR("VSync"),
|
||||
FSUI_CSTR("Synchronizes presentation of the console's frames to the host. Enable for smoother animations."),
|
||||
"Display", "SyncMode", Settings::DEFAULT_DISPLAY_SYNC_MODE, &Settings::ParseDisplaySyncMode,
|
||||
&Settings::GetDisplaySyncModeName, &Settings::GetDisplaySyncModeDisplayName, DisplaySyncMode::Count);
|
||||
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"),
|
||||
|
@ -4192,10 +4191,11 @@ void FullscreenUI::DrawDisplaySettingsPage()
|
|||
"Resampling are enabled."),
|
||||
"Main", "SyncToHostRefreshRate", false);
|
||||
|
||||
DrawToggleSetting(bsi, FSUI_CSTR("Optimal Frame Pacing"),
|
||||
FSUI_CSTR("Ensures every frame generated is displayed for optimal pacing. Disable if you are "
|
||||
"having speed or sound issues."),
|
||||
"Display", "DisplayAllFrames", 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"));
|
||||
|
||||
|
|
|
@ -296,7 +296,7 @@ bool Host::CreateGPUDevice(RenderAPI api)
|
|||
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(),
|
||||
SHADER_CACHE_VERSION, g_settings.gpu_use_debug_device, System::IsVSyncEffectivelyEnabled(),
|
||||
g_settings.gpu_threaded_presentation, exclusive_fullscreen_control,
|
||||
static_cast<GPUDevice::FeatureMask>(disabled_features), &error))
|
||||
{
|
||||
|
|
|
@ -248,10 +248,6 @@ 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",
|
||||
|
@ -270,6 +266,8 @@ void Settings::Load(SettingsInterface& si)
|
|||
.value_or(DEFAULT_DISPLAY_SCREENSHOT_FORMAT);
|
||||
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_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));
|
||||
display_active_end_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveEndOffset", 0));
|
||||
|
@ -286,7 +284,6 @@ void Settings::Load(SettingsInterface& si)
|
|||
display_show_status_indicators = si.GetBoolValue("Display", "ShowStatusIndicators", true);
|
||||
display_show_inputs = si.GetBoolValue("Display", "ShowInputs", false);
|
||||
display_show_enhancements = si.GetBoolValue("Display", "ShowEnhancements", false);
|
||||
display_all_frames = si.GetBoolValue("Display", "DisplayAllFrames", false);
|
||||
display_stretch_vertically = si.GetBoolValue("Display", "StretchVertically", false);
|
||||
display_max_fps = si.GetFloatValue("Display", "MaxFPS", DEFAULT_DISPLAY_MAX_FPS);
|
||||
display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
|
||||
|
@ -523,7 +520,8 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) 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.SetBoolValue("Display", "OptimalFramePacing", display_optimal_frame_pacing);
|
||||
si.SetBoolValue("Display", "VSync", display_vsync);
|
||||
si.SetStringValue("Display", "ExclusiveFullscreenControl",
|
||||
GetDisplayExclusiveFullscreenControlName(display_exclusive_fullscreen_control));
|
||||
si.SetStringValue("Display", "ScreenshotMode", GetDisplayScreenshotModeName(display_screenshot_mode));
|
||||
|
@ -547,7 +545,6 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
|
|||
si.SetFloatValue("Display", "OSDScale", display_osd_scale);
|
||||
}
|
||||
|
||||
si.SetBoolValue("Display", "DisplayAllFrames", display_all_frames);
|
||||
si.SetBoolValue("Display", "StretchVertically", display_stretch_vertically);
|
||||
si.SetFloatValue("Display", "MaxFPS", display_max_fps);
|
||||
|
||||
|
@ -1452,43 +1449,6 @@ 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",
|
||||
|
|
|
@ -134,7 +134,6 @@ 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;
|
||||
|
@ -145,6 +144,8 @@ struct Settings
|
|||
s16 display_active_end_offset = 0;
|
||||
s8 display_line_start_offset = 0;
|
||||
s8 display_line_end_offset = 0;
|
||||
bool display_optimal_frame_pacing : 1 = false;
|
||||
bool display_vsync : 1 = false;
|
||||
bool display_force_4_3_for_24bit : 1 = false;
|
||||
bool gpu_24bit_chroma_smoothing : 1 = false;
|
||||
bool display_show_osd_messages : 1 = true;
|
||||
|
@ -158,7 +159,6 @@ struct Settings
|
|||
bool display_show_status_indicators : 1 = true;
|
||||
bool display_show_inputs : 1 = false;
|
||||
bool display_show_enhancements : 1 = false;
|
||||
bool display_all_frames : 1 = false;
|
||||
bool display_stretch_vertically : 1 = false;
|
||||
float display_osd_scale = 100.0f;
|
||||
float display_max_fps = DEFAULT_DISPLAY_MAX_FPS;
|
||||
|
@ -416,10 +416,6 @@ 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);
|
||||
|
@ -496,7 +492,6 @@ 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;
|
||||
|
|
|
@ -166,8 +166,8 @@ 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 = 60.0f;
|
||||
static float s_target_speed = 1.0f;
|
||||
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;
|
||||
|
@ -177,8 +177,8 @@ static bool s_system_interrupted = false;
|
|||
static bool s_frame_step_request = false;
|
||||
static bool s_fast_forward_enabled = false;
|
||||
static bool s_turbo_enabled = false;
|
||||
static bool s_throttler_enabled = true;
|
||||
static bool s_display_all_frames = true;
|
||||
static bool s_throttler_enabled = false;
|
||||
static bool s_optimal_frame_pacing = false;
|
||||
static bool s_syncing_to_host = false;
|
||||
|
||||
static float s_average_frame_time_accumulator = 0.0f;
|
||||
|
@ -1862,13 +1862,11 @@ void System::FrameDone()
|
|||
}
|
||||
|
||||
// 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())
|
||||
if (s_optimal_frame_pacing && 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)
|
||||
if (current_time < s_next_frame_time || s_syncing_to_host || s_optimal_frame_pacing || s_last_frame_skipped)
|
||||
{
|
||||
s_last_frame_skipped = !PresentDisplay(true);
|
||||
}
|
||||
|
@ -1878,7 +1876,7 @@ void System::FrameDone()
|
|||
s_last_frame_skipped = true;
|
||||
}
|
||||
|
||||
if (throttle_after_present && s_throttler_enabled && !IsExecutionInterrupted())
|
||||
if (!s_optimal_frame_pacing && s_throttler_enabled && !IsExecutionInterrupted())
|
||||
Throttle();
|
||||
|
||||
// Input poll already done above
|
||||
|
@ -1949,7 +1947,7 @@ void System::Throttle()
|
|||
// Use a spinwait if we undersleep for all platforms except android.. don't want to burn battery.
|
||||
// Linux also seems to do a much better job of waking up at the requested time.
|
||||
#if !defined(__linux__) && !defined(__ANDROID__)
|
||||
Common::Timer::SleepUntil(s_next_frame_time, g_settings.display_all_frames);
|
||||
Common::Timer::SleepUntil(s_next_frame_time, g_settings.display_optimal_frame_pacing);
|
||||
#else
|
||||
Common::Timer::SleepUntil(s_next_frame_time, false);
|
||||
#endif
|
||||
|
@ -2648,7 +2646,7 @@ void System::UpdateSpeedLimiterState()
|
|||
g_settings.turbo_speed :
|
||||
(s_fast_forward_enabled ? g_settings.fast_forward_speed : g_settings.emulation_speed);
|
||||
s_throttler_enabled = (s_target_speed != 0.0f);
|
||||
s_display_all_frames = !s_throttler_enabled || g_settings.display_all_frames;
|
||||
s_optimal_frame_pacing = s_throttler_enabled && g_settings.display_optimal_frame_pacing;
|
||||
|
||||
s_syncing_to_host = false;
|
||||
if (g_settings.sync_to_host_refresh_rate && (g_settings.audio_stretch_mode != AudioStretchMode::Off) &&
|
||||
|
@ -2667,14 +2665,10 @@ void System::UpdateSpeedLimiterState()
|
|||
}
|
||||
|
||||
// When syncing to host and using vsync, we don't need to sleep.
|
||||
if (s_syncing_to_host && s_display_all_frames)
|
||||
if (s_syncing_to_host && IsVSyncEffectivelyEnabled())
|
||||
{
|
||||
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_InfoPrintf("Using host vsync for throttling.");
|
||||
s_throttler_enabled = false;
|
||||
}
|
||||
|
||||
Log_VerbosePrintf("Target speed: %f%%", s_target_speed * 100.0f);
|
||||
|
@ -2707,25 +2701,22 @@ void System::UpdateSpeedLimiterState()
|
|||
|
||||
void System::UpdateDisplaySync()
|
||||
{
|
||||
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 bool vsync_enabled = IsVSyncEffectivelyEnabled();
|
||||
const bool syncing_to_host_vsync = (s_syncing_to_host && vsync_enabled);
|
||||
const float max_display_fps = (s_throttler_enabled || s_syncing_to_host) ? 0.0f : g_settings.display_max_fps;
|
||||
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");
|
||||
Log_VerboseFmt("VSync: {}{}", vsync_enabled ? "Enabled" : "Disabled",
|
||||
syncing_to_host_vsync ? " (for throttling)" : "");
|
||||
Log_VerboseFmt("Max display fps: {}", max_display_fps);
|
||||
Log_VerboseFmt("Preset timing: {}", s_optimal_frame_pacing ? "consistent" : "immediate");
|
||||
|
||||
g_gpu_device->SetDisplayMaxFPS(max_display_fps);
|
||||
g_gpu_device->SetSyncMode(display_sync_mode);
|
||||
g_gpu_device->SetVSyncEnabled(vsync_enabled);
|
||||
}
|
||||
|
||||
DisplaySyncMode System::GetEffectiveDisplaySyncMode()
|
||||
bool System::IsVSyncEffectivelyEnabled()
|
||||
{
|
||||
// Disable vsync if running outside 100%.
|
||||
return (IsValid() && IsRunningAtNonStandardSpeed()) ? DisplaySyncMode::Disabled : g_settings.display_sync_mode;
|
||||
return (g_settings.display_vsync && IsValid() && !IsRunningAtNonStandardSpeed());
|
||||
}
|
||||
|
||||
bool System::IsFastForwardEnabled()
|
||||
|
@ -3780,12 +3771,12 @@ 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.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 ||
|
||||
g_settings.display_max_fps != old_settings.display_max_fps ||
|
||||
g_settings.display_all_frames != old_settings.display_all_frames ||
|
||||
g_settings.display_optimal_frame_pacing != old_settings.display_optimal_frame_pacing ||
|
||||
g_settings.display_vsync != old_settings.display_vsync ||
|
||||
g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate)
|
||||
{
|
||||
UpdateSpeedLimiterState();
|
||||
|
|
|
@ -453,7 +453,7 @@ void ToggleWidescreen();
|
|||
bool IsRunningAtNonStandardSpeed();
|
||||
|
||||
/// Returns true if vsync should be used.
|
||||
DisplaySyncMode GetEffectiveDisplaySyncMode();
|
||||
bool IsVSyncEffectivelyEnabled();
|
||||
|
||||
/// Quick switch between software and hardware rendering.
|
||||
void ToggleSoftwareRendering();
|
||||
|
|
|
@ -16,8 +16,9 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
|||
|
||||
m_ui.setupUi(this);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vsync, "Display", "VSync", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToHostRefreshRate, "Main", "SyncToHostRefreshRate", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.displayAllFrames, "Display", "DisplayAllFrames", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.optimalFramePacing, "Display", "OptimalFramePacing", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.rewindEnable, "Main", "RewindEnable", false);
|
||||
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.rewindSaveFrequency, "Main", "RewindFrequency", 10.0f);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.rewindSaveSlots, "Main", "RewindSaveSlots", 10);
|
||||
|
@ -67,6 +68,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
|||
}
|
||||
connect(m_ui.turboSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&EmulationSettingsWidget::onTurboSpeedIndexChanged);
|
||||
connect(m_ui.vsync, &QCheckBox::stateChanged, this, &EmulationSettingsWidget::onVSyncChanged);
|
||||
|
||||
connect(m_ui.rewindEnable, &QCheckBox::stateChanged, this, &EmulationSettingsWidget::updateRewind);
|
||||
connect(m_ui.rewindSaveFrequency, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
|
||||
|
@ -87,6 +89,11 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
|||
m_ui.turboSpeed, tr("Turbo Speed"), tr("User Preference"),
|
||||
tr("Sets the turbo speed. This speed will be used when the turbo hotkey is pressed/toggled. Turboing will take "
|
||||
"priority over fast forwarding if both hotkeys are pressed/toggled."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.vsync, tr("Display Vertical Sync (VSync)"), tr("Unchecked"),
|
||||
tr("Synchronizes presentation of the console's frames to the host. Enabling may result in smoother animations, at "
|
||||
"the cost of increased input lag. <strong>GSync/FreeSync users should enable Optimal Frame Pacing "
|
||||
"instead.</strong>"));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.syncToHostRefreshRate, tr("Sync To Host Refresh Rate"), tr("Unchecked"),
|
||||
tr("Adjusts the emulation speed so the console's refresh rate matches the host's refresh rate when both VSync and "
|
||||
|
@ -94,10 +101,11 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
|||
"potentially increasing the emulation speed by less than 1%. Sync To Host Refresh Rate will not take effect if "
|
||||
"the console's refresh rate is too far from the host's refresh rate. Users with variable refresh rate displays "
|
||||
"should disable this option."));
|
||||
dialog->registerWidgetHelp(m_ui.displayAllFrames, tr("Optimal Frame Pacing"), tr("Unchecked"),
|
||||
tr("Enabling this option will ensure every frame the console renders is displayed to the "
|
||||
"screen, for optimal frame pacing. If you are having difficulties maintaining full "
|
||||
"speed, or are getting audio glitches, try disabling this option."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.optimalFramePacing, tr("Optimal Frame Pacing"), tr("Unchecked"),
|
||||
tr("Enabling this option will ensure every frame the console renders is displayed to the screen, at a consistent "
|
||||
"rate, for optimal frame pacing. If you have a GSync/FreeSync display, enable this option. If you are having "
|
||||
"difficulties maintaining full speed, or are getting audio glitches, try disabling this option."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.rewindEnable, tr("Rewinding"), tr("Unchecked"),
|
||||
tr("<b>Enable Rewinding:</b> Saves state periodically so you can rewind any mistakes while playing.<br> "
|
||||
|
@ -110,6 +118,7 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* dialog, QWidget
|
|||
tr(
|
||||
"Simulates the system ahead of time and rolls back/replays to reduce input lag. Very high system requirements."));
|
||||
|
||||
onVSyncChanged();
|
||||
updateRewind();
|
||||
}
|
||||
|
||||
|
@ -175,6 +184,12 @@ void EmulationSettingsWidget::onTurboSpeedIndexChanged(int index)
|
|||
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::updateRewind()
|
||||
{
|
||||
const bool rewind_enabled = m_dialog->getEffectiveBoolValue("Main", "RewindEnable", false);
|
||||
|
|
|
@ -21,6 +21,7 @@ private Q_SLOTS:
|
|||
void onEmulationSpeedIndexChanged(int index);
|
||||
void onFastForwardSpeedIndexChanged(int index);
|
||||
void onTurboSpeedIndexChanged(int index);
|
||||
void onVSyncChanged();
|
||||
void updateRewind();
|
||||
|
||||
private:
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>672</width>
|
||||
<height>518</height>
|
||||
<width>568</width>
|
||||
<height>369</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
|
@ -61,15 +61,22 @@
|
|||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="1">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="vsync">
|
||||
<property name="text">
|
||||
<string>Display Vertical Sync (VSync)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QCheckBox" name="syncToHostRefreshRate">
|
||||
<property name="text">
|
||||
<string>Sync To Host Refresh Rate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QCheckBox" name="displayAllFrames">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="optimalFramePacing">
|
||||
<property name="text">
|
||||
<string>Optimal Frame Pacing</string>
|
||||
</property>
|
||||
|
@ -215,7 +222,7 @@
|
|||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
|
|
@ -75,9 +75,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
|||
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displayScaling, "Display", "Scaling",
|
||||
&Settings::ParseDisplayScaling, &Settings::GetDisplayScalingName,
|
||||
Settings::DEFAULT_DISPLAY_SCALING);
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.displaySyncMode, "Display", "SyncMode",
|
||||
&Settings::ParseDisplaySyncMode, &Settings::GetDisplaySyncModeName,
|
||||
Settings::DEFAULT_DISPLAY_SYNC_MODE);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gpuDownsampleScale, "GPU", "DownsampleScale", 1);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.trueColor, "GPU", "TrueColor", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableInterlacing, "GPU", "DisableInterlacing", true);
|
||||
|
@ -284,10 +281,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
|
|||
dialog->registerWidgetHelp(
|
||||
m_ui.displayScaling, tr("Scaling"), tr("Bilinear (Smooth)"),
|
||||
tr("Determines how the emulated console's output is upscaled or downscaled to your monitor's resolution."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.displaySyncMode, tr("VSync"), tr("Unchecked"),
|
||||
tr("Enable this option to match DuckStation's refresh rate with your current monitor or screen. "
|
||||
"VSync is automatically disabled when it is not possible (e.g. running at non-100% speed)."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.trueColor, tr("True Color Rendering"), tr("Checked"),
|
||||
tr("Forces the precision of colours output to the console's framebuffer to use the full 8 bits of precision per "
|
||||
|
@ -550,12 +543,6 @@ void GraphicsSettingsWidget::setupAdditionalUi()
|
|||
QString::fromUtf8(Settings::GetDisplayScalingDisplayName(static_cast<DisplayScalingMode>(i))));
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(DisplaySyncMode::Count); i++)
|
||||
{
|
||||
m_ui.displaySyncMode->addItem(
|
||||
QString::fromUtf8(Settings::GetDisplaySyncModeDisplayName(static_cast<DisplaySyncMode>(i))));
|
||||
}
|
||||
|
||||
// Advanced Tab
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(DisplayExclusiveFullscreenControl::Count); i++)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>584</width>
|
||||
<height>450</height>
|
||||
<height>430</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -260,6 +260,16 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Deinterlacing:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="displayDeinterlacing"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
|
@ -280,32 +290,8 @@
|
|||
<item row="6" column="1">
|
||||
<widget class="QComboBox" name="displayScaling"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>VSync:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QComboBox" name="displaySyncMode"/>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<item row="7" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="pgxpEnable">
|
||||
<property name="text">
|
||||
<string>PGXP Geometry Correction</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="trueColor">
|
||||
<property name="text">
|
||||
<string>True Color Rendering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="pgxpDepthBuffer">
|
||||
<property name="text">
|
||||
|
@ -313,20 +299,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="force43For24Bit">
|
||||
<property name="text">
|
||||
<string>Force 4:3 For FMVs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="chromaSmoothingFor24Bit">
|
||||
<property name="text">
|
||||
<string>FMV Chroma Smoothing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="disableInterlacing">
|
||||
<property name="text">
|
||||
|
@ -334,13 +306,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="widescreenHack">
|
||||
<property name="text">
|
||||
<string>Widescreen Rendering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="forceNTSCTimings">
|
||||
<property name="text">
|
||||
|
@ -348,25 +313,50 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="force43For24Bit">
|
||||
<property name="text">
|
||||
<string>Force 4:3 For FMVs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="trueColor">
|
||||
<property name="text">
|
||||
<string>True Color Rendering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="chromaSmoothingFor24Bit">
|
||||
<property name="text">
|
||||
<string>FMV Chroma Smoothing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="widescreenHack">
|
||||
<property name="text">
|
||||
<string>Widescreen Rendering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="pgxpEnable">
|
||||
<property name="text">
|
||||
<string>PGXP Geometry Correction</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Deinterlacing:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="displayDeinterlacing"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -437,20 +427,6 @@
|
|||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="advancedDisplayOptionsLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="gpuThread">
|
||||
<property name="text">
|
||||
<string>Threaded Rendering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="threadedPresentation">
|
||||
<property name="text">
|
||||
<string>Threaded Presentation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="blitSwapChain">
|
||||
<property name="text">
|
||||
|
@ -465,6 +441,20 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="threadedPresentation">
|
||||
<property name="text">
|
||||
<string>Threaded Presentation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="gpuThread">
|
||||
<property name="text">
|
||||
<string>Threaded Rendering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -488,13 +478,6 @@
|
|||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="scaledDithering">
|
||||
<property name="text">
|
||||
<string>Scaled Dithering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="debanding">
|
||||
<property name="text">
|
||||
|
@ -502,6 +485,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="scaledDithering">
|
||||
<property name="text">
|
||||
<string>Scaled Dithering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="useSoftwareRendererForReadbacks">
|
||||
<property name="text">
|
||||
|
@ -527,7 +517,7 @@
|
|||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -657,7 +647,7 @@
|
|||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -799,7 +789,7 @@
|
|||
<item>
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -874,7 +864,7 @@
|
|||
<item>
|
||||
<spacer name="verticalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -1008,7 +998,7 @@
|
|||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -1111,7 +1101,7 @@
|
|||
<item>
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
|
|
@ -92,6 +92,7 @@ static void SetResourcesDirectory();
|
|||
static bool SetDataDirectory();
|
||||
static bool SetCriticalFolders();
|
||||
static void SetDefaultSettings(SettingsInterface& si, bool system, bool controller);
|
||||
static void MigrateSettings();
|
||||
static void SaveSettings();
|
||||
static bool RunSetupWizard();
|
||||
static std::string GetResourcePath(std::string_view name, bool allow_override);
|
||||
|
@ -379,6 +380,7 @@ bool QtHost::InitializeConfig(std::string settings_filename)
|
|||
|
||||
EmuFolders::LoadConfig(*s_base_settings_interface.get());
|
||||
EmuFolders::EnsureFoldersExist();
|
||||
MigrateSettings();
|
||||
|
||||
// We need to create the console window early, otherwise it appears behind the main window.
|
||||
if (!Log::IsConsoleOutputEnabled() &&
|
||||
|
@ -596,6 +598,21 @@ void QtHost::SetDefaultSettings(SettingsInterface& si, bool system, bool control
|
|||
}
|
||||
}
|
||||
|
||||
void QtHost::MigrateSettings()
|
||||
{
|
||||
SmallString value;
|
||||
if (s_base_settings_interface->GetStringValue("Display", "SyncMode", &value))
|
||||
{
|
||||
s_base_settings_interface->SetBoolValue("Display", "VSync", (value == "VSync" || value == "VSyncRelaxed"));
|
||||
s_base_settings_interface->SetBoolValue(
|
||||
"Display", "OptimalFramePacing",
|
||||
(value == "VRR" || s_base_settings_interface->GetBoolValue("Display", "DisplayAllFrames", false)));
|
||||
s_base_settings_interface->DeleteValue("Display", "SyncMode");
|
||||
s_base_settings_interface->DeleteValue("Display", "DisplayAllFrames");
|
||||
s_base_settings_interface->Save();
|
||||
}
|
||||
}
|
||||
|
||||
bool EmuThread::shouldRenderToMain() const
|
||||
{
|
||||
return !Host::GetBoolSettingValue("Main", "RenderToSeparateWindow", false) && !QtHost::InNoGUIMode();
|
||||
|
@ -1551,7 +1568,7 @@ void EmuThread::run()
|
|||
if (g_gpu_device)
|
||||
{
|
||||
System::PresentDisplay(false);
|
||||
if (!g_gpu_device->IsVSyncActive())
|
||||
if (!g_gpu_device->IsVSyncEnabled())
|
||||
g_gpu_device->ThrottlePresentation();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -631,7 +631,7 @@ bool D3D11Device::BeginPresent(bool skip_present)
|
|||
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
||||
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
|
||||
// the time, when it's more like a couple of percent.
|
||||
if ((m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed) && m_gpu_timing_enabled)
|
||||
if (m_vsync_enabled && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
|
@ -648,11 +648,11 @@ void D3D11Device::EndPresent()
|
|||
{
|
||||
DebugAssert(m_num_current_render_targets == 0 && !m_current_depth_target);
|
||||
|
||||
if (m_sync_mode != DisplaySyncMode::VSync && m_sync_mode != DisplaySyncMode::VSyncRelaxed && m_gpu_timing_enabled)
|
||||
if (m_vsync_enabled && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
// DirectX has no concept of tear-or-sync. I guess if we measured times ourselves, we could implement it.
|
||||
if (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed)
|
||||
if (m_vsync_enabled)
|
||||
m_swap_chain->Present(BoolToUInt32(1), 0);
|
||||
else if (m_using_allow_tearing) // Disabled or VRR, VRR requires the allow tearing flag :/
|
||||
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||
|
|
|
@ -1108,7 +1108,7 @@ void D3D12Device::EndPresent()
|
|||
SubmitCommandList(false);
|
||||
|
||||
// DirectX has no concept of tear-or-sync. I guess if we measured times ourselves, we could implement it.
|
||||
if (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed)
|
||||
if (m_vsync_enabled)
|
||||
m_swap_chain->Present(BoolToUInt32(1), 0);
|
||||
else if (m_using_allow_tearing) // Disabled or VRR, VRR requires the allow tearing flag :/
|
||||
m_swap_chain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
|
||||
|
|
|
@ -271,11 +271,10 @@ bool GPUDevice::IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs)
|
|||
}
|
||||
|
||||
bool GPUDevice::Create(const std::string_view& adapter, const std::string_view& shader_cache_path,
|
||||
u32 shader_cache_version, bool debug_device, DisplaySyncMode sync_mode,
|
||||
bool threaded_presentation, std::optional<bool> exclusive_fullscreen_control,
|
||||
FeatureMask disabled_features, Error* error)
|
||||
u32 shader_cache_version, bool debug_device, bool vsync, bool threaded_presentation,
|
||||
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error)
|
||||
{
|
||||
m_sync_mode = sync_mode;
|
||||
m_vsync_enabled = vsync;
|
||||
m_debug_device = debug_device;
|
||||
|
||||
if (!AcquireWindow(true))
|
||||
|
@ -586,9 +585,9 @@ void GPUDevice::RenderImGui()
|
|||
}
|
||||
}
|
||||
|
||||
void GPUDevice::SetSyncMode(DisplaySyncMode mode)
|
||||
void GPUDevice::SetVSyncEnabled(bool enabled)
|
||||
{
|
||||
m_sync_mode = mode;
|
||||
m_vsync_enabled = enabled;
|
||||
}
|
||||
|
||||
void GPUDevice::UploadVertexBuffer(const void* vertices, u32 vertex_size, u32 vertex_count, u32* base_vertex)
|
||||
|
|
|
@ -562,7 +562,7 @@ public:
|
|||
virtual RenderAPI GetRenderAPI() const = 0;
|
||||
|
||||
bool Create(const std::string_view& adapter, const std::string_view& shader_cache_path, u32 shader_cache_version,
|
||||
bool debug_device, DisplaySyncMode sync_mode, bool threaded_presentation,
|
||||
bool debug_device, bool vsync, bool threaded_presentation,
|
||||
std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error);
|
||||
void Destroy();
|
||||
|
||||
|
@ -660,12 +660,8 @@ public:
|
|||
/// Renders ImGui screen elements. Call before EndPresent().
|
||||
void RenderImGui();
|
||||
|
||||
ALWAYS_INLINE DisplaySyncMode GetSyncMode() const { return m_sync_mode; }
|
||||
ALWAYS_INLINE bool IsVSyncActive() const
|
||||
{
|
||||
return (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed);
|
||||
}
|
||||
virtual void SetSyncMode(DisplaySyncMode mode);
|
||||
ALWAYS_INLINE bool IsVSyncEnabled() const { return m_vsync_enabled; }
|
||||
virtual void SetVSyncEnabled(bool enabled);
|
||||
|
||||
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
||||
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
||||
|
@ -780,7 +776,7 @@ private:
|
|||
protected:
|
||||
static Statistics s_stats;
|
||||
|
||||
DisplaySyncMode m_sync_mode = DisplaySyncMode::Disabled;
|
||||
bool m_vsync_enabled = false;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
bool m_debug_device = false;
|
||||
};
|
||||
|
|
|
@ -13,12 +13,3 @@ enum class RenderAPI : u32
|
|||
OpenGLES,
|
||||
Metal
|
||||
};
|
||||
|
||||
enum class DisplaySyncMode : u8
|
||||
{
|
||||
Disabled,
|
||||
VSync,
|
||||
VSyncRelaxed,
|
||||
VRR,
|
||||
Count
|
||||
};
|
||||
|
|
|
@ -265,7 +265,7 @@ public:
|
|||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void SetSyncMode(DisplaySyncMode mode) override;
|
||||
void SetVSyncEnabled(bool enabled) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent() override;
|
||||
|
|
|
@ -127,15 +127,14 @@ bool MetalDevice::GetHostRefreshRate(float* refresh_rate)
|
|||
return GPUDevice::GetHostRefreshRate(refresh_rate);
|
||||
}
|
||||
|
||||
void MetalDevice::SetSyncMode(DisplaySyncMode mode)
|
||||
void MetalDevice::SetVSyncEnabled(bool enabled)
|
||||
{
|
||||
m_sync_mode = mode;
|
||||
if (m_vsync_enabled == enabled)
|
||||
return;
|
||||
|
||||
m_vsync_enabled = enabled;
|
||||
if (m_layer != nil)
|
||||
{
|
||||
const bool enabled = (mode == DisplaySyncMode::VSync || mode == DisplaySyncMode::VSyncRelaxed);
|
||||
[m_layer setDisplaySyncEnabled:enabled];
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalDevice::CreateDevice(const std::string_view& adapter, bool threaded_presentation,
|
||||
|
@ -389,8 +388,7 @@ bool MetalDevice::CreateLayer()
|
|||
}
|
||||
});
|
||||
|
||||
const bool sync_enabled = (m_sync_mode == DisplaySyncMode::VSync || m_sync_mode == DisplaySyncMode::VSyncRelaxed);
|
||||
[m_layer setDisplaySyncEnabled:sync_enabled];
|
||||
[m_layer setDisplaySyncEnabled:m_vsync_enabled];
|
||||
|
||||
DebugAssert(m_layer_pass_desc == nil);
|
||||
m_layer_pass_desc = [[MTLRenderPassDescriptor renderPassDescriptor] retain];
|
||||
|
|
|
@ -139,7 +139,7 @@ bool OpenGLContextWGL::SwapBuffers()
|
|||
return ::SwapBuffers(m_dc);
|
||||
}
|
||||
|
||||
bool OpenGLContextWGL::IsCurrent()
|
||||
bool OpenGLContextWGL::IsCurrent() const
|
||||
{
|
||||
return (m_rc && wglGetCurrentContext() == m_rc);
|
||||
}
|
||||
|
|
|
@ -238,12 +238,12 @@ void OpenGLDevice::InsertDebugMessage(const char* msg)
|
|||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDevice::SetSyncMode(DisplaySyncMode mode)
|
||||
void OpenGLDevice::SetVSyncEnabled(bool enabled)
|
||||
{
|
||||
if (m_sync_mode == mode)
|
||||
if (m_vsync_enabled == enabled)
|
||||
return;
|
||||
|
||||
m_sync_mode = mode;
|
||||
m_vsync_enabled = enabled;
|
||||
SetSwapInterval();
|
||||
}
|
||||
|
||||
|
@ -582,14 +582,13 @@ void OpenGLDevice::SetSwapInterval()
|
|||
return;
|
||||
|
||||
// Window framebuffer has to be bound to call SetSwapInterval.
|
||||
const s32 interval =
|
||||
(m_sync_mode == DisplaySyncMode::VSync) ? 1 : ((m_sync_mode == DisplaySyncMode::VSyncRelaxed) ? -1 : 0);
|
||||
const s32 interval = m_vsync_enabled ? (m_gl_context->SupportsNegativeSwapInterval() ? -1 : 1) : 0;
|
||||
GLint current_fbo = 0;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
if (!m_gl_context->SetSwapInterval(interval))
|
||||
Log_WarningPrintf("Failed to set swap interval to %d", interval);
|
||||
Log_WarningFmt("Failed to set swap interval to {}", interval);
|
||||
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
}
|
||||
|
@ -1096,9 +1095,10 @@ void OpenGLDevice::UnmapUniformBuffer(u32 size)
|
|||
glBindBufferRange(GL_UNIFORM_BUFFER, 1, m_uniform_buffer->GetGLBufferId(), pos, size);
|
||||
}
|
||||
|
||||
void OpenGLDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds, GPUPipeline::RenderPassFlag feedback_loop)
|
||||
void OpenGLDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds,
|
||||
GPUPipeline::RenderPassFlag feedback_loop)
|
||||
{
|
||||
//DebugAssert(!feedback_loop); TODO
|
||||
// DebugAssert(!feedback_loop); TODO
|
||||
bool changed = (m_num_current_render_targets != num_rts || m_current_depth_target != ds);
|
||||
bool needs_ds_clear = (ds && ds->IsClearedOrInvalidated());
|
||||
bool needs_rt_clear = false;
|
||||
|
|
|
@ -100,7 +100,7 @@ public:
|
|||
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
|
||||
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
|
||||
|
||||
void SetSyncMode(DisplaySyncMode mode) override;
|
||||
void SetVSyncEnabled(bool enabled) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent() override;
|
||||
|
|
|
@ -2023,7 +2023,7 @@ bool VulkanDevice::CreateDevice(const std::string_view& adapter, bool threaded_p
|
|||
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_sync_mode, m_exclusive_fullscreen_control);
|
||||
m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, m_exclusive_fullscreen_control);
|
||||
if (!m_swap_chain)
|
||||
{
|
||||
Error::SetStringView(error, "Failed to create swap chain");
|
||||
|
@ -2244,7 +2244,7 @@ bool VulkanDevice::UpdateWindow()
|
|||
return false;
|
||||
}
|
||||
|
||||
m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_sync_mode, m_exclusive_fullscreen_control);
|
||||
m_swap_chain = VulkanSwapChain::Create(m_window_info, surface, m_vsync_enabled, m_exclusive_fullscreen_control);
|
||||
if (!m_swap_chain)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create swap chain");
|
||||
|
@ -2320,22 +2320,21 @@ std::string VulkanDevice::GetDriverInfo() const
|
|||
return ret;
|
||||
}
|
||||
|
||||
void VulkanDevice::SetSyncMode(DisplaySyncMode mode)
|
||||
void VulkanDevice::SetVSyncEnabled(bool enabled)
|
||||
{
|
||||
if (m_sync_mode == mode)
|
||||
if (m_vsync_enabled == enabled)
|
||||
return;
|
||||
|
||||
const DisplaySyncMode prev_mode = m_sync_mode;
|
||||
m_sync_mode = mode;
|
||||
m_vsync_enabled = enabled;
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
// This swap chain should not be used by the current buffer, thus safe to destroy.
|
||||
WaitForGPUIdle();
|
||||
if (!m_swap_chain->SetSyncMode(mode))
|
||||
if (!m_swap_chain->SetVSyncEnabled(enabled))
|
||||
{
|
||||
// Try switching back to the old mode..
|
||||
if (!m_swap_chain->SetSyncMode(prev_mode))
|
||||
if (!m_swap_chain->SetVSyncEnabled(!enabled))
|
||||
{
|
||||
Panic("Failed to reset old vsync mode after failure");
|
||||
m_swap_chain.reset();
|
||||
|
|
|
@ -128,7 +128,7 @@ public:
|
|||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
float GetAndResetAccumulatedGPUTime() override;
|
||||
|
||||
void SetSyncMode(DisplaySyncMode mode) override;
|
||||
void SetVSyncEnabled(bool enabled) override;
|
||||
|
||||
bool BeginPresent(bool skip_present) override;
|
||||
void EndPresent() override;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "vulkan_swap_chain.h"
|
||||
|
@ -70,22 +70,10 @@ static const char* PresentModeToString(VkPresentModeKHR mode)
|
|||
}
|
||||
}
|
||||
|
||||
static VkPresentModeKHR GetPreferredPresentModeForVsyncMode(DisplaySyncMode mode)
|
||||
{
|
||||
static constexpr std::array<VkPresentModeKHR, static_cast<size_t>(DisplaySyncMode::Count)> modes = {{
|
||||
VK_PRESENT_MODE_IMMEDIATE_KHR, // Disabled
|
||||
VK_PRESENT_MODE_FIFO_KHR, // VSync
|
||||
VK_PRESENT_MODE_FIFO_RELAXED_KHR, // VSyncRelaxed
|
||||
VK_PRESENT_MODE_IMMEDIATE_KHR, // VRR ??
|
||||
}};
|
||||
|
||||
return modes[static_cast<size_t>(mode)];
|
||||
}
|
||||
|
||||
VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR requested_present_mode,
|
||||
VulkanSwapChain::VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
||||
std::optional<bool> exclusive_fullscreen_control)
|
||||
: m_window_info(wi), m_surface(surface), m_requested_present_mode(requested_present_mode),
|
||||
m_exclusive_fullscreen_control(exclusive_fullscreen_control)
|
||||
: m_window_info(wi), m_surface(surface), m_exclusive_fullscreen_control(exclusive_fullscreen_control),
|
||||
m_vsync_enabled(vsync)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -220,13 +208,11 @@ void VulkanSwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo* wi,
|
|||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<VulkanSwapChain> VulkanSwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface,
|
||||
DisplaySyncMode sync_mode,
|
||||
std::unique_ptr<VulkanSwapChain> VulkanSwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
||||
std::optional<bool> exclusive_fullscreen_control)
|
||||
{
|
||||
const VkPresentModeKHR requested_mode = GetPreferredPresentModeForVsyncMode(sync_mode);
|
||||
std::unique_ptr<VulkanSwapChain> swap_chain =
|
||||
std::unique_ptr<VulkanSwapChain>(new VulkanSwapChain(wi, surface, requested_mode, exclusive_fullscreen_control));
|
||||
std::unique_ptr<VulkanSwapChain>(new VulkanSwapChain(wi, surface, vsync, exclusive_fullscreen_control));
|
||||
if (!swap_chain->CreateSwapChain())
|
||||
return nullptr;
|
||||
|
||||
|
@ -306,7 +292,7 @@ std::optional<VkPresentModeKHR> VulkanSwapChain::SelectPresentMode(VkSurfaceKHR
|
|||
}
|
||||
else if (requested_mode != VK_PRESENT_MODE_FIFO_KHR && CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
|
||||
{
|
||||
// Prefer mailbox over fifo for adaptive vsync/no-vsync.
|
||||
// Prefer mailbox over fifo for adaptive vsync/no-vsync. This way it'll only delay one frame.
|
||||
selected_mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
}
|
||||
else if (requested_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR && CheckForMode(VK_PRESENT_MODE_FIFO_KHR))
|
||||
|
@ -333,7 +319,11 @@ bool VulkanSwapChain::CreateSwapChain()
|
|||
|
||||
// Select swap chain format and present mode
|
||||
std::optional<VkSurfaceFormatKHR> surface_format = SelectSurfaceFormat(m_surface);
|
||||
std::optional<VkPresentModeKHR> present_mode = SelectPresentMode(m_surface, m_requested_present_mode);
|
||||
|
||||
// Prefer relaxed vsync if available, stalling is bad.
|
||||
const VkPresentModeKHR requested_mode =
|
||||
m_vsync_enabled ? VK_PRESENT_MODE_FIFO_RELAXED_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
std::optional<VkPresentModeKHR> present_mode = SelectPresentMode(m_surface, requested_mode);
|
||||
if (!surface_format.has_value() || !present_mode.has_value())
|
||||
return false;
|
||||
|
||||
|
@ -643,13 +633,12 @@ bool VulkanSwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_s
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VulkanSwapChain::SetSyncMode(DisplaySyncMode mode)
|
||||
bool VulkanSwapChain::SetVSyncEnabled(bool enabled)
|
||||
{
|
||||
const VkPresentModeKHR present_mode = GetPreferredPresentModeForVsyncMode(mode);
|
||||
if (m_requested_present_mode == present_mode)
|
||||
if (m_vsync_enabled == enabled)
|
||||
return true;
|
||||
|
||||
m_requested_present_mode = present_mode;
|
||||
m_vsync_enabled = enabled;
|
||||
|
||||
// Recreate the swap chain with the new present mode.
|
||||
Log_VerbosePrintf("Recreating swap chain to change present mode.");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
@ -25,7 +25,7 @@ public:
|
|||
static void DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface);
|
||||
|
||||
// Create a new swap chain from a pre-existing surface.
|
||||
static std::unique_ptr<VulkanSwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface, DisplaySyncMode sync_mode,
|
||||
static std::unique_ptr<VulkanSwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
||||
std::optional<bool> exclusive_fullscreen_control);
|
||||
|
||||
ALWAYS_INLINE VkSurfaceKHR GetSurface() const { return m_surface; }
|
||||
|
@ -73,10 +73,10 @@ public:
|
|||
bool ResizeSwapChain(u32 new_width = 0, u32 new_height = 0, float new_scale = 1.0f);
|
||||
|
||||
// Change vsync enabled state. This may fail as it causes a swapchain recreation.
|
||||
bool SetSyncMode(DisplaySyncMode mode);
|
||||
bool SetVSyncEnabled(bool enabled);
|
||||
|
||||
private:
|
||||
VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR requested_present_mode,
|
||||
VulkanSwapChain(const WindowInfo& wi, VkSurfaceKHR surface, bool vsync,
|
||||
std::optional<bool> exclusive_fullscreen_control);
|
||||
|
||||
static std::optional<VkSurfaceFormatKHR> SelectSurfaceFormat(VkSurfaceKHR surface);
|
||||
|
@ -111,11 +111,11 @@ private:
|
|||
std::vector<ImageSemaphores> m_semaphores;
|
||||
|
||||
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
||||
VkPresentModeKHR m_requested_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
VkPresentModeKHR m_actual_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
u32 m_current_image = 0;
|
||||
u32 m_current_semaphore = 0;
|
||||
|
||||
std::optional<VkResult> m_image_acquire_result;
|
||||
std::optional<bool> m_exclusive_fullscreen_control;
|
||||
bool m_vsync_enabled = false;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue