mirror of https://github.com/PCSX2/pcsx2.git
GS: Add sync to host refresh rate option
This commit is contained in:
parent
a05a655037
commit
ec43661664
pcsx2-qt
pcsx2
|
@ -27,6 +27,7 @@
|
|||
#include "common/StringUtil.h"
|
||||
|
||||
#include "pcsx2/CDVD/CDVD.h"
|
||||
#include "pcsx2/Counters.h"
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||
#include "pcsx2/GS.h"
|
||||
|
@ -361,6 +362,9 @@ void EmuThread::setFullscreen(bool fullscreen)
|
|||
m_is_fullscreen = fullscreen;
|
||||
GetMTGS().UpdateDisplayWindow();
|
||||
GetMTGS().WaitGS();
|
||||
|
||||
// If we're using exclusive fullscreen, the refresh rate may have changed.
|
||||
UpdateVSyncRate();
|
||||
}
|
||||
|
||||
void EmuThread::setSurfaceless(bool surfaceless)
|
||||
|
|
|
@ -435,6 +435,7 @@ struct Pcsx2Config
|
|||
PCRTCOffsets : 1,
|
||||
IntegerScaling : 1,
|
||||
LinearPresent : 1,
|
||||
SyncToHostRefreshRate : 1,
|
||||
UseDebugDevice : 1,
|
||||
UseBlitSwapChain : 1,
|
||||
DisableShaderCache : 1,
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
#include "ps2/HwInternal.h"
|
||||
#include "Sio.h"
|
||||
#include "HostDisplay.h"
|
||||
#include "SPU2/spu2.h"
|
||||
|
||||
#ifndef PCSX2_CORE
|
||||
#include "gui/App.h"
|
||||
|
@ -46,6 +48,7 @@ using namespace Threading;
|
|||
extern u8 psxhblankgate;
|
||||
static const uint EECNT_FUTURE_TARGET = 0x10000000;
|
||||
static int gates = 0;
|
||||
static bool s_use_vsync_for_timing = false;
|
||||
|
||||
uint g_FrameCount = 0;
|
||||
|
||||
|
@ -346,6 +349,39 @@ double GetVerticalFrequency()
|
|||
}
|
||||
}
|
||||
|
||||
static double AdjustToHostRefreshRate(double vertical_frequency, double frame_limit)
|
||||
{
|
||||
if (!EmuConfig.GS.SyncToHostRefreshRate || EmuConfig.GS.LimitScalar != 1.0)
|
||||
{
|
||||
SPU2SetDeviceSampleRateMultiplier(1.0);
|
||||
s_use_vsync_for_timing = false;
|
||||
return frame_limit;
|
||||
}
|
||||
|
||||
float host_refresh_rate;
|
||||
if (!Host::GetHostDisplay()->GetHostRefreshRate(&host_refresh_rate))
|
||||
{
|
||||
Console.Warning("Cannot sync to host refresh since the query failed.");
|
||||
SPU2SetDeviceSampleRateMultiplier(1.0);
|
||||
s_use_vsync_for_timing = false;
|
||||
return frame_limit;
|
||||
}
|
||||
|
||||
const double ratio = host_refresh_rate / vertical_frequency;
|
||||
const bool syncing_to_host = (ratio >= 0.95f && ratio <= 1.05f);
|
||||
s_use_vsync_for_timing = (syncing_to_host && !EmuConfig.GS.SkipDuplicateFrames && EmuConfig.GS.VsyncEnable != VsyncMode::Off);
|
||||
Console.WriteLn("Refresh rate: Host=%fhz Guest=%fhz Ratio=%f - %s %s", host_refresh_rate,
|
||||
vertical_frequency, ratio, syncing_to_host ? "can sync" : "can't sync",
|
||||
s_use_vsync_for_timing ? "and using vsync for pacing" : "and using sleep for pacing");
|
||||
|
||||
if (!syncing_to_host)
|
||||
return frame_limit;
|
||||
|
||||
frame_limit *= ratio;
|
||||
SPU2SetDeviceSampleRateMultiplier(ratio);
|
||||
return frame_limit;
|
||||
}
|
||||
|
||||
u32 UpdateVSyncRate()
|
||||
{
|
||||
// Notice: (and I probably repeat this elsewhere, but it's worth repeating)
|
||||
|
@ -357,7 +393,7 @@ u32 UpdateVSyncRate()
|
|||
const double vertical_frequency = GetVerticalFrequency();
|
||||
|
||||
const double frames_per_second = vertical_frequency / 2.0;
|
||||
const double frame_limit = frames_per_second * EmuConfig.GS.LimitScalar;
|
||||
const double frame_limit = AdjustToHostRefreshRate(vertical_frequency, frames_per_second * EmuConfig.GS.LimitScalar);
|
||||
|
||||
const double tick_rate = GetTickFrequency() / 2.0;
|
||||
const s64 ticks = static_cast<s64>(tick_rate / std::max(frame_limit, 1.0));
|
||||
|
@ -515,7 +551,7 @@ static __fi void frameLimitUpdateCore()
|
|||
static __fi void frameLimit()
|
||||
{
|
||||
// Framelimiter off in settings? Framelimiter go brrr.
|
||||
if (EmuConfig.GS.LimitScalar == 0.0)
|
||||
if (EmuConfig.GS.LimitScalar == 0.0 || s_use_vsync_for_timing)
|
||||
{
|
||||
frameLimitUpdateCore();
|
||||
return;
|
||||
|
|
|
@ -1371,6 +1371,7 @@ void GSApp::Init()
|
|||
m_default_configuration["extrathreads_height"] = "4";
|
||||
m_default_configuration["filter"] = std::to_string(static_cast<s8>(BiFiltering::PS2));
|
||||
m_default_configuration["FMVSoftwareRendererSwitch"] = "0";
|
||||
m_default_configuration["FullscreenMode"] = "";
|
||||
m_default_configuration["fxaa"] = "0";
|
||||
m_default_configuration["GSDumpCompression"] = "0";
|
||||
m_default_configuration["HWDisableReadbacks"] = "0";
|
||||
|
|
|
@ -502,7 +502,6 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
|
||||
const int fb_sprite_blits = g_perfmon.GetDisplayFramebufferSpriteBlits();
|
||||
const bool fb_sprite_frame = (fb_sprite_blits > 0);
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame);
|
||||
|
||||
bool skip_frame = m_frameskip;
|
||||
if (GSConfig.SkipDuplicateFrames)
|
||||
|
@ -540,6 +539,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
if (Host::BeginPresentFrame(true))
|
||||
Host::EndPresentFrame();
|
||||
g_gs_device->RestoreAPIState();
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -572,6 +572,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
|||
PerformanceMetrics::OnGPUPresent(Host::GetHostDisplay()->GetAndResetAccumulatedGPUTime());
|
||||
}
|
||||
g_gs_device->RestoreAPIState();
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame);
|
||||
|
||||
// snapshot
|
||||
// wx is dumb and call this from the UI thread...
|
||||
|
|
|
@ -301,6 +301,7 @@ Pcsx2Config::GSOptions::GSOptions()
|
|||
PCRTCOffsets = false;
|
||||
IntegerScaling = false;
|
||||
LinearPresent = true;
|
||||
SyncToHostRefreshRate = false;
|
||||
UseDebugDevice = false;
|
||||
UseBlitSwapChain = false;
|
||||
DisableShaderCache = false;
|
||||
|
@ -464,6 +465,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
|||
|
||||
#ifdef PCSX2_CORE
|
||||
// These are loaded from GSWindow in wx.
|
||||
SettingsWrapBitBool(SyncToHostRefreshRate);
|
||||
SettingsWrapEnumEx(AspectRatio, "AspectRatio", AspectRatioNames);
|
||||
SettingsWrapEnumEx(FMVAspectRatioSwitch, "FMVAspectRatioSwitch", FMVAspectRatioSwitchNames);
|
||||
|
||||
|
|
|
@ -149,14 +149,6 @@ bool SndBuffer::CheckUnderrunStatus(int& nSamples, int& quietSampleCount)
|
|||
return true;
|
||||
}
|
||||
|
||||
void SndBuffer::_InitFail()
|
||||
{
|
||||
// If a failure occurs, just initialize the NoSound driver. This'll allow
|
||||
// the game to emulate properly (hopefully), albeit without sound.
|
||||
OutputModule = FindOutputModuleById(NullOut->GetIdent());
|
||||
mods[OutputModule]->Init();
|
||||
}
|
||||
|
||||
int SndBuffer::_GetApproximateDataInBuffer()
|
||||
{
|
||||
// WARNING: not necessarily 100% up to date by the time it's used, but it will have to do.
|
||||
|
@ -357,13 +349,10 @@ void SndBuffer::_WriteSamples(StereoOut32* bData, int nSamples)
|
|||
_WriteSamples_Safe(bData, nSamples);
|
||||
}
|
||||
|
||||
void SndBuffer::Init()
|
||||
bool SndBuffer::Init()
|
||||
{
|
||||
if (mods[OutputModule] == nullptr)
|
||||
{
|
||||
_InitFail();
|
||||
return;
|
||||
}
|
||||
if (!mods[OutputModule])
|
||||
return false;
|
||||
|
||||
// initialize sound buffer
|
||||
// Buffer actually attempts to run ~50%, so allocate near double what
|
||||
|
@ -372,33 +361,25 @@ void SndBuffer::Init()
|
|||
m_rpos = 0;
|
||||
m_wpos = 0;
|
||||
|
||||
try
|
||||
{
|
||||
const float latencyMS = SndOutLatencyMS * 16;
|
||||
m_size = GetAlignedBufferSize((int)(latencyMS * SampleRate / 1000.0f));
|
||||
printf("%d SampleRate: \n", SampleRate);
|
||||
m_buffer = new StereoOut32[m_size];
|
||||
m_underrun_freeze = false;
|
||||
|
||||
sndTempBuffer = new StereoOut32[SndOutPacketSize];
|
||||
sndTempBuffer16 = new StereoOut16[SndOutPacketSize * 2]; // in case of leftovers.
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
{
|
||||
// out of memory exception (most likely)
|
||||
|
||||
SysMessage("Out of memory error occurred while initializing SPU2.");
|
||||
_InitFail();
|
||||
return;
|
||||
}
|
||||
const float latencyMS = SndOutLatencyMS * 16;
|
||||
m_size = GetAlignedBufferSize((int)(latencyMS * SampleRate / 1000.0f));
|
||||
m_buffer = new StereoOut32[m_size];
|
||||
m_underrun_freeze = false;
|
||||
|
||||
sndTempBuffer = new StereoOut32[SndOutPacketSize];
|
||||
sndTempBuffer16 = new StereoOut16[SndOutPacketSize * 2]; // in case of leftovers.
|
||||
sndTempProgress = 0;
|
||||
|
||||
soundtouchInit(); // initializes the timestretching
|
||||
|
||||
// initialize module
|
||||
if (!mods[OutputModule]->Init())
|
||||
_InitFail();
|
||||
{
|
||||
Cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SndBuffer::Cleanup()
|
||||
|
|
|
@ -586,7 +586,6 @@ private:
|
|||
static float eTempo;
|
||||
static int ssFreeze;
|
||||
|
||||
static void _InitFail();
|
||||
static bool CheckUnderrunStatus(int& nSamples, int& quietSampleCount);
|
||||
|
||||
static void soundtouchInit();
|
||||
|
@ -614,7 +613,7 @@ private:
|
|||
|
||||
public:
|
||||
static void UpdateTempoChangeAsyncMixing();
|
||||
static void Init();
|
||||
static bool Init();
|
||||
static void Cleanup();
|
||||
static void Write(const StereoOut32& Sample);
|
||||
static void ClearContents();
|
||||
|
|
|
@ -33,8 +33,11 @@ using namespace Threading;
|
|||
|
||||
std::recursive_mutex mtx_SPU2Status;
|
||||
|
||||
static int ConsoleSampleRate = 48000;
|
||||
int SampleRate = 48000;
|
||||
|
||||
static double DeviceSampleRateMultiplier = 1.0;
|
||||
|
||||
static bool IsOpened = false;
|
||||
static bool IsInitialized = false;
|
||||
|
||||
|
@ -120,9 +123,44 @@ void SPU2writeDMA7Mem(u16* pMem, u32 size)
|
|||
Cores[1].DoDMAwrite(pMem, size);
|
||||
}
|
||||
|
||||
s32 SPU2reset(PS2Modes isRunningPSXMode)
|
||||
static void SPU2InitSndBuffer()
|
||||
{
|
||||
int requiredSampleRate = (isRunningPSXMode == PS2Modes::PSX) ? 44100 : 48000;
|
||||
Console.WriteLn("Initializing SndBuffer at sample rate of %u...", SampleRate);
|
||||
if (SndBuffer::Init())
|
||||
return;
|
||||
|
||||
if (SampleRate != ConsoleSampleRate)
|
||||
{
|
||||
// TODO: Resample on our side...
|
||||
const int original_sample_rate = SampleRate;
|
||||
Console.Error("Failed to init SPU2 at adjusted sample rate %u, trying console rate.", SampleRate);
|
||||
SampleRate = ConsoleSampleRate;
|
||||
if (SndBuffer::Init())
|
||||
return;
|
||||
|
||||
SampleRate = original_sample_rate;
|
||||
}
|
||||
|
||||
// just use nullout
|
||||
OutputModule = FindOutputModuleById(NullOut->GetIdent());
|
||||
if (!SndBuffer::Init())
|
||||
pxFailRel("Failed to initialize nullout.");
|
||||
}
|
||||
|
||||
static void SPU2UpdateSampleRate()
|
||||
{
|
||||
const int new_sample_rate = static_cast<int>(std::round(static_cast<double>(ConsoleSampleRate) * DeviceSampleRateMultiplier));
|
||||
if (SampleRate == new_sample_rate)
|
||||
return;
|
||||
|
||||
SndBuffer::Cleanup();
|
||||
SampleRate = new_sample_rate;
|
||||
SPU2InitSndBuffer();
|
||||
}
|
||||
|
||||
static void SPU2InternalReset(PS2Modes isRunningPSXMode)
|
||||
{
|
||||
ConsoleSampleRate = (isRunningPSXMode == PS2Modes::PSX) ? 44100 : 48000;
|
||||
|
||||
if (isRunningPSXMode == PS2Modes::PS2)
|
||||
{
|
||||
|
@ -136,25 +174,24 @@ s32 SPU2reset(PS2Modes isRunningPSXMode)
|
|||
Cores[0].Init(0);
|
||||
Cores[1].Init(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (SampleRate != requiredSampleRate)
|
||||
{
|
||||
SampleRate = requiredSampleRate;
|
||||
SndBuffer::Cleanup();
|
||||
try
|
||||
{
|
||||
SndBuffer::Init();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
fprintf(stderr, "SPU2 Error: Could not initialize device, or something.\nReason: %s", ex.what());
|
||||
SPU2close();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
s32 SPU2reset(PS2Modes isRunningPSXMode)
|
||||
{
|
||||
SPU2InternalReset(isRunningPSXMode);
|
||||
SPU2UpdateSampleRate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SPU2SetDeviceSampleRateMultiplier(double multiplier)
|
||||
{
|
||||
if (DeviceSampleRateMultiplier == multiplier)
|
||||
return;
|
||||
|
||||
DeviceSampleRateMultiplier = multiplier;
|
||||
SPU2UpdateSampleRate();
|
||||
}
|
||||
|
||||
s32 SPU2init()
|
||||
{
|
||||
assert(regtable[0x400] == nullptr);
|
||||
|
@ -207,7 +244,7 @@ s32 SPU2init()
|
|||
}
|
||||
}
|
||||
|
||||
SPU2reset(PS2Modes::PS2);
|
||||
SPU2InternalReset(PS2Modes::PS2);
|
||||
|
||||
DMALogOpen();
|
||||
InitADSR();
|
||||
|
@ -289,7 +326,8 @@ s32 SPU2open()
|
|||
|
||||
try
|
||||
{
|
||||
SndBuffer::Init();
|
||||
SampleRate = static_cast<int>(std::round(static_cast<double>(ConsoleSampleRate) * DeviceSampleRateMultiplier));
|
||||
SPU2InitSndBuffer();
|
||||
|
||||
#if defined(_WIN32) && !defined(PCSX2_CORE)
|
||||
DspLoadLibrary(dspPlugin, dspPluginModule);
|
||||
|
|
|
@ -33,6 +33,7 @@ s32 SPU2open();
|
|||
void SPU2close();
|
||||
void SPU2shutdown();
|
||||
void SPU2SetOutputPaused(bool paused);
|
||||
void SPU2SetDeviceSampleRateMultiplier(double multiplier);
|
||||
void SPU2write(u32 mem, u16 value);
|
||||
u16 SPU2read(u32 mem);
|
||||
|
||||
|
|
|
@ -886,6 +886,7 @@ void AppConfig::GSWindowOptions::LoadSave(IniInterface& ini)
|
|||
// WARNING: array must be NULL terminated to compute it size
|
||||
NULL};
|
||||
|
||||
g_Conf->EmuOptions.GS.SyncToHostRefreshRate = ini.EntryBitBool(L"SyncToHostRefreshRate", g_Conf->EmuOptions.GS.SyncToHostRefreshRate, g_Conf->EmuOptions.GS.SyncToHostRefreshRate);
|
||||
ini.EnumEntry(L"AspectRatio", g_Conf->EmuOptions.GS.AspectRatio, AspectRatioNames, g_Conf->EmuOptions.GS.AspectRatio);
|
||||
if (ini.IsLoading())
|
||||
EmuConfig.CurrentAspectRatio = g_Conf->EmuOptions.GS.AspectRatio;
|
||||
|
|
|
@ -285,6 +285,7 @@ namespace Panels
|
|||
|
||||
pxCheckBox* m_check_HideMouse;
|
||||
pxCheckBox* m_check_DclickFullscreen;
|
||||
pxCheckBox* m_check_SyncToHostRefreshRate;
|
||||
|
||||
wxTextCtrl* m_text_WindowWidth;
|
||||
wxTextCtrl* m_text_WindowHeight;
|
||||
|
|
|
@ -74,6 +74,7 @@ Panels::GSWindowSettingsPanel::GSWindowSettingsPanel(wxWindow* parent)
|
|||
// Implement custom hotkeys (Alt + Enter) with translatable string intact + not blank in GUI.
|
||||
m_check_Fullscreen = new pxCheckBox(this, _("Start in fullscreen mode by default") + wxString(" (") + wxGetApp().GlobalAccels->findKeycodeWithCommandId("FullscreenToggle").toTitleizedString() + wxString(")"));
|
||||
m_check_DclickFullscreen = new pxCheckBox(this, _("Double-click toggles fullscreen mode"));
|
||||
m_check_SyncToHostRefreshRate = new pxCheckBox(this, _("Sync To Host Refresh Rate"));
|
||||
|
||||
m_combo_FMVAspectRatioSwitch->SetToolTip(pxEt(L"Off: Disables temporary aspect ratio switch. (It will use the above setting from Aspect Ratio instead of FMV Aspect Ratio Override.)\n\n"
|
||||
L"Auto 4:3/3:2: Temporarily switch to a 4:3 aspect ratio while an FMV plays to correctly display a 4:3 FMV. Will use 3:2 is the resolution is 480P\n\n"
|
||||
|
@ -136,6 +137,7 @@ Panels::GSWindowSettingsPanel::GSWindowSettingsPanel(wxWindow* parent)
|
|||
|
||||
*this += m_check_Fullscreen;
|
||||
*this += m_check_DclickFullscreen;
|
||||
*this += m_check_SyncToHostRefreshRate;
|
||||
*this += new wxStaticLine(this) | StdExpand();
|
||||
|
||||
*this += s_vsync | StdExpand();
|
||||
|
@ -169,6 +171,7 @@ void Panels::GSWindowSettingsPanel::ApplyConfigToGui(AppConfig& configToApply, i
|
|||
m_text_Zoom->ChangeValue(wxString::FromDouble(gsconf.Zoom, 2));
|
||||
|
||||
m_check_DclickFullscreen->SetValue(conf.IsToggleFullscreenOnDoubleClick);
|
||||
m_check_SyncToHostRefreshRate->SetValue(gsconf.SyncToHostRefreshRate);
|
||||
|
||||
m_text_WindowWidth->ChangeValue(wxsFormat(L"%d", conf.WindowSize.GetWidth()));
|
||||
m_text_WindowHeight->ChangeValue(wxsFormat(L"%d", conf.WindowSize.GetHeight()));
|
||||
|
@ -199,6 +202,7 @@ void Panels::GSWindowSettingsPanel::Apply()
|
|||
gsconf.VsyncEnable = static_cast<VsyncMode>(m_combo_vsync->GetSelection());
|
||||
|
||||
appconf.IsToggleFullscreenOnDoubleClick = m_check_DclickFullscreen->GetValue();
|
||||
gsconf.SyncToHostRefreshRate = m_check_SyncToHostRefreshRate->GetValue();
|
||||
|
||||
long xr, yr = 1;
|
||||
|
||||
|
|
Loading…
Reference in New Issue