mirror of
https://github.com/FeralInteractive/gamemode.git
synced 2025-06-26 17:31:45 +02:00
Add an option for using a different governor for integrated GPUs
This commit adds two new configuration options: igpu_desiredgov and igpu_power_threshold which allow for a different CPU governor when the Intel integrated GPU is under load. This currently only applies to Intel integrated GPUs and not AMD APUs because it uses the Intel RAPL infrastructure for getting power information. If on a platform that without an Intel integrated GPU or where the kernel does not support RAPL, the new options will be ignored and it will fall back to the old behavior. One of the core principals of gamemoded to date has been that, when playing a game, we want to use the "performance" CPU governor to increase CPU performance and prevent CPU-limiting. However, when the integrated GPU is under load, this can be counter-productive because the CPU and GPU share a thermal and power budget. By throwing the CPU governor to "performance" game mode currently makes the CPU frequency management far too aggressive and it burns more power than needed. With a discrete GPU, this is fine because the worst that happens is a bit more fan noise. With an integrated GPU, however, the additional power being burned by the CPU is power not available to the GPU and this can cause the GPU to clock down and lead to significantly worse performance. By using the "powersave" governor instead of the "performance" governor while the integrated GPU is under load, we can save power on the CPU side which lets the GPU clock up higher. On my Razer Blade Stealth 13 with an i7-1065G7, this improves the performance of "Shadow of the Tomb Raider" by around 25-30% according to its internal benchmark mode.
This commit is contained in:
@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "common-governors.h"
|
||||
#include "common-helpers.h"
|
||||
#include "common-logging.h"
|
||||
#include "common-power.h"
|
||||
|
||||
#include "gamemode.h"
|
||||
#include "gamemode-config.h"
|
||||
@ -66,6 +67,7 @@ struct GameModeClient {
|
||||
enum GameModeGovernor {
|
||||
GAME_MODE_GOVERNOR_DEFAULT,
|
||||
GAME_MODE_GOVERNOR_DESIRED,
|
||||
GAME_MODE_GOVERNOR_IGPU_DESIRED,
|
||||
};
|
||||
|
||||
struct GameModeContext {
|
||||
@ -82,6 +84,10 @@ struct GameModeContext {
|
||||
struct GameModeGPUInfo *stored_gpu; /**<Stored GPU info for the current GPU */
|
||||
struct GameModeGPUInfo *target_gpu; /**<Target GPU info for the current GPU */
|
||||
|
||||
bool igpu_optimization_enabled;
|
||||
uint32_t last_cpu_energy_uj;
|
||||
uint32_t last_igpu_energy_uj;
|
||||
|
||||
/* Reaper control */
|
||||
struct {
|
||||
pthread_t thread;
|
||||
@ -219,6 +225,11 @@ static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor g
|
||||
gov_str = gov_config_str[0] != '\0' ? gov_config_str : "performance";
|
||||
break;
|
||||
|
||||
case GAME_MODE_GOVERNOR_IGPU_DESIRED:
|
||||
config_get_igpu_desired_governor(self->config, gov_config_str);
|
||||
gov_str = gov_config_str[0] != '\0' ? gov_config_str : "powersave";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(!"Invalid governor requested");
|
||||
}
|
||||
@ -240,6 +251,93 @@ static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor g
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void game_mode_enable_igpu_optimization(GameModeContext *self)
|
||||
{
|
||||
float threshold = config_get_igpu_power_threshold(self->config);
|
||||
|
||||
/* There's no way the GPU is using 10000x the power. This lets us
|
||||
* short-circuit if the config file specifies an invalid threshold
|
||||
* and we want to disable the iGPU heuristic.
|
||||
*/
|
||||
if (threshold < 10000 && get_cpu_energy_uj(&self->last_cpu_energy_uj) &&
|
||||
get_igpu_energy_uj(&self->last_igpu_energy_uj)) {
|
||||
LOG_MSG(
|
||||
"Successfully queried power data for the CPU and iGPU. "
|
||||
"Enabling the integrated GPU optimization");
|
||||
self->igpu_optimization_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void game_mode_disable_igpu_optimization(GameModeContext *self)
|
||||
{
|
||||
self->igpu_optimization_enabled = false;
|
||||
}
|
||||
|
||||
static void game_mode_check_igpu_energy(GameModeContext *self)
|
||||
{
|
||||
pthread_rwlock_wrlock(&self->rwlock);
|
||||
|
||||
/* We only care if we're not in the default governor */
|
||||
if (self->current_govenor == GAME_MODE_GOVERNOR_DEFAULT)
|
||||
goto unlock;
|
||||
|
||||
if (!self->igpu_optimization_enabled)
|
||||
goto unlock;
|
||||
|
||||
uint32_t cpu_energy_uj, igpu_energy_uj;
|
||||
if (!get_cpu_energy_uj(&cpu_energy_uj) || !get_igpu_energy_uj(&igpu_energy_uj)) {
|
||||
/* We've already succeeded at getting power information once so
|
||||
* failing here is possible but very unexpected. */
|
||||
self->igpu_optimization_enabled = false;
|
||||
LOG_ERROR("Failed to get CPU and iGPU power data\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* The values we query from RAPL are in units of microjoules of energy
|
||||
* used since boot or since the last time the counter rolled over. You
|
||||
* can get average power over some time window T by sampling before and
|
||||
* after and doing the following calculation
|
||||
*
|
||||
* power_uw = (energy_uj_after - energy_uj_before) / seconds
|
||||
*
|
||||
* To get the power in Watts (rather than microwatts), you can simply
|
||||
* divide by 1000000.
|
||||
*
|
||||
* Because we're only concerned with the ratio between the GPU and CPU
|
||||
* power, we never bother dividing by 1000000 the length of time of the
|
||||
* sampling window because that would just algebraically cancel out.
|
||||
* Instead, we divide the GPU energy used in the window (difference of
|
||||
* before and after) by the CPU energy used. It nicely provides the
|
||||
* ratio of the averages and there are no instantaneous sampling
|
||||
* problems.
|
||||
*
|
||||
* Overflow is possible here. However, that would simply mean that
|
||||
* the HW counter has overflowed and us wrapping around is probably
|
||||
* the right thing to do. Wrapping at 32 bits is exactly what the
|
||||
* Linux kernel's turbostat utility does so it's probably right.
|
||||
*/
|
||||
uint32_t cpu_energy_delta_uj = cpu_energy_uj - self->last_cpu_energy_uj;
|
||||
uint32_t igpu_energy_delta_uj = igpu_energy_uj - self->last_igpu_energy_uj;
|
||||
self->last_cpu_energy_uj = cpu_energy_uj;
|
||||
self->last_igpu_energy_uj = igpu_energy_uj;
|
||||
|
||||
if (cpu_energy_delta_uj == 0) {
|
||||
LOG_ERROR("CPU reported no energy used\n");
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
float threshold = config_get_igpu_power_threshold(self->config);
|
||||
double ratio = (double)igpu_energy_delta_uj / (double)cpu_energy_delta_uj;
|
||||
if (ratio > threshold) {
|
||||
game_mode_set_governor(self, GAME_MODE_GOVERNOR_IGPU_DESIRED);
|
||||
} else {
|
||||
game_mode_set_governor(self, GAME_MODE_GOVERNOR_DESIRED);
|
||||
}
|
||||
|
||||
unlock:
|
||||
pthread_rwlock_unlock(&self->rwlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pivot into game mode.
|
||||
*
|
||||
@ -251,7 +349,12 @@ static void game_mode_context_enter(GameModeContext *self)
|
||||
LOG_MSG("Entering Game Mode...\n");
|
||||
sd_notifyf(0, "STATUS=%sGameMode is now active.%s\n", "\x1B[1;32m", "\x1B[0m");
|
||||
|
||||
game_mode_set_governor(self, GAME_MODE_GOVERNOR_DESIRED);
|
||||
if (game_mode_set_governor(self, GAME_MODE_GOVERNOR_DESIRED) == 0) {
|
||||
/* We just switched to a non-default governor. Enable the iGPU
|
||||
* optimization.
|
||||
*/
|
||||
game_mode_enable_igpu_optimization(self);
|
||||
}
|
||||
|
||||
/* Inhibit the screensaver */
|
||||
if (config_get_inhibit_screensaver(self->config))
|
||||
@ -290,6 +393,8 @@ static void game_mode_context_leave(GameModeContext *self)
|
||||
|
||||
game_mode_set_governor(self, GAME_MODE_GOVERNOR_DEFAULT);
|
||||
|
||||
game_mode_disable_igpu_optimization(self);
|
||||
|
||||
char scripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
|
||||
memset(scripts, 0, sizeof(scripts));
|
||||
config_get_gamemode_end_scripts(self->config, scripts);
|
||||
@ -822,6 +927,9 @@ static void *game_mode_context_reaper(void *userdata)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check on the CPU/iGPU energy balance */
|
||||
game_mode_check_igpu_energy(self);
|
||||
|
||||
/* Expire remaining entries */
|
||||
game_mode_context_auto_expire(self);
|
||||
|
||||
|
Reference in New Issue
Block a user