Merge pull request #13046 from SuperSamus/vi-overclock

Add VBI Frequency Override
This commit is contained in:
JMC47
2025-05-12 13:36:55 -04:00
committed by GitHub
20 changed files with 189 additions and 17 deletions

View File

@ -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};

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -165,6 +165,8 @@ public:
// Used by VideoInterface
bool GetVISkip() const;
float GetOverclock() const;
bool UseSyncOnSkipIdle() const;
private:

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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];

View File

@ -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;

View File

@ -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]);

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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)