diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c index 8ff056c..df24016 100644 --- a/daemon/daemon_config.c +++ b/daemon/daemon_config.c @@ -92,6 +92,7 @@ struct GameModeConfig { long nv_core_clock_mhz_offset; long nv_mem_clock_mhz_offset; long nv_perf_level; + long nv_powermizer_mode; char amd_performance_level[CONFIG_VALUE_MAX]; long require_supervisor; @@ -258,6 +259,8 @@ static int inih_handler(void *user, const char *section, const char *name, const valid = get_long_value(name, value, &self->values.nv_mem_clock_mhz_offset); } else if (strcmp(name, "nv_perf_level") == 0) { valid = get_long_value(name, value, &self->values.nv_perf_level); + } else if (strcmp(name, "nv_powermizer_mode") == 0) { + valid = get_long_value(name, value, &self->values.nv_powermizer_mode); } else if (strcmp(name, "amd_performance_level") == 0) { valid = get_string_value(value, self->values.amd_performance_level); } @@ -330,6 +333,7 @@ static void load_config_files(GameModeConfig *self) self->values.reaper_frequency = DEFAULT_REAPER_FREQ; self->values.gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */ self->values.nv_perf_level = -1; + self->values.nv_powermizer_mode = -1; self->values.script_timeout = 10; /* Default to 10 seconds for scripts */ /* @@ -582,6 +586,7 @@ DEFINE_CONFIG_GET(gpu_device) DEFINE_CONFIG_GET(nv_core_clock_mhz_offset) DEFINE_CONFIG_GET(nv_mem_clock_mhz_offset) DEFINE_CONFIG_GET(nv_perf_level) +DEFINE_CONFIG_GET(nv_powermizer_mode) void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VALUE_MAX]) { diff --git a/daemon/daemon_config.h b/daemon/daemon_config.h index b6c971c..d6795f5 100644 --- a/daemon/daemon_config.h +++ b/daemon/daemon_config.h @@ -143,6 +143,7 @@ long config_get_gpu_device(GameModeConfig *self); long config_get_nv_core_clock_mhz_offset(GameModeConfig *self); long config_get_nv_mem_clock_mhz_offset(GameModeConfig *self); long config_get_nv_perf_level(GameModeConfig *self); +long config_get_nv_powermizer_mode(GameModeConfig *self); void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VALUE_MAX]); /** diff --git a/daemon/gamemode-gpu.c b/daemon/gamemode-gpu.c index 1250480..d805049 100644 --- a/daemon/gamemode-gpu.c +++ b/daemon/gamemode-gpu.c @@ -118,6 +118,8 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) case Vendor_NVIDIA: new_info->nv_core = config_get_nv_core_clock_mhz_offset(config); new_info->nv_mem = config_get_nv_mem_clock_mhz_offset(config); + new_info->nv_perf_level = config_get_nv_perf_level(config); + new_info->nv_powermizer_mode = config_get_nv_powermizer_mode(config); /* Reject values over some guessed values * If a user wants to go into very unsafe levels they can recompile @@ -138,7 +140,6 @@ int game_mode_initialise_gpu(GameModeConfig *config, GameModeGPUInfo **info) } /* Sanity check the performance level value as well */ - new_info->nv_perf_level = config_get_nv_perf_level(config); if (new_info->nv_perf_level < 0 || new_info->nv_perf_level > 16) { LOG_ERROR( "NVIDIA Performance level value likely invalid (%ld), will not apply " @@ -204,6 +205,8 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info) snprintf(nv_mem, 8, "%ld", info->nv_mem); char nv_perf_level[4]; snprintf(nv_perf_level, 4, "%ld", info->nv_perf_level); + char nv_powermizer_mode[4]; + snprintf(nv_powermizer_mode, 4, "%ld", info->nv_powermizer_mode); // Set up our command line to pass to gpuclockctl const char *const exec_args[] = { @@ -213,8 +216,9 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info) device, "set", info->vendor == Vendor_NVIDIA ? nv_core : info->amd_performance_level, - info->vendor == Vendor_NVIDIA ? nv_mem : NULL, /* Only use this if Nvidia */ - info->vendor == Vendor_NVIDIA ? nv_perf_level : NULL, /* Only use this if Nvidia */ + info->vendor == Vendor_NVIDIA ? nv_mem : NULL, /* Only use this if Nvidia */ + info->vendor == Vendor_NVIDIA ? nv_perf_level : NULL, /* Only use this if Nvidia */ + info->vendor == Vendor_NVIDIA ? nv_powermizer_mode : NULL, /* Only use this if Nvidia */ NULL, }; @@ -255,17 +259,21 @@ int game_mode_get_gpu(GameModeGPUInfo *info) LOG_ERROR("Failed to call gpuclockctl, could not get values!\n"); return -1; } + strtok(buffer, "\n"); switch (info->vendor) { case Vendor_NVIDIA: - if (sscanf(buffer, "%ld %ld", &info->nv_core, &info->nv_mem) != 2) { + if (sscanf(buffer, + "%ld %ld %ld", + &info->nv_core, + &info->nv_mem, + &info->nv_powermizer_mode) != 3) { LOG_ERROR("Failed to parse gpuclockctl output: %s\n", buffer); return -1; } break; case Vendor_AMD: strncpy(info->amd_performance_level, buffer, CONFIG_VALUE_MAX); - strtok(info->amd_performance_level, "\n"); break; } diff --git a/daemon/gamemode-tests.c b/daemon/gamemode-tests.c index af70fe8..91004b0 100644 --- a/daemon/gamemode-tests.c +++ b/daemon/gamemode-tests.c @@ -444,6 +444,7 @@ int run_gpu_optimisation_tests(struct GameModeConfig *config) /* Grab the expected values */ long expected_core = gpuinfo->nv_core; long expected_mem = gpuinfo->nv_mem; + long expected_nv_powermizer_mode = gpuinfo->nv_powermizer_mode; char expected_amd_performance_level[CONFIG_VALUE_MAX]; strncpy(expected_amd_performance_level, gpuinfo->amd_performance_level, CONFIG_VALUE_MAX); @@ -451,6 +452,7 @@ int run_gpu_optimisation_tests(struct GameModeConfig *config) game_mode_get_gpu(gpuinfo); long original_nv_core = gpuinfo->nv_core; long original_nv_mem = gpuinfo->nv_mem; + long original_nv_powermizer_mode = gpuinfo->nv_powermizer_mode; char original_amd_performance_level[CONFIG_VALUE_MAX]; strncpy(original_amd_performance_level, gpuinfo->amd_performance_level, CONFIG_VALUE_MAX); @@ -465,14 +467,18 @@ int run_gpu_optimisation_tests(struct GameModeConfig *config) } if (gpuinfo->vendor == Vendor_NVIDIA && - (gpuinfo->nv_core != expected_core || gpuinfo->nv_mem != expected_mem)) { + (gpuinfo->nv_core != expected_core || gpuinfo->nv_mem != expected_mem || + gpuinfo->nv_powermizer_mode != expected_nv_powermizer_mode)) { LOG_ERROR( "Current Nvidia GPU clocks during gamemode do not match requested values!\n" - "\tnv_core - expected:%ld was:%ld | nv_mem - expected:%ld was:%ld\n", + "\tnv_core - expected:%ld was:%ld | nv_mem - expected:%ld was:%ld | nv_powermizer_mode " + "- expected:%ld was:%ld\n", expected_core, gpuinfo->nv_core, expected_mem, - gpuinfo->nv_mem); + gpuinfo->nv_mem, + expected_nv_powermizer_mode, + gpuinfo->nv_powermizer_mode); gpustatus = -1; } else if (gpuinfo->vendor == Vendor_AMD && strcmp(expected_amd_performance_level, gpuinfo->amd_performance_level) != 0) { @@ -494,14 +500,18 @@ int run_gpu_optimisation_tests(struct GameModeConfig *config) } if (gpuinfo->vendor == Vendor_NVIDIA && - (gpuinfo->nv_core != original_nv_core || gpuinfo->nv_mem != original_nv_mem)) { + (gpuinfo->nv_core != original_nv_core || gpuinfo->nv_mem != original_nv_mem || + gpuinfo->nv_powermizer_mode != original_nv_powermizer_mode)) { LOG_ERROR( "Current Nvidia GPU clocks after gamemode do not matcch original values!\n" - "\tcore - original:%ld was:%ld | nv_mem - original:%ld was:%ld\n", + "\tnv_core - original:%ld was:%ld | nv_mem - original:%ld was:%ld | nv_powermizer_mode " + "- original:%ld was:%ld\n", original_nv_core, gpuinfo->nv_core, original_nv_mem, - gpuinfo->nv_mem); + gpuinfo->nv_mem, + original_nv_powermizer_mode, + gpuinfo->nv_powermizer_mode); gpustatus = -1; } else if (gpuinfo->vendor == Vendor_AMD && strcmp(original_amd_performance_level, gpuinfo->amd_performance_level) != 0) { diff --git a/daemon/gpu-control.h b/daemon/gpu-control.h index 1da386c..325605f 100644 --- a/daemon/gpu-control.h +++ b/daemon/gpu-control.h @@ -49,8 +49,9 @@ struct GameModeGPUInfo { long device; /* path to device, ie. /sys/class/drm/card#/ */ long nv_perf_level; /* The Nvidia Performance Level to adjust */ - long nv_core; /* Nvidia core clock */ - long nv_mem; /* Nvidia mem clock */ + long nv_core; /* Nvidia core clock */ + long nv_mem; /* Nvidia mem clock */ + long nv_powermizer_mode; /* NV Powermizer Mode */ char amd_performance_level[CONFIG_VALUE_MAX]; /* The AMD performance level set to */ }; diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c index a227fde..ae74ba0 100644 --- a/daemon/gpuclockctl.c +++ b/daemon/gpuclockctl.c @@ -39,7 +39,9 @@ POSSIBILITY OF SUCH DAMAGE. /* NV constants */ #define NV_CORE_OFFSET_ATTRIBUTE "GPUGraphicsClockOffset" #define NV_MEM_OFFSET_ATTRIBUTE "GPUMemoryTransferRateOffset" -#define NV_ATTRIBUTE_FORMAT "[gpu:%ld]/%s[%ld]" +#define NV_POWERMIZER_MODE_ATTRIBUTE "GPUPowerMizerMode" +#define NV_ATTRIBUTE_FORMAT "[gpu:%ld]/%s" +#define NV_PERF_LEVEL_FORMAT "[%ld]" /* AMD constants */ #define AMD_DRM_PATH "/sys/class/drm/card%ld/device/%s" @@ -71,16 +73,16 @@ static int get_gpu_state_nv(struct GameModeGPUInfo *info) char buf[EXTERNAL_BUFFER_MAX] = { 0 }; char *end; - /* Set the GPUGraphicsClockOffset parameter */ + /* Get the GPUGraphicsClockOffset parameter */ snprintf(arg, 128, - NV_ATTRIBUTE_FORMAT, + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT, info->device, NV_CORE_OFFSET_ATTRIBUTE, info->nv_perf_level); const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; if (run_external_process(exec_args_core, buf, -1) != 0) { - LOG_ERROR("Failed to set %s!\n", arg); + LOG_ERROR("Failed to get %s!\n", arg); return -1; } @@ -90,16 +92,16 @@ static int get_gpu_state_nv(struct GameModeGPUInfo *info) return -1; } - /* Set the GPUMemoryTransferRateOffset parameter */ + /* Get the GPUMemoryTransferRateOffset parameter */ snprintf(arg, 128, - NV_ATTRIBUTE_FORMAT, + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT, info->device, NV_MEM_OFFSET_ATTRIBUTE, info->nv_perf_level); const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; if (run_external_process(exec_args_mem, buf, -1) != 0) { - LOG_ERROR("Failed to set %s!\n", arg); + LOG_ERROR("Failed to get %s!\n", arg); return -1; } @@ -109,6 +111,20 @@ static int get_gpu_state_nv(struct GameModeGPUInfo *info) return -1; } + /* Get the GPUPowerMizerMode parameter */ + snprintf(arg, 128, NV_ATTRIBUTE_FORMAT, info->device, NV_POWERMIZER_MODE_ATTRIBUTE); + const char *exec_args_pm[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; + if (run_external_process(exec_args_pm, buf, -1) != 0) { + LOG_ERROR("Failed to get %s!\n", arg); + return -1; + } + + info->nv_powermizer_mode = strtol(buf, &end, 10); + if (end == buf) { + LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); + return -1; + } + return 0; } @@ -125,33 +141,49 @@ static int set_gpu_state_nv(struct GameModeGPUInfo *info) "Setting Nvidia parameters requires DISPLAY and XAUTHORITY to be set - will likely " "fail!\n"); + char arg[128] = { 0 }; + /* Set the GPUGraphicsClockOffset parameter */ - char core_arg[128]; - snprintf(core_arg, + snprintf(arg, 128, - NV_ATTRIBUTE_FORMAT "=%ld", + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT "=%ld", info->device, NV_CORE_OFFSET_ATTRIBUTE, info->nv_perf_level, info->nv_core); - const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", core_arg, NULL }; + const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; if (run_external_process(exec_args_core, NULL, -1) != 0) { - LOG_ERROR("Failed to set %s!\n", core_arg); + LOG_ERROR("Failed to set %s!\n", arg); return -1; } /* Set the GPUMemoryTransferRateOffset parameter */ - char mem_arg[128]; - snprintf(mem_arg, + snprintf(arg, 128, - NV_ATTRIBUTE_FORMAT "=%ld", + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT "=%ld", info->device, NV_MEM_OFFSET_ATTRIBUTE, info->nv_perf_level, info->nv_mem); - const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-a", mem_arg, NULL }; + const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; if (run_external_process(exec_args_mem, NULL, -1) != 0) { - LOG_ERROR("Failed to set %s!\n", mem_arg); + LOG_ERROR("Failed to set %s!\n", arg); + return -1; + } + + /* Set the GPUPowerMizerMode parameter if requested */ + if (info->nv_powermizer_mode == -1) + return 0; + + snprintf(arg, + 128, + NV_ATTRIBUTE_FORMAT "=%ld", + info->device, + NV_POWERMIZER_MODE_ATTRIBUTE, + info->nv_powermizer_mode); + const char *exec_args_pm[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; + if (run_external_process(exec_args_pm, NULL, -1) != 0) { + LOG_ERROR("Failed to set %s!\n", arg); return -1; } @@ -306,7 +338,7 @@ int main(int argc, char *argv[]) /* Get nvidia power level */ if (get_gpu_state_nv(&info) != 0) exit(EXIT_FAILURE); - printf("%ld %ld\n", info.nv_core, info.nv_mem); + printf("%ld %ld %ld\n", info.nv_core, info.nv_mem, info.nv_powermizer_mode); break; case Vendor_AMD: if (get_gpu_state_amd(&info) != 0) @@ -318,7 +350,7 @@ int main(int argc, char *argv[]) break; } - } else if (argc >= 5 && argc <= 7 && strncmp(argv[3], "set", 3) == 0) { + } else if (argc >= 5 && argc <= 8 && strncmp(argv[3], "set", 3) == 0) { /* Get and verify the vendor and device */ struct GameModeGPUInfo info; memset(&info, 0, sizeof(info)); @@ -331,6 +363,10 @@ int main(int argc, char *argv[]) info.nv_mem = get_generic_value(argv[5]); if (argc > 6) info.nv_perf_level = get_generic_value(argv[6]); + if (argc > 7) + info.nv_powermizer_mode = get_generic_value(argv[7]); + else + info.nv_powermizer_mode = -1; break; case Vendor_AMD: strncpy(info.amd_performance_level, argv[4], CONFIG_VALUE_MAX); diff --git a/example/gamemode.ini b/example/gamemode.ini index 3bd0337..9b7817f 100644 --- a/example/gamemode.ini +++ b/example/gamemode.ini @@ -46,12 +46,17 @@ inhibit_screensaver=1 ; The DRM device number on the system (usually 0), ie. the number in /sys/class/drm/card0/ ;gpu_device=0 -; Nvidia specific settings (these are Mhz offsets from the baseline, ie. 0 applies no change) +; Nvidia specific settings ; Requires the coolbits extension activated in nvidia-xconfig -;nv_core_clock_mhz_offset=0 -;nv_mem_clock_mhz_offset=0 +; This corresponds to the desired GPUPowerMizerMode +; Generally "Adaptive"=0 "Prefer Maximum Performance"=1 and "Auto"=2) +;nv_powermizer_mode=1 + ; This corresponds to the performance level to edit in nvidia-xconfig (usually the highest level - on older cards 1, newer cards can be 4 or higher) ;nv_perf_level=1 +; (these two are Mhz offsets from the baseline, ie. 0 applies no change) +;nv_core_clock_mhz_offset=0 +;nv_mem_clock_mhz_offset=0 ; AMD specific settings ; Requires a relatively up to date AMDGPU kernel module