hocclk: significantly reduce memory usage

This commit is contained in:
souldbminersmwc
2026-04-30 19:15:05 -04:00
parent 89418b7cd0
commit a16cf8a2ef
5 changed files with 175 additions and 244 deletions

View File

@@ -3,7 +3,7 @@
"title_id": "0x00FF0000636C6BFF", "title_id": "0x00FF0000636C6BFF",
"title_id_range_min": "0x00FF0000636C6BFF", "title_id_range_min": "0x00FF0000636C6BFF",
"title_id_range_max": "0x00FF0000636C6BFF", "title_id_range_max": "0x00FF0000636C6BFF",
"main_thread_stack_size": "0x0000F000", "main_thread_stack_size": "0x0000B000",
"main_thread_priority": 16, "main_thread_priority": 16,
"default_cpu_id": 3, "default_cpu_id": 3,
"process_category": 1, "process_category": 1,

View File

@@ -292,10 +292,25 @@ namespace clockManager {
{ {
if (board::GetSocType() == HocClkSocType_Mariko && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) { if (board::GetSocType() == HocClkSocType_Mariko && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
board::PcvHijackGpuVolts(0); // Reset to vMin board::PcvHijackGpuVolts(0); // Reset to vMin
u32 targetHz = gContext.overrideFreqs[HocClkModule_GPU];
if (!targetHz) {
targetHz = config::GetAutoClockHz(gContext.applicationId, HocClkModule_GPU, gContext.profile, false);
if (!targetHz) {
targetHz = config::GetAutoClockHz(HOCCLK_GLOBAL_PROFILE_TID, HocClkModule_GPU, gContext.profile, false);
}
}
u32 maxHz = GetMaxAllowedHz(HocClkModule_GPU, gContext.profile);
u32 nearestHz = GetNearestHz(HocClkModule_GPU, targetHz, maxHz);
board::SetHz(HocClkModule_GPU, ~0); board::SetHz(HocClkModule_GPU, ~0);
if (targetHz) {
board::SetHz(HocClkModule_GPU, nearestHz);
} else {
board::ResetToStockGpu(); board::ResetToStockGpu();
} }
} }
}
void HandleFreqReset(HocClkModule module, bool isBoost) void HandleFreqReset(HocClkModule module, bool isBoost)
{ {
@@ -445,7 +460,11 @@ namespace clockManager {
// restore clocks to stock values on app or profile change // restore clocks to stock values on app or profile change
if (hasChanged) { if (hasChanged) {
board::ResetToStock(); board::ResetToStock();
DVFSReset(); if (board::GetSocType() == HocClkSocType_Mariko && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
board::PcvHijackGpuVolts(0);
board::SetHz(HocClkModule_GPU, ~0);
board::ResetToStockGpu();
}
WaitForNextTick(); WaitForNextTick();
} }

View File

@@ -33,13 +33,9 @@ namespace governor {
bool isCpuGovernorInBoostMode = false; bool isCpuGovernorInBoostMode = false;
bool isVRREnabled = false; bool isVRREnabled = false;
// thread handles Thread governorTHREAD;
Thread cpuGovernorTHREAD;
Thread gpuGovernorTHREAD;
Thread vrrTHREAD;
void HandleGovernor(uint32_t targetHz) void HandleGovernor(uint32_t targetHz) {
{
u32 tempTargetHz = clockManager::gContext.overrideFreqs[HocClkModule_Governor]; u32 tempTargetHz = clockManager::gContext.overrideFreqs[HocClkModule_Governor];
if (!tempTargetHz) { if (!tempTargetHz) {
tempTargetHz = config::GetAutoClockHz(clockManager::gContext.applicationId, HocClkModule_Governor, clockManager::gContext.profile, true); tempTargetHz = config::GetAutoClockHz(clockManager::gContext.applicationId, HocClkModule_Governor, clockManager::gContext.profile, true);
@@ -65,18 +61,13 @@ namespace governor {
isGpuGovernorEnabled = newGpuGovernorState; isGpuGovernorEnabled = newGpuGovernorState;
isVRREnabled = newVrrGovernorState; isVRREnabled = newVrrGovernorState;
if (newCpuGovernorState == false && lastCpuGovernorState == true) { if (newCpuGovernorState == false && lastCpuGovernorState == true)
svcSleepThread(100'000'000); // thread syncing. probably a cleaner way to do this but hey, it works!
board::ResetToStockCpu(); board::ResetToStockCpu();
} if (newGpuGovernorState == false && lastGpuGovernorState == true)
if (newGpuGovernorState == false && lastGpuGovernorState == true) {
svcSleepThread(100'000'000);
board::ResetToStockGpu(); board::ResetToStockGpu();
} if (newVrrGovernorState == false && lastVrrGovernorState == true)
if (newVrrGovernorState == false && lastVrrGovernorState == true) {
svcSleepThread(100'000'000);
board::ResetToStockDisplay(); board::ResetToStockDisplay();
}
if (newCpuGovernorState != lastCpuGovernorState || newGpuGovernorState != lastGpuGovernorState || newVrrGovernorState != lastVrrGovernorState) { if (newCpuGovernorState != lastCpuGovernorState || newGpuGovernorState != lastGpuGovernorState || newVrrGovernorState != lastVrrGovernorState) {
fileUtils::LogLine("[mgr] Governor state changed: CPU %s, GPU %s, VRR %s", newCpuGovernorState ? "enabled" : "disabled", newGpuGovernorState ? "enabled" : "disabled", newVrrGovernorState ? "enabled" : "disabled"); fileUtils::LogLine("[mgr] Governor state changed: CPU %s, GPU %s, VRR %s", newCpuGovernorState ? "enabled" : "disabled", newGpuGovernorState ? "enabled" : "disabled", newVrrGovernorState ? "enabled" : "disabled");
lastCpuGovernorState = newCpuGovernorState; lastCpuGovernorState = newCpuGovernorState;
@@ -85,22 +76,19 @@ namespace governor {
} }
} }
u32 SchedutilTargetHz(u32 util, u32 tableMaxHz) u32 SchedutilTargetHz(u32 util, u32 tableMaxHz) {
{
u64 hz = (u64)tableMaxHz * util / STEP_UTIL; u64 hz = (u64)tableMaxHz * util / STEP_UTIL;
return (u32)(std::min(hz, static_cast<u64>(tableMaxHz))); return (u32)(std::min(hz, static_cast<u64>(tableMaxHz)));
} }
u32 TableIndexForHz(const clockManager::FreqTable& table, u32 targetHz) u32 TableIndexForHz(const clockManager::FreqTable& table, u32 targetHz) {
{
for (u32 i = 0; i < table.count; i++) for (u32 i = 0; i < table.count; i++)
if (table.list[i] >= targetHz) if (table.list[i] >= targetHz)
return i; return i;
return table.count - 1; return table.count - 1;
} }
u32 ResolveTargetHz(HocClkModule module) u32 ResolveTargetHz(HocClkModule module) {
{
u32 hz = clockManager::gContext.overrideFreqs[module]; u32 hz = clockManager::gContext.overrideFreqs[module];
if (!hz) if (!hz)
hz = config::GetAutoClockHz( hz = config::GetAutoClockHz(
@@ -113,43 +101,44 @@ namespace governor {
return hz; return hz;
} }
void CpuGovernorThread(void* arg) void GovernorThread(void* arg) {
{
(void)arg; (void)arg;
u32 downHoldRemaining = 0; u32 cpuDownHoldRemaining = 0;
u32 lastHz = 0; u32 cpuLastHz = 0;
u32 gpuDownHoldRemaining = 0;
u32 gpuLastHz = 0;
u32 minHz = 612; u32 minHz = 612;
u32 tick = 0; u32 cpuTick = 0;
u8 vrrTick = 0;
u8 vrrFocusTick = 0;
for (;;) { for (;;) {
if (!clockManager::gRunning || !isCpuGovernorEnabled) {
downHoldRemaining = 0; if (!clockManager::gRunning) {
lastHz = 0; cpuDownHoldRemaining = 0;
cpuLastHz = 0;
gpuDownHoldRemaining = 0;
gpuLastHz = 0;
svcSleepThread(POLL_NS); svcSleepThread(POLL_NS);
continue; continue;
} }
if (isCpuGovernorEnabled) {
u32 mode = 0; u32 mode = 0;
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode); Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
if (R_SUCCEEDED(rc) && apmExtIsBoostMode(mode)) { if (R_SUCCEEDED(rc) && apmExtIsBoostMode(mode)) {
isCpuGovernorInBoostMode = true; isCpuGovernorInBoostMode = true;
downHoldRemaining = 0; cpuDownHoldRemaining = 0;
lastHz = 0; cpuLastHz = 0;
continue; // TODO: figure out a way to get boost clock easily and set it instead of just skipping the governor } else {
} else if (!apmExtIsBoostMode(mode)) {
isCpuGovernorInBoostMode = false; isCpuGovernorInBoostMode = false;
}
auto& table = clockManager::gFreqTable[HocClkModule_CPU]; auto& table = clockManager::gFreqTable[HocClkModule_CPU];
if (table.count == 0)
continue;
std::scoped_lock lock{clockManager::gContextMutex}; std::scoped_lock lock{clockManager::gContextMutex};
u32 cpuLoad = board::GetPartLoad(HocClkPartLoad_CPUMax); u32 cpuLoad = board::GetPartLoad(HocClkPartLoad_CPUMax);
u32 tableMaxHz = table.list[table.count - 1]; u32 tableMaxHz = table.list[table.count - 1];
u32 desiredHz = SchedutilTargetHz(cpuLoad, tableMaxHz); u32 desiredHz = SchedutilTargetHz(cpuLoad, tableMaxHz);
u32 targetHz = ResolveTargetHz(HocClkModule_CPU); u32 targetHz = ResolveTargetHz(HocClkModule_CPU);
@@ -157,60 +146,42 @@ namespace governor {
if (targetHz && desiredHz > targetHz) if (targetHz && desiredHz > targetHz)
desiredHz = targetHz; desiredHz = targetHz;
if (maxHz && desiredHz > maxHz) if (maxHz && desiredHz > maxHz)
desiredHz = maxHz; desiredHz = maxHz;
u32 newHz = table.list[TableIndexForHz(table, desiredHz)]; u32 newHz = table.list[TableIndexForHz(table, desiredHz)];
bool goingDown = (cpuLastHz != 0) && (newHz < cpuLastHz);
// ramp up fast, go down slow
bool goingDown = (lastHz != 0) && (newHz < lastHz);
if (!goingDown) if (!goingDown)
downHoldRemaining = 0; cpuDownHoldRemaining = 0;
else if (downHoldRemaining == 0) else if (cpuDownHoldRemaining == 0)
downHoldRemaining = DOWN_HOLD_TICKS; cpuDownHoldRemaining = DOWN_HOLD_TICKS;
if (downHoldRemaining > 0) if (cpuDownHoldRemaining > 0)
downHoldRemaining--; cpuDownHoldRemaining--;
if (++tick > 50) { if (++cpuTick > 50) {
minHz = config::GetConfigValue(HocClkConfigValue_CpuGovernorMinimumFreq); minHz = config::GetConfigValue(HocClkConfigValue_CpuGovernorMinimumFreq);
tick = 0; cpuTick = 0;
} }
if (newHz < minHz) if (newHz < minHz)
newHz = minHz; newHz = minHz;
if ((!goingDown || (downHoldRemaining == 0)) && clockManager::IsAssignableHz(HocClkModule_CPU, newHz)) { if ((!goingDown || (cpuDownHoldRemaining == 0)) && clockManager::IsAssignableHz(HocClkModule_CPU, newHz)) {
board::SetHz(HocClkModule_CPU, newHz); board::SetHz(HocClkModule_CPU, newHz);
clockManager::gContext.freqs[HocClkModule_CPU] = newHz; clockManager::gContext.freqs[HocClkModule_CPU] = newHz;
lastHz = newHz; cpuLastHz = newHz;
}
svcSleepThread(POLL_NS);
} }
} }
} else {
void GovernorThread(void* arg) isCpuGovernorInBoostMode = false;
{ cpuDownHoldRemaining = 0;
(void)arg; cpuLastHz = 0;
u32 downHoldRemaining = 0;
u32 lastHz = 0;
for (;;) {
if (!clockManager::gRunning || !isGpuGovernorEnabled) {
downHoldRemaining = 0;
lastHz = 0;
svcSleepThread(POLL_NS);
continue;
} }
if (isGpuGovernorEnabled) {
auto& table = clockManager::gFreqTable[HocClkModule_GPU]; auto& table = clockManager::gFreqTable[HocClkModule_GPU];
if (table.count == 0)
continue;
std::scoped_lock lock{clockManager::gContextMutex}; std::scoped_lock lock{clockManager::gContextMutex};
u32 gpuLoad = board::GetPartLoad(HocClkPartLoad_GPU); u32 gpuLoad = board::GetPartLoad(HocClkPartLoad_GPU);
@@ -221,76 +192,48 @@ namespace governor {
if (targetHz && desiredHz > targetHz) if (targetHz && desiredHz > targetHz)
desiredHz = targetHz; desiredHz = targetHz;
if (maxHz && desiredHz > maxHz) if (maxHz && desiredHz > maxHz)
desiredHz = maxHz; desiredHz = maxHz;
u32 newHz = table.list[TableIndexForHz(table, desiredHz)]; u32 newHz = table.list[TableIndexForHz(table, desiredHz)];
bool goingDown = (lastHz != 0) && (newHz < lastHz); bool goingDown = (gpuLastHz != 0) && (newHz < gpuLastHz);
if (!goingDown) if (!goingDown)
downHoldRemaining = 0; gpuDownHoldRemaining = 0;
else if (downHoldRemaining == 0) else if (gpuDownHoldRemaining == 0)
downHoldRemaining = DOWN_HOLD_TICKS; gpuDownHoldRemaining = DOWN_HOLD_TICKS;
if (downHoldRemaining > 0) if (gpuDownHoldRemaining > 0)
downHoldRemaining--; gpuDownHoldRemaining--;
if ((!goingDown || (downHoldRemaining == 0)) && clockManager::IsAssignableHz(HocClkModule_GPU, newHz)) { if ((!goingDown || (gpuDownHoldRemaining == 0)) && clockManager::IsAssignableHz(HocClkModule_GPU, newHz)) {
board::SetHz(HocClkModule_GPU, newHz); board::SetHz(HocClkModule_GPU, newHz);
clockManager::gContext.freqs[HocClkModule_GPU] = newHz; clockManager::gContext.freqs[HocClkModule_GPU] = newHz;
lastHz = newHz; gpuLastHz = newHz;
}
} else {
gpuDownHoldRemaining = 0;
gpuLastHz = 0;
} }
svcSleepThread(POLL_NS); if (isVRREnabled && clockManager::gContext.profile != HocClkProfile_Docked && clockManager::gContext.isSaltyNXInstalled) {
} bool skipVrr = false;
}
void VRRThread(void* arg) if (++vrrFocusTick > 100) {
{ vrrFocusTick = 0;
(void)arg;
u8 tick = 0, tick2 = 0;
for (;;) {
if (!clockManager::gRunning || clockManager::gContext.profile == HocClkProfile_Docked || !isVRREnabled) {
svcSleepThread(POLL_NS);
continue;
}
std::scoped_lock lock{clockManager::gContextMutex};
if(++tick2 > 100) {
bool isApplicationOutOfFocus = false; bool isApplicationOutOfFocus = false;
Result rc = processManagement::isApplicationOutOfFocus(&isApplicationOutOfFocus); Result rc = processManagement::isApplicationOutOfFocus(&isApplicationOutOfFocus);
if(R_FAILED(rc)) { if (R_FAILED(rc) || isApplicationOutOfFocus) {
svcSleepThread(POLL_NS);
continue;
}
if(isApplicationOutOfFocus) {
board::ResetToStockDisplay(); board::ResetToStockDisplay();
svcSleepThread(POLL_NS); skipVrr = true;
continue;
} }
} }
u8 fps; if (!skipVrr) {
u8 fps = integrations::GetSaltyNXFPS();
if (clockManager::gContext.isSaltyNXInstalled) { if (fps != 254) {
fps = integrations::GetSaltyNXFPS(); std::scoped_lock lock{clockManager::gContextMutex};
} else {
svcSleepThread(~0ULL); // effectively disable the thread if SaltyNX isn't installed, as there's no point in it running
continue;
}
if (fps == 254) {
svcSleepThread(POLL_NS);
continue;
}
// if(appletGetFocusState() != AppletFocusState_InFocus) {
// board::ResetToStockDisplay();
// continue;
// }
u32 targetHz = clockManager::gContext.overrideFreqs[HocClkModule_Display]; u32 targetHz = clockManager::gContext.overrideFreqs[HocClkModule_Display];
if (!targetHz) { if (!targetHz) {
@@ -299,17 +242,10 @@ namespace governor {
targetHz = config::GetAutoClockHz(HOCCLK_GLOBAL_PROFILE_TID, HocClkModule_Display, clockManager::gContext.profile, false); targetHz = config::GetAutoClockHz(HOCCLK_GLOBAL_PROFILE_TID, HocClkModule_Display, clockManager::gContext.profile, false);
} }
u8 maxDisplay; u8 maxDisplay = targetHz ? (u8)targetHz : 60;
if (targetHz) {
maxDisplay = targetHz;
} else {
maxDisplay = 60; // don't assume display stuff!
}
u8 minDisplay = board::GetConsoleType() == HocClkConsoleType_Aula ? 45 : 40; u8 minDisplay = board::GetConsoleType() == HocClkConsoleType_Aula ? 45 : 40;
if (maxDisplay == minDisplay)
continue;
if (maxDisplay != minDisplay) {
if (fps >= minDisplay && fps <= maxDisplay) { if (fps >= minDisplay && fps <= maxDisplay) {
board::SetHz(HocClkModule_Display, fps); board::SetHz(HocClkModule_Display, fps);
clockManager::gContext.freqs[HocClkModule_Display] = fps; clockManager::gContext.freqs[HocClkModule_Display] = fps;
@@ -326,31 +262,23 @@ namespace governor {
} }
} }
if (++tick > 50) { if (++vrrTick > 50) {
vrrTick = 0;
board::SetHz(HocClkModule_Display, maxDisplay); board::SetHz(HocClkModule_Display, maxDisplay);
tick = 0;
svcSleepThread(50'000'000); svcSleepThread(50'000'000);
} }
}
}
}
}
svcSleepThread(POLL_NS); svcSleepThread(POLL_NS);
} }
} }
void startThreads() { void startThreads() {
threadCreate( threadCreate(
&cpuGovernorTHREAD, &governorTHREAD,
CpuGovernorThread,
nullptr,
NULL,
0x2000,
0x3F,
-2
);
threadCreate(
&gpuGovernorTHREAD,
GovernorThread, GovernorThread,
nullptr, nullptr,
NULL, NULL,
@@ -358,25 +286,10 @@ namespace governor {
0x3F, 0x3F,
-2 -2
); );
threadStart(&governorTHREAD);
threadCreate(
&vrrTHREAD,
VRRThread,
nullptr,
NULL,
0x2000,
0x3F,
-2
);
threadStart(&cpuGovernorTHREAD);
threadStart(&gpuGovernorTHREAD);
threadStart(&vrrTHREAD);
} }
void exitThreads() { void exitThreads() {
threadClose(&cpuGovernorTHREAD); threadClose(&governorTHREAD);
threadClose(&gpuGovernorTHREAD);
threadClose(&vrrTHREAD);
} }
} }

View File

@@ -38,5 +38,4 @@ namespace governor {
void startThreads(); void startThreads();
void exitThreads(); void exitThreads();
void HandleGovernor(uint32_t targetHz); void HandleGovernor(uint32_t targetHz);
void GovernorThread(void* arg);
} }

View File

@@ -38,7 +38,7 @@
#include "ipc_service.hpp" #include "ipc_service.hpp"
#include "config.hpp" #include "config.hpp"
#define INNER_HEAP_SIZE 0x4A000 #define INNER_HEAP_SIZE 0x3A000
extern "C" extern "C"
{ {