sysclk: add CPU governor and more governor settings

This commit is contained in:
souldbminersmwc
2026-02-18 19:32:33 -05:00
parent ca5ddbd779
commit f0952119b6
10 changed files with 406 additions and 166 deletions

View File

@@ -1,61 +0,0 @@
@echo off
setlocal enabledelayedexpansion
REM --- Root directory ---
set ROOT_DIR=%~dp0
set DIST_DIR=%ROOT_DIR%dist
REM --- Number of CPU cores ---
set CORES=%NUMBER_OF_PROCESSORS%
REM --- Optional first argument as DIST_DIR ---
if not "%~1"=="" set DIST_DIR=%~1
echo DIST_DIR: %DIST_DIR%
echo CORES: %CORES%
REM ========================
REM sysmodule
REM ========================
echo *** sysmodule ***
REM Extract TITLE_ID from perms.json using findstr (rough approximation)
for /f "tokens=2 delims=: " %%A in ('findstr /i "title_id" "%ROOT_DIR%sysmodule\perms.json"') do (
set TITLE_ID=%%A
)
REM Remove quotes and 0x prefix
set TITLE_ID=!TITLE_ID:"=!
set TITLE_ID=!TITLE_ID:0x=!
REM Build sysmodule
pushd "%ROOT_DIR%sysmodule"
make -j %CORES%
popd
REM Copy sysmodule files to dist
if not exist "%DIST_DIR%\atmosphere\contents\%TITLE_ID%\flags" mkdir "%DIST_DIR%\atmosphere\contents\%TITLE_ID%\flags"
copy /Y "%ROOT_DIR%sysmodule\out\horizon-oc.nsp" "%DIST_DIR%\atmosphere\contents\%TITLE_ID%\exefs.nsp"
type nul > "%DIST_DIR%\atmosphere\contents\%TITLE_ID%\flags\boot2.flag"
copy /Y "%ROOT_DIR%sysmodule\toolbox.json" "%DIST_DIR%\atmosphere\contents\%TITLE_ID%\toolbox.json"
REM ========================
REM overlay
REM ========================
echo *** overlay ***
pushd "%ROOT_DIR%overlay"
make -j %CORES%
popd
if not exist "%DIST_DIR%\switch\.overlays" mkdir "%DIST_DIR%\switch\.overlays"
copy /Y "%ROOT_DIR%overlay\out\horizon-oc-overlay.ovl" "%DIST_DIR%\switch\.overlays\horizon-oc-overlay.ovl"
REM ========================
REM assets
REM ========================
echo *** assets ***
if not exist "%DIST_DIR%\config\horizon-oc" mkdir "%DIST_DIR%\config\horizon-oc"
copy /Y "%ROOT_DIR%config.ini.template" "%DIST_DIR%\config\horizon-oc\config.ini.template"
copy /Y "%ROOT_DIR%..\..\README.md" "%DIST_DIR%\README.md"
endlocal

View File

@@ -36,3 +36,7 @@ echo "*** assets ***"
mkdir -p "$DIST_DIR/config/horizon-oc" mkdir -p "$DIST_DIR/config/horizon-oc"
cp -vf "$ROOT_DIR/config.ini.template" "$DIST_DIR/config/horizon-oc/config.ini.template" cp -vf "$ROOT_DIR/config.ini.template" "$DIST_DIR/config/horizon-oc/config.ini.template"
cp -vf "$ROOT_DIR/../../README.md" "$DIST_DIR/README.md" cp -vf "$ROOT_DIR/../../README.md" "$DIST_DIR/README.md"
echo "*** lang ***"
cp -r "$ROOT_DIR/overlay/lang/" "$DIST_DIR/config/horizon-oc/lang/"

View File

