sysclk: add VRR

This commit is contained in:
souldbminersmwc
2026-03-12 20:47:11 -04:00
parent b601105998
commit d7e5c38a62
9 changed files with 148 additions and 20 deletions

View File

@@ -45,6 +45,7 @@ typedef struct
u16 iddq[HorizonOCSpeedo_EnumMax];
GpuSchedulingMode gpuSchedulingMode;
bool isSysDockInstalled;
bool isSaltyNXInstalled;
u8 maxDisplayFreq;
u8 dramID;
bool isDram8GB;

View File

@@ -66,6 +66,8 @@ typedef enum {
HorizonOCConfigValue_RAMVoltUsageDisplayMode,
HorizonOCConfigValue_VRR,
KipConfigValue_custRev,
// KipConfigValue_mtcConf,
KipConfigValue_hpMode,
@@ -250,6 +252,9 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
case HorizonOCConfigValue_RAMVoltUsageDisplayMode:
return pretty ? "RAM Voltage / Usage Display Mode" : "ram_volt_usage_display_mode";
case HorizonOCConfigValue_VRR:
return pretty ? "Variable Refresh Rate (VRR)" : "vrr";
// KIP config values
case KipConfigValue_custRev:
return pretty ? "Custom Revision" : "kip_cust_rev";
@@ -430,6 +435,7 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
case HorizonOCConfigValue_GPUScheduling:
case HorizonOCConfigValue_LiveCpuUv:
case HorizonOCConfigValue_GPUSchedulingMethod:
case HorizonOCConfigValue_VRR:
return 0ULL;
case HocClkConfigValue_EristaMaxCpuClock:
return 1785ULL;
@@ -479,6 +485,7 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case HorizonOCConfigValue_EnableExperimentalSettings:
case HorizonOCConfigValue_LiveCpuUv:
case HorizonOCConfigValue_GPUSchedulingMethod:
case HorizonOCConfigValue_VRR:
return (input & 0x1) == input;
case KipConfigValue_custRev:

View File

@@ -25,6 +25,8 @@
tsl::elm::ListItem* SpeedoItem = NULL;
tsl::elm::ListItem* IddqItem = NULL;
tsl::elm::ListItem* sysdockStatusItem = NULL;
tsl::elm::ListItem* saltyNXStatusItem = NULL;
ImageElement* CatImage = NULL;
HideableCategoryHeader* CatHeader = NULL;
HideableCustomDrawer* CatSpacer = NULL;
@@ -57,6 +59,10 @@ void AboutGui::listUI()
new tsl::elm::ListItem("sys-dock status:");
this->listElement->addItem(sysdockStatusItem);
saltyNXStatusItem =
new tsl::elm::ListItem("SaltyNX status:");
this->listElement->addItem(saltyNXStatusItem);
this->listElement->addItem(
new tsl::elm::CategoryHeader("Credits")
);
@@ -227,4 +233,5 @@ void AboutGui::refresh()
SpeedoItem->setValue(strings[0]);
IddqItem->setValue(strings[1]);
sysdockStatusItem->setValue(this->context->isSysDockInstalled ? "Installed" : "Not Installed");
saltyNXStatusItem->setValue(this->context->isSaltyNXInstalled ? "Installed" : "Not Installed");
}

View File

