diff --git a/Source/sys-clk/common/include/sysclk/clock_manager.h b/Source/sys-clk/common/include/sysclk/clock_manager.h index 362b6a85..351ca76a 100644 --- a/Source/sys-clk/common/include/sysclk/clock_manager.h +++ b/Source/sys-clk/common/include/sysclk/clock_manager.h @@ -45,6 +45,7 @@ typedef struct u16 iddq[HorizonOCSpeedo_EnumMax]; GpuSchedulingMode gpuSchedulingMode; bool isSysDockInstalled; + bool isSaltyNXInstalled; u8 maxDisplayFreq; u8 dramID; bool isDram8GB; diff --git a/Source/sys-clk/common/include/sysclk/config.h b/Source/sys-clk/common/include/sysclk/config.h index 94e76626..03565282 100644 --- a/Source/sys-clk/common/include/sysclk/config.h +++ b/Source/sys-clk/common/include/sysclk/config.h @@ -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: diff --git a/Source/sys-clk/overlay/src/ui/gui/about_gui.cpp b/Source/sys-clk/overlay/src/ui/gui/about_gui.cpp index ae566cd4..832332cd 100644 --- a/Source/sys-clk/overlay/src/ui/gui/about_gui.cpp +++ b/Source/sys-clk/overlay/src/ui/gui/about_gui.cpp @@ -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"); } \ No newline at end of file diff --git a/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.cpp b/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.cpp index 8225970a..e1167648 100644 --- a/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.cpp +++ b/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.cpp @@ -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); + } } } diff --git a/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp index 2198f018..0aeebdde 100644 --- a/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp +++ b/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp @@ -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); diff --git a/Source/sys-clk/sysmodule/src/clock_manager.cpp b/Source/sys-clk/sysmodule/src/clock_manager.cpp index b7441d42..931eb6c1 100644 --- a/Source/sys-clk/sysmodule/src/clock_manager.cpp +++ b/Source/sys-clk/sysmodule/src/clock_manager.cpp @@ -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(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; } diff --git a/Source/sys-clk/sysmodule/src/clock_manager.h b/Source/sys-clk/sysmodule/src/clock_manager.h index c3552525..c287db17 100644 --- a/Source/sys-clk/sysmodule/src/clock_manager.h +++ b/Source/sys-clk/sysmodule/src/clock_manager.h @@ -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 * diff --git a/Source/sys-clk/sysmodule/src/integrations.cpp b/Source/sys-clk/sysmodule/src/integrations.cpp index b91c0589..bd53423c 100644 --- a/Source/sys-clk/sysmodule/src/integrations.cpp +++ b/Source/sys-clk/sysmodule/src/integrations.cpp @@ -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; -} - \ No newline at end of file +} \ No newline at end of file diff --git a/Source/sys-clk/sysmodule/src/integrations.h b/Source/sys-clk/sysmodule/src/integrations.h index 1873d374..0146bc36 100644 --- a/Source/sys-clk/sysmodule/src/integrations.h +++ b/Source/sys-clk/sysmodule/src/integrations.h @@ -83,6 +83,8 @@ public: bool SharedMemoryUsed = false; Handle remoteSharedMemory = 1; SaltyNXIntegration(); + void LoadSaltyNX(); + bool getCurrentSaltyNXState(); bool CheckPort(); void LoadSharedMemory();