diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e524d79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 65d54fd..af3709a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.15) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) if(DEFINED ENV{VITASDK}) @@ -25,6 +25,7 @@ add_executable(${PROJECT_NAME} src/perf.c src/oc.c src/profile.c + src/power.c ) target_link_libraries(${PROJECT_NAME} @@ -41,6 +42,8 @@ target_link_libraries(${PROJECT_NAME} SceCtrlForDriver_stub SceIofilemgrForDriver_stub SceSblAIMgrForDriver_stub + SceDisplayForDriver_stub + SceSblACMgrForDriver_stub ) vita_create_self(${PROJECT_NAME}.skprx ${PROJECT_NAME} diff --git a/README.md b/README.md index 3e53eb1..dd7046b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Yet another overclocking plugin ## Features: - Allows you to change CPU, GPU, BUS and XBAR clocks separately, in these steps: - - **CPU:** 41, 83, 111, 166, 222, 333, 444, 500 MHz + - **CPU:** 41, 83, 111, 126, 147, 166, 181, 209, 222, 251, 292, 333, 389, 444, 468, 500 MHz - **GPU (ES4):** 41, 55, 83, 111, 166, 222 MHz - **BUS:** 55, 83, 111, 166, 222 MHz - **XBAR:** 83, 111, 166 MHz @@ -25,6 +25,14 @@ Yet another overclocking plugin - **Default freq.** (WHITE) - the plugin will not interfere, but rather use the default freq. for current game - **Manual freq.** (BLUE) - the plugin will use your specified freq. - press **LEFT/RIGHT** to immediately change the frequency +- Press **O** to activate auto frequency mode for currently selected **> device <** (only works for CPU): + - **Default freq.** (WHITE) - the plugin will not interfere, but rather use the default freq. for current game + - **Auto freq.** - the plugin will use your specified power plan. **(Do not use in LiveArea to avoid crashes)** + - press **LEFT/RIGHT** to immediately change the max frequency. This frequency won't be surpassed. + - press **LEFT TRIGGER/RIGHT TRIGGER** immediately change the power plan. + - **Power Saving** (GREEN) - Less agressive power plan. Ideal for less demanding apps. + - **Balanced** (YELLOW) - Balanced power plan. + - **Performance** (RED) - Most agressive power plan. Best suited for demanding games. - Press **X** when **> save profile <** is selected to save/delete profiles - All **Manual freq.** (BLUE) will be loaded and applied next time you start/resume the game - All **Default freq.** (WHITE) will be kept to default (set to whatever freq. the game asks for) diff --git a/src/gui.c b/src/gui.c index de0865a..a71fa1d 100644 --- a/src/gui.c +++ b/src/gui.c @@ -13,6 +13,7 @@ #include "perf.h" #include "oc.h" #include "profile.h" +#include "power.h" int vsnprintf(char *s, size_t n, const char *format, va_list arg); @@ -25,6 +26,12 @@ static int g_gui_fb_last_width = 960; static float g_gui_fb_w_ratio = 1.0f; static float g_gui_fb_h_ratio = 1.0f; +static int g_gui_width = GUI_WIDTH; +static int g_gui_height = GUI_HEIGHT; + +static uint8_t burn_off_x = 0; +static uint8_t burn_off_y = 0; + static rgba_t *g_gui_buffer; static SceUID g_gui_buffer_uid = -1; @@ -49,7 +56,7 @@ static bool g_gui_lazydraw_memusage = false; static const unsigned char GUI_CORNERS_XD[GUI_CORNERS_XD_RADIUS] = {9, 7, 5, 4, 3, 2, 2, 1, 1}; static const rgba_t WHITE = {.rgba = {.r = 255, .g = 255, .b = 255, .a = 255}}; -static const rgba_t BLACK = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 0}}; +static const rgba_t BLACK = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}}; static const rgba_t FPS_COLOR = {.rgba = {.r = 0, .g = 255, .b = 0, .a = 255}}; psvs_gui_mode_t psvs_gui_get_mode() { @@ -83,6 +90,22 @@ void psvs_gui_input_check(uint32_t buttons) { g_gui_mode--; // Hide g_gui_mode_changed = true; } + + switch (g_gui_mode) + { + case PSVS_GUI_MODE_OSD: + g_gui_width = GUI_WIDTH; + g_gui_height = GUI_OSD_HEIGHT; + break; + case PSVS_GUI_MODE_OSD2: + g_gui_width = GUI_OSD2_WIDTH; + g_gui_height = GUI_OSD2_HEIGHT; + break; + default: + g_gui_width = GUI_WIDTH; + g_gui_height = GUI_HEIGHT; + break; + } } // In full menu else if (g_gui_mode == PSVS_GUI_MODE_FULL) { @@ -112,9 +135,33 @@ void psvs_gui_input_check(uint32_t buttons) { if (psvs_oc_get_mode(device) == PSVS_OC_MODE_MANUAL) { // Move L/R if (buttons_new & SCE_CTRL_RIGHT) { - psvs_oc_change_manual(device, true); + psvs_oc_change(device, true); } else if (buttons_new & SCE_CTRL_LEFT) { - psvs_oc_change_manual(device, false); + psvs_oc_change(device, false); + } + // Back to default + else if (buttons_new & SCE_CTRL_CROSS) { + psvs_oc_set_mode(device, PSVS_OC_MODE_DEFAULT); + } + // Enable auto freq for CPU + else if (device == PSVS_OC_DEVICE_CPU && buttons_new & SCE_CTRL_CIRCLE) { + psvs_oc_set_mode(device, PSVS_OC_MODE_AUTO); + } + } + // In auto freq mode + else if (psvs_oc_get_mode(device) == PSVS_OC_MODE_AUTO) { + // Move L/R + if (buttons_new & SCE_CTRL_RIGHT) { + psvs_oc_change_max_freq(device, true); + } else if (buttons_new & SCE_CTRL_LEFT) { + psvs_oc_change_max_freq(device, false); + } + // Change power plan + if (buttons_new & SCE_CTRL_LTRIGGER) { + psvs_oc_raise_power_plan(false, device); + } + else if (buttons_new & SCE_CTRL_RTRIGGER) { + psvs_oc_raise_power_plan(true, device); } // Back to default else if (buttons_new & SCE_CTRL_CROSS) { @@ -124,8 +171,12 @@ void psvs_gui_input_check(uint32_t buttons) { // In default freq mode else { if (buttons_new & SCE_CTRL_CROSS) { - psvs_oc_reset_manual(device); - psvs_oc_set_mode(device, PSVS_OC_MODE_MANUAL); + psvs_oc_reset(device); + psvs_oc_set_mode(device, PSVS_OC_MODE_MANUAL); + } + if (buttons_new & SCE_CTRL_CIRCLE && device == PSVS_OC_DEVICE_CPU) { + psvs_oc_reset(device); + psvs_oc_set_mode(device, PSVS_OC_MODE_AUTO); } } } @@ -152,7 +203,7 @@ void psvs_gui_set_framebuf(const SceDisplayFrameBuf *pParam) { g_gui_font_width = 9; // <- trim last col, better scaling g_gui_font_height = 18; } else { - // 960x544 - Terminus 12x24 Bold + // 960x544 or more - Terminus 12x24 Bold g_gui_font = FONT_TER_U24B; g_gui_font_width = 12; g_gui_font_height = 24; @@ -224,7 +275,7 @@ static void _psvs_gui_dd_prchar(const char character, int x, int y) { uint8_t charByte = g_gui_font[charPosH + (xx_font / 8)]; if ((charByte >> (7 - (xx_font % 8))) & 1) { - ksceKernelMemcpyKernelToUser((uintptr_t)(px + xx), &FPS_COLOR, sizeof(rgba_t)); + *(px + xx) = FPS_COLOR; } } } @@ -235,13 +286,18 @@ void psvs_gui_dd_fps() { snprintf(buf, 4, "%d", psvs_perf_get_fps()); size_t len = strlen(buf); + uint32_t dacr; + DACR_UNRESTRICT(dacr); + for (int i = 0; i < len; i++) { _psvs_gui_dd_prchar(buf[i], 10 + i * g_gui_font_width * g_gui_font_scale, 10); } + + DACR_RESET(dacr); } void psvs_gui_clear() { - for (int i = 0; i < GUI_WIDTH * GUI_HEIGHT; i++) + for (int i = 0; i < g_gui_width * g_gui_height; i++) g_gui_buffer[i] = g_gui_color_bg; } @@ -249,7 +305,7 @@ static void _psvs_gui_prchar(const char character, int x, int y) { // Draw spaces faster if (character == ' ') { for (int yy = 0; yy < g_gui_font_height * g_gui_font_scale; yy++) { - rgba_t *buf = &g_gui_buffer[((y + yy) * GUI_WIDTH) + x]; + rgba_t *buf = &g_gui_buffer[((y + yy) * g_gui_width) + x]; for (int xx = 0; xx < g_gui_font_width * g_gui_font_scale; xx++) { buf[xx] = g_gui_color_bg; } @@ -262,14 +318,14 @@ static void _psvs_gui_prchar(const char character, int x, int y) { for (int yy = 0; yy < g_gui_font_height * g_gui_font_scale; yy++) { int yy_font = yy / g_gui_font_scale; - uint32_t displacement = x + (y + yy) * GUI_WIDTH; - if (displacement >= GUI_WIDTH * GUI_HEIGHT) + uint32_t displacement = x + (y + yy) * g_gui_width; + if (displacement >= g_gui_width * g_gui_height) return; // out of bounds rgba_t *px = (rgba_t *)g_gui_buffer + displacement; for (int xx = 0; xx < g_gui_font_width * g_gui_font_scale; xx++) { - if (x + xx >= GUI_WIDTH) + if (x + xx >= g_gui_width) return; // out of bounds // Get px 0/1 from osd_font.h @@ -346,40 +402,40 @@ static void _psvs_gui_draw_battery_template(int x, int y) { int w = GUI_RESCALE_X(GUI_BATT_SIZE_W); int h = GUI_RESCALE_Y(GUI_BATT_SIZE_H); - rgba_t *px = (rgba_t *)g_gui_buffer + (y * GUI_WIDTH) + x; + rgba_t *px = (rgba_t *)g_gui_buffer + (y * g_gui_width) + x; int xx, yy; for (xx = 0; xx < w; xx++) { // top *(px + xx) - = *(px + GUI_WIDTH + xx) = WHITE; + = *(px + g_gui_width + xx) = WHITE; // bottom - *(px + (h * GUI_WIDTH) + xx) - = *(px + ((h - 1) * GUI_WIDTH) + xx) = WHITE; + *(px + (h * g_gui_width) + xx) + = *(px + ((h - 1) * g_gui_width) + xx) = WHITE; } for (yy = 0; yy < h; yy++) { // left - *(px + (yy * GUI_WIDTH)) - = *(px + (yy * GUI_WIDTH) + 1) = WHITE; + *(px + (yy * g_gui_width)) + = *(px + (yy * g_gui_width) + 1) = WHITE; // right if (yy < h / 3 || yy > h - (h / 3)) { - *(px + (yy * GUI_WIDTH) + (w - 1)) - = *(px + (yy * GUI_WIDTH) + (w - 2)) = WHITE; + *(px + (yy * g_gui_width) + (w - 1)) + = *(px + (yy * g_gui_width) + (w - 2)) = WHITE; } else { - *(px + (yy * GUI_WIDTH) + (w - 1) + (h / 5)) - = *(px + (yy * GUI_WIDTH) + (w - 2) + (h / 5)) = WHITE; + *(px + (yy * g_gui_width) + (w - 1) + (h / 5)) + = *(px + (yy * g_gui_width) + (w - 2) + (h / 5)) = WHITE; } } // dzindzik for (xx = 0; xx < (h / 5) + 2; xx++) { // top - *(px + (GUI_WIDTH * (h / 3 - 1)) + (w - 2) + xx) - = *(px + (GUI_WIDTH * (h / 3)) + (w - 2) + xx) = WHITE; + *(px + (g_gui_width * (h / 3 - 1)) + (w - 2) + xx) + = *(px + (g_gui_width * (h / 3)) + (w - 2) + xx) = WHITE; // bottom - *(px + (GUI_WIDTH * (h - (h / 3))) + (w - 2) + xx) - = *(px + (GUI_WIDTH * (h - (h / 3) + 1)) + (w - 2) + xx) = WHITE; + *(px + (g_gui_width * (h - (h / 3))) + (w - 2) + xx) + = *(px + (g_gui_width * (h - (h / 3) + 1)) + (w - 2) + xx) = WHITE; } } @@ -389,16 +445,16 @@ static void _psvs_gui_draw_battery(int x, int y, int state, bool is_charging, rg int w = GUI_RESCALE_X(GUI_BATT_SIZE_W); int h = GUI_RESCALE_Y(GUI_BATT_SIZE_H); - rgba_t *px = (rgba_t *)g_gui_buffer + (y * GUI_WIDTH) + x; + rgba_t *px = (rgba_t *)g_gui_buffer + (y * g_gui_width) + x; int state_x = state > 95 ? w : (state * (w - 4) / 95) + 1; int xx, yy; for (xx = 2; xx < (w - 2); xx++) { for (yy = 2; yy < (h - 1); yy++) { if (xx <= state_x) { - *(px + (GUI_WIDTH * yy) + xx) = color; + *(px + (g_gui_width * yy) + xx) = color; } else { - *(px + (GUI_WIDTH * yy) + xx) = BLACK; + *(px + (g_gui_width * yy) + xx) = BLACK; } } } @@ -407,21 +463,23 @@ static void _psvs_gui_draw_battery(int x, int y, int state, bool is_charging, rg for (xx = 0; xx < (h / 5); xx++) { for (yy = 2; yy < (h / 3) + 2; yy++) { if (state > 95) { - *(px + (GUI_WIDTH * ((h / 3 - 1) + yy)) + w + xx - 2) = color; + *(px + (g_gui_width * ((h / 3 - 1) + yy)) + w + xx - 2) = color; } else { - *(px + (GUI_WIDTH * ((h / 3 - 1) + yy)) + w + xx - 2) = BLACK; + *(px + (g_gui_width * ((h / 3 - 1) + yy)) + w + xx - 2) = BLACK; } } } if (is_charging) { for (xx = 0; xx < (h / 3); xx++) - *(px + (GUI_WIDTH * (h / 2)) + (w / 5) + xx) = WHITE; + *(px + (g_gui_width * (h / 2)) + (w / 5) + xx) = WHITE; for (yy = 0; yy < (h / 3); yy++) - *(px + (GUI_WIDTH * ((h / 3) + yy + 1)) + (w / 5) + (h / 6)) = WHITE; + *(px + (g_gui_width * ((h / 3) + yy + 1)) + (w / 5) + (h / 6)) = WHITE; } } +// OSD mode + void psvs_gui_draw_osd_template() { psvs_gui_set_back_color(0, 0, 0, 255); psvs_gui_set_text_color(255, 255, 255, 255); @@ -430,7 +488,8 @@ void psvs_gui_draw_osd_template() { // CPU psvs_gui_printf(GUI_ANCHOR_LX(10, 0), GUI_ANCHOR_TY(8, 0), "CPU:"); psvs_gui_printf(GUI_ANCHOR_RX(10, 16), GUI_ANCHOR_TY(8, 0), "%% %% %% %%"); - psvs_gui_printf(GUI_ANCHOR_LX(10, 10), GUI_ANCHOR_TY(10, 1), "%%"); + //psvs_gui_printf(GUI_ANCHOR_LX(10, 10), GUI_ANCHOR_TY(10, 1), "%%"); + psvs_gui_printf(GUI_ANCHOR_LX(10, 10), GUI_ANCHOR_TY(10, 1), "MHz"); // FPS psvs_gui_printf(GUI_ANCHOR_LX(10, 3), GUI_ANCHOR_TY(10, 1), "FPS"); @@ -438,6 +497,10 @@ void psvs_gui_draw_osd_template() { // Battery psvs_gui_printf(GUI_ANCHOR_RX(20 + GUI_BATT_SIZE_W, 1), GUI_ANCHOR_TY(10, 1), "%%"); _psvs_gui_draw_battery_template(GUI_ANCHOR_RX(14 + GUI_BATT_SIZE_W, 0), GUI_ANCHOR_TY(13, 1)); + + // System Consumption + psvs_gui_printf(GUI_ANCHOR_LX(10, 0), GUI_ANCHOR_TY(12, 2), "Consumption:"); + psvs_gui_printf(GUI_ANCHOR_RX(10, 2), GUI_ANCHOR_TY(12, 2), "mW"); } void psvs_gui_draw_osd_cpu() { @@ -451,9 +514,12 @@ void psvs_gui_draw_osd_cpu() { } // Draw peak load - val = psvs_perf_get_peak(); - psvs_gui_set_text_color2(psvs_gui_scale_color(val, 0, 100)); - psvs_gui_printf(GUI_ANCHOR_LX(10, 7), GUI_ANCHOR_TY(10, 1), "%3d", val); + //val = psvs_perf_get_peak(); + // Draw cpu freq + val = psvs_oc_get_freq(PSVS_OC_DEVICE_CPU); + //psvs_gui_set_text_color2(psvs_gui_scale_color(val, 0, 100)); + psvs_gui_set_text_color2(psvs_gui_scale_color(val, 41, 500)); + psvs_gui_printf(GUI_ANCHOR_LX(8, 7), GUI_ANCHOR_TY(10, 1), "%3d", val); psvs_gui_set_text_color(255, 255, 255, 255); } @@ -477,6 +543,15 @@ void psvs_gui_draw_osd_batt() { color.rgba.b = (int)(color.rgba.b * 0.75f); _psvs_gui_draw_battery(GUI_ANCHOR_RX(14 + GUI_BATT_SIZE_W, 0), GUI_ANCHOR_TY(13, 1), batt->percent, batt->is_charging, color); + // Draw system power consumption + color = psvs_gui_scale_color(batt->power_cons, 500, 5000); + psvs_gui_set_text_color2(color); + + if (abs(batt->power_cons) < 10000) { + int len = 6 + (batt->power_cons < 0 ? 1 : 0); + + psvs_gui_printf(GUI_ANCHOR_RX(12, len), GUI_ANCHOR_TY(12, 2), "%4d", batt->power_cons); + } psvs_gui_set_text_color(255, 255, 255, 255); } @@ -492,6 +567,142 @@ void psvs_gui_draw_osd_fps() { psvs_gui_set_text_color(255, 255, 255, 255); } +// OSD2 mode + +void psvs_gui_draw_osd2_template() { + //psvs_gui_set_back_color(50, 50, 50, 255); + psvs_gui_set_back_color(0, 0, 0, 255); // Black background to avoid OLED burn + psvs_gui_set_text_color(255, 255, 255, 255); + psvs_gui_clear(); + + // Battery and System Consumption + psvs_gui_set_text_color(255, 155, 135, 255); + psvs_gui_printf(GUI_ANCHOR_LX(10, 0), GUI_ANCHOR_TY(4, 0), "BATT:"); + psvs_gui_set_text_color(255, 255, 255, 255); + psvs_gui_printf(GUI_ANCHOR_LX(10, 9), GUI_ANCHOR_TY(4, 0), "%%"); + psvs_gui_printf(GUI_ANCHOR_LX(12, 15), GUI_ANCHOR_TY(4, 0), "W"); + psvs_gui_set_text_color(196, 155, 207, 255); + psvs_gui_printf(GUI_ANCHOR_LX(12, 16), GUI_ANCHOR_TY(4, 0), " | "); + psvs_gui_set_text_color(255, 255, 255, 255); + + // GPU + psvs_gui_set_text_color(74, 232, 130, 255); + psvs_gui_printf(GUI_ANCHOR_LX(10, 19), GUI_ANCHOR_TY(4, 0), "GPU:"); + psvs_gui_set_text_color(255, 255, 255, 255); + psvs_gui_printf(GUI_ANCHOR_LX(12, 27), GUI_ANCHOR_TY(4, 0), "MHz"); + psvs_gui_printf(GUI_ANCHOR_LX(12, 34), GUI_ANCHOR_TY(4, 0), "MB"); + psvs_gui_set_text_color(196, 155, 207, 255); + psvs_gui_printf(GUI_ANCHOR_LX(12, 36), GUI_ANCHOR_TY(4, 0), " | "); + psvs_gui_set_text_color(255, 255, 255, 255); + + // CPU + psvs_gui_set_text_color(116, 217, 252, 255); + psvs_gui_printf(GUI_ANCHOR_LX(10, 39), GUI_ANCHOR_TY(4, 0), "CPU:"); + psvs_gui_set_text_color(255, 255, 255, 255); + psvs_gui_printf(GUI_ANCHOR_LX(12, 47), GUI_ANCHOR_TY(4, 0), "MHz"); + psvs_gui_printf(GUI_ANCHOR_LX(10, 54), GUI_ANCHOR_TY(4, 0), "%%"); + psvs_gui_set_text_color(196, 155, 207, 255); + psvs_gui_printf(GUI_ANCHOR_LX(10, 55), GUI_ANCHOR_TY(4, 0), " | "); + psvs_gui_set_text_color(255, 255, 255, 255); + + // RAM: + psvs_gui_set_text_color(255, 184, 218, 255); + psvs_gui_printf(GUI_ANCHOR_LX(10, 58), GUI_ANCHOR_TY(4, 0), "RAM:"); + psvs_gui_set_text_color(255, 255, 255, 255); + psvs_gui_printf(GUI_ANCHOR_LX(12, 66), GUI_ANCHOR_TY(4, 0), "MB"); + psvs_gui_set_text_color(196, 155, 207, 255); + psvs_gui_printf(GUI_ANCHOR_LX(12, 68), GUI_ANCHOR_TY(4, 0), " | "); + psvs_gui_set_text_color(255, 255, 255, 255); + + // FPS + psvs_gui_set_text_color(255, 172, 128, 255); + psvs_gui_printf(GUI_ANCHOR_LX(10, 71), GUI_ANCHOR_TY(4, 0), "FPS:"); + psvs_gui_set_text_color(255, 255, 255, 255); +} + +void psvs_gui_draw_osd2_cpu() { + int val; + + // Draw peak load + val = psvs_perf_get_peak(); + //psvs_gui_set_text_color2(psvs_gui_scale_color(val, 0, 100)); + psvs_gui_printf(GUI_ANCHOR_LX(10, 51), GUI_ANCHOR_TY(4, 0), "%3d", val); + + // Draw cpu freq + val = psvs_oc_get_freq(PSVS_OC_DEVICE_CPU); + //psvs_gui_set_text_color2(psvs_gui_scale_color(val, 41, 500)); + psvs_gui_printf(GUI_ANCHOR_LX(10, 44), GUI_ANCHOR_TY(4, 0), "%3d", val); + + //psvs_gui_set_text_color(255, 255, 255, 255); +} + +void psvs_gui_draw_osd2_gpu() { + int val = psvs_oc_get_freq(PSVS_OC_DEVICE_GPU_ES4); + //psvs_gui_set_text_color2(psvs_gui_scale_color(val, 41, 222)); + psvs_gui_printf(GUI_ANCHOR_LX(10, 24), GUI_ANCHOR_TY(4, 0), "%3d", val); + //psvs_gui_set_text_color(255, 255, 255, 255); +} + +void psvs_gui_draw_osd2_batt() { + psvs_battery_t *batt = psvs_perf_get_batt(); + if (!batt->_has_changed && !g_gui_lazydraw_batt) + return; + + batt->_has_changed = false; + g_gui_lazydraw_batt = false; + + // Draw battery percentage + //rgba_t color = psvs_gui_scale_color(60 - batt->percent, 0, 100); + //psvs_gui_set_text_color2(color); + psvs_gui_printf(GUI_ANCHOR_LX(10, 6), GUI_ANCHOR_TY(4, 0), "%3d", batt->percent); + + //color = psvs_gui_scale_color(batt->power_cons, 500, 5000); + //psvs_gui_set_text_color2(color); + + int watts = batt->power_cons / 1000; + int watts_decimal = (abs(batt->power_cons) - watts * 1000) / 100; + + // Draw system power consumption + if (abs(watts) < 10) { + int len = 3 + (watts >= 10); + + psvs_gui_printf(GUI_ANCHOR_LX(10, 14 - len), GUI_ANCHOR_TY(4, 0), "%2d.%d", watts, watts_decimal); + } + else if (abs(watts) < 100) { + psvs_gui_printf(GUI_ANCHOR_LX(10, 11), GUI_ANCHOR_TY(4, 0), "%3d", watts); + } + //psvs_gui_set_text_color(255, 255, 255, 255); +} + +void psvs_gui_draw_osd2_fps() { + int fps = psvs_perf_get_fps(); + + //psvs_gui_set_text_color2(psvs_gui_scale_color(30 - fps, 0, 30)); + psvs_gui_printf(GUI_ANCHOR_LX(10, 75), GUI_ANCHOR_TY(4, 0), "%3d", fps); + + //psvs_gui_set_text_color(255, 255, 255, 255); +} + +void psvs_gui_draw_osd2_mem() { + psvs_memory_t *mem = psvs_perf_get_memusage(); + if (!mem->_has_changed && !g_gui_lazydraw_memusage) + return; + + mem->_has_changed = false; + g_gui_lazydraw_memusage = false; + + int used_ram = (mem->main_total - mem->main_free) / (1024 * 1024); + int used_vram = (mem->cdram_total - mem->cdram_free) / (1024 * 1024); + + //psvs_gui_set_text_color2(psvs_gui_scale_color(used_vram, 0, 128)); + psvs_gui_printf(GUI_ANCHOR_LX(10, 31), GUI_ANCHOR_TY(4, 0), "%3d", used_vram); + //psvs_gui_set_text_color2(psvs_gui_scale_color(used_ram, 0, 512)); + psvs_gui_printf(GUI_ANCHOR_LX(10, 63), GUI_ANCHOR_TY(4, 0), "%3d", used_ram); + //psvs_gui_set_text_color(255, 255, 255, 255); +} + +// Full mode + void psvs_gui_draw_template() { psvs_gui_set_back_color(0, 0, 0, 255); psvs_gui_set_text_color(255, 255, 255, 255); @@ -499,7 +710,7 @@ void psvs_gui_draw_template() { // Header psvs_gui_set_text_scale(0.5f); - psvs_gui_printf(GUI_ANCHOR_CX2(13, 0.5f), GUI_ANCHOR_TY(8, 0), PSVS_VERSION_STRING); + psvs_gui_printf(GUI_ANCHOR_CX2(18, 0.5f), GUI_ANCHOR_TY(8, 0), PSVS_VERSION_STRING); psvs_gui_printf(GUI_ANCHOR_RX2(10, 10, 0.5f), GUI_ANCHOR_TY(8, 0), "by Electry"); psvs_gui_set_text_scale(1.0f); @@ -653,16 +864,34 @@ static void _psvs_gui_draw_menu_item(int lines, int clock, psvs_gui_menu_control psvs_gui_printf(GUI_ANCHOR_CX(19) + GUI_ANCHOR_LX(0, 18), GUI_ANCHOR_BY(10, lines), " "); } - // Highlight freq if in manual mode + // Highlight freq if in manual mode (blue) if (psvs_oc_get_mode(_psvs_gui_get_device_from_menuctrl(menuctrl)) == PSVS_OC_MODE_MANUAL) { psvs_gui_set_text_color(0, 200, 255, 255); } + // Highlight freq if in auto mode (red) + else if (psvs_oc_get_mode(_psvs_gui_get_device_from_menuctrl(menuctrl)) == PSVS_OC_MODE_AUTO) { + switch (psvs_oc_get_power_plan(_psvs_gui_get_device_from_menuctrl(menuctrl))) + { + case PSVS_POWER_PLAN_SAVER: + psvs_gui_set_text_color(51, 204, 51, 255); + break; + case PSVS_POWER_PLAN_BALANCED: + psvs_gui_set_text_color(255, 213, 0, 255); + break; + case PSVS_POWER_PLAN_PERFORMANCE: + psvs_gui_set_text_color(255, 0, 0, 255); + break; + + default: + break; + } + } psvs_gui_printf(GUI_ANCHOR_CX(15) + GUI_ANCHOR_LX(0, 6), GUI_ANCHOR_BY(10, lines), "%3d MHz", clock); psvs_gui_set_text_color(255, 255, 255, 255); } void psvs_gui_draw_menu() { - _psvs_gui_draw_menu_item(5, psvs_oc_get_freq(PSVS_OC_DEVICE_CPU), PSVS_GUI_MENUCTRL_CPU); + _psvs_gui_draw_menu_item(5, psvs_oc_get_max_freq(PSVS_OC_DEVICE_CPU), PSVS_GUI_MENUCTRL_CPU); _psvs_gui_draw_menu_item(4, psvs_oc_get_freq(PSVS_OC_DEVICE_GPU_ES4), PSVS_GUI_MENUCTRL_GPU_ES4); _psvs_gui_draw_menu_item(3, psvs_oc_get_freq(PSVS_OC_DEVICE_BUS), PSVS_GUI_MENUCTRL_BUS); _psvs_gui_draw_menu_item(2, psvs_oc_get_freq(PSVS_OC_DEVICE_GPU_XBAR), PSVS_GUI_MENUCTRL_GPU_XBAR); @@ -713,31 +942,62 @@ void psvs_gui_deinit() { } void psvs_gui_cpy() { - int height = (g_gui_mode == PSVS_GUI_MODE_OSD) ? GUI_OSD_HEIGHT : GUI_HEIGHT; + int w = GUI_RESCALE_X(g_gui_width); + int h = GUI_RESCALE_Y(g_gui_height); + int x, y; + + switch (g_gui_mode) + { + case PSVS_GUI_MODE_OSD: + x = 10; + y = 10; + break; + case PSVS_GUI_MODE_OSD2: + x = 0; + y = 0; + break; + default: + x = (g_gui_fb.width / 2) - (w / 2); + y = (g_gui_fb.height / 2) - (h / 2); + break; + } - int w = (int)(GUI_WIDTH * (g_gui_fb.width / 960.0f)); - int h = (int)(height * (g_gui_fb.height / 544.0f)); - int x = (g_gui_mode == PSVS_GUI_MODE_OSD) ? 10 : (g_gui_fb.width / 2) - (w / 2); - int y = (g_gui_mode == PSVS_GUI_MODE_OSD) ? 10 : (g_gui_fb.height / 2) - (h / 2); + uint32_t dacr; + DACR_UNRESTRICT(dacr); for (int line = 0; line < h; line++) { int xd = 0; - int xd_line = line * (544.0f / g_gui_fb.height); + int xd_line = line; + if (g_gui_fb.height < 544.0f) + xd_line = xd_line * (544.0f / g_gui_fb.height); // Top corners if (xd_line < GUI_CORNERS_XD_RADIUS) { - xd = GUI_CORNERS_XD[xd_line] * (g_gui_fb.width / 960.0f); + xd = GUI_RESCALE_X(GUI_CORNERS_XD[xd_line]); } // Bottom corners - if (xd_line >= height - GUI_CORNERS_XD_RADIUS) { - xd = GUI_CORNERS_XD[height - xd_line - 1] * (g_gui_fb.width / 960.0f); + else if (xd_line >= g_gui_height - GUI_CORNERS_XD_RADIUS) { + xd = GUI_RESCALE_X(GUI_CORNERS_XD[g_gui_height - xd_line - 1]); } int off = ((line + y) * g_gui_fb.pitch + x + xd); - ksceKernelMemcpyKernelToUser( - (uintptr_t)&((rgba_t *)g_gui_fb.base)[off], - &((rgba_t *)g_gui_buffer)[line * GUI_WIDTH + xd], - sizeof(rgba_t) * (w - xd*2)); + + void *src = &((rgba_t *)g_gui_buffer)[line * g_gui_width + xd]; + void *dest = &((rgba_t *)g_gui_fb.base)[off]; + int size = sizeof(rgba_t) * (w - xd*2); + + memcpy(dest, src, size); } + + DACR_RESET(dacr); +} + +void psvs_gui_change_bunr_off() +{ + burn_off_y = (burn_off_x + burn_off_y) % (GUI_BURN_OFF * 2); + burn_off_x = GUI_BURN_OFF - burn_off_x; + + g_gui_lazydraw_memusage = true; + g_gui_lazydraw_batt = true; } diff --git a/src/gui.h b/src/gui.h index dc1ddfd..12d4924 100644 --- a/src/gui.h +++ b/src/gui.h @@ -5,7 +5,10 @@ #define GUI_WIDTH 308 #define GUI_HEIGHT 344 -#define GUI_OSD_HEIGHT 64 +#define GUI_OSD2_WIDTH 960 + +#define GUI_OSD_HEIGHT 96 +#define GUI_OSD2_HEIGHT 32 #define GUI_BATT_SIZE_W 32 #define GUI_BATT_SIZE_H 16 @@ -13,20 +16,22 @@ #define GUI_FONT_W 12 #define GUI_FONT_H 24 -#define GUI_ANCHOR_LX(off, len) (off + (len) * GUI_FONT_W) -#define GUI_ANCHOR_RX(off, len) (GUI_WIDTH - (off) - (len) * GUI_FONT_W) -#define GUI_ANCHOR_RX2(off, len, scale) (GUI_WIDTH - (off) - (len) * GUI_FONT_W * (scale)) +#define GUI_BURN_OFF 3 + +#define GUI_ANCHOR_LX(off, len) (off + burn_off_x + (len) * GUI_FONT_W) +#define GUI_ANCHOR_RX(off, len) (GUI_WIDTH - (off + burn_off_x) - (len) * GUI_FONT_W) +#define GUI_ANCHOR_RX2(off, len, scale) (GUI_WIDTH - (off + burn_off_x) - (len) * GUI_FONT_W * (scale)) -#define GUI_ANCHOR_TY(off, lines) (off + (lines) * GUI_FONT_H) -#define GUI_ANCHOR_BY(off, lines) (GUI_HEIGHT - (off) - (lines) * GUI_FONT_H) -#define GUI_ANCHOR_BY2(off, lines, scale) (GUI_HEIGHT - (off) - (lines) * GUI_FONT_H * (scale)) +#define GUI_ANCHOR_TY(off, lines) (off + burn_off_y + (lines) * GUI_FONT_H) +#define GUI_ANCHOR_BY(off, lines) (GUI_HEIGHT - (off + burn_off_y) - (lines) * GUI_FONT_H) +#define GUI_ANCHOR_BY2(off, lines, scale) (GUI_HEIGHT - (off + burn_off_y) - (lines) * GUI_FONT_H * (scale)) #define GUI_ANCHOR_CX(len) (GUI_WIDTH / 2 - ((len) * GUI_FONT_W) / 2) #define GUI_ANCHOR_CX2(len, scale) (GUI_WIDTH / 2 - ((len) * GUI_FONT_W * (scale)) / 2) #define GUI_ANCHOR_CY(lines) (GUI_HEIGHT / 2 - ((lines) * GUI_FONT_H) / 2) -#define GUI_RESCALE_X(x) (int)((x) * g_gui_fb_w_ratio) -#define GUI_RESCALE_Y(y) (int)((y) * g_gui_fb_h_ratio) +#define GUI_RESCALE_X(x) (int)((x) * (g_gui_fb_w_ratio > 1.0f ? 1.0f : g_gui_fb_w_ratio)) +#define GUI_RESCALE_Y(y) (int)((y) * (g_gui_fb_h_ratio > 1.0f ? 1.0f : g_gui_fb_h_ratio)) #define GUI_GLOBAL_PROFILE_BUTTON_MOD SCE_CTRL_LTRIGGER @@ -45,6 +50,7 @@ typedef enum { PSVS_GUI_MODE_FULL, PSVS_GUI_MODE_FPS, PSVS_GUI_MODE_OSD, + PSVS_GUI_MODE_OSD2, PSVS_GUI_MODE_MAX } psvs_gui_mode_t; @@ -85,6 +91,13 @@ void psvs_gui_draw_osd_cpu(); void psvs_gui_draw_osd_fps(); void psvs_gui_draw_osd_batt(); +void psvs_gui_draw_osd2_template(); +void psvs_gui_draw_osd2_cpu(); +void psvs_gui_draw_osd2_gpu(); +void psvs_gui_draw_osd2_fps(); +void psvs_gui_draw_osd2_batt(); +void psvs_gui_draw_osd2_mem(); + void psvs_gui_draw_template(); void psvs_gui_draw_header(); void psvs_gui_draw_batt_section(); @@ -96,4 +109,6 @@ int psvs_gui_init(); void psvs_gui_deinit(); void psvs_gui_cpy(); +void psvs_gui_change_bunr_off(); + #endif diff --git a/src/main.c b/src/main.c index 6604476..98d24d5 100644 --- a/src/main.c +++ b/src/main.c @@ -9,29 +9,32 @@ #include "gui.h" #include "perf.h" #include "profile.h" +#include "power.h" int module_get_offset(SceUID pid, SceUID modid, int segidx, size_t offset, uintptr_t *addr); int module_get_export_func(SceUID pid, const char *modname, uint32_t libnid, uint32_t funcnid, uintptr_t *func); bool ksceAppMgrIsExclusiveProcessRunning(); -bool ksceSblAimgrIsGenuineDolce(); +int ksceSblAimgrIsGenuineDolce(); +int ksceSblACMgrIsPspEmu(SceUID pid); +int ksceSblACMgrIsSceShell(SceUID pid); #define PSVS_MAX_HOOKS 18 static tai_hook_ref_t g_hookrefs[PSVS_MAX_HOOKS]; static SceUID g_hooks[PSVS_MAX_HOOKS]; static SceUID g_injects[1]; +#define PSVS_NUM_THREADS 2 static SceUID g_mutex_cpufreq_uid = -1; static SceUID g_mutex_procevent_uid = -1; -static SceUID g_thread_uid = -1; +static SceUID g_mutex_framebuf_uid = -1; +static SceUID g_thread_uid[] = { -1, -1 }; static bool g_thread_run = true; SceUID g_pid = INVALID_PID; +psvs_app_t g_app = PSVS_APP_SCESHELL; char g_titleid[32] = ""; -bool g_is_in_pspemu = false; -bool g_is_dolce = false; -SceUID (*_ksceKernelGetProcessMainModule)(SceUID pid); -int (*_ksceKernelGetModuleInfo)(SceUID pid, SceUID modid, SceKernelModuleInfo *info); +bool g_is_dolce = false; int (*SceSysmemForKernel_0x3650963F)(uint32_t a1, SceSysmemAddressSpaceInfo *a2); int (*SceThreadmgrForDriver_0x7E280B69)(SceKernelSystemInfo *pInfo); @@ -53,7 +56,7 @@ int (*_kscePowerSetGpuXbarClockFrequency)(int freq); static void psvs_input_check(SceCtrlData *pad_data, int count) { // Do not pass input to fg app - if (psvs_gui_get_mode() == PSVS_GUI_MODE_FULL) { + if (g_app != PSVS_APP_BLACKLIST && psvs_gui_get_mode() == PSVS_GUI_MODE_FULL) { SceCtrlData kctrl; kctrl.buttons = 0; for (int i = 0; i < count; i++) @@ -62,34 +65,57 @@ static void psvs_input_check(SceCtrlData *pad_data, int count) { } int ksceDisplaySetFrameBufInternal_patched(int head, int index, const SceDisplayFrameBuf *pParam, int sync) { - if (!head || !pParam) + if (sync == PSVS_FRAMEBUF_HOOK_MAGIC) { + sync = 1; + goto DISPLAY_HOOK_RET; + } + + if (head != ksceDisplayGetPrimaryHead() || !pParam || !pParam->base) goto DISPLAY_HOOK_RET; - if (g_is_in_pspemu) + if (g_app == PSVS_APP_BLACKLIST) goto DISPLAY_HOOK_RET; - if (index && ksceAppMgrIsExclusiveProcessRunning()) + if (!index && g_app == PSVS_APP_SCESHELL) + goto DISPLAY_HOOK_RET; // Do not draw on i0 in SceShell + + if (index && (ksceAppMgrIsExclusiveProcessRunning() || g_app == PSVS_APP_GAME || g_app == PSVS_APP_SYSTEM_XCL)) goto DISPLAY_HOOK_RET; // Do not draw over SceShell overlay psvs_gui_mode_t mode = psvs_gui_get_mode(); if (mode == PSVS_GUI_MODE_HIDDEN) goto DISPLAY_HOOK_RET; + int ret = ksceKernelLockMutex(g_mutex_framebuf_uid, 1, NULL); + if (ret < 0) + goto DISPLAY_HOOK_RET; + psvs_perf_calc_fps(); - psvs_gui_set_framebuf(pParam); - if (mode == PSVS_GUI_MODE_FULL) { + if (mode == PSVS_GUI_MODE_FULL || mode == PSVS_GUI_MODE_OSD2) psvs_perf_poll_memory(); - } + + psvs_gui_set_framebuf(pParam); if (mode == PSVS_GUI_MODE_FPS || mode == PSVS_GUI_MODE_FULL) { psvs_gui_dd_fps(); // draw fps onto fb } - if (mode == PSVS_GUI_MODE_OSD || mode == PSVS_GUI_MODE_FULL) { + if (mode == PSVS_GUI_MODE_OSD || mode == PSVS_GUI_MODE_OSD2 || mode == PSVS_GUI_MODE_FULL) { psvs_gui_cpy(); // cpy from buffer + + if (sync && mode == PSVS_GUI_MODE_FULL && g_app != PSVS_APP_SCESHELL && g_app != PSVS_APP_SYSTEM) { + // update now to fix flicker when vblank period is missed + ksceKernelUnlockMutex(g_mutex_framebuf_uid, 1); + + int ret = TAI_CONTINUE(int, g_hookrefs[0], head, index, pParam, 0); + ret = ksceDisplaySetFrameBufInternal(head, index, pParam, PSVS_FRAMEBUF_HOOK_MAGIC); + return ret; + } } + ksceKernelUnlockMutex(g_mutex_framebuf_uid, 1); + DISPLAY_HOOK_RET: return TAI_CONTINUE(int, g_hookrefs[0], head, index, pParam, sync); } @@ -110,14 +136,16 @@ int kscePowerSetArmClockFrequency_patched(int freq) { freq = psvs_oc_get_target_freq(PSVS_OC_DEVICE_CPU, freq); - if (freq > 444 && freq <= 500) { - TAI_CONTINUE(int, g_hookrefs[9], 444); - psvs_oc_holy_shit(); - ret = 0; + if (freq == 468 || freq == 500) { + ret = TAI_CONTINUE(int, g_hookrefs[9], 444); } else { ret = TAI_CONTINUE(int, g_hookrefs[9], freq); } + if (ret == 0) { + ret = psvs_oc_set_cpu_freq(freq); + } + ksceKernelUnlockMutex(g_mutex_cpufreq_uid, 1); return ret; } @@ -141,8 +169,38 @@ DECL_FUNC_HOOK_PATCH_FREQ_GETTER(15, scePowerGetBusClockFrequency, PSVS_OC_D DECL_FUNC_HOOK_PATCH_FREQ_GETTER(16, scePowerGetGpuClockFrequency, PSVS_OC_DEVICE_GPU_ES4) DECL_FUNC_HOOK_PATCH_FREQ_GETTER(17, scePowerGetGpuXbarClockFrequency, PSVS_OC_DEVICE_GPU_XBAR) +static psvs_app_t _psvs_get_app_type(int pid, const char *titleid) { + psvs_app_t app = PSVS_APP_MAX; + + if (ksceSblACMgrIsPspEmu(pid)) { + app = PSVS_APP_BLACKLIST; + } else if (!strncmp(titleid, "NPXS", 4)) { + app = PSVS_APP_SYSTEM; + + // TODO: Figure out a way to do this on the fly + + if (!strncmp(&titleid[4], "10079", 5) || // Daily Checker BG + !strncmp(&titleid[4], "10063", 5)) { // MsgMW + app = PSVS_APP_MAX; // not an app + } else if (!strncmp(&titleid[4], "10007", 5) || // Welcome Park + !strncmp(&titleid[4], "10010", 5) || // Videos + !strncmp(&titleid[4], "10026", 5) || // Content Manager + !strncmp(&titleid[4], "10095", 5)) { // Panoramic Camera + app = PSVS_APP_SYSTEM_XCL; // exclusive + } + } else if (ksceSblACMgrIsSceShell(pid) && !strncmp(titleid, "main", 4)) { + app = PSVS_APP_SCESHELL; + } else { + app = PSVS_APP_GAME; + } + + return app; +} + int ksceKernelInvokeProcEventHandler_patched(int pid, int ev, int a3, int a4, int *a5, int a6) { char titleid[sizeof(g_titleid)]; + psvs_app_t app = PSVS_APP_SCESHELL; + int ret = ksceKernelLockMutex(g_mutex_procevent_uid, 1, NULL); if (ret < 0) goto PROCEVENT_EXIT; @@ -150,50 +208,44 @@ int ksceKernelInvokeProcEventHandler_patched(int pid, int ev, int a3, int a4, in switch (ev) { case 1: // startup case 5: // resume - // Ignore startup events if exclusive proc is already running - if (ksceAppMgrIsExclusiveProcessRunning() - && strncmp(g_titleid, "main", 4) != 0) + // Ignore startup events if non-SceShell app is running + if (g_app != PSVS_APP_SCESHELL) goto PROCEVENT_UNLOCK_EXIT; - // Check if pid is PspEmu - SceKernelModuleInfo info; - info.size = sizeof(SceKernelModuleInfo); - _ksceKernelGetModuleInfo(pid, _ksceKernelGetProcessMainModule(pid), &info); - if (!strncmp(info.module_name, "ScePspemu", 9)) { - g_is_in_pspemu = true; - snprintf(titleid, sizeof(titleid), "ScePspemu"); - break; - } - // Check titleid ksceKernelGetProcessTitleId(pid, titleid, sizeof(titleid)); - if (!strncmp(titleid, "NPXS", 4)) + + // Check app type + app = _psvs_get_app_type(pid, titleid); + if (app == PSVS_APP_MAX) // not an app goto PROCEVENT_UNLOCK_EXIT; break; case 3: // exit case 4: // suspend - // Check titleid - ksceKernelGetProcessTitleId(pid, titleid, sizeof(titleid)); - if (!strncmp(titleid, "NPXS", 4)) + if (g_pid != pid) goto PROCEVENT_UNLOCK_EXIT; - g_is_in_pspemu = false; + app = PSVS_APP_SCESHELL; snprintf(titleid, sizeof(titleid), "main"); break; } if (ev == 1 || ev == 5 || ev == 3 || ev == 4) { if (strncmp(g_titleid, titleid, sizeof(g_titleid))) { + // Set titleid strncpy(g_titleid, titleid, sizeof(g_titleid)); - // Set current pid + // Set pid g_pid = (ev == 1 || ev == 5) ? pid : INVALID_PID; - // Load profile if app changed - if (g_is_in_pspemu || !psvs_profile_load()) { - // If no profile exists or in PspEmu, + // Set type + g_app = app; + + // Load profile + if (g_app == PSVS_APP_BLACKLIST || !psvs_profile_load()) { + // If no profile exists or in blacklisted app, // reset all options to default psvs_oc_init(); } @@ -207,10 +259,12 @@ int ksceKernelInvokeProcEventHandler_patched(int pid, int ev, int a3, int a4, in return TAI_CONTINUE(int, g_hookrefs[13], pid, ev, a3, a4, a5, a6); } -static int psvs_thread(SceSize args, void *argp) { +static int psvs_gui_thread(SceSize args, void *argp) { + uint32_t burn_off_counter = 0; + while (g_thread_run) { - if (g_is_in_pspemu) { - // Don't do anything if PspEmu is running + if (g_app == PSVS_APP_BLACKLIST) { + // Don't do anything if blacklisted app is running ksceKernelDelayThread(200 * 1000); continue; } @@ -227,15 +281,37 @@ static int psvs_thread(SceSize args, void *argp) { psvs_gui_mode_t mode = psvs_gui_get_mode(); // If in OSD/FULL mode, poll shown info - if (mode == PSVS_GUI_MODE_OSD || mode == PSVS_GUI_MODE_FULL) { - psvs_perf_poll_cpu(); + if (mode == PSVS_GUI_MODE_OSD || mode == PSVS_GUI_MODE_OSD2 || mode == PSVS_GUI_MODE_FULL) { + if(psvs_oc_get_mode(PSVS_OC_DEVICE_CPU) != PSVS_OC_MODE_AUTO) + psvs_perf_poll_cpu(PSVS_POWER_PLAN_MAX); psvs_perf_poll_batt(); } + // Measure system power consumption + psvs_perf_compute_power(); + + // Move text to avoid OLED burn every 5 mins (6000 * 50 ms = 300000 ms = 300 s = 5 mins) + if (burn_off_counter >= 6000) + { + psvs_gui_change_bunr_off(); + + if (mode == PSVS_GUI_MODE_OSD) { + psvs_gui_draw_osd_template(); + } else if (mode == PSVS_GUI_MODE_OSD2) { + psvs_gui_draw_osd2_template(); + } else if (mode == PSVS_GUI_MODE_FULL) { + psvs_gui_draw_template(); + } + + burn_off_counter = 0; + } + // Redraw buffer template on gui mode or fb change if (fb_or_mode_changed) { if (mode == PSVS_GUI_MODE_OSD) { psvs_gui_draw_osd_template(); + } else if (mode == PSVS_GUI_MODE_OSD2) { + psvs_gui_draw_osd2_template(); } else if (mode == PSVS_GUI_MODE_FULL) { psvs_gui_draw_template(); } @@ -248,8 +324,17 @@ static int psvs_thread(SceSize args, void *argp) { psvs_gui_draw_osd_batt(); } + // Draw OSD2 mode + else if (mode == PSVS_GUI_MODE_OSD2) { + psvs_gui_draw_osd2_cpu(); + psvs_gui_draw_osd2_gpu(); + psvs_gui_draw_osd2_mem(); + psvs_gui_draw_osd2_fps(); + psvs_gui_draw_osd2_batt(); + } + // Draw FULL mode - if (mode == PSVS_GUI_MODE_FULL) { + else if (mode == PSVS_GUI_MODE_FULL) { psvs_gui_draw_header(); psvs_gui_draw_batt_section(); psvs_gui_draw_cpu_section(); @@ -257,12 +342,46 @@ static int psvs_thread(SceSize args, void *argp) { psvs_gui_draw_menu(); } + burn_off_counter++; + ksceKernelDelayThread(50 * 1000); } return 0; } +static int psvs_auto_clocks_thread(SceSize args, void *argp) { + uint8_t counter = 0; + const uint8_t min_lower_counter_value = 25; + const uint8_t min_raise_counter_value = 3; + while (g_thread_run) { + if (g_app == PSVS_APP_BLACKLIST || psvs_oc_get_mode(PSVS_OC_DEVICE_CPU) != PSVS_OC_MODE_AUTO) { + // Don't do anything if blacklisted app is running + ksceKernelDelayThread(200 * 1000); + continue; + } + + // Poll cpu information to perform clocks control + psvs_perf_poll_cpu(psvs_oc_get_power_plan(PSVS_OC_DEVICE_CPU)); + + // Compute dynamic cpu freq if auto mode is selected + if (psvs_oc_check_raise_freq(PSVS_OC_DEVICE_CPU) && counter >= min_raise_counter_value) { + psvs_oc_change(PSVS_OC_DEVICE_CPU, true); + counter = 0; + } + if (psvs_oc_check_lower_freq(PSVS_OC_DEVICE_CPU) && counter >= min_lower_counter_value) { + psvs_oc_change(PSVS_OC_DEVICE_CPU, false); + counter /= 2; + } + + if (counter < min_lower_counter_value) + counter++; + ksceKernelDelayThread(20 * 1000); + } + + return 0; +} + void _start() __attribute__ ((weak, alias ("module_start"))); int module_start(SceSize argc, const void *args) { int ret = 0; @@ -301,6 +420,7 @@ int module_start(SceSize argc, const void *args) { g_mutex_cpufreq_uid = ksceKernelCreateMutex("psvs_mutex_cpufreq", 0, 0, NULL); g_mutex_procevent_uid = ksceKernelCreateMutex("psvs_mutex_procevent", 0, 0, NULL); + g_mutex_framebuf_uid = ksceKernelCreateMutex("psvs_mutex_framebuf", 0, 0, NULL); psvs_oc_init(); // reset profile options to default @@ -345,19 +465,6 @@ int module_start(SceSize argc, const void *args) { g_hooks[17] = taiHookFunctionExportForKernel(KERNEL_PID, &g_hookrefs[17], "ScePower", 0x1082DA7F, 0x0A750DEE, scePowerGetGpuXbarClockFrequency_patched); - ret = module_get_export_func(KERNEL_PID, - "SceKernelModulemgr", 0xC445FA63, 0x20A27FA9, (uintptr_t *)&_ksceKernelGetProcessMainModule); // 3.60 - if (ret < 0) { - module_get_export_func(KERNEL_PID, - "SceKernelModulemgr", 0x92C9FFC2, 0x679F5144, (uintptr_t *)&_ksceKernelGetProcessMainModule); // 3.65 - } - ret = module_get_export_func(KERNEL_PID, - "SceKernelModulemgr", 0xC445FA63, 0xD269F915, (uintptr_t *)&_ksceKernelGetModuleInfo); // 3.60 - if (ret < 0) { - module_get_export_func(KERNEL_PID, - "SceKernelModulemgr", 0x92C9FFC2, 0xDAA90093, (uintptr_t *)&_ksceKernelGetModuleInfo); // 3.65 - } - ret = module_get_export_func(KERNEL_PID, "SceSysmem", 0x63A519E5, 0x3650963F, (uintptr_t *)&SceSysmemForKernel_0x3650963F); // 3.60 if (ret < 0) { @@ -377,17 +484,24 @@ int module_start(SceSize argc, const void *args) { snprintf(g_titleid, sizeof(g_titleid), "main"); psvs_profile_load(); - g_thread_uid = ksceKernelCreateThread("psvs_thread", psvs_thread, 0x3C, 0x3000, 0, 0x10000, 0); - ksceKernelStartThread(g_thread_uid, 0, NULL); + // Init variables for power measurement + psvs_perf_init_power_meter(); + + g_thread_uid[0] = ksceKernelCreateThread("psvs_gui_thread", psvs_gui_thread, 0x3C, 0x3000, 0, 0x10000, 0); + g_thread_uid[1] = ksceKernelCreateThread("psvs_auto_clocks_thread", psvs_auto_clocks_thread, 0x3B, 0x2000, 0, 0x10000, 0); + ksceKernelStartThread(g_thread_uid[0], 0, NULL); + ksceKernelStartThread(g_thread_uid[1], 0, NULL); return SCE_KERNEL_START_SUCCESS; } int module_stop(SceSize argc, const void *args) { - if (g_thread_uid >= 0) { - g_thread_run = 0; - ksceKernelWaitThreadEnd(g_thread_uid, NULL, NULL); - ksceKernelDeleteThread(g_thread_uid); + for(uint i = 0; i < PSVS_NUM_THREADS; i++) { + if (g_thread_uid[i] >= 0) { + g_thread_run = 0; + ksceKernelWaitThreadEnd(g_thread_uid[i], NULL, NULL); + ksceKernelDeleteThread(g_thread_uid[i]); + } } for (int i = 0; i < PSVS_MAX_HOOKS; i++) { @@ -402,6 +516,8 @@ int module_stop(SceSize argc, const void *args) { ksceKernelDeleteMutex(g_mutex_cpufreq_uid); if (g_mutex_procevent_uid >= 0) ksceKernelDeleteMutex(g_mutex_procevent_uid); + if (g_mutex_framebuf_uid >= 0) + ksceKernelDeleteMutex(g_mutex_framebuf_uid); psvs_gui_deinit(); diff --git a/src/main.h b/src/main.h index 1ccda27..02dbb06 100644 --- a/src/main.h +++ b/src/main.h @@ -2,7 +2,7 @@ #define _MAIN_H_ #include "perf.h" -#define PSVS_VERSION_STRING "PSVshell v1.1" +#define PSVS_VERSION_STRING "PSVshell v1.4.8beta" #define PSVS_VERSION_VER "PSVS0100" #define DECL_FUNC_HOOK_PATCH_CTRL(index, name) \ @@ -23,11 +23,29 @@ return freq; \ } +#define DACR_UNRESTRICT(state) \ + asm volatile ("mrc p15, 0, %0, c3, c0, 0\n\t" \ + "mcr p15, 0, %1, c3, c0, 0" : "=&r" (state) : "r" (0xffffffff)); + +#define DACR_RESET(state) \ + asm volatile ("mcr p15, 0, %0, c3, c0, 0" : : "r" (state)); + #define INVALID_PID -1 +#define PSVS_FRAMEBUF_HOOK_MAGIC 0x7183015 + +typedef enum { + PSVS_APP_SCESHELL, + PSVS_APP_SYSTEM, + PSVS_APP_SYSTEM_XCL, + PSVS_APP_GAME, // or homebrew + PSVS_APP_BLACKLIST, // pspemu + PSVS_APP_MAX +} psvs_app_t; + extern SceUID g_pid; +extern psvs_app_t g_app; extern char g_titleid[32]; -extern bool g_is_in_pspemu; extern bool g_is_dolce; extern int (*SceSysmemForKernel_0x3650963F)(uint32_t a1, SceSysmemAddressSpaceInfo *a2); diff --git a/src/oc.c b/src/oc.c index 42a1e6d..42a7869 100644 --- a/src/oc.c +++ b/src/oc.c @@ -5,6 +5,7 @@ #include "main.h" #include "oc.h" +#include "power.h" // Declare helper getter/setter for GpuEs4 static int __kscePowerGetGpuEs4ClockFrequency() { @@ -26,7 +27,7 @@ PSVS_OC_DECL_SETTER(_kscePowerSetGpuXbarClockFrequency); static psvs_oc_devopt_t g_oc_devopt[PSVS_OC_DEVICE_MAX] = { [PSVS_OC_DEVICE_CPU] = { - .freq_n = 8, .freq = {41, 83, 111, 166, 222, 333, 444, 500}, .default_freq = 333, + .freq_n = 16, .freq = {41, 83, 111, 126, 147, 166, 181, 209, 222, 251, 292, 333, 389, 444, 468, 500}, .default_freq = 333, .get_freq = __kscePowerGetArmClockFrequency, .set_freq = __kscePowerSetArmClockFrequency }, @@ -50,7 +51,9 @@ static psvs_oc_devopt_t g_oc_devopt[PSVS_OC_DEVICE_MAX] = { static psvs_oc_profile_t g_oc = { .ver = PSVS_VERSION_VER, .mode = {0}, - .manual_freq = {0} + .target_freq = {0}, + .max_freq = {0}, + .power_plan = {0} }; static bool g_oc_has_changed = true; @@ -62,25 +65,110 @@ int psvs_oc_set_freq(psvs_oc_device_t device, int freq) { return g_oc_devopt[device].set_freq(freq); } -void psvs_oc_holy_shit() { - // Apply mul:div (15:0) - ScePervasiveForDriver_0xE9D95643(15, 16 - 0); +// Called from kscePowerSetArmClockFrequency hook +int psvs_oc_set_cpu_freq(int freq) { + int mul, ndiv, ret; + switch (freq) { + case 41: + mul = 1; + ndiv = 16; + break; + case 83: + mul = 3; + ndiv = 16; + break; + case 111: + mul = 4; + ndiv = 16; + break; + case 126: + mul = 5; + ndiv = 16 - 4; + break; + case 147: + mul = 5; + ndiv = 16 - 2; + break; + case 166: + mul = 5; + ndiv = 16; + break; + case 181: + mul = 6; + ndiv = 16 - 3; + break; + case 209: + mul = 6; + ndiv = 16 - 1; + break; + case 222: + mul = 6; + ndiv = 16; + break; + case 251: + mul = 7; + ndiv = 16 - 4; + break; + case 292: + mul = 7; + ndiv = 16 - 2; + break; + case 333: + mul = 7; + ndiv = 16; + break; + case 389: + mul = 8; + ndiv = 16 - 2; + break; + case 444: + mul = 8; + ndiv = 16; + break; + case 468: + mul = 9; + ndiv = 16 - 1; + break; + case 500: + mul = 9; + ndiv = 16 - 0; + break; + default: + /** + * In this scenario the CPU frequency should have been rounded up to the nearest supported by scePower. + * So a request for 125MHz would be rounded to 166 and so on. + */ + return 0; + } + + ret = ScePervasiveForDriver_0xE9D95643(mul, ndiv); + if (ret == 0) { + *ScePower_41C8 = freq; + *ScePower_41CC = mul; + } - // Store global freq & mul for kscePowerGetArmClockFrequency() - *ScePower_41C8 = 500; - *ScePower_41CC = 15; + return ret; } int psvs_oc_get_target_freq(psvs_oc_device_t device, int default_freq) { - if (g_oc.mode[device] == PSVS_OC_MODE_MANUAL) - return g_oc.manual_freq[device]; + if (g_oc.mode[device] != PSVS_OC_MODE_DEFAULT) + return g_oc.target_freq[device]; return default_freq; } +int psvs_oc_get_max_freq(psvs_oc_device_t device) { + if (g_oc.mode[device] != PSVS_OC_MODE_DEFAULT) + return g_oc.max_freq[device]; + return g_oc_devopt[device].default_freq; +} + void psvs_oc_set_target_freq(psvs_oc_device_t device) { // Refresh manual clocks if (g_oc.mode[device] == PSVS_OC_MODE_MANUAL) - psvs_oc_set_freq(device, g_oc.manual_freq[device]); + psvs_oc_set_freq(device, g_oc.max_freq[device]); + // Refresh auto clocks + else if (g_oc.mode[device] == PSVS_OC_MODE_AUTO) + psvs_oc_set_freq(device, g_oc.target_freq[device]); // Restore default clocks else if (g_oc.mode[device] == PSVS_OC_MODE_DEFAULT) psvs_oc_set_freq(device, psvs_oc_get_default_freq(device)); @@ -147,13 +235,14 @@ int psvs_oc_get_default_freq(psvs_oc_device_t device) { return valid ? freq : g_oc_devopt[device].default_freq; } -void psvs_oc_reset_manual(psvs_oc_device_t device) { - g_oc.manual_freq[device] = psvs_oc_get_freq(device); +void psvs_oc_reset(psvs_oc_device_t device) { + g_oc.max_freq[device] = psvs_oc_get_freq(device); + g_oc.target_freq[device] = psvs_oc_get_freq(device); g_oc_has_changed = true; } -void psvs_oc_change_manual(psvs_oc_device_t device, bool raise_freq) { - int target_freq = g_oc.manual_freq[device]; // current manual freq +void psvs_oc_change(psvs_oc_device_t device, bool raise_freq) { + int target_freq = g_oc.target_freq[device]; // current freq for (int i = 0; i < g_oc_devopt[device].freq_n; i++) { int ii = raise_freq ? i : g_oc_devopt[device].freq_n - i - 1; @@ -164,18 +253,88 @@ void psvs_oc_change_manual(psvs_oc_device_t device, bool raise_freq) { } } - g_oc.manual_freq[device] = target_freq; - g_oc_has_changed = true; + // Keep clocks inside the limits (PSVS_OC_CPU_MIN_FREQ to max_freq) in AUTO mode + if (g_oc.mode[device] == PSVS_OC_MODE_AUTO) { + if (target_freq < PSVS_OC_CPU_MIN_FREQ) + target_freq = PSVS_OC_CPU_MIN_FREQ; + if (target_freq > g_oc.max_freq[device]) + target_freq = g_oc.max_freq[device]; + } + // In manual mode, max_freq and target_freq are the same + if (g_oc.mode[device] == PSVS_OC_MODE_MANUAL) { + g_oc.max_freq[device] = target_freq; + g_oc_has_changed = true; + } + g_oc.target_freq[device] = target_freq; // Refresh manual clocks - if (g_oc.mode[device] == PSVS_OC_MODE_MANUAL) - psvs_oc_set_freq(device, g_oc.manual_freq[device]); + if (g_oc.mode[device] != PSVS_OC_MODE_DEFAULT) + psvs_oc_set_freq(device, g_oc.max_freq[device]); +} + +bool psvs_oc_check_raise_freq(psvs_oc_device_t device) { + if(device != PSVS_OC_DEVICE_CPU) + return false; + + int freq = g_oc_devopt[device].get_freq(); + int peak = psvs_perf_get_smooth_peak(); + int avg = (psvs_perf_get_load(0) + psvs_perf_get_load(1) + psvs_perf_get_load(2)) / 3; + int power_plan = g_oc.power_plan[device]; + + return psvs_power_cpu_raise_freq(power_plan, freq, peak, avg); +} + +bool psvs_oc_check_lower_freq(psvs_oc_device_t device) { + if(device != PSVS_OC_DEVICE_CPU) + return false; + + int freq = g_oc_devopt[device].get_freq(); + int peak = psvs_perf_get_smooth_peak(); + int power_plan = g_oc.power_plan[device]; + + return psvs_power_cpu_lower_freq(power_plan, freq, peak); +} + +void psvs_oc_change_max_freq(psvs_oc_device_t device, bool raise_freq) { + int max_freq = g_oc.max_freq[device]; // current max freq + + for (int i = 0; i < g_oc_devopt[device].freq_n; i++) { + int ii = raise_freq ? i : g_oc_devopt[device].freq_n - i - 1; + if ((raise_freq && g_oc_devopt[device].freq[ii] > max_freq) + || (!raise_freq && g_oc_devopt[device].freq[ii] < max_freq)) { + max_freq = g_oc_devopt[device].freq[ii]; + break; + } + } + // Update max freq + g_oc.max_freq[device] = max_freq; + if (g_oc.target_freq[device] > max_freq) { + g_oc.target_freq[device] = max_freq; + psvs_oc_set_target_freq(device); + } + + if (device == PSVS_OC_DEVICE_CPU) + psvs_perf_reset_peak(raise_freq); + + g_oc_has_changed = true; +} + +int psvs_oc_get_power_plan(psvs_oc_device_t device) { + return g_oc.power_plan[device]; +} + +void psvs_oc_raise_power_plan(bool raise_plan, psvs_oc_device_t device) { + if ((raise_plan && g_oc.power_plan[device] == PSVS_POWER_PLAN_PERFORMANCE) || (!raise_plan && g_oc.power_plan[device] == PSVS_POWER_PLAN_SAVER)) + return; + + g_oc.power_plan[device] += (raise_plan ? 1 : -1); + g_oc_has_changed = true; } void psvs_oc_init() { g_oc_has_changed = true; for (int i = 0; i < PSVS_OC_DEVICE_MAX; i++) { g_oc.mode[i] = PSVS_OC_MODE_DEFAULT; - psvs_oc_reset_manual(i); + psvs_oc_reset(i); } } diff --git a/src/oc.h b/src/oc.h index a3d6dce..2426572 100644 --- a/src/oc.h +++ b/src/oc.h @@ -1,7 +1,8 @@ #ifndef _OC_H_ #define _OC_H_ -#define PSVS_OC_MAX_FREQ_N 10 +#define PSVS_OC_MAX_FREQ_N 31 +#define PSVS_OC_CPU_MIN_FREQ 111 #define PSVS_OC_DECL_SETTER(fun) \ static int _##fun(int freq) { return fun(freq); } @@ -19,13 +20,16 @@ typedef enum { typedef enum { PSVS_OC_MODE_DEFAULT, PSVS_OC_MODE_MANUAL, + PSVS_OC_MODE_AUTO, PSVS_OC_MODE_MAX } psvs_oc_mode_t; typedef struct { char ver[8]; psvs_oc_mode_t mode[PSVS_OC_DEVICE_MAX]; - int manual_freq[PSVS_OC_DEVICE_MAX]; + int target_freq[PSVS_OC_DEVICE_MAX]; + int max_freq[PSVS_OC_DEVICE_MAX]; + int power_plan[PSVS_OC_DEVICE_MAX]; } psvs_oc_profile_t; typedef struct { @@ -36,11 +40,13 @@ typedef struct { int (*set_freq)(int freq); } psvs_oc_devopt_t; + int psvs_oc_get_freq(psvs_oc_device_t device); int psvs_oc_set_freq(psvs_oc_device_t device, int freq); -void psvs_oc_holy_shit(); +int psvs_oc_set_cpu_freq(int freq); int psvs_oc_get_target_freq(psvs_oc_device_t device, int default_freq); +int psvs_oc_get_max_freq(psvs_oc_device_t device); void psvs_oc_set_target_freq(psvs_oc_device_t device); psvs_oc_mode_t psvs_oc_get_mode(psvs_oc_device_t device); void psvs_oc_set_mode(psvs_oc_device_t device, psvs_oc_mode_t mode); @@ -54,9 +60,18 @@ void psvs_oc_set_changed(bool changed); // default freq int psvs_oc_get_default_freq(psvs_oc_device_t device); -// manual freq adjust -void psvs_oc_reset_manual(psvs_oc_device_t device); -void psvs_oc_change_manual(psvs_oc_device_t device, bool raise_freq); +// freq adjust +void psvs_oc_reset(psvs_oc_device_t device); +void psvs_oc_change(psvs_oc_device_t device, bool raise_freq); + +// auto (cpu) freq adjust +bool psvs_oc_check_raise_freq(psvs_oc_device_t device); +bool psvs_oc_check_lower_freq(psvs_oc_device_t device); +void psvs_oc_change_max_freq(psvs_oc_device_t device, bool raise_freq); + +// power plan adjust +int psvs_oc_get_power_plan(psvs_oc_device_t device); +void psvs_oc_raise_power_plan(bool raise_plan, psvs_oc_device_t device); void psvs_oc_init(); diff --git a/src/perf.c b/src/perf.c index 8788ebc..1d42cf1 100644 --- a/src/perf.c +++ b/src/perf.c @@ -2,6 +2,7 @@ #include #include +#include "power.h" #include "main.h" SceUInt32 ksceKernelGetProcessTimeLowCore(); @@ -14,10 +15,12 @@ SceUInt32 ksceKernelSysrootGetCurrentAddressSpaceCB(); static int g_perf_peak_usage_samples[PSVS_PERF_PEAK_SAMPLES] = {0}; static int g_perf_peak_usage_rotation = 0; static int g_perf_usage[4] = {0, 0, 0, 0}; +static int g_peak_smooth_usage = 50; -static SceUInt32 g_perf_tick_last = 0; // AVG CPU load -static SceUInt32 g_perf_tick_q_last = 0; // Peak CPU load -static SceUInt32 g_perf_tick_fps_last = 0; // Framerate +static SceUInt32 g_perf_tick_last = 0; // AVG CPU load +static SceUInt32 g_perf_tick_q_last = 0; // Peak CPU load +static SceUInt32 g_perf_tick_fps_last = 0; // Framerate +static SceUInt32 g_perf_tick_power_last = 0; // Power consumption static SceKernelSysClock g_perf_idle_clock_last[4] = {0, 0, 0, 0}; static SceKernelSysClock g_perf_idle_clock_q_last[4] = {0, 0, 0, 0}; @@ -30,10 +33,22 @@ static uint32_t g_perf_frametime_sum = 0; static uint8_t g_perf_frametime_n = 0; static int g_perf_fps = 0; +static int g_perf_batt_capacity_last; + int psvs_perf_get_fps() { return g_perf_fps; } +int psvs_perf_get_fps_cap() +{ + if (g_perf_fps <= 31) + return PSVS_PERF_FPS_30; + else if (g_perf_fps <= 61) + return PSVS_PERF_FPS_60; + else + return PSVS_PERF_FPS_UNCAP; +} + int psvs_perf_get_load(int core) { return g_perf_usage[core]; } @@ -45,6 +60,10 @@ int psvs_perf_get_peak() { return peak_total / PSVS_PERF_PEAK_SAMPLES; } +int psvs_perf_get_smooth_peak() { + return g_peak_smooth_usage; +} + psvs_battery_t *psvs_perf_get_batt() { return &g_perf_batt; } @@ -70,7 +89,7 @@ void psvs_perf_calc_fps() { g_perf_tick_fps_last = tick_now; } -void psvs_perf_poll_cpu() { +void psvs_perf_poll_cpu(int performance_mode) { SceUInt32 tick_now = ksceKernelGetProcessTimeLowCore(); SceUInt32 tick_diff = tick_now - g_perf_tick_last; SceUInt32 tick_q_diff = tick_now - g_perf_tick_q_last; @@ -105,10 +124,30 @@ void psvs_perf_poll_cpu() { max_usage = 0; if (max_usage > 100) max_usage = 100; + g_perf_peak_usage_samples[g_perf_peak_usage_rotation] = max_usage; g_perf_peak_usage_rotation++; if (g_perf_peak_usage_rotation >= PSVS_PERF_PEAK_SAMPLES) g_perf_peak_usage_rotation = 0; // flip + + // Calculate a smooth peak usage for auto clocks + if (performance_mode != PSVS_POWER_PLAN_MAX) { + int avg_peak = psvs_perf_get_peak(); + if (performance_mode == PSVS_POWER_PLAN_SAVER) { + g_peak_smooth_usage = avg_peak >= g_peak_smooth_usage ? (avg_peak * 0.03 + g_peak_smooth_usage * 0.97) : + (avg_peak * 0.1 + g_peak_smooth_usage * 0.9); + } + else if (performance_mode == PSVS_POWER_PLAN_BALANCED) { + g_peak_smooth_usage = avg_peak >= g_peak_smooth_usage ? (avg_peak * 0.05 + g_peak_smooth_usage * 0.95) : + (avg_peak * 0.05 + g_peak_smooth_usage * 0.95); + } + else { // PSVS_POWER_PLAN_PERFORMANCE + g_peak_smooth_usage = avg_peak >= g_peak_smooth_usage ? (avg_peak * 0.175 + g_peak_smooth_usage * 0.825) : + (avg_peak * 0.02 + g_peak_smooth_usage * 0.98); + } + + } + g_perf_tick_q_last = tick_now; } @@ -174,3 +213,37 @@ void psvs_perf_poll_batt() { bool bval = kscePowerIsBatteryCharging(); PSVS_CHECK_ASSIGN(g_perf_batt, is_charging, bval); } + +void psvs_perf_init_power_meter() { + g_perf_batt_capacity_last = kscePowerGetBatteryRemainCapacity(); + g_perf_tick_power_last = ksceKernelGetProcessTimeLowCore(); + g_perf_batt.power_cons = 0; +} + +void psvs_perf_compute_power() { + int measuredCapacity = kscePowerGetBatteryRemainCapacity(); + if (g_perf_batt_capacity_last != measuredCapacity) { + SceUInt32 tick_now = ksceKernelGetProcessTimeLowCore(); + float elapsed_time = (float)(tick_now - g_perf_tick_power_last) / (float)SECOND; + + // Check elapsed_time to avoid crash (posible division by 0 or a really small number) + if (elapsed_time >= 5.0f) { + int milli_watts = (int) (abs(measuredCapacity - g_perf_batt_capacity_last) * kscePowerGetBatteryVolt() * 0.001f / (elapsed_time / 3600.0f)); + //double wattsHour = (abs(measuredCapacity - g_perf_batt_capacity_last) * scePowerGetBatteryVolt() * 0.001); + + milli_watts = g_perf_batt.is_charging ? -milli_watts : milli_watts; + + PSVS_CHECK_ASSIGN(g_perf_batt, power_cons, milli_watts); + + g_perf_batt_capacity_last = measuredCapacity; + g_perf_tick_power_last = ksceKernelGetProcessTimeLowCore(); + } + } +} + +void psvs_perf_reset_peak(bool raise) +{ + g_peak_smooth_usage = (int) ((raise ? g_peak_smooth_usage * 0.93f : g_peak_smooth_usage * 1.07f) + 0.5f); + for (uint8_t i = 0; i < PSVS_PERF_PEAK_SAMPLES; i++) + g_perf_peak_usage_samples[i] = g_peak_smooth_usage; +} diff --git a/src/perf.h b/src/perf.h index f724b9c..7497a19 100644 --- a/src/perf.h +++ b/src/perf.h @@ -5,6 +5,12 @@ if (struct.field != (new_value)) struct._has_changed = true; \ struct.field = (new_value) +typedef enum { + PSVS_PERF_FPS_30, + PSVS_PERF_FPS_60, + PSVS_PERF_FPS_UNCAP +} psvs_perf_fps_cap_t; + typedef struct SceKernelSystemInfo { SceSize size; SceUInt32 activeCpuMask; @@ -37,18 +43,25 @@ typedef struct psvs_battery_t { int percent; int lt_hours; int lt_minutes; + int power_cons; bool is_charging; bool _has_changed; } psvs_battery_t; void psvs_perf_calc_fps(); -void psvs_perf_poll_cpu(); +void psvs_perf_poll_cpu(int performance_mode); void psvs_perf_poll_memory(); void psvs_perf_poll_batt(); +void psvs_perf_init_power_meter(); +void psvs_perf_compute_power(); +void psvs_perf_reset_peak(bool raise); + int psvs_perf_get_fps(); +int psvs_perf_get_fps_cap(); int psvs_perf_get_load(int core); int psvs_perf_get_peak(); +int psvs_perf_get_smooth_peak(); psvs_battery_t *psvs_perf_get_batt(); psvs_memory_t *psvs_perf_get_memusage(); diff --git a/src/power.c b/src/power.c new file mode 100644 index 0000000..7d221ed --- /dev/null +++ b/src/power.c @@ -0,0 +1,87 @@ +#include + +#include "power.h" + +bool psvs_power_cpu_raise_freq(int power_plan, int freq, int peak_usage, int avg_usage) { + switch (power_plan) + { + case PSVS_POWER_PLAN_SAVER: + if(freq <= 111 && peak_usage >= 55) + return true; + if(freq <= 222 && peak_usage >= 65) + return true; + if(freq <= 333 && (peak_usage >= 75 || avg_usage >= 55)) + return true; + if(freq <= 500 && (peak_usage >= 85 || avg_usage >= 65)) + return true; + break; + + case PSVS_POWER_PLAN_BALANCED: + if(freq <= 111 && peak_usage >= 45) + return true; + if(freq <= 222 && peak_usage >= 50) + return true; + if(freq <= 333 && (peak_usage >= 60 || avg_usage >= 47)) + return true; + if(freq <= 500 && (peak_usage >= 65 || avg_usage >= 60)) + return true; + break; + + case PSVS_POWER_PLAN_PERFORMANCE: + if(freq <= 111 && peak_usage >= 40) + return true; + if(freq <= 222 && peak_usage >= 47) + return true; + if(freq <= 333 && (peak_usage >= 55 || avg_usage >= 42)) + return true; + if(freq <= 500 && (peak_usage >= 65 || avg_usage >= 50)) + return true; + break; + + default: + break; + } + return false; +} + +bool psvs_power_cpu_lower_freq(int power_plan, int freq, int peak_usage) { + switch (power_plan) + { + case PSVS_POWER_PLAN_SAVER: + if (freq <= 500 && peak_usage < 65) + return true; + if (freq <= 333 && peak_usage < 57) + return true; + if (freq <= 222 && peak_usage < 45) + return true; + if (freq <= 111 && peak_usage < 40) + return true; + break; + + case PSVS_POWER_PLAN_BALANCED: + if (freq <= 500 && peak_usage < 55) + return true; + if (freq <= 333 && peak_usage < 45) + return true; + if (freq <= 222 && peak_usage < 40) + return true; + if (freq <= 111 && peak_usage < 35) + return true; + break; + + case PSVS_POWER_PLAN_PERFORMANCE: + if (freq <= 500 && peak_usage < 55) + return true; + if (freq <= 333 && peak_usage < 40) + return true; + if (freq <= 222 && peak_usage < 35) + return true; + if (freq <= 111 && peak_usage < 30) + return true; + break; + + default: + break; + } + return false; +} \ No newline at end of file diff --git a/src/power.h b/src/power.h new file mode 100644 index 0000000..3f9de03 --- /dev/null +++ b/src/power.h @@ -0,0 +1,14 @@ +#ifndef _POWER_H_ +#define _POWER_H_ + +typedef enum { + PSVS_POWER_PLAN_SAVER, + PSVS_POWER_PLAN_BALANCED, + PSVS_POWER_PLAN_PERFORMANCE, + PSVS_POWER_PLAN_MAX +} psvs_power_plan_t; + +bool psvs_power_cpu_raise_freq(int power_plan, int freq, int peak_usage, int avg_usage); +bool psvs_power_cpu_lower_freq(int power_plan, int freq, int peak_usage); + +#endif \ No newline at end of file