@@ -168,8 +168,10 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
renderer->drawString(displayStrings[21], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Bat voltage
renderer->drawString(displayStrings[23], false, positions[2] - 2, y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Bat Age
renderer->drawString(labels[14], false, positions[4], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // FPS label
renderer->drawString(displayStrings[26], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // FPS
if(this->context->isSaltyNXInstalled) {
renderer->drawString(labels[14], false, positions[4], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // FPS label
renderer->drawString(displayStrings[26], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // FPS
}
y+=20;
}
@@ -285,12 +287,13 @@ void BaseMenuGui::refresh()
sprintf(displayStrings[24], "%u%%", context->partLoad[HocClkPartLoad_FAN]);
sprintf(displayStrings[25], "%u Hz", context->realFreqs[HorizonOCModule_Display]);
if(context->fps == 254) {
strcpy(displayStrings[26], "N/A");
} else {
memset(displayStrings[26], 0, sizeof(displayStrings[26]));
sprintf(displayStrings[26], "%u", context->fps);
if(this->context->isSaltyNXInstalled) {
if(context->fps == 254) {
strcpy(displayStrings[26], "N/A");
} else {
memset(displayStrings[26], 0, sizeof(displayStrings[26]));
sprintf(displayStrings[26], "%u", context->fps);
}
}
}

View File

@@ -366,6 +366,13 @@ void MiscGui::listUI()
addConfigButton(HorizonOCConfigValue_RAMVoltUsageDisplayMode, "RAM Voltage Display Mode", ValueRange(0, 12, 1, "", 0), "RAM Voltage Display Mode", &thresholdsDisabled, {}, ramVoltDispModes, false);
addConfigButton(
HocClkConfigValue_LiteTDPLimit,
"Polling Interval",
ValueRange(50, 1000, 50, "ms", 1),
"Polling Interval",
&thresholdsDisabled
);
tsl::elm::ListItem* safetySubmenu = new tsl::elm::ListItem("Safety Settings");
safetySubmenu->setClickListener([](u64 keys) {
if (keys & HidNpadButton_A) {
@@ -536,6 +543,7 @@ public:
protected:
void listUI() override {
addConfigToggle(HorizonOCConfigValue_OverwriteRefreshRate, nullptr);
addConfigToggle(HorizonOCConfigValue_VRR, nullptr);
tsl::elm::CustomDrawer* warningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString("\uE150 Enabling unsafe display", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
renderer->drawString("refresh rates may cause stress", false, x + 20, y + 50, 18, tsl::style::color::ColorText);

View File

@@ -55,11 +55,11 @@ bool hasChanged = true;
ClockManager *ClockManager::instance = NULL;
Thread cpuGovernorTHREAD;
Thread gpuGovernorTHREAD;
Thread vrrTHREAD;
u32 initialConfigValues[SysClkConfigValue_EnumMax]; // initial config. used for safety checks
bool kipAvailable = false;
bool isCpuGovernorInBoostMode = false;
ClockManager *ClockManager::GetInstance()
{
return instance;
@@ -125,8 +125,15 @@ ClockManager::ClockManager()
-2
);
threadStart(&cpuGovernorTHREAD);
threadStart(&gpuGovernorTHREAD);
threadCreate(
&vrrTHREAD,
ClockManager::VRRThread,
this,
NULL,
0x2000,
0x3F,
-2
);
for(int i = 0; i < HorizonOCSpeedo_EnumMax; i++) {
this->context->speedos[i] = Board::getSpeedo((HorizonOCSpeedo)i);
@@ -137,13 +144,27 @@ ClockManager::ClockManager()
this->context->isDram8GB = Board::IsDram8GB();
Board::SetGpuSchedulingMode((GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling), (GpuSchedulingOverrideMethod)this->config->GetConfigValue(HorizonOCConfigValue_GPUSchedulingMethod));
this->context->gpuSchedulingMode = (GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling);
this->context->isSysDockInstalled = this->sysDockIntegration->getCurrentSysDockState();
this->context->isSaltyNXInstalled = this->saltyNXIntegration->getCurrentSaltyNXState();
if(this->context->isSaltyNXInstalled) {
this->saltyNXIntegration->LoadSaltyNX();
}
threadStart(&cpuGovernorTHREAD);
threadStart(&gpuGovernorTHREAD);
threadStart(&vrrTHREAD);
}
ClockManager::~ClockManager()
{
threadClose(&cpuGovernorTHREAD);
threadClose(&gpuGovernorTHREAD);
threadClose(&vrrTHREAD);
delete this->sysDockIntegration;
delete this->saltyNXIntegration;
delete this->config;
delete this->context;
}
@@ -461,6 +482,67 @@ void ClockManager::GovernorThread(void* arg) {
svcSleepThread(POLL_NS);
}
}
void ClockManager::VRRThread(void* arg) {
ClockManager* mgr = static_cast<ClockManager*>(arg);
u8 tick = 0;
for (;;) {
if (!mgr->running || !mgr->config->GetConfigValue(HorizonOCConfigValue_VRR) || mgr->context->profile == SysClkProfile_Docked) {
continue;
}
if(Board::GetConsoleType() == HorizonOCConsoleType_Hoag) {
svcSleepThread(~0ULL);
continue;
}
std::scoped_lock lock{mgr->contextMutex};
u8 fps;
if(mgr->context->isSaltyNXInstalled) {
fps = mgr->saltyNXIntegration->GetFPS();
} else {
svcSleepThread(~0ULL); // effectively disable the thread if SaltyNX isn't installed, as there's no point in it running
continue;
}
if(fps == 254)
continue;
if(appletGetFocusState() != AppletFocusState_InFocus) {
Board::ResetToStockDisplay();
continue;
}
u8 maxDisplay;
if(Board::GetConsoleType() == HorizonOCConsoleType_Aula) {
maxDisplay = mgr->config->GetConfigValue(HorizonOCConfigValue_EnableUnsafeDisplayFreqs) ? 65 : 60;
} else {
maxDisplay = mgr->config->GetConfigValue(HorizonOCConfigValue_EnableUnsafeDisplayFreqs) ? 72 : 60;
}
u8 minDisplay = Board::GetConsoleType() == HorizonOCConsoleType_Aula ? 40 : 45;
if(fps >= minDisplay && fps <= maxDisplay)
Board::SetHz(HorizonOCModule_Display, fps);
else {
for(u32 i = 0; i < 10; i++) {
u32 compareHz = fps * i;
if(compareHz >= minDisplay && compareHz <= maxDisplay) {
Board::SetHz(HorizonOCModule_Display, compareHz);
break;
}
}
}
if(++tick > 10) {
Board::ResetToStockDisplay();
tick = 0;
svcSleepThread(10'000'000);
}
svcSleepThread(POLL_NS);
}
}
GovernorState ClockManager::GetEffectiveGovernorState(GovernorState appState, GovernorState tempState)
{
if (tempState == GovernorState_Disabled)
@@ -625,6 +707,12 @@ void ClockManager::HandleFreqReset(SysClkModule module, bool isBoost) {
case SysClkModule_MEM:
Board::ResetToStockMem();
DVFSReset();
break;
case HorizonOCModule_Display:
if(this->config->GetConfigValue(HorizonOCConfigValue_OverwriteRefreshRate) && Board::GetConsoleType() != HorizonOCConsoleType_Hoag) {
Board::ResetToStockDisplay();
}
break;
default:
break;
}
@@ -890,8 +978,10 @@ bool ClockManager::RefreshContext()
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag)
Board::SetDisplayRefreshDockedState(this->context->profile == SysClkProfile_Docked);
this->context->fps = saltyNXIntegration->GetFPS();
if(this->context->isSaltyNXInstalled)
this->context->fps = saltyNXIntegration->GetFPS();
else
this->context->fps = 254; // N/A
return hasChanged;
}

View File

@@ -185,6 +185,13 @@ class ClockManager
*/
static void GovernorThread(void* arg);
/**
* Runs the VRR Algorithm
*
* @param arg Cast to ClockManager* for context
*/
static void VRRThread(void* arg);
/**
* Gets the effective governor state from application/temporary override
*

View File

@@ -26,18 +26,22 @@ SysDockIntegration::SysDockIntegration() {
bool SysDockIntegration::getCurrentSysDockState() {
struct stat st = {0};
if (stat("sdmc:/atmosphere/contents/42000000000000A0", &st) == 0 && S_ISDIR(st.st_mode)) {
return true;
} else {
return false;
}
return stat("sdmc:/atmosphere/contents/42000000000000A0/flags/boot2.flag", &st) == 0;
}
SaltyNXIntegration::SaltyNXIntegration() {
}
void SaltyNXIntegration::LoadSaltyNX() {
if (!CheckPort())
return;
LoadSharedMemory();
}
bool SaltyNXIntegration::getCurrentSaltyNXState() {
struct stat st = {0};
return stat("sdmc:/atmosphere/contents/0000000000534C56/flags/boot2.flag", &st) == 0;
}
bool SaltyNXIntegration::CheckPort() {
Handle saltysd;
@@ -103,5 +107,4 @@ u8 SaltyNXIntegration::GetFPS() {
}
return NxFps ? NxFps->FPS : 254;
}
}

View File

@@ -83,6 +83,8 @@ public:
bool SharedMemoryUsed = false;
Handle remoteSharedMemory = 1;
SaltyNXIntegration();
void LoadSaltyNX();
bool getCurrentSaltyNXState();
bool CheckPort();
void LoadSharedMemory();