summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2025-05-12 13:36:55 -0400
committerGitHub <noreply@github.com>2025-05-12 13:36:55 -0400
commitcf3702cbc8be1a1bd353b4766e50687719d2a20c (patch)
tree68560be7647f77ed90055bf659f8b9725fd498c6 /Source/Core
parentf19a33340a1a27a7964b52474ff80f0eee014d9e (diff)
parentc720211542c2741b1b768a25a4a00ad3511be856 (diff)
Merge pull request #13046 from SuperSamus/vi-overclock
Add VBI Frequency Override
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/Config/MainSettings.cpp2
-rw-r--r--Source/Core/Core/Config/MainSettings.h2
-rw-r--r--Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp2
-rw-r--r--Source/Core/Core/CoreTiming.cpp9
-rw-r--r--Source/Core/Core/CoreTiming.h2
-rw-r--r--Source/Core/Core/DolphinAnalytics.cpp2
-rw-r--r--Source/Core/Core/HW/VideoInterface.cpp34
-rw-r--r--Source/Core/Core/HW/VideoInterface.h7
-rw-r--r--Source/Core/Core/IOS/DolphinDevice.cpp4
-rw-r--r--Source/Core/Core/NetPlayClient.cpp2
-rw-r--r--Source/Core/Core/NetPlayProto.h2
-rw-r--r--Source/Core/Core/NetPlayServer.cpp4
-rw-r--r--Source/Core/DolphinQt/Settings/AdvancedPane.cpp79
-rw-r--r--Source/Core/DolphinQt/Settings/AdvancedPane.h4
-rw-r--r--Source/Core/VideoCommon/FrameDumpFFMpeg.cpp13
-rw-r--r--Source/Core/VideoCommon/FrameDumpFFMpeg.h4
16 files changed, 156 insertions, 16 deletions
diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp
index 9f4d1cce17..0b6a2fd6c1 100644
--- a/Source/Core/Core/Config/MainSettings.cpp
+++ b/Source/Core/Core/Config/MainSettings.cpp
@@ -222,6 +222,8 @@ const Info<bool> MAIN_PRECISION_FRAME_TIMING{{System::Main, "Core", "PrecisionFr
DEFAULT_PRECISION_FRAME_TIMING};
const Info<float> MAIN_OVERCLOCK{{System::Main, "Core", "Overclock"}, 1.0f};
const Info<bool> MAIN_OVERCLOCK_ENABLE{{System::Main, "Core", "OverclockEnable"}, false};
+const Info<float> MAIN_VI_OVERCLOCK{{System::Main, "Core", "VIOverclock"}, 1.0f};
+const Info<bool> MAIN_VI_OVERCLOCK_ENABLE{{System::Main, "Core", "VIOverclockEnable"}, false};
const Info<bool> MAIN_RAM_OVERRIDE_ENABLE{{System::Main, "Core", "RAMOverrideEnable"}, false};
const Info<u32> MAIN_MEM1_SIZE{{System::Main, "Core", "MEM1Size"}, Memory::MEM1_SIZE_RETAIL};
const Info<u32> MAIN_MEM2_SIZE{{System::Main, "Core", "MEM2Size"}, Memory::MEM2_SIZE_RETAIL};
diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h
index 28a044d253..774824e450 100644
--- a/Source/Core/Core/Config/MainSettings.h
+++ b/Source/Core/Core/Config/MainSettings.h
@@ -128,6 +128,8 @@ extern const Info<float> MAIN_EMULATION_SPEED;
extern const Info<bool> MAIN_PRECISION_FRAME_TIMING;
extern const Info<float> MAIN_OVERCLOCK;
extern const Info<bool> MAIN_OVERCLOCK_ENABLE;
+extern const Info<float> MAIN_VI_OVERCLOCK;
+extern const Info<bool> MAIN_VI_OVERCLOCK_ENABLE;
extern const Info<bool> MAIN_RAM_OVERRIDE_ENABLE;
extern const Info<u32> MAIN_MEM1_SIZE;
extern const Info<u32> MAIN_MEM2_SIZE;
diff --git a/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp
index 5f772fd0e3..855cc181b9 100644
--- a/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp
+++ b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp
@@ -42,6 +42,8 @@ public:
layer->Set(Config::MAIN_DSP_HLE, m_settings.dsp_hle);
layer->Set(Config::MAIN_OVERCLOCK_ENABLE, m_settings.oc_enable);
layer->Set(Config::MAIN_OVERCLOCK, m_settings.oc_factor);
+ layer->Set(Config::MAIN_VI_OVERCLOCK_ENABLE, m_settings.vi_oc_enable);
+ layer->Set(Config::MAIN_VI_OVERCLOCK, m_settings.vi_oc_factor);
for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS)
layer->Set(Config::GetInfoForEXIDevice(slot), m_settings.exi_device[slot]);
layer->Set(Config::MAIN_MEMORY_CARD_SIZE, m_settings.memcard_size_override);
diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp
index 0de77f4446..f4c7d13fc0 100644
--- a/Source/Core/Core/CoreTiming.cpp
+++ b/Source/Core/Core/CoreTiming.cpp
@@ -119,7 +119,9 @@ void CoreTimingManager::Shutdown()
void CoreTimingManager::RefreshConfig()
{
m_config_oc_factor =
- Config::Get(Config::MAIN_OVERCLOCK_ENABLE) ? Config::Get(Config::MAIN_OVERCLOCK) : 1.0f;
+ (Config::Get(Config::MAIN_OVERCLOCK_ENABLE) ? Config::Get(Config::MAIN_OVERCLOCK) : 1.0f) *
+ (Config::Get(Config::MAIN_VI_OVERCLOCK_ENABLE) ? Config::Get(Config::MAIN_VI_OVERCLOCK) :
+ 1.0f);
m_config_oc_inv_factor = 1.0f / m_config_oc_factor;
m_config_sync_on_skip_idle = Config::Get(Config::MAIN_SYNC_ON_SKIP_IDLE);
@@ -482,6 +484,11 @@ bool CoreTimingManager::GetVISkip() const
return m_throttle_disable_vi_int && g_ActiveConfig.bVISkip && !Core::WantsDeterminism();
}
+float CoreTimingManager::GetOverclock() const
+{
+ return m_config_oc_factor;
+}
+
bool CoreTimingManager::UseSyncOnSkipIdle() const
{
return m_config_sync_on_skip_idle;
diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h
index d6f652c35a..ae0dccd38c 100644
--- a/Source/Core/Core/CoreTiming.h
+++ b/Source/Core/Core/CoreTiming.h
@@ -165,6 +165,8 @@ public:
// Used by VideoInterface
bool GetVISkip() const;
+ float GetOverclock() const;
+
bool UseSyncOnSkipIdle() const;
private:
diff --git a/Source/Core/Core/DolphinAnalytics.cpp b/Source/Core/Core/DolphinAnalytics.cpp
index 5613e3fea4..3551321e6f 100644
--- a/Source/Core/Core/DolphinAnalytics.cpp
+++ b/Source/Core/Core/DolphinAnalytics.cpp
@@ -365,6 +365,8 @@ void DolphinAnalytics::MakePerGameBuilder()
builder.AddData("cfg-audio-backend", Config::Get(Config::MAIN_AUDIO_BACKEND));
builder.AddData("cfg-oc-enable", Config::Get(Config::MAIN_OVERCLOCK_ENABLE));
builder.AddData("cfg-oc-factor", Config::Get(Config::MAIN_OVERCLOCK));
+ builder.AddData("cfg-vi-oc-enable", Config::Get(Config::MAIN_VI_OVERCLOCK_ENABLE));
+ builder.AddData("cfg-vi-oc-factor", Config::Get(Config::MAIN_VI_OVERCLOCK));
builder.AddData("cfg-render-to-main", Config::Get(Config::MAIN_RENDER_TO_MAIN));
if (g_video_backend)
{
diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp
index b03a8c5988..e1e822f08e 100644
--- a/Source/Core/Core/HW/VideoInterface.cpp
+++ b/Source/Core/Core/HW/VideoInterface.cpp
@@ -13,8 +13,10 @@
#include "Common/Config/Config.h"
#include "Common/Logging/Log.h"
+#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/PerformanceMetrics.h"
+#include "Core/AchievementManager.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/SYSCONFSettings.h"
@@ -41,7 +43,10 @@ VideoInterfaceManager::VideoInterfaceManager(Core::System& system) : m_system(sy
{
}
-VideoInterfaceManager::~VideoInterfaceManager() = default;
+VideoInterfaceManager::~VideoInterfaceManager()
+{
+ Config::RemoveConfigChangedCallback(m_config_changed_callback_id);
+}
static constexpr std::array<u32, 2> CLOCK_FREQUENCIES{{
27000000,
@@ -173,6 +178,22 @@ void VideoInterfaceManager::Preset(bool _bNTSC)
void VideoInterfaceManager::Init()
{
Preset(true);
+
+ m_config_changed_callback_id = Config::AddConfigChangedCallback([this] { RefreshConfig(); });
+ RefreshConfig();
+}
+
+void VideoInterfaceManager::RefreshConfig()
+{
+ m_config_vi_oc_factor =
+ Config::Get(Config::MAIN_VI_OVERCLOCK_ENABLE) ? Config::Get(Config::MAIN_VI_OVERCLOCK) : 1.0f;
+ if (AchievementManager::GetInstance().IsHardcoreModeActive() && m_config_vi_oc_factor < 1.0f)
+ {
+ Config::SetCurrent(Config::MAIN_VI_OVERCLOCK, 1.0f);
+ m_config_vi_oc_factor = 1.0f;
+ OSD::AddMessage("Minimum VBI frequency is 100% in Hardcore Mode");
+ }
+ UpdateRefreshRate();
}
void VideoInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
@@ -488,7 +509,8 @@ float VideoInterfaceManager::GetAspectRatio() const
float vertical_period = tick_length * GetTicksPerField();
float horizontal_period = tick_length * GetTicksPerHalfLine() * 2;
float vertical_active_area = active_lines * horizontal_period;
- float horizontal_active_area = tick_length * GetTicksPerSample() * active_width_samples;
+ float horizontal_active_area =
+ tick_length * GetTicksPerSample() * active_width_samples / m_config_vi_oc_factor;
// We are approximating the horizontal/vertical flyback transformers that control the
// position of the electron beam on the screen. Our flyback transformers create a
@@ -707,7 +729,11 @@ void VideoInterfaceManager::UpdateParameters()
m_vblank_timing_even.PRB - odd_even_psb_diff;
m_even_field_last_hl = m_even_field_first_hl + acv_hl - 1;
- // Refresh rate:
+ UpdateRefreshRate();
+}
+
+void VideoInterfaceManager::UpdateRefreshRate()
+{
m_target_refresh_rate_numerator = m_system.GetSystemTimers().GetTicksPerSecond() * 2;
m_target_refresh_rate_denominator = GetTicksPerEvenField() + GetTicksPerOddField();
m_target_refresh_rate =
@@ -736,7 +762,7 @@ u32 VideoInterfaceManager::GetTicksPerSample() const
u32 VideoInterfaceManager::GetTicksPerHalfLine() const
{
- return GetTicksPerSample() * m_h_timing_0.HLW;
+ return GetTicksPerSample() * m_h_timing_0.HLW / m_config_vi_oc_factor;
}
u32 VideoInterfaceManager::GetTicksPerField() const
diff --git a/Source/Core/Core/HW/VideoInterface.h b/Source/Core/Core/HW/VideoInterface.h
index 8bb1e23405..e35bc42427 100644
--- a/Source/Core/Core/HW/VideoInterface.h
+++ b/Source/Core/Core/HW/VideoInterface.h
@@ -7,6 +7,7 @@
#include <memory>
#include "Common/CommonTypes.h"
+#include "Common/Config/Config.h"
enum class FieldType;
class PointerWrap;
@@ -407,6 +408,9 @@ private:
void BeginField(FieldType field, u64 ticks);
void EndField(FieldType field, u64 ticks);
+ void RefreshConfig();
+ void UpdateRefreshRate();
+
// Registers listed in order:
UVIVerticalTimingRegister m_vertical_timing_register;
UVIDisplayControlRegister m_display_control_register;
@@ -447,6 +451,9 @@ private:
u32 m_even_field_last_hl = 0; // index last halfline of the even field
u32 m_odd_field_last_hl = 0; // index last halfline of the odd field
+ float m_config_vi_oc_factor = 0.0f;
+
+ Config::ConfigChangedCallbackID m_config_changed_callback_id;
Core::System& m_system;
};
} // namespace VideoInterface
diff --git a/Source/Core/Core/IOS/DolphinDevice.cpp b/Source/Core/Core/IOS/DolphinDevice.cpp
index 944bec41b2..c240b922b7 100644
--- a/Source/Core/Core/IOS/DolphinDevice.cpp
+++ b/Source/Core/Core/IOS/DolphinDevice.cpp
@@ -68,9 +68,7 @@ IPCReply GetCPUSpeed(Core::System& system, const IOCtlVRequest& request)
return IPCReply(IPC_EINVAL);
}
- const bool overclock_enabled = Config::Get(Config::MAIN_OVERCLOCK_ENABLE);
- const float oc = overclock_enabled ? Config::Get(Config::MAIN_OVERCLOCK) : 1.0f;
-
+ const bool oc = system.GetCoreTiming().GetOverclock();
const u32 core_clock = u32(float(system.GetSystemTimers().GetTicksPerSecond()) * oc);
auto& memory = system.GetMemory();
diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp
index aee89cbc1c..d3568ae2d1 100644
--- a/Source/Core/Core/NetPlayClient.cpp
+++ b/Source/Core/Core/NetPlayClient.cpp
@@ -862,6 +862,8 @@ void NetPlayClient::OnStartGame(sf::Packet& packet)
packet >> m_net_settings.allow_sd_writes;
packet >> m_net_settings.oc_enable;
packet >> m_net_settings.oc_factor;
+ packet >> m_net_settings.vi_oc_enable;
+ packet >> m_net_settings.vi_oc_factor;
for (auto slot : ExpansionInterface::SLOTS)
packet >> m_net_settings.exi_device[slot];
diff --git a/Source/Core/Core/NetPlayProto.h b/Source/Core/Core/NetPlayProto.h
index c97d04c2b2..62a9aba9a4 100644
--- a/Source/Core/Core/NetPlayProto.h
+++ b/Source/Core/Core/NetPlayProto.h
@@ -47,6 +47,8 @@ struct NetSettings
bool allow_sd_writes = false;
bool oc_enable = false;
float oc_factor = 0;
+ bool vi_oc_enable = false;
+ float vi_oc_factor = 0;
Common::EnumMap<ExpansionInterface::EXIDeviceType, ExpansionInterface::MAX_SLOT> exi_device{};
int memcard_size_override = -1;
diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp
index 4cff258e9b..51b3f1916b 100644
--- a/Source/Core/Core/NetPlayServer.cpp
+++ b/Source/Core/Core/NetPlayServer.cpp
@@ -1376,6 +1376,8 @@ bool NetPlayServer::SetupNetSettings()
settings.allow_sd_writes = Config::Get(Config::MAIN_ALLOW_SD_WRITES);
settings.oc_enable = Config::Get(Config::MAIN_OVERCLOCK_ENABLE);
settings.oc_factor = Config::Get(Config::MAIN_OVERCLOCK);
+ settings.vi_oc_enable = Config::Get(Config::MAIN_VI_OVERCLOCK_ENABLE);
+ settings.vi_oc_factor = Config::Get(Config::MAIN_VI_OVERCLOCK);
for (ExpansionInterface::Slot slot : ExpansionInterface::SLOTS)
{
@@ -1603,6 +1605,8 @@ bool NetPlayServer::StartGame()
spac << m_settings.allow_sd_writes;
spac << m_settings.oc_enable;
spac << m_settings.oc_factor;
+ spac << m_settings.vi_oc_enable;
+ spac << m_settings.vi_oc_factor;
for (auto slot : ExpansionInterface::SLOTS)
spac << static_cast<int>(m_settings.exi_device[slot]);
diff --git a/Source/Core/DolphinQt/Settings/AdvancedPane.cpp b/Source/Core/DolphinQt/Settings/AdvancedPane.cpp
index 917e7e898c..31e73a5b34 100644
--- a/Source/Core/DolphinQt/Settings/AdvancedPane.cpp
+++ b/Source/Core/DolphinQt/Settings/AdvancedPane.cpp
@@ -20,6 +20,7 @@
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/SystemTimers.h"
+#include "Core/HW/VideoInterface.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/System.h"
@@ -111,14 +112,47 @@ void AdvancedPane::CreateLayout()
m_cpu_clock_override_checkbox->SetDescription(
tr("Adjusts the emulated CPU's clock rate.<br><br>"
- "Higher values may make variable-framerate games run at a higher framerate, "
- "at the expense of performance. Lower values may activate a game's "
- "internal frameskip, potentially improving performance.<br><br>"
- "<b>WARNING</b>: Changing this from the default (100%) can and will "
+ "On games that have an unstable frame rate despite full emulation speed, "
+ "higher values can improve their performance, requiring a powerful device. "
+ "Lower values reduce the emulated console's performance, but improve the "
+ "emulation speed.<br><br>"
+ "WARNING: Changing this from the default (100%) can and will "
"break games and cause glitches. Do so at your own risk. "
"Please do not report bugs that occur with a non-default clock."
"<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
+ auto* vi_rate_override = new QGroupBox(tr("VBI Frequency Override"));
+ auto* vi_rate_override_layout = new QVBoxLayout();
+ vi_rate_override->setLayout(vi_rate_override_layout);
+ main_layout->addWidget(vi_rate_override);
+
+ m_vi_rate_override_checkbox =
+ new ConfigBool(tr("Enable VBI Frequency Override"), Config::MAIN_VI_OVERCLOCK_ENABLE);
+ vi_rate_override_layout->addWidget(m_vi_rate_override_checkbox);
+ connect(m_vi_rate_override_checkbox, &QCheckBox::toggled, this, &AdvancedPane::Update);
+
+ auto* vi_rate_override_slider_layout = new QHBoxLayout();
+ vi_rate_override_slider_layout->setContentsMargins(0, 0, 0, 0);
+ vi_rate_override_layout->addLayout(vi_rate_override_slider_layout);
+
+ m_vi_rate_override_slider = new QSlider(Qt::Horizontal);
+ m_vi_rate_override_slider->setRange(1, 500);
+ vi_rate_override_slider_layout->addWidget(m_vi_rate_override_slider);
+
+ m_vi_rate_override_slider_label = new QLabel();
+ vi_rate_override_slider_layout->addWidget(m_vi_rate_override_slider_label);
+
+ m_vi_rate_override_checkbox->SetDescription(
+ tr("Adjusts the VBI frequency. Also adjusts the emulated CPU's "
+ "clock rate, to keep it relatively the same.<br><br>"
+ "Makes games run at a different frame rate, making the emulation less "
+ "demanding when lowered, or improving smoothness when increased. This may "
+ "affect gameplay speed, as it is often tied to the frame rate.<br><br>"
+ "WARNING: Changing this from the default (100%) can and will "
+ "break games and cause glitches. Do so at your own risk. "
+ "Please do not report bugs that occur with a non-default frequency."
+ "<br><br><dolphin_emphasis>If unsure, leave this unchecked.</dolphin_emphasis>"));
+
auto* ram_override = new QGroupBox(tr("Memory Override"));
auto* ram_override_layout = new QVBoxLayout();
ram_override->setLayout(ram_override_layout);
@@ -199,6 +233,18 @@ void AdvancedPane::ConnectLayout()
Update();
});
+ connect(m_vi_rate_override_slider, &QSlider::valueChanged, [this](int oc_factor) {
+ const float factor = m_vi_rate_override_slider->value() / 100.f;
+ Config::SetBaseOrCurrent(Config::MAIN_VI_OVERCLOCK, factor);
+ Update();
+ });
+
+ m_ram_override_checkbox->setChecked(Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE));
+ connect(m_ram_override_checkbox, &QCheckBox::toggled, [this](bool enable_ram_override) {
+ Config::SetBaseOrCurrent(Config::MAIN_RAM_OVERRIDE_ENABLE, enable_ram_override);
+ Update();
+ });
+
connect(m_mem1_override_slider, &QSlider::valueChanged, [this](int slider_value) {
const u32 mem1_size = m_mem1_override_slider->value() * 0x100000;
Config::SetBaseOrCurrent(Config::MAIN_MEM1_SIZE, mem1_size);
@@ -222,6 +268,7 @@ void AdvancedPane::Update()
{
const bool is_uninitialized = Core::IsUninitialized(Core::System::GetInstance());
const bool enable_cpu_clock_override_widgets = Config::Get(Config::MAIN_OVERCLOCK_ENABLE);
+ const bool enable_vi_rate_override_widgets = Config::Get(Config::MAIN_VI_OVERCLOCK_ENABLE);
const bool enable_ram_override_widgets = Config::Get(Config::MAIN_RAM_OVERRIDE_ENABLE);
const bool enable_custom_rtc_widgets =
Config::Get(Config::MAIN_CUSTOM_RTC_ENABLE) && is_uninitialized;
@@ -264,6 +311,30 @@ void AdvancedPane::Update()
return tr("%1% (%2 MHz)").arg(QString::number(percent), QString::number(clock));
}());
+ QFont vi_bf = font();
+ vi_bf.setBold(Config::GetActiveLayerForConfig(Config::MAIN_VI_OVERCLOCK_ENABLE) !=
+ Config::LayerType::Base);
+ m_vi_rate_override_checkbox->setFont(vi_bf);
+ m_vi_rate_override_checkbox->setChecked(enable_vi_rate_override_widgets);
+
+ m_vi_rate_override_slider->setEnabled(enable_vi_rate_override_widgets);
+ m_vi_rate_override_slider_label->setEnabled(enable_vi_rate_override_widgets);
+
+ {
+ const QSignalBlocker blocker(m_vi_rate_override_slider);
+ m_vi_rate_override_slider->setValue(
+ static_cast<int>(std::round(Config::Get(Config::MAIN_VI_OVERCLOCK) * 100.f)));
+ }
+
+ m_vi_rate_override_slider_label->setText([] {
+ int percent = static_cast<int>(std::round(Config::Get(Config::MAIN_VI_OVERCLOCK) * 100.f));
+ float vps =
+ static_cast<float>(Core::System::GetInstance().GetVideoInterface().GetTargetRefreshRate());
+ if (vps == 0.0f || !Config::Get(Config::MAIN_VI_OVERCLOCK_ENABLE))
+ vps = 59.94f * Config::Get(Config::MAIN_VI_OVERCLOCK);
+ return tr("%1% (%2 VPS)").arg(QString::number(percent), QString::number(vps, 'f', 2));
+ }());
+
m_ram_override_checkbox->setEnabled(is_uninitialized);
SignalBlocking(m_ram_override_checkbox)->setChecked(enable_ram_override_widgets);
diff --git a/Source/Core/DolphinQt/Settings/AdvancedPane.h b/Source/Core/DolphinQt/Settings/AdvancedPane.h
index ff449aca65..6493e71a73 100644
--- a/Source/Core/DolphinQt/Settings/AdvancedPane.h
+++ b/Source/Core/DolphinQt/Settings/AdvancedPane.h
@@ -39,6 +39,10 @@ private:
QSlider* m_cpu_clock_override_slider;
QLabel* m_cpu_clock_override_slider_label;
+ ConfigBool* m_vi_rate_override_checkbox;
+ QSlider* m_vi_rate_override_slider;
+ QLabel* m_vi_rate_override_slider_label;
+
ConfigBool* m_custom_rtc_checkbox;
QDateTimeEdit* m_custom_rtc_datetime;
diff --git a/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp b/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp
index ea8bda7893..44ec0a6a27 100644
--- a/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp
+++ b/Source/Core/VideoCommon/FrameDumpFFMpeg.cpp
@@ -62,13 +62,13 @@ struct FrameDumpContext
namespace
{
-AVRational GetTimeBaseForCurrentRefreshRate()
+AVRational GetTimeBaseForCurrentRefreshRate(s64 max_denominator)
{
auto& vi = Core::System::GetInstance().GetVideoInterface();
int num;
int den;
av_reduce(&num, &den, int(vi.GetTargetRefreshRateDenominator()),
- int(vi.GetTargetRefreshRateNumerator()), std::numeric_limits<int>::max());
+ int(vi.GetTargetRefreshRateNumerator()), max_denominator);
return AVRational{num, den};
}
@@ -248,11 +248,16 @@ bool FFMpegFrameDump::CreateVideoFile()
return false;
}
+ m_max_denominator = std::numeric_limits<s64>::max();
+
// Force XVID FourCC for better compatibility when using H.263
if (codec->id == AV_CODEC_ID_MPEG4)
+ {
m_context->codec->codec_tag = MKTAG('X', 'V', 'I', 'D');
+ m_max_denominator = std::numeric_limits<unsigned short>::max();
+ }
- const auto time_base = GetTimeBaseForCurrentRefreshRate();
+ const auto time_base = GetTimeBaseForCurrentRefreshRate(m_max_denominator);
INFO_LOG_FMT(FRAMEDUMP, "Creating video file: {} x {} @ {}/{} fps", m_context->width,
m_context->height, time_base.den, time_base.num);
@@ -535,7 +540,7 @@ FrameState FFMpegFrameDump::FetchState(u64 ticks, int frame_number) const
state.frame_number = frame_number;
state.savestate_index = m_savestate_index;
- const auto time_base = GetTimeBaseForCurrentRefreshRate();
+ const auto time_base = GetTimeBaseForCurrentRefreshRate(m_max_denominator);
state.refresh_rate_num = time_base.den;
state.refresh_rate_den = time_base.num;
return state;
diff --git a/Source/Core/VideoCommon/FrameDumpFFMpeg.h b/Source/Core/VideoCommon/FrameDumpFFMpeg.h
index 8431e24274..87387b3e41 100644
--- a/Source/Core/VideoCommon/FrameDumpFFMpeg.h
+++ b/Source/Core/VideoCommon/FrameDumpFFMpeg.h
@@ -4,6 +4,7 @@
#pragma once
#include <ctime>
+#include <limits>
#include <memory>
#include "Common/CommonTypes.h"
@@ -62,6 +63,9 @@ private:
// Used for filename generation.
std::time_t m_start_time = {};
u32 m_file_index = 0;
+
+ // Some codecs (like MPEG4) have a limit to this
+ int64_t m_max_denominator = std::numeric_limits<s64>::max();
};
#if !defined(HAVE_FFMPEG)