@@ -123,7 +123,7 @@ typedef enum {
} GPUUndervoltLevel; } GPUUndervoltLevel;
enum { enum {
DVFSMode_Disabled, DVFSMode_Disabled = 0,
DVFSMode_Hijack, DVFSMode_Hijack,
// DVFSMode_OfficialService, // DVFSMode_OfficialService,
// DVFSMode_Hack, // DVFSMode_Hack,
@@ -137,6 +137,15 @@ typedef enum {
GpuSchedulingMode_EnumMax, GpuSchedulingMode_EnumMax,
} GpuSchedulingMode; } GpuSchedulingMode;
typedef enum {
GovernorState_DoNotOverride = 0,
GovernorState_Disabled,
GovernorState_Enabled_CpuGpu,
GovernorState_Enabled_Cpu,
GovernorState_Enabled_Gpu,
GovernorState_EnumMax,
} GovernorState;
#define SYSCLK_ENUM_VALID(n, v) ((v) < n##_EnumMax) #define SYSCLK_ENUM_VALID(n, v) ((v) < n##_EnumMax)
static inline const char* sysclkFormatModule(SysClkModule module, bool pretty) static inline const char* sysclkFormatModule(SysClkModule module, bool pretty)

View File

@@ -149,6 +149,36 @@ void AppProfileGui::addModuleListItemToggle(SysClkProfile profile, SysClkModule
this->listElement->addItem(toggle); this->listElement->addItem(toggle);
} }
std::string AppProfileGui::formatValueDisplay(
std::uint32_t value,
const std::vector<NamedValue>& namedValues,
const std::string& suffix,
std::uint32_t divisor,
int decimalPlaces
)
{
if (value == 0) {
return FREQ_DEFAULT_TEXT;
}
if (!namedValues.empty()) {
for (const auto& namedValue : namedValues) {
if (namedValue.value == value) {
return namedValue.name;
}
}
}
char buf[32];
if (decimalPlaces > 0) {
double displayValue = (double)value / divisor;
snprintf(buf, sizeof(buf), "%.*f%s", decimalPlaces, displayValue, suffix.c_str());
} else {
snprintf(buf, sizeof(buf), "%u%s", value / divisor, suffix.c_str());
}
return std::string(buf);
}
void AppProfileGui::addModuleListItemValue( void AppProfileGui::addModuleListItemValue(
SysClkProfile profile, SysClkProfile profile,
SysClkModule module, SysClkModule module,
@@ -160,24 +190,16 @@ void AppProfileGui::addModuleListItemValue(
std::uint32_t divisor, std::uint32_t divisor,
int decimalPlaces, int decimalPlaces,
ValueThresholds thresholds, ValueThresholds thresholds,
std::vector<NamedValue> namedValues std::vector<NamedValue> namedValues,
bool showDefaultValue
) )
{ {
tsl::elm::ListItem* listItem = tsl::elm::ListItem* listItem =
new tsl::elm::ListItem(sysclkFormatModule(module, true)); new tsl::elm::ListItem(sysclkFormatModule(module, true));
std::uint32_t storedValue = this->profileList->mhzMap[profile][module]; std::uint32_t storedValue = this->profileList->mhzMap[profile][module];
if (storedValue == 0) {
listItem->setValue(FREQ_DEFAULT_TEXT); listItem->setValue(this->formatValueDisplay(storedValue, namedValues, suffix, divisor, decimalPlaces));
} else {
char buf[32];
if (decimalPlaces > 0) {
double displayValue = (double)storedValue / divisor;
snprintf(buf, sizeof(buf), "%.*f%s", decimalPlaces, displayValue, suffix.c_str());
} else {
snprintf(buf, sizeof(buf), "%u%s", storedValue / divisor, suffix.c_str());
}
listItem->setValue(buf);
}
listItem->setClickListener( listItem->setClickListener(
[this, [this,
listItem, listItem,
@@ -191,7 +213,8 @@ void AppProfileGui::addModuleListItemValue(
divisor, divisor,
decimalPlaces, decimalPlaces,
thresholds, thresholds,
namedValues](u64 keys) namedValues,
showDefaultValue](u64 keys)
{ {
if ((keys & HidNpadButton_A) == HidNpadButton_A) if ((keys & HidNpadButton_A) == HidNpadButton_A)
{ {
@@ -210,23 +233,11 @@ void AppProfileGui::addModuleListItemValue(
currentValue, currentValue,
range, range,
categoryName, categoryName,
[this, listItem, profile, module, divisor, suffix, decimalPlaces, thresholds](std::uint32_t value) -> bool [this, listItem, profile, module, divisor, suffix, decimalPlaces, thresholds, namedValues](std::uint32_t value) -> bool
{ {
this->profileList->mhzMap[profile][module] = value / divisor; this->profileList->mhzMap[profile][module] = value / divisor;
if (value == 0) { listItem->setValue(this->formatValueDisplay(value / divisor, namedValues, suffix, divisor, decimalPlaces));
listItem->setValue(FREQ_DEFAULT_TEXT);
} else {
char buf[32];
if (decimalPlaces > 0) {
double displayValue = (double)value / divisor;
snprintf(buf, sizeof(buf), "%.*f%s",
decimalPlaces, displayValue, suffix.c_str());
} else {
snprintf(buf, sizeof(buf), "%u%s",
value / divisor, suffix.c_str());
}
listItem->setValue(buf);
}
Result rc = Result rc =
sysclkIpcSetProfiles(this->applicationId, sysclkIpcSetProfiles(this->applicationId,
this->profileList); this->profileList);
@@ -242,7 +253,7 @@ void AppProfileGui::addModuleListItemValue(
false, false,
{}, {},
namedValues, namedValues,
true showDefaultValue
); );
return true; return true;
} }
@@ -355,7 +366,15 @@ void AppProfileGui::addProfileUI(SysClkProfile profile)
} }
} }
#endif #endif
this->addModuleListItemToggle(profile, HorizonOCModule_Governor); std::vector<NamedValue> governorSettings = {
NamedValue("Do Not Override", GovernorState_DoNotOverride),
NamedValue("Disabled", GovernorState_Disabled),
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
NamedValue("CPU", GovernorState_Enabled_Cpu),
NamedValue("GPU", GovernorState_Enabled_Gpu),
};
this->addModuleListItemValue(profile, HorizonOCModule_Governor, "Governor", 0, 0, 1, "", 1, 0, ValueThresholds(), governorSettings, false);
} }
void AppProfileGui::listUI() void AppProfileGui::listUI()

View File

@@ -23,22 +23,17 @@
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors * stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
#pragma once #pragma once
#include "../../ipc.h" #include "../../ipc.h"
#include "base_menu_gui.h" #include "base_menu_gui.h"
#include "freq_choice_gui.h" #include "freq_choice_gui.h"
#include "value_choice_gui.h" #include "value_choice_gui.h"
#define SYSCLK_GLOBAL_PROFILE_TID 0xA111111111111111 #define SYSCLK_GLOBAL_PROFILE_TID 0xA111111111111111
class AppProfileGui : public BaseMenuGui class AppProfileGui : public BaseMenuGui
{ {
protected: protected:
std::uint64_t applicationId; std::uint64_t applicationId;
SysClkTitleProfileList* profileList; SysClkTitleProfileList* profileList;
void openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfile profile, SysClkModule module); void openFreqChoiceGui(tsl::elm::ListItem* listItem, SysClkProfile profile, SysClkModule module);
void addModuleListItem(SysClkProfile profile, SysClkModule module); void addModuleListItem(SysClkProfile profile, SysClkModule module);
void addModuleListItemToggle(SysClkProfile profile, SysClkModule module); void addModuleListItemToggle(SysClkProfile profile, SysClkModule module);
@@ -54,6 +49,13 @@ class AppProfileGui : public BaseMenuGui
const std::vector<NamedValue>& namedValues = {}, const std::vector<NamedValue>& namedValues = {},
bool showDefaultValue = true bool showDefaultValue = true
); );
std::string formatValueDisplay(
std::uint32_t value,
const std::vector<NamedValue>& namedValues,
const std::string& suffix,
std::uint32_t divisor,
int decimalPlaces
);
void addModuleListItemValue( void addModuleListItemValue(
SysClkProfile profile, SysClkProfile profile,
SysClkModule module, SysClkModule module,
@@ -65,10 +67,10 @@ class AppProfileGui : public BaseMenuGui
std::uint32_t divisor, std::uint32_t divisor,
int decimalPlaces, int decimalPlaces,
ValueThresholds thresholds, ValueThresholds thresholds,
std::vector<NamedValue> namedValues = {} std::vector<NamedValue> namedValues = {},
bool showDefaultValue = true
); );
void addProfileUI(SysClkProfile profile); void addProfileUI(SysClkProfile profile);
public: public:
AppProfileGui(std::uint64_t applicationId, SysClkTitleProfileList* profileList); AppProfileGui(std::uint64_t applicationId, SysClkTitleProfileList* profileList);
~AppProfileGui(); ~AppProfileGui();

View File

@@ -99,11 +99,16 @@ void GlobalOverrideGui::addModuleListItemValue(
const std::string& suffix, const std::string& suffix,
std::uint32_t divisor, std::uint32_t divisor,
int decimalPlaces, int decimalPlaces,
ValueThresholds thresholds ValueThresholds thresholds,
const std::vector<NamedValue>& namedValues,
bool showDefaultValue
) )
{ {
bool hasNamedValues = !namedValues.empty();
this->customFormatModules[module] = std::make_tuple(suffix, divisor, decimalPlaces); if (!hasNamedValues) {
this->customFormatModules[module] = std::make_tuple(suffix, divisor, decimalPlaces);
}
tsl::elm::ListItem* listItem = tsl::elm::ListItem* listItem =
new tsl::elm::ListItem(sysclkFormatModule(module, true)); new tsl::elm::ListItem(sysclkFormatModule(module, true));
@@ -121,7 +126,10 @@ void GlobalOverrideGui::addModuleListItemValue(
suffix, suffix,
divisor, divisor,
decimalPlaces, decimalPlaces,
thresholds](u64 keys) thresholds,
namedValues,
hasNamedValues,
showDefaultValue](u64 keys)
{ {
if ((keys & HidNpadButton_A) == HidNpadButton_A) if ((keys & HidNpadButton_A) == HidNpadButton_A)
{ {
@@ -147,7 +155,7 @@ void GlobalOverrideGui::addModuleListItemValue(
range, range,
categoryName, categoryName,
[this, listItem, module, divisor, suffix, decimalPlaces, thresholds](std::uint32_t value) -> bool [this, listItem, module, divisor, suffix, decimalPlaces, thresholds, namedValues, hasNamedValues, showDefaultValue](std::uint32_t value) -> bool
{ {
if (!this->context) { if (!this->context) {
return false; return false;
@@ -158,6 +166,13 @@ void GlobalOverrideGui::addModuleListItemValue(
if (value == 0) { if (value == 0) {
listItem->setValue(FREQ_DEFAULT_TEXT); listItem->setValue(FREQ_DEFAULT_TEXT);
} else if (hasNamedValues) {
for (const auto& namedValue : namedValues) {
if (namedValue.value == value / divisor) {
listItem->setValue(namedValue.name);
break;
}
}
} else { } else {
char buf[32]; char buf[32];
if (decimalPlaces > 0) { if (decimalPlaces > 0) {
@@ -188,8 +203,8 @@ void GlobalOverrideGui::addModuleListItemValue(
thresholds, thresholds,
false, false,
std::map<std::uint32_t, std::string>(), std::map<std::uint32_t, std::string>(),
std::vector<NamedValue>(), namedValues,
true showDefaultValue
); );
return true; return true;
@@ -294,7 +309,16 @@ void GlobalOverrideGui::listUI()
if(!IsHoag() && configList.values[HorizonOCConfigValue_OverwriteRefreshRate]) if(!IsHoag() && configList.values[HorizonOCConfigValue_OverwriteRefreshRate])
this->addModuleListItemValue(HorizonOCModule_Display, "Display", IsAula() ? 45 : 40, configList.values[HorizonOCConfigValue_EnableUnsafeDisplayFreqs] ? IsAula() ? 65 : 72 : 60, 1, " Hz", 1, 0, lcdThresholds); this->addModuleListItemValue(HorizonOCModule_Display, "Display", IsAula() ? 45 : 40, configList.values[HorizonOCConfigValue_EnableUnsafeDisplayFreqs] ? IsAula() ? 65 : 72 : 60, 1, " Hz", 1, 0, lcdThresholds);
#endif #endif
this->addModuleToggleItem(HorizonOCModule_Governor);
std::vector<NamedValue> governorSettings = {
NamedValue("Do Not Override", GovernorState_DoNotOverride),
NamedValue("Disabled", GovernorState_Disabled),
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
NamedValue("CPU", GovernorState_Enabled_Cpu),
NamedValue("GPU", GovernorState_Enabled_Gpu),
};
this->addModuleListItemValue(HorizonOCModule_Governor, "Governor", 0, 0, 1, "", 1, 0, ValueThresholds(), governorSettings, false);
} }
void GlobalOverrideGui::refresh() void GlobalOverrideGui::refresh()
@@ -306,17 +330,30 @@ void GlobalOverrideGui::refresh()
for (std::uint16_t m = 0; m < SysClkModule_EnumMax; m++) { for (std::uint16_t m = 0; m < SysClkModule_EnumMax; m++) {
if (m == HorizonOCModule_Governor) { if (m == HorizonOCModule_Governor) {
auto *toggle = if (this->listItems[m] != nullptr &&
static_cast<tsl::elm::ToggleListItem *>(this->listItems[m]); this->listHz[m] != this->context->overrideFreqs[m]) {
if (!toggle)
continue;
bool newState = this->context->overrideFreqs[m] != 0; std::string displayText = FREQ_DEFAULT_TEXT;
std::uint32_t currentValue = this->context->overrideFreqs[m];
if (toggle->getState() != newState) { std::vector<NamedValue> governorSettings = {
toggle->setState(newState); NamedValue("Do Not Override", GovernorState_DoNotOverride),
NamedValue("Disabled", GovernorState_Disabled),
NamedValue("CPU + GPU", GovernorState_Enabled_CpuGpu),
NamedValue("CPU", GovernorState_Enabled_Cpu),
NamedValue("GPU", GovernorState_Enabled_Gpu),
};
for (const auto& setting : governorSettings) {
if (setting.value == currentValue) {
displayText = setting.name;
break;
}
}
this->listItems[m]->setValue(displayText);
this->listHz[m] = currentValue;
} }
continue; continue;
} }

View File

@@ -1,4 +1,5 @@
/* /*
*
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -23,10 +24,7 @@
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors * stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
#pragma once #pragma once
#include "../../ipc.h" #include "../../ipc.h"
#include "base_menu_gui.h" #include "base_menu_gui.h"
#include "freq_choice_gui.h" #include "freq_choice_gui.h"
@@ -63,7 +61,9 @@ class GlobalOverrideGui : public BaseMenuGui
const std::string& suffix, const std::string& suffix,
std::uint32_t divisor, std::uint32_t divisor,
int decimalPlaces, int decimalPlaces,
ValueThresholds thresholds = {} ValueThresholds thresholds = {},
const std::vector<NamedValue>& namedValues = {},
bool showDefaultValue = true
); );
public: public:
GlobalOverrideGui(); GlobalOverrideGui();

View File

@@ -471,7 +471,7 @@ void Board::SetHz(SysClkModule module, std::uint32_t hz)
rc = clkrstSetClockRate(&session, hz); rc = clkrstSetClockRate(&session, hz);
ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); ASSERT_RESULT_OK(rc, "clkrstSetClockRate");
if (module == SysClkModule_CPU) { if (module == SysClkModule_CPU) {
svcSleepThread(300'000); svcSleepThread(200'000);
rc = clkrstSetClockRate(&session, hz); rc = clkrstSetClockRate(&session, hz);
ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); ASSERT_RESULT_OK(rc, "clkrstSetClockRate");
} }
@@ -482,7 +482,7 @@ void Board::SetHz(SysClkModule module, std::uint32_t hz)
rc = pcvSetClockRate(Board::GetPcvModule(module), hz); rc = pcvSetClockRate(Board::GetPcvModule(module), hz);
ASSERT_RESULT_OK(rc, "pcvSetClockRate"); ASSERT_RESULT_OK(rc, "pcvSetClockRate");
if (module == SysClkModule_CPU) { if (module == SysClkModule_CPU) {
svcSleepThread(300'000); svcSleepThread(200'000);
rc = pcvSetClockRate(Board::GetPcvModule(module), hz); rc = pcvSetClockRate(Board::GetPcvModule(module), hz);
ASSERT_RESULT_OK(rc, "pcvSetClockRate"); ASSERT_RESULT_OK(rc, "pcvSetClockRate");
} }

View File

@@ -41,14 +41,18 @@
#include <crc32.h> #include <crc32.h>
#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0)) #define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0))
bool isGovernorEnabled = false; // to avoid thread messes bool isGpuGovernorEnabled = false;
bool lastGovernorState = false; bool isCpuGovernorEnabled = false;
bool lastGpuGovernorState = false;
bool lastCpuGovernorState = false;
bool hasChanged = true; bool hasChanged = true;
ClockManager *ClockManager::instance = NULL; ClockManager *ClockManager::instance = NULL;
Thread governorTHREAD; Thread cpuGovernorTHREAD;
Thread gpuGovernorTHREAD;
u32 initialConfigValues[SysClkConfigValue_EnumMax]; // initial config. used for safety checks u32 initialConfigValues[SysClkConfigValue_EnumMax]; // initial config. used for safety checks
u64 previousRamHz; u64 previousRamHz;
bool kipAvailable = false; bool kipAvailable = false;
bool isCpuGovernorInBoostMode = false;
ClockManager *ClockManager::GetInstance() ClockManager *ClockManager::GetInstance()
{ {
@@ -92,8 +96,19 @@ ClockManager::ClockManager()
this->sysDockIntegration = new SysDockIntegration; this->sysDockIntegration = new SysDockIntegration;
memset(&initialConfigValues, 0, sizeof(initialConfigValues)); memset(&initialConfigValues, 0, sizeof(initialConfigValues));
this->GetKipData(); this->GetKipData();
threadCreate( threadCreate(
&governorTHREAD, &cpuGovernorTHREAD,
ClockManager::CpuGovernorThread,
this,
NULL,
0x2000,
0x3F,
-2
);
threadCreate(
&gpuGovernorTHREAD,
ClockManager::GovernorThread, ClockManager::GovernorThread,
this, this,
NULL, NULL,
@@ -102,7 +117,8 @@ ClockManager::ClockManager()
-2 -2
); );
threadStart(&governorTHREAD); threadStart(&cpuGovernorTHREAD);
threadStart(&gpuGovernorTHREAD);
for(int i = 0; i < HorizonOCSpeedo_EnumMax; i++) { for(int i = 0; i < HorizonOCSpeedo_EnumMax; i++) {
this->context->speedos[i] = Board::getSpeedo((HorizonOCSpeedo)i); this->context->speedos[i] = Board::getSpeedo((HorizonOCSpeedo)i);
@@ -119,7 +135,8 @@ ClockManager::ClockManager()
ClockManager::~ClockManager() ClockManager::~ClockManager()
{ {
threadClose(&governorTHREAD); threadClose(&cpuGovernorTHREAD);
threadClose(&gpuGovernorTHREAD);
delete this->config; delete this->config;
delete this->context; delete this->context;
} }
@@ -311,6 +328,160 @@ u32 findIndexMHz(u32 arr[], u32 size, u32 value) {
return 0; return 0;
} }
void ClockManager::CpuGovernorThread(void* arg)
{
ClockManager* mgr = static_cast<ClockManager*>(arg);
for (;;)
{
if (!mgr->running)
{
svcSleepThread(50'000'000);
continue;
}
if (!isCpuGovernorEnabled)
{
svcSleepThread(50'000'000);
continue;
}
std::uint32_t mode = 0;
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
bool isInBoostMode = R_SUCCEEDED(rc) && apmExtIsBoostMode(mode);
if (isInBoostMode)
{
isCpuGovernorInBoostMode = true;
svcSleepThread(50'000'000);
continue;
}
isCpuGovernorInBoostMode = false;
auto& table = mgr->freqTable[SysClkModule_CPU];
if (table.count == 0)
{
svcSleepThread(50'000'000);
continue;
}
std::scoped_lock lock{mgr->contextMutex};
u32 currentHz = Board::GetHz(SysClkModule_CPU);
u32 index = table.count - 1;
for (u32 i = 0; i < table.count; i++)
{
if (table.list[i] == currentHz)
{
index = i;
break;
}
}
if (table.list[index] != currentHz)
{
for (u32 i = 0; i < table.count; i++)
{
if (table.list[i] >= currentHz)
{
index = i;
break;
}
}
}
u32 targetHz = mgr->context->overrideFreqs[SysClkModule_CPU];
if (!targetHz)
{
targetHz = mgr->config->GetAutoClockHz(
mgr->context->applicationId,
SysClkModule_CPU,
mgr->context->profile,
false
);
if (!targetHz)
{
targetHz = mgr->config->GetAutoClockHz(
GLOBAL_PROFILE_ID,
SysClkModule_CPU,
mgr->context->profile,
false
);
}
}
int gpuLoad = Board::GetPartLoad(HocClkPartLoad_GPU);
int cpuLoad = Board::GetPartLoad(HocClkPartLoad_CPUMax);
if (isGpuGovernorEnabled && gpuLoad < 800)
{
if (cpuLoad < 600 && index > 0)
{
index--;
}
else if (cpuLoad > 800 && index + 1 < table.count)
{
index++;
}
}
else
{
if (cpuLoad < 600 && index > 0)
{
index--;
}
else if (cpuLoad > 800 && index + 1 < table.count)
{
index++;
}
}
u32 maxHz = mgr->GetMaxAllowedHz(SysClkModule_CPU, mgr->context->profile);
if (targetHz)
{
u32 targetIndex = table.count - 1;
for (u32 i = 0; i < table.count; i++)
{
if (table.list[i] >= targetHz)
{
targetIndex = i;
break;
}
}
if (index > targetIndex)
{
index = targetIndex;
}
}
if (maxHz > 0 && table.list[index] > maxHz)
{
for (u32 i = table.count; i > 0; i--)
{
if (table.list[i - 1] <= maxHz)
{
index = i - 1;
break;
}
}
}
u32 newHz = table.list[index];
if (mgr->IsAssignableHz(SysClkModule_CPU, newHz))
{
Board::SetHz(SysClkModule_CPU, newHz);
mgr->context->freqs[SysClkModule_CPU] = newHz;
}
svcSleepThread(50'000'000);
}
}
void ClockManager::GovernorThread(void* arg) void ClockManager::GovernorThread(void* arg)
{ {
ClockManager* mgr = static_cast<ClockManager*>(arg); ClockManager* mgr = static_cast<ClockManager*>(arg);
@@ -323,8 +494,7 @@ void ClockManager::GovernorThread(void* arg)
continue; continue;
} }
if (!isGpuGovernorEnabled)
if (!isGovernorEnabled)
{ {
svcSleepThread(50'000'000); svcSleepThread(50'000'000);
continue; continue;
@@ -341,7 +511,7 @@ void ClockManager::GovernorThread(void* arg)
u32 currentHz = Board::GetHz(SysClkModule_GPU); u32 currentHz = Board::GetHz(SysClkModule_GPU);
u32 index = 0; u32 index = table.count - 1;
for (u32 i = 0; i < table.count; i++) for (u32 i = 0; i < table.count; i++)
{ {
if (table.list[i] == currentHz) if (table.list[i] == currentHz)
@@ -351,6 +521,18 @@ void ClockManager::GovernorThread(void* arg)
} }
} }
if (table.list[index] != currentHz)
{
for (u32 i = 0; i < table.count; i++)
{
if (table.list[i] >= currentHz)
{
index = i;
break;
}
}
}
u32 targetHz = mgr->context->overrideFreqs[SysClkModule_GPU]; u32 targetHz = mgr->context->overrideFreqs[SysClkModule_GPU];
if (!targetHz) if (!targetHz)
{ {
@@ -372,17 +554,34 @@ void ClockManager::GovernorThread(void* arg)
} }
} }
int load = Board::GetPartLoad(HocClkPartLoad_GPU); int gpuLoad = Board::GetPartLoad(HocClkPartLoad_GPU);
int cpuLoad = Board::GetPartLoad(HocClkPartLoad_CPUMax);
if (load < 600 && index > 0) if (isCpuGovernorEnabled && !isCpuGovernorInBoostMode && cpuLoad < 600)
{ {
index--; if (gpuLoad < 600 && index > 0)
{
index--;
}
else if (gpuLoad > 750 && index + 1 < table.count)
{
index++;
}
} }
else if (load > 800 && index + 1 < table.count) else
{ {
index++; if (gpuLoad < 600 && index > 0)
{
index--;
}
else if (gpuLoad > 800 && index + 1 < table.count)
{
index++;
}
} }
u32 maxHz = mgr->GetMaxAllowedHz(SysClkModule_GPU, mgr->context->profile);
if (targetHz) if (targetHz)
{ {
u32 targetIndex = table.count - 1; u32 targetIndex = table.count - 1;
@@ -401,6 +600,17 @@ void ClockManager::GovernorThread(void* arg)
} }
} }
if (maxHz > 0 && table.list[index] > maxHz)
{
for (u32 i = table.count; i > 0; i--)
{
if (table.list[i - 1] <= maxHz)
{
index = i - 1;
break;
}
}
}
u32 newHz = table.list[index]; u32 newHz = table.list[index];
if (mgr->IsAssignableHz(SysClkModule_GPU, newHz)) if (mgr->IsAssignableHz(SysClkModule_GPU, newHz))
@@ -415,6 +625,19 @@ void ClockManager::GovernorThread(void* arg)
bool prevBoostMode = true; bool prevBoostMode = true;
GovernorState ClockManager::GetEffectiveGovernorState(GovernorState appState, GovernorState tempState)
{
if (tempState == GovernorState_Disabled)
{
return GovernorState_Disabled;
}
if (tempState != GovernorState_DoNotOverride)
{
return tempState;
}
return appState;
}
void ClockManager::Tick() void ClockManager::Tick()
{ {
std::scoped_lock lock{this->contextMutex}; std::scoped_lock lock{this->contextMutex};
@@ -454,8 +677,6 @@ void ClockManager::Tick()
// } // }
prevBoostMode = isBoost; prevBoostMode = isBoost;
bool noGPU = false;
if (this->RefreshContext() || this->config->Refresh()) if (this->RefreshContext() || this->config->Refresh())
{ {
@@ -490,15 +711,32 @@ void ClockManager::Tick()
} }
if(module == HorizonOCModule_Governor) { if(module == HorizonOCModule_Governor) {
bool newGovernorState = targetHz; GovernorState appGovernorState = (GovernorState)targetHz;
if(newGovernorState != lastGovernorState) {
FileUtils::LogLine("[mgr] Governor state changed: %s", newGovernorState ? "enabled" : "disabled"); u32 tempTargetHz = this->context->overrideFreqs[HorizonOCModule_Governor];
lastGovernorState = newGovernorState; if (!tempTargetHz)
{
tempTargetHz = this->config->GetAutoClockHz(this->context->applicationId, HorizonOCModule_Governor, this->context->profile, returnRaw);
if(!tempTargetHz)
tempTargetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Governor, this->context->profile, returnRaw);
}
GovernorState tempGovernorState = (GovernorState)tempTargetHz;
GovernorState effectiveState = this->GetEffectiveGovernorState(appGovernorState, tempGovernorState);
bool newCpuGovernorState = (effectiveState == GovernorState_Enabled_CpuGpu || effectiveState == GovernorState_Enabled_Cpu);
bool newGpuGovernorState = (effectiveState == GovernorState_Enabled_CpuGpu || effectiveState == GovernorState_Enabled_Gpu);
if(newCpuGovernorState != lastCpuGovernorState || newGpuGovernorState != lastGpuGovernorState) {
FileUtils::LogLine("[mgr] Governor state changed: CPU %s, GPU %s", newCpuGovernorState ? "enabled" : "disabled", newGpuGovernorState ? "enabled" : "disabled");
lastCpuGovernorState = newCpuGovernorState;
lastGpuGovernorState = newGpuGovernorState;
hasChanged = true; hasChanged = true;
Board::ResetToStock(); Board::ResetToStock();
} }
isGovernorEnabled = newGovernorState; isCpuGovernorEnabled = newCpuGovernorState;
isGpuGovernorEnabled = newGpuGovernorState;
} }
if(module == HorizonOCModule_Display && this->config->GetConfigValue(HorizonOCConfigValue_OverwriteRefreshRate) && Board::GetConsoleType() != HorizonOCConsoleType_Hoag) { if(module == HorizonOCModule_Display && this->config->GetConfigValue(HorizonOCConfigValue_OverwriteRefreshRate) && Board::GetConsoleType() != HorizonOCConsoleType_Hoag) {
@@ -512,15 +750,16 @@ void ClockManager::Tick()
if(targetHz && this->context->realFreqs[HorizonOCModule_Display] != targetHz && module == HorizonOCModule_Display) if(targetHz && this->context->realFreqs[HorizonOCModule_Display] != targetHz && module == HorizonOCModule_Display)
this->context->realFreqs[HorizonOCModule_Display] = targetHz; this->context->realFreqs[HorizonOCModule_Display] = targetHz;
// Skip GPU if governor handles it // Skip GPU and CPU if governors handle them
if(module > SysClkModule_MEM) { if(module > SysClkModule_MEM) {
continue; continue;
} }
if(isGovernorEnabled) {
noGPU = true; bool noCPU = isCpuGovernorEnabled;
} else { bool noGPU = isGpuGovernorEnabled;
noGPU = false;
} if(noCPU && module == SysClkModule_CPU)
continue;
if(noGPU && module == SysClkModule_GPU) if(noGPU && module == SysClkModule_GPU)
continue; continue;

View File

@@ -23,33 +23,24 @@
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors * stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <sysclk.h> #include <sysclk.h>
#include <switch.h> #include <switch.h>
#include "config.h" #include "config.h"
#include "board.h" #include "board.h"
#include <nxExt/cpp/lockable_mutex.h> #include <nxExt/cpp/lockable_mutex.h>
#include "integrations.h" #include "integrations.h"
void governorThread(void*);
class SysDockIntegration; class SysDockIntegration;
class ClockManager class ClockManager
{ {
public: public:
static ClockManager* GetInstance(); static ClockManager* GetInstance();
static void Initialize(); static void Initialize();
static void Exit(); static void Exit();
ClockManager(); ClockManager();
virtual ~ClockManager(); virtual ~ClockManager();
SysClkContext GetCurrentContext(); SysClkContext GetCurrentContext();
Config* GetConfig(); Config* GetConfig();
void SetRunning(bool running); void SetRunning(bool running);
@@ -60,7 +51,9 @@ class ClockManager
void WaitForNextTick(); void WaitForNextTick();
void SetKipData(); void SetKipData();
void GetKipData(); void GetKipData();
static void CpuGovernorThread(void* arg);
static void GovernorThread(void* arg); static void GovernorThread(void* arg);
GovernorState GetEffectiveGovernorState(GovernorState appState, GovernorState tempState);
struct { struct {
std::uint32_t count; std::uint32_t count;
std::uint32_t list[SYSCLK_FREQ_LIST_MAX]; std::uint32_t list[SYSCLK_FREQ_LIST_MAX];
@@ -75,9 +68,7 @@ class ClockManager
bool ConfigIntervalTimeout(SysClkConfigValue intervalMsConfigValue, std::uint64_t ns, std::uint64_t* lastLogNs); bool ConfigIntervalTimeout(SysClkConfigValue intervalMsConfigValue, std::uint64_t ns, std::uint64_t* lastLogNs);
void RefreshFreqTableRow(SysClkModule module); void RefreshFreqTableRow(SysClkModule module);
bool RefreshContext(); bool RefreshContext();
static ClockManager *instance; static ClockManager *instance;
std::atomic_bool running; std::atomic_bool running;
LockableMutex contextMutex; LockableMutex contextMutex;
Config* config; Config* config;